Base implementation of WFC disclaimer UI

Test: manual - Check that no error occurred when changing the wifi
calling settings to turn on.
Test: auto - Passed WifiCallingSettingsForSubTest,
WifiCallingDisclaimerFragmentTest and DisclaimerItemListAdapterTest.
Bug: 67872298

Change-Id: I789f530d3e16baa6e56feaa4269f6696976f747e
This commit is contained in:
manabu, shimoda
2017-11-08 11:32:31 +09:00
committed by Hall Liu
parent 60d64cecd4
commit 9fae73915c
16 changed files with 1029 additions and 15 deletions

View File

@@ -3124,6 +3124,18 @@
<activity android:name=".homepage.contextualcards.ContextualCardFeedbackDialog" <activity android:name=".homepage.contextualcards.ContextualCardFeedbackDialog"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" /> android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
<activity
android:name="Settings$WifiCallingDisclaimerActivity"
android:label="@string/wifi_calling_settings_title"
android:taskAffinity="com.android.settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.wifi.calling.WifiCallingDisclaimerFragment" />
</activity>
<!-- This is the longest AndroidManifest.xml ever. --> <!-- This is the longest AndroidManifest.xml ever. -->
</application> </application>
</manifest> </manifest>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginBottom="20dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/wfc_disclaimer_title_text" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/disclaimer_item_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/disagree_button"
style="@style/DisclaimerNegativeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/wfc_disclaimer_disagree_text" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:id="@+id/agree_button"
style="@style/DisclaimerPositiveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/wfc_disclaimer_agree_button_text" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="20dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/disclaimer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
<TextView
android:id="@+id/disclaimer_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"
android:layout_below="@id/disclaimer_title" />
</RelativeLayout>

View File

@@ -10758,6 +10758,15 @@
<!-- Summary for represent which device is playing media [CHAR LIMIT=NONE] --> <!-- Summary for represent which device is playing media [CHAR LIMIT=NONE] -->
<string name="media_output_panel_summary_of_playing_device">Currently playing on <xliff:g id="device_name" example="Bose headphone">%1$s</xliff:g></string> <string name="media_output_panel_summary_of_playing_device">Currently playing on <xliff:g id="device_name" example="Bose headphone">%1$s</xliff:g></string>
<!-- Label for the title on wfc disclaimer fragment. [CHAR LIMIT=40] -->
<string name="wfc_disclaimer_title_text">Important information</string>
<!-- Label for the agree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
<string name="wfc_disclaimer_agree_button_text">CONTINUE</string>
<!-- Label for the disagree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
<string name="wfc_disclaimer_disagree_text">NO THANKS</string>
<!-- Message for forget passpoint dialog [CHAR LIMIT=none] --> <!-- Message for forget passpoint dialog [CHAR LIMIT=none] -->
<string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string> <string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string>

View File

@@ -520,4 +520,17 @@
<!-- Padding between content and the start icon is 8dp. --> <!-- Padding between content and the start icon is 8dp. -->
<item name="contentStartPadding">8dp</item> <item name="contentStartPadding">8dp</item>
</style> </style>
<style name="DisclaimerPositiveButton" parent="@style/SudGlifButton.Primary">
<item name="android:layout_margin">16dp</item>
<item name="android:paddingStart">8dp</item>
<item name="android:paddingEnd">8dp</item>
</style>
<style name="DisclaimerNegativeButton" parent="@style/SudGlifButton.Secondary">
<item name="android:layout_margin">16dp</item>
<item name="android:paddingStart">8dp</item>
<item name="android:paddingEnd">8dp</item>
</style>
</resources> </resources>

View File

@@ -159,6 +159,7 @@ public class Settings extends SettingsActivity {
public static class WebViewAppPickerActivity extends SettingsActivity { /* empty */ } public static class WebViewAppPickerActivity extends SettingsActivity { /* empty */ }
public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ } public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ }
public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ } public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ }
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
// Top level categories for new IA // Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {} public static class NetworkDashboardActivity extends SettingsActivity {}

