permission.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /**
  2. * Android / iOS 权限与能力检测
  3. * Android 6+ 扫描 BLE 需要位置权限 + 位置服务开启
  4. * Android 12+ 需要 BLUETOOTH_SCAN / BLUETOOTH_CONNECT
  5. * iOS 需要 NSBluetoothAlwaysUsageDescription (在 manifest.json 中配置)
  6. *
  7. * 仅在 APP-PLUS 环境下调用 plus API, H5 / 小程序环境跳过
  8. */
  9. export function getPlatform() {
  10. try { return uni.getSystemInfoSync().platform } catch (_) { return '' }
  11. }
  12. /** 是否 App 环境 (plus 存在) */
  13. export function isAppPlus() {
  14. // #ifdef APP-PLUS
  15. return typeof plus !== 'undefined'
  16. // #endif
  17. // #ifndef APP-PLUS
  18. return false
  19. // #endif
  20. }
  21. /** 检查位置服务是否已开启 (Android) */
  22. export function isLocationEnabled() {
  23. return new Promise(resolve => {
  24. if (!isAppPlus() || getPlatform() !== 'android') return resolve(true)
  25. // #ifdef APP-PLUS
  26. try {
  27. const main = plus.android.runtimeMainActivity()
  28. const LocationManager = plus.android.importClass('android.location.LocationManager')
  29. const context = plus.android.importClass('android.content.Context')
  30. const lm = main.getSystemService(context.LOCATION_SERVICE)
  31. const gps = lm.isProviderEnabled(LocationManager.GPS_PROVIDER)
  32. const net = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
  33. resolve(gps || net)
  34. } catch (e) { resolve(true) }
  35. // #endif
  36. })
  37. }
  38. /** 跳转到系统位置服务设置 */
  39. export function openLocationSettings() {
  40. // #ifdef APP-PLUS
  41. if (!isAppPlus() || getPlatform() !== 'android') return
  42. try {
  43. const main = plus.android.runtimeMainActivity()
  44. const Intent = plus.android.importClass('android.content.Intent')
  45. const Settings = plus.android.importClass('android.provider.Settings')
  46. const intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
  47. main.startActivity(intent)
  48. } catch (e) { console.error(e) }
  49. // #endif
  50. }
  51. /** 请求 Android 运行时权限(定位/蓝牙扫描/连接). 返回 true/false */
  52. export function requestAndroidPermissions() {
  53. return new Promise(resolve => {
  54. if (!isAppPlus() || getPlatform() !== 'android') return resolve(true)
  55. // #ifdef APP-PLUS
  56. try {
  57. const main = plus.android.runtimeMainActivity()
  58. const Build = plus.android.importClass('android.os.Build')
  59. const sdkInt = Build.VERSION.SDK_INT
  60. const perms = ['android.permission.ACCESS_FINE_LOCATION']
  61. if (sdkInt >= 31) {
  62. perms.push('android.permission.BLUETOOTH_SCAN')
  63. perms.push('android.permission.BLUETOOTH_CONNECT')
  64. }
  65. const PackageManager = plus.android.importClass('android.content.pm.PackageManager')
  66. const need = perms.filter(p => main.checkSelfPermission(p) !== PackageManager.PERMISSION_GRANTED)
  67. if (!need.length) return resolve(true)
  68. const ActivityCompat = plus.android.importClass('androidx.core.app.ActivityCompat')
  69. // 注册结果回调
  70. const onReq = (activity, requestCode, permsArr, grantResults) => {
  71. plus.android.clearRequestPermissions && plus.android.clearRequestPermissions()
  72. const granted = grantResults && Array.from(grantResults).every(r => r === 0)
  73. resolve(!!granted)
  74. }
  75. if (plus.android.requestPermissions) {
  76. plus.android.requestPermissions(need, res => {
  77. resolve(res && res.granted && res.granted.length === need.length)
  78. }, () => resolve(false))
  79. } else {
  80. ActivityCompat.requestPermissions(main, need, 1001)
  81. // 无结果回调能力时, 乐观等待 2s 后查询
  82. setTimeout(() => {
  83. const still = need.filter(p => main.checkSelfPermission(p) !== PackageManager.PERMISSION_GRANTED)
  84. resolve(!still.length)
  85. }, 2000)
  86. }
  87. } catch (e) {
  88. console.error(e); resolve(false)
  89. }
  90. // #endif
  91. })
  92. }
  93. /** 检查系统蓝牙是否已开启 (Android 使用原生 API,iOS 依赖 uni API) */
  94. export function isBluetoothEnabled() {
  95. return new Promise(resolve => {
  96. const platform = getPlatform()
  97. if (!isAppPlus()) return resolve(true)
  98. // #ifdef APP-PLUS
  99. if (platform === 'android') {
  100. try {
  101. const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter')
  102. const adapter = BluetoothAdapter.getDefaultAdapter()
  103. resolve(adapter != null && adapter.isEnabled())
  104. } catch (e) {
  105. // 无法检测时放行,让后续流程处理
  106. resolve(true)
  107. }
  108. } else if (platform === 'ios') {
  109. // iOS 无法直接通过原生 API 检测,依赖 uni.openBluetoothAdapter 的结果
  110. resolve(true)
  111. } else {
  112. resolve(true)
  113. }
  114. // #endif
  115. // #ifndef APP-PLUS
  116. resolve(true)
  117. // #endif
  118. })
  119. }
  120. /** 跳转到系统蓝牙设置页面 */
  121. export function openBluetoothSettings() {
  122. // #ifdef APP-PLUS
  123. const platform = getPlatform()
  124. if (platform === 'android') {
  125. try {
  126. const main = plus.android.runtimeMainActivity()
  127. const Intent = plus.android.importClass('android.content.Intent')
  128. const Settings = plus.android.importClass('android.provider.Settings')
  129. const intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
  130. main.startActivity(intent)
  131. } catch (e) { console.error('跳转蓝牙设置失败', e) }
  132. } else if (platform === 'ios') {
  133. plus.runtime.openURL('App-Prefs:root=Bluetooth')
  134. }
  135. // #endif
  136. }
  137. /** 一键: 申请权限 + 校验定位服务 + 检测蓝牙开启. 失败抛异常 */
  138. export async function ensureBlePrerequisite() {
  139. const platform = getPlatform()
  140. if (platform === 'android') {
  141. const ok = await requestAndroidPermissions()
  142. if (!ok) throw new Error('BLE_PERMISSION_DENIED')
  143. const loc = await isLocationEnabled()
  144. if (!loc) throw new Error('BLE_LOCATION_OFF')
  145. }
  146. // 检测蓝牙是否开启(Android 原生检测,iOS 跳过此步)
  147. const bleOn = await isBluetoothEnabled()
  148. if (!bleOn) throw new Error('BLE_ADAPTER_OFF')
  149. return true
  150. }