1 /* 2 * Copyright (C) 2018 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 com.android.systemui.statusbar; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.media.AudioAttributes; 22 import android.os.Process; 23 import android.os.VibrationAttributes; 24 import android.os.VibrationEffect; 25 import android.os.Vibrator; 26 import android.view.View; 27 28 import androidx.annotation.VisibleForTesting; 29 30 import com.android.systemui.dagger.SysUISingleton; 31 32 import org.jetbrains.annotations.NotNull; 33 34 import java.util.concurrent.Executor; 35 import java.util.concurrent.Executors; 36 37 import javax.inject.Inject; 38 39 /** 40 * A Helper class that offloads {@link Vibrator} calls to a different thread. 41 * {@link Vibrator} makes blocking calls that may cause SysUI to ANR. 42 * TODO(b/245528624): Use regular Vibrator instance once new APIs are available. 43 */ 44 @SysUISingleton 45 public class VibratorHelper { 46 47 private final Vibrator mVibrator; 48 public static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = 49 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); 50 51 private static final VibrationEffect BIOMETRIC_SUCCESS_VIBRATION_EFFECT = 52 VibrationEffect.get(VibrationEffect.EFFECT_CLICK); 53 private static final VibrationEffect BIOMETRIC_ERROR_VIBRATION_EFFECT = 54 VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); 55 private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = 56 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); 57 58 private final Executor mExecutor; 59 60 /** 61 * Creates a vibrator helper on a new single threaded {@link Executor}. 62 */ 63 @Inject VibratorHelper(@ullable Vibrator vibrator)64 public VibratorHelper(@Nullable Vibrator vibrator) { 65 this(vibrator, Executors.newSingleThreadExecutor()); 66 } 67 68 /** 69 * Creates new vibrator helper on a specific {@link Executor}. 70 */ 71 @VisibleForTesting VibratorHelper(@ullable Vibrator vibrator, Executor executor)72 public VibratorHelper(@Nullable Vibrator vibrator, Executor executor) { 73 mExecutor = executor; 74 mVibrator = vibrator; 75 } 76 77 /** 78 * @see Vibrator#vibrate(long) 79 */ vibrate(final int effectId)80 public void vibrate(final int effectId) { 81 if (!hasVibrator()) { 82 return; 83 } 84 mExecutor.execute(() -> 85 mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */), 86 TOUCH_VIBRATION_ATTRIBUTES)); 87 } 88 89 /** 90 * @see Vibrator#vibrate(int, String, VibrationEffect, String, VibrationAttributes) 91 */ vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)92 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, 93 String reason, @NonNull VibrationAttributes attributes) { 94 if (!hasVibrator()) { 95 return; 96 } 97 mExecutor.execute(() -> mVibrator.vibrate(uid, opPkg, vibe, reason, attributes)); 98 } 99 100 /** 101 * @see Vibrator#vibrate(VibrationEffect, AudioAttributes) 102 */ vibrate(@onNull VibrationEffect effect, @NonNull AudioAttributes attributes)103 public void vibrate(@NonNull VibrationEffect effect, @NonNull AudioAttributes attributes) { 104 if (!hasVibrator()) { 105 return; 106 } 107 mExecutor.execute(() -> mVibrator.vibrate(effect, attributes)); 108 } 109 110 /** 111 * @see Vibrator#vibrate(VibrationEffect) 112 */ vibrate(@otNull VibrationEffect effect)113 public void vibrate(@NotNull VibrationEffect effect) { 114 if (!hasVibrator()) { 115 return; 116 } 117 mExecutor.execute(() -> mVibrator.vibrate(effect)); 118 } 119 120 /** 121 * @see Vibrator#hasVibrator() 122 */ hasVibrator()123 public boolean hasVibrator() { 124 return mVibrator != null && mVibrator.hasVibrator(); 125 } 126 127 /** 128 * @see Vibrator#cancel() 129 */ cancel()130 public void cancel() { 131 if (!hasVibrator()) { 132 return; 133 } 134 mExecutor.execute(mVibrator::cancel); 135 } 136 137 /** 138 * Perform vibration when biometric authentication success 139 */ vibrateAuthSuccess(String reason)140 public void vibrateAuthSuccess(String reason) { 141 vibrate(Process.myUid(), 142 "com.android.systemui", 143 BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason, 144 HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); 145 } 146 147 /** 148 * Perform vibration when biometric authentication error 149 */ vibrateAuthError(String reason)150 public void vibrateAuthError(String reason) { 151 vibrate(Process.myUid(), "com.android.systemui", 152 BIOMETRIC_ERROR_VIBRATION_EFFECT, reason, 153 HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); 154 } 155 156 /** 157 * Perform a vibration using a view and the one-way API with flags 158 * @see View#performHapticFeedback(int feedbackConstant, int flags) 159 */ performHapticFeedback(@onNull View view, int feedbackConstant, int flags)160 public void performHapticFeedback(@NonNull View view, int feedbackConstant, int flags) { 161 view.performHapticFeedback(feedbackConstant, flags); 162 } 163 164 /** 165 * Perform a vibration using a view and the one-way API 166 * @see View#performHapticFeedback(int feedbackConstant) 167 */ performHapticFeedback(@onNull View view, int feedbackConstant)168 public void performHapticFeedback(@NonNull View view, int feedbackConstant) { 169 view.performHapticFeedback(feedbackConstant); 170 } 171 } 172