Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +32 −9 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -1716,6 +1738,7 @@ private fun Umo( UmoLegacy(viewModel, modifier) } } } @Composable private fun UmoLegacy(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +18 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() Loading Loading @@ -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() Loading Loading @@ -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() = Loading packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt +117 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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() Loading @@ -63,6 +73,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(clock) whenever(mediaCarousel.contentContainer).thenReturn(contentContainer) mediaCarouselScrollHandler = MediaCarouselScrollHandler( mediaCarousel, Loading @@ -74,10 +85,9 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { closeGuts, falsingManager, logSmartspaceImpression, logger logger, ) mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth whenever(mediaCarousel.touchListener).thenReturn(mediaCarouselScrollHandler.touchListener) } Loading Loading @@ -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 } } packages/SystemUI/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -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] --> Loading packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +32 −9 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -1716,6 +1738,7 @@ private fun Umo( UmoLegacy(viewModel, modifier) } } } @Composable private fun UmoLegacy(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +18 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() Loading Loading @@ -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() Loading Loading @@ -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() = Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt +117 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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() Loading @@ -63,6 +73,7 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(clock) whenever(mediaCarousel.contentContainer).thenReturn(contentContainer) mediaCarouselScrollHandler = MediaCarouselScrollHandler( mediaCarousel, Loading @@ -74,10 +85,9 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() { closeGuts, falsingManager, logSmartspaceImpression, logger logger, ) mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth whenever(mediaCarousel.touchListener).thenReturn(mediaCarouselScrollHandler.touchListener) } Loading Loading @@ -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 } }
packages/SystemUI/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -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] --> Loading
packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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