From 936f1f215e25422be1d59b9510b1be38fcef29e8 Mon Sep 17 00:00:00 2001 From: clownshen Date: Fri, 22 Mar 2019 23:59:25 +0800 Subject: [PATCH] Provide connect button in Wi-Fi detail page User can click connect button to connect this AP at the detail page even the Wi-Fi disabled. Would turn on Wi-Fi if disabled and then connect to this saved network. Bug: 126503889 Test: atest and manual test Change-Id: I313e290474d6623a6c3e84027e81d0d1ebf16919 --- .../WifiDetailPreferenceController.java | 297 ++++++++++++++++-- 1 file changed, 272 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index 5fc350bbc1d..955525dd859 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -41,6 +41,7 @@ import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.CountDownTimer; import android.os.Handler; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -84,6 +85,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; +import java.time.Duration; import java.util.StringJoiner; import java.util.stream.Collectors; @@ -127,6 +129,19 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController @VisibleForTesting static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses"; + private static final int STATE_NONE = 1; + private static final int STATE_ENABLE_WIFI = 2; + private static final int STATE_ENABLE_WIFI_FAILED = 3; + private static final int STATE_CONNECTING = 4; + private static final int STATE_CONNECTED = 5; + private static final int STATE_FAILED = 6; + private static final int STATE_NOT_IN_RANGE = 7; + private static final int STATE_DISCONNECTED = 8; + private static final long TIMEOUT = Duration.ofSeconds(10).toMillis(); + + // Be static to avoid too much object not be reset. + private static CountDownTimer mTimer; + private AccessPoint mAccessPoint; private final ConnectivityManager mConnectivityManager; private final Fragment mFragment; @@ -143,6 +158,9 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private final WifiTracker mWifiTracker; private final MetricsFeatureProvider mMetricsFeatureProvider; private boolean mIsOutOfRange; + private boolean mConnected; + private int mConnectingState; + private WifiManager.ActionListener mConnectListener; // UI elements - in order of appearance private ActionButtonsPreference mButtonsPref; @@ -182,7 +200,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController // fall through case WifiManager.NETWORK_STATE_CHANGED_ACTION: case WifiManager.RSSI_CHANGED_ACTION: - updateNetworkInfo(); + refreshPage(); break; } } @@ -197,7 +215,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController public void onLinkPropertiesChanged(Network network, LinkProperties lp) { if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) { mLinkProperties = lp; - updateIpLayerInfo(); + refreshIpLayerInfo(); } } @@ -224,12 +242,17 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } mNetworkCapabilities = nc; refreshButtons(); - updateIpLayerInfo(); + refreshIpLayerInfo(); } } @Override public void onLost(Network network) { + // If support detail page for saved network, should update as disconnect but not exit. + if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) { + return; + } + if (network.equals(mNetwork)) { exitActivity(); } @@ -240,13 +263,23 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController /** Called when the state of Wifi has changed. */ public void onWifiStateChanged(int state) { Log.d(TAG, "onWifiStateChanged(" + state + ")"); - // Do nothing. + if (mConnectingState == STATE_ENABLE_WIFI && state == WifiManager.WIFI_STATE_ENABLED) { + updateConnectingState(STATE_CONNECTING); + } } /** Called when the connection state of wifi has changed. */ public void onConnectedChanged() { - Log.d(TAG, "onConnectedChanged"); - // Do nothing. + updateAccessPointFromScannedList(); + if (mConnected != mAccessPoint.isActive()) { + Log.d(TAG, "Connection state changed!"); + mConnected = mAccessPoint.isActive(); + if (mAccessPoint.isActive()) { + updateConnectingState(STATE_CONNECTED); + } else { + updateConnectingState(STATE_DISCONNECTED); + } + } } /** @@ -254,8 +287,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController * {@link WifiTracker#getAccessPoints()} should be called to get the updated list. */ public void onAccessPointsChanged() { - Log.d(TAG, "onAccessPointsChanged"); - updateNetworkInfo(); + refreshPage(); } }; @@ -314,6 +346,19 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } else { mWifiTracker = null; } + mConnected = mAccessPoint.isActive(); + mConnectingState = STATE_NONE; + mConnectListener = new WifiManager.ActionListener() { + @Override + public void onSuccess() { + // Do nothing + } + + @Override + public void onFailure(int reason) { + updateConnectingState(STATE_FAILED); + } + }; } @Override @@ -339,10 +384,14 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController .setButton1OnClickListener(view -> forgetNetwork()) .setButton2Text(R.string.wifi_sign_in_button_text) .setButton2OnClickListener(view -> signIntoNetwork()) - .setButton3Text(R.string.share) - .setButton3Icon(R.drawable.ic_qrcode_24dp) - .setButton3OnClickListener(view -> shareNetwork()) - .setButton3Visible(WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mAccessPoint)); + .setButton3Text(R.string.wifi_connect) + .setButton3Icon(R.drawable.ic_settings_wireless) + .setButton3OnClickListener(view -> connectNetwork()) + .setButton3Enabled(true) + .setButton4Text(R.string.share) + .setButton4Icon(R.drawable.ic_qrcode_24dp) + .setButton4OnClickListener(view -> shareNetwork()) + .setButton4Visible(WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mAccessPoint)); mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED); @@ -397,14 +446,18 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } } + private void updateNetworkInfo() { + mNetwork = mWifiManager.getCurrentNetwork(); + mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); + mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); + } + @Override public void onResume() { // Ensure mNetwork is set before any callbacks above are delivered, since our // NetworkCallback only looks at changes to mNetwork. - mNetwork = mWifiManager.getCurrentNetwork(); - mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); - mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); updateNetworkInfo(); + refreshPage(); mContext.registerReceiver(mReceiver, mFilter); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback, mHandler); @@ -421,11 +474,13 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); } - private void updateNetworkInfo() { + private void refreshPage() { if(!updateAccessPoint()) { return; } + Log.d(TAG, "Update UI!"); + // refresh header refreshEntityHeader(); @@ -441,26 +496,37 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController // Receive Link Speed Pref refreshRxSpeed(); // IP related information - updateIpLayerInfo(); + refreshIpLayerInfo(); // MAC Address Pref refreshMacAddress(); - } private boolean updateAccessPoint() { boolean changed = false; if (mWifiTracker != null) { + // remember mIsOutOfRange as old before updated + boolean oldState = mIsOutOfRange; updateAccessPointFromScannedList(); // refresh UI if signal level changed for disconnect network. changed = mRssiSignalLevel != mAccessPoint.getLevel(); + changed |= oldState != mIsOutOfRange; } if (mAccessPoint.isActive()) { - // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the - // callbacks. mNetwork doesn't change except in onResume. + // Sometimes {@link WifiManager#getCurrentNetwork()} return null after connected, + // refresh it if needed. + if (mNetwork == null) { + updateNetworkInfo(); + } mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork); mWifiInfo = mWifiManager.getConnectionInfo(); if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) { + // Once connected, can't get mNetworkInfo immediately, return false and wait for + // next time to update UI. + if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) { + return false; + } + exitActivity(); return false; } @@ -474,6 +540,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } private void updateAccessPointFromScannedList() { + if (mWifiTracker == null) return; + mIsOutOfRange = true; if (mAccessPoint.getConfig() == null) { @@ -484,7 +552,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController if (ap.getConfig() != null && mAccessPoint.matches(ap.getConfig())) { mAccessPoint = ap; - mIsOutOfRange = false; + mIsOutOfRange = !mAccessPoint.isReachable(); return; } } @@ -543,6 +611,11 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController band = mContext.getResources().getString(R.string.wifi_band_5ghz); } else { Log.e(TAG, "Unexpected frequency " + frequency); + // Connecting state is unstable, make it disappeared if unexpected + if (mConnectingState == STATE_CONNECTING) { + mFrequencyPref.setVisible(false); + } + return; } mFrequencyPref.setSummary(band); mFrequencyPref.setVisible(true); @@ -616,12 +689,21 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private void refreshButtons() { mButtonsPref.setButton1Visible(canForgetNetwork()); mButtonsPref.setButton2Visible(canSignIntoNetwork()); - mButtonsPref.setButton3Visible(canShareNetwork()); - mButtonsPref.setVisible( - canSignIntoNetwork() || canForgetNetwork() || canShareNetwork()); + mButtonsPref.setButton3Visible(canConnectNetwork()); + mButtonsPref.setButton4Visible(canShareNetwork()); + mButtonsPref.setVisible(canSignIntoNetwork() + || canForgetNetwork() + || canShareNetwork() + || canConnectNetwork()); } - private void updateIpLayerInfo() { + private boolean canConnectNetwork() { + // Display connect button for disconnected AP even not in the range. + return SavedAccessPointsWifiSettings.usingDetailsFragment(mContext) + && !mAccessPoint.isActive(); + } + + private void refreshIpLayerInfo() { // Hide IP layer info if not a connected network. if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) { mIpAddressPref.setVisible(false); @@ -823,4 +905,169 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private boolean usingDataUsageHeader(Context context) { return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER); } + + private void connectNetwork() { + final Activity activity = mFragment.getActivity(); + // error handling, connected/saved network should have mWifiConfig. + if (mWifiConfig == null) { + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + return; + } + + // init state before connect + mConnectingState = STATE_NONE; + + if (mWifiManager.isWifiEnabled()) { + updateConnectingState(STATE_CONNECTING); + } else { + // Enable Wi-Fi automatically to connect AP + updateConnectingState(STATE_ENABLE_WIFI); + } + } + + private void updateConnectingState(int state) { + final Activity activity = mFragment.getActivity(); + Log.d(TAG, "updateConnectingState from " + mConnectingState + " to " + state); + switch (mConnectingState) { + case STATE_NONE: + case STATE_ENABLE_WIFI: + if (state == STATE_ENABLE_WIFI) { + Log.d(TAG, "Turn on Wi-Fi automatically!"); + updateConnectedButton(STATE_ENABLE_WIFI); + Toast.makeText(activity, + R.string.wifi_turned_on_message, + Toast.LENGTH_SHORT).show(); + mWifiManager.setWifiEnabled(true); + // start timer for error handling + startTimer(); + } else if (state == STATE_CONNECTING) { + Log.d(TAG, "connecting..."); + updateConnectedButton(STATE_CONNECTING); + mWifiManager.connect(mWifiConfig.networkId, mConnectListener); + // start timer for error handling since framework didn't call back if failed + startTimer(); + } else if (state == STATE_ENABLE_WIFI_FAILED) { + Log.e(TAG, "Wi-Fi failed to enable network!"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_ENABLE_WIFI_FAILED); + } + // Do not break here for disconnected event. + case STATE_CONNECTED: + if (state == STATE_DISCONNECTED) { + Log.d(TAG, "disconnected"); + // reset state + state = STATE_NONE; + updateConnectedButton(STATE_DISCONNECTED); + refreshPage(); + // clear for getting MAC Address from saved configuration + mWifiInfo = null; + } + break; + case STATE_CONNECTING: + if (state == STATE_CONNECTED) { + Log.d(TAG, "connected"); + stopTimer(); + updateConnectedButton(STATE_CONNECTED); + Toast.makeText(activity, + mContext.getString(R.string.wifi_connected_to_message, + mAccessPoint.getTitle()), + Toast.LENGTH_SHORT).show(); + + updateNetworkInfo(); + refreshPage(); + } else if (state == STATE_NOT_IN_RANGE) { + Log.d(TAG, "AP not in range"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_not_in_range_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_NOT_IN_RANGE); + } else if (state == STATE_FAILED) { + Log.d(TAG, "failed"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_FAILED); + } + break; + default: + Log.e(TAG, "Invalid state : " + mConnectingState); + // don't update invalid state + return; + } + + mConnectingState = state; + } + + private void updateConnectedButton(int state) { + switch (state) { + case STATE_ENABLE_WIFI: + case STATE_CONNECTING: + mButtonsPref.setButton3Text(R.string.wifi_connecting) + .setButton3Enabled(false); + break; + case STATE_CONNECTED: + mButtonsPref.setButton3Visible(false); + break; + case STATE_DISCONNECTED: + case STATE_NOT_IN_RANGE: + case STATE_FAILED: + case STATE_ENABLE_WIFI_FAILED: + mButtonsPref.setButton3Text(R.string.wifi_connect) + .setButton3Icon(R.drawable.ic_settings_wireless) + .setButton3Enabled(true) + .setButton3Visible(true); + break; + default: + Log.e(TAG, "Invalid connect button state : " + state); + break; + } + } + + private void startTimer() { + if (mTimer != null) { + stopTimer(); + } + + mTimer = new CountDownTimer(TIMEOUT, TIMEOUT + 1) { + @Override + public void onTick(long millisUntilFinished) { + // Do nothing + } + @Override + public void onFinish() { + Log.e(TAG, "Timeout for state:" + mConnectingState); + if (mConnectingState == STATE_ENABLE_WIFI) { + updateConnectingState(STATE_ENABLE_WIFI_FAILED); + } else if (mConnectingState == STATE_CONNECTING) { + updateAccessPointFromScannedList(); + if (mIsOutOfRange) { + updateConnectingState(STATE_NOT_IN_RANGE); + } else { + updateConnectingState(STATE_FAILED); + } + } + } + }; + mTimer.start(); + } + + private void stopTimer() { + if (mTimer == null) return; + + mTimer.cancel(); + mTimer = null; + } }