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.dialer.app.settings;
18 
19 import android.content.Context;
20 import android.media.RingtoneManager;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.Vibrator;
26 import android.preference.CheckBoxPreference;
27 import android.preference.ListPreference;
28 import android.preference.Preference;
29 import android.preference.PreferenceFragment;
30 import android.preference.PreferenceScreen;
31 import android.provider.Settings;
32 import android.telephony.CarrierConfigManager;
33 import android.telephony.TelephonyManager;
34 import android.widget.Toast;
35 import com.android.dialer.app.R;
36 import com.android.dialer.compat.SdkVersionOverride;
37 import com.android.dialer.util.SettingsUtil;
38 
39 public class SoundSettingsFragment extends PreferenceFragment
40     implements Preference.OnPreferenceChangeListener {
41 
42   private static final int NO_DTMF_TONE = 0;
43   private static final int PLAY_DTMF_TONE = 1;
44 
45   private static final int NO_VIBRATION_FOR_CALLS = 0;
46   private static final int DO_VIBRATION_FOR_CALLS = 1;
47 
48   private static final int DTMF_TONE_TYPE_NORMAL = 0;
49 
50   private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
51 
52   private Preference mRingtonePreference;
53   private final Handler mRingtoneLookupComplete =
54       new Handler() {
55         @Override
56         public void handleMessage(Message msg) {
57           switch (msg.what) {
58             case MSG_UPDATE_RINGTONE_SUMMARY:
59               mRingtonePreference.setSummary((CharSequence) msg.obj);
60               break;
61           }
62         }
63       };
64   private final Runnable mRingtoneLookupRunnable =
65       new Runnable() {
66         @Override
67         public void run() {
68           updateRingtonePreferenceSummary();
69         }
70       };
71   private CheckBoxPreference mVibrateWhenRinging;
72   private CheckBoxPreference mPlayDtmfTone;
73   private ListPreference mDtmfToneLength;
74 
75   @Override
getContext()76   public Context getContext() {
77     return getActivity();
78   }
79 
80   @Override
onCreate(Bundle savedInstanceState)81   public void onCreate(Bundle savedInstanceState) {
82     super.onCreate(savedInstanceState);
83 
84     addPreferencesFromResource(R.xml.sound_settings);
85 
86     Context context = getActivity();
87 
88     mRingtonePreference = findPreference(context.getString(R.string.ringtone_preference_key));
89     mVibrateWhenRinging =
90         (CheckBoxPreference) findPreference(context.getString(R.string.vibrate_on_preference_key));
91     mPlayDtmfTone =
92         (CheckBoxPreference) findPreference(context.getString(R.string.play_dtmf_preference_key));
93     mDtmfToneLength =
94         (ListPreference)
95             findPreference(context.getString(R.string.dtmf_tone_length_preference_key));
96 
97     if (hasVibrator()) {
98       mVibrateWhenRinging.setOnPreferenceChangeListener(this);
99     } else {
100       getPreferenceScreen().removePreference(mVibrateWhenRinging);
101       mVibrateWhenRinging = null;
102     }
103 
104     mPlayDtmfTone.setOnPreferenceChangeListener(this);
105     mPlayDtmfTone.setChecked(shouldPlayDtmfTone());
106 
107     TelephonyManager telephonyManager =
108         (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
109     if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M
110         && telephonyManager.canChangeDtmfToneLength()
111         && (telephonyManager.isWorldPhone() || !shouldHideCarrierSettings())) {
112       mDtmfToneLength.setOnPreferenceChangeListener(this);
113       mDtmfToneLength.setValueIndex(
114           Settings.System.getInt(
115               context.getContentResolver(),
116               Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
117               DTMF_TONE_TYPE_NORMAL));
118     } else {
119       getPreferenceScreen().removePreference(mDtmfToneLength);
120       mDtmfToneLength = null;
121     }
122   }
123 
124   @Override
onResume()125   public void onResume() {
126     super.onResume();
127 
128     if (!Settings.System.canWrite(getContext())) {
129       // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS
130       // AppOp, then close the fragment since there is nothing useful to do.
131       getActivity().onBackPressed();
132       return;
133     }
134 
135     if (mVibrateWhenRinging != null) {
136       mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging());
137     }
138 
139     // Lookup the ringtone name asynchronously.
140     new Thread(mRingtoneLookupRunnable).start();
141   }
142 
143   /**
144    * Supports onPreferenceChangeListener to look for preference changes.
145    *
146    * @param preference The preference to be changed
147    * @param objValue The value of the selection, NOT its localized display value.
148    */
149   @Override
onPreferenceChange(Preference preference, Object objValue)150   public boolean onPreferenceChange(Preference preference, Object objValue) {
151     if (!Settings.System.canWrite(getContext())) {
152       // A user shouldn't be able to get here, but this protects against monkey crashes.
153       Toast.makeText(
154               getContext(),
155               getResources().getString(R.string.toast_cannot_write_system_settings),
156               Toast.LENGTH_SHORT)
157           .show();
158       return true;
159     }
160     if (preference == mVibrateWhenRinging) {
161       boolean doVibrate = (Boolean) objValue;
162       Settings.System.putInt(
163           getActivity().getContentResolver(),
164           Settings.System.VIBRATE_WHEN_RINGING,
165           doVibrate ? DO_VIBRATION_FOR_CALLS : NO_VIBRATION_FOR_CALLS);
166     } else if (preference == mDtmfToneLength) {
167       int index = mDtmfToneLength.findIndexOfValue((String) objValue);
168       Settings.System.putInt(
169           getActivity().getContentResolver(), Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
170     }
171     return true;
172   }
173 
174   /** Click listener for toggle events. */
175   @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)176   public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
177     if (!Settings.System.canWrite(getContext())) {
178       Toast.makeText(
179               getContext(),
180               getResources().getString(R.string.toast_cannot_write_system_settings),
181               Toast.LENGTH_SHORT)
182           .show();
183       return true;
184     }
185     if (preference == mPlayDtmfTone) {
186       Settings.System.putInt(
187           getActivity().getContentResolver(),
188           Settings.System.DTMF_TONE_WHEN_DIALING,
189           mPlayDtmfTone.isChecked() ? PLAY_DTMF_TONE : NO_DTMF_TONE);
190     }
191     return true;
192   }
193 
194   /** Updates the summary text on the ringtone preference with the name of the ringtone. */
updateRingtonePreferenceSummary()195   private void updateRingtonePreferenceSummary() {
196     SettingsUtil.updateRingtoneName(
197         getActivity(),
198         mRingtoneLookupComplete,
199         RingtoneManager.TYPE_RINGTONE,
200         mRingtonePreference.getKey(),
201         MSG_UPDATE_RINGTONE_SUMMARY);
202   }
203 
204   /**
205    * Obtain the value for "vibrate when ringing" setting. The default value is false.
206    *
207    * <p>Watch out: if the setting is missing in the device, this will try obtaining the old "vibrate
208    * on ring" setting from AudioManager, and save the previous setting to the new one.
209    */
shouldVibrateWhenRinging()210   private boolean shouldVibrateWhenRinging() {
211     int vibrateWhenRingingSetting =
212         Settings.System.getInt(
213             getActivity().getContentResolver(),
214             Settings.System.VIBRATE_WHEN_RINGING,
215             NO_VIBRATION_FOR_CALLS);
216     return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS);
217   }
218 
219   /** Obtains the value for dialpad/DTMF tones. The default value is true. */
shouldPlayDtmfTone()220   private boolean shouldPlayDtmfTone() {
221     int dtmfToneSetting =
222         Settings.System.getInt(
223             getActivity().getContentResolver(),
224             Settings.System.DTMF_TONE_WHEN_DIALING,
225             PLAY_DTMF_TONE);
226     return dtmfToneSetting == PLAY_DTMF_TONE;
227   }
228 
229   /** Whether the device hardware has a vibrator. */
hasVibrator()230   private boolean hasVibrator() {
231     Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
232     return vibrator != null && vibrator.hasVibrator();
233   }
234 
shouldHideCarrierSettings()235   private boolean shouldHideCarrierSettings() {
236     CarrierConfigManager configManager =
237         (CarrierConfigManager) getActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE);
238     return configManager
239         .getConfig()
240         .getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);
241   }
242 }
243