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