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 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 String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation) 156 + activity.getString(R.string.wifi_calling_off_explanation_2); 157 mEmptyView.setText(emptyViewText); 158 } 159 160 @Override 161 public void onDestroyView() { 162 super.onDestroyView(); 163 mSwitchBar.hide(); 164 } 165 166 private void showAlert(Intent intent) { 167 Context context = getActivity(); 168 169 CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE); 170 CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE); 171 172 AlertDialog.Builder builder = new AlertDialog.Builder(context); 173 builder.setMessage(message) 174 .setTitle(title) 175 .setIcon(android.R.drawable.ic_dialog_alert) 176 .setPositiveButton(android.R.string.ok, null); 177 AlertDialog dialog = builder.create(); 178 dialog.show(); 179 } 180 181 private IntentFilter mIntentFilter; 182 183 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 184 @Override 185 public void onReceive(Context context, Intent intent) { 186 String action = intent.getAction(); 187 if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) { 188 // If this fragment is active then we are immediately 189 // showing alert on screen. There is no need to add 190 // notification in this case. 191 // 192 // In order to communicate to ImsPhone that it should 193 // not show notification, we are changing result code here. 194 setResultCode(Activity.RESULT_CANCELED); 195 196 // UX requirement is to disable WFC in case of "permanent" registration failures. 197 mSwitch.setChecked(false); 198 199 showAlert(intent); 200 } 201 } 202 }; 203 204 @Override 205 public int getMetricsCategory() { 206 return MetricsEvent.WIFI_CALLING; 207 } 208 209 @Override 210 public void onCreate(Bundle savedInstanceState) { 211 super.onCreate(savedInstanceState); 212 213 addPreferencesFromResource(R.xml.wifi_calling_settings); 214 215 mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE); 216 mButtonWfcMode.setOnPreferenceChangeListener(this); 217 218 mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE); 219 mButtonWfcRoamingMode.setOnPreferenceChangeListener(this); 220 221 mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS); 222 mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener); 223 224 mIntentFilter = new IntentFilter(); 225 mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR); 226 227 CarrierConfigManager configManager = (CarrierConfigManager) 228 getSystemService(Context.CARRIER_CONFIG_SERVICE); 229 boolean isWifiOnlySupported = true; 230 if (configManager != null) { 231 PersistableBundle b = configManager.getConfig(); 232 if (b != null) { 233 mEditableWfcMode = b.getBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); 234 mEditableWfcRoamingMode = b.getBoolean( 235 CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); 236 isWifiOnlySupported = b.getBoolean( 237 CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); 238 } 239 } 240 241 if (!isWifiOnlySupported) { 242 mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only); 243 mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only); 244 mButtonWfcRoamingMode.setEntries( 245 R.array.wifi_calling_mode_choices_v2_without_wifi_only); 246 mButtonWfcRoamingMode.setEntryValues( 247 R.array.wifi_calling_mode_values_without_wifi_only); 248 } 249 } 250 251 @Override 252 public void onResume() { 253 super.onResume(); 254 255 final Context context = getActivity(); 256 257 // NOTE: Buttons will be enabled/disabled in mPhoneStateListener 258 boolean wfcEnabled = ImsManager.isWfcEnabledByUser(context) 259 && ImsManager.isNonTtyOrTtyOnVolteEnabled(context); 260 mSwitch.setChecked(wfcEnabled); 261 int wfcMode = ImsManager.getWfcMode(context, false); 262 int wfcRoamingMode = ImsManager.getWfcMode(context, true); 263 mButtonWfcMode.setValue(Integer.toString(wfcMode)); 264 mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode)); 265 updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode); 266 267 if (ImsManager.isWfcEnabledByPlatform(context)) { 268 TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 269 tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 270 271 mSwitchBar.addOnSwitchChangeListener(this); 272 273 mValidListener = true; 274 } 275 276 context.registerReceiver(mIntentReceiver, mIntentFilter); 277 278 Intent intent = getActivity().getIntent(); 279 if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) { 280 showAlert(intent); 281 } 282 } 283 284 @Override 285 public void onPause() { 286 super.onPause(); 287 288 final Context context = getActivity(); 289 290 if (mValidListener) { 291 mValidListener = false; 292 293 TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 294 tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 295 296 mSwitchBar.removeOnSwitchChangeListener(this); 297 } 298 299 context.unregisterReceiver(mIntentReceiver); 300 } 301 302 /** 303 * Listens to the state change of the switch. 304 */ 305 @Override 306 public void onSwitchChanged(Switch switchView, boolean isChecked) { 307 final Context context = getActivity(); 308 Log.d(TAG, "onSwitchChanged(" + isChecked + ")"); 309 310 if (!isChecked) { 311 updateWfcMode(context, false); 312 return; 313 } 314 315 // Call address management activity before turning on WFC 316 Intent carrierAppIntent = getCarrierActivityIntent(context); 317 if (carrierAppIntent != null) { 318 carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE); 319 startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS); 320 } else { 321 updateWfcMode(context, true); 322 } 323 } 324 325 /* 326 * Get the Intent to launch carrier emergency address management activity. 327 * Return null when no activity found. 328 */ 329 private static Intent getCarrierActivityIntent(Context context) { 330 // Retrive component name from carrirt config 331 CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); 332 if (configManager == null) return null; 333 334 PersistableBundle bundle = configManager.getConfig(); 335 if (bundle == null) return null; 336 337 String carrierApp = bundle.getString( 338 CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); 339 if (TextUtils.isEmpty(carrierApp)) return null; 340 341 ComponentName componentName = ComponentName.unflattenFromString(carrierApp); 342 if (componentName == null) return null; 343 344 // Build and return intent 345 Intent intent = new Intent(); 346 intent.setComponent(componentName); 347 return intent; 348 } 349 350 /* 351 * Turn on/off WFC mode with ImsManager and update UI accordingly 352 */ 353 private void updateWfcMode(Context context, boolean wfcEnabled) { 354 Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")"); 355 ImsManager.setWfcSetting(context, wfcEnabled); 356 357 int wfcMode = ImsManager.getWfcMode(context, false); 358 int wfcRoamingMode = ImsManager.getWfcMode(context, true); 359 updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode); 360 if (wfcEnabled) { 361 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode); 362 } else { 363 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1); 364 } 365 } 366 367 @Override 368 public void onActivityResult(int requestCode, int resultCode, Intent data) { 369 super.onActivityResult(requestCode, resultCode, data); 370 371 final Context context = getActivity(); 372 373 if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) { 374 Log.d(TAG, "WFC emergency address activity result = " + resultCode); 375 376 if (resultCode == Activity.RESULT_OK) { 377 updateWfcMode(context, true); 378 } 379 } 380 } 381 382 private void updateButtonWfcMode(Context context, boolean wfcEnabled, 383 int wfcMode, int wfcRoamingMode) { 384 mButtonWfcMode.setSummary(getWfcModeSummary(context, wfcMode)); 385 mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode); 386 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. 387 mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode); 388 389 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 390 boolean updateAddressEnabled = (getCarrierActivityIntent(context) != null); 391 if (wfcEnabled) { 392 if (mEditableWfcMode) { 393 preferenceScreen.addPreference(mButtonWfcMode); 394 } else { 395 // Don't show WFC (home) preference if it's not editable. 396 preferenceScreen.removePreference(mButtonWfcMode); 397 } 398 if (mEditableWfcRoamingMode) { 399 preferenceScreen.addPreference(mButtonWfcRoamingMode); 400 } else { 401 // Don't show WFC roaming preference if it's not editable. 402 preferenceScreen.removePreference(mButtonWfcRoamingMode); 403 } 404 if (updateAddressEnabled) { 405 preferenceScreen.addPreference(mUpdateAddress); 406 } else { 407 preferenceScreen.removePreference(mUpdateAddress); 408 } 409 } else { 410 preferenceScreen.removePreference(mButtonWfcMode); 411 preferenceScreen.removePreference(mButtonWfcRoamingMode); 412 preferenceScreen.removePreference(mUpdateAddress); 413 } 414 } 415 416 @Override 417 public boolean onPreferenceChange(Preference preference, Object newValue) { 418 final Context context = getActivity(); 419 if (preference == mButtonWfcMode) { 420 mButtonWfcMode.setValue((String) newValue); 421 int buttonMode = Integer.valueOf((String) newValue); 422 int currentWfcMode = ImsManager.getWfcMode(context, false); 423 if (buttonMode != currentWfcMode) { 424 ImsManager.setWfcMode(context, buttonMode, false); 425 mButtonWfcMode.setSummary(getWfcModeSummary(context, buttonMode)); 426 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); 427 } 428 if (!mEditableWfcRoamingMode) { 429 int currentWfcRoamingMode = ImsManager.getWfcMode(context, true); 430 if (buttonMode != currentWfcRoamingMode) { 431 ImsManager.setWfcMode(context, buttonMode, true); 432 // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value 433 } 434 } 435 } else if (preference == mButtonWfcRoamingMode) { 436 mButtonWfcRoamingMode.setValue((String) newValue); 437 int buttonMode = Integer.valueOf((String) newValue); 438 int currentMode = ImsManager.getWfcMode(context, true); 439 if (buttonMode != currentMode) { 440 ImsManager.setWfcMode(context, buttonMode, true); 441 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. 442 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); 443 } 444 } 445 return true; 446 } 447 448 public static int getWfcModeSummary(Context context, int wfcMode) { 449 int resId = com.android.internal.R.string.wifi_calling_off_summary; 450 if (ImsManager.isWfcEnabledByUser(context)) { 451 switch (wfcMode) { 452 case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY: 453 resId = com.android.internal.R.string.wfc_mode_wifi_only_summary; 454 break; 455 case ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED: 456 resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary; 457 break; 458 case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED: 459 resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; 460 break; 461 default: 462 Log.e(TAG, "Unexpected WFC mode value: " + wfcMode); 463 } 464 } 465 return resId; 466 } 467 } 468