Loading packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +78 −44 Original line number Original line Diff line number Diff line Loading @@ -16,16 +16,22 @@ package com.android.systemui.media package com.android.systemui.media import android.content.Context import android.content.res.Configuration import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.FeatureFlags 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 import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Utils import javax.inject.Inject import javax.inject.Inject import javax.inject.Named import javax.inject.Named Loading @@ -38,7 +44,10 @@ class KeyguardMediaController @Inject constructor( @param:Named(KEYGUARD) private val mediaHost: MediaHost, @param:Named(KEYGUARD) private val mediaHost: MediaHost, private val bypassController: KeyguardBypassController, private val bypassController: KeyguardBypassController, private val statusBarStateController: SysuiStatusBarStateController, private val statusBarStateController: SysuiStatusBarStateController, private val notifLockscreenUserManager: NotificationLockscreenUserManager private val notifLockscreenUserManager: NotificationLockscreenUserManager, private val featureFlags: FeatureFlags, private val context: Context, configurationController: ConfigurationController ) { ) { init { init { Loading @@ -46,12 +55,10 @@ class KeyguardMediaController @Inject constructor( override fun onStateChanged(newState: Int) { override fun onStateChanged(newState: Int) { refreshMediaPosition() refreshMediaPosition() } } }) override fun onDozingChanged(isDozing: Boolean) { configurationController.addCallback(object : ConfigurationController.ConfigurationListener { if (!isDozing) { override fun onConfigChanged(newConfig: Configuration?) { mediaHost.visible = true updateResources() refreshMediaPosition() } } } }) }) Loading @@ -62,6 +69,22 @@ class KeyguardMediaController @Inject constructor( // Let's now initialize this view, which also creates the host view for us. // Let's now initialize this view, which also creates the host view for us. mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN) mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN) updateResources() } private fun updateResources() { useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources) } @VisibleForTesting var useSplitShade = false set(value) { if (field == value) { return } field = value reattachHostView() refreshMediaPosition() } } /** /** Loading @@ -78,17 +101,25 @@ class KeyguardMediaController @Inject constructor( var singlePaneContainer: MediaHeaderView? = null var singlePaneContainer: MediaHeaderView? = null private set private set private var splitShadeContainer: ViewGroup? = null private var splitShadeContainer: ViewGroup? = null private var useSplitShadeContainer: () -> Boolean = { false } /** /** * Attaches media container in single pane mode, situated at the top of the notifications list * Attaches media container in single pane mode, situated at the top of the notifications list */ */ fun attachSinglePaneContainer(mediaView: MediaHeaderView?) { fun attachSinglePaneContainer(mediaView: MediaHeaderView?) { val needsListener = singlePaneContainer == null singlePaneContainer = mediaView singlePaneContainer = mediaView if (needsListener) { // On reinflation we don't want to add another listener mediaHost.addVisibilityChangeListener(this::onMediaHostVisibilityChanged) } reattachHostView() onMediaHostVisibilityChanged(mediaHost.visible) } // Required to show it for the first time, afterwards visibility is managed automatically /** mediaHost.visible = true * Called whenever the media hosts visibility changes mediaHost.addVisibilityChangeListener { visible -> */ private fun onMediaHostVisibilityChanged(visible: Boolean) { refreshMediaPosition() refreshMediaPosition() if (visible) { if (visible) { mediaHost.hostView.layoutParams.apply { mediaHost.hostView.layoutParams.apply { Loading @@ -97,15 +128,36 @@ class KeyguardMediaController @Inject constructor( } } } } } } refreshMediaPosition() } /** /** * Attaches media container in split shade mode, situated to the left of notifications * Attaches media container in split shade mode, situated to the left of notifications */ */ fun attachSplitShadeContainer(container: ViewGroup, useContainer: () -> Boolean) { fun attachSplitShadeContainer(container: ViewGroup) { splitShadeContainer = container splitShadeContainer = container useSplitShadeContainer = useContainer reattachHostView() refreshMediaPosition() } private fun reattachHostView() { val inactiveContainer: ViewGroup? val activeContainer: ViewGroup? if (useSplitShade) { activeContainer = splitShadeContainer inactiveContainer = singlePaneContainer } else { inactiveContainer = splitShadeContainer activeContainer = singlePaneContainer } if (inactiveContainer?.childCount == 1) { inactiveContainer.removeAllViews() } if (activeContainer?.childCount == 0) { // Detach the hostView from its parent view if exists mediaHost.hostView.parent?.let { (it as? ViewGroup)?.removeView(mediaHost.hostView) } activeContainer.addView(mediaHost.hostView) } } } fun refreshMediaPosition() { fun refreshMediaPosition() { Loading @@ -124,35 +176,17 @@ class KeyguardMediaController @Inject constructor( } } private fun showMediaPlayer() { private fun showMediaPlayer() { if (useSplitShadeContainer()) { if (useSplitShade) { showMediaPlayer( setVisibility(splitShadeContainer, View.VISIBLE) activeContainer = splitShadeContainer, setVisibility(singlePaneContainer, View.GONE) inactiveContainer = singlePaneContainer) } else { } else { showMediaPlayer( setVisibility(singlePaneContainer, View.VISIBLE) activeContainer = singlePaneContainer, setVisibility(splitShadeContainer, View.GONE) inactiveContainer = splitShadeContainer) } } private fun showMediaPlayer(activeContainer: ViewGroup?, inactiveContainer: ViewGroup?) { if (inactiveContainer?.childCount == 1) { inactiveContainer.removeAllViews() } // might be called a few times for the same view, no need to add hostView again if (activeContainer?.childCount == 0) { // Detach the hostView from its parent view if exists mediaHost.hostView.parent ?.let { (it as? ViewGroup)?.removeView(mediaHost.hostView) } activeContainer.addView(mediaHost.hostView) } } setVisibility(activeContainer, View.VISIBLE) setVisibility(inactiveContainer, View.GONE) } } private fun hideMediaPlayer() { private fun hideMediaPlayer() { if (useSplitShadeContainer()) { if (useSplitShade) { setVisibility(splitShadeContainer, View.GONE) setVisibility(splitShadeContainer, View.GONE) } else { } else { setVisibility(singlePaneContainer, View.GONE) setVisibility(singlePaneContainer, View.GONE) Loading packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +23 −8 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,11 @@ class MediaHost constructor( private var inited: Boolean = false private var inited: Boolean = false /** * Are we listening to media data changes? */ private var listeningToMediaData = false /** /** * Get the current bounds on the screen. This makes sure the state is fresh and up to date * Get the current bounds on the screen. This makes sure the state is fresh and up to date */ */ Loading Loading @@ -97,18 +102,17 @@ class MediaHost constructor( this.location = location this.location = location hostView = mediaHierarchyManager.register(this) hostView = mediaHierarchyManager.register(this) // Listen by default, as the host might not be attached by our clients, until // they get a visibility change. We still want to stay up to date in that case! setListeningToMediaData(true) hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View?) { override fun onViewAttachedToWindow(v: View?) { // we should listen to the combined state change, since otherwise there might setListeningToMediaData(true) // be a delay until the views and the controllers are initialized, leaving us // with either a blank view or the controllers not yet initialized and the // measuring wrong mediaDataManager.addListener(listener) updateViewVisibility() updateViewVisibility() } } override fun onViewDetachedFromWindow(v: View?) { override fun onViewDetachedFromWindow(v: View?) { mediaDataManager.removeListener(listener) setListeningToMediaData(false) } } }) }) Loading @@ -135,8 +139,19 @@ class MediaHost constructor( updateViewVisibility() updateViewVisibility() } } private fun setListeningToMediaData(listen: Boolean) { if (listen != listeningToMediaData) { listeningToMediaData = listen if (listen) { mediaDataManager.addListener(listener) } else { mediaDataManager.removeListener(listener) } } } private fun updateViewVisibility() { private fun updateViewVisibility() { visible = if (showsOnlyActiveMedia) { state.visible = if (showsOnlyActiveMedia) { mediaDataManager.hasActiveMedia() mediaDataManager.hasActiveMedia() } else { } else { mediaDataManager.hasAnyMedia() mediaDataManager.hasAnyMedia() Loading Loading @@ -300,7 +315,7 @@ interface MediaHostState { /** /** * If the view should be VISIBLE or GONE. * If the view should be VISIBLE or GONE. */ */ var visible: Boolean val visible: Boolean /** /** * Does this host need any falsing protection? * Does this host need any falsing protection? Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +1 −2 Original line number Original line Diff line number Diff line Loading @@ -1085,8 +1085,7 @@ public class NotificationPanelViewController extends PanelViewController { } } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { mKeyguardMediaController.attachSplitShadeContainer(container, mKeyguardMediaController.attachSplitShadeContainer(container); () -> mShouldUseSplitNotificationShade); } } private void initBottomArea() { private void initBottomArea() { Loading packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt +41 −14 Original line number Original line Diff line number Diff line Loading @@ -22,13 +22,16 @@ import android.view.View.VISIBLE import android.widget.FrameLayout import android.widget.FrameLayout import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.FeatureFlags 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 import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.UniqueObjectHostView import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Before import org.junit.Rule import org.junit.Rule import org.junit.Test import org.junit.Test Loading @@ -48,11 +51,16 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Mock @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var featureFlags: FeatureFlags @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager @JvmField @Rule @JvmField @Rule val mockito = MockitoJUnit.rule() val mockito = MockitoJUnit.rule() private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null) private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null) private val hostView = UniqueObjectHostView(context) private lateinit var keyguardMediaController: KeyguardMediaController private lateinit var keyguardMediaController: KeyguardMediaController @Before @Before Loading @@ -62,10 +70,17 @@ class KeyguardMediaControllerTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) .thenReturn(true) .thenReturn(true) whenever(mediaHost.hostView).thenReturn(UniqueObjectHostView(context)) whenever(mediaHost.hostView).thenReturn(hostView) keyguardMediaController = KeyguardMediaController(mediaHost, bypassController, keyguardMediaController = KeyguardMediaController( statusBarStateController, notificationLockscreenUserManager) mediaHost, bypassController, statusBarStateController, notificationLockscreenUserManager, featureFlags, context, configurationController ) keyguardMediaController.attachSinglePaneContainer(mediaHeaderView) keyguardMediaController.attachSinglePaneContainer(mediaHeaderView) } } Loading Loading @@ -105,11 +120,8 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Test @Test fun testActivatesSplitShadeContainerInSplitShadeMode() { fun testActivatesSplitShadeContainerInSplitShadeMode() { val splitShadeContainer = FrameLayout(context) val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer( keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) splitShadeContainer, keyguardMediaController.useSplitShade = true useContainer = { true }) keyguardMediaController.refreshMediaPosition() assertThat(splitShadeContainer.visibility).isEqualTo(VISIBLE) assertThat(splitShadeContainer.visibility).isEqualTo(VISIBLE) } } Loading @@ -117,13 +129,28 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Test @Test fun testActivatesSinglePaneContainerInSinglePaneMode() { fun testActivatesSinglePaneContainerInSinglePaneMode() { val splitShadeContainer = FrameLayout(context) val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer( keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) splitShadeContainer, useContainer = { false }) keyguardMediaController.refreshMediaPosition() assertThat(splitShadeContainer.visibility).isEqualTo(GONE) assertThat(splitShadeContainer.visibility).isEqualTo(GONE) assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE) assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE) } } @Test fun testAttachedToSplitShade() { val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) keyguardMediaController.useSplitShade = true assertTrue("HostView wasn't attached to the split pane container", splitShadeContainer.childCount == 1) } @Test fun testAttachedToSinglePane() { val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) assertTrue("HostView wasn't attached to the single pane container", mediaHeaderView.childCount == 1) } } } Loading
packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +78 −44 Original line number Original line Diff line number Diff line Loading @@ -16,16 +16,22 @@ package com.android.systemui.media package com.android.systemui.media import android.content.Context import android.content.res.Configuration import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.FeatureFlags 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 import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Utils import javax.inject.Inject import javax.inject.Inject import javax.inject.Named import javax.inject.Named Loading @@ -38,7 +44,10 @@ class KeyguardMediaController @Inject constructor( @param:Named(KEYGUARD) private val mediaHost: MediaHost, @param:Named(KEYGUARD) private val mediaHost: MediaHost, private val bypassController: KeyguardBypassController, private val bypassController: KeyguardBypassController, private val statusBarStateController: SysuiStatusBarStateController, private val statusBarStateController: SysuiStatusBarStateController, private val notifLockscreenUserManager: NotificationLockscreenUserManager private val notifLockscreenUserManager: NotificationLockscreenUserManager, private val featureFlags: FeatureFlags, private val context: Context, configurationController: ConfigurationController ) { ) { init { init { Loading @@ -46,12 +55,10 @@ class KeyguardMediaController @Inject constructor( override fun onStateChanged(newState: Int) { override fun onStateChanged(newState: Int) { refreshMediaPosition() refreshMediaPosition() } } }) override fun onDozingChanged(isDozing: Boolean) { configurationController.addCallback(object : ConfigurationController.ConfigurationListener { if (!isDozing) { override fun onConfigChanged(newConfig: Configuration?) { mediaHost.visible = true updateResources() refreshMediaPosition() } } } }) }) Loading @@ -62,6 +69,22 @@ class KeyguardMediaController @Inject constructor( // Let's now initialize this view, which also creates the host view for us. // Let's now initialize this view, which also creates the host view for us. mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN) mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN) updateResources() } private fun updateResources() { useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources) } @VisibleForTesting var useSplitShade = false set(value) { if (field == value) { return } field = value reattachHostView() refreshMediaPosition() } } /** /** Loading @@ -78,17 +101,25 @@ class KeyguardMediaController @Inject constructor( var singlePaneContainer: MediaHeaderView? = null var singlePaneContainer: MediaHeaderView? = null private set private set private var splitShadeContainer: ViewGroup? = null private var splitShadeContainer: ViewGroup? = null private var useSplitShadeContainer: () -> Boolean = { false } /** /** * Attaches media container in single pane mode, situated at the top of the notifications list * Attaches media container in single pane mode, situated at the top of the notifications list */ */ fun attachSinglePaneContainer(mediaView: MediaHeaderView?) { fun attachSinglePaneContainer(mediaView: MediaHeaderView?) { val needsListener = singlePaneContainer == null singlePaneContainer = mediaView singlePaneContainer = mediaView if (needsListener) { // On reinflation we don't want to add another listener mediaHost.addVisibilityChangeListener(this::onMediaHostVisibilityChanged) } reattachHostView() onMediaHostVisibilityChanged(mediaHost.visible) } // Required to show it for the first time, afterwards visibility is managed automatically /** mediaHost.visible = true * Called whenever the media hosts visibility changes mediaHost.addVisibilityChangeListener { visible -> */ private fun onMediaHostVisibilityChanged(visible: Boolean) { refreshMediaPosition() refreshMediaPosition() if (visible) { if (visible) { mediaHost.hostView.layoutParams.apply { mediaHost.hostView.layoutParams.apply { Loading @@ -97,15 +128,36 @@ class KeyguardMediaController @Inject constructor( } } } } } } refreshMediaPosition() } /** /** * Attaches media container in split shade mode, situated to the left of notifications * Attaches media container in split shade mode, situated to the left of notifications */ */ fun attachSplitShadeContainer(container: ViewGroup, useContainer: () -> Boolean) { fun attachSplitShadeContainer(container: ViewGroup) { splitShadeContainer = container splitShadeContainer = container useSplitShadeContainer = useContainer reattachHostView() refreshMediaPosition() } private fun reattachHostView() { val inactiveContainer: ViewGroup? val activeContainer: ViewGroup? if (useSplitShade) { activeContainer = splitShadeContainer inactiveContainer = singlePaneContainer } else { inactiveContainer = splitShadeContainer activeContainer = singlePaneContainer } if (inactiveContainer?.childCount == 1) { inactiveContainer.removeAllViews() } if (activeContainer?.childCount == 0) { // Detach the hostView from its parent view if exists mediaHost.hostView.parent?.let { (it as? ViewGroup)?.removeView(mediaHost.hostView) } activeContainer.addView(mediaHost.hostView) } } } fun refreshMediaPosition() { fun refreshMediaPosition() { Loading @@ -124,35 +176,17 @@ class KeyguardMediaController @Inject constructor( } } private fun showMediaPlayer() { private fun showMediaPlayer() { if (useSplitShadeContainer()) { if (useSplitShade) { showMediaPlayer( setVisibility(splitShadeContainer, View.VISIBLE) activeContainer = splitShadeContainer, setVisibility(singlePaneContainer, View.GONE) inactiveContainer = singlePaneContainer) } else { } else { showMediaPlayer( setVisibility(singlePaneContainer, View.VISIBLE) activeContainer = singlePaneContainer, setVisibility(splitShadeContainer, View.GONE) inactiveContainer = splitShadeContainer) } } private fun showMediaPlayer(activeContainer: ViewGroup?, inactiveContainer: ViewGroup?) { if (inactiveContainer?.childCount == 1) { inactiveContainer.removeAllViews() } // might be called a few times for the same view, no need to add hostView again if (activeContainer?.childCount == 0) { // Detach the hostView from its parent view if exists mediaHost.hostView.parent ?.let { (it as? ViewGroup)?.removeView(mediaHost.hostView) } activeContainer.addView(mediaHost.hostView) } } setVisibility(activeContainer, View.VISIBLE) setVisibility(inactiveContainer, View.GONE) } } private fun hideMediaPlayer() { private fun hideMediaPlayer() { if (useSplitShadeContainer()) { if (useSplitShade) { setVisibility(splitShadeContainer, View.GONE) setVisibility(splitShadeContainer, View.GONE) } else { } else { setVisibility(singlePaneContainer, View.GONE) setVisibility(singlePaneContainer, View.GONE) Loading
packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +23 −8 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,11 @@ class MediaHost constructor( private var inited: Boolean = false private var inited: Boolean = false /** * Are we listening to media data changes? */ private var listeningToMediaData = false /** /** * Get the current bounds on the screen. This makes sure the state is fresh and up to date * Get the current bounds on the screen. This makes sure the state is fresh and up to date */ */ Loading Loading @@ -97,18 +102,17 @@ class MediaHost constructor( this.location = location this.location = location hostView = mediaHierarchyManager.register(this) hostView = mediaHierarchyManager.register(this) // Listen by default, as the host might not be attached by our clients, until // they get a visibility change. We still want to stay up to date in that case! setListeningToMediaData(true) hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View?) { override fun onViewAttachedToWindow(v: View?) { // we should listen to the combined state change, since otherwise there might setListeningToMediaData(true) // be a delay until the views and the controllers are initialized, leaving us // with either a blank view or the controllers not yet initialized and the // measuring wrong mediaDataManager.addListener(listener) updateViewVisibility() updateViewVisibility() } } override fun onViewDetachedFromWindow(v: View?) { override fun onViewDetachedFromWindow(v: View?) { mediaDataManager.removeListener(listener) setListeningToMediaData(false) } } }) }) Loading @@ -135,8 +139,19 @@ class MediaHost constructor( updateViewVisibility() updateViewVisibility() } } private fun setListeningToMediaData(listen: Boolean) { if (listen != listeningToMediaData) { listeningToMediaData = listen if (listen) { mediaDataManager.addListener(listener) } else { mediaDataManager.removeListener(listener) } } } private fun updateViewVisibility() { private fun updateViewVisibility() { visible = if (showsOnlyActiveMedia) { state.visible = if (showsOnlyActiveMedia) { mediaDataManager.hasActiveMedia() mediaDataManager.hasActiveMedia() } else { } else { mediaDataManager.hasAnyMedia() mediaDataManager.hasAnyMedia() Loading Loading @@ -300,7 +315,7 @@ interface MediaHostState { /** /** * If the view should be VISIBLE or GONE. * If the view should be VISIBLE or GONE. */ */ var visible: Boolean val visible: Boolean /** /** * Does this host need any falsing protection? * Does this host need any falsing protection? Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +1 −2 Original line number Original line Diff line number Diff line Loading @@ -1085,8 +1085,7 @@ public class NotificationPanelViewController extends PanelViewController { } } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { mKeyguardMediaController.attachSplitShadeContainer(container, mKeyguardMediaController.attachSplitShadeContainer(container); () -> mShouldUseSplitNotificationShade); } } private void initBottomArea() { private void initBottomArea() { Loading
packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt +41 −14 Original line number Original line Diff line number Diff line Loading @@ -22,13 +22,16 @@ import android.view.View.VISIBLE import android.widget.FrameLayout import android.widget.FrameLayout import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.FeatureFlags 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 import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.UniqueObjectHostView import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Before import org.junit.Rule import org.junit.Rule import org.junit.Test import org.junit.Test Loading @@ -48,11 +51,16 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Mock @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var featureFlags: FeatureFlags @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager @JvmField @Rule @JvmField @Rule val mockito = MockitoJUnit.rule() val mockito = MockitoJUnit.rule() private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null) private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null) private val hostView = UniqueObjectHostView(context) private lateinit var keyguardMediaController: KeyguardMediaController private lateinit var keyguardMediaController: KeyguardMediaController @Before @Before Loading @@ -62,10 +70,17 @@ class KeyguardMediaControllerTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) .thenReturn(true) .thenReturn(true) whenever(mediaHost.hostView).thenReturn(UniqueObjectHostView(context)) whenever(mediaHost.hostView).thenReturn(hostView) keyguardMediaController = KeyguardMediaController(mediaHost, bypassController, keyguardMediaController = KeyguardMediaController( statusBarStateController, notificationLockscreenUserManager) mediaHost, bypassController, statusBarStateController, notificationLockscreenUserManager, featureFlags, context, configurationController ) keyguardMediaController.attachSinglePaneContainer(mediaHeaderView) keyguardMediaController.attachSinglePaneContainer(mediaHeaderView) } } Loading Loading @@ -105,11 +120,8 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Test @Test fun testActivatesSplitShadeContainerInSplitShadeMode() { fun testActivatesSplitShadeContainerInSplitShadeMode() { val splitShadeContainer = FrameLayout(context) val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer( keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) splitShadeContainer, keyguardMediaController.useSplitShade = true useContainer = { true }) keyguardMediaController.refreshMediaPosition() assertThat(splitShadeContainer.visibility).isEqualTo(VISIBLE) assertThat(splitShadeContainer.visibility).isEqualTo(VISIBLE) } } Loading @@ -117,13 +129,28 @@ class KeyguardMediaControllerTest : SysuiTestCase() { @Test @Test fun testActivatesSinglePaneContainerInSinglePaneMode() { fun testActivatesSinglePaneContainerInSinglePaneMode() { val splitShadeContainer = FrameLayout(context) val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer( keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) splitShadeContainer, useContainer = { false }) keyguardMediaController.refreshMediaPosition() assertThat(splitShadeContainer.visibility).isEqualTo(GONE) assertThat(splitShadeContainer.visibility).isEqualTo(GONE) assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE) assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE) } } @Test fun testAttachedToSplitShade() { val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) keyguardMediaController.useSplitShade = true assertTrue("HostView wasn't attached to the split pane container", splitShadeContainer.childCount == 1) } @Test fun testAttachedToSinglePane() { val splitShadeContainer = FrameLayout(context) keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) assertTrue("HostView wasn't attached to the single pane container", mediaHeaderView.childCount == 1) } } }