<lambda>null1 package com.android.systemui.unfold
2
3 import android.os.SystemProperties
4 import android.os.VibrationAttributes
5 import android.os.VibrationEffect
6 import android.os.Vibrator
7 import com.android.systemui.dagger.qualifiers.Main
8 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
9 import com.android.systemui.unfold.config.UnfoldTransitionConfig
10 import com.android.systemui.unfold.updates.FoldProvider
11 import java.util.concurrent.Executor
12 import javax.inject.Inject
13
14 /** Class that plays a haptics effect during unfolding a foldable device */
15 @SysUIUnfoldScope
16 class UnfoldHapticsPlayer
17 @Inject
18 constructor(
19 unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
20 foldProvider: FoldProvider,
21 transitionConfig: UnfoldTransitionConfig,
22 @Main private val mainExecutor: Executor,
23 private val vibrator: Vibrator?
24 ) : TransitionProgressListener {
25
26 private var isFirstAnimationAfterUnfold = false
27 private val touchVibrationAttributes =
28 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
29
30 init {
31 if (vibrator != null && transitionConfig.isHapticsEnabled) {
32 // We don't need to remove the callback because we should listen to it
33 // the whole time when SystemUI process is alive
34 unfoldTransitionProgressProvider.addCallback(this)
35
36 foldProvider.registerCallback({ isFolded ->
37 if (isFolded) {
38 isFirstAnimationAfterUnfold = true
39 }
40 }, mainExecutor)
41 }
42 }
43
44 private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
45
46 override fun onTransitionStarted() {
47 lastTransitionProgress = TRANSITION_PROGRESS_CLOSED
48 }
49
50 override fun onTransitionProgress(progress: Float) {
51 lastTransitionProgress = progress
52 }
53
54 override fun onTransitionFinishing() {
55 // Run haptics only when unfolding the device (first animation after unfolding)
56 if (!isFirstAnimationAfterUnfold) {
57 return
58 }
59
60 isFirstAnimationAfterUnfold = false
61
62 // Run haptics only if the animation is long enough to notice
63 if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) {
64 playHaptics()
65 }
66 }
67
68 override fun onTransitionFinished() {
69 lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
70 }
71
72 private fun playHaptics() {
73 vibrator?.vibrate(effect, touchVibrationAttributes)
74 }
75
76 private val hapticsScale: Float
77 get() {
78 val intensityString = SystemProperties.get("persist.unfold.haptics_scale", "0.5")
79 return intensityString.toFloatOrNull() ?: 0.5f
80 }
81
82 private val hapticsScaleTick: Float
83 get() {
84 val intensityString =
85 SystemProperties.get("persist.unfold.haptics_scale_end_tick", "1.0")
86 return intensityString.toFloatOrNull() ?: 1.0f
87 }
88
89 private val primitivesCount: Int
90 get() {
91 val count = SystemProperties.get("persist.unfold.primitives_count", "18")
92 return count.toIntOrNull() ?: 18
93 }
94
95 private val effect: VibrationEffect by lazy {
96 val composition =
97 VibrationEffect.startComposition()
98 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0F, 0)
99
100 repeat(primitivesCount) {
101 composition.addPrimitive(
102 VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
103 hapticsScale,
104 0
105 )
106 }
107
108 composition
109 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, hapticsScaleTick)
110 .compose()
111 }
112 }
113
114 private const val TRANSITION_PROGRESS_CLOSED = 0f
115 private const val TRANSITION_PROGRESS_FULL_OPEN = 1f
116 private const val TRANSITION_NOTICEABLE_THRESHOLD = 0.9f
117