Loading packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +64 −32 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.os.ServiceManager; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; Loading @@ -85,6 +86,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import androidx.annotation.Nullable; import androidx.lifecycle.Observer; import com.android.internal.annotations.VisibleForTesting; Loading @@ -95,6 +97,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; Loading Loading @@ -282,6 +285,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected boolean mTelephonyCapable; private final boolean mAcquiredHapticEnabled; @Nullable private final Vibrator mVibrator; // Device provisioning state private boolean mDeviceProvisioned; Loading Loading @@ -1334,6 +1340,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback = new AuthenticationCallback() { private boolean mPlayedAcquiredHaptic; @Override public void onAuthenticationFailed() { Loading @@ -1345,6 +1352,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); Trace.endSection(); // on auth success, we sometimes never received an acquired haptic if (!mPlayedAcquiredHaptic) { playAcquiredHaptic(); } } @Override Loading @@ -1360,17 +1372,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationAcquired(int acquireInfo) { handleFingerprintAcquired(acquireInfo); if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) { playAcquiredHaptic(); } } @Override public void onUdfpsPointerDown(int sensorId) { Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId); mPlayedAcquiredHaptic = false; } @Override public void onUdfpsPointerUp(int sensorId) { Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId); } private void playAcquiredHaptic() { if (mAcquiredHapticEnabled && mVibrator != null && isUdfpsEnrolled()) { mPlayedAcquiredHaptic = true; String effect = Settings.Global.getString( mContext.getContentResolver(), "udfps_acquired_type"); mVibrator.vibrate(UdfpsController.getVibration(effect, UdfpsController.EFFECT_TICK), UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } } }; private final FaceManager.FaceDetectionCallback mFaceDetectionCallback Loading Loading @@ -1663,7 +1691,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab LockPatternUtils lockPatternUtils, AuthController authController, TelephonyListenerManager telephonyListenerManager, FeatureFlags featureFlags) { FeatureFlags featureFlags, @Nullable Vibrator vibrator) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mTelephonyListenerManager = telephonyListenerManager; Loading @@ -1678,6 +1707,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils = lockPatternUtils; mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(), "udfps_acquired", 0) == 1; mVibrator = vibrator; mHandler = new Handler(mainLooper) { @Override Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +27 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; Loading Loading @@ -147,32 +149,22 @@ public class UdfpsController implements DozeReceiver { private Runnable mAodInterruptRunnable; @VisibleForTesting static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); private final VibrationEffect mEffectTextureTick = public static final VibrationEffect EFFECT_TICK = VibrationEffect.get(VibrationEffect.EFFECT_TICK); private static final VibrationEffect EFFECT_TEXTURE_TICK = VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); @VisibleForTesting final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private final VibrationEffect mEffectHeavy = static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private static final VibrationEffect EFFECT_HEAVY = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); private final VibrationEffect mDoubleClick = private static final VibrationEffect EFFECT_DOUBLE_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); private final Runnable mAcquiredVibration = new Runnable() { @Override public void run() { if (mVibrator == null) { return; } String effect = Settings.Global.getString(mContext.getContentResolver(), "udfps_acquired_type"); mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES); } }; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override Loading Loading @@ -449,16 +441,7 @@ public class UdfpsController implements DozeReceiver { String startEffectSetting = Settings.Global.getString( contentResolver, "udfps_start_type"); mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES); } int acquiredEnabled = Settings.Global.getInt(contentResolver, "udfps_acquired", 0); if (acquiredEnabled > 0) { int delay = Settings.Global.getInt(contentResolver, "udfps_acquired_delay", 500); mMainHandler.removeCallbacks(mAcquiredVibration); mMainHandler.postDelayed(mAcquiredVibration, delay); EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES); } } Loading Loading @@ -843,7 +826,6 @@ public class UdfpsController implements DozeReceiver { mExecution.assertIsMainThread(); mActivePointerId = -1; mGoodCaptureReceived = false; mMainHandler.removeCallbacks(mAcquiredVibration); if (mView == null) { Log.w(TAG, "Null view in onFingerUp"); return; Loading @@ -855,23 +837,34 @@ public class UdfpsController implements DozeReceiver { } private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { /** * get vibration to play given string * used for testing purposes (b/185124905) */ public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { if (TextUtils.isEmpty(effect)) { return defaultEffect; } switch (effect.toLowerCase()) { case "click": return mEffectClick; return EFFECT_CLICK; case "heavy": return mEffectHeavy; return EFFECT_HEAVY; case "texture_tick": return mEffectTextureTick; return EFFECT_TEXTURE_TICK; case "tick": return mEffectTick; return EFFECT_TICK; case "double_tap": return mDoubleClick; return EFFECT_DOUBLE_CLICK; default: try { int primitive = Integer.parseInt(effect); if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) { return VibrationEffect.startComposition().addPrimitive(primitive).compose(); } } catch (NumberFormatException e) { } return defaultEffect; } } Loading packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +6 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading Loading @@ -108,6 +109,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading Loading @@ -168,6 +170,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private TelephonyListenerManager mTelephonyListenerManager; @Mock private FeatureFlags mFeatureFlags; @Mock private Vibrator mVibrator; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; // Direct executor Loading Loading @@ -979,7 +983,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mBroadcastDispatcher, mDumpManager, mRingerModeTracker, mBackgroundExecutor, mStatusBarStateController, mLockPatternUtils, mAuthController, mTelephonyListenerManager, mFeatureFlags); mAuthController, mTelephonyListenerManager, mFeatureFlags, mVibrator); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -344,7 +344,7 @@ public class UdfpsControllerTest extends SysuiTestCase { moveEvent.recycle(); // THEN click haptic is played verify(mVibrator).vibrate(mUdfpsController.mEffectClick, verify(mVibrator).vibrate(mUdfpsController.EFFECT_CLICK, UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } } services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +8 −6 Original line number Diff line number Diff line Loading @@ -192,25 +192,27 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); } protected @NonNull VibrationEffect getSuccessVibrationEffect() { protected @Nullable VibrationEffect getSuccessVibrationEffect() { return mSuccessVibrationEffect; } protected @NonNull VibrationEffect getErrorVibrationEffect() { protected @Nullable VibrationEffect getErrorVibrationEffect() { return mErrorVibrationEffect; } protected final void vibrateSuccess() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); VibrationEffect effect = getSuccessVibrationEffect(); if (vibrator != null && effect != null) { vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } protected final void vibrateError() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); VibrationEffect effect = getErrorVibrationEffect(); if (vibrator != null && effect != null) { vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } Loading Loading
packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +64 −32 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import android.os.ServiceManager; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; Loading @@ -85,6 +86,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import androidx.annotation.Nullable; import androidx.lifecycle.Observer; import com.android.internal.annotations.VisibleForTesting; Loading @@ -95,6 +97,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; Loading Loading @@ -282,6 +285,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected boolean mTelephonyCapable; private final boolean mAcquiredHapticEnabled; @Nullable private final Vibrator mVibrator; // Device provisioning state private boolean mDeviceProvisioned; Loading Loading @@ -1334,6 +1340,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback = new AuthenticationCallback() { private boolean mPlayedAcquiredHaptic; @Override public void onAuthenticationFailed() { Loading @@ -1345,6 +1352,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); Trace.endSection(); // on auth success, we sometimes never received an acquired haptic if (!mPlayedAcquiredHaptic) { playAcquiredHaptic(); } } @Override Loading @@ -1360,17 +1372,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationAcquired(int acquireInfo) { handleFingerprintAcquired(acquireInfo); if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) { playAcquiredHaptic(); } } @Override public void onUdfpsPointerDown(int sensorId) { Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId); mPlayedAcquiredHaptic = false; } @Override public void onUdfpsPointerUp(int sensorId) { Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId); } private void playAcquiredHaptic() { if (mAcquiredHapticEnabled && mVibrator != null && isUdfpsEnrolled()) { mPlayedAcquiredHaptic = true; String effect = Settings.Global.getString( mContext.getContentResolver(), "udfps_acquired_type"); mVibrator.vibrate(UdfpsController.getVibration(effect, UdfpsController.EFFECT_TICK), UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } } }; private final FaceManager.FaceDetectionCallback mFaceDetectionCallback Loading Loading @@ -1663,7 +1691,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab LockPatternUtils lockPatternUtils, AuthController authController, TelephonyListenerManager telephonyListenerManager, FeatureFlags featureFlags) { FeatureFlags featureFlags, @Nullable Vibrator vibrator) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mTelephonyListenerManager = telephonyListenerManager; Loading @@ -1678,6 +1707,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils = lockPatternUtils; mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(), "udfps_acquired", 0) == 1; mVibrator = vibrator; mHandler = new Handler(mainLooper) { @Override Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +27 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; Loading Loading @@ -147,32 +149,22 @@ public class UdfpsController implements DozeReceiver { private Runnable mAodInterruptRunnable; @VisibleForTesting static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); private final VibrationEffect mEffectTextureTick = public static final VibrationEffect EFFECT_TICK = VibrationEffect.get(VibrationEffect.EFFECT_TICK); private static final VibrationEffect EFFECT_TEXTURE_TICK = VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); @VisibleForTesting final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private final VibrationEffect mEffectHeavy = static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private static final VibrationEffect EFFECT_HEAVY = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); private final VibrationEffect mDoubleClick = private static final VibrationEffect EFFECT_DOUBLE_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); private final Runnable mAcquiredVibration = new Runnable() { @Override public void run() { if (mVibrator == null) { return; } String effect = Settings.Global.getString(mContext.getContentResolver(), "udfps_acquired_type"); mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES); } }; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override Loading Loading @@ -449,16 +441,7 @@ public class UdfpsController implements DozeReceiver { String startEffectSetting = Settings.Global.getString( contentResolver, "udfps_start_type"); mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES); } int acquiredEnabled = Settings.Global.getInt(contentResolver, "udfps_acquired", 0); if (acquiredEnabled > 0) { int delay = Settings.Global.getInt(contentResolver, "udfps_acquired_delay", 500); mMainHandler.removeCallbacks(mAcquiredVibration); mMainHandler.postDelayed(mAcquiredVibration, delay); EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES); } } Loading Loading @@ -843,7 +826,6 @@ public class UdfpsController implements DozeReceiver { mExecution.assertIsMainThread(); mActivePointerId = -1; mGoodCaptureReceived = false; mMainHandler.removeCallbacks(mAcquiredVibration); if (mView == null) { Log.w(TAG, "Null view in onFingerUp"); return; Loading @@ -855,23 +837,34 @@ public class UdfpsController implements DozeReceiver { } private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { /** * get vibration to play given string * used for testing purposes (b/185124905) */ public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { if (TextUtils.isEmpty(effect)) { return defaultEffect; } switch (effect.toLowerCase()) { case "click": return mEffectClick; return EFFECT_CLICK; case "heavy": return mEffectHeavy; return EFFECT_HEAVY; case "texture_tick": return mEffectTextureTick; return EFFECT_TEXTURE_TICK; case "tick": return mEffectTick; return EFFECT_TICK; case "double_tap": return mDoubleClick; return EFFECT_DOUBLE_CLICK; default: try { int primitive = Integer.parseInt(effect); if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) { return VibrationEffect.startComposition().addPrimitive(primitive).compose(); } } catch (NumberFormatException e) { } return defaultEffect; } } Loading
packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +6 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading Loading @@ -108,6 +109,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading Loading @@ -168,6 +170,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private TelephonyListenerManager mTelephonyListenerManager; @Mock private FeatureFlags mFeatureFlags; @Mock private Vibrator mVibrator; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; // Direct executor Loading Loading @@ -979,7 +983,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mBroadcastDispatcher, mDumpManager, mRingerModeTracker, mBackgroundExecutor, mStatusBarStateController, mLockPatternUtils, mAuthController, mTelephonyListenerManager, mFeatureFlags); mAuthController, mTelephonyListenerManager, mFeatureFlags, mVibrator); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -344,7 +344,7 @@ public class UdfpsControllerTest extends SysuiTestCase { moveEvent.recycle(); // THEN click haptic is played verify(mVibrator).vibrate(mUdfpsController.mEffectClick, verify(mVibrator).vibrate(mUdfpsController.EFFECT_CLICK, UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } }
services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +8 −6 Original line number Diff line number Diff line Loading @@ -192,25 +192,27 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); } protected @NonNull VibrationEffect getSuccessVibrationEffect() { protected @Nullable VibrationEffect getSuccessVibrationEffect() { return mSuccessVibrationEffect; } protected @NonNull VibrationEffect getErrorVibrationEffect() { protected @Nullable VibrationEffect getErrorVibrationEffect() { return mErrorVibrationEffect; } protected final void vibrateSuccess() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); VibrationEffect effect = getSuccessVibrationEffect(); if (vibrator != null && effect != null) { vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } protected final void vibrateError() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); VibrationEffect effect = getErrorVibrationEffect(); if (vibrator != null && effect != null) { vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } Loading