feat 增加定位权限
This commit is contained in:
@@ -34,12 +34,14 @@ export default defineManifestConfig({
|
||||
'<uses-permission android:name="android.permission.VIBRATE"/>',
|
||||
'<uses-permission android:name="android.permission.READ_LOGS"/>',
|
||||
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||
'<uses-permission android:name="android.permission.CHANGE_WIFI_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_COARSE_LOCATION"/>',
|
||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||
'<uses-permission android:name="android.permission.CAMERA"/>',
|
||||
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
||||
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
|
||||
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
|
||||
'<uses-feature android:name="android.hardware.camera"/>',
|
||||
|
||||
5
src/auto-imports.d.ts
vendored
5
src/auto-imports.d.ts
vendored
@@ -329,6 +329,7 @@ declare global {
|
||||
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||
const whenever: typeof import('@vueuse/core')['whenever']
|
||||
const wifi: typeof import('./utils/wifi')['default']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
@@ -338,6 +339,9 @@ declare global {
|
||||
// @ts-ignore
|
||||
export type { SystemInfo } from './utils/device'
|
||||
import('./utils/device')
|
||||
// @ts-ignore
|
||||
export type { WifiInfo, ConnectWifiParams, WifiStateCallback, WifiScanCallback } from './utils/wifi'
|
||||
import('./utils/wifi')
|
||||
}
|
||||
|
||||
// for vue template auto import
|
||||
@@ -667,5 +671,6 @@ declare module 'vue' {
|
||||
readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
|
||||
readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
|
||||
readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
|
||||
readonly wifi: UnwrapRef<typeof import('./utils/wifi')['default']>
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import wifi from '@/utils/wifi'
|
||||
|
||||
const wifiEnabled = ref(true)
|
||||
|
||||
const networks = ref([
|
||||
@@ -14,6 +16,12 @@ function handleDisconnect(name: string) {
|
||||
function handleConnect(name: string) {
|
||||
console.log('连接', name)
|
||||
}
|
||||
|
||||
async function handleRefreshWifi() {
|
||||
console.log('点击了搜索WiFi')
|
||||
const result = await wifi.scanWifi()
|
||||
console.log('扫描到的WiFi:', result)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -42,7 +50,7 @@ function handleConnect(name: string) {
|
||||
<text class="card-title">
|
||||
可用网络
|
||||
</text>
|
||||
<wd-icon name="refresh" size="20px" class="refresh-icon" />
|
||||
<wd-icon name="refresh" size="20px" class="refresh-icon" @click="handleRefreshWifi" />
|
||||
</view>
|
||||
<view class="network-list">
|
||||
<view v-for="(network, index) in networks" :key="index" class="network-item">
|
||||
|
||||
493
src/utils/wifi.ts
Normal file
493
src/utils/wifi.ts
Normal file
@@ -0,0 +1,493 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
// ======================== 类型定义 ========================
|
||||
|
||||
/** 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<string, WifiStateCallback>(),
|
||||
scan: new Map<string, WifiScanCallback>(),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// ======================== WiFi 工具 ========================
|
||||
|
||||
const wifi = {
|
||||
/** 检查 WiFi 是否可用 */
|
||||
isWifiAvailable(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
const wm = getWifiManager()
|
||||
resolve(!!invoke(wm, 'isWifiEnabled'))
|
||||
}
|
||||
catch {
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取当前 WiFi 连接信息 */
|
||||
getConnectedWifi(): Promise<WifiInfo | null> {
|
||||
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 === '<unknown ssid>' || networkId === -1) {
|
||||
resolve(null)
|
||||
return
|
||||
}
|
||||
|
||||
resolve({
|
||||
SSID: ssid.replace(/"/g, ''),
|
||||
BSSID: bssid || '',
|
||||
signalStrength: rssi,
|
||||
connected: true,
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 扫描附近 WiFi 网络 */
|
||||
scanWifi(): Promise<WifiInfo[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const wm = getWifiManager()
|
||||
|
||||
// 检查 WiFi 是否开启
|
||||
if (!invoke(wm, 'isWifiEnabled')) {
|
||||
console.warn('WiFi 未开启,无法扫描')
|
||||
resolve([])
|
||||
return
|
||||
}
|
||||
|
||||
// 启动扫描
|
||||
const started = invoke(wm, 'startScan')
|
||||
console.log('WiFi 扫描启动:', started)
|
||||
|
||||
// 等待 3 秒确保扫描完成
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const results = invoke(wm, 'getScanResults')
|
||||
if (!results) {
|
||||
console.log('扫描结果为 null')
|
||||
resolve([])
|
||||
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) {
|
||||
console.error('获取扫描结果失败:', err)
|
||||
reject(err)
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
catch (err) {
|
||||
console.error('扫描启动失败:', err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 连接指定 WiFi */
|
||||
connectWifi(params: ConnectWifiParams): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<WifiInfo[]> {
|
||||
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<void> {
|
||||
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() {
|
||||
wifi.scanWifi().then(callback)
|
||||
},
|
||||
})
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user