feat 增加定位权限

This commit is contained in:
Ironsp
2026-09-12 16:28:37 +08:00
parent 8d97b532ea
commit 6d3e975f49
4 changed files with 511 additions and 3 deletions

View File

@@ -34,12 +34,14 @@ export default defineManifestConfig({
'<uses-permission android:name="android.permission.VIBRATE"/>', '<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>', '<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>', '<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_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.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>', '<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>', '<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.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>', '<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>', '<uses-feature android:name="android.hardware.camera"/>',

View File

@@ -329,6 +329,7 @@ declare global {
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever'] const whenever: typeof import('@vueuse/core')['whenever']
const wifi: typeof import('./utils/wifi')['default']
} }
// for type re-export // for type re-export
declare global { declare global {
@@ -338,6 +339,9 @@ declare global {
// @ts-ignore // @ts-ignore
export type { SystemInfo } from './utils/device' export type { SystemInfo } from './utils/device'
import('./utils/device') import('./utils/device')
// @ts-ignore
export type { WifiInfo, ConnectWifiParams, WifiStateCallback, WifiScanCallback } from './utils/wifi'
import('./utils/wifi')
} }
// for vue template auto import // for vue template auto import
@@ -667,5 +671,6 @@ declare module 'vue' {
readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']> readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']> readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']> readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
readonly wifi: UnwrapRef<typeof import('./utils/wifi')['default']>
} }
} }

View File

@@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import wifi from '@/utils/wifi'
const wifiEnabled = ref(true) const wifiEnabled = ref(true)
const networks = ref([ const networks = ref([
@@ -14,6 +16,12 @@ function handleDisconnect(name: string) {
function handleConnect(name: string) { function handleConnect(name: string) {
console.log('连接', name) console.log('连接', name)
} }
async function handleRefreshWifi() {
console.log('点击了搜索WiFi')
const result = await wifi.scanWifi()
console.log('扫描到的WiFi:', result)
}
</script> </script>
<template> <template>
@@ -42,7 +50,7 @@ function handleConnect(name: string) {
<text class="card-title"> <text class="card-title">
可用网络 可用网络
</text> </text>
<wd-icon name="refresh" size="20px" class="refresh-icon" /> <wd-icon name="refresh" size="20px" class="refresh-icon" @click="handleRefreshWifi" />
</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">

493
src/utils/wifi.ts Normal file
View 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