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

Commit b96ccfbc authored by Peter Kalauskas's avatar Peter Kalauskas Committed by Android (Google) Code Review
Browse files

Merge "Add jank tracker to back panel" into main

parents 61432865 73d4add6
Loading
Loading
Loading
Loading
+29 −8
Original line number Diff line number Diff line
@@ -21,19 +21,20 @@ import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.os.Handler
import android.os.SystemClock
import android.util.Log
import android.util.MathUtils
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.core.os.postDelayed
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
@@ -41,6 +42,7 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.abs
@@ -84,6 +86,7 @@ internal constructor(
    private val windowManager: WindowManager,
    private val viewConfiguration: ViewConfiguration,
    @Main private val mainHandler: Handler,
    private val systemClock: SystemClock,
    private val vibratorHelper: VibratorHelper,
    private val configurationController: ConfigurationController,
    private val latencyTracker: LatencyTracker,
@@ -102,6 +105,7 @@ internal constructor(
        private val windowManager: WindowManager,
        private val viewConfiguration: ViewConfiguration,
        @Main private val mainHandler: Handler,
        private val systemClock: SystemClock,
        private val vibratorHelper: VibratorHelper,
        private val configurationController: ConfigurationController,
        private val latencyTracker: LatencyTracker,
@@ -115,6 +119,7 @@ internal constructor(
                    windowManager,
                    viewConfiguration,
                    mainHandler,
                    systemClock,
                    vibratorHelper,
                    configurationController,
                    latencyTracker,
@@ -158,9 +163,9 @@ internal constructor(
    private var gestureInactiveTime = 0L

    private val elapsedTimeSinceInactive
        get() = SystemClock.uptimeMillis() - gestureInactiveTime
        get() = systemClock.uptimeMillis() - gestureInactiveTime
    private val elapsedTimeSinceEntry
        get() = SystemClock.uptimeMillis() - gestureEntryTime
        get() = systemClock.uptimeMillis() - gestureEntryTime

    private var pastThresholdWhileEntryOrInactiveTime = 0L
    private var entryToActiveDelay = 0F
@@ -178,7 +183,7 @@ internal constructor(
    // Distance in pixels a drag can be considered for a fling event
    private var minFlingDistance = 0

    private val failsafeRunnable = Runnable { onFailsafe() }
    internal val failsafeRunnable = Runnable { onFailsafe() }

    internal enum class GestureState {
        /* Arrow is off the screen and invisible */
@@ -370,6 +375,7 @@ internal constructor(
                // Receiving a CANCEL implies that something else intercepted
                // the gesture, i.e., the user did not cancel their gesture.
                // Therefore, disappear immediately, with minimum fanfare.
                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
                updateArrowState(GestureState.GONE)
                velocityTracker = null
            }
@@ -692,10 +698,10 @@ internal constructor(
        }

        if (isPastThresholdForFirstTime) {
            pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
            pastThresholdWhileEntryOrInactiveTime = systemClock.uptimeMillis()
            entryToActiveDelay = dynamicDelay()
        }
        val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
        val timePastThreshold = systemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime

        return timePastThreshold > entryToActiveDelay
    }
@@ -881,6 +887,16 @@ internal constructor(
        previousState = currentState
        currentState = newState

        // First, update the jank tracker
        when (currentState) {
            GestureState.ENTRY -> {
                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
                interactionJankMonitor.begin(mView, Cuj.CUJ_BACK_PANEL_ARROW)
            }
            GestureState.GONE -> interactionJankMonitor.end(Cuj.CUJ_BACK_PANEL_ARROW)
            else -> {}
        }

        when (currentState) {
            GestureState.CANCELLED -> {
                backCallback.cancelBack()
@@ -912,7 +928,7 @@ internal constructor(
                mView.isVisible = true

                updateRestingArrowDimens()
                gestureEntryTime = SystemClock.uptimeMillis()
                gestureEntryTime = systemClock.uptimeMillis()
            }
            GestureState.ACTIVE -> {
                previousXTranslationOnActiveOffset = previousXTranslation
@@ -927,7 +943,7 @@ internal constructor(
                mView.popOffEdge(popVelocity)
            }
            GestureState.INACTIVE -> {
                gestureInactiveTime = SystemClock.uptimeMillis()
                gestureInactiveTime = systemClock.uptimeMillis()

                // Typically entering INACTIVE means
                // totalTouchDelta <= deactivationSwipeTriggerThreshold
@@ -1041,6 +1057,11 @@ internal constructor(
        pw.println("  isLeftPanel=${mView.isLeftPanel}")
    }

    @VisibleForTesting
    internal fun getBackPanelView(): BackPanel {
        return mView
    }

    init {
        if (DEBUG)
            mView.drawDebugInfo = { canvas ->
+37 −8
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.navigationbar.gestural

import android.os.Handler
import android.os.Looper
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
@@ -28,6 +27,7 @@ import android.view.MotionEvent.ACTION_UP
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.jank.interactionJankMonitor
@@ -35,6 +35,7 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -43,6 +44,7 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@@ -55,6 +57,7 @@ class BackPanelControllerTest : SysuiTestCase() {
    }
    private val kosmos = testKosmos()
    private lateinit var mBackPanelController: BackPanelController
    private lateinit var systemClock: FakeSystemClock
    private lateinit var testableLooper: TestableLooper
    private var triggerThreshold: Float = 0.0f
    private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@@ -69,12 +72,15 @@ class BackPanelControllerTest : SysuiTestCase() {
    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        testableLooper = TestableLooper.get(this)
        systemClock = FakeSystemClock()
        mBackPanelController =
            BackPanelController(
                context,
                windowManager,
                ViewConfiguration.get(context),
                Handler.createAsync(checkNotNull(Looper.myLooper())),
                Handler.createAsync(testableLooper.looper),
                systemClock,
                vibratorHelper,
                configurationController,
                latencyTracker,
@@ -83,7 +89,6 @@ class BackPanelControllerTest : SysuiTestCase() {
        mBackPanelController.setLayoutParams(layoutParams)
        mBackPanelController.setBackCallback(backCallback)
        mBackPanelController.setIsLeftPanel(true)
        testableLooper = TestableLooper.get(this)
        triggerThreshold = mBackPanelController.params.staticTriggerThreshold
    }

@@ -103,6 +108,7 @@ class BackPanelControllerTest : SysuiTestCase() {

        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.GONE)
        verify(interactionJankMonitor, never()).begin(any())
    }

    @Test
@@ -110,23 +116,37 @@ class BackPanelControllerTest : SysuiTestCase() {
        startTouch()
        // Move once to cross the touch slop
        continueTouch(START_X + touchSlop.toFloat() + 1)
        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.ENTRY)
        verify(interactionJankMonitor).cancel(Cuj.CUJ_BACK_PANEL_ARROW)
        verify(interactionJankMonitor)
            .begin(mBackPanelController.getBackPanelView(), Cuj.CUJ_BACK_PANEL_ARROW)
        // Move again to cross the back trigger threshold
        continueTouch(START_X + touchSlop + triggerThreshold + 1)
        // Wait threshold duration and hold touch past trigger threshold
        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        continueTouch(START_X + touchSlop + triggerThreshold + 1)

        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.ACTIVE)
        verify(backCallback).setTriggerBack(true)
        testableLooper.moveTimeForward(100)
        testableLooper.processAllMessages()
        moveTimeForward(100)
        verify(vibratorHelper)
            .performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
        finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.COMMITTED)
        verify(backCallback).triggerBack()

        // Because the Handler that is typically used for transitioning the arrow state from
        // COMMITTED to GONE is used as an animation-end-listener on a SpringAnimation,
        // there is no way to meaningfully test that the state becomes GONE and that the tracked
        // jank interaction is ended. So instead, manually trigger the failsafe, which does
        // the same thing:
        mBackPanelController.failsafeRunnable.run()
        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.GONE)
        verify(interactionJankMonitor).end(Cuj.CUJ_BACK_PANEL_ARROW)
    }

    @Test
@@ -134,19 +154,22 @@ class BackPanelControllerTest : SysuiTestCase() {
        startTouch()
        // Move once to cross the touch slop
        continueTouch(START_X + touchSlop.toFloat() + 1)
        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.ENTRY)
        // Move again to cross the back trigger threshold
        continueTouch(
            START_X + touchSlop + triggerThreshold -
                mBackPanelController.params.deactivationTriggerThreshold
        )
        // Wait threshold duration and hold touch before trigger threshold
        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        continueTouch(
            START_X + touchSlop + triggerThreshold -
                mBackPanelController.params.deactivationTriggerThreshold
        )
        clearInvocations(backCallback)
        Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
        moveTimeForward(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)

        // Move in the opposite direction to cross the deactivation threshold and cancel back
        continueTouch(START_X)

@@ -175,4 +198,10 @@ class BackPanelControllerTest : SysuiTestCase() {
    private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
        return MotionEvent.obtain(0L, 0L, action, x, y, 0)
    }

    private fun moveTimeForward(millis: Long) {
        systemClock.advanceTime(millis)
        testableLooper.moveTimeForward(millis)
        testableLooper.processAllMessages()
    }
}