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.settings.SettingsEnums;
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.content.res.Resources;
27 import android.os.Bundle;
28 import android.os.PersistableBundle;
29 import android.telephony.CarrierConfigManager;
30 import android.telephony.PhoneStateListener;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.TelephonyCallback;
33 import android.telephony.TelephonyManager;
34 import android.telephony.ims.ImsManager;
35 import android.telephony.ims.ImsMmTelManager;
36 import android.telephony.ims.ProvisioningManager;
37 import android.text.TextUtils;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.widget.Switch;
43 import android.widget.TextView;
44 
45 import androidx.appcompat.app.AlertDialog;
46 import androidx.preference.Preference;
47 import androidx.preference.Preference.OnPreferenceClickListener;
48 import androidx.preference.PreferenceScreen;
49 
50 import com.android.ims.ImsConfig;
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.telephony.Phone;
53 import com.android.settings.R;
54 import com.android.settings.SettingsActivity;
55 import com.android.settings.SettingsPreferenceFragment;
56 import com.android.settings.Utils;
57 import com.android.settings.core.SubSettingLauncher;
58 import com.android.settings.network.ims.WifiCallingQueryImsState;
59 import com.android.settings.widget.SettingsMainSwitchBar;
60 import com.android.settings.wifi.calling.LinkifyDescriptionPreference;
61 import com.android.settingslib.widget.OnMainSwitchChangeListener;
62 
63 import java.util.List;
64 
65 /**
66  * This is the inner class of {@link WifiCallingSettings} fragment.
67  * The preference screen lets you enable/disable Wi-Fi Calling and change Wi-Fi Calling mode.
68  */
69 public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
70         implements OnMainSwitchChangeListener,
71         Preference.OnPreferenceChangeListener {
72     private static final String TAG = "WifiCallingForSub";
73 
74     //String keys for preference lookup
75     private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
76     private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
77     private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
78     private static final String PREFERENCE_NO_OPTIONS_DESC = "no_options_description";
79 
80     @VisibleForTesting
81     static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
82     @VisibleForTesting
83     static final int REQUEST_CHECK_WFC_DISCLAIMER = 2;
84 
85     public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
86     public static final String EXTRA_SUB_ID = "EXTRA_SUB_ID";
87 
88     protected static final String FRAGMENT_BUNDLE_SUBID = "subId";
89 
90     public static final int LAUCH_APP_ACTIVATE = 0;
91     public static final int LAUCH_APP_UPDATE = 1;
92 
93     //UI objects
94     private SettingsMainSwitchBar mSwitchBar;
95     private ListWithEntrySummaryPreference mButtonWfcMode;
96     private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
97     private Preference mUpdateAddress;
98 
99     private boolean mValidListener = false;
100     private boolean mEditableWfcMode = true;
101     private boolean mEditableWfcRoamingMode = true;
102     private boolean mUseWfcHomeModeForRoaming = false;
103 
104     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
105     private ImsMmTelManager mImsMmTelManager;
106     private ProvisioningManager mProvisioningManager;
107     private TelephonyManager mTelephonyManager;
108 
109     private final PhoneTelephonyCallback mTelephonyCallback = new PhoneTelephonyCallback();
110 
111     private class PhoneTelephonyCallback extends TelephonyCallback implements
112             TelephonyCallback.CallStateListener {
113         /*
114          * Enable/disable controls when in/out of a call and depending on
115          * TTY mode and TTY support over VoLTE.
116          * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
117          * java.lang.String)
118          */
119         @Override
onCallStateChanged(int state)120         public void onCallStateChanged(int state) {
121             final SettingsActivity activity = (SettingsActivity) getActivity();
122             final boolean isNonTtyOrTtyOnVolteEnabled =
123                     queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl();
124             final boolean isWfcEnabled = mSwitchBar.isChecked()
125                     && isNonTtyOrTtyOnVolteEnabled;
126             boolean isCallStateIdle = getTelephonyManagerForSub(
127                     WifiCallingSettingsForSub.this.mSubId).getCallState()
128                     == TelephonyManager.CALL_STATE_IDLE;
129             mSwitchBar.setEnabled(isCallStateIdle
130                     && isNonTtyOrTtyOnVolteEnabled);
131 
132             boolean isWfcModeEditable = true;
133             boolean isWfcRoamingModeEditable = false;
134             final CarrierConfigManager configManager = (CarrierConfigManager)
135                     activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
136             if (configManager != null) {
137                 PersistableBundle b =
138                         configManager.getConfigForSubId(WifiCallingSettingsForSub.this.mSubId);
139                 if (b != null) {
140                     isWfcModeEditable = b.getBoolean(
141                             CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
142                     isWfcRoamingModeEditable = b.getBoolean(
143                             CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
144                 }
145             }
146 
147             final Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
148             if (pref != null) {
149                 pref.setEnabled(isWfcEnabled && isWfcModeEditable
150                         && isCallStateIdle);
151             }
152             final Preference pref_roam =
153                     getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
154             if (pref_roam != null) {
155                 pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
156                         && isCallStateIdle);
157             }
158         }
159     }
160 
161     /*
162      * Launch carrier emergency address management activity
163      */
164     private final OnPreferenceClickListener mUpdateAddressListener =
165             preference -> {
166                 final Intent carrierAppIntent = getCarrierActivityIntent();
167                 if (carrierAppIntent != null) {
168                     carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
169                     startActivity(carrierAppIntent);
170                 }
171                 return true;
172             };
173 
174     private final ProvisioningManager.Callback mProvisioningCallback =
175             new ProvisioningManager.Callback() {
176                 @Override
177                 public void onProvisioningIntChanged(int item, int value) {
178                     if (item == ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED
179                             || item == ImsConfig.ConfigConstants.VLT_SETTING_ENABLED) {
180                         // The provisioning policy might have changed. Update the body to make sure
181                         // this change takes effect if needed.
182                         updateBody();
183                     }
184                 }
185             };
186 
187     @Override
onActivityCreated(Bundle savedInstanceState)188     public void onActivityCreated(Bundle savedInstanceState) {
189         super.onActivityCreated(savedInstanceState);
190 
191         mSwitchBar = getView().findViewById(R.id.switch_bar);
192         mSwitchBar.show();
193     }
194 
195     @Override
onDestroyView()196     public void onDestroyView() {
197         super.onDestroyView();
198         mSwitchBar.hide();
199     }
200 
201     @VisibleForTesting
showAlert(Intent intent)202     void showAlert(Intent intent) {
203         final Context context = getActivity();
204 
205         final CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
206         final CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
207 
208         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
209         builder.setMessage(message)
210                 .setTitle(title)
211                 .setIcon(android.R.drawable.ic_dialog_alert)
212                 .setPositiveButton(android.R.string.ok, null);
213         final AlertDialog dialog = builder.create();
214         dialog.show();
215     }
216 
217     private IntentFilter mIntentFilter;
218 
219     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
220         @Override
221         public void onReceive(Context context, Intent intent) {
222             final String action = intent.getAction();
223             if (action.equals(ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR)) {
224                 // If this fragment is active then we are immediately
225                 // showing alert on screen. There is no need to add
226                 // notification in this case.
227                 //
228                 // In order to communicate to ImsPhone that it should
229                 // not show notification, we are changing result code here.
230                 setResultCode(Activity.RESULT_CANCELED);
231 
232                 showAlert(intent);
233             }
234         }
235     };
236 
237     @Override
getMetricsCategory()238     public int getMetricsCategory() {
239         return SettingsEnums.WIFI_CALLING_FOR_SUB;
240     }
241 
242     @Override
getHelpResource()243     public int getHelpResource() {
244         // Return 0 to suppress help icon. The help will be populated by parent page.
245         return 0;
246     }
247 
248     @VisibleForTesting
getTelephonyManagerForSub(int subId)249     TelephonyManager getTelephonyManagerForSub(int subId) {
250         if (mTelephonyManager == null) {
251             mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
252         }
253         return mTelephonyManager.createForSubscriptionId(subId);
254     }
255 
256     @VisibleForTesting
queryImsState(int subId)257     WifiCallingQueryImsState queryImsState(int subId) {
258         return new WifiCallingQueryImsState(getContext(), subId);
259     }
260 
261     @VisibleForTesting
getImsProvisioningManager()262     ProvisioningManager getImsProvisioningManager() {
263         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
264             return null;
265         }
266         return ProvisioningManager.createForSubscriptionId(mSubId);
267     }
268 
269     @VisibleForTesting
getImsMmTelManager()270     ImsMmTelManager getImsMmTelManager() {
271         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
272             return null;
273         }
274         return ImsMmTelManager.createForSubscriptionId(mSubId);
275     }
276 
277     @Override
onCreate(Bundle savedInstanceState)278     public void onCreate(Bundle savedInstanceState) {
279         super.onCreate(savedInstanceState);
280 
281         addPreferencesFromResource(R.xml.wifi_calling_settings);
282 
283         // SubId should always be specified when creating this fragment. Either through
284         // fragment.setArguments() or through savedInstanceState.
285         if (getArguments() != null && getArguments().containsKey(FRAGMENT_BUNDLE_SUBID)) {
286             mSubId = getArguments().getInt(FRAGMENT_BUNDLE_SUBID);
287         } else if (savedInstanceState != null) {
288             mSubId = savedInstanceState.getInt(
289                     FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
290         }
291 
292         mProvisioningManager = getImsProvisioningManager();
293         mImsMmTelManager = getImsMmTelManager();
294 
295         mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
296         mButtonWfcMode.setOnPreferenceChangeListener(this);
297 
298         mButtonWfcRoamingMode = findPreference(BUTTON_WFC_ROAMING_MODE);
299         mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
300 
301         mUpdateAddress = findPreference(PREFERENCE_EMERGENCY_ADDRESS);
302         mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);
303 
304         mIntentFilter = new IntentFilter();
305         mIntentFilter.addAction(ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR);
306 
307         updateDescriptionForOptions(
308                 List.of(mButtonWfcMode, mButtonWfcRoamingMode, mUpdateAddress));
309     }
310 
311     @Override
onSaveInstanceState(Bundle outState)312     public void onSaveInstanceState(Bundle outState) {
313         outState.putInt(FRAGMENT_BUNDLE_SUBID, mSubId);
314         super.onSaveInstanceState(outState);
315     }
316 
317     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)318     public View onCreateView(LayoutInflater inflater, ViewGroup container,
319             Bundle savedInstanceState) {
320 
321         final View view = inflater.inflate(
322                 R.layout.wifi_calling_settings_preferences, container, false);
323 
324         final ViewGroup prefs_container = view.findViewById(android.R.id.tabcontent);
325         Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
326         final View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
327         prefs_container.addView(prefs);
328 
329         return view;
330     }
331 
332     @VisibleForTesting
isWfcProvisionedOnDevice()333     boolean isWfcProvisionedOnDevice() {
334         return queryImsState(mSubId).isWifiCallingProvisioned();
335     }
336 
updateBody()337     private void updateBody() {
338         if (!isWfcProvisionedOnDevice()) {
339             // This screen is not allowed to be shown due to provisioning policy and should
340             // therefore be closed.
341             finish();
342             return;
343         }
344 
345         final CarrierConfigManager configManager = (CarrierConfigManager)
346                 getSystemService(Context.CARRIER_CONFIG_SERVICE);
347         boolean isWifiOnlySupported = true;
348 
349         if (configManager != null) {
350             final PersistableBundle b = configManager.getConfigForSubId(mSubId);
351             if (b != null) {
352                 mEditableWfcMode = b.getBoolean(
353                         CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
354                 mEditableWfcRoamingMode = b.getBoolean(
355                         CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
356                 mUseWfcHomeModeForRoaming = b.getBoolean(
357                         CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL,
358                         false);
359                 isWifiOnlySupported = b.getBoolean(
360                         CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
361             }
362         }
363 
364         final Resources res = getResourcesForSubId();
365         mButtonWfcMode.setTitle(res.getString(R.string.wifi_calling_mode_title));
366         mButtonWfcMode.setDialogTitle(res.getString(R.string.wifi_calling_mode_dialog_title));
367         mButtonWfcRoamingMode.setTitle(res.getString(R.string.wifi_calling_roaming_mode_title));
368         mButtonWfcRoamingMode.setDialogTitle(
369                 res.getString(R.string.wifi_calling_roaming_mode_dialog_title));
370 
371         if (isWifiOnlySupported) {
372             // Set string resources WITH option wifi only in mButtonWfcMode.
373             mButtonWfcMode.setEntries(
374                     res.getStringArray(R.array.wifi_calling_mode_choices));
375             mButtonWfcMode.setEntryValues(res.getStringArray(R.array.wifi_calling_mode_values));
376             mButtonWfcMode.setEntrySummaries(
377                     res.getStringArray(R.array.wifi_calling_mode_summaries));
378 
379             // Set string resources WITH option wifi only in mButtonWfcRoamingMode.
380             mButtonWfcRoamingMode.setEntries(
381                     res.getStringArray(R.array.wifi_calling_mode_choices_v2));
382             mButtonWfcRoamingMode.setEntryValues(
383                     res.getStringArray(R.array.wifi_calling_mode_values));
384             mButtonWfcRoamingMode.setEntrySummaries(
385                     res.getStringArray(R.array.wifi_calling_mode_summaries));
386         } else {
387             // Set string resources WITHOUT option wifi only in mButtonWfcMode.
388             mButtonWfcMode.setEntries(
389                     res.getStringArray(R.array.wifi_calling_mode_choices_without_wifi_only));
390             mButtonWfcMode.setEntryValues(
391                     res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only));
392             mButtonWfcMode.setEntrySummaries(
393                     res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only));
394 
395             // Set string resources WITHOUT option wifi only in mButtonWfcRoamingMode.
396             mButtonWfcRoamingMode.setEntries(
397                     res.getStringArray(R.array.wifi_calling_mode_choices_v2_without_wifi_only));
398             mButtonWfcRoamingMode.setEntryValues(
399                     res.getStringArray(R.array.wifi_calling_mode_values_without_wifi_only));
400             mButtonWfcRoamingMode.setEntrySummaries(
401                     res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only));
402         }
403 
404         // NOTE: Buttons will be enabled/disabled in mTelephonyCallback
405         final WifiCallingQueryImsState queryIms = queryImsState(mSubId);
406         final boolean wfcEnabled = queryIms.isEnabledByUser()
407                 && queryIms.isAllowUserControl();
408         mSwitchBar.setChecked(wfcEnabled);
409         final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
410         final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
411         mButtonWfcMode.setValue(Integer.toString(wfcMode));
412         mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode));
413         updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode);
414     }
415 
416     @Override
onResume()417     public void onResume() {
418         super.onResume();
419 
420         updateBody();
421 
422         final Context context = getActivity();
423         if (queryImsState(mSubId).isWifiCallingSupported()) {
424             getTelephonyManagerForSub(mSubId).registerTelephonyCallback(
425                     context.getMainExecutor(), mTelephonyCallback);
426 
427             mSwitchBar.addOnSwitchChangeListener(this);
428 
429             mValidListener = true;
430         }
431 
432         context.registerReceiver(mIntentReceiver, mIntentFilter);
433 
434         final Intent intent = getActivity().getIntent();
435         if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
436             showAlert(intent);
437         }
438 
439         // Register callback for provisioning changes.
440         registerProvisioningChangedCallback();
441     }
442 
443     @Override
onPause()444     public void onPause() {
445         super.onPause();
446 
447         final Context context = getActivity();
448 
449         if (mValidListener) {
450             mValidListener = false;
451 
452             getTelephonyManagerForSub(mSubId).unregisterTelephonyCallback(mTelephonyCallback);
453 
454             mSwitchBar.removeOnSwitchChangeListener(this);
455         }
456 
457         context.unregisterReceiver(mIntentReceiver);
458 
459         // Remove callback for provisioning changes.
460         unregisterProvisioningChangedCallback();
461     }
462 
463     /**
464      * Listens to the state change of the switch.
465      */
466     @Override
onSwitchChanged(Switch switchView, boolean isChecked)467     public void onSwitchChanged(Switch switchView, boolean isChecked) {
468         Log.d(TAG, "onSwitchChanged(" + isChecked + ")");
469 
470         if (!isChecked) {
471             updateWfcMode(false);
472             return;
473         }
474 
475         // Launch disclaimer fragment before turning on WFC
476         final Context context = getActivity();
477         final Bundle args = new Bundle();
478         args.putInt(EXTRA_SUB_ID, mSubId);
479         new SubSettingLauncher(context)
480                 .setDestination(WifiCallingDisclaimerFragment.class.getName())
481                 .setArguments(args)
482                 .setTitleRes(R.string.wifi_calling_settings_title)
483                 .setSourceMetricsCategory(getMetricsCategory())
484                 .setResultListener(this, REQUEST_CHECK_WFC_DISCLAIMER)
485                 .launch();
486     }
487 
488     /*
489      * Get the Intent to launch carrier emergency address management activity.
490      * Return null when no activity found.
491      */
getCarrierActivityIntent()492     private Intent getCarrierActivityIntent() {
493         // Retrive component name from carrier config
494         final CarrierConfigManager configManager =
495                 getActivity().getSystemService(CarrierConfigManager.class);
496         if (configManager == null) return null;
497 
498         final PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
499         if (bundle == null) return null;
500 
501         final String carrierApp = bundle.getString(
502                 CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
503         if (TextUtils.isEmpty(carrierApp)) return null;
504 
505         final ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
506         if (componentName == null) return null;
507 
508         // Build and return intent
509         final Intent intent = new Intent();
510         intent.setComponent(componentName);
511         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mSubId);
512         return intent;
513     }
514 
515     /*
516      * Turn on/off WFC mode with ImsManager and update UI accordingly
517      */
updateWfcMode(boolean wfcEnabled)518     private void updateWfcMode(boolean wfcEnabled) {
519         Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
520         mImsMmTelManager.setVoWiFiSettingEnabled(wfcEnabled);
521 
522         final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
523         final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
524         updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode);
525         if (wfcEnabled) {
526             mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode);
527         } else {
528             mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1);
529         }
530     }
531 
532     @Override
onActivityResult(int requestCode, int resultCode, Intent data)533     public void onActivityResult(int requestCode, int resultCode, Intent data) {
534         super.onActivityResult(requestCode, resultCode, data);
535 
536         Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
537 
538         switch (requestCode) {
539             case REQUEST_CHECK_WFC_EMERGENCY_ADDRESS:
540                 if (resultCode == Activity.RESULT_OK) {
541                     updateWfcMode(true);
542                 }
543                 break;
544             case REQUEST_CHECK_WFC_DISCLAIMER:
545                 if (resultCode == Activity.RESULT_OK) {
546                     // Call address management activity before turning on WFC
547                     final Intent carrierAppIntent = getCarrierActivityIntent();
548                     if (carrierAppIntent != null) {
549                         carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
550                         startActivityForResult(carrierAppIntent,
551                                 REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
552                     } else {
553                         updateWfcMode(true);
554                     }
555                 }
556                 break;
557             default:
558                 Log.e(TAG, "Unexpected request: " + requestCode);
559                 break;
560         }
561     }
562 
updateButtonWfcMode(boolean wfcEnabled, int wfcMode, int wfcRoamingMode)563     private void updateButtonWfcMode(boolean wfcEnabled,
564             int wfcMode, int wfcRoamingMode) {
565         mButtonWfcMode.setSummary(getWfcModeSummary(wfcMode));
566         mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
567         // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
568         mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);
569 
570         final PreferenceScreen preferenceScreen = getPreferenceScreen();
571         final boolean updateAddressEnabled = (getCarrierActivityIntent() != null);
572         if (wfcEnabled) {
573             // Don't show WFC (home) preference if it's not editable.
574             mButtonWfcMode.setVisible(mEditableWfcMode);
575             // Don't show WFC roaming preference if it's not editable.
576             mButtonWfcRoamingMode.setVisible(
577                     mEditableWfcRoamingMode && !mUseWfcHomeModeForRoaming);
578             mUpdateAddress.setVisible(updateAddressEnabled);
579         } else {
580             mButtonWfcMode.setVisible(false);
581             mButtonWfcRoamingMode.setVisible(false);
582             mUpdateAddress.setVisible(false);
583         }
584         updateDescriptionForOptions(
585                 List.of(mButtonWfcMode, mButtonWfcRoamingMode, mUpdateAddress));
586     }
587 
updateDescriptionForOptions(List<Preference> visibleOptions)588     private void updateDescriptionForOptions(List<Preference> visibleOptions) {
589         LinkifyDescriptionPreference pref = findPreference(PREFERENCE_NO_OPTIONS_DESC);
590         if (pref == null) {
591             return;
592         }
593 
594         boolean optionsAvailable = visibleOptions.stream().anyMatch(Preference::isVisible);
595         if (!optionsAvailable) {
596             final Resources res = getResourcesForSubId();
597             String emptyViewText = res.getString(R.string.wifi_calling_off_explanation,
598                     res.getString(R.string.wifi_calling_off_explanation_2));
599             pref.setSummary(emptyViewText);
600         }
601         pref.setVisible(!optionsAvailable);
602     }
603 
604     @Override
onPreferenceChange(Preference preference, Object newValue)605     public boolean onPreferenceChange(Preference preference, Object newValue) {
606         if (preference == mButtonWfcMode) {
607             Log.d(TAG, "onPreferenceChange mButtonWfcMode " + newValue);
608             mButtonWfcMode.setValue((String) newValue);
609             final int buttonMode = Integer.valueOf((String) newValue);
610             final int currentWfcMode = mImsMmTelManager.getVoWiFiModeSetting();
611             if (buttonMode != currentWfcMode) {
612                 mImsMmTelManager.setVoWiFiModeSetting(buttonMode);
613                 mButtonWfcMode.setSummary(getWfcModeSummary(buttonMode));
614                 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
615 
616                 if (mUseWfcHomeModeForRoaming) {
617                     mImsMmTelManager.setVoWiFiRoamingModeSetting(buttonMode);
618                     // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value
619                 }
620             }
621         } else if (preference == mButtonWfcRoamingMode) {
622             mButtonWfcRoamingMode.setValue((String) newValue);
623             final int buttonMode = Integer.valueOf((String) newValue);
624             final int currentMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
625             if (buttonMode != currentMode) {
626                 mImsMmTelManager.setVoWiFiRoamingModeSetting(buttonMode);
627                 // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
628                 mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
629             }
630         }
631         return true;
632     }
633 
getWfcModeSummary(int wfcMode)634     private CharSequence getWfcModeSummary(int wfcMode) {
635         int resId = com.android.internal.R.string.wifi_calling_off_summary;
636         if (queryImsState(mSubId).isEnabledByUser()) {
637             switch (wfcMode) {
638                 case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
639                     resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
640                     break;
641                 case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
642                     resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary;
643                     break;
644                 case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
645                     resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
646                     break;
647                 default:
648                     Log.e(TAG, "Unexpected WFC mode value: " + wfcMode);
649             }
650         }
651         return getResourcesForSubId().getString(resId);
652     }
653 
654     @VisibleForTesting
getResourcesForSubId()655     Resources getResourcesForSubId() {
656         return SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
657     }
658 
659     @VisibleForTesting
registerProvisioningChangedCallback()660     void registerProvisioningChangedCallback() {
661         if (mProvisioningManager == null) {
662             return;
663         }
664         try {
665             mProvisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
666                     mProvisioningCallback);
667         } catch (Exception ex) {
668             Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
669         }
670     }
671 
672     @VisibleForTesting
unregisterProvisioningChangedCallback()673     void unregisterProvisioningChangedCallback() {
674         if (mProvisioningManager == null) {
675             return;
676         }
677         mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback);
678     }
679 }
680