腦圖
1.WifiManager
基礎(chǔ)功能
interface IWifiManager {
// wifi是否打開
fun isWifiEnabled(): Boolean
// 操作wifi開關(guān)
fun switchWifi(isOpen: Boolean)
// 開始掃描附近wifi
fun startScan()
// 掃描后邑退,獲取掃描wifi列表
fun getScanResults(): MutableList<ScanResult>
// 主動連接wifi品嚣,password為空則為無密碼
fun connectWifi(wifiBean: WifiBean, password: String?): Boolean
// 忘記wifi
fun forgetWifi(wifiBean: WifiBean): Boolean
}
實現(xiàn)流程
1.權(quán)限檢測
/**
@description 權(quán)限相關(guān)
*/
private val wifiPermission = activity.multiplePermissionsForResult()
private val permissionList = arrayOf(
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.WRITE_SETTINGS //用于sdk29調(diào)用系統(tǒng)wifi設(shè)置權(quán)限用
)
activity.multiplePermissionsForResult():
使用了registerForActivityResult(resultContract: ActivityResultContract<I, O>他巨,callback: ActivityResultCallback<O>) 這是新的權(quán)限申請方式居扒,具體可看官方文檔酷含。方法在LifecycleOwner中携添,調(diào)用所需的倆個參數(shù)全蝶,resultContract是請求結(jié)果協(xié)議類涯曲,用于包裹輸入值I和輸出值O雀监,其中輸入值例如權(quán)限傳遞就是String(Manifest.permission.CHANGE_WIFI_STATE)双吆,輸出值是Boolean,返回權(quán)限授權(quán)結(jié)果会前。第二個參數(shù)值callback好乐,是onActivityResult的結(jié)果,用于最終結(jié)果回調(diào)瓦宜。
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
這三個權(quán)限是為了兼容8.0蔚万、9.0中所需申請的wifi權(quán)限,用于wifi狀態(tài)的監(jiān)聽和wifi掃描
Manifest.permission.WRITE_SETTINGS //用于sdk29調(diào)用系統(tǒng)wifi設(shè)置權(quán)限用
private fun checkPermission(block: () -> Unit) {
if (permissionList.hasPermission()) {
block.invoke()
} else {
wifiPermission.launch(permissionList) {
// 檢測權(quán)限完整
var hasPermission = false
for (entry in it) {
if (!entry.value) {
hasPermission = false
break
} else {
hasPermission = true
}
}
if (hasPermission) block.invoke()
}
}
}
2.基礎(chǔ)功能的具體實現(xiàn)
// 系統(tǒng)WifiManager的獲取
val wifiManager by lazy {
activity.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
override fun isWifiEnabled(): Boolean {
return wifiManager.isWifiEnabled
}
override fun switchWifi(isOpen: Boolean) {
// 新版本如果是android29的临庇,只能通過上面這種凡是操作wifi
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
// activity.startActivity(panelIntent)
// } else {
// wifiManager.isWifiEnabled = isOpen
// }
wifiManager.isWifiEnabled = isOpen
}
override fun startScan() {
checkPermission {
wifiManager.startScan()
}
}
/**
@description 獲取系統(tǒng)掃描的wifi列表信息
*/
override fun getScanResults(): MutableList<ScanResult> {
//過濾空的ssid并去重
val resultMap =
wifiManager.scanResults?.filter { !it.SSID.isNullOrBlank() }?.groupBy { it.SSID }
val scanResults = mutableListOf<ScanResult>()
if (resultMap.isNullOrEmpty()) {
return scanResults
}
for ((_, value) in resultMap) {
// 取首個值
value.firstOrNull()?.let {
scanResults.add(it)
}
}
return scanResults
}
/**
@description 連接有密碼的wifi反璃,只支持版本低于29
@return 是否連接成功
*/
override fun connectWifi(wifiBean: WifiBean, password: String?): Boolean {
if (!wifiManager.isWifiEnabled) {
return false
}
// 無密碼連接
if (password == null) {
val scanResult = wifiManager.scanResults.filter { it.SSID == wifiBean.ssid }
scanResult.forEach {
if (connectScanResult(it)) {
return true
}
}
return false
} else {
val padWifiNetwork =
createWifiConfig(
wifiBean.ssid ?: "",
password,
WifiBean.getCapabilityTag(wifiBean.capabilities)
)
return wifiManager.enableNetwork(wifiManager.addNetwork(padWifiNetwork), true)
}
}
override fun forgetWifi(wifiBean: WifiBean): Boolean {
val configurations = wifiManager.configuredNetworks
for (configuration in configurations) {
val replace = configuration.SSID.replace("\"", "")
if (replace == wifiBean.ssid) {
var removeResult = wifiManager.removeNetwork(configuration.networkId)
removeResult = removeResult and wifiManager.saveConfiguration()
return removeResult
}
}
return false
}
主要對IWifiMananger的接口方法進(jìn)行實現(xiàn),也是定義了對外調(diào)用最近本的幾個方法
3.其他功能方法
/**
@description 主動連接掃描到的wifi
@param scanResult 系統(tǒng)wifi掃描返回的信息類
@return 是否連接成功
*/
private fun connectScanResult(scanResult: ScanResult?): Boolean {
return if (scanResult != null) {
// 1.查找曾經(jīng)的wifi配置假夺,已連上系統(tǒng)會存儲下來
val config = getWifiConfig(scanResult.SSID)
if (config != null && config.status != WifiConfiguration.Status.DISABLED) {
if (BuildConfig.DEBUG) {
logSettingD(TAG, "找到了歷史wifi:{scanResult.SSID}")
}
// 1.1找到以后調(diào)用連接
wifiManager.enableNetwork(config.networkId, true)
} else {
// 2.沒有wifi配置淮蜈,判斷當(dāng)前模板wifi加密方式
val capabilityTag = WifiBean.getCapabilityTag(scanResult.capabilities)
if (capabilityTag == WifiBean.CapabilityTag.NONE) {
// 2.1無加密,直接連接
val padWifiNetwork =
createWifiConfig(
scanResult.SSID,
capabilityTag = WifiBean.getCapabilityTag(scanResult.capabilities)
)
val netId = wifiManager.addNetwork(padWifiNetwork)
if (BuildConfig.DEBUG) {
logSettingD(TAG, "不需要密碼連接wifi:{scanResult.SSID}")
}
wifiManager.enableNetwork(netId, true)
wifiManager.reconnect()
} else {
// 2.2有加密已卷,并且曾經(jīng)也沒有wifi配置梧田,自然不需要主動連接
if (BuildConfig.DEBUG) {
logSettingD(TAG, "需要密碼連接wifi:${scanResult.SSID}")
}
false
}
}
} else {
if (BuildConfig.DEBUG) {
logSettingD(TAG, "connectWifi 沒有找到")
}
false
}
}
/**
@description 將"ssid"替換成ssid
*/
private fun getWifiConfig(ssid: String?): WifiConfiguration? {
return wifiManager.configuredNetworks.firstOrNull {
it.SSID.replace("\"", "") == ssid
}
}
/**
@description 根據(jù)ssid、密碼侧蘸、加密方式裁眯,生產(chǎn)wifi配置信息
*/
private fun createWifiConfig(
ssid: String,
password: String = "",
capabilityTag: WifiBean.CapabilityTag
): WifiConfiguration {
// 1.初始化WifiConfiguration
val config = WifiConfiguration()
config.allowedAuthAlgorithms.clear()
config.allowedGroupCiphers.clear()
config.allowedKeyManagement.clear()
config.allowedPairwiseCiphers.clear()
config.allowedProtocols.clear()
//指定對應(yīng)的SSID
config.SSID = "\"" + ssid + "\""
// 2.如果之前有類似的配置
val tempConfig = wifiManager.configuredNetworks.singleOrNull { it.BSSID == "\"$ssid\"" }
if (tempConfig != null) {
//則清除舊有配置 不是自己創(chuàng)建的network 這里其實是刪不掉的
val isDisable = wifiManager.disableNetwork(tempConfig.networkId)
val isRemove = wifiManager.removeNetwork(tempConfig.networkId)
val isSave = wifiManager.saveConfiguration()
if (BuildConfig.DEBUG) {
logSettingD(TAG, "清除wifi配置:${tempConfig.SSID + (isDisable && isRemove && isSave)}")
}
}
// 3.根據(jù)加密方式生成不同配置
when (capabilityTag) {
WifiBean.CapabilityTag.NONE -> {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
//以WEP加密的場景
}
WifiBean.CapabilityTag.WEP -> {
config.hiddenSSID = true
config.wepKeys[0] = "\"" + password + "\""
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
config.wepTxKeyIndex = 0
//以WPA加密的場景
}
WifiBean.CapabilityTag.PSK -> {
config.preSharedKey = "\"" + password + "\""
config.hiddenSSID = true
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
config.status = WifiConfiguration.Status.ENABLED
}
else -> {
}
}
return config
}
/**
@description 獲取wifi列表信息,WifiBean是期望的數(shù)據(jù)類型
*/
fun getWifiBeanList(): List<WifiBean> {
//按照強(qiáng)度排序
val wifiList = getScanResults()
.sortedByDescending { it.level }
.map { scanResult ->
WifiBean.transform(scanResult)
}
val connectionInfoSsid = wifiManager.connectionInfo?.ssid?.replace("\"", "")
logSettingD(WifiProxy.TAG, "getWifiBeanList: connectionInfoSsid = $connectionInfoSsid")
return changeWifiListBySsid(connectionInfoSsid, WifiBean.ConnectStatus.CONNECTED, wifiList)
}
/**
@description 修改匹配后的wifiBean列表
@param ssid 要匹配的ssid
@param targetStatus 目標(biāo)狀態(tài)
@param list 要修改的wifiList數(shù)據(jù)
*/
fun changeWifiListBySsid(
ssid: String?,
targetStatus: WifiBean.ConnectStatus,
list: List<WifiBean>
): MutableList<WifiBean> {
val temps: MutableList<WifiBean> = mutableListOf()
temps.addAll(list)
// 為空直接返回原有數(shù)據(jù)列表
if (ssid == null) return temps
for (i in list.indices) {
val temp = list[i]
if (ssid == temp.ssid) {
temp.connectStatus = targetStatus
temps.remove(temp)
temps.add(0, temp)
} else {
temp.connectStatus = WifiBean.ConnectStatus.DISCONNECT
}
}
return temps
}
2.WifiScanReceiver
/**
@description Wifi連接狀態(tài)接收器
@param switchListener wifi開關(guān)狀態(tài)監(jiān)聽
@param scanSucListener wifi掃描結(jié)果是否成功
@param connectListener wifi請求連接的狀態(tài)監(jiān)聽
*/
class WifiScanReceiver(
private val wifiManager: WifiManager,
private val switchListener: (Boolean) -> Unit,
private val scanSucListener: (Boolean) -> Unit,
private val connectListener: (String, Boolean) -> Unit,
) : BroadcastReceiver() {
companion object {
const val TAG = "WifiScanReceiver"
}
fun register(activity: AppCompatActivity) {
activity.registerReceiver(this, IntentFilter().apply {
addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
addAction(WifiManager.RSSI_CHANGED_ACTION)
})
}
override fun onReceive(context: Context?, intent: Intent?) {
logSettingD(TAG, "onReceive: action = ${intent?.action}")
intent?.run {
when (action) {
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION -> {
scanSucListener.invoke(isScanSuc(intent))
}
WifiManager.WIFI_STATE_CHANGED_ACTION -> {
handleSwitchChange(intent)
}
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION -> {
handleSupplicantState(intent)
}
WifiManager.RSSI_CHANGED_ACTION ->{
}
}
}
}
/**
@description 處理wifi連接請求的狀態(tài)變化
*/
private fun handleSupplicantState(intent: Intent) {
// 密碼錯誤
val error = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1)
if (error == WifiManager.ERROR_AUTHENTICATING) {
wifiManager.connectionInfo?.ssid?.replace("\"", "")?.let {
logSettingD(TAG, "獲取連接狀態(tài):密碼錯誤")
connectListener.invoke(it, true)
}
return
}
// 獲取連接狀態(tài)
val supplicantState: SupplicantState? =
intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)
when (supplicantState) {
// 成功
SupplicantState.COMPLETED -> {
logSettingD(TAG, "獲取連接狀態(tài):成功")
wifiManager.connectionInfo?.ssid?.replace("\"", "")?.let {
connectListener.invoke(it, false)
}
}
// 不活躍的
SupplicantState.INACTIVE -> {
logSettingD(TAG, "獲取連接狀態(tài):不活躍的")
}
// 接口禁用
SupplicantState.INTERFACE_DISABLED -> {
logSettingD(TAG, "獲取連接狀態(tài):接口禁用")
}
SupplicantState.DISCONNECTED -> {
logSettingD(TAG, "獲取連接狀態(tài):斷開連接")
}
SupplicantState.SCANNING -> {
logSettingD(TAG, "獲取連接狀態(tài):正在掃描")
}
SupplicantState.AUTHENTICATING -> {
logSettingD(TAG, "獲取連接狀態(tài):正在驗證")
}
SupplicantState.ASSOCIATING -> {
logSettingD(TAG, "獲取連接狀態(tài):正在關(guān)聯(lián)")
}
SupplicantState.ASSOCIATED -> {
logSettingD(TAG, "獲取連接狀態(tài):已經(jīng)關(guān)聯(lián)")
}
SupplicantState.FOUR_WAY_HANDSHAKE -> {
logSettingD(TAG, "獲取連接狀態(tài):四次握手")
}
SupplicantState.GROUP_HANDSHAKE -> {
logSettingD(TAG, "獲取連接狀態(tài):組握手")
}
SupplicantState.DORMANT -> {
logSettingD(TAG, "獲取連接狀態(tài):休眠")
}
SupplicantState.UNINITIALIZED -> {
logSettingD(TAG, "獲取連接狀態(tài):未初始化")
}
SupplicantState.INVALID -> {
logSettingD(TAG, "獲取連接狀態(tài):無效的")
}
else -> {
logSettingD(TAG, "wifi連接結(jié)果通知")
}
}
}
/**
@description 處理wifi掃描結(jié)果是否成功
*/
private fun isScanSuc(intent: Intent): Boolean {
if (wifiManager.isWifiEnabled) {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
} else {
true
}
}
return false
}
/**
@description 處理wifi開關(guān)狀態(tài)變化
*/
private fun handleSwitchChange(intent: Intent) {
val state =
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)
logSettingD(TAG, "handleStateChange: state = ${getWifiStateStr(state)}")
if (state == WifiManager.WIFI_STATE_ENABLED) {
switchListener.invoke(true)
} else if (state == WifiManager.WIFI_STATE_DISABLING || state == WifiManager.WIFI_STATE_DISABLED) {
switchListener.invoke(false)
}
}
private fun getWifiStateStr(state: Int): String {
return when (state) {
WifiManager.WIFI_STATE_DISABLED -> "Wifi已關(guān)閉"
WifiManager.WIFI_STATE_DISABLING -> "Wifi關(guān)閉中"
WifiManager.WIFI_STATE_ENABLED -> "Wifi已打開"
WifiManager.WIFI_STATE_ENABLING -> "Wifi打開中"
WifiManager.WIFI_STATE_UNKNOWN -> "Wifi未知狀態(tài)"
else -> "Wifi未知狀態(tài)"
}
}
}
主要用于配合wifiManager讳癌,獲取到三種監(jiān)聽
1.SCAN_RESULTS_AVAILABLE_ACTION:監(jiān)聽掃描結(jié)果穿稳,掃描成功或失敗會觸發(fā)
2.WIFI_STATE_CHANGED_ACTION:處理wifi開關(guān)狀態(tài)變化時,進(jìn)行回調(diào)處理
3.SUPPLICANT_STATE_CHANGED_ACTION:處理wifi連接請求的狀態(tài)變化
sdk29以后也出現(xiàn)了其他新的api監(jiān)聽回調(diào)晌坤,wifiManager.registerXXX()就能監(jiān)聽對應(yīng)狀態(tài)逢艘,但由于不兼容舊設(shè)備,因此全部wifi狀態(tài)都用廣播方式獲取泡仗,廣播獲取頻率會受到具體限制埋虹,具體情況詳見官方文檔
3.ConnectManager
/**
@author: Zed.Qiu
@date: 2022/8/15
@description: 用于管理網(wǎng)絡(luò)狀態(tài)發(fā)生變化的回調(diào),以及處理信號變化回調(diào)
*/
class TyphurConnectManager(private var context: Context = TyphurCoreConst.app) {
companion object {
const val TAG = "TyphurConnectManager"
@Volatile
var instance: TyphurConnectManager? = null
fun getInstance(context: Context = TyphurCoreConst.app): TyphurConnectManager {
if (instance == null) {
synchronized(TyphurConnectManager::class.java) {
if (instance == null) {
instance = TyphurConnectManager(context)
}
}
}
instance?.context = context
return instance!!
}
/**
@description 是否有網(wǎng)絡(luò)連接
*/
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 當(dāng)前是否有網(wǎng)絡(luò)
val nw = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
return when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
// 蜂窩網(wǎng)
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
// 以太網(wǎng)
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
// vpn
actNw.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> true
else -> false
}
} else {
return connectivityManager.activeNetworkInfo?.isConnected ?: false
}
}
}
private var isRegister = false
/**
@description 網(wǎng)絡(luò)狀態(tài)變化回調(diào)管理列表
*/
private val networkCallbackList = mutableListOf<ConnectivityManager.NetworkCallback>()
/**
@description 網(wǎng)絡(luò)連接管理器
*/
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val networkCallback = object :
ConnectivityManager.NetworkCallback() {
override fun onUnavailable() {
super.onUnavailable()
logSettingD(TAG, "NetworkCallback onUnavailable! 網(wǎng)絡(luò)不可訪問")
networkCallbackList.forEach { callback ->
callback.onUnavailable()
}
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
// 網(wǎng)絡(luò)可訪問時綁定
val bindResult = connectivityManager.bindProcessToNetwork(network)
logSettingD(TAG, "NetworkCallback onAvailable! 網(wǎng)絡(luò)已連接 bindResult = $bindResult")
networkCallbackList.forEach { callback ->
callback.onAvailable(network)
}
}
override fun onLosing(network: Network, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
logSettingD(TAG, "NetworkCallback onLosing! 網(wǎng)絡(luò)斷開連接中")
networkCallbackList.forEach { callback ->
callback.onLosing(network, maxMsToLive)
}
}
override fun onLost(network: Network) {
super.onLost(network)
logSettingD(TAG, "NetworkCallback onLost! 網(wǎng)絡(luò)已斷開連接")
networkCallbackList.forEach { callback ->
callback.onLost(network)
}
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
logSettingD(TAG, "NetworkCallback onCapabilitiesChanged! 信號強(qiáng)度發(fā)生變化")
logSettingD(TAG, "networkCapabilities = $networkCapabilities")
networkCallbackList.forEach { callback ->
callback.onCapabilitiesChanged(network, networkCapabilities)
}
}
}
init {
register()
}
fun register(builder :NetworkRequest.Builder? =null) {
if (isRegister) return
if (builder == null){
registerNetworkCallback()
}else{
connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
}
isRegister = true
}
fun unRegister() {
connectivityManager.bindProcessToNetwork(null)
connectivityManager.unregisterNetworkCallback(networkCallback)
networkCallbackList.clear()
isRegister = false
}
/**
@description 添加監(jiān)聽回調(diào)
*/
fun addCallback(callback: ConnectivityManager.NetworkCallback?) {
callback?.run {
if (!networkCallbackList.contains(this)) {
networkCallbackList.add(this)
}
}
}
/**
@description 移除監(jiān)聽回調(diào)
*/
fun removeCallback(callback: ConnectivityManager.NetworkCallback?) {
networkCallbackList.remove(callback)
}
/**
@description 注冊wifi網(wǎng)絡(luò)狀態(tài)回調(diào)
*/
private fun registerNetworkCallback() {
val builder = NetworkRequest.Builder().apply {
addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
addTransportType(NetworkCapabilities.TRANSPORT_VPN)
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
}
connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
}
}
主要功能:
1.持有系統(tǒng)ConnectivityManager娩怎,并注冊了相關(guān)網(wǎng)絡(luò)變化回調(diào)
2.isNetworkAvailable()搔课,可判斷當(dāng)前網(wǎng)絡(luò)是否可達(dá)
3.生命周期跟隨Application,只需注冊一次,可在任一地方進(jìn)行對網(wǎng)絡(luò)狀態(tài)的監(jiān)聽
調(diào)用方式如下:
// 獲取TyphurConnectManager對象
private val typhurConnectManager by lazy { TyphurConnectManager.getInstance() }
// 添加網(wǎng)絡(luò)回調(diào), isConnect代表是否連接爬泥, level代表信號強(qiáng)度
typhurConnectManager.addCallback(TyphurConnectCallback { isConnect, level ->
refreshByNetworkChange(isConnect, level)
}})
4.TyphurConnectCallback
/**
@author: Zed.Qiu
@date: 2022/8/15
@description: 默認(rèn)網(wǎng)絡(luò)回調(diào)
@param listener 參數(shù)1 是否聯(lián)網(wǎng) 參數(shù)2 wifi信號等級
*/
class TyphurConnectCallback(var listener: Function2<Boolean, WifiBean.LEVEL, Unit>) : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
onNetworkChange()
}
override fun onLosing(network: Network, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
onNetworkChange()
}
override fun onLost(network: Network) {
super.onLost(network)
onNetworkChange()
}
/**
@description 網(wǎng)絡(luò)發(fā)生變化
*/
private fun onNetworkChange() {
val networkAvailable =
TyphurConnectManager.isNetworkAvailable(TyphurCoreConst.app)
TyphurCoreConst.mainScope.launchOnUi {
listener.invoke(networkAvailable, WifiBean.LEVEL.LEVEL_UNKNOW)
}
}
/**
@description desc
@param methodParameters
@return methodReturnType
*/
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
TyphurCoreConst.mainScope.launchOnUi {
val networkAvailable =
TyphurConnectManager.isNetworkAvailable(TyphurCoreConst.app)
val level = WifiBean.calculateLevel(networkCapabilities.signalStrength)
listener.invoke(networkAvailable, level)
}
}
}
只是對系統(tǒng)的網(wǎng)絡(luò)回調(diào)做了一層簡單的封裝柬讨,暴露期望的回調(diào)監(jiān)聽提供給其他人
5.WifiBean
1.屬性介紹
屬性名稱 | 類型 | 作用 |
---|---|---|
wifiName | String | wifi名稱 |
levelValue | Int | 原始的信號值 |
ssid | String | wifi唯一標(biāo)識 |
bssid | String | 無線網(wǎng)址的mac地址 |
connectStatus | ConnectStatus | ConnectStatus.DISCONNECT默認(rèn)未不連接 |
2.方法介紹
方法名 | 作用 |
---|---|
fun isCapabilityEAP(capabilities: String?): Boolean | 是否為企業(yè)加密wifi eap |
fun getCapabilityTag(capabilities: String?): CapabilityTag | 獲取加密類型標(biāo)識 |
fun isCapabilityNone(capabilities: String?): Boolean | 是否為不加密wifi |
fun getCapabilityStr(capability: CapabilityTag): String | 獲取加密方式字符 |
fun calculateLevel(levelValue: Int): LEVEL | 計算信號等級 |
fun transform(scanResult: ScanResult): WifiBean | ScanResult:掃描結(jié)果類轉(zhuǎn)WifiBean |
3.具體實現(xiàn)
/**
* @author zed
*/
@Keep
data class WifiBean(
var wifiName: String? = null,
var levelValue: Int = 0,
var ssid: String? = null,
var bssid: String? = null,
var connectStatus: ConnectStatus = ConnectStatus.DISCONNECT,
/**
* 加密方式
*/
var capabilities: String? = null
) {
companion object {
const val CAPABILITY_NONE_STR = "NONE"
const val CAPABILITY_WEP_STR = "WEP"
const val CAPABILITY_PSK_STR = "PSK"
const val CAPABILITY_EAP_STR = "EAP"
/**
@description 獲取加密類型標(biāo)識
*/
fun getCapabilityTag(capabilities: String?): CapabilityTag {
capabilities?.run {
return when {
this.contains(CAPABILITY_WEP_STR) -> CapabilityTag.WEP
this.contains(CAPABILITY_PSK_STR) -> CapabilityTag.PSK
this.contains(CAPABILITY_EAP_STR) -> CapabilityTag.EAP
else -> CapabilityTag.NONE
}
}
return CapabilityTag.NONE
}
/**
@description 是否為企業(yè)加密wifi eap
*/
fun isCapabilityEAP(capabilities: String?): Boolean {
return CapabilityTag.EAP == getCapabilityTag(capabilities)
}
/**
@description 是否為不加密wifi
*/
fun isCapabilityNone(capabilities: String?): Boolean {
return CapabilityTag.NONE == getCapabilityTag(capabilities)
}
/**
@description 獲取加密方式字符
*/
fun getCapabilityStr(capability: CapabilityTag): String {
return when (capability) {
CapabilityTag.WEP -> CAPABILITY_WEP_STR
CapabilityTag.PSK -> CAPABILITY_PSK_STR
CapabilityTag.EAP -> CAPABILITY_EAP_STR
else -> CAPABILITY_NONE_STR
}
}
/**
@description 信號等級, level1 > level2 > level3
*/
const val LEVEL1_ABS_VALUE = 50
const val LEVEL2_ABS_VALUE = 75
const val LEVEL3_ABS_VALUE = 90
/**
@description 計算信號等級
*/
fun calculateLevel(levelValue: Int): LEVEL {
val absValue = abs(levelValue)
return when {
absValue < LEVEL1_ABS_VALUE -> {
LEVEL.LEVEL1
}
absValue < LEVEL2_ABS_VALUE -> {
LEVEL.LEVEL2
}
absValue < LEVEL3_ABS_VALUE -> {
LEVEL.LEVEL3
}
absValue >= LEVEL3_ABS_VALUE -> {
LEVEL.LEVEL4
}
else -> {
LEVEL.LEVEL_UNKNOW
}
}
}
fun transform(scanResult: ScanResult): WifiBean {
return WifiBean().apply {
wifiName = scanResult.SSID?.replace("\"", "")
ssid = scanResult.SSID
bssid = scanResult.BSSID
connectStatus = ConnectStatus.DISCONNECT
capabilities = scanResult.capabilities
levelValue = scanResult.level
}
}
}
/**
@description 加密標(biāo)識
*/
enum class CapabilityTag {
WEP, PSK, EAP, NONE
}
/**
@description Wifi信號等級
*/
enum class LEVEL {
LEVEL_UNKNOW, LEVEL1, LEVEL2, LEVEL3, LEVEL4
}
/**
@description 連接狀態(tài)
*/
enum class ConnectStatus {
DISCONNECT, CONNECTED, CONNECTING
}
}
6.WifiProxy
基本功能
/**
@author: Zed.Qiu
@date: 2022/8/2
@description: wifi代理類接口
*/
interface IWifiProxy {
fun register()
fun unRegister()
/**
@description 檢測wifi是否連接
*/
fun checkIsOpenWifi()
/**
@description 主動連接wifi
*/
fun connectWifi(wifiBean: WifiBean, password: String?)
}
具體實現(xiàn)
/**
@author: Zed.Qiu
@date: 2022/8/2
@description: Wifi狀態(tài)代理類
*/
class WifiProxy(
val activity: AppCompatActivity,
private val wifiBeanListListener: ((List<WifiBean>) -> Unit),
private val switchListener: Function1<Boolean, Unit>,
private val connectListener: (String, Boolean) -> Unit
) :IWifiProxy{
companion object {
const val TAG: String = "WifiProxy"
}
val typhurWifiManager by lazy {
TyphurWifiManager.getInstance(activity)
}
private val typhurConnectManager by lazy {
TyphurConnectManager.getInstance(activity)
}
private var receiver: WifiScanReceiver? = null
private val networkCallback = object :
ConnectivityManager.NetworkCallback() {
override fun onUnavailable() {
super.onUnavailable()
activity.lifecycleScope.launchOnUi {
wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
}
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
// 網(wǎng)絡(luò)可訪問時綁定
activity.lifecycleScope.launchOnUi {
wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
}
}
override fun onLost(network: Network) {
super.onLost(network)
activity.lifecycleScope.launchOnUi {
wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
}
}
}
override fun register() {
registerWifiNetworkCallback()
registerWifiScanReceiver()
}
override fun unRegister() {
// 解綁當(dāng)前網(wǎng)絡(luò)
activity.unregisterReceiver(receiver)
typhurConnectManager.removeCallback(networkCallback)
}
/**
@description 注冊wifi網(wǎng)絡(luò)狀態(tài)回調(diào)
*/
private fun registerWifiNetworkCallback() {
typhurConnectManager.register()
typhurConnectManager.addCallback(networkCallback)
}
/**
@description 注冊wifi掃描接收器
*/
private fun registerWifiScanReceiver() {
if (receiver == null) {
receiver = WifiScanReceiver(
typhurWifiManager.wifiManager, onWifiEnable(),
onScanWifiList(), connectListener
).apply {
register(activity)
}
}
}
/**
@description 掃描返回最終的WifiBean列表
*/
private fun onScanWifiList(): (Boolean) -> Unit = {
if (it) {
wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
} else {
wifiBeanListListener.invoke(listOf())
}
}
/**
@description wifi開關(guān)狀態(tài)變化
*/
private fun onWifiEnable(): (Boolean) -> Unit = {
switchListener.invoke(it)
if (it) {
typhurWifiManager.startScan()
} else {
wifiBeanListListener.invoke(listOf())
}
}
/**
* 檢查是否打開wifi
*/
override fun checkIsOpenWifi() {
val isWifiEnabled = typhurWifiManager.isWifiEnabled()
switchListener.invoke(isWifiEnabled)
if (isWifiEnabled) {
typhurWifiManager.startScan()
}
}
/**
@description 連接wifi
*/
override fun connectWifi(wifiBean: WifiBean, password: String?){
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
connectWifiSdk29(wifiBean, password)
}else{
typhurWifiManager.connectWifi(wifiBean, password)
}*/
typhurWifiManager.connectWifi(wifiBean, password)
}
/* *//**
@description 版本大于等于29的新連接方法
*//*
private fun connectWifiSdk29(wifiBean: WifiBean, password: String?) {
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.apply {
setSsid(wifiBean.ssid ?: "")
if (password != null) {
setWpa2Passphrase(password)
}
}.build()
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build()
connectivityManager.requestNetwork(networkRequest, networkCallback)
}
*//**
@description 企業(yè)wifi EAP連接袍啡,暫不能實現(xiàn)
*//*
fun connectWifiEAP(wifiBean: WifiBean, identity: String, password: String) {
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(wifiBean.ssid ?: "")
.setWpa2EnterpriseConfig(WifiEnterpriseConfig().apply {
eapMethod = WifiEnterpriseConfig.Eap.PEAP
this.identity = identity
this.password = password
phase2Method = WifiEnterpriseConfig.Phase2.NONE
})
.build()
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build()
connectivityManager.requestNetwork(networkRequest, networkCallback)
}
*/
}
本質(zhì)上踩官,是持有倆個Manager,分別是TyphurConnectManager(管理網(wǎng)絡(luò)狀態(tài)變化)和TyphurWifiManager(管理wifi列表信息變化)境输,另外對sdk版本29蔗牡,做了一些差異化的網(wǎng)絡(luò)連接處理,由于系統(tǒng)會強(qiáng)制彈窗wifi列表嗅剖,并且不能連接上部分類型wifi辩越,因此在這里注釋調(diào)了,但舊方法還是可以用的信粮,只是target sdk必須是28黔攒。android在sdk29開始就不允許應(yīng)用層面去修改wifi相關(guān)配置,在sdk31也做了大量方法舍棄强缘,在綜合設(shè)備兼容后督惰,還是考慮用舊版本實現(xiàn)。
compileSdk Integer.parseInt(project.findProperty("android.compile.sdk"))
defaultConfig {
applicationId "com.typhur.module.demo"
minSdk Integer.parseInt(project.findProperty("android.min.sdk"))
targetSdk Integer.parseInt(project.findProperty("android.target.sdk"))
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
//gradle.properties文件
#android sdk info
android.compile.sdk=32
android.min.sdk=28
android.target.sdk=28
7.WifiLifecycleHelper
/**
@author: Zed.Qiu
@date: 2022/8/2
@description: Wifi生命周期管理器
*/
class WifiLifecycleHelper(
private val activity: AppCompatActivity,
private val wifiProxy: WifiProxy
) : LifecycleEventObserver {
init {
activity.lifecycle.addObserver(this)
}
private lateinit var typhurWifiManager: TyphurWifiManager
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
when (event) {
Lifecycle.Event.ON_CREATE -> {
lifeCycleCreate()
}
Lifecycle.Event.ON_START -> {
}
Lifecycle.Event.ON_RESUME -> {
}
Lifecycle.Event.ON_PAUSE -> {
}
Lifecycle.Event.ON_STOP -> {
}
Lifecycle.Event.ON_DESTROY -> {
lifeCycleDestroy()
}
Lifecycle.Event.ON_ANY -> {
}
}
}
private fun lifeCycleCreate() {
typhurWifiManager = TyphurWifiManager.getInstance(activity)
wifiProxy.register()
wifiProxy.checkIsOpenWifi()
}
private fun lifeCycleDestroy() {
wifiProxy.unRegister()
}
fun isWifiEnabled(): Boolean {
return typhurWifiManager.isWifiEnabled()
}
fun switchWifi(wifiEnabled : Boolean){
typhurWifiManager.switchWifi(wifiEnabled)
}
fun forgetWifi(wifiBean: WifiBean){
typhurWifiManager.forgetWifi(wifiBean)
}
fun changeWifiListBySsid(
ssid: String?,
targetStatus: WifiBean.ConnectStatus,
list: List<WifiBean>
): MutableList<WifiBean> {
return typhurWifiManager.changeWifiListBySsid(ssid, targetStatus, list)
}
fun connectWifi(wifiBean: WifiBean, password: String?){
wifiProxy.connectWifi(wifiBean, password)
}
fun connectWifiEAP(wifiBean: WifiBean, identity: String, password: String){
// wifiProxy.connectWifiEAP(wifiBean, identity, password)
}
}
只是簡單的生命周期輔助類旅掂,對所有方法同意封裝了下
8.UI調(diào)用
wifiHelper = WifiLifecycleHelper(
this@SettingWifiActivity, WifiProxy(this,
{ list ->
Log.d("WifiProxy", "refreshList: ${list.toJsonString()}")
adapter.refreshList(list)
},
{
mBinding.btnTop.text = if (it) "關(guān)" else "開"
}, { ssid, isPwError ->
if (isPwError) {
Toast.makeText(this, "${connectWifiBean?.ssid} 密碼錯誤", Toast.LENGTH_SHORT)
.show()
}
})
)
// 主動連接wifi赏胚,兼容sdk版本
wifiHelper.connectWifi(wifiBean, password)
// 當(dāng)前是否有網(wǎng)絡(luò)連接
val networkAvailable = WifiProxy.isNetworkAvailable(context)
// 判斷當(dāng)前wifi是否開啟
val wifiEnabled = wifiHelper.isWifiEnabled()
// 切換開關(guān)
wifiHelper.switchWifi(!wifiEnabled)
// 忘記wifi
wifiHelper.forgetWifi(wifiBean)
// 修改wifi列表中某個wifi的連接狀態(tài)
wifiHelper.changeWifiListBySsid(
wifiBean.ssid, wifiBean.connectStatus, adapter.currentList)