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