View File

@@ -135,6 +135,7 @@ import com.android.settings.wifi.ConfigureWifiSettings;
import com.android.settings.wifi.WifiAPITest; import com.android.settings.wifi.WifiAPITest;
import com.android.settings.wifi.WifiInfo; import com.android.settings.wifi.WifiInfo;
import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.calling.WifiCallingDisclaimerFragment;
import com.android.settings.wifi.calling.WifiCallingSettings; import com.android.settings.wifi.calling.WifiCallingSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings; import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings; import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
@@ -260,6 +261,7 @@ public class SettingsGateway {
ConnectedDeviceDashboardFragment.class.getName(), ConnectedDeviceDashboardFragment.class.getName(),
UsbDetailsFragment.class.getName(), UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(), AppAndNotificationDashboardFragment.class.getName(),
WifiCallingDisclaimerFragment.class.getName(),
AccountDashboardFragment.class.getName(), AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(), EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(), WebViewAppPicker.class.getName(),

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2018 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.calling;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
/**
* Interface to control disclaimer item from {@link WifiCallingDisclaimerFragment}.
*/
@VisibleForTesting
public abstract class DisclaimerItem {
private static final String SHARED_PREFERENCES_NAME = "wfc_disclaimer_prefs";
protected final Context mContext;
protected final int mSubId;
private final CarrierConfigManager mCarrierConfigManager;
DisclaimerItem(Context context, int subId) {
mContext = context;
mSubId = subId;
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
}
/**
* Called by the {@link WifiCallingDisclaimerFragment} when a user has clicked the agree button.
*/
void onAgreed() {
setBooleanSharedPrefs(getPrefKey(), true);
}
/**
* Checks whether the disclaimer item need to be displayed or not.
*
* @return Returns {@code true} if disclaimer item need to be displayed,
* {@code false} if not displayed.
*/
boolean shouldShow() {
if (getBooleanSharedPrefs(getPrefKey(), false)) {
logd("shouldShow: false due to a user has already agreed.");
return false;
}
logd("shouldShow: true");
return true;
}
/**
* Gets the configuration values for a particular sub id.
*
* @return The {@link PersistableBundle} instance containing the config value for a
* particular phone id, or default values.
*/
protected PersistableBundle getCarrierConfig() {
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(mSubId);
if (config != null) {
return config;
}
// Return static default defined in CarrierConfigManager.
return CarrierConfigManager.getDefaultConfig();
}
protected void logd(String msg) {
Log.d(getName(), "[" + mSubId + "] " + msg);
}
/**
* Gets a title id for disclaimer item.
*
* @return Title id for disclaimer item.
*/
protected abstract int getTitleId();
/**
* Gets a message id for disclaimer item.
*
* @return Message id for disclaimer item.
*/
protected abstract int getMessageId();
/**
* Gets a name of disclaimer item.
*
* @return Name of disclaimer item.
*/
protected abstract String getName();
/**
* Gets a preference key to keep user's consent.
*
* @return Preference key to keep user's consent.
*/
protected abstract String getPrefKey();
/**
* Gets the boolean value from shared preferences.
*
* @param key The key for the preference item.
* @param defValue Value to return if this preference does not exist.
* @return The boolean value of corresponding key, or defValue.
*/
private boolean getBooleanSharedPrefs(String key, boolean defValue) {
SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
return prefs.getBoolean(key + mSubId, defValue);
}
/**
* Sets the boolean value to shared preferences.
*
* @param key The key for the preference item.
* @param value The value to be set for shared preferences.
*/
private void setBooleanSharedPrefs(String key, boolean value) {
SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
prefs.edit().putBoolean(key + mSubId, value).apply();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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.calling;
import android.content.Context;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Factory class to create disclaimer items list.
*/
@VisibleForTesting
public final class DisclaimerItemFactory {
/**
* Creates disclaimer items list.
*
* @param context The application context
* @param subId The subscription id.
* @return The {@link DisclaimerItem} list instance, if there are no items, return empty list.
*/
public static List<DisclaimerItem> create(Context context, int subId) {
List<DisclaimerItem> itemList = getDisclaimerItemList(context, subId);
Iterator itr = itemList.iterator();
while (itr.hasNext()) {
DisclaimerItem item = (DisclaimerItem) itr.next();
if (!item.shouldShow()) {
itr.remove();
}
}
return itemList;
}
private static List<DisclaimerItem> getDisclaimerItemList(Context context, int subId) {
List<DisclaimerItem> itemList = new ArrayList<DisclaimerItem>();
return itemList;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2018 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.calling;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import java.util.List;
/**
* Adapter for disclaimer items list.
*/
public class DisclaimerItemListAdapter extends
RecyclerView.Adapter<DisclaimerItemListAdapter.DisclaimerItemViewHolder> {
private List<DisclaimerItem> mDisclaimerItemList;
public DisclaimerItemListAdapter(List<DisclaimerItem> list) {
mDisclaimerItemList = list;
}
@Override
public DisclaimerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.wfc_simple_disclaimer_item, null, false);
return new DisclaimerItemViewHolder(view);
}
@Override
public void onBindViewHolder(DisclaimerItemViewHolder holder, int position) {
holder.titleView.setText(mDisclaimerItemList.get(position).getTitleId());
holder.descriptionView.setText(mDisclaimerItemList.get(position).getMessageId());
}
@Override
public int getItemCount() {
return mDisclaimerItemList.size();
}
public static class DisclaimerItemViewHolder extends RecyclerView.ViewHolder {
@VisibleForTesting
static final int ID_DISCLAIMER_ITEM_TITLE = R.id.disclaimer_title;
@VisibleForTesting
static final int ID_DISCLAIMER_ITEM_DESCRIPTION = R.id.disclaimer_desc;
public final TextView titleView;
public final TextView descriptionView;
public DisclaimerItemViewHolder(View itemView) {
super(itemView);
titleView = itemView.findViewById(ID_DISCLAIMER_ITEM_TITLE);
descriptionView = itemView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION);
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2018 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.calling;
import android.app.Activity;
import android.os.Bundle;
import android.telephony.SubscriptionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import java.util.ArrayList;
import java.util.List;
/**
* Fragment for displaying disclaimers for WFC.
*/
public class WifiCallingDisclaimerFragment extends InstrumentedFragment
implements View.OnClickListener {
private static final String TAG = "WifiCallingDisclaimerFragment";
private static final String STATE_IS_SCROLL_TO_BOTTOM = "state_is_scroll_to_bottom";
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<DisclaimerItem>();
private Button mAgreeButton;
private Button mDisagreeButton;
private boolean mScrollToBottom;
@Override
public int getMetricsCategory() {
return MetricsEvent.WIFI_CALLING;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle args = getArguments();
final int subId = (args != null) ? args.getInt(WifiCallingSettingsForSub.EXTRA_SUB_ID)
: SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
mDisclaimerItemList = DisclaimerItemFactory.create(getActivity(), subId);
if (mDisclaimerItemList.isEmpty()) {
finish(Activity.RESULT_OK);
return;
}
if (savedInstanceState != null) {
mScrollToBottom = savedInstanceState.getBoolean(
STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.wfc_disclaimer_fragment, container, false);
mAgreeButton = view.findViewById(R.id.agree_button);
mAgreeButton.setOnClickListener(this);
mDisagreeButton = view.findViewById(R.id.disagree_button);
mDisagreeButton.setOnClickListener(this);
final RecyclerView recyclerView = (RecyclerView) view.findViewById(
R.id.disclaimer_item_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(new DisclaimerItemListAdapter(mDisclaimerItemList));
RecyclerView.ItemDecoration itemDecoration =
new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(itemDecoration);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1 /* scrolling down */)) {
mScrollToBottom = true;
updateButtonState();
recyclerView.removeOnScrollListener(this);
}
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
updateButtonState();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
}
private void updateButtonState() {
mAgreeButton.setEnabled(mScrollToBottom);
}
@Override
public void onClick(View v) {
if (v == mAgreeButton) {
for (DisclaimerItem item : mDisclaimerItemList) {
item.onAgreed();
}
finish(Activity.RESULT_OK);
} else if (v == mDisagreeButton) {
finish(Activity.RESULT_CANCELED);
}
}
@VisibleForTesting
void finish(int result) {
Activity activity = getActivity();
activity.setResult(result, null);
activity.finish();
}
}

View File

@@ -53,6 +53,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBar;
/** /**
@@ -69,9 +70,13 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_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 String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1; @VisibleForTesting
static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
@VisibleForTesting
static final int REQUEST_CHECK_WFC_DISCLAIMER = 2;
public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP"; public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
public static final String EXTRA_SUB_ID = "EXTRA_SUB_ID";
protected static final String FRAGMENT_BUNDLE_SUBID = "subId"; protected static final String FRAGMENT_BUNDLE_SUBID = "subId";
@@ -172,7 +177,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
mEmptyView = getView().findViewById(android.R.id.empty); mEmptyView = getView().findViewById(android.R.id.empty);
setEmptyView(mEmptyView); setEmptyView(mEmptyView);
final Resources res = SubscriptionManager.getResourcesForSubId(getContext(), mSubId); final Resources res = getResourcesForSubId();
String emptyViewText = res.getString(R.string.wifi_calling_off_explanation, String emptyViewText = res.getString(R.string.wifi_calling_off_explanation,
res.getString(R.string.wifi_calling_off_explanation_2)); res.getString(R.string.wifi_calling_off_explanation_2));
mEmptyView.setText(emptyViewText); mEmptyView.setText(emptyViewText);
@@ -416,14 +421,17 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
return; return;
} }
// Call address management activity before turning on WFC // Launch disclaimer fragment before turning on WFC
Intent carrierAppIntent = getCarrierActivityIntent(); final Context context = getActivity();
if (carrierAppIntent != null) { final Bundle args = new Bundle();
carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE); args.putInt(EXTRA_SUB_ID, mSubId);
startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS); new SubSettingLauncher(context)
} else { .setDestination(WifiCallingDisclaimerFragment.class.getName())
updateWfcMode(true); .setArguments(args)
} .setTitleRes(R.string.wifi_calling_settings_title)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, REQUEST_CHECK_WFC_DISCLAIMER)
.launch();
} }
/* /*
@@ -476,12 +484,30 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
final Context context = getActivity(); final Context context = getActivity();
if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) { Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
Log.d(TAG, "WFC emergency address activity result = " + resultCode);
if (resultCode == Activity.RESULT_OK) { switch (requestCode) {
updateWfcMode(true); case REQUEST_CHECK_WFC_EMERGENCY_ADDRESS:
} if (resultCode == Activity.RESULT_OK) {
updateWfcMode(true);
}
break;
case REQUEST_CHECK_WFC_DISCLAIMER:
if (resultCode == Activity.RESULT_OK) {
// 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);
}
}
break;
default:
Log.e(TAG, "Unexpected request: " + requestCode);
break;
} }
} }
@@ -568,4 +594,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
} }
return resId; return resId;
} }
@VisibleForTesting
Resources getResourcesForSubId() {
return SubscriptionManager.getResourcesForSubId(getContext(), mSubId, false);
}
} }

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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.shadow;
import android.content.Context;
import com.android.settings.wifi.calling.DisclaimerItemFactory;
import com.android.settings.wifi.calling.DisclaimerItem;
import java.util.List;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(DisclaimerItemFactory.class)
public final class ShadowDisclaimerItemFactory {
private static List<DisclaimerItem> sMockDisclaimerItemList;
public static void setDisclaimerItemList(List<DisclaimerItem> list) {
sMockDisclaimerItemList = list;
}
@Implementation
public static List<DisclaimerItem> create(Context context, int subId) {
return sMockDisclaimerItemList;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2018 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.calling;
import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
.DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_TITLE;
import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
.DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_DESCRIPTION;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class DisclaimerItemListAdapterTest {
private static final int ITEM_POSITION = 0;
private static final int DISCLAIMER_TITLE_STRING_ID = 0;
private static final int DISCLAIMER_MESSAGE_STRING_ID = 1;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private TextView mTestView;
@Mock
private TextView mDescView;
@Mock
private View mView;
@Mock
private ViewGroup mViewGroup;
@Mock
private Context mContext;
private MockDisclaimerItem mDisclaimerItem;
private DisclaimerItemListAdapter mDisclaimerItemListAdapter;
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mDisclaimerItem = spy(new MockDisclaimerItem(mContext, 0 /* subId */));
mDisclaimerItemList.add(mDisclaimerItem);
when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
when(mViewGroup.getContext()).thenReturn(mContext);
when(mViewGroup.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(
mLayoutInflater);
when(mView.findViewById(ID_DISCLAIMER_ITEM_TITLE)).thenReturn(mTestView);
when(mView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION)).thenReturn(mDescView);
}
@Test
public void onBindViewHolder_haveItem_shouldSetText() {
final DisclaimerItemListAdapter.DisclaimerItemViewHolder viewHolder =
new DisclaimerItemListAdapter.DisclaimerItemViewHolder(mView);
mDisclaimerItemListAdapter = new DisclaimerItemListAdapter(mDisclaimerItemList);
mDisclaimerItemListAdapter.onCreateViewHolder(mViewGroup, 0 /* viewType */);
mDisclaimerItemListAdapter.onBindViewHolder(viewHolder, ITEM_POSITION);
// Check the text is set when the DisclaimerItem exists.
verify(viewHolder.titleView).setText(DISCLAIMER_TITLE_STRING_ID);
verify(viewHolder.descriptionView).setText(DISCLAIMER_MESSAGE_STRING_ID);
}
private class MockDisclaimerItem extends DisclaimerItem {
MockDisclaimerItem(Context context, int subId) {
super(context, subId);
}
@Override
protected int getTitleId() {
return DISCLAIMER_TITLE_STRING_ID;
}
@Override
protected int getMessageId() {
return DISCLAIMER_MESSAGE_STRING_ID;
}
@Override
protected String getName() {
return "MockDisclaimerItem";
}
@Override
protected String getPrefKey() {
return "mock_pref_key";
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2018 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.calling;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowDisclaimerItemFactory;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowDisclaimerItemFactory.class)
public class WifiCallingDisclaimerFragmentTest {
@Mock
private Activity mActivity;
@Mock
private DisclaimerItem mDisclaimerItem;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private View mView;
@Mock
private ViewGroup mViewGroup;
@Mock
private Button mAgreeButton;
@Mock
private Button mDisagreeButton;
@Mock
private RecyclerView mRecyclerView;
@Captor
ArgumentCaptor<OnClickListener> mOnClickListenerCaptor;
@Captor
ArgumentCaptor<OnScrollListener> mOnScrollListenerCaptor;
private WifiCallingDisclaimerFragment mFragment;
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
private List<DisclaimerItem> mEmptyDisclaimerItemList = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.setupActivity(Activity.class);
mFragment = spy(new WifiCallingDisclaimerFragment());
doReturn(mActivity).when(mFragment).getActivity();
when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
when(mView.findViewById(R.id.agree_button)).thenReturn(mAgreeButton);
when(mView.findViewById(R.id.disagree_button)).thenReturn(mDisagreeButton);
when(mView.findViewById(R.id.disclaimer_item_list)).thenReturn(mRecyclerView);
when(mView.getId()).thenReturn(R.id.agree_button);
mOnScrollListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
doNothing().when(mRecyclerView).addOnScrollListener(mOnScrollListenerCaptor.capture());
mOnClickListenerCaptor = ArgumentCaptor.forClass(OnClickListener.class);
doNothing().when(mAgreeButton).setOnClickListener(mOnClickListenerCaptor.capture());
mDisclaimerItemList.add(mDisclaimerItem);
}
@Test
public void onCreate_notHaveItem_shouldFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mEmptyDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
// Check the fragment is finished when the DisclaimerItemList is empty.
verify(mFragment).finish(Activity.RESULT_OK);
}
@Test
public void onCreate_haveItem_shouldNotFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
// Check the fragment is not finished when the DisclaimerItemList is not empty.
verify(mFragment, never()).finish(Activity.RESULT_OK);
}
@Test
public void onScrolled_canNotScroll_shouldEnableAgreeButton() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
when(mRecyclerView.canScrollVertically(1)).thenReturn(false);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
// Check the agreeButton is enabled when the view is scrolled to the bottom end.
verify(mAgreeButton).setEnabled(true);
// Check the OnScrollListener is removed when the view is scrolled to the bottom end.
verify(mRecyclerView).removeOnScrollListener(any());
}
@Test
public void onScrolled_canScroll_shouldNotEnableAgreeButton() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
when(mRecyclerView.canScrollVertically(1)).thenReturn(true);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
// Check the agreeButton is not enabled when the view is not scrolled to the bottom end.
verify(mAgreeButton, never()).setEnabled(anyBoolean());
// Check the OnScrollListener is not removed when the view is not scrolled to
// the bottom end.
verify(mRecyclerView, never()).removeOnScrollListener(any());
}
@Test
public void onClick_agreeButton_shouldFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnClickListenerCaptor.getValue().onClick(mAgreeButton);
// Check the onAgreed callback is called when "CONTINUE" button is clicked.
verify(mDisclaimerItem).onAgreed();
// Check the WFC disclaimer fragment is finished when "CONTINUE" button is clicked.
verify(mFragment).finish(Activity.RESULT_OK);
}
}

