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.os.Build.VERSION;
22 import android.os.Build.VERSION_CODES;
23 import android.os.Bundle;
24 import android.os.UserManager;
25 import android.preference.PreferenceManager;
26 import android.provider.Settings;
27 import android.support.annotation.Nullable;
28 import android.telecom.PhoneAccount;
29 import android.telecom.PhoneAccountHandle;
30 import android.telecom.TelecomManager;
31 import android.telephony.TelephonyManager;
32 import android.view.MenuItem;
33 import android.widget.Toast;
34 import com.android.contacts.common.compat.TelephonyManagerCompat;
35 import com.android.dialer.about.AboutPhoneFragment;
36 import com.android.dialer.app.R;
37 import com.android.dialer.blocking.FilteredNumberCompat;
38 import com.android.dialer.common.LogUtil;
39 import com.android.dialer.compat.CompatUtils;
40 import com.android.dialer.proguard.UsedByReflection;
41 import com.android.voicemail.VoicemailClient;
42 import com.android.voicemail.VoicemailComponent;
43 import java.util.List;
44 
45 /** Activity for dialer settings. */
46 @SuppressWarnings("FragmentInjection") // Activity not exported
47 @UsedByReflection(value = "AndroidManifest-app.xml")
48 public class DialerSettingsActivity extends AppCompatPreferenceActivity {
49 
50   protected SharedPreferences mPreferences;
51   private boolean migrationStatusOnBuildHeaders;
52 
53   @Override
onCreate(Bundle savedInstanceState)54   protected void onCreate(Bundle savedInstanceState) {
55     super.onCreate(savedInstanceState);
56     mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
57   }
58 
59   @Override
onResume()60   protected void onResume() {
61     super.onResume();
62     /*
63      * The blockedCallsHeader need to be recreated if the migration status changed because
64      * the intent needs to be updated.
65      */
66     if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking(this)) {
67       invalidateHeaders();
68     }
69   }
70 
71   @Override
onBuildHeaders(List<Header> target)72   public void onBuildHeaders(List<Header> target) {
73     if (showDisplayOptions()) {
74       Header displayOptionsHeader = new Header();
75       displayOptionsHeader.titleRes = R.string.display_options_title;
76       displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
77       target.add(displayOptionsHeader);
78     }
79 
80     Header soundSettingsHeader = new Header();
81     soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
82     soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
83     soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
84     target.add(soundSettingsHeader);
85 
86     if (CompatUtils.isMarshmallowCompatible()) {
87       Header quickResponseSettingsHeader = new Header();
88       Intent quickResponseSettingsIntent =
89           new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
90       quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
91       quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
92       target.add(quickResponseSettingsHeader);
93     }
94 
95     TelephonyManager telephonyManager =
96         (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
97 
98     // "Call Settings" (full settings) is shown if the current user is primary user and there
99     // is only one SIM. Before N, "Calling accounts" setting is shown if the current user is
100     // primary user and there are multiple SIMs. In N+, "Calling accounts" is shown whenever
101     // "Call Settings" is not shown.
102     boolean isPrimaryUser = isPrimaryUser();
103     if (isPrimaryUser && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
104       Header callSettingsHeader = new Header();
105       Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
106       callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
107 
108       callSettingsHeader.titleRes = R.string.call_settings_label;
109       callSettingsHeader.intent = callSettingsIntent;
110       target.add(callSettingsHeader);
111     } else if ((VERSION.SDK_INT >= VERSION_CODES.N) || isPrimaryUser) {
112       Header phoneAccountSettingsHeader = new Header();
113       Intent phoneAccountSettingsIntent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
114       phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
115 
116       phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
117       phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
118       target.add(phoneAccountSettingsHeader);
119     }
120     if (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) {
121       Header blockedCallsHeader = new Header();
122       blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
123       blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
124       target.add(blockedCallsHeader);
125       migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking(this);
126     }
127 
128     addVoicemailSettings(target, isPrimaryUser);
129 
130     if (isPrimaryUser
131         && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
132             || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
133       Header accessibilitySettingsHeader = new Header();
134       Intent accessibilitySettingsIntent =
135           new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
136       accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
137       accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
138       target.add(accessibilitySettingsHeader);
139     }
140 
141     Header aboutPhoneHeader = new Header();
142     aboutPhoneHeader.titleRes = R.string.about_phone_label;
143     aboutPhoneHeader.fragment = AboutPhoneFragment.class.getName();
144     target.add(aboutPhoneHeader);
145   }
146 
addVoicemailSettings(List<Header> target, boolean isPrimaryUser)147   private void addVoicemailSettings(List<Header> target, boolean isPrimaryUser) {
148     if (!isPrimaryUser) {
149       LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "user not primary user");
150       return;
151     }
152     String voicemailSettingsFragment =
153         VoicemailComponent.get(this).getVoicemailClient().getSettingsFragment();
154     if (voicemailSettingsFragment == null) {
155       LogUtil.i(
156           "DialerSettingsActivity.addVoicemailSettings",
157           "VoicemailClient does not provide settings");
158       return;
159     }
160 
161     LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "adding voicemail settings");
162     Header voicemailSettings = new Header();
163     voicemailSettings.titleRes = R.string.voicemail_settings_label;
164     PhoneAccountHandle soleAccount = getSoleSimAccount();
165     if (soleAccount == null) {
166       LogUtil.i(
167           "DialerSettingsActivity.addVoicemailSettings", "showing multi-SIM voicemail settings");
168       voicemailSettings.fragment = PhoneAccountSelectionFragment.class.getName();
169       Bundle bundle = new Bundle();
170       bundle.putString(
171           PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT, voicemailSettingsFragment);
172       bundle.putString(
173           PhoneAccountSelectionFragment.PARAM_PHONE_ACCOUNT_HANDLE_KEY,
174           VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
175       bundle.putBundle(PhoneAccountSelectionFragment.PARAM_ARGUMENTS, new Bundle());
176       bundle.putInt(
177           PhoneAccountSelectionFragment.PARAM_TARGET_TITLE_RES, R.string.voicemail_settings_label);
178       voicemailSettings.fragmentArguments = bundle;
179       target.add(voicemailSettings);
180     } else {
181       LogUtil.i(
182           "DialerSettingsActivity.addVoicemailSettings", "showing single-SIM voicemail settings");
183       voicemailSettings.fragment = voicemailSettingsFragment;
184       Bundle bundle = new Bundle();
185       bundle.putParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, soleAccount);
186       voicemailSettings.fragmentArguments = bundle;
187       target.add(voicemailSettings);
188     }
189   }
190 
191   /**
192    * @return the only SIM phone account, or {@code null} if there are none or more than one. Note:
193    *     having a empty SIM slot still count as a PhoneAccountHandle that is "invalid", and
194    *     voicemail settings should still be available for it.
195    */
196   @Nullable
getSoleSimAccount()197   private PhoneAccountHandle getSoleSimAccount() {
198     TelecomManager telecomManager = getSystemService(TelecomManager.class);
199     PhoneAccountHandle result = null;
200     for (PhoneAccountHandle phoneAccountHandle : telecomManager.getCallCapablePhoneAccounts()) {
201       PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
202       if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
203         LogUtil.i(
204             "DialerSettingsActivity.getSoleSimAccount", phoneAccountHandle + " is a SIM account");
205         if (result != null) {
206           return null;
207         }
208         result = phoneAccountHandle;
209       }
210     }
211     return result;
212   }
213 
214   /**
215    * Returns {@code true} or {@code false} based on whether the display options setting should be
216    * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful since
217    * contacts are sorted and displayed family name first by default.
218    *
219    * @return {@code true} if the display options should be shown, {@code false} otherwise.
220    */
showDisplayOptions()221   private boolean showDisplayOptions() {
222     return getResources().getBoolean(R.bool.config_display_order_user_changeable)
223         && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
224   }
225 
226   @Override
onHeaderClick(Header header, int position)227   public void onHeaderClick(Header header, int position) {
228     if (header.id == R.id.settings_header_sounds_and_vibration) {
229       // If we don't have the permission to write to system settings, go to system sound
230       // settings instead. Otherwise, perform the super implementation (which launches our
231       // own preference fragment.
232       if (!Settings.System.canWrite(this)) {
233         Toast.makeText(
234                 this,
235                 getResources().getString(R.string.toast_cannot_write_system_settings),
236                 Toast.LENGTH_SHORT)
237             .show();
238         startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
239         return;
240       }
241     }
242     super.onHeaderClick(header, position);
243   }
244 
245   @Override
onOptionsItemSelected(MenuItem item)246   public boolean onOptionsItemSelected(MenuItem item) {
247     if (item.getItemId() == android.R.id.home) {
248       onBackPressed();
249       return true;
250     }
251     return false;
252   }
253 
254   @Override
onBackPressed()255   public void onBackPressed() {
256     if (!isSafeToCommitTransactions()) {
257       return;
258     }
259     super.onBackPressed();
260   }
261 
262   @Override
isValidFragment(String fragmentName)263   protected boolean isValidFragment(String fragmentName) {
264     return true;
265   }
266 
267   /** @return Whether the current user is the primary user. */
isPrimaryUser()268   private boolean isPrimaryUser() {
269     return getSystemService(UserManager.class).isSystemUser();
270   }
271 }
272