1 /* 2 * Copyright 2023 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.core.view; 18 19 import android.os.Build; 20 21 import androidx.annotation.IntDef; 22 import androidx.annotation.RestrictTo; 23 import androidx.annotation.VisibleForTesting; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.RetentionPolicy; 27 28 /** 29 * Helper class for accessing values in {@link android.view.HapticFeedbackConstants}. 30 */ 31 public final class HapticFeedbackConstantsCompat { 32 33 /** 34 * No haptic feedback should be performed. Applications may use this value to indicate skipping 35 * a call to {@link android.view.View#performHapticFeedback} entirely, or else rely that it 36 * will immediately return {@code false}. 37 * 38 * <p>Compatibility: 39 * <ul> 40 * <li>API < 34: Same behavior, immediately returns false</li> 41 * </ul> 42 */ 43 public static final int NO_HAPTICS = -1; 44 45 /** 46 * The user has performed a long press on an object that is resulting in an action being 47 * performed. 48 */ 49 public static final int LONG_PRESS = 0; 50 51 /** 52 * The user has pressed on a virtual on-screen key. 53 */ 54 public static final int VIRTUAL_KEY = 1; 55 56 /** 57 * The user has pressed a soft keyboard key. 58 */ 59 public static final int KEYBOARD_TAP = 3; 60 61 /** 62 * The user has pressed either an hour or minute tick of a Clock. 63 * 64 * <p>Compatibility: 65 * <ul> 66 * <li>API < 21: No-op</li> 67 * </ul> 68 */ 69 public static final int CLOCK_TICK = 4; 70 71 /** 72 * The user has performed a context click on an object. 73 * 74 * <p>Compatibility: 75 * <ul> 76 * <li>API < 23: Same feedback as CLOCK_TICK</li> 77 * <li>API < 21: No-op</li> 78 * </ul> 79 */ 80 public static final int CONTEXT_CLICK = 6; 81 82 /** 83 * The user has pressed a virtual or software keyboard key. 84 * 85 * <p>Compatibility: 86 * <ul> 87 * <li>API < 27: Same feedback as KEYBOARD_TAP</li> 88 * </ul> 89 */ 90 public static final int KEYBOARD_PRESS = KEYBOARD_TAP; // Platform constant is also the same. 91 92 /** 93 * The user has released a virtual keyboard key. 94 * 95 * <p>Compatibility: 96 * <ul> 97 * <li>API < 27: No-op</li> 98 * </ul> 99 */ 100 public static final int KEYBOARD_RELEASE = 7; 101 102 /** 103 * The user has released a virtual key. 104 * 105 * <p>Compatibility: 106 * <ul> 107 * <li>API < 27: No-op</li> 108 * </ul> 109 */ 110 public static final int VIRTUAL_KEY_RELEASE = 8; 111 112 /** 113 * The user has performed a selection/insertion handle move on text field. 114 * 115 * <p>Compatibility: 116 * <ul> 117 * <li>API < 27: No-op</li> 118 * </ul> 119 */ 120 public static final int TEXT_HANDLE_MOVE = 9; 121 122 /** 123 * The user has started a gesture (e.g. on the soft keyboard). 124 * 125 * <p>Compatibility: 126 * <ul> 127 * <li>API < 30: Same feedback as VIRTUAL_KEY</li> 128 * </ul> 129 */ 130 public static final int GESTURE_START = 12; 131 132 /** 133 * The user has finished a gesture (e.g. on the soft keyboard). 134 * 135 * <p>Compatibility: 136 * <ul> 137 * <li>API < 30: Same feedback as CONTEXT_CLICK</li> 138 * <li>API < 21: No-op</li> 139 * </ul> 140 */ 141 public static final int GESTURE_END = 13; 142 143 /** 144 * A haptic effect to signal the confirmation or successful completion of a user interaction. 145 * 146 * <p>Compatibility: 147 * <ul> 148 * <li>API < 30: Same feedback as VIRTUAL_KEY</li> 149 * </ul> 150 */ 151 public static final int CONFIRM = 16; 152 153 /** 154 * A haptic effect to signal the rejection or failure of a user interaction. 155 * 156 * <p>Compatibility: 157 * <ul> 158 * <li>API < 30: Same feedback as LONG_PRESS</li> 159 * </ul> 160 */ 161 public static final int REJECT = 17; 162 163 /** 164 * The user has toggled a switch or button into the on position. 165 * 166 * <p>Compatibility: 167 * <ul> 168 * <li>API < 34: Same feedback as CONTEXT_CLICK</li> 169 * <li>API < 21: No-op</li> 170 * </ul> 171 */ 172 public static final int TOGGLE_ON = 21; 173 174 /** 175 * The user has toggled a switch or button into the off position. 176 * 177 * <p>Compatibility: 178 * <ul> 179 * <li>API < 34: Same feedback as CLOCK_TICK</li> 180 * <li>API < 21: No-op</li> 181 * </ul> 182 */ 183 public static final int TOGGLE_OFF = 22; 184 185 /** 186 * The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the 187 * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by 188 * moving back past the threshold. This constant indicates that the user's motion has just 189 * passed the threshold for the action to be activated on release. 190 * 191 * @see #GESTURE_THRESHOLD_DEACTIVATE 192 * 193 * <p>Compatibility: 194 * <ul> 195 * <li>API < 34: Same feedback as CONTEXT_CLICK</li> 196 * <li>API < 21: No-op</li> 197 * </ul> 198 */ 199 public static final int GESTURE_THRESHOLD_ACTIVATE = 23; 200 201 /** 202 * The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the 203 * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by 204 * moving back past the threshold. This constant indicates that the user's motion has just 205 * re-crossed back "under" the threshold for the action to be activated, meaning the gesture is 206 * currently in a cancelled state. 207 * 208 * @see #GESTURE_THRESHOLD_ACTIVATE 209 * 210 * <p>Compatibility: 211 * <ul> 212 * <li>API < 34: Same feedback as CLOCK_TICK</li> 213 * <li>API < 21: No-op</li> 214 * </ul> 215 */ 216 public static final int GESTURE_THRESHOLD_DEACTIVATE = 24; 217 218 /** 219 * The user has started a drag-and-drop gesture. The drag target has just been "picked up". 220 * 221 * <p>Compatibility: 222 * <ul> 223 * <li>API < 34: Same feedback as LONG_PRESS</li> 224 * </ul> 225 */ 226 public static final int DRAG_START = 25; 227 228 /** 229 * The user is switching between a series of potential choices, for example items in a list 230 * or discrete points on a slider. 231 * 232 * <p>See also {@link #SEGMENT_FREQUENT_TICK} for cases where density of choices is high, and 233 * the haptics should be lighter or suppressed for a better user experience. 234 * 235 * <p>Compatibility: 236 * <ul> 237 * <li>API < 34: Same feedback as CONTEXT_CLICK</li> 238 * <li>API < 21: No-op</li> 239 * </ul> 240 */ 241 public static final int SEGMENT_TICK = 26; 242 243 /** 244 * The user is switching between a series of many potential choices, for example minutes on a 245 * clock face, or individual percentages. This constant is expected to be very soft, so as 246 * not to be uncomfortable when performed a lot in quick succession. If the device can’t make 247 * a suitably soft vibration, then it may not make any vibration. 248 * 249 * <p>Some specializations of this constant exist for specific actions, notably 250 * {@link #CLOCK_TICK} and {@link #TEXT_HANDLE_MOVE}. 251 * 252 * <p>See also {@link #SEGMENT_TICK}. 253 * 254 * <p>Compatibility: 255 * <ul> 256 * <li>API < 34: Same feedback as CLOCK_TICK</li> 257 * <li>API < 21: No-op</li> 258 * </ul> 259 */ 260 public static final int SEGMENT_FREQUENT_TICK = 27; 261 262 /** First constant value, excluding {@link #NO_HAPTICS} constant. */ 263 @VisibleForTesting 264 static final int FIRST_CONSTANT_INT = LONG_PRESS; 265 266 /** Last constant value used. */ 267 @VisibleForTesting 268 static final int LAST_CONSTANT_INT = SEGMENT_FREQUENT_TICK; 269 270 /** 271 * Flag for {@link ViewCompat#performHapticFeedback(android.view.View, int, int)}: Ignore the 272 * setting in the view for whether to perform haptic feedback, do it always. 273 */ 274 public static final int FLAG_IGNORE_VIEW_SETTING = 0x0001; 275 276 /** Haptic feedback types. */ 277 @IntDef(value = { 278 NO_HAPTICS, 279 LONG_PRESS, 280 VIRTUAL_KEY, 281 KEYBOARD_TAP, 282 CLOCK_TICK, 283 CONTEXT_CLICK, 284 KEYBOARD_PRESS, 285 KEYBOARD_RELEASE, 286 VIRTUAL_KEY_RELEASE, 287 TEXT_HANDLE_MOVE, 288 GESTURE_START, 289 GESTURE_END, 290 CONFIRM, 291 REJECT, 292 TOGGLE_ON, 293 TOGGLE_OFF, 294 GESTURE_THRESHOLD_ACTIVATE, 295 GESTURE_THRESHOLD_DEACTIVATE, 296 DRAG_START, 297 SEGMENT_TICK, 298 SEGMENT_FREQUENT_TICK 299 }) 300 @Retention(RetentionPolicy.SOURCE) 301 @RestrictTo(RestrictTo.Scope.LIBRARY) 302 public @interface HapticFeedbackType { 303 } 304 305 /** Flags for performing haptic feedback. */ 306 @IntDef(flag = true, value = { 307 FLAG_IGNORE_VIEW_SETTING 308 }) 309 @Retention(RetentionPolicy.SOURCE) 310 @RestrictTo(RestrictTo.Scope.LIBRARY) 311 public @interface HapticFeedbackFlags { 312 } 313 314 /** 315 * Returns a haptic feedback constant that is available for this platform build. 316 * 317 * @param feedbackConstant The feedback constant requested 318 * @return The same constant, if supported by this platform build, or a supported fallback. 319 */ 320 @HapticFeedbackType getFeedbackConstantOrFallback(@apticFeedbackType int feedbackConstant)321 static int getFeedbackConstantOrFallback(@HapticFeedbackType int feedbackConstant) { 322 if (feedbackConstant == NO_HAPTICS) { 323 // Skip fallback logic if constant is no-op. 324 return NO_HAPTICS; 325 } 326 if (Build.VERSION.SDK_INT < 34) { 327 switch (feedbackConstant) { 328 case DRAG_START: 329 feedbackConstant = LONG_PRESS; 330 break; 331 case TOGGLE_ON: 332 case SEGMENT_TICK: 333 case GESTURE_THRESHOLD_ACTIVATE: 334 feedbackConstant = CONTEXT_CLICK; 335 break; 336 case TOGGLE_OFF: 337 case SEGMENT_FREQUENT_TICK: 338 case GESTURE_THRESHOLD_DEACTIVATE: 339 feedbackConstant = CLOCK_TICK; 340 break; 341 } 342 } 343 if (Build.VERSION.SDK_INT < 30) { 344 switch (feedbackConstant) { 345 case REJECT: 346 feedbackConstant = LONG_PRESS; 347 break; 348 case CONFIRM: 349 case GESTURE_START: 350 feedbackConstant = VIRTUAL_KEY; 351 break; 352 case GESTURE_END: 353 feedbackConstant = CONTEXT_CLICK; 354 break; 355 } 356 } 357 if (Build.VERSION.SDK_INT < 27) { 358 switch (feedbackConstant) { 359 case KEYBOARD_RELEASE: 360 case VIRTUAL_KEY_RELEASE: 361 case TEXT_HANDLE_MOVE: 362 feedbackConstant = NO_HAPTICS; 363 break; 364 } 365 } 366 if (Build.VERSION.SDK_INT < 23) { 367 switch (feedbackConstant) { 368 case CONTEXT_CLICK: 369 feedbackConstant = CLOCK_TICK; 370 break; 371 } 372 } 373 if (Build.VERSION.SDK_INT < 21) { 374 switch (feedbackConstant) { 375 case CLOCK_TICK: 376 feedbackConstant = NO_HAPTICS; 377 break; 378 } 379 } 380 return feedbackConstant; 381 } 382 HapticFeedbackConstantsCompat()383 private HapticFeedbackConstantsCompat() {} 384 } 385