permission.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /**
  2. * 权限管理工具类
  3. * 用于iOS和Android的权限检查、请求和本地缓存
  4. */
  5. // 判断是否为iOS系统
  6. let isIos = false;
  7. // #ifdef APP-PLUS
  8. isIos = (plus.os.name == "iOS");
  9. // #endif
  10. /**
  11. * 权限类型枚举
  12. * @enum {string}
  13. */
  14. export const PermissionType = {
  15. // 相机权限
  16. CAMERA: 'camera',
  17. // 相册权限
  18. PHOTO_LIBRARY: 'photoLibrary',
  19. // 相册写入权限
  20. PHOTO_LIBRARY_ADD_ONLY: 'photoLibraryAddOnly',
  21. // 麦克风权限
  22. MICROPHONE: 'microphone',
  23. // 位置权限
  24. LOCATION: 'location',
  25. // 位置权限(始终允许)
  26. LOCATION_ALWAYS: 'locationAlways',
  27. // 位置权限(使用期间)
  28. LOCATION_WHEN_IN_USE: 'locationWhenInUse',
  29. // 位置权限(精确位置)
  30. LOCATION_PRECISE: 'locationPrecise',
  31. // 位置权限(粗略位置)
  32. LOCATION_APPROXIMATE: 'locationApproximate',
  33. // 通知权限
  34. NOTIFICATION: 'notification',
  35. // 通讯录权限
  36. CONTACTS: 'contacts',
  37. // 日历权限
  38. CALENDAR: 'calendar',
  39. // 备忘录权限
  40. MEMO: 'memo',
  41. // 蓝牙权限
  42. BLUETOOTH: 'bluetooth',
  43. // 蓝牙外设权限
  44. BLUETOOTH_PERIPHERAL: 'bluetoothPeripheral',
  45. // 蓝牙中心设备权限
  46. BLUETOOTH_CENTRAL: 'bluetoothCentral',
  47. // 健康数据权限
  48. HEALTH: 'health',
  49. // 运动与健身权限
  50. MOTION: 'motion',
  51. // 生物识别权限(指纹/面容)
  52. BIOMETRICS: 'biometrics',
  53. // 网络权限
  54. NETWORK: 'network',
  55. // 后台运行权限
  56. BACKGROUND_FETCH: 'backgroundFetch',
  57. // 后台处理权限
  58. BACKGROUND_PROCESSING: 'backgroundProcessing',
  59. // 后台音频播放权限
  60. BACKGROUND_AUDIO: 'backgroundAudio',
  61. // 后台位置更新权限
  62. BACKGROUND_LOCATION: 'backgroundLocation',
  63. // 后台网络权限
  64. BACKGROUND_NETWORK: 'backgroundNetwork',
  65. // 后台蓝牙权限
  66. BACKGROUND_BLUETOOTH: 'backgroundBluetooth',
  67. // 后台通知权限
  68. BACKGROUND_NOTIFICATION: 'backgroundNotification',
  69. // 后台传感器权限
  70. BACKGROUND_SENSOR: 'backgroundSensor',
  71. // 后台任务权限
  72. BACKGROUND_TASK: 'backgroundTask',
  73. // 后台下载权限
  74. BACKGROUND_DOWNLOAD: 'backgroundDownload',
  75. // 后台上传权限
  76. BACKGROUND_UPLOAD: 'backgroundUpload',
  77. // 后台同步权限
  78. BACKGROUND_SYNC: 'backgroundSync',
  79. // 后台定位权限
  80. BACKGROUND_LOCATION_ALWAYS: 'backgroundLocationAlways',
  81. // 后台定位权限(使用期间)
  82. BACKGROUND_LOCATION_WHEN_IN_USE: 'backgroundLocationWhenInUse',
  83. // 后台定位权限(精确位置)
  84. BACKGROUND_LOCATION_PRECISE: 'backgroundLocationPrecise',
  85. // 后台定位权限(粗略位置)
  86. BACKGROUND_LOCATION_APPROXIMATE: 'backgroundLocationApproximate'
  87. };
  88. // 默认权限说明配置
  89. const DefaultPermissionConfig = {
  90. [PermissionType.LOCATION]: {
  91. title: '定位权限说明',
  92. describe: '便于您使用该功能在位置展示时显示距搜索位置的距离,请您确认授权,否则无法使用该功能',
  93. androidPermission: 'android.permission.ACCESS_FINE_LOCATION',
  94. permissionName: '位置'
  95. },
  96. [PermissionType.LOCATION_ALWAYS]: {
  97. title: '后台定位权限说明',
  98. describe: '便于您在应用后台运行时获取位置信息,请您确认授权,否则无法使用该功能',
  99. androidPermission: 'android.permission.ACCESS_BACKGROUND_LOCATION',
  100. permissionName: '后台定位'
  101. },
  102. [PermissionType.LOCATION_WHEN_IN_USE]: {
  103. title: '使用期间定位权限说明',
  104. describe: '便于您在应用使用期间获取位置信息,请您确认授权,否则无法使用该功能',
  105. androidPermission: 'android.permission.ACCESS_COARSE_LOCATION',
  106. permissionName: '使用期间定位'
  107. },
  108. [PermissionType.PHOTO_LIBRARY]: {
  109. title: '相册权限说明',
  110. describe: '便于您使用该功能上传您的照片/图片/及用户修改头像,请您确认授权,否则无法使用该功能',
  111. androidPermission: 'android.permission.READ_EXTERNAL_STORAGE',
  112. permissionName: '相册'
  113. },
  114. [PermissionType.PHOTO_LIBRARY_ADD_ONLY]: {
  115. title: '相册写入权限说明',
  116. describe: '便于您保存图片到相册,请您确认授权,否则无法使用该功能',
  117. androidPermission: 'android.permission.WRITE_EXTERNAL_STORAGE',
  118. permissionName: '相册写入'
  119. },
  120. [PermissionType.CAMERA]: {
  121. title: '拍摄权限说明',
  122. describe: '便于您使用该功能拍摄照片修改头像、意见反馈上传图片等信息,请您确认授权,否则无法使用该功能',
  123. androidPermission: 'android.permission.CAMERA',
  124. permissionName: '相机'
  125. },
  126. [PermissionType.MICROPHONE]: {
  127. title: '麦克风权限说明',
  128. describe: '便于您使用该功能录制音频,请您确认授权,否则无法使用该功能',
  129. androidPermission: 'android.permission.RECORD_AUDIO',
  130. permissionName: '麦克风'
  131. },
  132. [PermissionType.NOTIFICATION]: {
  133. title: '通知权限说明',
  134. describe: '便于您接收重要消息通知,请您确认授权,否则无法使用该功能',
  135. androidPermission: 'android.permission.POST_NOTIFICATIONS',
  136. permissionName: '通知'
  137. },
  138. [PermissionType.CONTACTS]: {
  139. title: '通讯录权限说明',
  140. describe: '便于您使用该功能访问通讯录,请您确认授权,否则无法使用该功能',
  141. androidPermission: 'android.permission.READ_CONTACTS',
  142. permissionName: '通讯录'
  143. },
  144. [PermissionType.CALENDAR]: {
  145. title: '日历权限说明',
  146. describe: '便于您使用该功能访问日历,请您确认授权,否则无法使用该功能',
  147. androidPermission: 'android.permission.READ_CALENDAR',
  148. permissionName: '日历'
  149. },
  150. [PermissionType.MEMO]: {
  151. title: '备忘录权限说明',
  152. describe: '便于您使用该功能访问备忘录,请您确认授权,否则无法使用该功能',
  153. androidPermission: 'android.permission.READ_CALENDAR',
  154. permissionName: '备忘录'
  155. },
  156. [PermissionType.BLUETOOTH]: {
  157. title: '蓝牙权限说明',
  158. describe: '便于您使用蓝牙功能,请您确认授权,否则无法使用该功能',
  159. androidPermission: 'android.permission.BLUETOOTH',
  160. permissionName: '蓝牙'
  161. },
  162. [PermissionType.BLUETOOTH_PERIPHERAL]: {
  163. title: '蓝牙外设权限说明',
  164. describe: '便于您使用蓝牙外设功能,请您确认授权,否则无法使用该功能',
  165. androidPermission: 'android.permission.BLUETOOTH_ADMIN',
  166. permissionName: '蓝牙外设'
  167. },
  168. [PermissionType.BLUETOOTH_CENTRAL]: {
  169. title: '蓝牙中心设备权限说明',
  170. describe: '便于您使用蓝牙中心设备功能,请您确认授权,否则无法使用该功能',
  171. androidPermission: 'android.permission.BLUETOOTH_SCAN',
  172. permissionName: '蓝牙中心设备'
  173. },
  174. [PermissionType.HEALTH]: {
  175. title: '健康数据权限说明',
  176. describe: '便于您使用健康数据相关功能,请您确认授权,否则无法使用该功能',
  177. androidPermission: 'android.permission.ACTIVITY_RECOGNITION',
  178. permissionName: '健康数据'
  179. },
  180. [PermissionType.MOTION]: {
  181. title: '运动与健身权限说明',
  182. describe: '便于您使用运动与健身相关功能,请您确认授权,否则无法使用该功能',
  183. androidPermission: 'android.permission.ACTIVITY_RECOGNITION',
  184. permissionName: '运动与健身'
  185. },
  186. [PermissionType.BIOMETRICS]: {
  187. title: '生物识别权限说明',
  188. describe: '便于您使用指纹/面容识别功能,请您确认授权,否则无法使用该功能',
  189. androidPermission: 'android.permission.USE_BIOMETRICS',
  190. permissionName: '生物识别'
  191. }
  192. };
  193. /**
  194. * 获取权限配置
  195. * @param {string} permissionType 权限类型
  196. * @param {Object} customConfig 自定义配置
  197. * @returns {Object} 权限配置
  198. */
  199. function getPermissionConfig(permissionType, customConfig = {}) {
  200. const defaultConfig = DefaultPermissionConfig[permissionType] || {
  201. title: '权限说明',
  202. describe: '便于您使用该功能,请您确认授权,否则无法使用该功能',
  203. androidPermission: '',
  204. permissionName: '未知权限'
  205. };
  206. return {
  207. ...defaultConfig,
  208. ...customConfig
  209. };
  210. }
  211. // iOS权限相关的常量和工具函数
  212. const IOS_PERMISSION_HANDLERS = {
  213. // 定位权限
  214. location: {
  215. check: () => {
  216. const cllocationManger = plus.ios.import("CLLocationManager");
  217. const status = cllocationManger.authorizationStatus();
  218. const result = (status != 2) ? 1 : 0;
  219. plus.ios.deleteObject(cllocationManger);
  220. return result;
  221. },
  222. request: () => {
  223. return new Promise((resolve) => {
  224. const cllocationManger = plus.ios.import("CLLocationManager");
  225. const manager = new cllocationManger();
  226. manager.requestWhenInUseAuthorization();
  227. plus.ios.deleteObject(manager);
  228. plus.ios.deleteObject(cllocationManger);
  229. setTimeout(() => resolve(IOS_PERMISSION_HANDLERS.location.check() === 1), 1000);
  230. });
  231. }
  232. },
  233. // 相机权限
  234. camera: {
  235. check: () => {
  236. const AVCaptureDevice = plus.ios.import("AVCaptureDevice");
  237. const authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
  238. const result = authStatus === 3 ? 1 : 0;
  239. plus.ios.deleteObject(AVCaptureDevice);
  240. return result;
  241. },
  242. request: () => {
  243. return new Promise((resolve) => {
  244. const AVCaptureDevice = plus.ios.import("AVCaptureDevice");
  245. AVCaptureDevice.requestAccessForMediaTypeCompletionHandler('vide', (granted) => {
  246. plus.ios.deleteObject(AVCaptureDevice);
  247. resolve(granted);
  248. });
  249. });
  250. }
  251. },
  252. // 相册权限
  253. photoLibrary: {
  254. check: () => {
  255. const PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
  256. const authStatus = PHPhotoLibrary.authorizationStatus();
  257. const result = authStatus === 3 ? 1 : 0;
  258. plus.ios.deleteObject(PHPhotoLibrary);
  259. return result;
  260. },
  261. request: () => {
  262. return new Promise((resolve) => {
  263. const PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
  264. PHPhotoLibrary.requestAuthorization((status) => {
  265. plus.ios.deleteObject(PHPhotoLibrary);
  266. resolve(status === 3);
  267. });
  268. });
  269. }
  270. },
  271. // 麦克风权限
  272. microphone: {
  273. check: () => {
  274. const avaudiosession = plus.ios.import("AVAudioSession");
  275. const avaudio = avaudiosession.sharedInstance();
  276. const permissionStatus = avaudio.recordPermission();
  277. const result = (permissionStatus !== 1684369017 && permissionStatus !== 1970168948) ? 1 : 0;
  278. plus.ios.deleteObject(avaudiosession);
  279. return result;
  280. },
  281. request: () => {
  282. return new Promise((resolve) => {
  283. const avaudiosession = plus.ios.import("AVAudioSession");
  284. const avaudio = avaudiosession.sharedInstance();
  285. avaudio.requestRecordPermission((granted) => {
  286. plus.ios.deleteObject(avaudiosession);
  287. resolve(granted);
  288. });
  289. });
  290. }
  291. },
  292. // 其他iOS权限处理器...
  293. };
  294. // Android权限处理
  295. const ANDROID_PERMISSION_HANDLERS = {
  296. request: (permissionID) => {
  297. return new Promise((resolve) => {
  298. plus.android.requestPermissions(
  299. [permissionID],
  300. (resultObj) => {
  301. let result = 0;
  302. if (resultObj.granted.length > 0) {
  303. result = 1;
  304. } else if (resultObj.deniedAlways.length > 0) {
  305. result = -1;
  306. }
  307. resolve(result);
  308. },
  309. (error) => {
  310. console.error('Request permission error:', error);
  311. resolve(0);
  312. }
  313. );
  314. });
  315. }
  316. };
  317. // 权限配置映射
  318. const PERMISSION_CONFIG = {
  319. [PermissionType.CAMERA]: {
  320. title: '相机权限申请',
  321. describe: '需要使用相机权限来拍摄照片',
  322. androidPermission: 'android.permission.CAMERA',
  323. iosHandler: 'camera'
  324. },
  325. [PermissionType.PHOTO_LIBRARY]: {
  326. title: '相册权限申请',
  327. describe: '需要访问相册权限来选择照片',
  328. androidPermission: 'android.permission.READ_EXTERNAL_STORAGE',
  329. iosHandler: 'photoLibrary'
  330. },
  331. // ... 其他权限配置
  332. };
  333. // 统一的权限处理类
  334. class PermissionManager {
  335. static async check(permissionType) {
  336. const config = PERMISSION_CONFIG[permissionType];
  337. if (!config) return false;
  338. if (isIos) {
  339. const handler = IOS_PERMISSION_HANDLERS[config.iosHandler];
  340. return handler ? handler.check() === 1 : false;
  341. } else {
  342. const MainActivity = plus.android.runtimeMainActivity();
  343. const permission = config.androidPermission;
  344. return MainActivity.checkSelfPermission(permission) === 0;
  345. }
  346. }
  347. /**
  348. * 请求权限
  349. * @param {string} permissionType 权限类型
  350. * @param {Object} options 配置选项
  351. * @returns {Promise<boolean>} 是否获得权限
  352. */
  353. static async request(permissionType, options = {}) {
  354. // 在 H5 环境下直接返回 true
  355. // #ifdef H5
  356. return true;
  357. // #endif
  358. const config = getPermissionConfig(permissionType, options);
  359. if (!config) return false;
  360. // 先检查权限
  361. const hasPermission = await this.check(permissionType);
  362. if (hasPermission) return true;
  363. // 显示权限申请对话框
  364. const dialogResult = await showPermissionDialog(config);
  365. if (!dialogResult) return false;
  366. // #ifdef APP-PLUS
  367. if (isIos) {
  368. // iOS权限请求逻辑
  369. return true;
  370. } else {
  371. // Android权限请求
  372. const result = await ANDROID_PERMISSION_HANDLERS.request(config.androidPermission);
  373. // 如果权限被永久拒绝,引导用户去设置页面
  374. if (result === -1) {
  375. return new Promise((resolve) => {
  376. uni.showModal({
  377. title: '权限申请',
  378. content: `${config.permissionName}权限被永久拒绝,请到设置中手动开启`,
  379. confirmText: '去设置',
  380. cancelText: '取消',
  381. success: function(res) {
  382. if (res.confirm) {
  383. // 跳转到应用权限设置页面
  384. if (plus.os.name.toLowerCase() === 'android') {
  385. const main = plus.android.runtimeMainActivity();
  386. const Intent = plus.android.importClass('android.content.Intent');
  387. const Settings = plus.android.importClass('android.provider.Settings');
  388. const Uri = plus.android.importClass('android.net.Uri');
  389. const intent = new Intent();
  390. intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
  391. const uri = Uri.fromParts('package', main.getPackageName(), null);
  392. intent.setData(uri);
  393. main.startActivity(intent);
  394. }
  395. }
  396. resolve(false);
  397. }
  398. });
  399. });
  400. }
  401. return result === 1;
  402. }
  403. // #endif
  404. return false;
  405. }
  406. static openSettings() {
  407. if (isIos) {
  408. const UIApplication = plus.ios.import("UIApplication");
  409. const application = UIApplication.sharedApplication();
  410. const settingsUrl = plus.ios.newObject("NSURL").URLWithString("app-settings:");
  411. application.openURL(settingsUrl);
  412. plus.ios.deleteObject(settingsUrl);
  413. plus.ios.deleteObject(application);
  414. } else {
  415. const Intent = plus.android.importClass("android.content.Intent");
  416. const Settings = plus.android.importClass("android.provider.Settings");
  417. const Uri = plus.android.importClass("android.net.Uri");
  418. const MainActivity = plus.android.runtimeMainActivity();
  419. const intent = new Intent();
  420. intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
  421. const uri = Uri.fromParts("package", MainActivity.getPackageName(), null);
  422. intent.setData(uri);
  423. MainActivity.startActivity(intent);
  424. }
  425. }
  426. }
  427. // 显示权限说明弹窗
  428. const showPermissionDialog = (options) => {
  429. return new Promise((resolve) => {
  430. uni.showModal({
  431. title: options.title || '权限申请',
  432. content: options.content || options.describe,
  433. cancelText: '暂不授权',
  434. confirmText: '去授权',
  435. success: (res) => {
  436. resolve(res.confirm);
  437. }
  438. });
  439. });
  440. };
  441. /**
  442. * 清除权限缓存
  443. * @param {string} permissionType 权限类型,不传则清除所有权限缓存
  444. */
  445. function clearPermissionCache(permissionType) {
  446. if (permissionType) {
  447. uni.removeStorageSync(`permission_${permissionType}`);
  448. } else {
  449. // 清除所有权限缓存
  450. Object.values(PermissionType).forEach(type => {
  451. uni.removeStorageSync(`permission_${type}`);
  452. });
  453. }
  454. }
  455. // 导出模块
  456. export default {
  457. PermissionType,
  458. check: PermissionManager.check.bind(PermissionManager),
  459. request: PermissionManager.request.bind(PermissionManager),
  460. openSettings: PermissionManager.openSettings.bind(PermissionManager),
  461. showPermissionDialog,
  462. clearPermissionCache,
  463. getPermissionConfig
  464. };