/** * WiFi 工具类 (Android App 专用) * 用于 uni-app Android 平台 WiFi 操作 * * ======================== 使用说明 ======================== * * import wifi from '@/utils/wifi' * * // 检查 WiFi 是否可用 * const available = await wifi.isWifiAvailable() * * // 获取当前连接信息 * const info = await wifi.getConnectedWifi() * * // 扫描附近 WiFi * const networks = await wifi.scanWifi() * * // 连接指定 WiFi * await wifi.connectWifi({ SSID: 'MyWiFi', password: '12345678' }) * * // 断开当前连接 * await wifi.disconnectWifi() * * // 连接已保存的 WiFi(免密) * await wifi.connectSavedWifi({ SSID: 'MyWiFi' }) * * // 获取已保存的 WiFi 列表 * const savedList = await wifi.getSavedWifiList() * * // 移除已保存的 WiFi * await wifi.removeSavedWifi({ SSID: 'MyWiFi' }) * * // 监听 WiFi 状态变化 * const unwatch = wifi.onWifiStateChanged((state) => { * console.log(state.isConnected ? '已连接' : '未连接') * }) * unwatch() // 取消监听 * * // 监听扫描结果 * const unwatch = wifi.onWifiScan((networks) => { * console.log('扫描到', networks.length, '个网络') * }) * unwatch() // 取消监听 * * // 获取信号强度描述 * wifi.getSignalDescription(-55) // "强" * * // 判断频段 * wifi.is5GHz(5180) // true * * ======================== 前置条件 ======================== * * - 仅支持 Android App 环境 * - 需要在 manifest.json 配置权限: * - android.permission.ACCESS_WIFI_STATE * - android.permission.CHANGE_WIFI_STATE * - android.permission.ACCESS_NETWORK_STATE * - android.permission.ACCESS_FINE_LOCATION * - android.permission.ACCESS_COARSE_LOCATION * - Android 6+ 还需运行时授予定位权限,且系统「位置信息」开关需开启 */ // ======================== 类型定义 ======================== /** WiFi 网络信息 */ export interface WifiInfo { SSID: string BSSID: string signalStrength: number securityType?: string frequency?: number connected?: boolean } /** 连接 WiFi 参数 */ export interface ConnectWifiParams { SSID: string password?: string timeout?: number } /** WiFi 状态变化回调 */ export type WifiStateCallback = (state: { isConnected: boolean wifiInfo: WifiInfo | null }) => void /** WiFi 扫描结果回调 */ export type WifiScanCallback = (networks: WifiInfo[]) => void // ======================== 内部工具 ======================== const listeners = { stateChange: new Map(), scan: new Map(), } let receiverId = 0 /** 获取 WiFi Manager 实例 */ function getWifiManager() { const ctx = (plus.android as any).importClass('android.content.Context') const activity = (plus.android as any).runtimeMainActivity() return activity.getSystemService(ctx.WIFI_SERVICE) } /** 调用 Android 方法 */ function invoke(obj: any, method: string, ...args: any[]) { return (plus.android as any).invoke(obj, method, ...args) } /** 导入 Android 类 */ function importClass(name: string) { return (plus.android as any).importClass(name) } /** 读取 Java 对象字段(ScanResult.SSID 等是字段不是方法) */ function getAttr(obj: any, name: string) { try { return (plus.android as any).getAttribute(obj, name) } catch { return invoke(obj, name) } } /** 申请定位权限(Android 扫描 WiFi 必需) */ function requestLocationPermission(): Promise { return new Promise((resolve) => { try { ;(plus.android as any).requestPermissions( [ 'android.permission.ACCESS_FINE_LOCATION', 'android.permission.ACCESS_COARSE_LOCATION', ], (result: { granted?: string[], deniedAlways?: string[], deniedPresent?: string[] }) => { const granted = result?.granted || [] const ok = granted.includes('android.permission.ACCESS_FINE_LOCATION') || granted.includes('android.permission.ACCESS_COARSE_LOCATION') console.log('定位权限申请结果:', result) resolve(ok) }, (err: unknown) => { console.error('定位权限申请失败:', err) resolve(false) }, ) } catch (err) { console.error('requestPermissions 不可用:', err) resolve(false) } }) } /** 系统定位开关是否开启 */ function isLocationEnabled(): boolean { try { const LocationManager = importClass('android.location.LocationManager') const Context = importClass('android.content.Context') const activity = (plus.android as any).runtimeMainActivity() const lm = activity.getSystemService(Context.LOCATION_SERVICE) return !!( invoke(lm, 'isProviderEnabled', LocationManager.GPS_PROVIDER) || invoke(lm, 'isProviderEnabled', LocationManager.NETWORK_PROVIDER) ) } catch { return true } } /** 解析 getScanResults 列表 */ function parseScanResults(wm: any): WifiInfo[] { const results = invoke(wm, 'getScanResults') if (!results) return [] const size = invoke(results, 'size') || 0 const networks: WifiInfo[] = [] for (let i = 0; i < size; i++) { const r = invoke(results, 'get', i) const ssid = String(getAttr(r, 'SSID') || '').replace(/"/g, '') if (!ssid || ssid === '') continue networks.push({ SSID: ssid, BSSID: String(getAttr(r, 'BSSID') || ''), signalStrength: Number(getAttr(r, 'level') || 0), securityType: String(getAttr(r, 'capabilities') || ''), frequency: Number(getAttr(r, 'frequency') || 0), connected: false, }) } return networks } // ======================== WiFi 工具 ======================== const wifi = { /** 检查 WiFi 是否可用 */ isWifiAvailable(): Promise { return new Promise((resolve) => { try { const wm = getWifiManager() resolve(!!invoke(wm, 'isWifiEnabled')) } catch { resolve(false) } }) }, /** 获取当前 WiFi 连接信息 */ getConnectedWifi(): Promise { return new Promise((resolve, reject) => { try { const wm = getWifiManager() const info = invoke(wm, 'getConnectionInfo') const ssid = invoke(info, 'getSSID') const bssid = invoke(info, 'getBSSID') const rssi = invoke(info, 'getRssi') const networkId = invoke(info, 'getNetworkId') if (!ssid || ssid === '' || networkId === -1) { resolve(null) return } resolve({ SSID: ssid.replace(/"/g, ''), BSSID: bssid || '', signalStrength: rssi, connected: true, }) } catch (err) { reject(err) } }) }, /** 扫描附近 WiFi 网络 */ async scanWifi(): Promise { const wm = getWifiManager() const isEnabled = invoke(wm, 'isWifiEnabled') console.log('WiFi 是否开启:', isEnabled) if (!isEnabled) throw new Error('请先开启 WiFi') const hasPermission = await requestLocationPermission() if (!hasPermission) throw new Error('需要定位权限才能扫描 WiFi,请在系统设置中允许') if (!isLocationEnabled()) throw new Error('请先打开系统「位置信息/定位」开关,否则无法扫描 WiFi') return new Promise((resolve, reject) => { let settled = false let receiver: any = null let timer: ReturnType | undefined const activity = (plus.android as any).runtimeMainActivity() const finish = (networks: WifiInfo[], err?: Error) => { if (settled) return settled = true if (timer) clearTimeout(timer) try { if (receiver) activity.unregisterReceiver(receiver) } catch {} if (err) reject(err) else resolve(networks) } try { const Filter = importClass('android.content.IntentFilter') const Receiver = importClass('android.content.BroadcastReceiver') const filter = new Filter() filter.addAction('android.net.wifi.SCAN_RESULTS') receiver = new Receiver({ onReceive(_ctx: any, intent: any) { const updated = invoke(intent, 'getBooleanExtra', 'resultsUpdated', false) console.log('收到 SCAN_RESULTS, resultsUpdated:', updated) const networks = parseScanResults(wm) console.log('解析后网络数量:', networks.length) finish(networks) }, }) activity.registerReceiver(receiver, filter) const started = invoke(wm, 'startScan') console.log('WiFi 扫描启动:', started) // startScan 失败时仍尝试读取缓存结果(限流时常见) if (!started) { const cached = parseScanResults(wm) console.log('startScan 失败,缓存网络数量:', cached.length) if (cached.length > 0) { finish(cached) return } finish([], new Error('WiFi 扫描被拒绝(可能被系统限流,请稍后再试)')) return } } catch (err) { finish([], err instanceof Error ? err : new Error(String(err))) return } timer = setTimeout(() => { const networks = parseScanResults(wm) console.log('扫描超时,当前网络数量:', networks.length) if (networks.length > 0) finish(networks) else finish([], new Error('扫描超时,未发现可用网络')) }, 8000) }) }, /** 连接指定 WiFi */ connectWifi(params: ConnectWifiParams): Promise { return new Promise((resolve, reject) => { const { SSID, password, timeout = 10000 } = params if (!SSID) { reject(new Error('SSID 不能为空')) return } try { const wm = getWifiManager() const Config = importClass('android.net.wifi.WifiConfiguration') const config = new Config() invoke(config, 'SSID', `"${SSID}"`) config.networkId = -1 const keyMgmt = invoke(config, 'allowedKeyManagement') if (password) { config.preSharedKey = `"${password}"` invoke(keyMgmt, 'set', 1) // WPA_PSK } else { invoke(keyMgmt, 'set', 0) // NONE } const networkId = invoke(wm, 'addNetwork', config) if (networkId === -1) { const networks = invoke(wm, 'getConfiguredNetworks') const size = invoke(networks, 'size') for (let i = 0; i < size; i++) { const existing = invoke(networks, 'get', i) if (invoke(existing, 'SSID') === `"${SSID}"`) { invoke(wm, 'updateNetwork', existing, config) invoke(wm, 'disconnect') invoke(wm, 'enableNetwork', invoke(existing, 'networkId'), true) invoke(wm, 'reconnect') resolve() return } } reject(new Error('无法连接到网络')) return } invoke(wm, 'disconnect') invoke(wm, 'enableNetwork', networkId, true) invoke(wm, 'reconnect') let elapsed = 0 const timer = setInterval(() => { const info = invoke(wm, 'getConnectionInfo') if (invoke(info, 'getSupplicantState') === 3) { clearInterval(timer) resolve() return } elapsed += 500 if (elapsed >= timeout) { clearInterval(timer) reject(new Error('连接超时')) } }, 500) } catch (err) { reject(err) } }) }, /** 断开当前 WiFi 连接 */ disconnectWifi(): Promise { return new Promise((resolve, reject) => { try { const wm = getWifiManager() invoke(wm, 'disableNetwork', -1) const result = invoke(wm, 'disconnect') result ? resolve() : reject(new Error('断开连接失败')) } catch (err) { reject(err) } }) }, /** 连接已保存的 WiFi(免密) */ connectSavedWifi(params: { SSID: string }): Promise { return new Promise((resolve, reject) => { const { SSID } = params try { const wm = getWifiManager() const networks = invoke(wm, 'getConfiguredNetworks') const size = invoke(networks, 'size') for (let i = 0; i < size; i++) { const config = invoke(networks, 'get', i) if (invoke(config, 'SSID') === `"${SSID}"`) { invoke(wm, 'disconnect') invoke(wm, 'enableNetwork', invoke(config, 'networkId'), true) invoke(wm, 'reconnect') resolve() return } } reject(new Error(`未找到已保存的网络: ${SSID}`)) } catch (err) { reject(err) } }) }, /** 获取已保存的 WiFi 列表 */ getSavedWifiList(): Promise { return new Promise((resolve, reject) => { try { const wm = getWifiManager() const networks = invoke(wm, 'getConfiguredNetworks') const size = invoke(networks, 'size') const result: WifiInfo[] = [] for (let i = 0; i < size; i++) { const config = invoke(networks, 'get', i) const ssid = invoke(config, 'SSID').replace(/"/g, '') if (!ssid) continue result.push({ SSID: ssid, BSSID: invoke(config, 'BSSID') || '', signalStrength: 0, connected: false, }) } resolve(result) } catch (err) { reject(err) } }) }, /** 移除已保存的 WiFi 网络 */ removeSavedWifi(params: { SSID: string }): Promise { return new Promise((resolve, reject) => { const { SSID } = params try { const wm = getWifiManager() const networks = invoke(wm, 'getConfiguredNetworks') const size = invoke(networks, 'size') for (let i = 0; i < size; i++) { const config = invoke(networks, 'get', i) if (invoke(config, 'SSID') === `"${SSID}"`) { invoke(wm, 'removeNetwork', invoke(config, 'networkId')) invoke(wm, 'saveConfiguration') resolve() return } } reject(new Error(`未找到已保存的网络: ${SSID}`)) } catch (err) { reject(err) } }) }, /** 监听 WiFi 状态变化 */ onWifiStateChanged(callback: WifiStateCallback): () => void { const id = `state_${++receiverId}` listeners.stateChange.set(id, callback) try { const activity = (plus.android as any).runtimeMainActivity() const Filter = importClass('android.content.IntentFilter') const Receiver = importClass('android.content.BroadcastReceiver') const filter = new Filter() filter.addAction('android.net.wifi.STATE_CHANGE') const receiver = new Receiver({ onReceive(_ctx: any, intent: any) { const action = invoke(intent, 'getAction') if (action !== 'android.net.wifi.STATE_CHANGE') return const networkInfo = invoke(intent, 'getParcelableExtra', 'networkInfo') const state = invoke(networkInfo, 'getState') if (state === 2) { wifi.getConnectedWifi().then(info => callback({ isConnected: true, wifiInfo: info })) } else { callback({ isConnected: false, wifiInfo: null }) } }, }) activity.registerReceiver(receiver, filter) } catch {} return () => listeners.stateChange.delete(id) }, /** 监听 WiFi 扫描结果 */ onWifiScan(callback: WifiScanCallback): () => void { const id = `scan_${++receiverId}` listeners.scan.set(id, callback) try { const activity = (plus.android as any).runtimeMainActivity() const Filter = importClass('android.content.IntentFilter') const Receiver = importClass('android.content.BroadcastReceiver') const filter = new Filter() filter.addAction('android.net.wifi.SCAN_RESULTS') const receiver = new Receiver({ onReceive() { const wm = getWifiManager() callback(parseScanResults(wm)) }, }) activity.registerReceiver(receiver, filter) } catch {} return () => listeners.scan.delete(id) }, /** 获取信号强度等级 (0-4) */ getSignalLevel(rssi: number): number { if (rssi >= -50) return 4 if (rssi >= -60) return 3 if (rssi >= -70) return 2 if (rssi >= -80) return 1 return 0 }, /** 获取信号强度描述 */ getSignalDescription(rssi: number): string { const level = this.getSignalLevel(rssi) return ['无信号', '弱', '一般', '强', '非常强'][level] || '未知' }, /** 判断是否为 5GHz 频段 */ is5GHz(frequency: number): boolean { return frequency >= 5000 && frequency <= 5900 }, /** 获取频段描述 */ getFrequencyDescription(frequency: number): string { if (this.is5GHz(frequency)) return '5GHz' if (frequency >= 2400 && frequency <= 2500) return '2.4GHz' return '未知' }, } export default wifi