Merge "[Unfold transition] Do not play haptics when hitting a timeout on folding" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
76c74ee9f2
@@ -3,26 +3,43 @@ package com.android.systemui.unfold
|
|||||||
import android.os.SystemProperties
|
import android.os.SystemProperties
|
||||||
import android.os.VibrationEffect
|
import android.os.VibrationEffect
|
||||||
import android.os.Vibrator
|
import android.os.Vibrator
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
|
import com.android.systemui.unfold.updates.FoldProvider
|
||||||
|
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
|
||||||
|
import java.util.concurrent.Executor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/** Class that plays a haptics effect during unfolding a foldable device */
|
||||||
* Class that plays a haptics effect during unfolding a foldable device
|
|
||||||
*/
|
|
||||||
@SysUIUnfoldScope
|
@SysUIUnfoldScope
|
||||||
class UnfoldHapticsPlayer
|
class UnfoldHapticsPlayer
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
|
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
|
||||||
|
foldProvider: FoldProvider,
|
||||||
|
@Main private val mainExecutor: Executor,
|
||||||
private val vibrator: Vibrator?
|
private val vibrator: Vibrator?
|
||||||
) : TransitionProgressListener {
|
) : TransitionProgressListener {
|
||||||
|
|
||||||
|
private var isFirstAnimationAfterUnfold = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (vibrator != null) {
|
if (vibrator != null) {
|
||||||
// We don't need to remove the callback because we should listen to it
|
// We don't need to remove the callback because we should listen to it
|
||||||
// the whole time when SystemUI process is alive
|
// the whole time when SystemUI process is alive
|
||||||
unfoldTransitionProgressProvider.addCallback(this)
|
unfoldTransitionProgressProvider.addCallback(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foldProvider.registerCallback(
|
||||||
|
object : FoldCallback {
|
||||||
|
override fun onFoldUpdated(isFolded: Boolean) {
|
||||||
|
if (isFolded) {
|
||||||
|
isFirstAnimationAfterUnfold = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mainExecutor
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
|
private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
|
||||||
@@ -36,6 +53,13 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTransitionFinishing() {
|
override fun onTransitionFinishing() {
|
||||||
|
// Run haptics only when unfolding the device (first animation after unfolding)
|
||||||
|
if (!isFirstAnimationAfterUnfold) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirstAnimationAfterUnfold = false
|
||||||
|
|
||||||
// Run haptics only if the animation is long enough to notice
|
// Run haptics only if the animation is long enough to notice
|
||||||
if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) {
|
if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) {
|
||||||
playHaptics()
|
playHaptics()
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, Transitio
|
|||||||
listeners.forEach { it.onTransitionFinished() }
|
listeners.forEach { it.onTransitionFinished() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTransitionFinishing() {
|
||||||
|
listeners.forEach { it.onTransitionFinishing() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTransitionProgress(progress: Float) {
|
override fun onTransitionProgress(progress: Float) {
|
||||||
listeners.forEach { it.onTransitionProgress(progress) }
|
listeners.forEach { it.onTransitionProgress(progress) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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.unfold
|
||||||
|
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.unfold.updates.FoldProvider
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.clearInvocations
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@SmallTest
|
||||||
|
class UnfoldHapticsPlayerTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private val progressProvider = TestUnfoldTransitionProvider()
|
||||||
|
private val vibrator: Vibrator = mock()
|
||||||
|
private val testFoldProvider = TestFoldProvider()
|
||||||
|
|
||||||
|
private lateinit var player: UnfoldHapticsPlayer
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, Runnable::run, vibrator)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUnfoldingTransitionFinishingEarly_playsHaptics() {
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = false)
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinishing()
|
||||||
|
|
||||||
|
verify(vibrator).vibrate(any<VibrationEffect>())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUnfoldingTransitionFinishingLate_doesNotPlayHaptics() {
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = false)
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.99f)
|
||||||
|
progressProvider.onTransitionFinishing()
|
||||||
|
|
||||||
|
verify(vibrator, never()).vibrate(any<VibrationEffect>())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFoldingAfterUnfolding_doesNotPlayHaptics() {
|
||||||
|
// Unfold
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = false)
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinishing()
|
||||||
|
progressProvider.onTransitionFinished()
|
||||||
|
clearInvocations(vibrator)
|
||||||
|
|
||||||
|
// Fold
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinished()
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
|
||||||
|
verify(vibrator, never()).vibrate(any<VibrationEffect>())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUnfoldingAfterFoldingAndUnfolding_playsHaptics() {
|
||||||
|
// Unfold
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = false)
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinishing()
|
||||||
|
progressProvider.onTransitionFinished()
|
||||||
|
|
||||||
|
// Fold
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinished()
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
clearInvocations(vibrator)
|
||||||
|
|
||||||
|
// Unfold again
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = true)
|
||||||
|
testFoldProvider.onFoldUpdate(isFolded = false)
|
||||||
|
progressProvider.onTransitionStarted()
|
||||||
|
progressProvider.onTransitionProgress(0.5f)
|
||||||
|
progressProvider.onTransitionFinishing()
|
||||||
|
progressProvider.onTransitionFinished()
|
||||||
|
|
||||||
|
verify(vibrator).vibrate(any<VibrationEffect>())
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFoldProvider : FoldProvider {
|
||||||
|
private val listeners = arrayListOf<FoldProvider.FoldCallback>()
|
||||||
|
|
||||||
|
override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
|
||||||
|
listeners += callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
|
||||||
|
listeners -= callback
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFoldUpdate(isFolded: Boolean) {
|
||||||
|
listeners.forEach { it.onFoldUpdated(isFolded) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user