快捷开关之无线网路

概述

无线网络作为快捷开关三大金刚之一,重要性不言而喻。为用户提供开关wifi以及切换wifi的功能。无线网络虽然只是作为一个入口,和设置中无线网络页面扮演着相同的角色,但是因为可随时访问的特性,会比设置无线网络有着更多的用户场景,实时性,交互性,稳定性都有着更高的要求。

FeuPNF.png

从以上的UML类图中我们初步分析一下无线网络开关相关的模块结构:

  • 从View的方面来说

    • 与所有的快捷开关一致,WifiTile是无线网络开关的View层,继承自QSTile(顶部3个方法为开关必须要实现的基本功能,其他方法根据需要可选择实现,比如有些开关没有长按功能就不需要实现handleLongClick()
    • 除此之外还有下拉菜单功能,这部分是由接口DetailAdapter来规范的,如果要使用下拉菜单,需要自行实现以上接口,并在QSTile中的getDetailAdapter()方法中返回实例
  • 从Controller的方面来说

    • NetworkController,用于开关WIFI和获取WIFI的状态变化信息,是快捷开关的基本功能
    • AccessPointController,用于发起扫描WIFI和获取扫描之后的WIFI列表,用于下拉菜单的展示

以上就是整个无线网络开关的模块相关结构了,重要的细节我们稍后讨论

正文

开关无线网络

开关网络

在点击开关或者点开下拉菜单的时候,会操作网络状态,Android提供了接口操作wifi开关,因为WIFI与移动热点相关连,所以这里加入了移动热点的相关判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// com.android.systemui.statusbar.policy.NetworkControllerImpl#setWifiEnabled

@Override
public void setWifiEnabled(final boolean enabled) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... args) {
// Disable tethering if enabling Wifi
final int wifiApState = mWifiManager.getWifiApState();
if(DEBUG) Log.d(TAG, "doInBackground: enabled = " + enabled + " wifiApState = " + wifiApState);
if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
mWifiManager.setWifiApEnabled(null, false);
}

mWifiManager.setWifiEnabled(enabled);
return null;
}
}.execute();
}

响应状态

wifi的状态响应是通过SignalCallback回调传过来的数据响应,而这个回调在NetworkController的实现类中统一通过广播处理,这个实现类除了处理wifi的信号和状态变化,还会处理移动数据的状态变化,用于更新状态栏信号,通知栏头部的运营商名称等信息,这个模块的结构我们在另一篇文章中(TODO)解析,这里我们只需要知晓状态响应的来源位置就好

开关的状态会通过handleUpdateState()方法更新,然后交由父类统一处理,这块逻辑我们会在另一篇文章中(TODO)解析,这里我们只需要在handleUpdateState()中更新正确的状态即可

切换无线网络

无线网络的切换操作方式是点击开关下拉菜单,然后点击需要切换的网络,展示是在WifiTile这个View中,而数据来源是AccessPointController,对应的实现类AccessPointControllerImpl

切换网络

通过以下方法切换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public boolean connect(AccessPoint ap) {
if (ap == null) return false;
if (ap.isFreeWifi()) {
if (WifiUtils.isFreeWifiOnceConnected(mContext)) {
mWifiTracker.connect(ap);
}
Log.d(TAG, "connect: free wifi ssid = " + ap.getSsidStr() + " bssid = " + ap.getBssid());
Intent intent = new Intent(MZ_ACTION_FREE_WIFI_ACTIVITY);
Bundle args = ap.getFreeWifiApInfo().getBundle(ap);
intent.putExtra(EXTRA_FREE_WIFI_BUNDLE, args);
fireSettingsIntentCallback(intent);
return true;
}
else if (ap.isSaved()) {
if (DEBUG) Log.d(TAG, "connect networkId=" + ap.getConfig().networkId);
mWifiTracker.getManager().connect(ap.getConfig().networkId, mConnectListener);
} else {
// Unknown network, need to add it.
if (ap.getSecurity() != AccessPoint.SECURITY_NONE) {
Intent intent = new Intent(/*Settings.ACTION_WIFI_SETTINGS*/MZ_ACTION_PICK_WIFI_NETWORK);
intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsidStr());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
fireSettingsIntentCallback(intent);
return true;
} else {
ap.generateOpenNetworkConfig();
mWifiTracker.getManager().connect(ap.getConfig(), mConnectListener);
}
}
return false;
}

需要注意的是:

  • 对于已经保存的网络或者开放网络,可以直接通过android.net.wifi.WifiManager#connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener)连接
  • 而对于免费WIFI和加密WIFI,需要跳转到设置页面进行相应的处理,免费wifi需要设置认证,而加密wifi需要在设置输入密码

响应刷新

状态响应比较简单,SettingsLib会通过WifiListening这个接口来通知下拉菜单的wifi热点的变化,另外这个回调会在下拉菜单展开时注册,收起时取消注册

作者

0xforee

发布于

2017-04-14

更新于

2017-04-20

许可协议


欢迎关注我的公众号 0xforee,第一时间获取更多有价值的思考

评论