1 /* 2 * Copyright (C) 2022 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 com.android.settings.accessibility.AccessibilityUtil.State.ON; 20 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.database.ContentObserver; 27 import android.media.AudioManager; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.VibrationAttributes; 31 import android.os.VibrationEffect; 32 import android.os.Vibrator; 33 import android.provider.Settings; 34 35 import androidx.annotation.Nullable; 36 import androidx.preference.Preference; 37 38 import com.android.settings.R; 39 import com.android.settingslib.core.AbstractPreferenceController; 40 41 /** 42 * Vibration intensity settings configuration to be shared between different preference 43 * controllers that handle the same setting key. 44 */ 45 public abstract class VibrationPreferenceConfig { 46 47 /** 48 * SettingsProvider key for the main "Vibration & haptics" toggle preference, that can disable 49 * all device vibrations. 50 */ 51 public static final String MAIN_SWITCH_SETTING_KEY = Settings.System.VIBRATE_ON; 52 private static final VibrationEffect PREVIEW_VIBRATION_EFFECT = 53 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); 54 55 protected final ContentResolver mContentResolver; 56 private final AudioManager mAudioManager; 57 private final Vibrator mVibrator; 58 private final String mSettingKey; 59 private final String mRingerModeSilentSummary; 60 private final int mDefaultIntensity; 61 private final VibrationAttributes mPreviewVibrationAttributes; 62 63 /** Returns true if the user setting for enabling device vibrations is enabled. */ isMainVibrationSwitchEnabled(ContentResolver contentResolver)64 public static boolean isMainVibrationSwitchEnabled(ContentResolver contentResolver) { 65 return Settings.System.getInt(contentResolver, MAIN_SWITCH_SETTING_KEY, ON) == ON; 66 } 67 68 /** Play a vibration effect with intensity just selected by the user. */ playVibrationPreview(Vibrator vibrator, @VibrationAttributes.Usage int vibrationUsage)69 public static void playVibrationPreview(Vibrator vibrator, 70 @VibrationAttributes.Usage int vibrationUsage) { 71 playVibrationPreview(vibrator, createPreviewVibrationAttributes(vibrationUsage)); 72 } 73 74 /** 75 * Play a vibration effect with intensity just selected by the user. 76 * 77 * @param vibrator The {@link Vibrator} used to play the vibration. 78 * @param vibrationAttributes The {@link VibrationAttributes} to indicate the 79 * vibration information. 80 */ playVibrationPreview(Vibrator vibrator, VibrationAttributes vibrationAttributes)81 public static void playVibrationPreview(Vibrator vibrator, 82 VibrationAttributes vibrationAttributes) { 83 vibrator.vibrate(PREVIEW_VIBRATION_EFFECT, vibrationAttributes); 84 } 85 VibrationPreferenceConfig(Context context, String settingKey, @VibrationAttributes.Usage int vibrationUsage)86 public VibrationPreferenceConfig(Context context, String settingKey, 87 @VibrationAttributes.Usage int vibrationUsage) { 88 mContentResolver = context.getContentResolver(); 89 mVibrator = context.getSystemService(Vibrator.class); 90 mAudioManager = context.getSystemService(AudioManager.class); 91 mRingerModeSilentSummary = context.getString( 92 R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary); 93 mSettingKey = settingKey; 94 mDefaultIntensity = mVibrator.getDefaultVibrationIntensity(vibrationUsage); 95 mPreviewVibrationAttributes = createPreviewVibrationAttributes(vibrationUsage); 96 } 97 98 /** Returns the setting key for this setting preference. */ getSettingKey()99 public String getSettingKey() { 100 return mSettingKey; 101 } 102 103 /** Returns the summary string for this setting preference. */ 104 @Nullable getSummary()105 public CharSequence getSummary() { 106 return isRestrictedByRingerModeSilent() && isRingerModeSilent() 107 ? mRingerModeSilentSummary : null; 108 } 109 110 /** Returns true if this setting preference is enabled for user update. */ isPreferenceEnabled()111 public boolean isPreferenceEnabled() { 112 return isMainVibrationSwitchEnabled(mContentResolver) 113 && (!isRestrictedByRingerModeSilent() || !isRingerModeSilent()); 114 } 115 116 /** 117 * Returns true if this setting preference should be disabled when the device is in silent mode. 118 */ isRestrictedByRingerModeSilent()119 public boolean isRestrictedByRingerModeSilent() { 120 return false; 121 } 122 123 /** Returns the default intensity to be displayed when the setting value is not set. */ getDefaultIntensity()124 public int getDefaultIntensity() { 125 return mDefaultIntensity; 126 } 127 128 /** Reads setting value for corresponding {@link VibrationPreferenceConfig} */ readIntensity()129 public int readIntensity() { 130 return Settings.System.getInt(mContentResolver, mSettingKey, mDefaultIntensity); 131 } 132 133 /** Update setting value for corresponding {@link VibrationPreferenceConfig} */ updateIntensity(int intensity)134 public boolean updateIntensity(int intensity) { 135 return Settings.System.putInt(mContentResolver, mSettingKey, intensity); 136 } 137 138 /** Play a vibration effect with intensity just selected by the user. */ playVibrationPreview()139 public void playVibrationPreview() { 140 mVibrator.vibrate(PREVIEW_VIBRATION_EFFECT, mPreviewVibrationAttributes); 141 } 142 isRingerModeSilent()143 private boolean isRingerModeSilent() { 144 // AudioManager.isSilentMode() also returns true when ringer mode is VIBRATE. 145 // The vibration preferences are only disabled when the ringer mode is SILENT. 146 return mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT; 147 } 148 createPreviewVibrationAttributes( @ibrationAttributes.Usage int vibrationUsage)149 static VibrationAttributes createPreviewVibrationAttributes( 150 @VibrationAttributes.Usage int vibrationUsage) { 151 return new VibrationAttributes.Builder() 152 .setUsage(vibrationUsage) 153 .setFlags( 154 // Enforce fresh settings to be applied for the preview vibration, as they 155 // are played immediately after the new user values are set. 156 VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE 157 // Bypass user settings to allow vibration previews to be played while in 158 // limited interruptions' mode, e.g. zen mode. 159 | VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) 160 .build(); 161 } 162 163 /** {@link ContentObserver} for a setting described by a {@link VibrationPreferenceConfig}. */ 164 public static final class SettingObserver extends ContentObserver { 165 private static final Uri MAIN_SWITCH_SETTING_URI = 166 Settings.System.getUriFor(MAIN_SWITCH_SETTING_KEY); 167 private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER = 168 new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); 169 170 private final Uri mUri; 171 @Nullable 172 private final BroadcastReceiver mRingerModeChangeReceiver; 173 174 private AbstractPreferenceController mPreferenceController; 175 private Preference mPreference; 176 177 /** Creates observer for given preference. */ SettingObserver(VibrationPreferenceConfig preferenceConfig)178 public SettingObserver(VibrationPreferenceConfig preferenceConfig) { 179 super(new Handler(/* async= */ true)); 180 mUri = Settings.System.getUriFor(preferenceConfig.getSettingKey()); 181 182 if (preferenceConfig.isRestrictedByRingerModeSilent()) { 183 // If this preference is restricted by AudioManager.getRingerModeInternal() result 184 // for the device mode, then listen to changes in that value using the broadcast 185 // intent action INTERNAL_RINGER_MODE_CHANGED_ACTION. 186 mRingerModeChangeReceiver = new BroadcastReceiver() { 187 @Override 188 public void onReceive(Context context, Intent intent) { 189 final String action = intent.getAction(); 190 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { 191 notifyChange(); 192 } 193 } 194 }; 195 } else { 196 // No need to register a receiver if this preference is not affected by ringer mode. 197 mRingerModeChangeReceiver = null; 198 } 199 } 200 201 @Override onChange(boolean selfChange, Uri uri)202 public void onChange(boolean selfChange, Uri uri) { 203 if (mUri.equals(uri) || MAIN_SWITCH_SETTING_URI.equals(uri)) { 204 notifyChange(); 205 } 206 } 207 notifyChange()208 private void notifyChange() { 209 if (mPreferenceController != null && mPreference != null) { 210 mPreferenceController.updateState(mPreference); 211 } 212 } 213 214 /** 215 * Register this observer to given {@link Context}, to be called from lifecycle 216 * {@code onStart} method. 217 */ register(Context context)218 public void register(Context context) { 219 if (mRingerModeChangeReceiver != null) { 220 context.registerReceiver(mRingerModeChangeReceiver, 221 INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER); 222 } 223 context.getContentResolver().registerContentObserver( 224 mUri, /* notifyForDescendants= */ false, this); 225 context.getContentResolver().registerContentObserver( 226 MAIN_SWITCH_SETTING_URI, /* notifyForDescendants= */ false, this); 227 } 228 229 /** 230 * Unregister this observer from given {@link Context}, to be called from lifecycle 231 * {@code onStop} method. 232 */ unregister(Context context)233 public void unregister(Context context) { 234 if (mRingerModeChangeReceiver != null) { 235 context.unregisterReceiver(mRingerModeChangeReceiver); 236 } 237 context.getContentResolver().unregisterContentObserver(this); 238 } 239 240 /** 241 * Binds this observer to given controller and preference, once it has been displayed to the 242 * user. 243 */ onDisplayPreference(AbstractPreferenceController controller, Preference preference)244 public void onDisplayPreference(AbstractPreferenceController controller, 245 Preference preference) { 246 mPreferenceController = controller; 247 mPreference = preference; 248 } 249 } 250 } 251