Add tests for the new back affordance.

Bug: b/241117239
Test: atest BackPanelControllerTest
Change-Id: Ic708da7102d11f8f133bcba49c6624c9a132eab3
This commit is contained in:
Shan Huang
2023-02-03 02:12:04 +00:00
parent 1a83f7a642
commit db4876e390
2 changed files with 177 additions and 7 deletions

View File

@@ -30,6 +30,7 @@ import android.view.MotionEvent
import android.view.VelocityTracker
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
@@ -52,24 +53,24 @@ private const val ENABLE_FAILSAFE = true
private const val PX_PER_SEC = 1000
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_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_CONSIDERED_AS_FLING = 100L
private const val FAILSAFE_DELAY_MS = 350L
private const val POP_ON_FLING_DELAY = 160L
private val VIBRATE_ACTIVATED_EFFECT =
internal val VIBRATE_ACTIVATED_EFFECT =
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
private val VIBRATE_DEACTIVATED_EFFECT =
internal val VIBRATE_DEACTIVATED_EFFECT =
VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
private const val DEBUG = false
class BackPanelController private constructor(
class BackPanelController internal constructor(
context: Context,
private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@@ -114,8 +115,10 @@ class BackPanelController private constructor(
}
}
private var params: EdgePanelParams = EdgePanelParams(resources)
private var currentState: GestureState = GestureState.GONE
@VisibleForTesting
internal var params: EdgePanelParams = EdgePanelParams(resources)
@VisibleForTesting
internal var currentState: GestureState = GestureState.GONE
private var previousState: GestureState = GestureState.GONE
// Screen attributes
@@ -159,7 +162,7 @@ class BackPanelController private constructor(
private val failsafeRunnable = Runnable { onFailsafe() }
private enum class GestureState {
internal enum class GestureState {
/* Arrow is off the screen and invisible */
GONE,

View File

@@ -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)
}
}