AzureCommunicationUI/sdk/AzureCommunicationUICalling/Sources/Presentation/Manager/PermissionsManager.swift (123 lines of code) (raw):
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
import Foundation
import AVFoundation
import Combine
protocol PermissionsManagerProtocol {
func resolveStatus(for permission: AppPermission) -> AppPermission.Status
func request(_ permission: AppPermission) -> Future<AppPermission.Status, Never>
}
class PermissionsManager: PermissionsManagerProtocol {
private var audioPermission: AppPermission.Status?
private var cameraPermission: AppPermission.Status?
private let store: Store<AppState, Action>
var cancellables = Set<AnyCancellable>()
init(store: Store<AppState, Action>) {
self.store = store
store.$state
.sink { [weak self] state in
self?.receive(state)
}.store(in: &cancellables)
}
private func receive(_ state: AppState) {
let permissionState = state.permissionState
if audioPermission != permissionState.audioPermission {
audioPermission = permissionState.audioPermission
handle(permission: .audioPermission,
state: permissionState.audioPermission)
}
if cameraPermission != permissionState.cameraPermission {
cameraPermission = permissionState.cameraPermission
handle(permission: .cameraPermission,
state: permissionState.cameraPermission)
}
}
private func handle(permission: AppPermission, state: AppPermission.Status) {
switch state {
case .unknown:
let state = resolveStatus(for: permission)
self.setPermissionState(permission: permission, state: state)
case .requesting:
request(permission)
.sink(receiveValue: { [weak self] state in
guard let self = self else {
return
}
self.setPermissionState(permission: permission, state: state)
}).store(in: &cancellables)
default:
break
}
}
private func setPermissionState(permission: AppPermission, state: AppPermission.Status) {
let setPermission = PermissionAction.generateAction(permission: permission, state: state)
self.store.dispatch(action: .permissionAction(setPermission))
}
func resolveStatus(for permission: AppPermission) -> AppPermission.Status {
switch permission {
case .audioPermission:
return getAudioPermissionStatus()
case .cameraPermission:
return getVideoPermissionStatus()
}
}
func request(_ permission: AppPermission) -> Future<AppPermission.Status, Never> {
switch permission {
case .audioPermission:
return requestAudioPermissions()
case .cameraPermission:
return requestVideoPermissions()
}
}
}
// MARK: - Push Notifications
extension AVAudioSession.RecordPermission {
var map: AppPermission.Status {
switch self {
case .denied:
return .denied
case .granted:
return .granted
case .undetermined:
return .notAsked
@unknown default:
return .notAsked
}
}
}
extension AVAuthorizationStatus {
var map: AppPermission.Status {
switch self {
case .denied,
.restricted:
return .denied
case .authorized:
return .granted
case .notDetermined:
return .notAsked
@unknown default:
return .notAsked
}
}
}
private extension PermissionsManager {
func getAudioPermissionStatus() -> AppPermission.Status {
let audioSession = AVAudioSession.sharedInstance().recordPermission
return audioSession.map
}
func getVideoPermissionStatus() -> AppPermission.Status {
let audioSession = AVCaptureDevice.authorizationStatus(for: .video)
return audioSession.map
}
func requestAudioPermissions() -> Future<AppPermission.Status, Never> {
return Future { promise in
AVAudioSession.sharedInstance().requestRecordPermission { [weak self] _ in
promise(Result.success(self?.getAudioPermissionStatus() ?? .unknown))
}
}
}
func requestVideoPermissions() -> Future<AppPermission.Status, Never> {
return Future { promise in
AVCaptureDevice.requestAccess(for: .video) { [weak self] _ in
promise(Result.success(self?.getVideoPermissionStatus() ?? .unknown))
}
}
}
}