I\'m trying to create a Dialog
which shows something like ACTION_PICK_WIFI_NETWORK
but instead of open Android Settings / WiFi
open it
From the ui perspective you need a Custom adapter:
private class WifiAdapter extends ArrayAdapter<ScanResult> {
public WifiAdapter(Context context, int resource, List<ScanResult> objects) {
super(context, resource, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.wifi_item, parent, false);
}
ScanResult result = getItem(position);
((TextView) convertView.findViewById(R.id.wifi_name)).setText(formatSSDI(result));
((ImageView) convertView.findViewById(R.id.wifi_img)).setImageLevel(getNormalizedLevel(result));
return convertView;
}
private int getNormalizedLevel(ScanResult r) {
int level = WifiManager.calculateSignalLevel(r.level,
5);
Log.e(getClass().getSimpleName(), "level " + level);
return level;
}
private String formatSSDI(ScanResult r) {
if (r == null || r.SSID == null || "".equalsIgnoreCase(r.SSID.trim())) {
return "no data";
}
return r.SSID.replace("\"", "");
}
I slightly changed your showWifiListDialog
:
private void showWifiListDialog(List<ScanResult> results) {
Collections.sort(results, new Comparator<ScanResult>() {
@Override
public int compare(ScanResult lhs, ScanResult rhs) {
return rhs.level > lhs.level ? 1 : rhs.level < lhs.level ? -1 : 0;
}
});
AlertDialog.Builder builderSingle = new AlertDialog.Builder(
this);
final WifiAdapter arrayAdapter = new WifiAdapter(
this,
android.R.layout.select_dialog_item, results);
builderSingle.setNegativeButton(getString(android.R.string.cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builderSingle.setAdapter(arrayAdapter,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String strName = arrayAdapter.getItem(which).SSID;
Toast.makeText(getApplicationContext(), "Selected " + strName, Toast.LENGTH_SHORT).show();
}
});
AlertDialog dialog = builderSingle.create();
dialog.show();
}
the Wifi Item is
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:textSize="16sp"
android:padding="5dp"
android:layout_gravity="center_vertical"
android:id="@+id/wifi_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageView
android:id="@+id/wifi_img"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/wifi_level" />
</LinearLayout>
and the drawable wifi_level is
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/ic_signal_wifi_0_bar_black_24dp"
android:maxLevel="0" />
<item
android:drawable="@drawable/ic_signal_wifi_1_bar_black_24dp"
android:maxLevel="1" />
<item
android:drawable="@drawable/ic_signal_wifi_2_bar_black_24dp"
android:maxLevel="2" />
<item
android:drawable="@drawable/ic_signal_wifi_3_bar_black_24dp"
android:maxLevel="3" />
<item
android:drawable="@drawable/ic_signal_wifi_4_bar_black_24dp"
android:maxLevel="4" />
</level-list>
I took the five png from here
For the Connection, the answer is yes it should be possible. Accordingly to the documentation at least. You can instantiate an object of WifiConfiguration, and feed it with info of the network you want to connect to (SSID
and password
). It is not a straightforward thing to do. If you have to take in consideration the different kind of keys encryption, (WPA
, WEP
, free wifi
). Onceyou filled up the object, you have to call
mWifiManager.disconect();
int resId = mWifiManager.addNetwork(config);
mWifiManager.enableNetwork(resId, true);
Edit:
If you want to show the wifi-signal-strength icon with and without padlock, you could use custom attribute
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="wifi">
<attr name="state_locked" format="boolean" />
</declare-styleable>
</resources>
and update its state in a subclass of ImageView:
public class WifiImageView extends ImageView {
private static final int[] STATE_LOCKED = {R.attr.state_locked};
private boolean mWifiLocked;
public WifiImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (mWifiLocked) {
mergeDrawableStates(drawableState, STATE_LOCKED);
}
return drawableState;
}
public void setStateLocked(boolean locked) {
mWifiLocked = locked;
refreshDrawableState();
}
}
Now assuming that the android:src of your WifeImageView
is a selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item custom:state_locked="false" android:drawable="@drawable/wifi_level" />
<item custom:state_locked="true" android:drawable="@drawable/wifi_level_lock" />
</selector>
In your Adapter you can easily switch between the two level-list
, adding the following two lines of code
boolean protectedWifi = result.capabilities.contains ("WEP") || result.capabilities.contains("WPA");
((WifiImageView) convertView.findViewById(R.id.wifi_img)).setStateLocked(protectedWifi);
protectedWifi
is evaluated true if result.capabilities
contains WEP
or WPA
, and setStateLocked(protectedWifi);
will switch between the two level-list
s accordingly to its value. Of course, in the wifi_item.xml
, you have two change from ImageView
, to the custom WifiImageView
.
I found the library WifiUtils to be extremely useful. To connect to a selected wifi network, create connectToWifi(ssid, pass)
private fun connectToWifi(ssid: String, password: String) {
WifiUtils.withContext(applicationContext)
.connectWith(ssid, password)
.onConnectionResult(::connectWifiResultListener)
.start()
}
Then, replace Toast.makeText(getApplicationContext(), "Selected " + strName, Toast.LENGTH_SHORT).show();
in showWifiListDialog
setAdapter onClick with connectToWifi(strName, password)
The password can be empty ""
if the wifi network doesn't require authentication
The result will return to connectWifiResultListener
private fun connectWifiResultListener(isSuccess: Boolean) {
if (isSuccess)
// do something
else}
// show error
}
In addition, I adapted scanWifi
from the WifiUtils
library with @Skizo-ozᴉʞS wifi scanning solution and it worked like a charm. So, instead of the startWifiScans
method and the wifiReceiver
which calls showWifiListDialog(results)
I used
WifiUtils.withContext(applicationContext).scanWifi(::getScanResults).start()
And call showWifiListDialog
in getScanResults
private fun getScanResults(results: List<ScanResult>) {
if (results.isEmpty()) { return }
showWifiListDialog(results)
}