diff options
2 files changed, 140 insertions, 9 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt index ce50a11cd85d..04f05ec0b643 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt @@ -26,6 +26,7 @@ import android.view.ViewOutlineProvider import androidx.core.view.GestureDetectorCompat import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringForce +import com.android.internal.annotations.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.Gefingerpoken import com.android.systemui.R @@ -148,7 +149,8 @@ class MediaCarouselScrollHandler( } /** The touch listener for the scroll view */ - private val touchListener = + @VisibleForTesting + val touchListener = object : Gefingerpoken { override fun onTouchEvent(motionEvent: MotionEvent?) = onTouch(motionEvent!!) override fun onInterceptTouchEvent(ev: MotionEvent?) = onInterceptTouch(ev!!) @@ -284,15 +286,14 @@ class MediaCarouselScrollHandler( } else if (isUp || motionEvent.action == MotionEvent.ACTION_CANCEL) { // It's an up and the fling didn't take it above val relativePos = scrollView.relativeScrollX % playerWidthPlusPadding - val scrollXAmount: Int - if (relativePos > playerWidthPlusPadding / 2) { - scrollXAmount = playerWidthPlusPadding - relativePos - } else { - scrollXAmount = -1 * relativePos - } + val scrollXAmount: Int = + if (isRtl xor (relativePos > playerWidthPlusPadding / 2)) { + playerWidthPlusPadding - relativePos + } else { + -1 * relativePos + } if (scrollXAmount != 0) { - val dx = if (isRtl) -scrollXAmount else scrollXAmount - val newScrollX = scrollView.relativeScrollX + dx + val newScrollX = scrollView.relativeScrollX + scrollXAmount // Delay the scrolling since scrollView calls springback which cancels // the animation again.. mainExecutor.execute { scrollView.smoothScrollTo(newScrollX, scrollView.scrollY) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt new file mode 100644 index 000000000000..6fafb086ca9d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.controls.ui + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.MotionEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +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.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidTestingRunner::class) +class MediaCarouselScrollHandlerTest : SysuiTestCase() { + + private val carouselWidth = 1038 + private val motionEventUp = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0f, 0f, 0) + + @Mock lateinit var mediaCarousel: MediaScrollView + @Mock lateinit var pageIndicator: PageIndicator + @Mock lateinit var dismissCallback: () -> Unit + @Mock lateinit var translationChangedListener: () -> Unit + @Mock lateinit var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit + @Mock lateinit var closeGuts: (immediate: Boolean) -> Unit + @Mock lateinit var falsingCollector: FalsingCollector + @Mock lateinit var falsingManager: FalsingManager + @Mock lateinit var logSmartspaceImpression: (Boolean) -> Unit + @Mock lateinit var logger: MediaUiEventLogger + + lateinit var executor: FakeExecutor + private val clock = FakeSystemClock() + + private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + executor = FakeExecutor(clock) + mediaCarouselScrollHandler = + MediaCarouselScrollHandler( + mediaCarousel, + pageIndicator, + executor, + dismissCallback, + translationChangedListener, + seekBarUpdateListener, + closeGuts, + falsingCollector, + falsingManager, + logSmartspaceImpression, + logger + ) + mediaCarouselScrollHandler.playerWidthPlusPadding = carouselWidth + + whenever(mediaCarousel.touchListener).thenReturn(mediaCarouselScrollHandler.touchListener) + } + + @Test + fun testCarouselScroll_shortScroll() { + whenever(mediaCarousel.isLayoutRtl).thenReturn(false) + whenever(mediaCarousel.relativeScrollX).thenReturn(300) + + mediaCarousel.touchListener?.onTouchEvent(motionEventUp) + executor.runAllReady() + + verify(mediaCarousel).smoothScrollTo(eq(0), anyInt()) + } + + @Test + fun testCarouselScroll_shortScroll_isRTL() { + whenever(mediaCarousel.isLayoutRtl).thenReturn(true) + whenever(mediaCarousel.relativeScrollX).thenReturn(300) + + mediaCarousel.touchListener?.onTouchEvent(motionEventUp) + executor.runAllReady() + + verify(mediaCarousel).smoothScrollTo(eq(carouselWidth), anyInt()) + } + + @Test + fun testCarouselScroll_longScroll() { + whenever(mediaCarousel.isLayoutRtl).thenReturn(false) + whenever(mediaCarousel.relativeScrollX).thenReturn(600) + + mediaCarousel.touchListener?.onTouchEvent(motionEventUp) + executor.runAllReady() + + verify(mediaCarousel).smoothScrollTo(eq(carouselWidth), anyInt()) + } + + @Test + fun testCarouselScroll_longScroll_isRTL() { + whenever(mediaCarousel.isLayoutRtl).thenReturn(true) + whenever(mediaCarousel.relativeScrollX).thenReturn(600) + + mediaCarousel.touchListener?.onTouchEvent(motionEventUp) + executor.runAllReady() + + verify(mediaCarousel).smoothScrollTo(eq(0), anyInt()) + } +} |