1 /* 2 * Copyright (C) 2018 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.settings.accessibility; 17 18 import static android.os.Vibrator.VibrationIntensity; 19 20 import android.support.annotation.VisibleForTesting; 21 import android.content.Context; 22 import android.database.ContentObserver; 23 import android.graphics.drawable.Drawable; 24 import android.media.AudioAttributes; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.VibrationEffect; 28 import android.os.Vibrator; 29 import android.provider.Settings; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 import com.android.internal.accessibility.AccessibilityShortcutController; 34 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 35 import com.android.settings.R; 36 import com.android.settings.widget.RadioButtonPickerFragment; 37 import com.android.settingslib.widget.CandidateInfo; 38 39 import java.util.ArrayList; 40 import java.util.Comparator; 41 import java.util.List; 42 import java.util.Map; 43 44 /** 45 * Fragment for changing vibration settings. 46 */ 47 public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragment { 48 private static final String TAG = "VibrationPreferenceFragment"; 49 50 @VisibleForTesting 51 final static String KEY_INTENSITY_OFF = "intensity_off"; 52 @VisibleForTesting 53 final static String KEY_INTENSITY_LOW = "intensity_low"; 54 @VisibleForTesting 55 final static String KEY_INTENSITY_MEDIUM = "intensity_medium"; 56 @VisibleForTesting 57 final static String KEY_INTENSITY_HIGH = "intensity_high"; 58 // KEY_INTENSITY_ON is only used when the device doesn't support multiple intensity levels. 59 @VisibleForTesting 60 final static String KEY_INTENSITY_ON = "intensity_on"; 61 62 private final Map<String, VibrationIntensityCandidateInfo> mCandidates; 63 private final SettingsObserver mSettingsObserver; 64 VibrationPreferenceFragment()65 public VibrationPreferenceFragment() { 66 mCandidates = new ArrayMap<>(); 67 mSettingsObserver = new SettingsObserver(); 68 } 69 70 @Override onAttach(Context context)71 public void onAttach(Context context) { 72 super.onAttach(context); 73 mSettingsObserver.register(); 74 if (mCandidates.isEmpty()) { 75 loadCandidates(context); 76 } 77 } 78 loadCandidates(Context context)79 private void loadCandidates(Context context) { 80 final boolean supportsMultipleIntensities = context.getResources().getBoolean( 81 R.bool.config_vibration_supports_multiple_intensities); 82 if (supportsMultipleIntensities) { 83 mCandidates.put(KEY_INTENSITY_OFF, 84 new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, 85 R.string.accessibility_vibration_intensity_off, 86 Vibrator.VIBRATION_INTENSITY_OFF)); 87 mCandidates.put(KEY_INTENSITY_LOW, 88 new VibrationIntensityCandidateInfo(KEY_INTENSITY_LOW, 89 R.string.accessibility_vibration_intensity_low, 90 Vibrator.VIBRATION_INTENSITY_LOW)); 91 mCandidates.put(KEY_INTENSITY_MEDIUM, 92 new VibrationIntensityCandidateInfo(KEY_INTENSITY_MEDIUM, 93 R.string.accessibility_vibration_intensity_medium, 94 Vibrator.VIBRATION_INTENSITY_MEDIUM)); 95 mCandidates.put(KEY_INTENSITY_HIGH, 96 new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH, 97 R.string.accessibility_vibration_intensity_high, 98 Vibrator.VIBRATION_INTENSITY_HIGH)); 99 } else { 100 mCandidates.put(KEY_INTENSITY_OFF, 101 new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF, 102 R.string.switch_off_text, Vibrator.VIBRATION_INTENSITY_OFF)); 103 mCandidates.put(KEY_INTENSITY_ON, 104 new VibrationIntensityCandidateInfo(KEY_INTENSITY_ON, 105 R.string.switch_on_text, getDefaultVibrationIntensity())); 106 } 107 } 108 109 @Override onDetach()110 public void onDetach() { 111 super.onDetach(); 112 mSettingsObserver.unregister(); 113 } 114 115 /** 116 * Get the setting string of the vibration intensity setting this preference is dealing with. 117 */ getVibrationIntensitySetting()118 protected abstract String getVibrationIntensitySetting(); 119 120 /** 121 * Get the default intensity for the desired setting. 122 */ getDefaultVibrationIntensity()123 protected abstract int getDefaultVibrationIntensity(); 124 125 /** 126 * When a new vibration intensity is selected by the user. 127 */ onVibrationIntensitySelected(int intensity)128 protected void onVibrationIntensitySelected(int intensity) { } 129 130 /** 131 * Play a vibration effect with intensity just selected by user 132 */ playVibrationPreview()133 protected void playVibrationPreview() { 134 Vibrator vibrator = getContext().getSystemService(Vibrator.class); 135 VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); 136 AudioAttributes.Builder builder = new AudioAttributes.Builder(); 137 builder.setUsage(getPreviewVibrationAudioAttributesUsage()); 138 vibrator.vibrate(effect, builder.build()); 139 } 140 141 /** 142 * Get the AudioAttributes usage for vibration preview. 143 */ getPreviewVibrationAudioAttributesUsage()144 protected int getPreviewVibrationAudioAttributesUsage() { 145 return AudioAttributes.USAGE_UNKNOWN; 146 } 147 148 @Override getCandidates()149 protected List<? extends CandidateInfo> getCandidates() { 150 List<VibrationIntensityCandidateInfo> candidates = new ArrayList<>(mCandidates.values()); 151 candidates.sort( 152 Comparator.comparing(VibrationIntensityCandidateInfo::getIntensity).reversed()); 153 return candidates; 154 } 155 156 @Override getDefaultKey()157 protected String getDefaultKey() { 158 final int vibrationIntensity = Settings.System.getInt(getContext().getContentResolver(), 159 getVibrationIntensitySetting(), getDefaultVibrationIntensity()); 160 for (VibrationIntensityCandidateInfo candidate : mCandidates.values()) { 161 final boolean matchesIntensity = candidate.getIntensity() == vibrationIntensity; 162 final boolean matchesOn = candidate.getKey().equals(KEY_INTENSITY_ON) 163 && vibrationIntensity != Vibrator.VIBRATION_INTENSITY_OFF; 164 if (matchesIntensity || matchesOn) { 165 return candidate.getKey(); 166 } 167 } 168 return null; 169 } 170 171 @Override setDefaultKey(String key)172 protected boolean setDefaultKey(String key) { 173 VibrationIntensityCandidateInfo candidate = mCandidates.get(key); 174 if (candidate == null) { 175 Log.e(TAG, "Tried to set unknown intensity (key=" + key + ")!"); 176 return false; 177 } 178 Settings.System.putInt(getContext().getContentResolver(), 179 getVibrationIntensitySetting(), candidate.getIntensity()); 180 onVibrationIntensitySelected(candidate.getIntensity()); 181 return true; 182 } 183 184 @VisibleForTesting 185 class VibrationIntensityCandidateInfo extends CandidateInfo { 186 private String mKey; 187 private int mLabelId; 188 @VibrationIntensity 189 private int mIntensity; 190 VibrationIntensityCandidateInfo(String key, int labelId, int intensity)191 public VibrationIntensityCandidateInfo(String key, int labelId, int intensity) { 192 super(true /* enabled */); 193 mKey = key; 194 mLabelId = labelId; 195 mIntensity = intensity; 196 } 197 198 @Override loadLabel()199 public CharSequence loadLabel() { 200 return getContext().getString(mLabelId); 201 } 202 203 @Override loadIcon()204 public Drawable loadIcon() { 205 return null; 206 } 207 208 @Override getKey()209 public String getKey() { 210 return mKey; 211 } 212 getIntensity()213 public int getIntensity() { 214 return mIntensity; 215 } 216 } 217 218 private class SettingsObserver extends ContentObserver { SettingsObserver()219 public SettingsObserver() { 220 super(new Handler()); 221 } 222 register()223 public void register() { 224 getContext().getContentResolver().registerContentObserver( 225 Settings.System.getUriFor(getVibrationIntensitySetting()), false, this); 226 } 227 unregister()228 public void unregister() { 229 getContext().getContentResolver().unregisterContentObserver(this); 230 } 231 232 @Override onChange(boolean selfChange, Uri uri)233 public void onChange(boolean selfChange, Uri uri) { 234 updateCandidates(); 235 playVibrationPreview(); 236 } 237 } 238 } 239