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

Commit 3ac88b40 authored by Coco Duan's avatar Coco Duan Committed by Android (Google) Code Review
Browse files

Merge "Add custom actions to improve Voice Access swipe on UMO in hub" into main

parents 1a3eebfd 664054d9
Loading
Loading
Loading
Loading
+32 −9
Original line number Diff line number Diff line
@@ -1704,6 +1704,28 @@ private fun Umo(
    viewModel: BaseCommunalViewModel,
    contentScope: ContentScope?,
    modifier: Modifier = Modifier,
) {
    val showNextActionLabel = stringResource(R.string.accessibility_action_label_umo_show_next)
    val showPreviousActionLabel =
        stringResource(R.string.accessibility_action_label_umo_show_previous)

    Box(
        modifier =
            modifier.thenIf(!viewModel.isEditMode) {
                Modifier.semantics {
                    customActions =
                        listOf(
                            CustomAccessibilityAction(showNextActionLabel) {
                                viewModel.onShowNextMedia()
                                true
                            },
                            CustomAccessibilityAction(showPreviousActionLabel) {
                                viewModel.onShowPreviousMedia()
                                true
                            },
                        )
                }
            }
    ) {
        if (SceneContainerFlag.isEnabled && contentScope != null) {
            contentScope.MediaCarousel(
@@ -1716,6 +1738,7 @@ private fun Umo(
            UmoLegacy(viewModel, modifier)
        }
    }
}

@Composable
private fun UmoLegacy(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) {
+18 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.controller.mediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -120,6 +121,7 @@ import platform.test.runner.parameterized.Parameters
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
    @Mock private lateinit var mediaHost: MediaHost
    @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
    @Mock private lateinit var metricsLogger: CommunalMetricsLogger

    private val kosmos = testKosmos()
@@ -161,6 +163,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

        kosmos.fakeUserTracker.set(userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0)
        whenever(mediaHost.visible).thenReturn(true)
        whenever(kosmos.mediaCarouselController.mediaCarouselScrollHandler)
            .thenReturn(mediaCarouselScrollHandler)

        kosmos.powerInteractor.setAwakeForTest()

@@ -902,6 +906,20 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
            assertThat(selectedKey2).isEqualTo(key)
        }

    @Test
    fun onShowPreviousMedia_scrollHandler_isCalled() =
        testScope.runTest {
            underTest.onShowPreviousMedia()
            verify(mediaCarouselScrollHandler).scrollByStep(-1)
        }

    @Test
    fun onShowNextMedia_scrollHandler_isCalled() =
        testScope.runTest {
            underTest.onShowNextMedia()
            verify(mediaCarouselScrollHandler).scrollByStep(1)
        }

    @Test
    @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
    fun uiIsBlurred_whenPrimaryBouncerIsShowing() =
+117 −4
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.systemui.media.controls.ui.view

import android.content.res.Resources
import android.testing.TestableLooper
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -25,16 +28,19 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -42,6 +48,7 @@ import org.mockito.MockitoAnnotations
class MediaCarouselScrollHandlerTest : SysuiTestCase() {

    private val carouselWidth = 1038
    private val settingsButtonWidth = 200
    private val motionEventUp = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0f, 0f, 0)

    @Mock lateinit var mediaCarousel: MediaScrollView
@@ -53,6 +60,9 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {
    @Mock lateinit var falsingManager: FalsingManager
    @Mock lateinit var logSmartspaceImpression: (Boolean) -> Unit
    @Mock lateinit var logger: MediaUiEventLogger
    @Mock lateinit var contentContainer: ViewGroup
    @Mock lateinit var settingsButton: View
    @Mock lateinit var resources: Resources

    lateinit var executor: FakeExecutor
    private val clock = FakeSystemClock()
@@ -63,6 +73,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {
    fun setup() {
        MockitoAnnotations.initMocks(this)
        executor = FakeExecutor(clock)
        whenever(mediaCarousel.contentContainer).thenReturn(contentContainer)
        mediaCarouselScrollHandler =
            MediaCarouselScrollHandler(
                mediaCarousel,
@@ -74,10 +85,9 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {
                closeGuts,
                falsingManager,
                logSmartspaceImpression,
                logger
                logger,
            )
        mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth

        whenever(mediaCarousel.touchListener).thenReturn(mediaCarouselScrollHandler.touchListener)
    }

@@ -128,4 +138,107 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {

        verify(mediaCarousel).smoothScrollTo(eq(0), anyInt())
    }

    @Test
    fun testCarouselScrollByStep_scrollRight() {
        setupMediaContainer(visibleIndex = 0)

        mediaCarouselScrollHandler.scrollByStep(1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel).smoothScrollTo(eq(carouselWidth), anyInt())
    }

    @Test
    fun testCarouselScrollByStep_scrollLeft() {
        setupMediaContainer(visibleIndex = 1)

        mediaCarouselScrollHandler.scrollByStep(-1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel).smoothScrollTo(eq(0), anyInt())
    }

    @Test
    fun testCarouselScrollByStep_scrollRight_alreadyAtEnd() {
        setupMediaContainer(visibleIndex = 1)

        mediaCarouselScrollHandler.scrollByStep(1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
        verify(mediaCarousel).animationTargetX = eq(-settingsButtonWidth.toFloat())
    }

    @Test
    fun testCarouselScrollByStep_scrollLeft_alreadyAtStart() {
        setupMediaContainer(visibleIndex = 0)

        mediaCarouselScrollHandler.scrollByStep(-1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
        verify(mediaCarousel).animationTargetX = eq(settingsButtonWidth.toFloat())
    }

    @Test
    fun testCarouselScrollByStep_scrollLeft_alreadyAtStart_isRTL() {
        setupMediaContainer(visibleIndex = 0)
        whenever(mediaCarousel.isLayoutRtl).thenReturn(true)

        mediaCarouselScrollHandler.scrollByStep(-1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
        verify(mediaCarousel).animationTargetX = eq(-settingsButtonWidth.toFloat())
    }

    @Test
    fun testCarouselScrollByStep_scrollRight_alreadyAtEnd_isRTL() {
        setupMediaContainer(visibleIndex = 1)
        whenever(mediaCarousel.isLayoutRtl).thenReturn(true)

        mediaCarouselScrollHandler.scrollByStep(1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
        verify(mediaCarousel).animationTargetX = eq(settingsButtonWidth.toFloat())
    }

    @Test
    fun testScrollByStep_noScroll_notDismissible() {
        setupMediaContainer(visibleIndex = 1, showsSettingsButton = false)

        mediaCarouselScrollHandler.scrollByStep(1)
        clock.advanceTime(DISMISS_DELAY)
        executor.runAllReady()

        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
        verify(mediaCarousel, never()).animationTargetX = anyFloat()
    }

    private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) {
        whenever(contentContainer.childCount).thenReturn(2)
        val child1: View = mock()
        val child2: View = mock()
        whenever(child1.left).thenReturn(0)
        whenever(child2.left).thenReturn(carouselWidth)
        whenever(contentContainer.getChildAt(0)).thenReturn(child1)
        whenever(contentContainer.getChildAt(1)).thenReturn(child2)

        whenever(settingsButton.width).thenReturn(settingsButtonWidth)
        whenever(settingsButton.context).thenReturn(context)
        whenever(settingsButton.resources).thenReturn(resources)
        whenever(settingsButton.resources.getDimensionPixelSize(anyInt())).thenReturn(20)
        mediaCarouselScrollHandler.onSettingsButtonUpdated(settingsButton)

        mediaCarouselScrollHandler.visibleMediaIndex = visibleIndex
        mediaCarouselScrollHandler.showsSettingsButton = showsSettingsButton
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -1351,6 +1351,10 @@
    <string name="accessibility_action_label_shrink_widget">Decrease height</string>
    <!-- Label for accessibility action to expand a widget in edit mode. [CHAR LIMIT=NONE] -->
    <string name="accessibility_action_label_expand_widget">Increase height</string>
    <!-- Label for accessibility action to show the next media player. [CHAR LIMIT=NONE] -->
    <string name="accessibility_action_label_umo_show_next">Show next</string>
    <!-- Label for accessibility action to show the previous media player. [CHAR LIMIT=NONE] -->
    <string name="accessibility_action_label_umo_show_previous">Show previous</string>
    <!-- Title shown above information regarding lock screen widgets. [CHAR LIMIT=50] -->
    <string name="communal_widgets_disclaimer_title">Lock screen widgets</string>
    <!-- Information about lock screen widgets presented to the user. [CHAR LIMIT=NONE] -->
+6 −0
Original line number Diff line number Diff line
@@ -202,6 +202,12 @@ abstract class BaseCommunalViewModel(
    /** Called as the user request to show the customize widget button. */
    open fun onLongClick() {}

    /** Called as the user requests to switch to the previous player in UMO. */
    open fun onShowPreviousMedia() {}

    /** Called as the user requests to switch to the next player in UMO. */
    open fun onShowNextMedia() {}

    /** Called as the UI determines that a new widget has been added to the grid. */
    open fun onNewWidgetAdded(provider: AppWidgetProviderInfo) {}

Loading