Merge "Add tests for the new back affordance." into tm-qpr-dev
This commit is contained in:
@@ -30,6 +30,7 @@ import android.view.MotionEvent
|
|||||||
import android.view.VelocityTracker
|
import android.view.VelocityTracker
|
||||||
import android.view.ViewConfiguration
|
import android.view.ViewConfiguration
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||||
@@ -52,24 +53,24 @@ private const val ENABLE_FAILSAFE = true
|
|||||||
private const val PX_PER_SEC = 1000
|
private const val PX_PER_SEC = 1000
|
||||||
private const val PX_PER_MS = 1
|
private const val PX_PER_MS = 1
|
||||||
|
|
||||||
|
internal const val MIN_DURATION_ACTIVE_ANIMATION = 300L
|
||||||
private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
|
private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
|
||||||
private const val MIN_DURATION_COMMITTED_ANIMATION = 200L
|
private const val MIN_DURATION_COMMITTED_ANIMATION = 200L
|
||||||
private const val MIN_DURATION_ACTIVE_ANIMATION = 300L
|
|
||||||
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
|
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
|
||||||
private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L
|
private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L
|
||||||
|
|
||||||
private const val FAILSAFE_DELAY_MS = 350L
|
private const val FAILSAFE_DELAY_MS = 350L
|
||||||
private const val POP_ON_FLING_DELAY = 160L
|
private const val POP_ON_FLING_DELAY = 160L
|
||||||
|
|
||||||
private val VIBRATE_ACTIVATED_EFFECT =
|
internal val VIBRATE_ACTIVATED_EFFECT =
|
||||||
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
|
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
|
||||||
|
|
||||||
private val VIBRATE_DEACTIVATED_EFFECT =
|
internal val VIBRATE_DEACTIVATED_EFFECT =
|
||||||
VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
|
VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
|
||||||
|
|
||||||
private const val DEBUG = false
|
private const val DEBUG = false
|
||||||
|
|
||||||
class BackPanelController private constructor(
|
class BackPanelController internal constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val windowManager: WindowManager,
|
private val windowManager: WindowManager,
|
||||||
private val viewConfiguration: ViewConfiguration,
|
private val viewConfiguration: ViewConfiguration,
|
||||||
@@ -114,8 +115,10 @@ class BackPanelController private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var params: EdgePanelParams = EdgePanelParams(resources)
|
@VisibleForTesting
|
||||||
private var currentState: GestureState = GestureState.GONE
|
internal var params: EdgePanelParams = EdgePanelParams(resources)
|
||||||
|
@VisibleForTesting
|
||||||
|
internal var currentState: GestureState = GestureState.GONE
|
||||||
private var previousState: GestureState = GestureState.GONE
|
private var previousState: GestureState = GestureState.GONE
|
||||||
|
|
||||||
// Screen attributes
|
// Screen attributes
|
||||||
@@ -159,7 +162,7 @@ class BackPanelController private constructor(
|
|||||||
|
|
||||||
private val failsafeRunnable = Runnable { onFailsafe() }
|
private val failsafeRunnable = Runnable { onFailsafe() }
|
||||||
|
|
||||||
private enum class GestureState {
|
internal enum class GestureState {
|
||||||
/* Arrow is off the screen and invisible */
|
/* Arrow is off the screen and invisible */
|
||||||
GONE,
|
GONE,
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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.navigationbar.gestural
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.MotionEvent.ACTION_DOWN
|
||||||
|
import android.view.MotionEvent.ACTION_MOVE
|
||||||
|
import android.view.MotionEvent.ACTION_UP
|
||||||
|
import android.view.ViewConfiguration
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.util.LatencyTracker
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.plugins.NavigationEdgeBackPlugin
|
||||||
|
import com.android.systemui.statusbar.VibratorHelper
|
||||||
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.clearInvocations
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
class BackPanelControllerTest : SysuiTestCase() {
|
||||||
|
companion object {
|
||||||
|
private const val START_X: Float = 0f
|
||||||
|
}
|
||||||
|
private lateinit var mBackPanelController: BackPanelController
|
||||||
|
private lateinit var testableLooper: TestableLooper
|
||||||
|
private var triggerThreshold: Float = 0.0f
|
||||||
|
private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
|
||||||
|
@Mock private lateinit var vibratorHelper: VibratorHelper
|
||||||
|
@Mock private lateinit var windowManager: WindowManager
|
||||||
|
@Mock private lateinit var configurationController: ConfigurationController
|
||||||
|
@Mock private lateinit var latencyTracker: LatencyTracker
|
||||||
|
@Mock private lateinit var layoutParams: WindowManager.LayoutParams
|
||||||
|
@Mock private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
mBackPanelController =
|
||||||
|
BackPanelController(
|
||||||
|
context,
|
||||||
|
windowManager,
|
||||||
|
ViewConfiguration.get(context),
|
||||||
|
Handler.createAsync(Looper.myLooper()),
|
||||||
|
vibratorHelper,
|
||||||
|
configurationController,
|
||||||
|
latencyTracker
|
||||||
|
)
|
||||||
|
mBackPanelController.setLayoutParams(layoutParams)
|
||||||
|
mBackPanelController.setBackCallback(backCallback)
|
||||||
|
mBackPanelController.setIsLeftPanel(true)
|
||||||
|
testableLooper = TestableLooper.get(this)
|
||||||
|
triggerThreshold = mBackPanelController.params.staticTriggerThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlesActionDown() {
|
||||||
|
startTouch()
|
||||||
|
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.GONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun staysHiddenBeforeSlopCrossed() {
|
||||||
|
startTouch()
|
||||||
|
// Move just enough to not cross the touch slop
|
||||||
|
continueTouch(START_X + touchSlop - 1)
|
||||||
|
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.GONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlesDragSlopCrossed() {
|
||||||
|
startTouch()
|
||||||
|
continueTouch(START_X + touchSlop + 1)
|
||||||
|
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.ENTRY)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlesBackCommitted() {
|
||||||
|
startTouch()
|
||||||
|
// Move once to cross the touch slop
|
||||||
|
continueTouch(START_X + touchSlop.toFloat() + 1)
|
||||||
|
// Move again to cross the back trigger threshold
|
||||||
|
continueTouch(START_X + touchSlop + triggerThreshold + 1)
|
||||||
|
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.ACTIVE)
|
||||||
|
verify(backCallback).setTriggerBack(true)
|
||||||
|
testableLooper.moveTimeForward(100)
|
||||||
|
testableLooper.processAllMessages()
|
||||||
|
verify(vibratorHelper).vibrate(VIBRATE_ACTIVATED_EFFECT)
|
||||||
|
|
||||||
|
finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.FLUNG)
|
||||||
|
verify(backCallback).triggerBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handlesBackCancelled() {
|
||||||
|
startTouch()
|
||||||
|
continueTouch(START_X + touchSlop.toFloat() + 1)
|
||||||
|
continueTouch(
|
||||||
|
START_X + touchSlop + triggerThreshold -
|
||||||
|
mBackPanelController.params.deactivationSwipeTriggerThreshold
|
||||||
|
)
|
||||||
|
clearInvocations(backCallback)
|
||||||
|
Thread.sleep(MIN_DURATION_ACTIVE_ANIMATION)
|
||||||
|
// Move in the opposite direction to cross the deactivation threshold and cancel back
|
||||||
|
continueTouch(START_X)
|
||||||
|
|
||||||
|
assertThat(mBackPanelController.currentState)
|
||||||
|
.isEqualTo(BackPanelController.GestureState.INACTIVE)
|
||||||
|
verify(backCallback).setTriggerBack(false)
|
||||||
|
verify(vibratorHelper).vibrate(VIBRATE_DEACTIVATED_EFFECT)
|
||||||
|
|
||||||
|
finishTouchActionUp(START_X)
|
||||||
|
verify(backCallback).cancelBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startTouch() {
|
||||||
|
mBackPanelController.onMotionEvent(createMotionEvent(ACTION_DOWN, START_X, 0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun continueTouch(x: Float) {
|
||||||
|
mBackPanelController.onMotionEvent(createMotionEvent(ACTION_MOVE, x, 0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun finishTouchActionUp(x: Float) {
|
||||||
|
mBackPanelController.onMotionEvent(createMotionEvent(ACTION_UP, x, 0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
|
||||||
|
return MotionEvent.obtain(0L, 0L, action, x, y, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user