• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<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