提交 edbd7400 作者: 方治民

feat: 添加 usePermissions hook 组件用来实现通用的 Android 权限监听

上级 a0b9d9b2
...@@ -161,5 +161,6 @@ ...@@ -161,5 +161,6 @@
"engines": { "engines": {
"node": ">=18", "node": ">=18",
"pnpm": ">=9.0.0" "pnpm": ">=9.0.0"
} },
"appName": "基础工程"
} }
<script setup lang="ts"> <script setup lang="ts">
// import * as Push from '@/utils/push' import { onExit } from '@dcloudio/uni-app'
import { isDevMode } from '@/utils/env' import { isDevMode } from '@/utils/env'
import { usePermissions } from '@/hooks/app/usePermissions'
// import * as Push from '@/utils/push'
// 监听权限获取
const { listen, stop } = usePermissions()
onLaunch(() => { onLaunch(() => {
console.log('App Launch') console.log('App Launch')
// #ifdef APP-PLUS
// 监听权限获取
listen()
// #endif
// 清除消息角标 // 清除消息角标
// Push.setBadge(0) // Push.setBadge(0)
...@@ -21,6 +32,11 @@ ...@@ -21,6 +32,11 @@
// #endif // #endif
}) })
onExit(() => {
// 停用监听权限
stop()
})
onShow(() => { onShow(() => {
console.log('App Show') console.log('App Show')
}) })
......
import { debounce } from 'lodash-es'
import { Storage } from '@/utils/storage'
export interface Permissions {
listen: () => void
stop: () => void
gotoAppPermissionSetting: () => void
}
export interface RequestPermissionListener {
onRequest: (e: any) => void
onConfirm: (e: any) => void
onComplete: (e: any) => void
stop: () => void
}
/**
* 权限申请说明(注:仅适用于 Android)
* @link https://uniapp.dcloud.net.cn/tutorial/app-nativeresource-android.html#permissions
*/
export function getRequestPermissionTipMessage(id: string) {
switch (id) {
case 'android.permission.INTERNET':
return { name: '网络权限', message: '允许访问网络,便于使用应用相关功能。' }
case 'android.permission.CAMERA':
return {
name: '相机权限',
message:
'允许访问摄像头,便于拍照上传照片/图片用于意见反馈、保存相册、缓存数据提升访问速度等场景中拍摄照片。',
}
case 'android.permission.READ_MEDIA_IMAGES':
case 'android.permission.READ_EXTERNAL_STORAGE':
case 'android.permission.WRITE_EXTERNAL_STORAGE':
return {
name: '存储权限',
message:
'允许访问存储,便于上传照片/图片用于意见反馈、保存相册、缓存数据提升访问速度等场景中读取和写入相册和文件内容。',
}
case 'android.permission.READ_PHONE_STATE':
case 'android.permission.ACCESS_NETWORK_STATE':
case 'android.permission.ACCESS_WIFI_STATE':
return {
name: '设备状态权限',
message: '允许访问设备状态,便于收集应用异常信息。',
}
case 'android.permission.ACCESS_COARSE_LOCATION':
case 'android.permission.ACCESS_FINE_LOCATION':
case 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS':
case 'android.permission.ACCESS_MOCK_LOCATION':
return {
name: '位置权限',
message: '允许访问位置信息,便于确认上传随手拍问题时的所在位置。',
}
case 'android.permission.INSTALL_PACKAGES':
case 'android.permission.REQUEST_INSTALL_PACKAGES':
return {
name: '安装应用权限',
message: '允许访问安装应用,便于应用更新包的安装。',
}
}
}
export function usePermissions(): Permissions {
const system = uni.getSystemInfoSync()
const listener = ref<RequestPermissionListener>()
const requestPermissionId = ref<string>()
const show = ref(false)
const debounceShow = debounce((value) => {
show.value = value
}, 200)
watch(
() => show.value,
(value) => {
const id = requestPermissionId.value
if (value) {
const statusBarHeight = system.statusBarHeight
const navigationBarHeight = system.platform === 'android' ? 48 : 44
const totalHeight = statusBarHeight + navigationBarHeight
let view = new plus.nativeObj.View(id, {
top: '0px',
left: '0px',
width: '100%',
backgroundColor: 'rgba(0,0,0,0.3)',
})
view.drawRect(
{
color: '#fff',
radius: '5px',
},
{
top: `${totalHeight}px`,
left: '5%',
width: '90%',
height: '100px',
},
)
view.drawText(
'权限使用说明',
{
top: `${totalHeight + 5}px`,
left: '8%',
height: '30px',
},
{
align: 'left',
color: '#000',
size: '20px',
},
)
view.drawText(
getRequestPermissionTipMessage(id)?.message,
{
top: `${totalHeight + 35}px`,
height: '60px',
left: '8%',
width: '84%',
},
{
whiteSpace: 'normal',
size: '14px',
align: 'left',
color: '#656563',
},
)
view.show()
view = null
} else {
// @ts-expect-error
let view = plus.nativeObj.View.getViewById(id)
view && view.close()
view = null
requestPermissionId.value = null
}
},
)
function listen() {
// #ifdef APP-PLUS
// FIXED: 由于 iOS 下无问题,此处仅处理安卓平台的权限申请问题
if (system.platform === 'android') {
// @ts-expect-error
listener.value = uni.createRequestPermissionListener()
listener.value.onConfirm((e: string[]) => {
console.log('onConfirm', e)
// 创建弹窗视图
requestPermissionId.value = e[0]
debounceShow(true)
})
listener.value.onComplete((e: string[]) => {
console.log('onComplete', e)
// 关闭弹窗视图
const id = e[0]
// 判断是否触发了对应的权限显示
if (requestPermissionId.value !== id && Storage.get(id) !== 'granted') {
if (Storage.get(id) === 'denied') {
handlerPermissionModal(id)
} else {
plus.android.requestPermissions(e, (v) => {
console.log('requestPermissions', e, v)
if (v.granted.includes(id)) {
Storage.set(id, 'granted')
}
if (v.deniedAlways.includes(id)) {
Storage.set(id, 'denied')
handlerPermissionModal(id)
}
})
}
} else {
requestPermissionId.value = id
debounceShow(false)
}
})
console.log('[usePermissions] start listen...')
}
// #endif
}
function stop() {
// #ifdef APP-PLUS
listener.value?.stop()
// 清除权限标记,防止用户主动设置了权限允许重新打开仍然无法使用的问题
Storage.keys().forEach((key) => {
if (key.startsWith('android.permission.')) {
Storage.remove(key)
}
})
console.log('[usePermissions] stop listen')
// #endif
}
// 权限设置弹窗
const settingConfirmVisible = ref(false)
function handlerPermissionModal(id: string) {
if (settingConfirmVisible.value) {
return
}
settingConfirmVisible.value = true
Message.confirm(`缺少${getRequestPermissionTipMessage(id).name},立即去设置开启?`, '温馨提示', '去设置').then(
(confirm) => {
settingConfirmVisible.value = false
if (confirm) {
try {
Storage.remove(id)
gotoAppPermissionSetting()
} catch (e) {
console.error('[Permissions] gotoAppPermissionSetting fail!', e)
}
}
},
)
}
// 跳转到 APP 权限设置
function gotoAppPermissionSetting() {
// #ifdef APP-PLUS
if (system.platform === 'ios') {
const UIApplication = plus.ios.importClass('UIApplication')
const application = UIApplication.sharedApplication()
const NSURL = plus.ios.importClass('NSURL')
const setting = NSURL.URLWithString('app-settings:')
application.openURL(setting)
plus.ios.deleteObject(setting)
plus.ios.deleteObject(NSURL)
plus.ios.deleteObject(application)
} else {
// console.log(plus.device.vendor);
const Intent = plus.android.importClass('android.content.Intent')
const Settings = plus.android.importClass('android.provider.Settings')
const Uri = plus.android.importClass('android.net.Uri')
const mainActivity = plus.android.runtimeMainActivity()
// @ts-expect-error
const intent = new Intent()
// @ts-expect-error
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
// @ts-expect-error
const uri = Uri.fromParts('package', mainActivity.getPackageName(), null)
intent.setData(uri)
// @ts-expect-error
mainActivity.startActivity(intent)
}
// #endif
}
return {
listen,
stop,
gotoAppPermissionSetting,
}
}
{ {
"name" : "Beta App", "name": "Beta App",
"appid" : "__UNI__2E9441A", "appid": "__UNI__2E9441A",
"description" : "APP 基础工程", "description": "APP 基础工程",
"versionName" : "1.0.1", "versionName": "1.0.1",
"versionCode" : 10001, "versionCode": 10001,
"transformPx" : false, "transformPx": false,
"locale" : "zh-Hans", "locale": "zh-Hans",
"vueVersion" : "3", "vueVersion": "3",
/* 5+App特有相关 */// 配置文件详细说明 /* 5+App特有相关 */// 配置文件详细说明
// https://uniapp.dcloud.net.cn/collocation/manifest-app.html#full-manifest // https://uniapp.dcloud.net.cn/collocation/manifest-app.html#full-manifest
"app-plus" : { "app-plus": {
"usingComponents" : true, "usingComponents": true,
"nvueStyleCompiler" : "uni-app", "nvueCompiler": "uni-app",
"compilerVersion" : 3, "nvueStyleCompiler": "uni-app",
"splashscreen" : { "nvueLaunchMode": "fast",
"alwaysShowBeforeRender" : false, "compilerVersion": 3,
"autoclose" : false, "splashscreen": {
"waiting" : true "alwaysShowBeforeRender": false,
}, "autoclose": false,
"screenOrientation" : [ "portrait-primary", "landscape-primary" ], "waiting": true
"compatible" : { },
"screenOrientation": ["portrait-primary", "landscape-primary"],
"compatible": {
// 忽略版本提示 // 忽略版本提示
"ignoreVersion" : true "ignoreVersion": true
}, },
/* 模块配置 */ /* 模块配置 */
"modules" : { "modules": {
"Geolocation" : {}, "Geolocation": {},
"Camera" : {} "Camera": {}
}, },
/* 应用发布信息 */ /* 应用发布信息 */
"distribute" : { "distribute": {
/* android打包配置 */ /* android打包配置 */
"android" : { "android": {
"permissions" : [ "permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
...@@ -49,94 +51,97 @@ ...@@ -49,94 +51,97 @@
"<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
], ],
"permissionPhoneState" : { "permissionPhoneState": {
// app首次启动关闭权限申请 // app首次启动关闭权限申请
"request" : "none" "request": "none"
} },
"minSdkVersion": 23,
"targetSdkVersion": 30,
"abiFilters": ["armeabi-v7a", "arm64-v8a"]
}, },
/* ios打包配置 */ /* ios打包配置 */
"ios" : { "ios": {
"dSYMs" : false, "dSYMs": false,
"privacyDescription" : { "privacyDescription": {
"NSUserTrackingUsageDescription" : "请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验", "NSUserTrackingUsageDescription": "请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验",
"NSLocationAlwaysAndWhenInUseUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationAlwaysAndWhenInUseUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSLocationAlwaysUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationAlwaysUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSLocationWhenInUseUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationWhenInUseUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSPhotoLibraryUsageDescription" : "请允许访问您的照片图库,以便能够上传应用异常问题的截图" "NSPhotoLibraryUsageDescription": "请允许访问您的照片图库,以便能够上传应用异常问题的截图"
} }
}, },
/* SDK配置 */ /* SDK配置 */
"sdkConfigs" : { "sdkConfigs": {
"ad" : {}, "ad": {},
"statics" : {}, "statics": {},
"geolocation" : { "geolocation": {
"system" : { "system": {
"__platform__" : [ "ios", "android" ] "__platform__": ["ios", "android"]
} }
} }
}, },
"icons" : { "icons": {
"android" : { "android": {
"hdpi" : "unpackage/res/icons/72x72.png", "hdpi": "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png", "xhdpi": "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png", "xxhdpi": "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png" "xxxhdpi": "unpackage/res/icons/192x192.png"
}, },
"ios" : { "ios": {
"appstore" : "unpackage/res/icons/1024x1024.png", "appstore": "unpackage/res/icons/1024x1024.png",
"ipad" : { "ipad": {
"app" : "unpackage/res/icons/76x76.png", "app": "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png", "app@2x": "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png", "notification": "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png", "proapp@2x": "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png", "settings": "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png", "spotlight": "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png" "spotlight@2x": "unpackage/res/icons/80x80.png"
}, },
"iphone" : { "iphone": {
"app@2x" : "unpackage/res/icons/120x120.png", "app@2x": "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png", "app@3x": "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png", "notification@3x": "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png", "settings@3x": "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png", "spotlight@2x": "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png" "spotlight@3x": "unpackage/res/icons/120x120.png"
} }
} }
}, },
"splashscreen" : { "splashscreen": {
"useOriginalMsgbox" : true "useOriginalMsgbox": true
} }
}, },
"uniStatistics" : { "uniStatistics": {
"enable" : true "enable": true
} }
}, },
"uniStatistics" : { "uniStatistics": {
"enable" : true, "enable": true,
"version" : "2", "version": "2",
"debug" : true "debug": true
}, },
/* 快应用特有相关 */ /* 快应用特有相关 */
"quickapp" : {}, "quickapp": {},
/* 小程序特有相关 */ /* 小程序特有相关 */
"mp-weixin" : { "mp-weixin": {
"appid" : "", "appid": "",
"setting" : { "setting": {
"urlCheck" : false "urlCheck": false
}, },
"usingComponents" : true "usingComponents": true
}, },
"mp-alipay" : { "mp-alipay": {
"usingComponents" : true "usingComponents": true
}, },
"mp-baidu" : { "mp-baidu": {
"usingComponents" : true "usingComponents": true
}, },
"mp-toutiao" : { "mp-toutiao": {
"usingComponents" : true "usingComponents": true
} }
} }
/** /**
* App 信息 * App 信息
*/ */
type Application = { interface Application {
/**
* 应用名称
*/
appName: string
/** /**
* 项目名称 * 项目名称
*/ */
...@@ -31,7 +36,7 @@ type Application = { ...@@ -31,7 +36,7 @@ type Application = {
/** /**
* App 作者信息 * App 作者信息
*/ */
type Author = { interface Author {
/** /**
* 作者名称 * 作者名称
*/ */
......
...@@ -45,6 +45,7 @@ export default ({ mode }: ConfigEnv): UserConfig => { ...@@ -45,6 +45,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
}, },
define: { define: {
__APP__: { __APP__: {
appName: pkg.appName || pkg.description,
name: pkg.name, name: pkg.name,
version: pkg.version, version: pkg.version,
description: pkg.description, description: pkg.description,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论