Loading packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt +16 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyFloat import org.mockito.Mock import org.mockito.Mockito.anyInt Loading Loading @@ -61,6 +62,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { @Mock lateinit var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit @Mock lateinit var closeGuts: (immediate: Boolean) -> Unit @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var onCarouselVisibleToUser: () -> Unit @Mock lateinit var logger: MediaUiEventLogger @Mock lateinit var contentContainer: ViewGroup @Mock lateinit var settingsButton: View Loading Loading @@ -90,6 +92,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { seekBarUpdateListener, closeGuts, falsingManager, onCarouselVisibleToUser, logger, ) mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth Loading Loading @@ -248,6 +251,19 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { verify(mediaCarousel, never()).animationTargetX = anyFloat() } @Test fun testCarouselScrollToNewIndex_onCarouselVisibleToUser() { setupMediaContainer(visibleIndex = 0) whenever(mediaCarousel.relativeScrollX).thenReturn(carouselWidth) mediaCarouselScrollHandler.visibleToUser = true val captor = ArgumentCaptor.forClass(View.OnScrollChangeListener::class.java) verify(mediaCarousel).setOnScrollChangeListener(captor.capture()) captor.value.onScrollChange(null, 0, 0, 0, 0) verify(onCarouselVisibleToUser).invoke() } private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) { whenever(contentContainer.childCount).thenReturn(2) val child1: View = mock() Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +11 −6 Original line number Diff line number Diff line Loading @@ -204,6 +204,9 @@ constructor( private var started = false private var playbackType = PLAYBACK_TYPE_UNKNOWN private var playbackVolumeControlId: String? = null private val requestSuggestionRunnable = Runnable { bgExecutor.execute { localMediaManager.requestDeviceSuggestion() } } private var current: MediaDeviceData? = null set(value) { val sameWithoutIcon = value != null && value.equalsWithoutIcon(field) Loading @@ -213,9 +216,11 @@ constructor( } } private var suggestionData: SuggestionData? = null private var suggestionData = SuggestionData(onSuggestionSpaceVisible = requestSuggestionRunnable) set(value) { if (field != value) { val sameWithoutConnect = value.equalsWithoutConnect(field) if (!sameWithoutConnect) { field = value fgExecutor.execute { processSuggestionData(key, oldKey, value) } } Loading @@ -238,6 +243,9 @@ constructor( if (!started) { // Fetch in case a suggestion already exists before registering for suggestions localMediaManager.registerCallback(this) if (enableSuggestedDeviceUi()) { onSuggestedDeviceUpdated(localMediaManager.getSuggestedDevice()) } if (!Flags.removeUnnecessaryRouteScanning()) { localMediaManager.startScan() } Loading Loading @@ -320,10 +328,7 @@ constructor( connect = { localMediaManager.connectSuggestedDevice(it) }, ) }, onSuggestionSpaceVisible = Runnable { bgExecutor.execute { localMediaManager.requestDeviceSuggestion() } }, onSuggestionSpaceVisible = requestSuggestionRunnable, ) } } Loading packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt +32 −3 Original line number Diff line number Diff line Loading @@ -221,17 +221,46 @@ constructor( /** Action to invoke to transfer media playback to this device. */ val connect: () -> Unit, ) ) { fun equalsWithoutConnect(other: SuggestedMediaDeviceData?): Boolean { if (other == null) { return false } return name == other.name && connectionState == other.connectionState && icon == other.icon } } /** Wrapper for data needed to support suggestions in the media player. */ data class SuggestionData constructor( /** The suggested device for playback. Null if no suggestion exists. */ val suggestedMediaDeviceData: SuggestedMediaDeviceData?, val suggestedMediaDeviceData: SuggestedMediaDeviceData? = null, /** * Callback to be invoked when the area to surface the suggestion becomes visible. Suggestion * providers are notified of the visibility update and can provide suggestions. */ val onSuggestionSpaceVisible: Runnable, ) ) { /** * Check whether [SuggestionData] objects are equal in all fields except the underlying connect * method, which can't be easily compared for equality, and is based on the underlying * suggestion anyway. */ fun equalsWithoutConnect(other: SuggestionData?): Boolean { if (other == null) { return false } if (onSuggestionSpaceVisible != other.onSuggestionSpaceVisible) { return false } if (suggestedMediaDeviceData == null) { return other.suggestedMediaDeviceData == null } return suggestedMediaDeviceData.equalsWithoutConnect(other.suggestedMediaDeviceData) } } packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +13 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Dumpable import com.android.systemui.Flags import com.android.systemui.Flags.enableSuggestedDeviceUi import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background Loading Loading @@ -352,6 +353,7 @@ constructor( this::updateSeekbarListening, this::closeGuts, falsingManager, this::onCarouselVisibleToUser, logger, ) carouselLocale = context.resources.configuration.locales.get(0) Loading Loading @@ -1214,6 +1216,17 @@ constructor( } } fun onCarouselVisibleToUser() { if (!enableSuggestedDeviceUi() || !mediaCarouselScrollHandler.visibleToUser) { return } val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex if (MediaPlayerData.players().size > visibleMediaIndex) { val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex) mediaControlPanel?.onSuggestionSpaceVisible() } } @VisibleForTesting fun onSwipeToDismiss() { if (SceneContainerFlag.isEnabled) { Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +15 −0 Original line number Diff line number Diff line Loading @@ -239,6 +239,7 @@ public class MediaControlPanel { private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; private boolean mWasPlaying = false; private boolean mButtonClicked = false; @Nullable private Runnable mOnSuggestionSpaceVisibleRunnable = null; private final PaintDrawCallback mNoiseDrawCallback = new PaintDrawCallback() { Loading Loading @@ -617,6 +618,19 @@ public class MediaControlPanel { Trace.endSection(); } /** * Should be called when the space that holds device suggestions becomes visible to the user. */ public void onSuggestionSpaceVisible() { if (!Flags.enableSuggestedDeviceUi()) { return; } @Nullable Runnable onSuggestionVisibleRunnable = mOnSuggestionSpaceVisibleRunnable; if (onSuggestionVisibleRunnable != null) { onSuggestionVisibleRunnable.run(); } } private void bindDeviceSuggestion(@NonNull MediaData data) { if (!Flags.enableSuggestedDeviceUi()) { return; Loading @@ -625,6 +639,7 @@ public class MediaControlPanel { TextView deviceText = mMediaViewHolder.getSeamlessText(); @Nullable SuggestionData suggestionData = data.getSuggestionData(); if (suggestionData != null) { mOnSuggestionSpaceVisibleRunnable = suggestionData.getOnSuggestionSpaceVisible(); @Nullable SuggestedMediaDeviceData suggestionDeviceData = suggestionData.getSuggestedMediaDeviceData(); Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt +16 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyFloat import org.mockito.Mock import org.mockito.Mockito.anyInt Loading Loading @@ -61,6 +62,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { @Mock lateinit var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit @Mock lateinit var closeGuts: (immediate: Boolean) -> Unit @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var onCarouselVisibleToUser: () -> Unit @Mock lateinit var logger: MediaUiEventLogger @Mock lateinit var contentContainer: ViewGroup @Mock lateinit var settingsButton: View Loading Loading @@ -90,6 +92,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { seekBarUpdateListener, closeGuts, falsingManager, onCarouselVisibleToUser, logger, ) mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth Loading Loading @@ -248,6 +251,19 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { verify(mediaCarousel, never()).animationTargetX = anyFloat() } @Test fun testCarouselScrollToNewIndex_onCarouselVisibleToUser() { setupMediaContainer(visibleIndex = 0) whenever(mediaCarousel.relativeScrollX).thenReturn(carouselWidth) mediaCarouselScrollHandler.visibleToUser = true val captor = ArgumentCaptor.forClass(View.OnScrollChangeListener::class.java) verify(mediaCarousel).setOnScrollChangeListener(captor.capture()) captor.value.onScrollChange(null, 0, 0, 0, 0) verify(onCarouselVisibleToUser).invoke() } private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) { whenever(contentContainer.childCount).thenReturn(2) val child1: View = mock() Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +11 −6 Original line number Diff line number Diff line Loading @@ -204,6 +204,9 @@ constructor( private var started = false private var playbackType = PLAYBACK_TYPE_UNKNOWN private var playbackVolumeControlId: String? = null private val requestSuggestionRunnable = Runnable { bgExecutor.execute { localMediaManager.requestDeviceSuggestion() } } private var current: MediaDeviceData? = null set(value) { val sameWithoutIcon = value != null && value.equalsWithoutIcon(field) Loading @@ -213,9 +216,11 @@ constructor( } } private var suggestionData: SuggestionData? = null private var suggestionData = SuggestionData(onSuggestionSpaceVisible = requestSuggestionRunnable) set(value) { if (field != value) { val sameWithoutConnect = value.equalsWithoutConnect(field) if (!sameWithoutConnect) { field = value fgExecutor.execute { processSuggestionData(key, oldKey, value) } } Loading @@ -238,6 +243,9 @@ constructor( if (!started) { // Fetch in case a suggestion already exists before registering for suggestions localMediaManager.registerCallback(this) if (enableSuggestedDeviceUi()) { onSuggestedDeviceUpdated(localMediaManager.getSuggestedDevice()) } if (!Flags.removeUnnecessaryRouteScanning()) { localMediaManager.startScan() } Loading Loading @@ -320,10 +328,7 @@ constructor( connect = { localMediaManager.connectSuggestedDevice(it) }, ) }, onSuggestionSpaceVisible = Runnable { bgExecutor.execute { localMediaManager.requestDeviceSuggestion() } }, onSuggestionSpaceVisible = requestSuggestionRunnable, ) } } Loading
packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt +32 −3 Original line number Diff line number Diff line Loading @@ -221,17 +221,46 @@ constructor( /** Action to invoke to transfer media playback to this device. */ val connect: () -> Unit, ) ) { fun equalsWithoutConnect(other: SuggestedMediaDeviceData?): Boolean { if (other == null) { return false } return name == other.name && connectionState == other.connectionState && icon == other.icon } } /** Wrapper for data needed to support suggestions in the media player. */ data class SuggestionData constructor( /** The suggested device for playback. Null if no suggestion exists. */ val suggestedMediaDeviceData: SuggestedMediaDeviceData?, val suggestedMediaDeviceData: SuggestedMediaDeviceData? = null, /** * Callback to be invoked when the area to surface the suggestion becomes visible. Suggestion * providers are notified of the visibility update and can provide suggestions. */ val onSuggestionSpaceVisible: Runnable, ) ) { /** * Check whether [SuggestionData] objects are equal in all fields except the underlying connect * method, which can't be easily compared for equality, and is based on the underlying * suggestion anyway. */ fun equalsWithoutConnect(other: SuggestionData?): Boolean { if (other == null) { return false } if (onSuggestionSpaceVisible != other.onSuggestionSpaceVisible) { return false } if (suggestedMediaDeviceData == null) { return other.suggestedMediaDeviceData == null } return suggestedMediaDeviceData.equalsWithoutConnect(other.suggestedMediaDeviceData) } }
packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +13 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Dumpable import com.android.systemui.Flags import com.android.systemui.Flags.enableSuggestedDeviceUi import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background Loading Loading @@ -352,6 +353,7 @@ constructor( this::updateSeekbarListening, this::closeGuts, falsingManager, this::onCarouselVisibleToUser, logger, ) carouselLocale = context.resources.configuration.locales.get(0) Loading Loading @@ -1214,6 +1216,17 @@ constructor( } } fun onCarouselVisibleToUser() { if (!enableSuggestedDeviceUi() || !mediaCarouselScrollHandler.visibleToUser) { return } val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex if (MediaPlayerData.players().size > visibleMediaIndex) { val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex) mediaControlPanel?.onSuggestionSpaceVisible() } } @VisibleForTesting fun onSwipeToDismiss() { if (SceneContainerFlag.isEnabled) { Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +15 −0 Original line number Diff line number Diff line Loading @@ -239,6 +239,7 @@ public class MediaControlPanel { private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; private boolean mWasPlaying = false; private boolean mButtonClicked = false; @Nullable private Runnable mOnSuggestionSpaceVisibleRunnable = null; private final PaintDrawCallback mNoiseDrawCallback = new PaintDrawCallback() { Loading Loading @@ -617,6 +618,19 @@ public class MediaControlPanel { Trace.endSection(); } /** * Should be called when the space that holds device suggestions becomes visible to the user. */ public void onSuggestionSpaceVisible() { if (!Flags.enableSuggestedDeviceUi()) { return; } @Nullable Runnable onSuggestionVisibleRunnable = mOnSuggestionSpaceVisibleRunnable; if (onSuggestionVisibleRunnable != null) { onSuggestionVisibleRunnable.run(); } } private void bindDeviceSuggestion(@NonNull MediaData data) { if (!Flags.enableSuggestedDeviceUi()) { return; Loading @@ -625,6 +639,7 @@ public class MediaControlPanel { TextView deviceText = mMediaViewHolder.getSeamlessText(); @Nullable SuggestionData suggestionData = data.getSuggestionData(); if (suggestionData != null) { mOnSuggestionSpaceVisibleRunnable = suggestionData.getOnSuggestionSpaceVisible(); @Nullable SuggestedMediaDeviceData suggestionDeviceData = suggestionData.getSuggestedMediaDeviceData(); Loading