Add prompt to sign in at captive portal to Wi-Fi Slice

- Support signing in captive portal APs in WifiSlice
- Show ContextualWifiSlice when signing in is required
- Generifies SliceBackgroundWorker.getInstance() to return <T extends SliceBackgroundWorker>

Fixes: 128056349
Test: make RunSettingsRoboTests -j
Change-Id: Ib4d3942591a65e81018389e4c0bbddfea6854dbc
(cherry picked from commit dd9f92280b)
This commit is contained in:
Jason Chiu
2019-04-02 13:57:29 +08:00
committed by Tsung-Mao Fang
parent 44427259e8
commit 8f6c06974c
9 changed files with 277 additions and 24 deletions

View File

@@ -17,6 +17,8 @@
package com.android.settings.wifi.slice;
import android.app.Activity;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.wifi.WifiManager;
import android.os.Bundle;
@@ -36,10 +38,15 @@ public class ConnectToWifiHandler extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Network network = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
final Bundle accessPointState = getIntent().getBundleExtra(
WifiDialogActivity.KEY_ACCESS_POINT_STATE);
if (accessPointState != null) {
if (network != null) {
final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
// start captive portal app to sign in to network
cm.startCaptivePortalApp(network);
} else if (accessPointState != null) {
connect(new AccessPoint(this, accessPointState));
}
finish();

View File

@@ -57,7 +57,7 @@ public class ContextualWifiSlice extends WifiSlice {
sActiveUiSession = currentUiSession;
sPreviouslyDisplayed = false;
}
if (!sPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) {
if (!sPreviouslyDisplayed && hasWorkingNetwork()) {
Log.d(TAG, "Wifi is connected, no point showing any suggestion.");
return null;
}
@@ -67,4 +67,8 @@ public class ContextualWifiSlice extends WifiSlice {
return super.getSlice();
}
private boolean hasWorkingNetwork() {
return !TextUtils.equals(getActiveSSID(), WifiSsid.NONE) && !isCaptivePortal();
}
}

View File

@@ -16,14 +16,27 @@
package com.android.settings.wifi.slice;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.wifi.WifiUtils;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.WifiTracker;
@@ -33,16 +46,23 @@ import java.util.List;
/**
* {@link SliceBackgroundWorker} for Wi-Fi, used by WifiSlice.
*/
public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint>
implements WifiTracker.WifiListener {
public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> implements
WifiTracker.WifiListener {
private static final String TAG = "WifiScanWorker";
@VisibleForTesting
CaptivePortalNetworkCallback mCaptivePortalNetworkCallback;
private final Context mContext;
private WifiTracker mWifiTracker;
private ConnectivityManager mConnectivityManager;
public WifiScanWorker(Context context, Uri uri) {
super(context, uri);
mContext = context;
mConnectivityManager = context.getSystemService(ConnectivityManager.class);
}
@Override
@@ -58,6 +78,7 @@ public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint>
@Override
protected void onSliceUnpinned() {
mWifiTracker.onStop();
unregisterCaptivePortalNetworkCallback();
}
@Override
@@ -124,4 +145,71 @@ public class WifiScanWorker extends SliceBackgroundWorker<AccessPoint>
}
return null;
}
}
public void registerCaptivePortalNetworkCallback(Network wifiNetwork) {
if (wifiNetwork == null) {
return;
}
if (mCaptivePortalNetworkCallback != null
&& mCaptivePortalNetworkCallback.isSameNetwork(wifiNetwork)) {
return;
}
unregisterCaptivePortalNetworkCallback();
mCaptivePortalNetworkCallback = new CaptivePortalNetworkCallback(wifiNetwork);
mConnectivityManager.registerNetworkCallback(
new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_WIFI)
.build(),
mCaptivePortalNetworkCallback,
new Handler(Looper.getMainLooper()));
}
public void unregisterCaptivePortalNetworkCallback() {
if (mCaptivePortalNetworkCallback != null) {
try {
mConnectivityManager.unregisterNetworkCallback(mCaptivePortalNetworkCallback);
} catch (RuntimeException e) {
Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e);
}
mCaptivePortalNetworkCallback = null;
}
}
class CaptivePortalNetworkCallback extends NetworkCallback {
private final Network mNetwork;
private boolean mIsCaptivePortal;
CaptivePortalNetworkCallback(Network network) {
mNetwork = Preconditions.checkNotNull(network);
}
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
if (!mNetwork.equals(network)) {
return;
}
final boolean isCaptivePortal = WifiUtils.canSignIntoNetwork(networkCapabilities);
if (mIsCaptivePortal == isCaptivePortal) {
return;
}
mIsCaptivePortal = isCaptivePortal;
notifySliceChange();
}
/**
* Returns true if the supplied network is not null and is the same as the originally
* supplied value.
*/
public boolean isSameNetwork(Network network) {
return mNetwork.equals(network);
}
}
}

