diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index ae96ed31930..eaf36788eec 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -146,7 +146,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private static final long TIMEOUT = Duration.ofSeconds(10).toMillis(); // Be static to avoid too much object not be reset. - private static CountDownTimer mTimer; + @VisibleForTesting + static CountDownTimer mTimer; private AccessPoint mAccessPoint; private final ConnectivityManager mConnectivityManager; @@ -256,20 +257,15 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController @Override public void onLost(Network network) { - final boolean lostCurrentNetwork = network.equals(mNetwork); - if (lostCurrentNetwork) { - // Should update as disconnect but not exit. Except for ephemeral network which - // should not show on saved network list. - if (!mIsEphemeral) { - return; - } - + // Ephemeral network not a saved network, leave detail page once disconnected + if (mIsEphemeral && network.equals(mNetwork)) { exitActivity(); } } }; - private final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() { + @VisibleForTesting + final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() { /** Called when the state of Wifi has changed. */ public void onWifiStateChanged(int state) { Log.d(TAG, "onWifiStateChanged(" + state + ")"); @@ -284,16 +280,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController /** Called when the connection state of wifi has changed. */ public void onConnectedChanged() { - updateAccessPointFromScannedList(); - if (mConnected != mAccessPoint.isActive()) { - Log.d(TAG, "Connection state changed!"); - mConnected = mAccessPoint.isActive(); - if (mAccessPoint.isActive()) { - updateConnectingState(STATE_CONNECTED); - } else { - updateConnectingState(STATE_DISCONNECTED); - } - } + refreshPage(); } /** @@ -516,42 +503,41 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController refreshMacAddress(); } - private boolean updateAccessPoint() { + @VisibleForTesting + 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; - } + // remember mIsOutOfRange as old before updated + boolean oldState = mIsOutOfRange; + updateAccessPointFromScannedList(); if (mAccessPoint.isActive()) { - // Sometimes {@link WifiManager#getCurrentNetwork()} return null after connected, - // refresh it if needed. - if (mNetwork == null) { - updateNetworkInfo(); - } + 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. + // Once connected, can't get mNetwork immediately, return false and wait for + // next time to update UI. also reset {@code mIsOutOfRange} + mIsOutOfRange = oldState; return false; } - changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo); - // If feature for saved network not enabled, always return true. - return mWifiTracker == null || changed; + } + + // signal level changed + changed |= mRssiSignalLevel != mAccessPoint.getLevel(); + // In/Out of range changed + changed |= oldState != mIsOutOfRange; + // connect state changed + if (mConnected != mAccessPoint.isActive()) { + mConnected = mAccessPoint.isActive(); + changed = true; + updateConnectingState(mAccessPoint.isActive() ? STATE_CONNECTED : STATE_DISCONNECTED); } return changed; } private void updateAccessPointFromScannedList() { - if (mWifiTracker == null) return; - mIsOutOfRange = true; for (AccessPoint ap : mWifiTracker.getAccessPoints()) { @@ -967,7 +953,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER); } - private void connectNetwork() { + @VisibleForTesting + void connectNetwork() { final Activity activity = mFragment.getActivity(); // error handling, connected/saved network should have mWifiConfig. if (mWifiConfig == null) { @@ -1045,7 +1032,6 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController mAccessPoint.getTitle()), Toast.LENGTH_SHORT).show(); - updateNetworkInfo(); refreshPage(); } else if (state == STATE_NOT_IN_RANGE) { Log.d(TAG, "AP not in range"); @@ -1084,7 +1070,11 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController .setButton3Enabled(false); break; case STATE_CONNECTED: - mButtonsPref.setButton3Visible(false); + // init button state and set as invisible + mButtonsPref.setButton3Text(R.string.wifi_connect) + .setButton3Icon(R.drawable.ic_settings_wireless) + .setButton3Enabled(true) + .setButton3Visible(false); break; case STATE_DISCONNECTED: case STATE_NOT_IN_RANGE: diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 906c55c39f2..2bf3556586f 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.content.pm.PackageManager; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; @@ -96,6 +97,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowToast; import java.net.Inet4Address; import java.net.InetAddress; @@ -1309,6 +1311,406 @@ public class WifiDetailPreferenceControllerTest { verify(mockButtonsPref).setButton2Visible(false); } + @Test + public void testConnectButton_shouldInvisibleForConnectNetwork() { + setUpForConnectedNetwork(); + + displayAndResume(); + + verify(mockButtonsPref, times(1)).setButton3Visible(false); + } + + @Test + public void testConnectButton_shouldVisibleForDisconnectNetwork() { + setUpForDisconnectedNetwork(); + + displayAndResume(); + + verify(mockButtonsPref, times(1)).setButton3Visible(true); + verify(mockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect); + } + + private void setUpForToast() { + Resources res = mContext.getResources(); + when(mockActivity.getResources()).thenReturn(res); + } + + @Test + public void testConnectButton_clickConnect_displayAsSuccess() { + setUpForDisconnectedNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(true); + InOrder inOrder = inOrder(mockButtonsPref); + String label = "title"; + when(mockAccessPoint.getTitle()).thenReturn(label); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check display button as connecting + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as connected + when(mockAccessPoint.isActive()).thenReturn(true); + mController.updateAccessPoint(); + + // check connect button invisible, be init as default state and toast success message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(false); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_connected_to_message, label)); + } + + @Test + public void testConnectButton_clickConnectButFailed_displayFailMessage() { + setUpForDisconnectedNetwork(); + ArgumentCaptor connectListenerCaptor = + ArgumentCaptor.forClass(WifiManager.ActionListener.class); + when(mockWifiManager.isWifiEnabled()).thenReturn(true); + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check display button as connecting + verify(mockWifiManager, times(1)).connect(anyInt(), connectListenerCaptor.capture()); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as failed + connectListenerCaptor.getValue().onFailure(-1); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_failed_connect_message)); + } + + private void verifyConnectBtnSetUpAsVisible(InOrder inOrder) { + inOrder.verify(mockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect); + inOrder.verify(mockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + } + + private void verifyConnectBtnSetUpAsConnecting(InOrder inOrder) { + inOrder.verify(mockButtonsPref, times(1)).setButton3Text(R.string.wifi_connecting); + inOrder.verify(mockButtonsPref, times(1)).setButton3Enabled(false); + } + + private void verifyConnectBtnBeInitAsDefault(InOrder inOrder) { + inOrder.verify(mockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect); + inOrder.verify(mockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless); + inOrder.verify(mockButtonsPref, times(1)).setButton3Enabled(true); + } + + @Test + public void testConnectButton_clickConnectButTimeout_displayFailMessage() { + setUpForDisconnectedNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(true); + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check display button as connecting + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as failed + mController.mTimer.onFinish(); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_failed_connect_message)); + } + + @Test + public void testConnectButton_clickConnectButTimeout_displayNotInRangeMessage() { + setUpForNotInRangeNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(true); + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check display button as connecting + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as failed + mController.mTimer.onFinish(); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_not_in_range_message)); + } + + @Test + public void testConnectButton_clickConnectWhenWiFiDisabled_displaySuccessMessage() { + setUpForDisconnectedNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled + InOrder inOrder = inOrder(mockButtonsPref); + String label = "title"; + when(mockAccessPoint.getTitle()).thenReturn(label); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message + verify(mockWifiManager, times(1)).setWifiEnabled(true); + verifyConnectBtnSetUpAsConnecting(inOrder); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_turned_on_message)); + + // notify Wi-Fi enabled + mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED); + + // check had connect network and icon display as expected + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as connected + when(mockAccessPoint.isActive()).thenReturn(true); + mController.updateAccessPoint(); + + // check connect button invisible, be init as default state and toast success message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(false); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_connected_to_message, label)); + } + + @Test + public void testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWiFi() { + setUpForDisconnectedNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message + verify(mockWifiManager, times(1)).setWifiEnabled(true); + verifyConnectBtnSetUpAsConnecting(inOrder); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_turned_on_message)); + + // notify Wi-Fi enabled + mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED); + + // check had connect network and icon display as expected + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as failed + mController.mTimer.onFinish(); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_failed_connect_message)); + } + + @Test + public void + testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWifiBecauseNotInRange() { + setUpForNotInRangeNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message + verify(mockWifiManager, times(1)).setWifiEnabled(true); + verifyConnectBtnSetUpAsConnecting(inOrder); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_turned_on_message)); + + // notify Wi-Fi enabled + mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED); + + // check had connect network and icon display as expected + verify(mockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class)); + verifyConnectBtnSetUpAsConnecting(inOrder); + + // update as failed + mController.mTimer.onFinish(); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_not_in_range_message)); + } + + @Test + public void testConnectButton_clickConnectWhenWiFiDisabled_failedToEnableWifi() { + setUpForDisconnectedNetwork(); + when(mockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled + InOrder inOrder = inOrder(mockButtonsPref); + setUpForToast(); + + displayAndResume(); + + // check connect button exist + verifyConnectBtnSetUpAsVisible(inOrder); + + // click connect button + mController.connectNetwork(); + + // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message + verify(mockWifiManager, times(1)).setWifiEnabled(true); + verifyConnectBtnSetUpAsConnecting(inOrder); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_turned_on_message)); + + // notify turn on Wi-Fi failed + mController.mTimer.onFinish(); + + // check connect button visible, be init as default and toast failed message + verifyConnectBtnBeInitAsDefault(inOrder); + inOrder.verify(mockButtonsPref, times(1)).setButton3Visible(true); + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_failed_connect_message)); + } + + @Test + public void updateAccessPoint_returnFalseForNothingChanged() { + setUpForDisconnectedNetwork(); + + displayAndResume(); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isFalse(); + } + + @Test + public void updateAccessPoint_returnTrueForSignalLevelChanged() { + setUpForDisconnectedNetwork(); + + displayAndResume(); + + // Level changed + when(mockAccessPoint.getLevel()).thenReturn(LEVEL + 1); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + + @Test + public void updateAccessPoint_returnTrueForChangeAsNotInRange() { + setUpForDisconnectedNetwork(); + + displayAndResume(); + + // change as not in range + when(mockAccessPoint.matches(any(WifiConfiguration.class))).thenReturn(false); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + + @Test + public void updateAccessPoint_returnTrueForChangeAsInRange() { + setUpForNotInRangeNetwork(); + + displayAndResume(); + + // change as in range + when(mockAccessPoint.matches(any(WifiConfiguration.class))).thenReturn(true); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + + @Test + public void updateAccessPoint_returnTrueForChangeAsConnected() { + setUpForDisconnectedNetwork(); + + displayAndResume(); + + // change as connected + when(mockAccessPoint.isActive()).thenReturn(true); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + + @Test + public void updateAccessPoint_returnTrueForChangeAsDisconnected() { + setUpForConnectedNetwork(); + + displayAndResume(); + + // change as disconnected + when(mockAccessPoint.isActive()).thenReturn(false); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + + @Test + public void updateAccessPoint_returnTrueForAccessPointUpdated() { + setUpForConnectedNetwork(); + + displayAndResume(); + + // change as disconnected + when(mockAccessPoint.update(mockWifiConfig, mockWifiInfo, mockNetworkInfo)) + .thenReturn(true); + boolean changed = mController.updateAccessPoint(); + + assertThat(changed).isTrue(); + } + @Test public void testRefreshRssiViews_shouldNotUpdateIfLevelIsSameForConnectedNetwork() { setUpForConnectedNetwork();