Home | History | Annotate | Download | only in calling
      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