From 13d50cb0f5b506c1f4134141dd55bc90b9e69a1c Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Fri, 22 Sep 2017 11:58:02 -0700 Subject: [PATCH] MSIM support for Wi-Fi calling setting. UI change to support Wi-Fi calling settings for multi-SIM devices. Bug: b/65648147 Test: manual Change-Id: Ia2e3a835400873bf6d8bfc7a7690d98e74049076 --- .../wifi_calling_settings_preferences.xml | 45 ++ res/layout/wifi_calling_settings_tabs.xml | 36 ++ .../android/settings/WifiCallingSettings.java | 473 +++------------- .../settings/WifiCallingSettingsForSub.java | 521 ++++++++++++++++++ .../grandfather_not_implementing_indexable | 1 + tests/unit/Android.mk | 2 +- .../settings/utils/MockedServiceManager.java | 107 ++++ .../wifi/WifiCallingSettingUiTest.java | 299 ++++++++++ 8 files changed, 1099 insertions(+), 385 deletions(-) create mode 100644 res/layout/wifi_calling_settings_preferences.xml create mode 100644 res/layout/wifi_calling_settings_tabs.xml create mode 100644 src/com/android/settings/WifiCallingSettingsForSub.java create mode 100644 tests/unit/src/com/android/settings/utils/MockedServiceManager.java create mode 100644 tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java diff --git a/res/layout/wifi_calling_settings_preferences.xml b/res/layout/wifi_calling_settings_preferences.xml new file mode 100644 index 00000000000..4e64f4072be --- /dev/null +++ b/res/layout/wifi_calling_settings_preferences.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/res/layout/wifi_calling_settings_tabs.xml b/res/layout/wifi_calling_settings_tabs.xml new file mode 100644 index 00000000000..1e27b47508e --- /dev/null +++ b/res/layout/wifi_calling_settings_tabs.xml @@ -0,0 +1,36 @@ + + + + + + + + + + diff --git a/src/com/android/settings/WifiCallingSettings.java b/src/com/android/settings/WifiCallingSettings.java index cb661ed9d57..e872fb80446 100644 --- a/src/com/android/settings/WifiCallingSettings.java +++ b/src/com/android/settings/WifiCallingSettings.java @@ -16,190 +16,38 @@ package com.android.settings; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.ComponentName; +import android.app.Fragment; +import android.app.FragmentManager; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; -import android.os.PersistableBundle; -import android.support.v7.preference.ListPreference; -import android.support.v7.preference.Preference; -import android.support.v7.preference.Preference.OnPreferenceClickListener; -import android.support.v7.preference.PreferenceScreen; -import android.telephony.CarrierConfigManager; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.text.TextUtils; +import android.support.v13.app.FragmentPagerAdapter; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.util.Log; -import android.widget.Switch; -import android.widget.TextView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import com.android.ims.ImsConfig; import com.android.ims.ImsManager; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.telephony.Phone; -import com.android.settings.widget.SwitchBar; +import com.android.settings.widget.RtlCompatibleViewPager; +import com.android.settings.widget.SlidingTabLayout; + +import java.util.List; /** - * "Wi-Fi Calling settings" screen. This preference screen lets you - * enable/disable Wi-Fi Calling and change Wi-Fi Calling mode. + * "Wi-Fi Calling settings" screen. This is the container fragment which holds + * {@link WifiCallingSettingsForSub} fragments. */ -public class WifiCallingSettings extends SettingsPreferenceFragment - implements SwitchBar.OnSwitchChangeListener, - Preference.OnPreferenceChangeListener { - +public class WifiCallingSettings extends SettingsPreferenceFragment { private static final String TAG = "WifiCallingSettings"; - - //String keys for preference lookup - private static final String BUTTON_WFC_MODE = "wifi_calling_mode"; - private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode"; - private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key"; - - private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1; - - public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP"; - - public static final int LAUCH_APP_ACTIVATE = 0; - public static final int LAUCH_APP_UPDATE = 1; + private List mSil; //UI objects - private SwitchBar mSwitchBar; - private Switch mSwitch; - private ListPreference mButtonWfcMode; - private ListPreference mButtonWfcRoamingMode; - private Preference mUpdateAddress; - private TextView mEmptyView; - - private boolean mValidListener = false; - private boolean mEditableWfcMode = true; - private boolean mEditableWfcRoamingMode = true; - - private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - /* - * Enable/disable controls when in/out of a call and depending on - * TTY mode and TTY support over VoLTE. - * @see android.telephony.PhoneStateListener#onCallStateChanged(int, - * java.lang.String) - */ - @Override - public void onCallStateChanged(int state, String incomingNumber) { - final SettingsActivity activity = (SettingsActivity) getActivity(); - boolean isNonTtyOrTtyOnVolteEnabled = ImsManager - .isNonTtyOrTtyOnVolteEnabled(activity); - final SwitchBar switchBar = activity.getSwitchBar(); - boolean isWfcEnabled = switchBar.getSwitch().isChecked() - && isNonTtyOrTtyOnVolteEnabled; - - switchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE) - && isNonTtyOrTtyOnVolteEnabled); - - boolean isWfcModeEditable = true; - boolean isWfcRoamingModeEditable = false; - final CarrierConfigManager configManager = (CarrierConfigManager) - activity.getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - PersistableBundle b = configManager.getConfig(); - if (b != null) { - isWfcModeEditable = b.getBoolean( - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); - isWfcRoamingModeEditable = b.getBoolean( - CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); - } - } - - Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE); - if (pref != null) { - pref.setEnabled(isWfcEnabled && isWfcModeEditable - && (state == TelephonyManager.CALL_STATE_IDLE)); - } - Preference pref_roam = getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE); - if (pref_roam != null) { - pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable - && (state == TelephonyManager.CALL_STATE_IDLE)); - } - } - }; - - private final OnPreferenceClickListener mUpdateAddressListener = - new OnPreferenceClickListener() { - /* - * Launch carrier emergency address managemnent activity - */ - @Override - public boolean onPreferenceClick(Preference preference) { - final Context context = getActivity(); - Intent carrierAppIntent = getCarrierActivityIntent(context); - if (carrierAppIntent != null) { - carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE); - startActivity(carrierAppIntent); - } - return true; - } - }; - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - final SettingsActivity activity = (SettingsActivity) getActivity(); - - mSwitchBar = activity.getSwitchBar(); - mSwitch = mSwitchBar.getSwitch(); - mSwitchBar.show(); - - mEmptyView = (TextView) getView().findViewById(android.R.id.empty); - setEmptyView(mEmptyView); - String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation) - + activity.getString(R.string.wifi_calling_off_explanation_2); - mEmptyView.setText(emptyViewText); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - mSwitchBar.hide(); - } - - private void showAlert(Intent intent) { - Context context = getActivity(); - - CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE); - CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE); - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(message) - .setTitle(title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(android.R.string.ok, null); - AlertDialog dialog = builder.create(); - dialog.show(); - } - - private IntentFilter mIntentFilter; - - private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) { - // If this fragment is active then we are immediately - // showing alert on screen. There is no need to add - // notification in this case. - // - // In order to communicate to ImsPhone that it should - // not show notification, we are changing result code here. - setResultCode(Activity.RESULT_CANCELED); - - // UX requirement is to disable WFC in case of "permanent" registration failures. - mSwitch.setChecked(false); - - showAlert(intent); - } - } - }; + private RtlCompatibleViewPager mViewPager; + private WifiCallingViewPagerAdapter mPagerAdapter; + private SlidingTabLayout mTabLayout; @Override public int getMetricsCategory() { @@ -207,242 +55,81 @@ public class WifiCallingSettings extends SettingsPreferenceFragment } @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.wifi_calling_settings_tabs, container, false); - addPreferencesFromResource(R.xml.wifi_calling_settings); + mTabLayout = view.findViewById(R.id.sliding_tabs); + mViewPager = (RtlCompatibleViewPager) view.findViewById(R.id.view_pager); - mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE); - mButtonWfcMode.setOnPreferenceChangeListener(this); + mPagerAdapter = new WifiCallingViewPagerAdapter(getChildFragmentManager(), mViewPager); + mViewPager.setAdapter(mPagerAdapter); - mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE); - mButtonWfcRoamingMode.setOnPreferenceChangeListener(this); - - mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS); - mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener); - - mIntentFilter = new IntentFilter(); - mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR); - - CarrierConfigManager configManager = (CarrierConfigManager) - getSystemService(Context.CARRIER_CONFIG_SERVICE); - boolean isWifiOnlySupported = true; - if (configManager != null) { - PersistableBundle b = configManager.getConfig(); - if (b != null) { - mEditableWfcMode = b.getBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); - mEditableWfcRoamingMode = b.getBoolean( - CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); - isWifiOnlySupported = b.getBoolean( - CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); - } - } - - if (!isWifiOnlySupported) { - mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only); - mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only); - mButtonWfcRoamingMode.setEntries( - R.array.wifi_calling_mode_choices_v2_without_wifi_only); - mButtonWfcRoamingMode.setEntryValues( - R.array.wifi_calling_mode_values_without_wifi_only); - } + return view; } @Override - public void onResume() { - super.onResume(); - - final Context context = getActivity(); - - // NOTE: Buttons will be enabled/disabled in mPhoneStateListener - boolean wfcEnabled = ImsManager.isWfcEnabledByUser(context) - && ImsManager.isNonTtyOrTtyOnVolteEnabled(context); - mSwitch.setChecked(wfcEnabled); - int wfcMode = ImsManager.getWfcMode(context, false); - int wfcRoamingMode = ImsManager.getWfcMode(context, true); - mButtonWfcMode.setValue(Integer.toString(wfcMode)); - mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode)); - updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode); - - if (ImsManager.isWfcEnabledByPlatform(context)) { - TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); - tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - - mSwitchBar.addOnSwitchChangeListener(this); - - mValidListener = true; - } - - context.registerReceiver(mIntentReceiver, mIntentFilter); - - Intent intent = getActivity().getIntent(); - if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) { - showAlert(intent); - } + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + // TODO: besides in onCreate, we should also update subList when SIM / Sub status + // changes. + updateSubList(); } @Override - public void onPause() { - super.onPause(); + public void onStart() { + super.onStart(); - final Context context = getActivity(); - - if (mValidListener) { - mValidListener = false; - - TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); - tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); - - mSwitchBar.removeOnSwitchChangeListener(this); - } - - context.unregisterReceiver(mIntentReceiver); - } - - /** - * Listens to the state change of the switch. - */ - @Override - public void onSwitchChanged(Switch switchView, boolean isChecked) { - final Context context = getActivity(); - Log.d(TAG, "onSwitchChanged(" + isChecked + ")"); - - if (!isChecked) { - updateWfcMode(context, false); - return; - } - - // Call address management activity before turning on WFC - Intent carrierAppIntent = getCarrierActivityIntent(context); - if (carrierAppIntent != null) { - carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE); - startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS); + if (mSil != null && mSil.size() > 1) { + mTabLayout.setViewPager(mViewPager); } else { - updateWfcMode(context, true); + mTabLayout.setVisibility(View.GONE); } } - /* - * Get the Intent to launch carrier emergency address management activity. - * Return null when no activity found. - */ - private static Intent getCarrierActivityIntent(Context context) { - // Retrive component name from carrirt config - CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class); - if (configManager == null) return null; + private final class WifiCallingViewPagerAdapter extends FragmentPagerAdapter { + private final RtlCompatibleViewPager mViewPager; - PersistableBundle bundle = configManager.getConfig(); - if (bundle == null) return null; - - String carrierApp = bundle.getString( - CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); - if (TextUtils.isEmpty(carrierApp)) return null; - - ComponentName componentName = ComponentName.unflattenFromString(carrierApp); - if (componentName == null) return null; - - // Build and return intent - Intent intent = new Intent(); - intent.setComponent(componentName); - return intent; - } - - /* - * Turn on/off WFC mode with ImsManager and update UI accordingly - */ - private void updateWfcMode(Context context, boolean wfcEnabled) { - Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")"); - ImsManager.setWfcSetting(context, wfcEnabled); - - int wfcMode = ImsManager.getWfcMode(context, false); - int wfcRoamingMode = ImsManager.getWfcMode(context, true); - updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode); - if (wfcEnabled) { - mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode); - } else { - mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1); + public WifiCallingViewPagerAdapter(FragmentManager fragmentManager, + RtlCompatibleViewPager viewPager) { + super(fragmentManager); + mViewPager = viewPager; } - } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - final Context context = getActivity(); - - if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) { - Log.d(TAG, "WFC emergency address activity result = " + resultCode); - - if (resultCode == Activity.RESULT_OK) { - updateWfcMode(context, true); - } + @Override + public CharSequence getPageTitle(int position) { + return String.valueOf(mSil.get(position).getDisplayName()); } - } - private void updateButtonWfcMode(Context context, boolean wfcEnabled, - int wfcMode, int wfcRoamingMode) { - mButtonWfcMode.setSummary(getWfcModeSummary(context, wfcMode)); - mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode); - // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. - mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode); + @Override + public Fragment getItem(int position) { + Log.d(TAG, "Adapter getItem " + position); + final Bundle args = new Bundle(); + args.putInt(WifiCallingSettingsForSub.FRAGMENT_BUNDLE_SUBID, + mSil.get(position).getSubscriptionId()); + WifiCallingSettingsForSub fragment = new WifiCallingSettingsForSub(); + fragment.setArguments(args); - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - boolean updateAddressEnabled = (getCarrierActivityIntent(context) != null); - if (wfcEnabled) { - if (mEditableWfcMode) { - preferenceScreen.addPreference(mButtonWfcMode); + return fragment; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + Log.d(TAG, "Adapter instantiateItem " + position); + return super.instantiateItem(container, + mViewPager.getRtlAwareIndex(position)); + } + + @Override + public int getCount() { + if (mSil == null) { + Log.d(TAG, "Adapter getCount null mSil "); + return 0; } else { - // Don't show WFC (home) preference if it's not editable. - preferenceScreen.removePreference(mButtonWfcMode); - } - if (mEditableWfcRoamingMode) { - preferenceScreen.addPreference(mButtonWfcRoamingMode); - } else { - // Don't show WFC roaming preference if it's not editable. - preferenceScreen.removePreference(mButtonWfcRoamingMode); - } - if (updateAddressEnabled) { - preferenceScreen.addPreference(mUpdateAddress); - } else { - preferenceScreen.removePreference(mUpdateAddress); - } - } else { - preferenceScreen.removePreference(mButtonWfcMode); - preferenceScreen.removePreference(mButtonWfcRoamingMode); - preferenceScreen.removePreference(mUpdateAddress); - } - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final Context context = getActivity(); - if (preference == mButtonWfcMode) { - mButtonWfcMode.setValue((String) newValue); - int buttonMode = Integer.valueOf((String) newValue); - int currentWfcMode = ImsManager.getWfcMode(context, false); - if (buttonMode != currentWfcMode) { - ImsManager.setWfcMode(context, buttonMode, false); - mButtonWfcMode.setSummary(getWfcModeSummary(context, buttonMode)); - mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); - } - if (!mEditableWfcRoamingMode) { - int currentWfcRoamingMode = ImsManager.getWfcMode(context, true); - if (buttonMode != currentWfcRoamingMode) { - ImsManager.setWfcMode(context, buttonMode, true); - // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value - } - } - } else if (preference == mButtonWfcRoamingMode) { - mButtonWfcRoamingMode.setValue((String) newValue); - int buttonMode = Integer.valueOf((String) newValue); - int currentMode = ImsManager.getWfcMode(context, true); - if (buttonMode != currentMode) { - ImsManager.setWfcMode(context, buttonMode, true); - // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. - mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); + Log.d(TAG, "Adapter getCount " + mSil.size()); + return mSil.size(); } } - return true; } public static int getWfcModeSummary(Context context, int wfcMode) { @@ -464,4 +151,22 @@ public class WifiCallingSettings extends SettingsPreferenceFragment } return resId; } + + private void updateSubList() { + mSil = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList(); + + // Only config Wfc if it's enabled by platform. + if (mSil == null) { + return; + } + for (int i = 0; i < mSil.size();) { + ImsManager imsManager = ImsManager.getInstance(getActivity(), + mSil.get(i).getSimSlotIndex()); + if (!imsManager.isWfcEnabledByPlatform()) { + mSil.remove(i); + } else { + i++; + } + } + } } diff --git a/src/com/android/settings/WifiCallingSettingsForSub.java b/src/com/android/settings/WifiCallingSettingsForSub.java new file mode 100644 index 00000000000..57a4ab2eb8a --- /dev/null +++ b/src/com/android/settings/WifiCallingSettingsForSub.java @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceClickListener; +import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; +import android.widget.TextView; + +import com.android.ims.ImsConfig; +import com.android.ims.ImsManager; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.Phone; +import com.android.settings.widget.SwitchBar; + +/** + * This is the inner class of {@link WifiCallingSettings} fragment. + * The preference screen lets you enable/disable Wi-Fi Calling and change Wi-Fi Calling mode. + */ +public class WifiCallingSettingsForSub extends SettingsPreferenceFragment + implements SwitchBar.OnSwitchChangeListener, + Preference.OnPreferenceChangeListener { + private static final String TAG = "WifiCallingSettingsForSub"; + + //String keys for preference lookup + private static final String BUTTON_WFC_MODE = "wifi_calling_mode"; + private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode"; + private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key"; + + private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1; + + public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP"; + + protected static final String FRAGMENT_BUNDLE_SUBID = "subId"; + + public static final int LAUCH_APP_ACTIVATE = 0; + public static final int LAUCH_APP_UPDATE = 1; + + //UI objects + private SwitchBar mSwitchBar; + private Switch mSwitch; + private ListPreference mButtonWfcMode; + private ListPreference mButtonWfcRoamingMode; + private Preference mUpdateAddress; + private TextView mEmptyView; + + private boolean mValidListener = false; + private boolean mEditableWfcMode = true; + private boolean mEditableWfcRoamingMode = true; + + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private ImsManager mImsManager; + + private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + /* + * Enable/disable controls when in/out of a call and depending on + * TTY mode and TTY support over VoLTE. + * @see android.telephony.PhoneStateListener#onCallStateChanged(int, + * java.lang.String) + */ + @Override + public void onCallStateChanged(int state, String incomingNumber) { + final SettingsActivity activity = (SettingsActivity) getActivity(); + boolean isNonTtyOrTtyOnVolteEnabled = mImsManager.isNonTtyOrTtyOnVolteEnabled(); + boolean isWfcEnabled = mSwitchBar.isChecked() + && isNonTtyOrTtyOnVolteEnabled; + + mSwitchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE) + && isNonTtyOrTtyOnVolteEnabled); + + boolean isWfcModeEditable = true; + boolean isWfcRoamingModeEditable = false; + final CarrierConfigManager configManager = (CarrierConfigManager) + activity.getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager != null) { + PersistableBundle b = configManager.getConfigForSubId(mSubId); + if (b != null) { + isWfcModeEditable = b.getBoolean( + CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); + isWfcRoamingModeEditable = b.getBoolean( + CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); + } + } + + Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE); + if (pref != null) { + pref.setEnabled(isWfcEnabled && isWfcModeEditable + && (state == TelephonyManager.CALL_STATE_IDLE)); + } + Preference pref_roam = + getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE); + if (pref_roam != null) { + pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable + && (state == TelephonyManager.CALL_STATE_IDLE)); + } + } + }; + + @Override + protected int getHelpResource() { + // Helper resource is already defined in the container fragment. + return 0; + } + + private final OnPreferenceClickListener mUpdateAddressListener = + new OnPreferenceClickListener() { + /* + * Launch carrier emergency address managemnent activity + */ + @Override + public boolean onPreferenceClick(Preference preference) { + Intent carrierAppIntent = getCarrierActivityIntent(); + if (carrierAppIntent != null) { + carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE); + startActivity(carrierAppIntent); + } + return true; + } + }; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + final SettingsActivity activity = (SettingsActivity) getActivity(); + + mEmptyView = (TextView) getView().findViewById(android.R.id.empty); + setEmptyView(mEmptyView); + String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation) + + activity.getString(R.string.wifi_calling_off_explanation_2); + mEmptyView.setText(emptyViewText); + + mSwitchBar = getView().findViewById(R.id.switch_bar); + mSwitchBar.show(); + mSwitch = mSwitchBar.getSwitch(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mSwitchBar.hide(); + } + + private void showAlert(Intent intent) { + Context context = getActivity(); + + CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE); + CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(message) + .setTitle(title) + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(android.R.string.ok, null); + AlertDialog dialog = builder.create(); + dialog.show(); + } + + private IntentFilter mIntentFilter; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) { + // If this fragment is active then we are immediately + // showing alert on screen. There is no need to add + // notification in this case. + // + // In order to communicate to ImsPhone that it should + // not show notification, we are changing result code here. + setResultCode(Activity.RESULT_CANCELED); + + // UX requirement is to disable WFC in case of "permanent" registration failures. + mSwitch.setChecked(false); + + showAlert(intent); + } + } + }; + + @Override + public int getMetricsCategory() { + return MetricsEvent.WIFI_CALLING_FOR_SUB; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.wifi_calling_settings); + + // SubId should always be specified when creating this fragment. Either through + // fragment.setArguments() or through savedInstanceState. + if (getArguments() != null && getArguments().containsKey(FRAGMENT_BUNDLE_SUBID)) + { + mSubId = getArguments().getInt(FRAGMENT_BUNDLE_SUBID); + } else if (savedInstanceState != null) { + mSubId = savedInstanceState.getInt( + FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + mImsManager = ImsManager.getInstance( + getActivity(), SubscriptionManager.getPhoneId(mSubId)); + + mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE); + mButtonWfcMode.setOnPreferenceChangeListener(this); + + mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE); + mButtonWfcRoamingMode.setOnPreferenceChangeListener(this); + + mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS); + mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener); + + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putInt(FRAGMENT_BUNDLE_SUBID, mSubId); + super.onSaveInstanceState(outState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate( + R.layout.wifi_calling_settings_preferences, container, false); + + final ViewGroup prefs_container = view.findViewById(R.id.prefs_container); + Utils.prepareCustomPreferencesList(container, view, prefs_container, false); + View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState); + prefs_container.addView(prefs); + + return view; + } + + private void updateBody() { + CarrierConfigManager configManager = (CarrierConfigManager) + getSystemService(Context.CARRIER_CONFIG_SERVICE); + boolean isWifiOnlySupported = true; + + if (configManager != null) { + PersistableBundle b = configManager.getConfigForSubId(mSubId); + if (b != null) { + mEditableWfcMode = b.getBoolean( + CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL); + mEditableWfcRoamingMode = b.getBoolean( + CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); + isWifiOnlySupported = b.getBoolean( + CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); + } + } + + if (!isWifiOnlySupported) { + mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only); + mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only); + mButtonWfcRoamingMode.setEntries( + R.array.wifi_calling_mode_choices_v2_without_wifi_only); + mButtonWfcRoamingMode.setEntryValues( + R.array.wifi_calling_mode_values_without_wifi_only); + } + + + // NOTE: Buttons will be enabled/disabled in mPhoneStateListener + boolean wfcEnabled = mImsManager.isWfcEnabledByUser() + && mImsManager.isNonTtyOrTtyOnVolteEnabled(); + mSwitch.setChecked(wfcEnabled); + int wfcMode = mImsManager.getWfcMode(false); + int wfcRoamingMode = mImsManager.getWfcMode(true); + mButtonWfcMode.setValue(Integer.toString(wfcMode)); + mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode)); + updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode); + } + + @Override + public void onResume() { + super.onResume(); + + final Context context = getActivity(); + + updateBody(); + + if (mImsManager.isWfcEnabledByPlatform()) { + TelephonyManager tm = + (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + + mSwitchBar.addOnSwitchChangeListener(this); + + mValidListener = true; + } + + context.registerReceiver(mIntentReceiver, mIntentFilter); + + Intent intent = getActivity().getIntent(); + if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) { + showAlert(intent); + } + } + + @Override + public void onPause() { + super.onPause(); + + final Context context = getActivity(); + + if (mValidListener) { + mValidListener = false; + + TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + + mSwitchBar.removeOnSwitchChangeListener(this); + } + + context.unregisterReceiver(mIntentReceiver); + } + + /** + * Listens to the state change of the switch. + */ + @Override + public void onSwitchChanged(Switch switchView, boolean isChecked) { + Log.d(TAG, "onSwitchChanged(" + isChecked + ")"); + + if (!isChecked) { + updateWfcMode(false); + return; + } + + // Call address management activity before turning on WFC + Intent carrierAppIntent = getCarrierActivityIntent(); + if (carrierAppIntent != null) { + carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE); + startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS); + } else { + updateWfcMode(true); + } + } + + /* + * Get the Intent to launch carrier emergency address management activity. + * Return null when no activity found. + */ + private Intent getCarrierActivityIntent() { + // Retrive component name from carrier config + CarrierConfigManager configManager = + getActivity().getSystemService(CarrierConfigManager.class); + if (configManager == null) return null; + + PersistableBundle bundle = configManager.getConfigForSubId(mSubId); + if (bundle == null) return null; + + String carrierApp = bundle.getString( + CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); + if (TextUtils.isEmpty(carrierApp)) return null; + + ComponentName componentName = ComponentName.unflattenFromString(carrierApp); + if (componentName == null) return null; + + // Build and return intent + Intent intent = new Intent(); + intent.setComponent(componentName); + return intent; + } + + /* + * Turn on/off WFC mode with ImsManager and update UI accordingly + */ + private void updateWfcMode(boolean wfcEnabled) { + Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")"); + mImsManager.setWfcSetting(wfcEnabled); + + int wfcMode = mImsManager.getWfcMode(false); + int wfcRoamingMode = mImsManager.getWfcMode(true); + updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode); + if (wfcEnabled) { + mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode); + } else { + mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + final Context context = getActivity(); + + if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) { + Log.d(TAG, "WFC emergency address activity result = " + resultCode); + + if (resultCode == Activity.RESULT_OK) { + updateWfcMode(true); + } + } + } + + private void updateButtonWfcMode(boolean wfcEnabled, + int wfcMode, int wfcRoamingMode) { + mButtonWfcMode.setSummary(getWfcModeSummary(wfcMode)); + mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode); + // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. + mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode); + + final PreferenceScreen preferenceScreen = getPreferenceScreen(); + boolean updateAddressEnabled = (getCarrierActivityIntent() != null); + if (wfcEnabled) { + if (mEditableWfcMode) { + preferenceScreen.addPreference(mButtonWfcMode); + } else { + // Don't show WFC (home) preference if it's not editable. + preferenceScreen.removePreference(mButtonWfcMode); + } + if (mEditableWfcRoamingMode) { + preferenceScreen.addPreference(mButtonWfcRoamingMode); + } else { + // Don't show WFC roaming preference if it's not editable. + preferenceScreen.removePreference(mButtonWfcRoamingMode); + } + if (updateAddressEnabled) { + preferenceScreen.addPreference(mUpdateAddress); + } else { + preferenceScreen.removePreference(mUpdateAddress); + } + } else { + preferenceScreen.removePreference(mButtonWfcMode); + preferenceScreen.removePreference(mButtonWfcRoamingMode); + preferenceScreen.removePreference(mUpdateAddress); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mButtonWfcMode) { + Log.d(TAG, "onPreferenceChange mButtonWfcMode " + newValue); + mButtonWfcMode.setValue((String) newValue); + int buttonMode = Integer.valueOf((String) newValue); + int currentWfcMode = mImsManager.getWfcMode(false); + if (buttonMode != currentWfcMode) { + mImsManager.setWfcMode(buttonMode, false); + mButtonWfcMode.setSummary(getWfcModeSummary(buttonMode)); + mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); + } + if (!mEditableWfcRoamingMode) { + int currentWfcRoamingMode = mImsManager.getWfcMode(true); + if (buttonMode != currentWfcRoamingMode) { + mImsManager.setWfcMode(buttonMode, true); + // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value + } + } + } else if (preference == mButtonWfcRoamingMode) { + mButtonWfcRoamingMode.setValue((String) newValue); + int buttonMode = Integer.valueOf((String) newValue); + int currentMode = mImsManager.getWfcMode(true); + if (buttonMode != currentMode) { + mImsManager.setWfcMode(buttonMode, true); + // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value. + mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode); + } + } + return true; + } + + private int getWfcModeSummary(int wfcMode) { + int resId = com.android.internal.R.string.wifi_calling_off_summary; + if (mImsManager.isWfcEnabledByUser()) { + switch (wfcMode) { + case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY: + resId = com.android.internal.R.string.wfc_mode_wifi_only_summary; + break; + case ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED: + resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary; + break; + case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED: + resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; + break; + default: + Log.e(TAG, "Unexpected WFC mode value: " + wfcMode); + } + } + return resId; + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable index d4a8d2d1645..a9fb589970a 100644 --- a/tests/robotests/assets/grandfather_not_implementing_indexable +++ b/tests/robotests/assets/grandfather_not_implementing_indexable @@ -79,6 +79,7 @@ com.android.settings.ApnSettings com.android.settings.SecuritySettings$SecuritySubSettings com.android.settings.PrivacySettings com.android.settings.WifiCallingSettings +com.android.settings.WifiCallingSettingsForSub com.android.settings.password.SetupChooseLockGeneric$SetupChooseLockGenericFragment com.android.settings.SetupRedactionInterstitial$SetupRedactionInterstitialFragment com.android.settings.TrustAgentSettings diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk index a91dcb1539e..ec0f0b6662c 100644 --- a/tests/unit/Android.mk +++ b/tests/unit/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ diff --git a/tests/unit/src/com/android/settings/utils/MockedServiceManager.java b/tests/unit/src/com/android/settings/utils/MockedServiceManager.java new file mode 100644 index 00000000000..ea04974ddc3 --- /dev/null +++ b/tests/unit/src/com/android/settings/utils/MockedServiceManager.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.testutils; + +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +// This class is for replacing existing system service with the mocked service. +// Copied from CellBroadcastReceiver app. +public final class MockedServiceManager { + + private final String TAG = MockedServiceManager.class.getSimpleName(); + + private final HashMap mServiceManagerMockedServices = new HashMap<>(); + + private final HashMap mOldInstances = new HashMap<>(); + + private final LinkedList mInstanceKeys = new LinkedList<>(); + + private static class InstanceKey { + final Class mClass; + final String mInstName; + final Object mObj; + + InstanceKey(final Class c, final String instName, final Object obj) { + mClass = c; + mInstName = instName; + mObj = obj; + } + + @Override + public int hashCode() { + return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + + InstanceKey other = (InstanceKey) obj; + return (other.mClass == mClass && other.mInstName.equals(mInstName) + && other.mObj == mObj); + } + } + + public MockedServiceManager() throws Exception { + replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices); + } + + public void replaceService(String key, IBinder binder) { + mServiceManagerMockedServices.put(key, binder); + } + + public void restoreAllServices() throws Exception { + restoreInstances(); + } + + public synchronized void replaceInstance(final Class c, final String instanceName, + final Object obj, final Object newValue) + throws Exception { + Field field = c.getDeclaredField(instanceName); + field.setAccessible(true); + + InstanceKey key = new InstanceKey(c, instanceName, obj); + if (!mOldInstances.containsKey(key)) { + mOldInstances.put(key, field.get(obj)); + mInstanceKeys.add(key); + } + field.set(obj, newValue); + } + + public synchronized void restoreInstances() throws Exception { + Iterator it = mInstanceKeys.descendingIterator(); + + while (it.hasNext()) { + InstanceKey key = it.next(); + Field field = key.mClass.getDeclaredField(key.mInstName); + field.setAccessible(true); + field.set(key.mObj, mOldInstances.get(key)); + } + + mInstanceKeys.clear(); + mOldInstances.clear(); + } +} diff --git a/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java b/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java new file mode 100644 index 00000000000..16617d046bf --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java @@ -0,0 +1,299 @@ +/** + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.wifi; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isSelected; +import static android.support.test.espresso.matcher.ViewMatchers.withResourceName; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.anything; +import static org.junit.Assert.assertEquals; +import static org.junit.matchers.JUnitMatchers.containsString; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.support.test.InstrumentationRegistry; +import android.support.test.espresso.NoMatchingViewException; +import android.support.test.espresso.ViewInteraction; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; +import android.telephony.SubscriptionInfo; + +import com.android.ims.ImsConfig; +import com.android.ims.ImsManager; +import com.android.internal.telephony.SubscriptionController; +import com.android.settings.testutils.MockedServiceManager; + +import junit.framework.Assert; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class WifiCallingSettingUiTest { + private static final String SUBSCRIPTION0_NAME = "SUB0"; + private static final String SUBSCRIPTION1_NAME = "SUB1"; + private static final String WFC_MODE_TITLE = "Calling preference"; + private static final String WFC_MODE_WIFI_ONLY = "Wi-Fi only"; + private static final String WFC_MODE_WIFI_PREFERRED = "Wi-Fi preferred"; + private static final String WFC_MODE_CELLULAR_PREFERRED = "Mobile preferred"; + + private Instrumentation mInstrumentation; + private Context mContext; + private UiDevice mDevice; + @Mock + SubscriptionController mSubscriptionController; + MockedServiceManager mMockedServiceManager; + protected HashMap mImsManagerInstances = new HashMap<>(); + List mSils = new ArrayList(); + @Mock + SubscriptionInfo mSubscriptionInfo0; + @Mock + SubscriptionInfo mSubscriptionInfo1; + @Mock + ImsManager mImsManager0; + @Mock + ImsManager mImsManager1; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mContext = mInstrumentation.getTargetContext(); + mDevice = UiDevice.getInstance(mInstrumentation); + + mMockedServiceManager = new MockedServiceManager(); + mMockedServiceManager.replaceService("isub", mSubscriptionController); + + mMockedServiceManager.replaceInstance( + ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances); + mMockedServiceManager.replaceInstance( + SubscriptionController.class, "sInstance", null, mSubscriptionController); + doReturn(mSubscriptionController) + .when(mSubscriptionController).queryLocalInterface(anyString()); + mImsManagerInstances.put(0, mImsManager0); + mImsManagerInstances.put(1, mImsManager1); + doReturn(mSils).when(mSubscriptionController).getActiveSubscriptionInfoList(anyString()); + doReturn(0).when(mSubscriptionController).getPhoneId(0); + doReturn(1).when(mSubscriptionController).getPhoneId(1); + doReturn(0).when(mSubscriptionInfo0).getSubscriptionId(); + doReturn(1).when(mSubscriptionInfo1).getSubscriptionId(); + doReturn(0).when(mSubscriptionInfo0).getSimSlotIndex(); + doReturn(1).when(mSubscriptionInfo1).getSimSlotIndex(); + doReturn(SUBSCRIPTION0_NAME).when(mSubscriptionInfo0).getDisplayName(); + doReturn(SUBSCRIPTION1_NAME).when(mSubscriptionInfo1).getDisplayName(); + + doReturn(true).when(mImsManager0).isWfcEnabledByPlatform(); + doReturn(true).when(mImsManager0).isNonTtyOrTtyOnVolteEnabled(); + doReturn(true).when(mImsManager1).isWfcEnabledByPlatform(); + doReturn(true).when(mImsManager1).isNonTtyOrTtyOnVolteEnabled(); + + mDevice.wakeUp(); + mDevice.pressMenu(); + } + + @After + public void tearDown() throws Exception { + mMockedServiceManager.restoreAllServices(); + } + + @Test + public void testSingleSimUi() throws InterruptedException { + configureSingleSim(); + doReturn(true).when(mImsManager0).isWfcEnabledByUser(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(anyBoolean()); + + mInstrumentation.startActivitySync(createActivityIntent()); + + checkSingleSimUi(); + + try { + mDevice.setOrientationLeft(); + } catch (Exception e) { + Assert.fail("Exception " + e); + } + + // Re-check after rotation. Fragment should be recreated properly. + checkSingleSimUi(); + + try { + mDevice.setOrientationNatural(); + } catch (Exception e) { + Assert.fail("Exception " + e); + } + + // Re-check after rotation. Fragment should be resumed properly. + checkSingleSimUi(); + } + + private void checkSingleSimUi() { + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME)))); + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME)))); + assertEquals(true, checkExists(onView(withText(WFC_MODE_TITLE)))); + assertEquals(true, checkExists(onView(withText(WFC_MODE_WIFI_PREFERRED)))); + checkSwitchBarStatus(true, true); + checkEmptyViewStatus(false); + } + + @Test + public void testNoValidSub() throws InterruptedException { + configureDualSim(); + doReturn(false).when(mImsManager0).isWfcEnabledByPlatform(); + doReturn(false).when(mImsManager0).isNonTtyOrTtyOnVolteEnabled(); + doReturn(false).when(mImsManager1).isWfcEnabledByPlatform(); + doReturn(false).when(mImsManager1).isNonTtyOrTtyOnVolteEnabled(); + doReturn(false).when(mImsManager0).isWfcEnabledByUser(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(anyBoolean()); + + Activity activity = mInstrumentation.startActivitySync(createActivityIntent()); + + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME)))); + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME)))); + assertEquals(false, checkExists(onView(withText(WFC_MODE_TITLE)))); + + checkSwitchBarStatus(false, false); + checkEmptyViewStatus(false); + } + + @Test + public void testWfcDisabled() throws InterruptedException { + configureSingleSim(); + doReturn(false).when(mImsManager0).isWfcEnabledByUser(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED) + .when(mImsManager0).getWfcMode(anyBoolean()); + + Activity activity = mInstrumentation.startActivitySync(createActivityIntent()); + + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME)))); + assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME)))); + assertEquals(false, checkExists(onView(withText(WFC_MODE_TITLE)))); + + checkSwitchBarStatus(true, false); + checkEmptyViewStatus(true); + } + + @Test + public void testDualSimUi() throws InterruptedException { + configureDualSim(); + doReturn(true).when(mImsManager0).isWfcEnabledByUser(); + doReturn(false).when(mImsManager1).isWfcEnabledByUser(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED) + .when(mImsManager0).getWfcMode(); + doReturn(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED) + .when(mImsManager0).getWfcMode(anyBoolean()); + + mInstrumentation.startActivitySync(createActivityIntent()); + + assertEquals(true, checkExists(onView(withText(SUBSCRIPTION0_NAME)))); + assertEquals(true, checkExists(onView(withText(SUBSCRIPTION1_NAME)))); + assertEquals(true, checkExists(onView(withText(WFC_MODE_TITLE)))); + assertEquals(true, checkExists(onView(withText(WFC_MODE_CELLULAR_PREFERRED)))); + + onView(withText(SUBSCRIPTION0_NAME)).check(matches(isSelected())); + checkSwitchBarStatus(true, true); + checkEmptyViewStatus(false); + + // Switch to SUB1. + onView(withText(SUBSCRIPTION1_NAME)).perform(click()); + + checkSwitchBarStatus(true, false); + checkEmptyViewStatus(true); + onView(withText(SUBSCRIPTION1_NAME)).check(matches(isSelected())); + } + + private boolean checkExists(ViewInteraction v) { + try { + v.check(matches(isCompletelyDisplayed())); + return true; + } catch (NoMatchingViewException e) { + return false; + } + } + + private Intent createActivityIntent() { + Intent intent = new Intent(mContext, + com.android.settings.Settings.WifiCallingSettingsActivity.class); + intent.setPackage("com.android.settings"); + intent.setAction("android.intent.action.MAIN"); + return intent; + } + + private void configureSingleSim() { + mSils.clear(); + mSils.add(mSubscriptionInfo0); + } + + private void configureDualSim() { + mSils.clear(); + mSils.add(mSubscriptionInfo0); + mSils.add(mSubscriptionInfo1); + } + + private void checkSwitchBarStatus(boolean shouldDisplay, boolean statusOn) { + if (shouldDisplay) { + try { + onView(allOf(withResourceName("switch_text"), isCompletelyDisplayed())) + .check(matches(withText(containsString(statusOn ? "On" : "Off")))); + } catch (Exception e) { + Assert.fail("Exception " + e); + } + } else { + onView(allOf(withResourceName("switch_text"), isCompletelyDisplayed())) + .check(doesNotExist()); + } + } + + private void checkEmptyViewStatus(boolean shouldDisplay) { + try { + if (!shouldDisplay) { + onView(allOf(withResourceName("empty"), isCompletelyDisplayed())) + .check(doesNotExist()); + } else { + onView(allOf(withResourceName("empty"), isCompletelyDisplayed())) + .check(matches(anything())); + } + } catch (Exception e) { + Assert.fail("Exception " + e); + } + } +}