1 /*
2  * Copyright (C) 2015 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;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Bundle;
27 import android.os.PersistableBundle;
28 import android.support.v7.preference.ListPreference;
29 import android.support.v7.preference.Preference;
30 import android.support.v7.preference.Preference.OnPreferenceClickListener;
31 import android.support.v7.preference.PreferenceScreen;
32 import android.telephony.CarrierConfigManager;
33 import android.telephony.PhoneStateListener;
34 import android.telephony.TelephonyManager;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.widget.Switch;
38 import android.widget.TextView;
39 
40 import com.android.ims.ImsConfig;
41 import com.android.ims.ImsManager;
42 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
43 import com.android.internal.telephony.Phone;
44 import com.android.settings.widget.SwitchBar;
45 
46 /**
47  * "Wi-Fi Calling settings" screen.  This preference screen lets you
48  * enable/disable Wi-Fi Calling and change Wi-Fi Calling mode.
49  */
50 public class WifiCallingSettings extends SettingsPreferenceFragment
51         implements SwitchBar.OnSwitchChangeListener,
52         Preference.OnPreferenceChangeListener {
53 
54     private static final String TAG = "WifiCallingSettings";
55 
56     //String keys for preference lookup
57     private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
58     private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
59     private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
60 
61     private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
62 
63     public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
64 
65     public static final int LAUCH_APP_ACTIVATE = 0;
66     public static final int LAUCH_APP_UPDATE = 1;
67 
68     //UI objects
69     private SwitchBar mSwitchBar;
70     private Switch mSwitch;
71     private ListPreference mButtonWfcMode;
72     private ListPreference mButtonWfcRoamingMode;
73     private Preference mUpdateAddress;
74     private TextView mEmptyView;
75 
76     private boolean mValidListener = false;
77     private boolean mEditableWfcMode = true;
78     private boolean mEditableWfcRoamingMode = true;
79 
80     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
81         /*
82          * Enable/disable controls when in/out of a call and depending on
83          * TTY mode and TTY support over VoLTE.
84          * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
85          * java.lang.String)
86          */
87         @Override
88         public void onCallStateChanged(int state, String incomingNumber) {
89             final SettingsActivity activity = (SettingsActivity) getActivity();
90             boolean isNonTtyOrTtyOnVolteEnabled = ImsManager
91                     .isNonTtyOrTtyOnVolteEnabled(activity);
92             final SwitchBar switchBar = activity.getSwitchBar();
93             boolean isWfcEnabled = switchBar.getSwitch().isChecked()
94                     && isNonTtyOrTtyOnVolteEnabled;
95 
96             switchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE)
97                     && isNonTtyOrTtyOnVolteEnabled);
98 
99             boolean isWfcModeEditable = true;
100             boolean isWfcRoamingModeEditable = false;
101             final CarrierConfigManager configManager = (CarrierConfigManager)
102                     activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
103             if (configManager != null) {
104                 PersistableBundle b = configManager.getConfig();
105                 if (b != null) {
106                     isWfcModeEditable = b.getBoolean(
107                             CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
108                     isWfcRoamingModeEditable = b.getBoolean(
109                             CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
110                 }
111             }
112 
113             Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
114             if (pref != null) {
115                 pref.setEnabled(isWfcEnabled && isWfcModeEditable
116                         && (state == TelephonyManager.CALL_STATE_IDLE));
117             }
118             Preference pref_roam = getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
119             if (pref_roam != null) {
120                 pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
121                         && (state == TelephonyManager.CALL_STATE_IDLE));
122             }
123         }
124     };
125 
126     private final OnPreferenceClickListener mUpdateAddressListener =
127             new OnPreferenceClickListener() {
128                 /*
129                  * Launch carrier emergency address managemnent activity
130                  */
131                 @Override
132                 public boolean onPreferenceClick(Preference preference) {
133                     final Context context = getActivity();
134                     Intent carrierAppIntent = getCarrierActivityIntent(context);
135                     if (carrierAppIntent != null) {
136                         carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
137                         startActivity(carrierAppIntent);
138                     }
139                     return true;
140                 }
141     };
142 
143     @Override
onActivityCreated(Bundle savedInstanceState)144     public void onActivityCreated(Bundle savedInstanceState) {
145         super.onActivityCreated(savedInstanceState);
146 
147         final SettingsActivity activity = (SettingsActivity) getActivity();
148 
149         mSwitchBar = activity.getSwitchBar();
150         mSwitch = mSwitchBar.getSwitch();
151         mSwitchBar.show();
152 
153         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
154         setEmptyView(mEmptyView);
155         mEmptyView.setText(R.string.wifi_calling_off_explanation);
156     }
157 
158     @Override
onDestroyView()159     public void onDestroyView() {
160         super.onDestroyView();
161         mSwitchBar.hide();
162     }
163 
showAlert(Intent intent)164     private void showAlert(Intent intent) {
165         Context context = getActivity();
166 
167         CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
168         CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
169 
170         AlertDialog.Builder builder = new AlertDialog.Builder(context);
171         builder.setMessage(message)
172                 .setTitle(title)
173                 .setIcon(android.R.drawable.ic_dialog_alert)
174                 .setPositiveButton(android.R.string.ok, null);
175         AlertDialog dialog = builder.create();
176         dialog.show();
177     }
178 
179     private IntentFilter mIntentFilter;
180 
181     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
182         @Override
183         public void onReceive(Context context, Intent intent) {
184             String action = intent.getAction();
185             if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) {
186                 // If this fragment is active then we are immediately
187                 // showing alert on screen. There is no need to add
188                 // notification in this case.
189                 //
190                 // In order to communicate to ImsPhone that it should
191                 // not show notification, we are changing result code here.
192                 setResultCode(Activity.RESULT_CANCELED);
193 
194                 // UX requirement is to disable WFC in case of "permanent" registration failures.
195                 mSwitch.setChecked(false);
196 
197                 showAlert(intent);
198             }
199         }
200     };
201 
202     @Override
getMetricsCategory()203     public int getMetricsCategory() {
204         return MetricsEvent.WIFI_CALLING;
205     }
206 
207     @Override
onCreate(Bundle savedInstanceState)208     public void onCreate(Bundle savedInstanceState) {
209         super.onCreate(savedInstanceState);
210 
211         addPreferencesFromResource(R.xml.wifi_calling_settings);
212 
213         mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
214         mButtonWfcMode.setOnPreferenceChangeListener(this);
215 
216         mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
217         mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
218 
219         mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
220         mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);
221 
222         mIntentFilter = new IntentFilter();
223         mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
224 
225         CarrierConfigManager configManager = (CarrierConfigManager)
226                 getSystemService(Context.CARRIER_CONFIG_SERVICE);
227         boolean isWifiOnlySupported = true;
228         if (configManager != null) {
229             PersistableBundle b = configManager.getConfig();
230             if (b != null) {
231                 mEditableWfcMode = b.getBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
232                 mEditableWfcRoamingMode = b.getBoolean(
233                         CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
234                 isWifiOnlySupported = b.getBoolean(
235                         CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
236             }
237         }
238 
239         if (!isWifiOnlySupported) {
240             mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
241             mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
242             mButtonWfcRoamingMode.setEntries(
243                     R.array.wifi_calling_mode_choices_v2_without_wifi_only);
244             mButtonWfcRoamingMode.setEntryValues(
245                     R.array.wifi_calling_mode_values_without_wifi_only);
246         }
247     }
248 
249     @Override
onResume()250     public void onResume() {
251         super.onResume();
252 
253         final Context context = getActivity();
254 
255         // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
256         boolean wfcEnabled = ImsManager.isWfcEnabledByUser(context)
257                 && ImsManager.isNonTtyOrTtyOnVolteEnabled(context);
258         mSwitch.setChecked(wfcEnabled);
259         int wfcMode = ImsManager.getWfcMode(context, false);
260         int wfcRoamingMode = ImsManager.getWfcMode(context, true);
261         mButtonWfcMode.setValue(Integer.toString(wfcMode));
262         mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode));
263         updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);
264 
265         if (ImsManager.isWfcEnabledByPlatform(context)) {
266             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
267             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
268 
269             mSwitchBar.addOnSwitchChangeListener(this);
270 
271             mValidListener = true;
272         }
273 
274         context.registerReceiver(mIntentReceiver, mIntentFilter);
275 
276         Intent intent = getActivity().getIntent();
277         if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
278             showAlert(intent);
279         }
280     }
281 
282     @Override
onPause()283     public void onPause() {
284         super.onPause();
285 
286         final Context context = getActivity();
287 
288         if (mValidListener) {
289             mValidListener = false;
290 
291             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
292             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
293 
294             mSwitchBar.removeOnSwitchChangeListener(this);
295         }
296 
297         context.unregisterReceiver(mIntentReceiver);
298     }
299 
300     /**
301      * Listens to the state change of the switch.
302      */
303     @Override
onSwitchChanged(Switch switchView, boolean isChecked)304     public void onSwitchChanged(Switch switchView, boolean isChecked) {
305         final Context context = getActivity();
306         Log.d(TAG, "onSwitchChanged(" + isChecked + ")");
307 
308         if (!isChecked) {
309             updateWfcMode(context, false);
310             return;
311         }
312 
313         // Call address management activity before turning on WFC
314         Intent carrierAppIntent = getCarrierActivityIntent(context);
315         if (carrierAppIntent != null) {
316             carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
317             startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
318         } else {
319             updateWfcMode(context, true);
320         }
321     }
322 
323     /*
324      * Get the Intent to launch carrier emergency address management activity.
325      * Return null when no activity found.
326      */
getCarrierActivityIntent(Context context)327     private static Intent getCarrierActivityIntent(Context context) {
328         // Retrive component name from carrirt config
329         CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class);
330         if (configManager == null) return null;
331 
332         PersistableBundle bundle = configManager.getConfig();
333         if (bundle == null) return null;
334 
335         String carrierApp = bundle.getString(
336                 CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
337         if (TextUtils.isEmpty(carrierApp)) return null;
338 
339         ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
340         if (componentName == null) return null;
341 
342         // Build and return intent
343         Intent intent = new Intent();
344         intent.setComponent(componentName);
345         return intent;
346     }
347 
348     /*
349      * Turn on/off WFC mode with ImsManager and update UI accordingly
350      */
updateWfcMode(Context context, boolean wfcEnabled)351     private void updateWfcMode(Context context, boolean wfcEnabled) {
352         Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
353         ImsManager.setWfcSetting(context, wfcEnabled);
354 
355         int wfcMode = ImsManager.getWfcMode(context, false);
356         int wfcRoamingMode = ImsManager.getWfcMode(context, true);
357         updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);
358         if (wfcEnabled) {
359             mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode);
360         } else {
361             mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1);
362         }
363     }
364 
365     @Override
onActivityResult(int requestCode, int resultCode, Intent data)366     public void onActivityResult(int requestCode, int resultCode, Intent data) {
367         super.onActivityResult(requestCode, resultCode, data);
368 
369         final Context context = getActivity();
370 
371         if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
372             Log.d(TAG, "WFC emergency address activity result = " + resultCode);
373 
374             if (resultCode == Activity.RESULT_OK) {
375                 updateWfcMode(context, true);
376             }
377         }
378     }
379 
updateButtonWfcMode(Context context, boolean wfcEnabled, int wfcMode, int wfcRoamingMode)380     private void updateButtonWfcMode(Context context, boolean wfcEnabled,
381                                      int wfcMode, int wfcRoamingMode) {
382         mButtonWfcMode.setSummary(getWfcModeSummary(context, wfcMode));
383         mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
384         // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
385         mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);
386 
387         final PreferenceScreen preferenceScreen = getPreferenceScreen();
388         boolean updateAddressEnabled = (getCarrierActivityIntent(context) != null);
389         if (wfcEnabled) {
390             if (mEditableWfcMode) {
391                 preferenceScreen.addPreference(mButtonWfcMode);
392             } else {
393                 // Don't show WFC (home) preference if it's not editable.
394                 preferenceScreen.removePreference(mButtonWfcMode);
395             }
396             if (mEditableWfcRoamingMode) {
397                 preferenceScreen.addPreference(mButtonWfcRoamingMode);
398             } else {
399                 // Don't show WFC roaming preference if it's not editable.
400                 preferenceScreen.removePreference(mButtonWfcRoamingMode);
401             }
402             if (updateAddressEnabled) {
403                 preferenceScreen.addPreference(mUpdateAddress);
404             } else {
405                 preferenceScreen.removePreference(mUpdateAddress);
406             }
407         } else {
408             preferenceScreen.removePreference(mButtonWfcMode);
409             preferenceScreen.removePreference(mButtonWfcRoamingMode);
410             preferenceScreen.removePreference(mUpdateAddress);
411         }
412     }
413 
414     @Override
onPreferenceChange(Preference preference, Object newValue)415     public boolean onPreferenceChange(Preference preference, Object newValue) {
416         final Context context = getActivity();
417         if (preference == mButtonWfcMode) {
418             mButtonWfcMode.setValue((String) newValue);
419             int buttonMode = Integer.valueOf((String) newValue);
420             int currentWfcMode = ImsManager.getWfcMode(context, false);
421             if (buttonMode != currentWfcMode) {
422                 ImsManager.setWfcMode(context, buttonMode, false);
423                 mButtonWfcMode.setSummary(getWfcModeSummary(context, buttonMode));
424                 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
425             }
426             if (!mEditableWfcRoamingMode) {
427                 int currentWfcRoamingMode = ImsManager.getWfcMode(context, true);
428                 if (buttonMode != currentWfcRoamingMode) {
429                     ImsManager.setWfcMode(context, buttonMode, true);
430                     // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value
431                 }
432             }
433         } else if (preference == mButtonWfcRoamingMode) {
434             mButtonWfcRoamingMode.setValue((String) newValue);
435             int buttonMode = Integer.valueOf((String) newValue);
436             int currentMode = ImsManager.getWfcMode(context, true);
437             if (buttonMode != currentMode) {
438                 ImsManager.setWfcMode(context, buttonMode, true);
439                 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
440                 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
441             }
442         }
443         return true;
444     }
445 
getWfcModeSummary(Context context, int wfcMode)446     public static int getWfcModeSummary(Context context, int wfcMode) {
447         int resId = com.android.internal.R.string.wifi_calling_off_summary;
448         if (ImsManager.isWfcEnabledByUser(context)) {
449             switch (wfcMode) {
450                 case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY:
451                     resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
452                     break;
453                 case ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED:
454                     resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary;
455                     break;
456                 case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED:
457                     resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
458                     break;
459                 default:
460                     Log.e(TAG, "Unexpected WFC mode value: " + wfcMode);
461             }
462         }
463         return resId;
464     }
465 }
466