diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java index c58ff579110..7078ae08a8e 100644 --- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java +++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java @@ -19,9 +19,18 @@ package com.android.settings.wifi; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.drawable.Drawable; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; +import android.net.wifi.WifiManager.NetworkRequestMatchCallback; import android.os.Handler; import android.os.Message; +import android.widget.BaseAdapter; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; +import androidx.preference.internal.PreferenceImageView; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -32,20 +41,23 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settingslib.Utils; import com.android.settingslib.wifi.AccessPoint; import java.util.ArrayList; import java.util.List; public class NetworkRequestDialogFragment extends InstrumentedDialogFragment implements - DialogInterface.OnClickListener { + DialogInterface.OnClickListener, NetworkRequestMatchCallback { /** Message sent to us to stop scanning wifi and pop up timeout dialog. */ private static final int MESSAGE_STOP_SCAN_WIFI_LIST = 0; /** Delayed time to stop scanning wifi. */ - private static final int DELAY_TIME_STOP_SCAN_MS = 30*1000; + private static final int DELAY_TIME_STOP_SCAN_MS = 30 * 1000; private List mAccessPointList; + private AccessPointAdapter mDialogAdapter; + private NetworkRequestUserSelectionCallback mUserSelectionCallback; public static NetworkRequestDialogFragment newInstance() { NetworkRequestDialogFragment dialogFragment = new NetworkRequestDialogFragment(); @@ -54,28 +66,29 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Context context = getContext(); + final Context context = getContext(); // Prepares title. - LayoutInflater inflater = LayoutInflater.from(context); - View customTitle = inflater.inflate(R.layout.network_request_dialog_title, null); + final LayoutInflater inflater = LayoutInflater.from(context); + final View customTitle = inflater.inflate(R.layout.network_request_dialog_title, null); - TextView title = customTitle.findViewById(R.id.network_request_title_text); + final TextView title = customTitle.findViewById(R.id.network_request_title_text); title.setText(R.string.network_connection_request_dialog_title); - ProgressBar progressBar = customTitle.findViewById(R.id.network_request_title_progress); + final ProgressBar progressBar = customTitle.findViewById(R.id.network_request_title_progress); progressBar.setVisibility(View.VISIBLE); // Prepares adapter. - AccessPointAdapter adapter = new AccessPointAdapter(context, + mDialogAdapter = new AccessPointAdapter(context, R.layout.preference_access_point, getAccessPointList()); - AlertDialog.Builder builder = new AlertDialog.Builder(context) + final AlertDialog.Builder builder = new AlertDialog.Builder(context) .setCustomTitle(customTitle) - .setAdapter(adapter, this) + .setAdapter(mDialogAdapter, this) .setPositiveButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); return builder.create(); } + @NonNull List getAccessPointList() { // Initials list for adapter, in case of display crashing. if (mAccessPointList == null) { @@ -84,8 +97,24 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp return mAccessPointList; } + private BaseAdapter getDialogAdapter() { + return mDialogAdapter; + } + @Override public void onClick(DialogInterface dialog, int which) { + final List accessPointList = getAccessPointList(); + if (accessPointList.size() == 0) { + return; // Invalid values. + } + if (mUserSelectionCallback == null) { + return; // Callback is missing or not ready. + } + + if (which < accessPointList.size()) { + WifiConfiguration wifiConfig = accessPointList.get(which).getConfig(); + mUserSelectionCallback.select(wifiConfig); + } } @Override @@ -93,19 +122,27 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp super.onPause(); mHandler.removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST); + final WifiManager wifiManager = getContext().getApplicationContext() + .getSystemService(WifiManager.class); + if (wifiManager != null) { + wifiManager.unregisterNetworkRequestMatchCallback(this); + } } @Override public void onResume() { super.onResume(); - // TODO(b/117399926): Starts to scan current WiFi. - + final WifiManager wifiManager = getContext().getApplicationContext() + .getSystemService(WifiManager.class); + if (wifiManager != null) { + wifiManager.registerNetworkRequestMatchCallback(this, mHandler); + } // Sets time-out to stop scanning. mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS); } - private Handler mHandler = new Handler() { + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -121,8 +158,12 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp }; protected void stopScanningAndPopTimeoutDialog() { + // Dismisses current dialog. dismiss(); - NetworkRequestTimeoutDialogFragment fragment = NetworkRequestTimeoutDialogFragment.newInstance(); + + // Throws new timeout dialog. + final NetworkRequestTimeoutDialogFragment fragment = NetworkRequestTimeoutDialogFragment + .newInstance(); fragment.show(getActivity().getSupportFragmentManager(), null); } @@ -146,14 +187,108 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp public View getView(int position, View view, ViewGroup parent) { if (view == null) { view = mInflater.inflate(mResourceId, parent, false); + + final View divider = view.findViewById(com.android.settingslib.R.id.two_target_divider); + divider.setVisibility(View.GONE); } - // TODO: Sets correct information to list item. - final View divider = view.findViewById(com.android.settingslib.R.id.two_target_divider); - divider.setVisibility(View.GONE); + final AccessPoint accessPoint = getItem(position); + + final TextView titleView = view.findViewById(android.R.id.title); + if (titleView != null) { + titleView.setText(accessPoint.getSsidStr()); + } + + final TextView summary = view.findViewById(android.R.id.summary); + if (summary != null) { + summary.setText(accessPoint.getSettingsSummary()); + } + + final PreferenceImageView imageView = view.findViewById(android.R.id.icon); + final int level = accessPoint.getLevel(); + if (imageView != null) { + final Drawable drawable = getContext().getDrawable(Utils.getWifiIconResource(level)); + drawable.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal)); + imageView.setImageDrawable(drawable); + } return view; } } -} + @Override + public void onAbort() { + // TODO(b/117399926): We should have a UI notify user here. + } + + @Override + public void onUserSelectionCallbackRegistration( + NetworkRequestUserSelectionCallback userSelectionCallback) { + mUserSelectionCallback = userSelectionCallback; + } + + @Override + public void onMatch(List scanResults) { + // TODO(b/119846365): Checks if we could escalate the converting effort. + // Converts ScanResult to WifiConfiguration. + List wifiConfigurations = null; + final WifiManager wifiManager = getContext().getApplicationContext() + .getSystemService(WifiManager.class); + if (wifiManager != null) { + wifiConfigurations = wifiManager.getAllMatchingWifiConfigs(scanResults); + } + + setUpAccessPointList(wifiConfigurations); + + if (getDialogAdapter() != null) { + getDialogAdapter().notifyDataSetChanged(); + } + } + + @Override + public void onUserSelectionConnectSuccess(WifiConfiguration wificonfiguration) { + if (getDialogAdapter() != null) { + updateAccessPointListItem(wificonfiguration); + getDialogAdapter().notifyDataSetChanged(); + } + } + + @Override + public void onUserSelectionConnectFailure(WifiConfiguration wificonfiguration) { + if (mDialogAdapter != null) { + updateAccessPointListItem(wificonfiguration); + getDialogAdapter().notifyDataSetChanged(); + } + } + + private void updateAccessPointListItem(WifiConfiguration wificonfiguration) { + if (wificonfiguration == null) { + return; + } + + final List accessPointList = getAccessPointList(); + final int accessPointListSize = accessPointList.size(); + + for (int i = 0; i < accessPointListSize; i++) { + AccessPoint accessPoint = accessPointList.get(i); + // It is the same AccessPoint SSID, and should be replaced to update latest properties. + if (accessPoint.matches(wificonfiguration)) { + accessPointList.set(i, new AccessPoint(getContext(), wificonfiguration)); + break; + } + } + } + + private void setUpAccessPointList(List wifiConfigurations) { + // Grants for zero size input, since maybe current wifi is off or somethings are wrong. + if (wifiConfigurations == null) { + return; + } + + final List accessPointList = getAccessPointList(); + accessPointList.clear(); + for (WifiConfiguration config : wifiConfigurations) { + accessPointList.add(new AccessPoint(getContext(), config)); + } + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java index 17718a692c7..2e806ddfed0 100644 --- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java @@ -17,18 +17,34 @@ package com.android.settings.wifi; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; import android.content.DialogInterface; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; +import android.os.Bundle; import android.widget.Button; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +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; import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import org.robolectric.shadows.ShadowLooper; @@ -37,13 +53,17 @@ import org.robolectric.shadows.ShadowLooper; @Config(shadows = {SettingsShadowResourcesImpl.class, ShadowAlertDialogCompat.class}) public class NetworkRequestDialogFragmentTest { + final String KEY_SSID = "key_ssid"; + private FragmentActivity mActivity; private NetworkRequestDialogFragment networkRequestDialogFragment; + private Context mContext; @Before public void setUp() { mActivity = Robolectric.setupActivity(FragmentActivity.class); networkRequestDialogFragment = spy(NetworkRequestDialogFragment.newInstance()); + mContext = spy(RuntimeEnvironment.application); } @Test @@ -88,4 +108,137 @@ public class NetworkRequestDialogFragmentTest { bCalledStopAndPop = true; } } + + @Test + public void onResume_shouldRegisterCallback() { + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + + networkRequestDialogFragment.onResume(); + + verify(wifiManager).registerNetworkRequestMatchCallback(any(), any()); + } + + @Test + public void onPause_shouldUnRegisterCallback() { + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + + networkRequestDialogFragment.onPause(); + + verify(wifiManager).unregisterNetworkRequestMatchCallback(networkRequestDialogFragment); + } + + @Test + public void updateAccessPointList_onUserSelectionConnectSuccess_updateCorrectly() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); + + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + + // Test if config would update list. + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "Test AP 3"; + networkRequestDialogFragment.onUserSelectionConnectSuccess(config); + + AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); + verify(accessPointList, times(1)).set(2, verifyAccessPoint); + } + + @Test + public void updateAccessPointList_onUserSelectionConnectFailure_updateCorrectly() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); + + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + + // Test if config would update list. + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "Test AP 3"; + networkRequestDialogFragment.onUserSelectionConnectFailure(config); + + AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); + verify(accessPointList, times(1)).set(2, verifyAccessPoint); + } + + @Test + public void onUserSelectionCallbackRegistration_shouldCallSelect() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle); + accessPointList.add(clickedAccessPoint); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + + NetworkRequestUserSelectionCallback selectionCallback = mock( + NetworkRequestUserSelectionCallback.class); + AlertDialog dialog = mock(AlertDialog.class); + networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback); + + networkRequestDialogFragment.onClick(dialog, 2); + + verify(selectionCallback, times(1)).select(clickedAccessPoint.getConfig()); + } + + @Test + public void onMatch_shouldUpdatedList() { + // Prepares WifiManager. + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + + List wifiConfigurationList = new ArrayList<>(); + WifiConfiguration config = new WifiConfiguration(); + final String SSID_AP1 = "Test AP 1"; + config.SSID = SSID_AP1; + wifiConfigurationList.add(config); + config = new WifiConfiguration(); + final String SSID_AP2 = "Test AP 2"; + config.SSID = SSID_AP2; + wifiConfigurationList.add(config); + + // Prepares callback converted data. + List scanResults = new ArrayList<>(); + when(wifiManager.getAllMatchingWifiConfigs(scanResults)).thenReturn(wifiConfigurationList); + + networkRequestDialogFragment.onMatch(scanResults); + + List accessPointList = networkRequestDialogFragment.getAccessPointList(); + assertThat(accessPointList).isNotEmpty(); + assertThat(accessPointList.size()).isEqualTo(2); + assertThat(accessPointList.get(0).getSsid()).isEqualTo(SSID_AP1); + assertThat(accessPointList.get(1).getSsid()).isEqualTo(SSID_AP2); + } }