1 /* 2 * Copyright (C) 2014 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.notification; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.UserHandle; 29 import android.preference.SeekBarVolumizer; 30 import android.text.TextUtils; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.preference.ListPreference; 34 import androidx.preference.Preference; 35 36 import com.android.settings.R; 37 import com.android.settings.RingtonePreference; 38 import com.android.settings.core.OnActivityResultListener; 39 import com.android.settings.dashboard.DashboardFragment; 40 import com.android.settings.search.BaseSearchIndexProvider; 41 import com.android.settings.sound.HandsFreeProfileOutputPreferenceController; 42 import com.android.settings.widget.PreferenceCategoryController; 43 import com.android.settingslib.core.AbstractPreferenceController; 44 import com.android.settingslib.core.instrumentation.Instrumentable; 45 import com.android.settingslib.core.lifecycle.Lifecycle; 46 import com.android.settingslib.search.SearchIndexable; 47 import com.android.settingslib.widget.UpdatableListPreferenceDialogFragment; 48 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.List; 52 53 @SearchIndexable 54 public class SoundSettings extends DashboardFragment implements OnActivityResultListener { 55 private static final String TAG = "SoundSettings"; 56 57 private static final String SELECTED_PREFERENCE_KEY = "selected_preference"; 58 private static final int REQUEST_CODE = 200; 59 private static final int SAMPLE_CUTOFF = 2000; // manually cap sample playback at 2 seconds 60 61 @VisibleForTesting 62 static final int STOP_SAMPLE = 1; 63 64 @VisibleForTesting 65 final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback(); 66 @VisibleForTesting 67 final Handler mHandler = new Handler(Looper.getMainLooper()) { 68 @Override 69 public void handleMessage(Message msg) { 70 switch (msg.what) { 71 case STOP_SAMPLE: 72 mVolumeCallback.stopSample(); 73 break; 74 } 75 } 76 }; 77 78 private RingtonePreference mRequestPreference; 79 private UpdatableListPreferenceDialogFragment mDialogFragment; 80 private String mHfpOutputControllerKey; 81 private String mVibrationPreferencesKey = "vibration_preference_screen"; 82 83 @Override getMetricsCategory()84 public int getMetricsCategory() { 85 return SettingsEnums.SOUND; 86 } 87 88 @Override onCreate(Bundle savedInstanceState)89 public void onCreate(Bundle savedInstanceState) { 90 super.onCreate(savedInstanceState); 91 if (savedInstanceState != null) { 92 String selectedPreference = savedInstanceState.getString(SELECTED_PREFERENCE_KEY, null); 93 if (!TextUtils.isEmpty(selectedPreference)) { 94 mRequestPreference = (RingtonePreference) findPreference(selectedPreference); 95 } 96 97 UpdatableListPreferenceDialogFragment dialogFragment = 98 (UpdatableListPreferenceDialogFragment) getFragmentManager() 99 .findFragmentByTag(TAG); 100 mDialogFragment = dialogFragment; 101 } 102 replaceEnterpriseStringTitle("sound_work_settings", 103 WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER, 104 R.string.sound_work_settings); 105 } 106 107 @Override getHelpResource()108 public int getHelpResource() { 109 return R.string.help_url_sound; 110 } 111 112 @Override onPause()113 public void onPause() { 114 super.onPause(); 115 mVolumeCallback.stopSample(); 116 } 117 118 @Override onPreferenceTreeClick(Preference preference)119 public boolean onPreferenceTreeClick(Preference preference) { 120 if (preference instanceof RingtonePreference) { 121 writePreferenceClickMetric(preference); 122 mRequestPreference = (RingtonePreference) preference; 123 mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent()); 124 getActivity().startActivityForResultAsUser( 125 mRequestPreference.getIntent(), 126 REQUEST_CODE, 127 null, 128 UserHandle.of(mRequestPreference.getUserId())); 129 return true; 130 } 131 return super.onPreferenceTreeClick(preference); 132 } 133 134 @Override onDisplayPreferenceDialog(Preference preference)135 public void onDisplayPreferenceDialog(Preference preference) { 136 if (TextUtils.equals(mVibrationPreferencesKey, preference.getKey())) { 137 super.onDisplayPreferenceDialog(preference); 138 return; 139 } 140 final int metricsCategory; 141 if (mHfpOutputControllerKey.equals(preference.getKey())) { 142 metricsCategory = SettingsEnums.DIALOG_SWITCH_HFP_DEVICES; 143 } else { 144 metricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN; 145 } 146 147 mDialogFragment = UpdatableListPreferenceDialogFragment. 148 newInstance(preference.getKey(), metricsCategory); 149 mDialogFragment.setTargetFragment(this, 0); 150 mDialogFragment.show(getFragmentManager(), TAG); 151 } 152 153 @Override getLogTag()154 protected String getLogTag() { 155 return TAG; 156 } 157 158 @Override getPreferenceScreenResId()159 protected int getPreferenceScreenResId() { 160 return R.xml.sound_settings; 161 } 162 163 @Override createPreferenceControllers(Context context)164 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 165 return buildPreferenceControllers(context, this, getSettingsLifecycle()); 166 } 167 168 @Override onActivityResult(int requestCode, int resultCode, Intent data)169 public void onActivityResult(int requestCode, int resultCode, Intent data) { 170 if (mRequestPreference != null) { 171 mRequestPreference.onActivityResult(requestCode, resultCode, data); 172 mRequestPreference = null; 173 } 174 } 175 176 @Override onSaveInstanceState(Bundle outState)177 public void onSaveInstanceState(Bundle outState) { 178 super.onSaveInstanceState(outState); 179 if (mRequestPreference != null) { 180 outState.putString(SELECTED_PREFERENCE_KEY, mRequestPreference.getKey()); 181 } 182 } 183 184 @Override onAttach(Context context)185 public void onAttach(Context context) { 186 super.onAttach(context); 187 ArrayList<VolumeSeekBarPreferenceController> volumeControllers = new ArrayList<>(); 188 volumeControllers.add(use(AlarmVolumePreferenceController.class)); 189 volumeControllers.add(use(MediaVolumePreferenceController.class)); 190 volumeControllers.add(use(RingVolumePreferenceController.class)); 191 volumeControllers.add(use(SeparateRingVolumePreferenceController.class)); 192 volumeControllers.add(use(NotificationVolumePreferenceController.class)); 193 volumeControllers.add(use(CallVolumePreferenceController.class)); 194 195 use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference -> 196 onPreferenceDataChanged(listPreference)); 197 mHfpOutputControllerKey = 198 use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey(); 199 200 for (VolumeSeekBarPreferenceController controller : volumeControllers) { 201 controller.setCallback(mVolumeCallback); 202 getSettingsLifecycle().addObserver(controller); 203 } 204 } 205 206 // === Volumes === 207 208 final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback { 209 private SeekBarVolumizer mCurrent; 210 211 @Override onSampleStarting(SeekBarVolumizer sbv)212 public void onSampleStarting(SeekBarVolumizer sbv) { 213 if (mCurrent != null) { 214 mHandler.removeMessages(STOP_SAMPLE); 215 mHandler.sendEmptyMessageDelayed(STOP_SAMPLE, SAMPLE_CUTOFF); 216 } 217 } 218 219 @Override onStreamValueChanged(int stream, int progress)220 public void onStreamValueChanged(int stream, int progress) { 221 if (mCurrent != null) { 222 mHandler.removeMessages(STOP_SAMPLE); 223 mHandler.sendEmptyMessageDelayed(STOP_SAMPLE, SAMPLE_CUTOFF); 224 } 225 } 226 227 @Override onStartTrackingTouch(SeekBarVolumizer sbv)228 public void onStartTrackingTouch(SeekBarVolumizer sbv) { 229 // stop the ringtone when other seek bar is adjust 230 if (mCurrent != null && mCurrent != sbv) { 231 mCurrent.stopSample(); 232 } 233 mCurrent = sbv; 234 } 235 stopSample()236 public void stopSample() { 237 if (mCurrent != null) { 238 mCurrent.stopSample(); 239 } 240 } 241 } 242 buildPreferenceControllers(Context context, SoundSettings fragment, Lifecycle lifecycle)243 private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, 244 SoundSettings fragment, Lifecycle lifecycle) { 245 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 246 247 // Volumes are added via xml 248 249 // === Phone & notification ringtone === 250 controllers.add(new PhoneRingtonePreferenceController(context)); 251 controllers.add(new AlarmRingtonePreferenceController(context)); 252 controllers.add(new NotificationRingtonePreferenceController(context)); 253 254 // === Other Sound Settings === 255 final DialPadTonePreferenceController dialPadTonePreferenceController = 256 new DialPadTonePreferenceController(context, fragment, lifecycle); 257 final ScreenLockSoundPreferenceController screenLockSoundPreferenceController = 258 new ScreenLockSoundPreferenceController(context, fragment, lifecycle); 259 final ChargingSoundPreferenceController chargingSoundPreferenceController = 260 new ChargingSoundPreferenceController(context, fragment, lifecycle); 261 final DockingSoundPreferenceController dockingSoundPreferenceController = 262 new DockingSoundPreferenceController(context, fragment, lifecycle); 263 final TouchSoundPreferenceController touchSoundPreferenceController = 264 new TouchSoundPreferenceController(context, fragment, lifecycle); 265 final DockAudioMediaPreferenceController dockAudioMediaPreferenceController = 266 new DockAudioMediaPreferenceController(context, fragment, lifecycle); 267 final BootSoundPreferenceController bootSoundPreferenceController = 268 new BootSoundPreferenceController(context); 269 final EmergencyTonePreferenceController emergencyTonePreferenceController = 270 new EmergencyTonePreferenceController(context, fragment, lifecycle); 271 final VibrateIconPreferenceController vibrateIconPreferenceController = 272 new VibrateIconPreferenceController(context, fragment, lifecycle); 273 274 controllers.add(dialPadTonePreferenceController); 275 controllers.add(screenLockSoundPreferenceController); 276 controllers.add(chargingSoundPreferenceController); 277 controllers.add(dockingSoundPreferenceController); 278 controllers.add(touchSoundPreferenceController); 279 controllers.add(vibrateIconPreferenceController); 280 controllers.add(dockAudioMediaPreferenceController); 281 controllers.add(bootSoundPreferenceController); 282 controllers.add(emergencyTonePreferenceController); 283 controllers.add(new PreferenceCategoryController(context, 284 "other_sounds_and_vibrations_category").setChildren( 285 Arrays.asList(dialPadTonePreferenceController, 286 screenLockSoundPreferenceController, 287 chargingSoundPreferenceController, 288 dockingSoundPreferenceController, 289 touchSoundPreferenceController, 290 vibrateIconPreferenceController, 291 dockAudioMediaPreferenceController, 292 bootSoundPreferenceController, 293 emergencyTonePreferenceController))); 294 295 return controllers; 296 } 297 298 // === Indexing === 299 300 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 301 new BaseSearchIndexProvider(R.xml.sound_settings) { 302 303 @Override 304 public List<AbstractPreferenceController> createPreferenceControllers( 305 Context context) { 306 return buildPreferenceControllers(context, null /* fragment */, 307 null /* lifecycle */); 308 } 309 }; 310 onPreferenceDataChanged(ListPreference preference)311 private void onPreferenceDataChanged(ListPreference preference) { 312 if (mDialogFragment != null) { 313 mDialogFragment.onListPreferenceUpdated(preference); 314 } 315 } 316 } 317