View File

@@ -31,6 +31,8 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.net.NetworkInfo.DetailedState;
@@ -78,10 +80,12 @@ public class WifiSlice implements CustomSliceable {
protected final Context mContext;
protected final WifiManager mWifiManager;
protected final ConnectivityManager mConnectivityManager;
public WifiSlice(Context context) {
mContext = context;
mWifiManager = mContext.getSystemService(WifiManager.class);
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
}
@Override
@@ -100,13 +104,16 @@ public class WifiSlice implements CustomSliceable {
return listBuilder.build();
}
final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(getUri());
final WifiScanWorker worker = SliceBackgroundWorker.getInstance(getUri());
final List<AccessPoint> results = worker != null ? worker.getResults() : null;
final int apCount = results == null ? 0 : results.size();
final boolean isFirstApActive = apCount > 0 && results.get(0).isActive();
handleCaptivePortalCallback(worker, isFirstApActive);
// Need a loading text when results are not ready or out of date.
boolean needLoadingRow = true;
int index = apCount > 0 && results.get(0).isActive() ? 1 : 0;
// 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++) {
@@ -159,19 +166,35 @@ public class WifiSlice implements CustomSliceable {
.setPrimaryAction(primarySliceAction));
}
private void handleCaptivePortalCallback(WifiScanWorker worker, boolean isFirstApActive) {
if (worker == null) {
return;
}
if (isFirstApActive) {
worker.registerCaptivePortalNetworkCallback(mWifiManager.getCurrentNetwork());
} else {
worker.unregisterCaptivePortalNetworkCallback();
}
}
private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
final CharSequence title = getAccessPointName(accessPoint);
final IconCompat levelIcon = getAccessPointLevelIcon(accessPoint);
final boolean isCaptivePortal = accessPoint.isActive() && isCaptivePortal();
final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
.setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
.setSubtitle(title)
.setPrimaryAction(SliceAction.create(
getAccessPointAction(accessPoint), levelIcon, ListBuilder.ICON_IMAGE,
title));
.setPrimaryAction(SliceAction.createDeeplink(
getAccessPointAction(accessPoint, isCaptivePortal), levelIcon,
ListBuilder.ICON_IMAGE, title));
final IconCompat endIcon = getEndIcon(accessPoint);
if (endIcon != null) {
rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE);
if (isCaptivePortal) {
rowBuilder.addEndItem(getCaptivePortalEndAction(accessPoint, title));
} else {
final IconCompat endIcon = getEndIcon(accessPoint);
if (endIcon != null) {
rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE);
}
}
return rowBuilder;
}
@@ -218,12 +241,22 @@ public class WifiSlice implements CustomSliceable {
return null;
}
private PendingIntent getAccessPointAction(AccessPoint accessPoint) {
private SliceAction getCaptivePortalEndAction(AccessPoint accessPoint, CharSequence title) {
return SliceAction.createDeeplink(
getAccessPointAction(accessPoint, false /* isCaptivePortal */),
IconCompat.createWithResource(mContext, R.drawable.ic_settings_accent),
ListBuilder.ICON_IMAGE, title);
}
private PendingIntent getAccessPointAction(AccessPoint accessPoint, boolean isCaptivePortal) {
final Bundle extras = new Bundle();
accessPoint.saveWifiState(extras);
Intent intent;
if (accessPoint.isActive()) {
if (isCaptivePortal) {
intent = new Intent(mContext, ConnectToWifiHandler.class);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mWifiManager.getCurrentNetwork());
} else if (accessPoint.isActive()) {
intent = new SubSettingLauncher(mContext)
.setTitleRes(R.string.pref_title_network_details)
.setDestination(WifiNetworkDetailsFragment.class.getName())
@@ -253,6 +286,12 @@ public class WifiSlice implements CustomSliceable {
.setSubtitle(title);
}
protected boolean isCaptivePortal() {
final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
mWifiManager.getCurrentNetwork());
return WifiUtils.canSignIntoNetwork(nc);
}
/**
* Update the current wifi status to the boolean value keyed by
* {@link android.app.slice.Slice#EXTRA_TOGGLE_STATE} on {@param intent}.
@@ -317,6 +356,12 @@ public class WifiSlice implements CustomSliceable {
}
private CharSequence getSummary(AccessPoint accessPoint) {
if (isCaptivePortal()) {
final int id = mContext.getResources()
.getIdentifier("network_available_sign_in", "string", "android");
return mContext.getText(id);
}
if (accessPoint == null) {
return getSummary();
}