• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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