1 /*
2  * Copyright (C) 2013 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 package com.android.dialer.app.settings;
17 
18 import android.content.Context;
19 import android.content.Intent;
20 import android.content.SharedPreferences;
21 import android.net.Uri;
22 import android.os.Build.VERSION;
23 import android.os.Build.VERSION_CODES;
24 import android.os.Bundle;
25 import android.os.UserManager;
26 import android.preference.PreferenceManager;
27 import android.provider.Settings;
28 import android.support.annotation.Nullable;
29 import android.telecom.PhoneAccount;
30 import android.telecom.PhoneAccountHandle;
31 import android.telecom.TelecomManager;
32 import android.telephony.TelephonyManager;
33 import android.view.MenuItem;
34 import android.widget.Toast;
35 import com.android.dialer.about.AboutPhoneFragment;
36 import com.android.dialer.app.R;
37 import com.android.dialer.assisteddialing.ConcreteCreator;
38 import com.android.dialer.blocking.FilteredNumberCompat;
39 import com.android.dialer.common.LogUtil;
40 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
41 import com.android.dialer.configprovider.ConfigProviderComponent;
42 import com.android.dialer.proguard.UsedByReflection;
43 import com.android.dialer.util.PermissionsUtil;
44 import com.android.dialer.voicemail.settings.VoicemailSettingsFragment;
45 import com.android.voicemail.VoicemailClient;
46 import java.util.List;
47 
48 /** Activity for dialer settings. */
49 @SuppressWarnings("FragmentInjection") // Activity not exported
50 @UsedByReflection(value = "AndroidManifest-app.xml")
51 public class DialerSettingsActivity extends AppCompatPreferenceActivity {
52 
53   protected SharedPreferences preferences;
54   private boolean migrationStatusOnBuildHeaders;
55   private List<Header> headers;
56 
57   @Override
onCreate(Bundle savedInstanceState)58   protected void onCreate(Bundle savedInstanceState) {
59     LogUtil.enterBlock("DialerSettingsActivity.onCreate");
60     super.onCreate(savedInstanceState);
61     preferences = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
62 
63     Intent intent = getIntent();
64     Uri data = intent.getData();
65     if (data != null) {
66       String headerToOpen = data.getSchemeSpecificPart();
67       if (headerToOpen != null && headers != null) {
68         for (Header header : headers) {
69           if (headerToOpen.equals(header.fragment)) {
70             LogUtil.i("DialerSettingsActivity.onCreate", "switching to header: " + headerToOpen);
71             switchToHeader(header);
72             break;
73           }
74         }
75       }
76     }
77   }
78 
79   @Override
onResume()80   protected void onResume() {
81     super.onResume();
82     /*
83      * The blockedCallsHeader need to be recreated if the migration status changed because
84      * the intent needs to be updated.
85      */
86     if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking(this)) {
87       invalidateHeaders();
88     }
89   }
90 
91   @Override
onBuildHeaders(List<Header> target)92   public void onBuildHeaders(List<Header> target) {
93     // Keep a reference to the list of headers (since PreferenceActivity.getHeaders() is @Hide)
94     headers = target;
95 
96     if (showDisplayOptions()) {
97       Header displayOptionsHeader = new Header();
98       displayOptionsHeader.titleRes = R.string.display_options_title;
99       displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
100       target.add(displayOptionsHeader);
101     }
102 
103     Header soundSettingsHeader = new Header();
104     soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
105     soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
106     target.add(soundSettingsHeader);
107 
108     Header quickResponseSettingsHeader = new Header();
109     Intent quickResponseSettingsIntent =
110         new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
111     quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
112     quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
113     target.add(quickResponseSettingsHeader);
114 
115     TelephonyManager telephonyManager =
116         (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
117 
118     // "Call Settings" (full settings) is shown if the current user is primary user and there
119     // is only one SIM. Otherwise, "Calling accounts" is shown.
120     boolean isPrimaryUser = isPrimaryUser();
121     if (isPrimaryUser && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
122       Header callSettingsHeader = new Header();
123       Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
124       callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
125 
126       callSettingsHeader.titleRes = R.string.call_settings_label;
127       callSettingsHeader.intent = callSettingsIntent;
128       target.add(callSettingsHeader);
129     } else {
130       Header phoneAccountSettingsHeader = new Header();
131       Intent phoneAccountSettingsIntent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
132       phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
133 
134       phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
135       phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
136       target.add(phoneAccountSettingsHeader);
137     }
138     if (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) {
139       Header blockedCallsHeader = new Header();
140       blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
141       blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
142       target.add(blockedCallsHeader);
143       migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking(this);
144     }
145 
146     addVoicemailSettings(target, isPrimaryUser);
147 
148     if (isPrimaryUser
149         && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
150             || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
151       Header accessibilitySettingsHeader = new Header();
152       Intent accessibilitySettingsIntent =
153           new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
154       accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
155       accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
156       target.add(accessibilitySettingsHeader);
157     }
158 
159     boolean isAssistedDialingEnabled =
160         ConcreteCreator.isAssistedDialingEnabled(
161             ConfigProviderComponent.get(getApplicationContext()).getConfigProvider());
162     LogUtil.i(
163         "DialerSettingsActivity.onBuildHeaders",
164         "showing assisted dialing header: " + isAssistedDialingEnabled);
165     if (isAssistedDialingEnabled) {
166 
167       Header assistedDialingSettingsHeader = new Header();
168       assistedDialingSettingsHeader.titleRes =
169           com.android.dialer.assisteddialing.ui.R.string.assisted_dialing_setting_title;
170       assistedDialingSettingsHeader.intent =
171           new Intent("com.android.dialer.app.settings.SHOW_ASSISTED_DIALING_SETTINGS");
172       target.add(assistedDialingSettingsHeader);
173     }
174 
175     if (showAbout()) {
176       Header aboutPhoneHeader = new Header();
177       aboutPhoneHeader.titleRes = R.string.about_phone_label;
178       aboutPhoneHeader.fragment = AboutPhoneFragment.class.getName();
179       target.add(aboutPhoneHeader);
180     }
181   }
182 
addVoicemailSettings(List<Header> target, boolean isPrimaryUser)183   private void addVoicemailSettings(List<Header> target, boolean isPrimaryUser) {
184     if (!isPrimaryUser) {
185       LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "user not primary user");
186       return;
187     }
188     if (VERSION.SDK_INT < VERSION_CODES.O) {
189       LogUtil.i(
190           "DialerSettingsActivity.addVoicemailSettings",
191           "Dialer voicemail settings not supported by system");
192       return;
193     }
194 
195     if (!PermissionsUtil.hasReadPhoneStatePermissions(this)) {
196       LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "Missing READ_PHONE_STATE");
197       return;
198     }
199 
200     LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "adding voicemail settings");
201     Header voicemailSettings = new Header();
202     voicemailSettings.titleRes = R.string.voicemail_settings_label;
203     PhoneAccountHandle soleAccount = getSoleSimAccount();
204     if (soleAccount == null) {
205       LogUtil.i(
206           "DialerSettingsActivity.addVoicemailSettings", "showing multi-SIM voicemail settings");
207       voicemailSettings.fragment = PhoneAccountSelectionFragment.class.getName();
208       Bundle bundle = new Bundle();
209       bundle.putString(
210           PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT,
211           VoicemailSettingsFragment.class.getName());
212       bundle.putString(
213           PhoneAccountSelectionFragment.PARAM_PHONE_ACCOUNT_HANDLE_KEY,
214           VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
215       bundle.putBundle(PhoneAccountSelectionFragment.PARAM_ARGUMENTS, new Bundle());
216       bundle.putInt(
217           PhoneAccountSelectionFragment.PARAM_TARGET_TITLE_RES, R.string.voicemail_settings_label);
218       voicemailSettings.fragmentArguments = bundle;
219       target.add(voicemailSettings);
220     } else {
221       LogUtil.i(
222           "DialerSettingsActivity.addVoicemailSettings", "showing single-SIM voicemail settings");
223       voicemailSettings.fragment = VoicemailSettingsFragment.class.getName();
224       Bundle bundle = new Bundle();
225       bundle.putParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, soleAccount);
226       voicemailSettings.fragmentArguments = bundle;
227       target.add(voicemailSettings);
228     }
229   }
230 
231   /**
232    * @return the only SIM phone account, or {@code null} if there are none or more than one. Note:
233    *     having a empty SIM slot still count as a PhoneAccountHandle that is "invalid", and
234    *     voicemail settings should still be available for it.
235    */
236   @Nullable
getSoleSimAccount()237   private PhoneAccountHandle getSoleSimAccount() {
238     TelecomManager telecomManager = getSystemService(TelecomManager.class);
239     PhoneAccountHandle result = null;
240     for (PhoneAccountHandle phoneAccountHandle : telecomManager.getCallCapablePhoneAccounts()) {
241       PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
242       if (phoneAccount == null) {
243         continue;
244       }
245       if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
246         LogUtil.i(
247             "DialerSettingsActivity.getSoleSimAccount", phoneAccountHandle + " is a SIM account");
248         if (result != null) {
249           return null;
250         }
251         result = phoneAccountHandle;
252       }
253     }
254     return result;
255   }
256 
257   /** Whether "about" should be shown in settings. Override to hide about. */
showAbout()258   public boolean showAbout() {
259     return true;
260   }
261 
262   /**
263    * Returns {@code true} or {@code false} based on whether the display options setting should be
264    * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful since
265    * contacts are sorted and displayed family name first by default.
266    *
267    * @return {@code true} if the display options should be shown, {@code false} otherwise.
268    */
showDisplayOptions()269   private boolean showDisplayOptions() {
270     return getResources().getBoolean(R.bool.config_display_order_user_changeable)
271         && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
272   }
273 
274   /**
275    * For the "sounds and vibration" setting, we go directly to the system sound settings fragment.
276    * This helps since:
277    * <li>We don't need a separate Dialer sounds and vibrations fragment, as everything we need is
278    *     present in the system sounds fragment.
279    * <li>OEM's e.g Moto that support dual sim ring-tones no longer need to update the dialer sound
280    *     and settings fragment.
281    *
282    *     <p>For all other settings, we launch our our preferences fragment.
283    */
284   @Override
onHeaderClick(Header header, int position)285   public void onHeaderClick(Header header, int position) {
286     if (header.id == R.id.settings_header_sounds_and_vibration) {
287 
288       if (!Settings.System.canWrite(this)) {
289         Toast.makeText(
290                 this,
291                 getResources().getString(R.string.toast_cannot_write_system_settings),
292                 Toast.LENGTH_SHORT)
293             .show();
294       }
295 
296       startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
297       return;
298     }
299 
300     super.onHeaderClick(header, position);
301   }
302 
303   @Override
onOptionsItemSelected(MenuItem item)304   public boolean onOptionsItemSelected(MenuItem item) {
305     if (item.getItemId() == android.R.id.home) {
306       onBackPressed();
307       return true;
308     }
309     return false;
310   }
311 
312   @Override
onBackPressed()313   public void onBackPressed() {
314     if (!isSafeToCommitTransactions()) {
315       return;
316     }
317     super.onBackPressed();
318   }
319 
320   @Override
isValidFragment(String fragmentName)321   protected boolean isValidFragment(String fragmentName) {
322     return true;
323   }
324 
325   /** @return Whether the current user is the primary user. */
isPrimaryUser()326   private boolean isPrimaryUser() {
327     return getSystemService(UserManager.class).isSystemUser();
328   }
329 }
330