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