feat 配置WiFi模块
This commit is contained in:
@@ -39,6 +39,7 @@ export default defineManifestConfig({
|
|||||||
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
||||||
'<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>',
|
'<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>',
|
||||||
'<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>',
|
'<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>',
|
||||||
|
'<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation"/>',
|
||||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||||
'<uses-permission android:name="android.permission.CAMERA"/>',
|
'<uses-permission android:name="android.permission.CAMERA"/>',
|
||||||
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
import wifi from '@/utils/wifi'
|
import wifi from '@/utils/wifi'
|
||||||
|
|
||||||
const wifiEnabled = ref(true)
|
const wifiEnabled = ref(true)
|
||||||
|
const scanning = ref(false)
|
||||||
|
|
||||||
const networks = ref([
|
interface NetworkItem {
|
||||||
{ name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: true },
|
name: string
|
||||||
{ name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: false },
|
signal: string
|
||||||
{ name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: false },
|
ip: string
|
||||||
])
|
connected: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const networks = ref<NetworkItem[]>([])
|
||||||
|
|
||||||
function handleDisconnect(name: string) {
|
function handleDisconnect(name: string) {
|
||||||
console.log('断开', name)
|
console.log('断开', name)
|
||||||
@@ -18,9 +22,35 @@ function handleConnect(name: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleRefreshWifi() {
|
async function handleRefreshWifi() {
|
||||||
|
if (scanning.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
scanning.value = true
|
||||||
console.log('点击了搜索WiFi')
|
console.log('点击了搜索WiFi')
|
||||||
const result = await wifi.scanWifi()
|
try {
|
||||||
console.log('扫描到的WiFi:', result)
|
const result = await wifi.scanWifi()
|
||||||
|
console.log('扫描到的WiFi:', result)
|
||||||
|
const connected = await wifi.getConnectedWifi().catch(() => null)
|
||||||
|
|
||||||
|
networks.value = result.map(item => ({
|
||||||
|
name: item.SSID,
|
||||||
|
signal: `信号强度:${wifi.getSignalDescription(item.signalStrength)}`,
|
||||||
|
ip: connected?.SSID === item.SSID ? '已连接' : (wifi.is5GHz(item.frequency || 0) ? '5GHz' : '2.4GHz'),
|
||||||
|
connected: connected?.SSID === item.SSID,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (!result.length) {
|
||||||
|
uni.showToast({ title: '未发现可用网络', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : '扫描失败'
|
||||||
|
console.error('WiFi 扫描失败:', err)
|
||||||
|
uni.showToast({ title: message, icon: 'none', duration: 3000 })
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
scanning.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -50,7 +80,16 @@ async function handleRefreshWifi() {
|
|||||||
<text class="card-title">
|
<text class="card-title">
|
||||||
可用网络
|
可用网络
|
||||||
</text>
|
</text>
|
||||||
<wd-icon name="refresh" size="20px" class="refresh-icon" @click="handleRefreshWifi" />
|
<wd-icon
|
||||||
|
name="refresh"
|
||||||
|
size="20px"
|
||||||
|
class="refresh-icon"
|
||||||
|
:class="{ spinning: scanning }"
|
||||||
|
@click="handleRefreshWifi"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view v-if="!networks.length && !scanning" class="empty-tip">
|
||||||
|
点击右上角刷新扫描附近 WiFi
|
||||||
</view>
|
</view>
|
||||||
<view class="network-list">
|
<view class="network-list">
|
||||||
<view v-for="(network, index) in networks" :key="index" class="network-item">
|
<view v-for="(network, index) in networks" :key="index" class="network-item">
|
||||||
@@ -119,6 +158,22 @@ async function handleRefreshWifi() {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.refresh-icon.spinning {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
padding: 12px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.wifi-toggle {
|
.wifi-toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@@ -55,6 +55,9 @@
|
|||||||
* - android.permission.ACCESS_WIFI_STATE
|
* - android.permission.ACCESS_WIFI_STATE
|
||||||
* - android.permission.CHANGE_WIFI_STATE
|
* - android.permission.CHANGE_WIFI_STATE
|
||||||
* - android.permission.ACCESS_NETWORK_STATE
|
* - android.permission.ACCESS_NETWORK_STATE
|
||||||
|
* - android.permission.ACCESS_FINE_LOCATION
|
||||||
|
* - android.permission.ACCESS_COARSE_LOCATION
|
||||||
|
* - Android 6+ 还需运行时授予定位权限,且系统「位置信息」开关需开启
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// ======================== 类型定义 ========================
|
// ======================== 类型定义 ========================
|
||||||
@@ -111,6 +114,90 @@ function importClass(name: string) {
|
|||||||
return (plus.android as any).importClass(name)
|
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<boolean> {
|
||||||
|
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 === '<unknown 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 工具 ========================
|
// ======================== WiFi 工具 ========================
|
||||||
|
|
||||||
const wifi = {
|
const wifi = {
|
||||||
@@ -157,74 +244,89 @@ const wifi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/** 扫描附近 WiFi 网络 */
|
/** 扫描附近 WiFi 网络 */
|
||||||
scanWifi(): Promise<WifiInfo[]> {
|
async scanWifi(): Promise<WifiInfo[]> {
|
||||||
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
let settled = false
|
||||||
const wm = getWifiManager()
|
let receiver: any = null
|
||||||
|
let timer: ReturnType<typeof setTimeout> | undefined
|
||||||
|
const activity = (plus.android as any).runtimeMainActivity()
|
||||||
|
|
||||||
// 检查 WiFi 是否开启
|
const finish = (networks: WifiInfo[], err?: Error) => {
|
||||||
const isEnabled = invoke(wm, 'isWifiEnabled')
|
if (settled)
|
||||||
console.log('WiFi 是否开启:', isEnabled)
|
|
||||||
|
|
||||||
if (!isEnabled) {
|
|
||||||
console.warn('WiFi 未开启,返回模拟数据')
|
|
||||||
// 模拟器或 WiFi 未开启时返回模拟数据
|
|
||||||
resolve([
|
|
||||||
{ SSID: 'MOCK_WIFI_1', BSSID: 'AA:BB:CC:DD:EE:01', signalStrength: -45, frequency: 5180, connected: false },
|
|
||||||
{ SSID: 'MOCK_WIFI_2', BSSID: 'AA:BB:CC:DD:EE:02', signalStrength: -65, frequency: 2437, connected: false },
|
|
||||||
{ SSID: 'TestNetwork', BSSID: 'AA:BB:CC:DD:EE:03', signalStrength: -72, frequency: 2412, connected: false },
|
|
||||||
])
|
|
||||||
return
|
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')
|
const started = invoke(wm, 'startScan')
|
||||||
console.log('WiFi 扫描启动:', started)
|
console.log('WiFi 扫描启动:', started)
|
||||||
|
|
||||||
// 等待 3 秒确保扫描完成
|
// startScan 失败时仍尝试读取缓存结果(限流时常见)
|
||||||
setTimeout(() => {
|
if (!started) {
|
||||||
try {
|
const cached = parseScanResults(wm)
|
||||||
const results = invoke(wm, 'getScanResults')
|
console.log('startScan 失败,缓存网络数量:', cached.length)
|
||||||
if (!results) {
|
if (cached.length > 0) {
|
||||||
console.log('扫描结果为 null')
|
finish(cached)
|
||||||
resolve([])
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = invoke(results, 'size')
|
|
||||||
console.log('扫描到网络数量:', size)
|
|
||||||
|
|
||||||
const networks: WifiInfo[] = []
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
const r = invoke(results, 'get', i)
|
|
||||||
const ssid = (invoke(r, 'SSID') || '').replace(/"/g, '')
|
|
||||||
if (!ssid || ssid === '<unknown ssid>')
|
|
||||||
continue
|
|
||||||
|
|
||||||
networks.push({
|
|
||||||
SSID: ssid,
|
|
||||||
BSSID: invoke(r, 'BSSID') || '',
|
|
||||||
signalStrength: invoke(r, 'level') || 0,
|
|
||||||
securityType: invoke(r, 'capabilities') || '',
|
|
||||||
frequency: invoke(r, 'frequency') || 0,
|
|
||||||
connected: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('解析后网络数量:', networks.length)
|
|
||||||
resolve(networks)
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
finish([], new Error('WiFi 扫描被拒绝(可能被系统限流,请稍后再试)'))
|
||||||
console.error('获取扫描结果失败:', err)
|
return
|
||||||
reject(err)
|
}
|
||||||
}
|
|
||||||
}, 3000)
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error('扫描启动失败:', err)
|
finish([], err instanceof Error ? err : new Error(String(err)))
|
||||||
reject(err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
const networks = parseScanResults(wm)
|
||||||
|
console.log('扫描超时,当前网络数量:', networks.length)
|
||||||
|
if (networks.length > 0)
|
||||||
|
finish(networks)
|
||||||
|
else
|
||||||
|
finish([], new Error('扫描超时,未发现可用网络'))
|
||||||
|
}, 8000)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -453,7 +555,8 @@ const wifi = {
|
|||||||
|
|
||||||
const receiver = new Receiver({
|
const receiver = new Receiver({
|
||||||
onReceive() {
|
onReceive() {
|
||||||
wifi.scanWifi().then(callback)
|
const wm = getWifiManager()
|
||||||
|
callback(parseScanResults(wm))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user