/** * 权限管理工具类 * 用于iOS和Android的权限检查、请求和本地缓存 */ // 判断是否为iOS系统 let isIos = false; // #ifdef APP-PLUS isIos = (plus.os.name == "iOS"); // #endif /** * 权限类型枚举 * @enum {string} */ export const PermissionType = { // 相机权限 CAMERA: 'camera', // 相册权限 PHOTO_LIBRARY: 'photoLibrary', // 相册写入权限 PHOTO_LIBRARY_ADD_ONLY: 'photoLibraryAddOnly', // 麦克风权限 MICROPHONE: 'microphone', // 位置权限 LOCATION: 'location', // 位置权限(始终允许) LOCATION_ALWAYS: 'locationAlways', // 位置权限(使用期间) LOCATION_WHEN_IN_USE: 'locationWhenInUse', // 位置权限(精确位置) LOCATION_PRECISE: 'locationPrecise', // 位置权限(粗略位置) LOCATION_APPROXIMATE: 'locationApproximate', // 通知权限 NOTIFICATION: 'notification', // 通讯录权限 CONTACTS: 'contacts', // 日历权限 CALENDAR: 'calendar', // 备忘录权限 MEMO: 'memo', // 蓝牙权限 BLUETOOTH: 'bluetooth', // 蓝牙外设权限 BLUETOOTH_PERIPHERAL: 'bluetoothPeripheral', // 蓝牙中心设备权限 BLUETOOTH_CENTRAL: 'bluetoothCentral', // 健康数据权限 HEALTH: 'health', // 运动与健身权限 MOTION: 'motion', // 生物识别权限(指纹/面容) BIOMETRICS: 'biometrics', // 网络权限 NETWORK: 'network', // 后台运行权限 BACKGROUND_FETCH: 'backgroundFetch', // 后台处理权限 BACKGROUND_PROCESSING: 'backgroundProcessing', // 后台音频播放权限 BACKGROUND_AUDIO: 'backgroundAudio', // 后台位置更新权限 BACKGROUND_LOCATION: 'backgroundLocation', // 后台网络权限 BACKGROUND_NETWORK: 'backgroundNetwork', // 后台蓝牙权限 BACKGROUND_BLUETOOTH: 'backgroundBluetooth', // 后台通知权限 BACKGROUND_NOTIFICATION: 'backgroundNotification', // 后台传感器权限 BACKGROUND_SENSOR: 'backgroundSensor', // 后台任务权限 BACKGROUND_TASK: 'backgroundTask', // 后台下载权限 BACKGROUND_DOWNLOAD: 'backgroundDownload', // 后台上传权限 BACKGROUND_UPLOAD: 'backgroundUpload', // 后台同步权限 BACKGROUND_SYNC: 'backgroundSync', // 后台定位权限 BACKGROUND_LOCATION_ALWAYS: 'backgroundLocationAlways', // 后台定位权限(使用期间) BACKGROUND_LOCATION_WHEN_IN_USE: 'backgroundLocationWhenInUse', // 后台定位权限(精确位置) BACKGROUND_LOCATION_PRECISE: 'backgroundLocationPrecise', // 后台定位权限(粗略位置) BACKGROUND_LOCATION_APPROXIMATE: 'backgroundLocationApproximate' }; // 默认权限说明配置 const DefaultPermissionConfig = { [PermissionType.LOCATION]: { title: '定位权限说明', describe: '便于您使用该功能在位置展示时显示距搜索位置的距离,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.ACCESS_FINE_LOCATION', permissionName: '位置' }, [PermissionType.LOCATION_ALWAYS]: { title: '后台定位权限说明', describe: '便于您在应用后台运行时获取位置信息,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.ACCESS_BACKGROUND_LOCATION', permissionName: '后台定位' }, [PermissionType.LOCATION_WHEN_IN_USE]: { title: '使用期间定位权限说明', describe: '便于您在应用使用期间获取位置信息,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.ACCESS_COARSE_LOCATION', permissionName: '使用期间定位' }, [PermissionType.PHOTO_LIBRARY]: { title: '相册权限说明', describe: '便于您使用该功能上传您的照片/图片/及用户修改头像,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.READ_EXTERNAL_STORAGE', permissionName: '相册' }, [PermissionType.PHOTO_LIBRARY_ADD_ONLY]: { title: '相册写入权限说明', describe: '便于您保存图片到相册,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.WRITE_EXTERNAL_STORAGE', permissionName: '相册写入' }, [PermissionType.CAMERA]: { title: '拍摄权限说明', describe: '便于您使用该功能拍摄照片修改头像、意见反馈上传图片等信息,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.CAMERA', permissionName: '相机' }, [PermissionType.MICROPHONE]: { title: '麦克风权限说明', describe: '便于您使用该功能录制音频,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.RECORD_AUDIO', permissionName: '麦克风' }, [PermissionType.NOTIFICATION]: { title: '通知权限说明', describe: '便于您接收重要消息通知,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.POST_NOTIFICATIONS', permissionName: '通知' }, [PermissionType.CONTACTS]: { title: '通讯录权限说明', describe: '便于您使用该功能访问通讯录,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.READ_CONTACTS', permissionName: '通讯录' }, [PermissionType.CALENDAR]: { title: '日历权限说明', describe: '便于您使用该功能访问日历,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.READ_CALENDAR', permissionName: '日历' }, [PermissionType.MEMO]: { title: '备忘录权限说明', describe: '便于您使用该功能访问备忘录,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.READ_CALENDAR', permissionName: '备忘录' }, [PermissionType.BLUETOOTH]: { title: '蓝牙权限说明', describe: '便于您使用蓝牙功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.BLUETOOTH', permissionName: '蓝牙' }, [PermissionType.BLUETOOTH_PERIPHERAL]: { title: '蓝牙外设权限说明', describe: '便于您使用蓝牙外设功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.BLUETOOTH_ADMIN', permissionName: '蓝牙外设' }, [PermissionType.BLUETOOTH_CENTRAL]: { title: '蓝牙中心设备权限说明', describe: '便于您使用蓝牙中心设备功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.BLUETOOTH_SCAN', permissionName: '蓝牙中心设备' }, [PermissionType.HEALTH]: { title: '健康数据权限说明', describe: '便于您使用健康数据相关功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.ACTIVITY_RECOGNITION', permissionName: '健康数据' }, [PermissionType.MOTION]: { title: '运动与健身权限说明', describe: '便于您使用运动与健身相关功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.ACTIVITY_RECOGNITION', permissionName: '运动与健身' }, [PermissionType.BIOMETRICS]: { title: '生物识别权限说明', describe: '便于您使用指纹/面容识别功能,请您确认授权,否则无法使用该功能', androidPermission: 'android.permission.USE_BIOMETRICS', permissionName: '生物识别' } }; /** * 获取权限配置 * @param {string} permissionType 权限类型 * @param {Object} customConfig 自定义配置 * @returns {Object} 权限配置 */ function getPermissionConfig(permissionType, customConfig = {}) { const defaultConfig = DefaultPermissionConfig[permissionType] || { title: '权限说明', describe: '便于您使用该功能,请您确认授权,否则无法使用该功能', androidPermission: '', permissionName: '未知权限' }; return { ...defaultConfig, ...customConfig }; } // iOS权限相关的常量和工具函数 const IOS_PERMISSION_HANDLERS = { // 定位权限 location: { check: () => { const cllocationManger = plus.ios.import("CLLocationManager"); const status = cllocationManger.authorizationStatus(); const result = (status != 2) ? 1 : 0; plus.ios.deleteObject(cllocationManger); return result; }, request: () => { return new Promise((resolve) => { const cllocationManger = plus.ios.import("CLLocationManager"); const manager = new cllocationManger(); manager.requestWhenInUseAuthorization(); plus.ios.deleteObject(manager); plus.ios.deleteObject(cllocationManger); setTimeout(() => resolve(IOS_PERMISSION_HANDLERS.location.check() === 1), 1000); }); } }, // 相机权限 camera: { check: () => { const AVCaptureDevice = plus.ios.import("AVCaptureDevice"); const authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide'); const result = authStatus === 3 ? 1 : 0; plus.ios.deleteObject(AVCaptureDevice); return result; }, request: () => { return new Promise((resolve) => { const AVCaptureDevice = plus.ios.import("AVCaptureDevice"); AVCaptureDevice.requestAccessForMediaTypeCompletionHandler('vide', (granted) => { plus.ios.deleteObject(AVCaptureDevice); resolve(granted); }); }); } }, // 相册权限 photoLibrary: { check: () => { const PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); const authStatus = PHPhotoLibrary.authorizationStatus(); const result = authStatus === 3 ? 1 : 0; plus.ios.deleteObject(PHPhotoLibrary); return result; }, request: () => { return new Promise((resolve) => { const PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); PHPhotoLibrary.requestAuthorization((status) => { plus.ios.deleteObject(PHPhotoLibrary); resolve(status === 3); }); }); } }, // 麦克风权限 microphone: { check: () => { const avaudiosession = plus.ios.import("AVAudioSession"); const avaudio = avaudiosession.sharedInstance(); const permissionStatus = avaudio.recordPermission(); const result = (permissionStatus !== 1684369017 && permissionStatus !== 1970168948) ? 1 : 0; plus.ios.deleteObject(avaudiosession); return result; }, request: () => { return new Promise((resolve) => { const avaudiosession = plus.ios.import("AVAudioSession"); const avaudio = avaudiosession.sharedInstance(); avaudio.requestRecordPermission((granted) => { plus.ios.deleteObject(avaudiosession); resolve(granted); }); }); } }, // 其他iOS权限处理器... }; // Android权限处理 const ANDROID_PERMISSION_HANDLERS = { request: (permissionID) => { return new Promise((resolve) => { plus.android.requestPermissions( [permissionID], (resultObj) => { let result = 0; if (resultObj.granted.length > 0) { result = 1; } else if (resultObj.deniedAlways.length > 0) { result = -1; } resolve(result); }, (error) => { console.error('Request permission error:', error); resolve(0); } ); }); } }; // 权限配置映射 const PERMISSION_CONFIG = { [PermissionType.CAMERA]: { title: '相机权限申请', describe: '需要使用相机权限来拍摄照片', androidPermission: 'android.permission.CAMERA', iosHandler: 'camera' }, [PermissionType.PHOTO_LIBRARY]: { title: '相册权限申请', describe: '需要访问相册权限来选择照片', androidPermission: 'android.permission.READ_EXTERNAL_STORAGE', iosHandler: 'photoLibrary' }, // ... 其他权限配置 }; // 统一的权限处理类 class PermissionManager { static async check(permissionType) { const config = PERMISSION_CONFIG[permissionType]; if (!config) return false; if (isIos) { const handler = IOS_PERMISSION_HANDLERS[config.iosHandler]; return handler ? handler.check() === 1 : false; } else { const MainActivity = plus.android.runtimeMainActivity(); const permission = config.androidPermission; return MainActivity.checkSelfPermission(permission) === 0; } } /** * 请求权限 * @param {string} permissionType 权限类型 * @param {Object} options 配置选项 * @returns {Promise} 是否获得权限 */ static async request(permissionType, options = {}) { // 在 H5 环境下直接返回 true // #ifdef H5 return true; // #endif const config = getPermissionConfig(permissionType, options); if (!config) return false; // 先检查权限 const hasPermission = await this.check(permissionType); if (hasPermission) return true; // 显示权限申请对话框 const dialogResult = await showPermissionDialog(config); if (!dialogResult) return false; // #ifdef APP-PLUS if (isIos) { // iOS权限请求逻辑 return true; } else { // Android权限请求 const result = await ANDROID_PERMISSION_HANDLERS.request(config.androidPermission); // 如果权限被永久拒绝,引导用户去设置页面 if (result === -1) { return new Promise((resolve) => { uni.showModal({ title: '权限申请', content: `${config.permissionName}权限被永久拒绝,请到设置中手动开启`, confirmText: '去设置', cancelText: '取消', success: function(res) { if (res.confirm) { // 跳转到应用权限设置页面 if (plus.os.name.toLowerCase() === 'android') { const main = plus.android.runtimeMainActivity(); 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 intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); const uri = Uri.fromParts('package', main.getPackageName(), null); intent.setData(uri); main.startActivity(intent); } } resolve(false); } }); }); } return result === 1; } // #endif return false; } static openSettings() { if (isIos) { const UIApplication = plus.ios.import("UIApplication"); const application = UIApplication.sharedApplication(); const settingsUrl = plus.ios.newObject("NSURL").URLWithString("app-settings:"); application.openURL(settingsUrl); plus.ios.deleteObject(settingsUrl); plus.ios.deleteObject(application); } else { 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(); const intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); const uri = Uri.fromParts("package", MainActivity.getPackageName(), null); intent.setData(uri); MainActivity.startActivity(intent); } } } // 显示权限说明弹窗 const showPermissionDialog = (options) => { return new Promise((resolve) => { uni.showModal({ title: options.title || '权限申请', content: options.content || options.describe, cancelText: '暂不授权', confirmText: '去授权', success: (res) => { resolve(res.confirm); } }); }); }; /** * 清除权限缓存 * @param {string} permissionType 权限类型,不传则清除所有权限缓存 */ function clearPermissionCache(permissionType) { if (permissionType) { uni.removeStorageSync(`permission_${permissionType}`); } else { // 清除所有权限缓存 Object.values(PermissionType).forEach(type => { uni.removeStorageSync(`permission_${type}`); }); } } // 导出模块 export default { PermissionType, check: PermissionManager.check.bind(PermissionManager), request: PermissionManager.request.bind(PermissionManager), openSettings: PermissionManager.openSettings.bind(PermissionManager), showPermissionDialog, clearPermissionCache, getPermissionConfig };