1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.platform
18 
19 import android.content.Context
20 import android.os.Build
21 import android.os.VibrationEffect
22 import android.os.Vibrator
23 import android.view.HapticFeedbackConstants
24 import android.view.View
25 import androidx.compose.ui.hapticfeedback.HapticFeedback
26 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
27 
28 /**
29  * Provide a default implementation of HapticFeedback to call through to the view's
30  * [performHapticFeedback] with the associated HapticFeedbackConstant.
31  *
32  * @param view The current view, used for forwarding haptic feedback requests.
33  */
34 internal class DefaultHapticFeedback(private val view: View) : HapticFeedback {
performHapticFeedbacknull35     override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
36         when (hapticFeedbackType) {
37             HapticFeedbackType.Confirm ->
38                 view.performHapticFeedback(HapticFeedbackConstants.CONFIRM)
39             HapticFeedbackType.ContextClick ->
40                 view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
41             HapticFeedbackType.GestureEnd ->
42                 view.performHapticFeedback(HapticFeedbackConstants.GESTURE_END)
43             HapticFeedbackType.GestureThresholdActivate ->
44                 view.performHapticFeedback(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE)
45             HapticFeedbackType.KeyboardTap ->
46                 view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
47             HapticFeedbackType.LongPress ->
48                 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
49             HapticFeedbackType.Reject -> view.performHapticFeedback(HapticFeedbackConstants.REJECT)
50             HapticFeedbackType.SegmentFrequentTick ->
51                 view.performHapticFeedback(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK)
52             HapticFeedbackType.SegmentTick ->
53                 view.performHapticFeedback(HapticFeedbackConstants.SEGMENT_TICK)
54             HapticFeedbackType.TextHandleMove ->
55                 view.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE)
56             HapticFeedbackType.ToggleOff ->
57                 view.performHapticFeedback(HapticFeedbackConstants.TOGGLE_OFF)
58             HapticFeedbackType.ToggleOn ->
59                 view.performHapticFeedback(HapticFeedbackConstants.TOGGLE_ON)
60             HapticFeedbackType.VirtualKey ->
61                 view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
62         }
63     }
64 }
65 
66 /** Provide a no-op implementation of HapticFeedback */
67 internal class NoHapticFeedback : HapticFeedback {
performHapticFeedbacknull68     override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
69         // No-op
70     }
71 }
72 
73 /** Contains defaults for haptics functionality */
74 internal object HapticDefaults {
75     /**
76      * Returns whether the device supports premium haptic feedback.
77      *
78      * @param context The current context for access to the Vibrator via System Service.
79      */
isPremiumVibratorEnablednull80     fun isPremiumVibratorEnabled(context: Context): Boolean {
81         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
82             val vibrator = context.getSystemService(Vibrator::class.java)
83 
84             // NB whilst the 'areAllPrimitivesSupported' API needs R (API 30), we need S (API
85             // 31) so that PRIMITIVE_THUD is available.
86             if (
87                 vibrator.areAllPrimitivesSupported(
88                     VibrationEffect.Composition.PRIMITIVE_CLICK,
89                     VibrationEffect.Composition.PRIMITIVE_TICK,
90                     VibrationEffect.Composition.PRIMITIVE_THUD
91                 )
92             ) {
93                 return true
94             }
95         }
96 
97         return false
98     }
99 }
100