• 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.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         vibrator.vibrate(PREVIEW_VIBRATION_EFFECT,
72                 createPreviewVibrationAttributes(vibrationUsage));
73     }
74 
VibrationPreferenceConfig(Context context, String settingKey, @VibrationAttributes.Usage int vibrationUsage)75     public VibrationPreferenceConfig(Context context, String settingKey,
76             @VibrationAttributes.Usage int vibrationUsage) {
77         mContentResolver = context.getContentResolver();
78         mVibrator = context.getSystemService(Vibrator.class);
79         mAudioManager = context.getSystemService(AudioManager.class);
80         mRingerModeSilentSummary = context.getString(
81                 R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary);
82         mSettingKey = settingKey;
83         mDefaultIntensity = mVibrator.getDefaultVibrationIntensity(vibrationUsage);
84         mPreviewVibrationAttributes = createPreviewVibrationAttributes(vibrationUsage);
85     }
86 
87     /** Returns the setting key for this setting preference. */
getSettingKey()88     public String getSettingKey() {
89         return mSettingKey;
90     }
91 
92     /** Returns the summary string for this setting preference. */
93     @Nullable
getSummary()94     public CharSequence getSummary() {
95         return isRestrictedByRingerModeSilent() && isRingerModeSilent()
96                 ? mRingerModeSilentSummary : null;
97     }
98 
99     /** Returns true if this setting preference is enabled for user update. */
isPreferenceEnabled()100     public boolean isPreferenceEnabled() {
101         return isMainVibrationSwitchEnabled(mContentResolver)
102                 && (!isRestrictedByRingerModeSilent() || !isRingerModeSilent());
103     }
104 
105     /**
106      * Returns true if this setting preference should be disabled when the device is in silent mode.
107      */
isRestrictedByRingerModeSilent()108     public boolean isRestrictedByRingerModeSilent() {
109         return false;
110     }
111 
112     /** Returns the default intensity to be displayed when the setting value is not set. */
getDefaultIntensity()113     public int getDefaultIntensity() {
114         return mDefaultIntensity;
115     }
116 
117     /** Reads setting value for corresponding {@link VibrationPreferenceConfig} */
readIntensity()118     public int readIntensity() {
119         return Settings.System.getInt(mContentResolver, mSettingKey, mDefaultIntensity);
120     }
121 
122     /** Update setting value for corresponding {@link VibrationPreferenceConfig} */
updateIntensity(int intensity)123     public boolean updateIntensity(int intensity) {
124         return Settings.System.putInt(mContentResolver, mSettingKey, intensity);
125     }
126 
127     /** Play a vibration effect with intensity just selected by the user. */
playVibrationPreview()128     public void playVibrationPreview() {
129         mVibrator.vibrate(PREVIEW_VIBRATION_EFFECT, mPreviewVibrationAttributes);
130     }
131 
isRingerModeSilent()132     private boolean isRingerModeSilent() {
133         // AudioManager.isSilentMode() also returns true when ringer mode is VIBRATE.
134         // The vibration preferences are only disabled when the ringer mode is SILENT.
135         return mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT;
136     }
137 
createPreviewVibrationAttributes( @ibrationAttributes.Usage int vibrationUsage)138     private static VibrationAttributes createPreviewVibrationAttributes(
139             @VibrationAttributes.Usage int vibrationUsage) {
140         return new VibrationAttributes.Builder()
141                 .setUsage(vibrationUsage)
142                 // Enforce fresh settings to be applied for the preview vibration, as they
143                 // are played immediately after the new user values are set.
144                 .setFlags(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)
145                 .build();
146     }
147 
148     /** {@link ContentObserver} for a setting described by a {@link VibrationPreferenceConfig}. */
149     public static final class SettingObserver extends ContentObserver {
150         private static final Uri MAIN_SWITCH_SETTING_URI =
151                 Settings.System.getUriFor(MAIN_SWITCH_SETTING_KEY);
152         private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
153                 new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
154 
155         private final Uri mUri;
156         @Nullable
157         private final BroadcastReceiver mRingerModeChangeReceiver;
158 
159         private AbstractPreferenceController mPreferenceController;
160         private Preference mPreference;
161 
162         /** Creates observer for given preference. */
SettingObserver(VibrationPreferenceConfig preferenceConfig)163         public SettingObserver(VibrationPreferenceConfig preferenceConfig) {
164             super(new Handler(/* async= */ true));
165             mUri = Settings.System.getUriFor(preferenceConfig.getSettingKey());
166 
167             if (preferenceConfig.isRestrictedByRingerModeSilent()) {
168                 // If this preference is restricted by AudioManager.getRingerModeInternal() result
169                 // for the device mode, then listen to changes in that value using the broadcast
170                 // intent action INTERNAL_RINGER_MODE_CHANGED_ACTION.
171                 mRingerModeChangeReceiver = new BroadcastReceiver() {
172                     @Override
173                     public void onReceive(Context context, Intent intent) {
174                         final String action = intent.getAction();
175                         if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
176                             notifyChange();
177                         }
178                     }
179                 };
180             } else {
181                 // No need to register a receiver if this preference is not affected by ringer mode.
182                 mRingerModeChangeReceiver = null;
183             }
184         }
185 
186         @Override
onChange(boolean selfChange, Uri uri)187         public void onChange(boolean selfChange, Uri uri) {
188             if (mUri.equals(uri) || MAIN_SWITCH_SETTING_URI.equals(uri)) {
189                 notifyChange();
190             }
191         }
192 
notifyChange()193         private void notifyChange() {
194             if (mPreferenceController != null && mPreference != null) {
195                 mPreferenceController.updateState(mPreference);
196             }
197         }
198 
199         /**
200          * Register this observer to given {@link Context}, to be called from lifecycle
201          * {@code onStart} method.
202          */
register(Context context)203         public void register(Context context) {
204             if (mRingerModeChangeReceiver != null) {
205                 context.registerReceiver(mRingerModeChangeReceiver,
206                         INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
207             }
208             context.getContentResolver().registerContentObserver(
209                     mUri, /* notifyForDescendants= */ false, this);
210             context.getContentResolver().registerContentObserver(
211                     MAIN_SWITCH_SETTING_URI, /* notifyForDescendants= */ false, this);
212         }
213 
214         /**
215          * Unregister this observer from given {@link Context}, to be called from lifecycle
216          * {@code onStop} method.
217          */
unregister(Context context)218         public void unregister(Context context) {
219             if (mRingerModeChangeReceiver != null) {
220                 context.unregisterReceiver(mRingerModeChangeReceiver);
221             }
222             context.getContentResolver().unregisterContentObserver(this);
223         }
224 
225         /**
226          * Binds this observer to given controller and preference, once it has been displayed to the
227          * user.
228          */
onDisplayPreference(AbstractPreferenceController controller, Preference preference)229         public void onDisplayPreference(AbstractPreferenceController controller,
230                 Preference preference) {
231             mPreferenceController = controller;
232             mPreference = preference;
233         }
234     }
235 }
236