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.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,
|
||||
|
||||
|
||||
@@ -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