From fbf3d7574a9b527fd74099d565e004bc5cf8918d Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 10 Jan 2020 11:03:37 +0800 Subject: [PATCH] Implement new design of Wi-Fi card - always show Wi-Fi card - collapse the card in the new UI session when connecting to a stable network - hide toggle, show a level icon and subtext in the new collapsed mode - show loading row when the AP list is not full Test: robotest Fixes: 147473096 Change-Id: I893064ef04d40d8e7cb8e62c1e72a2cb5e97f6ac --- .../wifi/slice/ContextualWifiScanWorker.java | 5 + .../wifi/slice/ContextualWifiSlice.java | 94 ++++++++++++-- .../settings/wifi/slice/WifiScanWorker.java | 7 +- .../settings/wifi/slice/WifiSlice.java | 74 ++++++----- .../wifi/slice/ContextualWifiSliceTest.java | 117 ++++++++++++------ .../settings/wifi/slice/WifiSliceTest.java | 17 +-- 6 files changed, 215 insertions(+), 99 deletions(-) diff --git a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java index 5e69b8add68..616c92fb33a 100644 --- a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java +++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java @@ -62,4 +62,9 @@ public class ContextualWifiScanWorker extends WifiScanWorker { } return true; } + + @Override + protected int getApRowCount() { + return ContextualWifiSlice.getApRowCount(); + } } \ No newline at end of file diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java index 6b9dd3c3fca..bb9be77508c 100644 --- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java +++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java @@ -17,30 +17,42 @@ package com.android.settings.wifi.slice; import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.NetworkInfo.State; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.VisibleForTesting; +import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceable; +import com.android.settingslib.wifi.AccessPoint; /** * {@link CustomSliceable} for Wi-Fi, used by contextual homepage. */ public class ContextualWifiSlice extends WifiSlice { - private static final String TAG = "ContextualWifiSlice"; + @VisibleForTesting + static final int COLLAPSED_ROW_COUNT = 0; + @VisibleForTesting static long sActiveUiSession = -1000; @VisibleForTesting - static boolean sPreviouslyDisplayed; + static boolean sToggleNeeded = true; public ContextualWifiSlice(Context context) { super(context); @@ -57,19 +69,77 @@ public class ContextualWifiSlice extends WifiSlice { .getSlicesFeatureProvider().getUiSessionToken(); if (currentUiSession != sActiveUiSession) { sActiveUiSession = currentUiSession; - sPreviouslyDisplayed = false; + sToggleNeeded = !hasWorkingNetwork(); + } else if (!mWifiManager.isWifiEnabled()) { + sToggleNeeded = true; } - if (!sPreviouslyDisplayed && hasWorkingNetwork()) { - Log.d(TAG, "Wifi is connected, no point showing any suggestion."); - return null; - } - // Set sPreviouslyDisplayed to true - we will show *something* on the screen. So we should - // keep showing this card to keep UI stable, even if wifi connects to a network later. - sPreviouslyDisplayed = true; - return super.getSlice(); } + static int getApRowCount() { + return sToggleNeeded ? DEFAULT_EXPANDED_ROW_COUNT : COLLAPSED_ROW_COUNT; + } + + @Override + protected boolean isToggleNeeded() { + return sToggleNeeded; + } + + @Override + protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) { + final ListBuilder.RowBuilder builder = super.getHeaderRow(accessPoint); + if (!sToggleNeeded) { + builder.setTitleItem(getLevelIcon(accessPoint), ListBuilder.ICON_IMAGE) + .setSubtitle(getSubtitle(accessPoint)); + } + return builder; + } + + private IconCompat getLevelIcon(AccessPoint accessPoint) { + if (accessPoint != null) { + return getAccessPointLevelIcon(accessPoint); + } + + final Drawable drawable = mContext.getDrawable( + com.android.settingslib.Utils.getWifiIconResource(0)); + final int color = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.colorControlNormal); + drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + return Utils.createIconWithDrawable(drawable); + } + + private CharSequence getSubtitle(AccessPoint accessPoint) { + if (isCaptivePortal()) { + final int id = mContext.getResources() + .getIdentifier("network_available_sign_in", "string", "android"); + return mContext.getText(id); + } + + if (accessPoint == null) { + return mContext.getText(R.string.disconnected); + } + + final NetworkInfo networkInfo = accessPoint.getNetworkInfo(); + if (networkInfo == null) { + return mContext.getText(R.string.disconnected); + } + + final State state = networkInfo.getState(); + DetailedState detailedState; + if (state == State.CONNECTING) { + detailedState = DetailedState.CONNECTING; + } else if (state == State.CONNECTED) { + detailedState = DetailedState.CONNECTED; + } else { + detailedState = networkInfo.getDetailedState(); + } + + final String[] formats = mContext.getResources().getStringArray( + R.array.wifi_status_with_ssid); + final int index = detailedState.ordinal(); + return String.format(formats[index], accessPoint.getTitle()); + } + private boolean hasWorkingNetwork() { return !TextUtils.equals(getActiveSSID(), WifiManager.UNKNOWN_SSID) && hasInternetAccess(); } diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java index a5bd74dea6d..be1e005b2bc 100644 --- a/src/com/android/settings/wifi/slice/WifiScanWorker.java +++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java @@ -111,10 +111,11 @@ public class WifiScanWorker extends SliceBackgroundWorker implement // AccessPoints are sorted by the WifiTracker final List accessPoints = mWifiTracker.getAccessPoints(); final List resultList = new ArrayList<>(); + final int apRowCount = getApRowCount(); for (AccessPoint ap : accessPoints) { if (ap.isReachable()) { resultList.add(clone(ap)); - if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) { + if (resultList.size() >= apRowCount) { break; } } @@ -122,6 +123,10 @@ public class WifiScanWorker extends SliceBackgroundWorker implement updateResults(resultList); } + protected int getApRowCount() { + return DEFAULT_EXPANDED_ROW_COUNT; + } + private AccessPoint clone(AccessPoint accessPoint) { final Bundle savedState = new Bundle(); accessPoint.saveWifiState(savedState); diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index 97ac9c55a56..8e2d7a6eb76 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -95,7 +95,7 @@ public class WifiSlice implements CustomSliceable { mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); final boolean isWifiEnabled = isWifiEnabled(); - ListBuilder listBuilder = getHeaderRow(isWifiEnabled); + ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */); if (!isWifiEnabled) { WifiScanWorker.clearClickedWifi(); return listBuilder.build(); @@ -107,17 +107,12 @@ public class WifiSlice implements CustomSliceable { final boolean isFirstApActive = apCount > 0 && apList.get(0).isActive(); handleNetworkCallback(worker, isFirstApActive); - // Need a loading text when results are not ready or out of date. - boolean needLoadingRow = true; - // Skip checking the existence of the first access point if it's active - int index = isFirstApActive ? 1 : 0; - // This loop checks the existence of reachable APs to determine the validity of the current - // AP list. - for (; index < apCount; index++) { - if (apList.get(index).isReachable()) { - needLoadingRow = false; - break; + if (!isToggleNeeded()) { + if (isFirstApActive) { + // refresh header subtext + listBuilder = getListBuilder(true /* isWifiEnabled */, apList.get(0)); } + return listBuilder.build(); } // Add AP rows @@ -125,9 +120,8 @@ public class WifiSlice implements CustomSliceable { for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) { if (i < apCount) { listBuilder.addRow(getAccessPointRow(apList.get(i))); - } else if (needLoadingRow) { + } else if (i == apCount) { listBuilder.addRow(getLoadingRow(placeholder)); - needLoadingRow = false; } else { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(placeholder) @@ -148,24 +142,34 @@ public class WifiSlice implements CustomSliceable { } } - private ListBuilder getHeaderRow(boolean isWifiEnabled) { + protected boolean isToggleNeeded() { + return true; + } + + protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_wireless); final String title = mContext.getString(R.string.wifi_settings); - final PendingIntent toggleAction = getBroadcastIntent(mContext); final PendingIntent primaryAction = getPrimaryAction(); final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon, ListBuilder.ICON_IMAGE, title); - final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, - null /* actionTitle */, isWifiEnabled); - return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) + return new ListBuilder.RowBuilder() + .setTitle(title) + .setPrimaryAction(primarySliceAction); + } + + private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) { + final ListBuilder builder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(COLOR_NOT_TINTED) .setKeywords(getKeywords()) - .addRow(new ListBuilder.RowBuilder() - .setTitle(title) - .addEndItem(toggleSliceAction) - .setPrimaryAction(primarySliceAction)); + .addRow(getHeaderRow(accessPoint)); + if (isToggleNeeded()) { + final PendingIntent toggleAction = getBroadcastIntent(mContext); + builder.addAction(SliceAction.createToggle(toggleAction, null /* actionTitle */, + isWifiEnabled)); + } + return builder; } private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) { @@ -200,7 +204,7 @@ public class WifiSlice implements CustomSliceable { return TextUtils.isEmpty(summary) ? mContext.getText(R.string.disconnected) : summary; } - private IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) { + protected IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) { final Drawable d = mContext.getDrawable( com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel())); @@ -250,24 +254,16 @@ public class WifiSlice implements CustomSliceable { accessPoint.saveWifiState(extras); if (accessPoint.isActive()) { - Intent intent; + final SubSettingLauncher launcher = new SubSettingLauncher(mContext) + .setTitleRes(R.string.pref_title_network_details) + .setArguments(extras) + .setSourceMetricsCategory(SettingsEnums.WIFI); if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) { - intent = new SubSettingLauncher(mContext) - .setTitleRes(R.string.pref_title_network_details) - .setDestination(WifiNetworkDetailsFragment2.class.getName()) - .setArguments(extras) - .setSourceMetricsCategory(SettingsEnums.WIFI) - .toIntent(); - return getActivityAction(requestCode, intent, icon, title); + launcher.setDestination(WifiNetworkDetailsFragment2.class.getName()); } else { - intent = new SubSettingLauncher(mContext) - .setTitleRes(R.string.pref_title_network_details) - .setDestination(WifiNetworkDetailsFragment.class.getName()) - .setArguments(extras) - .setSourceMetricsCategory(SettingsEnums.WIFI) - .toIntent(); - return getActivityAction(requestCode, intent, icon, title); + launcher.setDestination(WifiNetworkDetailsFragment.class.getName()); } + return getActivityAction(requestCode, launcher.toIntent(), icon, title); } else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) { final Intent intent = new Intent(mContext, ConnectToWifiHandler.class) .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras); @@ -307,7 +303,7 @@ public class WifiSlice implements CustomSliceable { .setSubtitle(title); } - private boolean isCaptivePortal() { + protected boolean isCaptivePortal() { final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities( mWifiManager.getCurrentNetwork()); return WifiUtils.canSignIntoNetwork(nc); diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java index d39228bab22..6593ec3ef39 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java @@ -16,6 +16,9 @@ package com.android.settings.wifi.slice; +import static com.android.settings.wifi.slice.ContextualWifiSlice.COLLAPSED_ROW_COUNT; +import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; @@ -75,75 +78,87 @@ public class ContextualWifiSliceTest { mWifiManager.setWifiEnabled(true); mWifiSlice = new ContextualWifiSlice(mContext); - mWifiSlice.sPreviouslyDisplayed = false; } @Test - public void getWifiSlice_hasActiveConnection_shouldReturnNull() { - mWifiSlice.sPreviouslyDisplayed = false; - connectToWifi(makeValidatedNetworkCapabilities()); - - final Slice wifiSlice = mWifiSlice.getSlice(); - - assertThat(wifiSlice).isNull(); - } - - @Test - public void getWifiSlice_newSession_hasActiveConnection_shouldReturnNull() { - // Session: use a non-active value - // previous displayed: yes - mWifiSlice.sPreviouslyDisplayed = true; + public void getWifiSlice_newSession_hasActiveConnection_shouldCollapseSlice() { mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); connectToWifi(makeValidatedNetworkCapabilities()); final Slice wifiSlice = mWifiSlice.getSlice(); - assertThat(wifiSlice).isNull(); + assertTitleAndIcon(wifiSlice); + assertNoToggle(wifiSlice); + assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT); } @Test - public void getWifiSlice_previousDisplayed_hasActiveConnection_shouldHaveTitleAndToggle() { + public void getWifiSlice_newSession_noConnection_shouldExpandSlice() { + mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); + + final Slice wifiSlice = mWifiSlice.getSlice(); + + assertTitleAndIcon(wifiSlice); + assertToggle(wifiSlice); + assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT); + } + + @Test + public void getWifiSlice_previousExpanded_hasActiveConnection_shouldExpandSlice() { mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); - mWifiSlice.sPreviouslyDisplayed = true; + mWifiSlice.sToggleNeeded = true; connectToWifi(makeValidatedNetworkCapabilities()); final Slice wifiSlice = mWifiSlice.getSlice(); - final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice); - assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings)); - - final List toggles = metadata.getToggles(); - assertThat(toggles).hasSize(1); - - final SliceAction primaryAction = metadata.getPrimaryAction(); - final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, - R.drawable.ic_settings_wireless); - assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + assertTitleAndIcon(wifiSlice); + assertToggle(wifiSlice); + assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT); } @Test - public void getWifiSlice_isCaptivePortal_shouldHaveTitleAndToggle() { - mWifiSlice.sPreviouslyDisplayed = false; - connectToWifi(WifiSliceTest.makeCaptivePortalNetworkCapabilities()); + public void getWifiSlice_previousExpanded_disableWifi_shouldHaveToggle() { + mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); + mWifiSlice.sToggleNeeded = true; + connectToWifi(makeValidatedNetworkCapabilities()); + mWifiManager.setWifiEnabled(false); final Slice wifiSlice = mWifiSlice.getSlice(); - final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice); - assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings)); + assertTitleAndIcon(wifiSlice); + assertToggle(wifiSlice); + } - final List toggles = metadata.getToggles(); - assertThat(toggles).hasSize(1); + @Test + public void getWifiSlice_previousCollapsed_disableWifi_shouldHaveToggle() { + mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); + mWifiSlice.sToggleNeeded = false; + connectToWifi(makeValidatedNetworkCapabilities()); - final SliceAction primaryAction = metadata.getPrimaryAction(); - final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, - R.drawable.ic_settings_wireless); - assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + mWifiManager.setWifiEnabled(false); + final Slice wifiSlice = mWifiSlice.getSlice(); + + assertTitleAndIcon(wifiSlice); + assertToggle(wifiSlice); + } + + @Test + public void getWifiSlice_previousCollapsed_connectionLoss_shouldCollapseSlice() { + mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); + mWifiSlice.sToggleNeeded = false; + connectToWifi(makeValidatedNetworkCapabilities()); + + mWifiManager.disconnect(); + final Slice wifiSlice = mWifiSlice.getSlice(); + + assertTitleAndIcon(wifiSlice); + assertNoToggle(wifiSlice); + assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT); } @Test public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() { mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken(); - mWifiSlice.sPreviouslyDisplayed = true; final Slice wifiSlice = mWifiSlice.getSlice(); @@ -165,4 +180,26 @@ public class ContextualWifiSliceTest { nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); return nc; } + + private void assertTitleAndIcon(Slice slice) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings)); + + final SliceAction primaryAction = metadata.getPrimaryAction(); + final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, + R.drawable.ic_settings_wireless); + assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + } + + private void assertToggle(Slice slice) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + } + + private void assertNoToggle(Slice slice) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + final List toggles = metadata.getToggles(); + assertThat(toggles).isEmpty(); + } } diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java index 18ed0065c2b..ad74a326c83 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -51,9 +51,6 @@ import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.testutils.SliceTester; import com.android.settingslib.wifi.AccessPoint; -import java.util.ArrayList; -import java.util.List; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,12 +60,16 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.ArrayList; +import java.util.List; + @RunWith(RobolectricTestRunner.class) @Config(shadows = WifiSliceTest.ShadowSliceBackgroundWorker.class) public class WifiSliceTest { private static final String AP1_NAME = "ap1"; private static final String AP2_NAME = "ap2"; + private static final String AP3_NAME = "ap3"; private Context mContext; private ContentResolver mResolver; @@ -205,15 +206,15 @@ public class WifiSliceTest { } @Test - public void getWifiSlice_oneReachableAp_shouldNotReturnLoadingRow() { + public void getWifiSlice_oneReachableAp_shouldReturnLoadingRow() { setWorkerResults(createAccessPoint(AP1_NAME, false, true)); final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); - // No scanning text - SliceTester.assertNoSliceItemContainsSubtitle(sliceItems, + // Has scanning text + SliceTester.assertAnySliceItemContainsSubtitle(sliceItems, mContext.getString(R.string.wifi_empty_list_wifi_on)); } @@ -221,13 +222,15 @@ public class WifiSliceTest { public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() { setWorkerResults( createAccessPoint(AP1_NAME, false, true), - createAccessPoint(AP2_NAME, false, true)); + createAccessPoint(AP2_NAME, false, true), + createAccessPoint(AP3_NAME, false, true)); final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME); + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP3_NAME); // No scanning text SliceTester.assertNoSliceItemContainsSubtitle(sliceItems, mContext.getString(R.string.wifi_empty_list_wifi_on));