diff options
| author | 2023-01-17 16:41:53 +0000 | |
|---|---|---|
| committer | 2023-01-17 16:41:53 +0000 | |
| commit | 76c74ee9f255c89a7a028e8781aaab8897dbe79e (patch) | |
| tree | 8514a16b1848d7112f4c09a21c8cc6a283dda833 | |
| parent | a2896db85b829725ecdedb21fda98c3525cf9903 (diff) | |
| parent | 8b28e859d66169a16acce89d1d7c79f1c18f7f86 (diff) | |
Merge "[Unfold transition] Do not play haptics when hitting a timeout on folding" into tm-qpr-dev
3 files changed, 165 insertions, 3 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt index 7726d09cf971..8214822f0335 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt @@ -3,26 +3,43 @@ package com.android.systemui.unfold import android.os.SystemProperties import android.os.VibrationEffect import android.os.Vibrator +import com.android.systemui.dagger.qualifiers.Main 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 -/** - * Class that plays a haptics effect during unfolding a foldable device - */ +/** Class that plays a haptics effect during unfolding a foldable device */ @SysUIUnfoldScope class UnfoldHapticsPlayer @Inject constructor( unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider, + foldProvider: FoldProvider, + @Main private val mainExecutor: Executor, private val vibrator: Vibrator? ) : TransitionProgressListener { + private var isFirstAnimationAfterUnfold = false + init { if (vibrator != null) { // We don't need to remove the callback because we should listen to it // the whole time when SystemUI process is alive unfoldTransitionProgressProvider.addCallback(this) } + + foldProvider.registerCallback( + object : FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + if (isFolded) { + isFirstAnimationAfterUnfold = true + } + } + }, + mainExecutor + ) } private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN @@ -36,6 +53,13 @@ constructor( } 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 if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) { playHaptics() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt index c31640279305..4a28cd1de255 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt @@ -26,6 +26,10 @@ class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, Transitio listeners.forEach { it.onTransitionFinished() } } + override fun onTransitionFinishing() { + listeners.forEach { it.onTransitionFinishing() } + } + override fun onTransitionProgress(progress: Float) { listeners.forEach { it.onTransitionProgress(progress) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt new file mode 100644 index 000000000000..d3fdbd94a5ac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt @@ -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) } + } + } +} |