1 /* 2 * Copyright (C) 2019 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 package com.android.launcher3.util; 17 18 import static android.os.VibrationEffect.createPredefined; 19 import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; 20 21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 22 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 23 24 import android.annotation.TargetApi; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.database.ContentObserver; 28 import android.os.Build; 29 import android.os.VibrationEffect; 30 import android.os.Vibrator; 31 import android.provider.Settings; 32 33 /** 34 * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary. 35 */ 36 @TargetApi(Build.VERSION_CODES.Q) 37 public class VibratorWrapper { 38 39 public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE = 40 new MainThreadInitializedObject<>(VibratorWrapper::new); 41 42 private static final VibrationEffect EFFECT_CLICK = 43 createPredefined(VibrationEffect.EFFECT_CLICK); 44 45 /** 46 * Haptic when entering overview. 47 */ 48 public static final VibrationEffect OVERVIEW_HAPTIC = EFFECT_CLICK; 49 50 private final Vibrator mVibrator; 51 private final boolean mHasVibrator; 52 53 private boolean mIsHapticFeedbackEnabled; 54 VibratorWrapper(Context context)55 public VibratorWrapper(Context context) { 56 mVibrator = context.getSystemService(Vibrator.class); 57 mHasVibrator = mVibrator.hasVibrator(); 58 if (mHasVibrator) { 59 final ContentResolver resolver = context.getContentResolver(); 60 mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver); 61 final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { 62 @Override 63 public void onChange(boolean selfChange) { 64 mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver); 65 } 66 }; 67 resolver.registerContentObserver(Settings.System.getUriFor(HAPTIC_FEEDBACK_ENABLED), 68 false /* notifyForDescendents */, observer); 69 } else { 70 mIsHapticFeedbackEnabled = false; 71 } 72 } 73 isHapticFeedbackEnabled(ContentResolver resolver)74 private boolean isHapticFeedbackEnabled(ContentResolver resolver) { 75 return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1; 76 } 77 78 /** Vibrates with the given effect if haptic feedback is available and enabled. */ vibrate(VibrationEffect vibrationEffect)79 public void vibrate(VibrationEffect vibrationEffect) { 80 if (mHasVibrator && mIsHapticFeedbackEnabled) { 81 UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect)); 82 } 83 } 84 } 85