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
diff --git a/res/layout/wifi_calling_settings_preferences.xml b/res/layout/wifi_calling_settings_preferences.xml
new file mode 100644
index 0000000..4e64f40
--- /dev/null
+++ b/res/layout/wifi_calling_settings_preferences.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tabs_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.android.settings.widget.SwitchBar
+ android:id="@+id/switch_bar"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:background="@drawable/switchbar_background"
+ android:theme="?attr/switchBarTheme" />
+
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="0dip"
+ android:layout_height="0dip" />
+
+ <FrameLayout
+ android:id="@+id/prefs_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:smoothScrollbar="false" />
+
+</LinearLayout>
diff --git a/res/layout/wifi_calling_settings_tabs.xml b/res/layout/wifi_calling_settings_tabs.xml
new file mode 100644
index 0000000..1e27b47
--- /dev/null
+++ b/res/layout/wifi_calling_settings_tabs.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tabs_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.android.settings.widget.SlidingTabLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sliding_tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="none"
+ android:fillViewport="true"/>
+
+ <com.android.settings.widget.RtlCompatibleViewPager
+ android:id="@+id/view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/src/com/android/settings/WifiCallingSettings.java b/src/com/android/settings/WifiCallingSettings.java
index cb661ed..e872fb8 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<SubscriptionInfo> 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 @@
}
@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 @@
}
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 0000000..57a4ab2
--- /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 d4a8d2d..a9fb589 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -79,6 +79,7 @@
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 a91dcb1..ec0f0b6 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -5,7 +5,7 @@
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 0000000..ea04974
--- /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<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
+
+ private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
+
+ private final LinkedList<InstanceKey> 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<InstanceKey> 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 0000000..16617d0
--- /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<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
+ List<SubscriptionInfo> 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);
+ }
+ }
+}