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

Commit d2765fa3 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez
Browse files

Supporting more programmatic progress changes in haptics sliders.

The default SliderStateTracker can also handle cases in which progress
changes occur in a slider when the tracker is IDLE. This can happen if
the slider reached a bookend (and resets to IDLE) but the program
continues to supply progress changes. Before, slider haptics would
remain stuck in the IDLE state until the state logic was reset by
terminating the slider interaction.

Test: added Unit tests to SliderStateTrackerTest
Test: manual. Verified haptics continue to work on the volume slider by
 using the volume keys after reaching either bookend.
Bug: 406120925
Flag: EXEMPT bug fix
Change-Id: I3eed6dd37e4e502c280c0a696732a04ca28dfddf
parent 4df49144
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
@@ -32,7 +33,9 @@ import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.eq

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class SliderStateTrackerTest : SysuiTestCase() {
@@ -98,6 +101,54 @@ class SliderStateTrackerTest : SysuiTestCase() {
        assertThat(mSliderStateTracker.isWaiting).isTrue()
    }

    @Test
    fun onProgressChangeByProgram_onIdle_insideSliderRange_movesToArrowMovedOnce() = runTest {
        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))

        // GIVEN a progress change by the program at the middle of the slider
        val progress = 0.5f
        sliderEventProducer.sendEvent(
            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
        )

        // THEN the tracker moves to the ARROW_HANDLE_MOVED_ONCE state and the state listener is
        // called accordingly
        assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
        verify(sliderStateListener).onSelectAndArrow(eq(progress))
    }

    @Test
    fun onProgressChangeByProgram_onIdle_onUpperBookend_executesOnUpperBookendAndResets() =
        runTest {
            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))

            // GIVEN a progress change by the program at the middle of the slider
            val progress = 1f
            sliderEventProducer.sendEvent(
                SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
            )

            // THEN the tracker executes on the upper bookend and resets the state
            assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.IDLE)
            verify(sliderStateListener).onUpperBookend()
        }

    @Test
    fun onProgressChangeByProgram_onIdle_onLowerBookend_executesOnLowerBookendAndResets() =
        runTest {
            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))

            // GIVEN a progress change by the program at the middle of the slider
            val progress = 0f
            sliderEventProducer.sendEvent(
                SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
            )

            // THEN the tracker executes on the upper bookend and resets the state
            assertThat(mSliderStateTracker.currentState).isEqualTo(SliderState.IDLE)
            verify(sliderStateListener).onLowerBookend()
        }

    // Tests on the WAIT state

    @Test
+5 −2
Original line number Diff line number Diff line
@@ -17,12 +17,12 @@
package com.android.systemui.haptics.slider

import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import com.android.app.tracing.coroutines.launchTraced as launch

/**
 * Slider tracker attached to a slider.
@@ -80,7 +80,10 @@ class SliderStateTracker(
            // This will disambiguate between an imprecise touch that acquires the slider handle,
            // and a select and jump operation in the slider track.
            setState(SliderState.WAIT)
        } else if (newEventType == SliderEventType.STARTED_TRACKING_PROGRAM) {
        } else if (
            newEventType == SliderEventType.STARTED_TRACKING_PROGRAM ||
                newEventType == SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
        ) {
            val state =
                if (bookendReached(currentProgress)) SliderState.ARROW_HANDLE_REACHED_BOOKEND
                else SliderState.ARROW_HANDLE_MOVED_ONCE
+1 −1
Original line number Diff line number Diff line
@@ -16,10 +16,10 @@

package com.android.systemui.haptics.slider

import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import com.android.app.tracing.coroutines.launchTraced as launch

/**
 * Tracker component for a slider.