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