| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- import { AVCaptureDevice, AVMediaType, AVAuthorizationStatus } from 'AVFoundation'
- import { AVAudioSession } from 'AVFoundation'
- import { PHPhotoLibrary, PHAuthorizationStatus } from 'Photos'
- import { CLLocationManager, CLAuthorizationStatus } from 'CoreLocation'
- import { CNContactStore, CNAuthorizationStatus, CNEntityType } from 'Contacts'
- import { CBManager, CBManagerAuthorization, CBCentralManager, CBManagerState } from 'CoreBluetooth'
- import { UIApplication } from 'UIKit'
- import { DispatchQueue } from 'Dispatch'
- import { NSError, URL } from 'Foundation'
- import {
- BluetoothDemoInfo,
- CheckPermission,
- CheckPermissionOptions,
- CheckPermissions,
- CheckPermissionsOptions,
- CheckPermissionsResult,
- CheckPermissionsSync,
- CheckPermissionSync,
- GetSupportedPermissionsResult,
- GetSupportedPermissionsSync,
- GetBluetoothDemoInfoSync,
- OpenAppSettings,
- OpenAppSettingsOptions,
- PermissionCheckResult,
- PermissionKey,
- PermissionGrantResult,
- PermissionStatus,
- RequestPermission,
- RequestPermissionOptions,
- RequestPermissions,
- RequestPermissionsOptions,
- RequestPermissionsResult,
- } from '../interface.uts'
- import { PermissionFailImpl } from '../unierror'
- const SUPPORTED_PERMISSIONS : PermissionKey[] = [
- 'camera',
- 'photoLibrary',
- 'storage',
- 'readStorage',
- 'location',
- 'coarseLocation',
- 'microphone',
- 'contacts',
- 'writeContacts',
- 'bluetooth',
- ]
- let locationManager : CLLocationManager | null = null
- let pendingLocationOptions : RequestPermissionOptions | null = null
- function createCheckResult(permission : PermissionKey, status : PermissionStatus) : PermissionCheckResult {
- return {
- permission: permission,
- status: status,
- granted: status == 'granted',
- }
- }
- function createRequestResult(permission : PermissionKey, status : PermissionStatus) : PermissionGrantResult {
- return {
- permission: permission,
- status: status,
- granted: status == 'granted',
- }
- }
- function isIosUnsupported(permission : PermissionKey) : boolean {
- return permission == 'phone'
- || permission == 'phoneState'
- || permission == 'sms'
- || permission == 'readSms'
- || permission == 'receiveSms'
- || permission == 'overlay'
- || permission == 'writeSettings'
- }
- function mapPhotoStatus(status : PHAuthorizationStatus) : PermissionStatus {
- if (status == PHAuthorizationStatus.authorized || status == PHAuthorizationStatus.limited) {
- return 'granted'
- }
- if (status == PHAuthorizationStatus.notDetermined) {
- return 'notDetermined'
- }
- if (status == PHAuthorizationStatus.restricted) {
- return 'restricted'
- }
- return 'denied'
- }
- function mapLocationStatus(status : CLAuthorizationStatus) : PermissionStatus {
- if (status == CLAuthorizationStatus.authorizedWhenInUse || status == CLAuthorizationStatus.authorizedAlways) {
- return 'granted'
- }
- if (status == CLAuthorizationStatus.notDetermined) {
- return 'notDetermined'
- }
- if (status == CLAuthorizationStatus.restricted) {
- return 'restricted'
- }
- return 'denied'
- }
- function mapContactStatus(status : CNAuthorizationStatus) : PermissionStatus {
- if (status == CNAuthorizationStatus.authorized) {
- return 'granted'
- }
- if (status == CNAuthorizationStatus.notDetermined) {
- return 'notDetermined'
- }
- if (status == CNAuthorizationStatus.restricted) {
- return 'restricted'
- }
- return 'denied'
- }
- function mapBluetoothStatus(status : CBManagerAuthorization) : PermissionStatus {
- if (status == CBManagerAuthorization.allowedAlways) {
- return 'granted'
- }
- if (status == CBManagerAuthorization.notDetermined) {
- return 'notDetermined'
- }
- if (status == CBManagerAuthorization.restricted) {
- return 'restricted'
- }
- return 'denied'
- }
- function resolveCameraPermissionStatus() : PermissionStatus {
- const status = AVCaptureDevice.authorizationStatus(for = AVMediaType.video)
- if (status == AVAuthorizationStatus.authorized) {
- return 'granted'
- }
- if (status == AVAuthorizationStatus.notDetermined) {
- return 'notDetermined'
- }
- if (status == AVAuthorizationStatus.restricted) {
- return 'restricted'
- }
- return 'denied'
- }
- function resolveMicrophonePermissionStatus() : PermissionStatus {
- const recordPermission = AVAudioSession.sharedInstance().recordPermission
- if (recordPermission == AVAudioSession.RecordPermission.granted) {
- return 'granted'
- }
- if (recordPermission == AVAudioSession.RecordPermission.undetermined) {
- return 'notDetermined'
- }
- return 'denied'
- }
- function resolvePermissionStatus(permission : PermissionKey) : PermissionStatus {
- if (isIosUnsupported(permission)) {
- return 'unsupported'
- }
- if (permission == 'camera') {
- return resolveCameraPermissionStatus()
- }
- if (permission == 'photoLibrary' || permission == 'storage' || permission == 'readStorage') {
- return mapPhotoStatus(PHPhotoLibrary.authorizationStatus())
- }
- if (permission == 'location' || permission == 'coarseLocation') {
- return mapLocationStatus(CLLocationManager.authorizationStatus())
- }
- if (permission == 'microphone') {
- return resolveMicrophonePermissionStatus()
- }
- if (permission == 'contacts' || permission == 'writeContacts') {
- return mapContactStatus(CNContactStore.authorizationStatus(for = CNEntityType.contacts))
- }
- if (permission == 'bluetooth') {
- return mapBluetoothStatus(CBManager.authorization)
- }
- return 'unsupported'
- }
- function succeedCheckPermission(options : CheckPermissionOptions, res : PermissionCheckResult) {
- const successCallback = options.success
- if (successCallback != null) {
- successCallback(res)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(res)
- }
- }
- function succeedCheckPermissions(options : CheckPermissionsOptions, res : CheckPermissionsResult) {
- const successCallback = options.success
- if (successCallback != null) {
- successCallback(res)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(res)
- }
- }
- function succeedRequestPermission(options : RequestPermissionOptions, res : PermissionGrantResult) {
- const successCallback = options.success
- if (successCallback != null) {
- successCallback(res)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(res)
- }
- }
- function succeedRequestPermissions(options : RequestPermissionsOptions, res : RequestPermissionsResult) {
- const successCallback = options.success
- if (successCallback != null) {
- successCallback(res)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(res)
- }
- }
- function succeedOpenAppSettings(options : OpenAppSettingsOptions, res : PermissionCheckResult) {
- const successCallback = options.success
- if (successCallback != null) {
- successCallback(res)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(res)
- }
- }
- function failRequestPermission(options : RequestPermissionOptions, err : PermissionFailImpl) {
- const failCallback = options.fail
- if (failCallback != null) {
- failCallback(err)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(err)
- }
- }
- function failRequestPermissions(options : RequestPermissionsOptions, err : PermissionFailImpl) {
- const failCallback = options.fail
- if (failCallback != null) {
- failCallback(err)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(err)
- }
- }
- function failOpenAppSettings(options : OpenAppSettingsOptions, err : PermissionFailImpl) {
- const failCallback = options.fail
- if (failCallback != null) {
- failCallback(err)
- }
- const completeCallback = options.complete
- if (completeCallback != null) {
- completeCallback(err)
- }
- }
- function requestCameraPermission(options : RequestPermissionOptions) {
- AVCaptureDevice.requestAccess(for = AVMediaType.video, completionHandler = (granted : boolean) => {
- DispatchQueue.main.async(execute = (): void => {
- const status : PermissionStatus = granted ? 'granted' : 'denied'
- succeedRequestPermission(options, createRequestResult(options.permission, status))
- })
- })
- }
- function requestPhotoPermission(options : RequestPermissionOptions) {
- PHPhotoLibrary.requestAuthorization((status : PHAuthorizationStatus) => {
- DispatchQueue.main.async(execute = (): void => {
- succeedRequestPermission(options, createRequestResult(options.permission, mapPhotoStatus(status)))
- })
- })
- }
- function requestMicrophonePermission(options : RequestPermissionOptions) {
- AVAudioSession.sharedInstance().requestRecordPermission((granted : boolean) => {
- DispatchQueue.main.async(execute = (): void => {
- const status : PermissionStatus = granted ? 'granted' : 'denied'
- succeedRequestPermission(options, createRequestResult(options.permission, status))
- })
- })
- }
- function requestContactsPermission(options : RequestPermissionOptions) {
- const store = new CNContactStore()
- store.requestAccess(for = CNEntityType.contacts, completionHandler = (granted : boolean, _ : NSError | null) => {
- DispatchQueue.main.async(execute = (): void => {
- const status : PermissionStatus = granted ? 'granted' : 'denied'
- succeedRequestPermission(options, createRequestResult(options.permission, status))
- })
- })
- }
- function ensureLocationManager() : CLLocationManager {
- if (locationManager == null) {
- locationManager = new CLLocationManager()
- }
- return locationManager!
- }
- function requestLocationPermission(options : RequestPermissionOptions) {
- const currentStatus = CLLocationManager.authorizationStatus()
- if (currentStatus != CLAuthorizationStatus.notDetermined) {
- succeedRequestPermission(options, createRequestResult(options.permission, mapLocationStatus(currentStatus)))
- return
- }
- pendingLocationOptions = options
- const manager = ensureLocationManager()
- manager.requestWhenInUseAuthorization()
- scheduleLocationPermissionCheck(0)
- }
- function scheduleLocationPermissionCheck(attempt : Int) {
- const maxAttempts : Int = 60
- DispatchQueue.main.asyncAfter(deadline = DispatchTime.now() + 0.3, execute = (): void => {
- const options = pendingLocationOptions
- if (options == null) {
- return
- }
- const authStatus = CLLocationManager.authorizationStatus()
- if (authStatus == CLAuthorizationStatus.notDetermined) {
- if (attempt >= maxAttempts) {
- pendingLocationOptions = null
- succeedRequestPermission(options, createRequestResult(options.permission, 'denied'))
- return
- }
- scheduleLocationPermissionCheck(attempt + 1)
- return
- }
- pendingLocationOptions = null
- succeedRequestPermission(options, createRequestResult(options.permission, mapLocationStatus(authStatus)))
- })
- }
- function requestBluetoothPermission(options : RequestPermissionOptions) {
- const currentStatus = CBManager.authorization
- if (currentStatus != CBManagerAuthorization.notDetermined) {
- succeedRequestPermission(options, createRequestResult(options.permission, mapBluetoothStatus(currentStatus)))
- return
- }
- // 创建 CBCentralManager 实例会触发系统蓝牙权限弹窗
- const _centralManager = new CBCentralManager()
- DispatchQueue.main.async(execute = (): void => {
- succeedRequestPermission(options, createRequestResult(options.permission, mapBluetoothStatus(CBManager.authorization)))
- })
- }
- export const checkPermissionSync : CheckPermissionSync = function (permission : PermissionKey) : PermissionCheckResult {
- return createCheckResult(permission, resolvePermissionStatus(permission))
- }
- export const checkPermissionsSync : CheckPermissionsSync = function (permissions : PermissionKey[]) : CheckPermissionsResult {
- const results : PermissionCheckResult[] = []
- let allGranted = true
- for (let i = 0; i < permissions.length; i++) {
- const item = checkPermissionSync(permissions[i])
- results.push(item)
- if (!item.granted) {
- allGranted = false
- }
- }
- return {
- results: results,
- allGranted: allGranted,
- }
- }
- export const checkPermission : CheckPermission = function (options : CheckPermissionOptions) {
- succeedCheckPermission(options, checkPermissionSync(options.permission))
- }
- export const checkPermissions : CheckPermissions = function (options : CheckPermissionsOptions) {
- succeedCheckPermissions(options, checkPermissionsSync(options.permissions))
- }
- export const requestPermission : RequestPermission = function (options : RequestPermissionOptions) {
- if (isIosUnsupported(options.permission)) {
- failRequestPermission(options, new PermissionFailImpl(9020003))
- return
- }
- const currentStatus = resolvePermissionStatus(options.permission)
- if (currentStatus == 'granted') {
- succeedRequestPermission(options, createRequestResult(options.permission, 'granted'))
- return
- }
- if (currentStatus == 'restricted') {
- succeedRequestPermission(options, createRequestResult(options.permission, 'restricted'))
- return
- }
- switch (options.permission) {
- case 'camera':
- requestCameraPermission(options)
- break
- case 'photoLibrary':
- case 'storage':
- case 'readStorage':
- requestPhotoPermission(options)
- break
- case 'location':
- case 'coarseLocation':
- requestLocationPermission(options)
- break
- case 'microphone':
- requestMicrophonePermission(options)
- break
- case 'contacts':
- case 'writeContacts':
- requestContactsPermission(options)
- break
- case 'bluetooth':
- requestBluetoothPermission(options)
- break
- default:
- failRequestPermission(options, new PermissionFailImpl(9020003))
- }
- }
- export const requestPermissions : RequestPermissions = function (options : RequestPermissionsOptions) {
- const permissions = options.permissions
- if (permissions == null) {
- const emptyResult : RequestPermissionsResult = {
- results: [],
- allGranted: true,
- }
- succeedRequestPermissions(options, emptyResult)
- return
- }
- const permissionList = permissions!
- if (permissionList.length == 0) {
- const emptyResult : RequestPermissionsResult = {
- results: [],
- allGranted: true,
- }
- succeedRequestPermissions(options, emptyResult)
- return
- }
- let currentIndex = 0
- const collectedResults : PermissionGrantResult[] = []
- function requestNext() {
- if (currentIndex >= permissionList.length) {
- let allGranted = true
- for (let i = 0; i < collectedResults.length; i++) {
- if (!collectedResults[i].granted) {
- allGranted = false
- break
- }
- }
- const finalResult : RequestPermissionsResult = {
- results: collectedResults,
- allGranted: allGranted,
- }
- succeedRequestPermissions(options, finalResult)
- return
- }
- const permission = permissionList[currentIndex]
- currentIndex += 1
- requestPermission({
- permission: permission,
- success: (res : PermissionGrantResult) => {
- collectedResults.push(res)
- requestNext()
- },
- fail: (err : PermissionFailImpl) => {
- collectedResults.push(createRequestResult(permission, 'denied'))
- failRequestPermissions(options, err)
- },
- })
- }
- requestNext()
- }
- export const openAppSettings : OpenAppSettings = function (options : OpenAppSettingsOptions) {
- const settingsUrl = URL(string = UIApplication.openSettingsURLString)
- if (settingsUrl == null) {
- failOpenAppSettings(options, new PermissionFailImpl(9020004))
- return
- }
- DispatchQueue.main.async(execute = (): void => {
- UIApplication.shared.open(settingsUrl!)
- const resultPermission = options.permission != null ? options.permission! : 'camera'
- succeedOpenAppSettings(options, createCheckResult(resultPermission, 'granted'))
- })
- }
- export const getSupportedPermissionsSync : GetSupportedPermissionsSync = function () : GetSupportedPermissionsResult {
- return {
- permissions: SUPPORTED_PERMISSIONS,
- }
- }
- function mapIOSBluetoothState(state : CBManagerState) : string {
- switch (state) {
- case CBManagerState.poweredOn:
- return '已开启'
- case CBManagerState.poweredOff:
- return '已关闭'
- case CBManagerState.unsupported:
- return '不支持'
- case CBManagerState.unauthorized:
- return '未授权'
- case CBManagerState.resetting:
- return '重置中'
- case CBManagerState.unknown:
- return '未知'
- default:
- return '未知'
- }
- }
- export const getBluetoothDemoInfoSync : GetBluetoothDemoInfoSync = function () : BluetoothDemoInfo {
- const manager = new CBCentralManager()
- const state = manager.state
- return {
- available: state != CBManagerState.unsupported,
- enabled: state == CBManagerState.poweredOn,
- stateText: mapIOSBluetoothState(state),
- stateCode: state as number,
- }
- }
|