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