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

Commit bdd4fbb3 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Fix carousel scroll in RTL

In RTL, the scroll amount left for a player when the percentage of the
relative scroll on x-axis is higher that 50% should match with the value
of scroll amount left when percentage of relative scroll on x-axis is
less than or equal 50% in LTR. Xoring the RTL boolean with the relative
scroll condition fixes the issue.

Fixes: 287168003
Fixes: 289174065
Test: Checked UMO in QS and LS, videos attached in bug link.
Test: atest MediaCarouselScrollHandlerTest
Test: atest PlatformScenarioTests:android.platform.test.scenario.sysui.media.QSMediaController
Change-Id: Iab3175cfd344c189d97cf598fd57dadd58d19184
parent 6dd0dcc0
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -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
@@ -146,7 +147,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!!)
@@ -279,15 +281,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
            val scrollXAmount: Int =
                if (isRtl xor (relativePos > playerWidthPlusPadding / 2)) {
                    playerWidthPlusPadding - relativePos
                } else {
                scrollXAmount = -1 * relativePos
                    -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) }
+127 −0
Original line number Diff line number Diff line
/*
 * 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.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 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,
                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())
    }
}