1 /* 2 * Copyright (C) 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 com.android.settings.accessibility; 18 19 import static android.provider.Settings.System.KEYBOARD_VIBRATION_ENABLED; 20 21 import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; 22 import static com.android.settings.accessibility.AccessibilityUtil.State.ON; 23 24 import android.app.settings.SettingsEnums; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.database.ContentObserver; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.VibrationAttributes; 31 import android.os.Vibrator; 32 import android.os.vibrator.Flags; 33 import android.provider.Settings; 34 import android.util.Log; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 import androidx.lifecycle.DefaultLifecycleObserver; 39 import androidx.lifecycle.LifecycleOwner; 40 import androidx.preference.Preference; 41 import androidx.preference.PreferenceScreen; 42 import androidx.preference.TwoStatePreference; 43 44 import com.android.settings.R; 45 import com.android.settings.core.TogglePreferenceController; 46 import com.android.settings.overlay.FeatureFactory; 47 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 48 49 50 /** 51 * A preference controller to turn on/off keyboard vibration state with a single toggle. 52 */ 53 public class KeyboardVibrationTogglePreferenceController extends TogglePreferenceController 54 implements DefaultLifecycleObserver { 55 56 private static final String TAG = "KeyboardVibrateControl"; 57 58 private static final Uri MAIN_VIBRATION_SWITCH_URI = 59 Settings.System.getUriFor(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY); 60 61 private final ContentObserver mContentObserver; 62 63 private final Vibrator mVibrator; 64 65 @Nullable 66 private TwoStatePreference mPreference; 67 68 private MetricsFeatureProvider mMetricsFeatureProvider; 69 KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey)70 public KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey) { 71 super(context, preferenceKey); 72 mVibrator = context.getSystemService(Vibrator.class); 73 mContentObserver = new ContentObserver(new Handler(/* async= */ true)) { 74 @Override 75 public void onChange(boolean selfChange, Uri uri) { 76 if (uri.equals(MAIN_VIBRATION_SWITCH_URI)) { 77 updateState(mPreference); 78 } else { 79 Log.w(TAG, "Unexpected uri change:" + uri); 80 } 81 } 82 }; 83 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 84 } 85 86 @Override onStart(@onNull LifecycleOwner owner)87 public void onStart(@NonNull LifecycleOwner owner) { 88 mContext.getContentResolver().registerContentObserver(MAIN_VIBRATION_SWITCH_URI, 89 /* notifyForDescendants= */ false, mContentObserver); 90 } 91 92 @Override onStop(@onNull LifecycleOwner owner)93 public void onStop(@NonNull LifecycleOwner owner) { 94 mContext.getContentResolver().unregisterContentObserver(mContentObserver); 95 } 96 97 @Override displayPreference(PreferenceScreen screen)98 public void displayPreference(PreferenceScreen screen) { 99 super.displayPreference(screen); 100 mPreference = screen.findPreference(getPreferenceKey()); 101 } 102 103 @Override updateState(@ullable Preference preference)104 public void updateState(@Nullable Preference preference) { 105 if (preference != null) { 106 super.updateState(preference); 107 preference.setEnabled(isPreferenceEnabled()); 108 } 109 } 110 111 @Override getAvailabilityStatus()112 public int getAvailabilityStatus() { 113 if (Flags.keyboardCategoryEnabled() 114 && mContext.getResources().getBoolean(R.bool.config_keyboard_vibration_supported) 115 && mContext.getResources().getFloat( 116 com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude) > 0) { 117 return AVAILABLE; 118 } 119 return UNSUPPORTED_ON_DEVICE; 120 } 121 122 @Override isChecked()123 public boolean isChecked() { 124 // Always unchecked if the preference disabled 125 return isPreferenceEnabled() && isKeyboardVibrationSwitchEnabled(); 126 } 127 128 @Override setChecked(boolean isChecked)129 public boolean setChecked(boolean isChecked) { 130 final boolean success = updateKeyboardVibrationSetting(isChecked); 131 mMetricsFeatureProvider.action(mContext, 132 SettingsEnums.ACTION_KEYBOARD_VIBRATION_CHANGED, isChecked); 133 if (success && isChecked) { 134 // Play the preview vibration effect when the toggle is on. 135 final VibrationAttributes touchAttrs = 136 VibrationPreferenceConfig.createPreviewVibrationAttributes( 137 VibrationAttributes.USAGE_TOUCH); 138 final VibrationAttributes keyboardAttrs = 139 new VibrationAttributes.Builder(touchAttrs) 140 .setCategory(VibrationAttributes.CATEGORY_KEYBOARD) 141 .build(); 142 VibrationPreferenceConfig.playVibrationPreview(mVibrator, keyboardAttrs); 143 } 144 return true; 145 } 146 147 @Override getSliceHighlightMenuRes()148 public int getSliceHighlightMenuRes() { 149 return R.string.menu_key_accessibility; 150 } 151 isPreferenceEnabled()152 private boolean isPreferenceEnabled() { 153 return VibrationPreferenceConfig.isMainVibrationSwitchEnabled( 154 mContext.getContentResolver()); 155 } 156 isKeyboardVibrationSwitchEnabled()157 private boolean isKeyboardVibrationSwitchEnabled() { 158 return Settings.System.getInt(mContext.getContentResolver(), KEYBOARD_VIBRATION_ENABLED, 159 mVibrator.isDefaultKeyboardVibrationEnabled() ? ON : OFF) == ON; 160 } 161 updateKeyboardVibrationSetting(boolean enable)162 private boolean updateKeyboardVibrationSetting(boolean enable) { 163 final ContentResolver contentResolver = mContext.getContentResolver(); 164 final boolean success = Settings.System.putInt(contentResolver, 165 KEYBOARD_VIBRATION_ENABLED, enable ? ON : OFF); 166 contentResolver.notifyChange(Settings.System.getUriFor(KEYBOARD_VIBRATION_ENABLED), 167 null /* observer */, ContentResolver.NOTIFY_NO_DELAY); 168 if (!success) { 169 Log.w(TAG, "Update settings database error!"); 170 } 171 return success; 172 } 173 } 174