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