Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09baf7f2 authored by Derek Jedral's avatar Derek Jedral
Browse files

Notify routers when the suggestion space is visible

Part of un-reverting ag/32935663. Notify the router when the suggestion
space becomes visible, to allow suggestion providers to provide
suggestions.

Test: atest
Bug: 409098190
Flag: com.android.systemui.enable_suggested_device_ui
Change-Id: I9f001b3d4220a02df257ef383f382e46167d3d92
parent 899202ec
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -90,6 +92,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {
                seekBarUpdateListener,
                closeGuts,
                falsingManager,
                onCarouselVisibleToUser,
                logger,
            )
        mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth
@@ -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()
+11 −6
Original line number Diff line number Diff line
@@ -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)
@@ -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) }
                }
@@ -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()
                    }
@@ -320,10 +328,7 @@ constructor(
                                    connect = { localMediaManager.connectSuggestedDevice(it) },
                                )
                            },
                        onSuggestionSpaceVisible =
                            Runnable {
                                bgExecutor.execute { localMediaManager.requestDeviceSuggestion() }
                            },
                        onSuggestionSpaceVisible = requestSuggestionRunnable,
                    )
            }
        }
+32 −3
Original line number Diff line number Diff line
@@ -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)
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -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
@@ -352,6 +353,7 @@ constructor(
                this::updateSeekbarListening,
                this::closeGuts,
                falsingManager,
                this::onCarouselVisibleToUser,
                logger,
            )
        carouselLocale = context.resources.configuration.locales.get(0)
@@ -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) {
+15 −0
Original line number Diff line number Diff line
@@ -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() {
@@ -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;
@@ -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