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 private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES = 58 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST); 59 60 private final Executor mExecutor; 61 62 /** 63 * Creates a vibrator helper on a new single threaded {@link Executor}. 64 */ 65 @Inject VibratorHelper(@ullable Vibrator vibrator)66 public VibratorHelper(@Nullable Vibrator vibrator) { 67 this(vibrator, Executors.newSingleThreadExecutor()); 68 } 69 70 /** 71 * Creates new vibrator helper on a specific {@link Executor}. 72 */ 73 @VisibleForTesting VibratorHelper(@ullable Vibrator vibrator, Executor executor)74 public VibratorHelper(@Nullable Vibrator vibrator, Executor executor) { 75 mExecutor = executor; 76 mVibrator = vibrator; 77 } 78 79 /** 80 * @see Vibrator#vibrate(long) 81 */ vibrate(final int effectId)82 public void vibrate(final int effectId) { 83 if (!hasVibrator()) { 84 return; 85 } 86 mExecutor.execute(() -> 87 mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */), 88 TOUCH_VIBRATION_ATTRIBUTES)); 89 } 90 91 /** 92 * @see Vibrator#vibrate(int, String, VibrationEffect, String, VibrationAttributes) 93 */ vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)94 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, 95 String reason, @NonNull VibrationAttributes attributes) { 96 if (!hasVibrator()) { 97 return; 98 } 99 mExecutor.execute(() -> mVibrator.vibrate(uid, opPkg, vibe, reason, attributes)); 100 } 101 102 /** 103 * @see Vibrator#vibrate(VibrationEffect, AudioAttributes) 104 */ vibrate(@onNull VibrationEffect effect, @NonNull AudioAttributes attributes)105 public void vibrate(@NonNull VibrationEffect effect, @NonNull AudioAttributes attributes) { 106 if (!hasVibrator()) { 107 return; 108 } 109 mExecutor.execute(() -> mVibrator.vibrate(effect, attributes)); 110 } 111 112 /** 113 * @see Vibrator#vibrate(VibrationEffect) 114 */ vibrate(@otNull VibrationEffect effect)115 public void vibrate(@NotNull VibrationEffect effect) { 116 if (!hasVibrator()) { 117 return; 118 } 119 mExecutor.execute(() -> mVibrator.vibrate(effect)); 120 } 121 122 /** 123 * @see Vibrator#vibrate(VibrationEffect, VibrationAttributes) 124 */ vibrate(@onNull VibrationEffect effect, @NonNull VibrationAttributes attributes)125 public void vibrate(@NonNull VibrationEffect effect, @NonNull VibrationAttributes attributes) { 126 if (!hasVibrator()) { 127 return; 128 } 129 mExecutor.execute(() -> mVibrator.vibrate(effect, attributes)); 130 } 131 132 /** 133 * @see Vibrator#hasVibrator() 134 */ hasVibrator()135 public boolean hasVibrator() { 136 return mVibrator != null && mVibrator.hasVibrator(); 137 } 138 139 /** 140 * @see Vibrator#cancel() 141 */ cancel()142 public void cancel() { 143 if (!hasVibrator()) { 144 return; 145 } 146 mExecutor.execute(mVibrator::cancel); 147 } 148 149 /** 150 * Perform vibration when biometric authentication success 151 */ vibrateAuthSuccess(String reason)152 public void vibrateAuthSuccess(String reason) { 153 vibrate(Process.myUid(), 154 "com.android.systemui", 155 BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason, 156 COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES); 157 } 158 159 /** 160 * Perform vibration when biometric authentication error 161 */ vibrateAuthError(String reason)162 public void vibrateAuthError(String reason) { 163 vibrate(Process.myUid(), "com.android.systemui", 164 BIOMETRIC_ERROR_VIBRATION_EFFECT, reason, 165 COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES); 166 } 167 168 /** 169 * @see Vibrator#getPrimitiveDurations(int...) 170 */ getPrimitiveDurations(int... primitiveIds)171 public int[] getPrimitiveDurations(int... primitiveIds) { 172 return mVibrator.getPrimitiveDurations(primitiveIds); 173 } 174 175 /** 176 * Perform a vibration using a view and the one-way API with flags 177 * @see View#performHapticFeedback(int feedbackConstant, int flags) 178 */ performHapticFeedback(@onNull View view, int feedbackConstant, int flags)179 public void performHapticFeedback(@NonNull View view, int feedbackConstant, int flags) { 180 view.performHapticFeedback(feedbackConstant, flags); 181 } 182 183 /** 184 * Perform a vibration using a view and the one-way API 185 * @see View#performHapticFeedback(int feedbackConstant) 186 */ performHapticFeedback(@onNull View view, int feedbackConstant)187 public void performHapticFeedback(@NonNull View view, int feedbackConstant) { 188 view.performHapticFeedback(feedbackConstant); 189 } 190 } 191