View File

@@ -16,11 +16,14 @@
package com.android.settings.wifi.calling; package com.android.settings.wifi.calling;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
@@ -31,6 +34,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
@@ -41,18 +46,22 @@ import android.telephony.ims.ProvisioningManager;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.ims.ImsConfig; import com.android.ims.ImsConfig;
import com.android.ims.ImsManager; import com.android.ims.ImsManager;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.ToggleSwitch; import com.android.settings.widget.ToggleSwitch;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
@@ -63,6 +72,8 @@ import org.robolectric.util.ReflectionHelpers;
public class WifiCallingSettingsForSubTest { public class WifiCallingSettingsForSubTest {
private static final String BUTTON_WFC_MODE = "wifi_calling_mode"; 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 BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String TEST_EMERGENCY_ADDRESS_CARRIER_APP =
"com.android.settings/.wifi.calling.TestEmergencyAddressCarrierApp";
private TestFragment mFragment; private TestFragment mFragment;
private Context mContext; private Context mContext;
@@ -70,6 +81,7 @@ public class WifiCallingSettingsForSubTest {
private final PersistableBundle mBundle = new PersistableBundle(); private final PersistableBundle mBundle = new PersistableBundle();
@Mock private static CarrierConfigManager sCarrierConfigManager; @Mock private static CarrierConfigManager sCarrierConfigManager;
@Mock private CarrierConfigManager mMockConfigManager;
@Mock private ImsManager mImsManager; @Mock private ImsManager mImsManager;
@Mock private TelephonyManager mTelephonyManager; @Mock private TelephonyManager mTelephonyManager;
@Mock private PreferenceScreen mPreferenceScreen; @Mock private PreferenceScreen mPreferenceScreen;
@@ -80,6 +92,7 @@ public class WifiCallingSettingsForSubTest {
@Mock private ImsConfig mImsConfig; @Mock private ImsConfig mImsConfig;
@Mock private ListWithEntrySummaryPreference mButtonWfcMode; @Mock private ListWithEntrySummaryPreference mButtonWfcMode;
@Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode; @Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
@Mock private Preference mUpdateAddress;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -121,6 +134,11 @@ public class WifiCallingSettingsForSubTest {
doReturn(mBundle).when(sCarrierConfigManager).getConfigForSubId(anyInt()); doReturn(mBundle).when(sCarrierConfigManager).getConfigForSubId(anyInt());
setDefaultCarrierConfigValues(); setDefaultCarrierConfigValues();
doReturn(sCarrierConfigManager).when(mActivity).getSystemService(
CarrierConfigManager.class);
doReturn(mContext.getResources()).when(mFragment).getResourcesForSubId();
doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt());
mFragment.onAttach(mContext); mFragment.onAttach(mContext);
mFragment.onCreate(null); mFragment.onCreate(null);
mFragment.onActivityCreated(null); mFragment.onActivityCreated(null);
@@ -131,6 +149,9 @@ public class WifiCallingSettingsForSubTest {
CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false); CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, true); mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, true);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true); mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true);
mBundle.putString(
CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING,
TEST_EMERGENCY_ADDRESS_CARRIER_APP);
} }
@Test @Test
@@ -259,6 +280,61 @@ public class WifiCallingSettingsForSubTest {
eq(true)); eq(true));
} }
@Test
public void onSwitchChanged_enableSetting_shouldLaunchWfcDisclaimerFragment() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mFragment.onSwitchChanged(null, true);
// Check the WFC disclaimer fragment is launched.
verify(mFragment).startActivityForResult(intentCaptor.capture(),
eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER));
Intent intent = intentCaptor.getValue();
assertThat(intent.getStringExtra(EXTRA_SHOW_FRAGMENT))
.isEqualTo(WifiCallingDisclaimerFragment.class.getName());
}
@Test
public void onActivityResult_finishWfcDisclaimerFragment_shouldLaunchCarrierActivity() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
// Emulate the WfcDisclaimerActivity finish.
mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER,
Activity.RESULT_OK, null);
// Check the WFC emergency address activity is launched.
verify(mFragment).startActivityForResult(intentCaptor.capture(),
eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS));
Intent intent = intentCaptor.getValue();
assertEquals(intent.getComponent(), ComponentName.unflattenFromString(
TEST_EMERGENCY_ADDRESS_CARRIER_APP));
}
@Test
public void onActivityResult_finishCarrierActivity_shouldShowWfcPreference() {
ReflectionHelpers.setField(mFragment, "mButtonWfcMode", mButtonWfcMode);
ReflectionHelpers.setField(mFragment, "mButtonWfcRoamingMode", mButtonWfcRoamingMode);
ReflectionHelpers.setField(mFragment, "mUpdateAddress", mUpdateAddress);
mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS,
Activity.RESULT_OK, null);
// Check the WFC preferences is added.
verify(mPreferenceScreen).addPreference(mButtonWfcMode);
verify(mPreferenceScreen).addPreference(mButtonWfcRoamingMode);
verify(mPreferenceScreen).addPreference(mUpdateAddress);
// Check the WFC enable request.
verify(mImsManager).setWfcSetting(true);
}
@Test
public void onSwitchChanged_disableSetting_shouldNotLaunchWfcDisclaimerFragment() {
mFragment.onSwitchChanged(null, false);
// Check the WFC disclaimer fragment is not launched.
verify(mFragment, never()).startActivityForResult(any(Intent.class), anyInt());
}
protected class TestFragment extends WifiCallingSettingsForSub { protected class TestFragment extends WifiCallingSettingsForSub {
@Override @Override
protected Object getSystemService(final String name) { protected Object getSystemService(final String name) {