iosMain.kmm.essentials.service.IosPermissionService.kt Maven / Gradle / Ivy
package kmm.essentials.service
import kmm.essentials.system.LocationManager
import kmm.essentials.system.mainContinuation
import platform.AVFoundation.*
import platform.CoreLocation.*
import platform.Foundation.NSBundle
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
internal class IosPermissionService : PermissionService {
override fun getPermission(type: PermissionType): Permission = when (type) {
PermissionType.Camera -> CameraPermission()
PermissionType.Microphone -> MicrophonePermission()
PermissionType.ForegroundLocation -> ForegroundLocationPermission()
PermissionType.BackgroundLocation -> BackgroundLocationPermission()
}
}
private class CameraPermission : IosPermission() {
override fun checkStatus(): PermissionStatus {
checkPlist("NSCameraUsageDescription")
return getMediaTypePermissionStatus(AVMediaTypeVideo)
}
override suspend fun request(): PermissionStatus {
if (checkStatus() == PermissionStatus.Granted) {
return PermissionStatus.Granted
}
return requestMediaTypePermission(AVMediaTypeVideo)
}
}
private class MicrophonePermission : IosPermission() {
override fun checkStatus(): PermissionStatus {
checkPlist("NSMicrophoneUsageDescription")
return getMediaTypePermissionStatus(AVMediaTypeAudio)
}
override suspend fun request(): PermissionStatus {
if (checkStatus() == PermissionStatus.Granted) {
return PermissionStatus.Granted
}
return requestMediaTypePermission(AVMediaTypeAudio)
}
}
private class ForegroundLocationPermission : IosPermission() {
override fun checkStatus(): PermissionStatus {
checkPlist("NSLocationWhenInUseUsageDescription")
val serviceEnabled = CLLocationManager.locationServicesEnabled()
if (!serviceEnabled) return PermissionStatus.Disabled
return getLocationManagerStatus(false)
}
override suspend fun request(): PermissionStatus {
if (checkStatus() == PermissionStatus.Granted) {
return PermissionStatus.Granted
}
return requestLocationPermission(false)
}
}
private class BackgroundLocationPermission : IosPermission() {
override fun checkStatus(): PermissionStatus {
checkPlist(
"NSLocationWhenInUseUsageDescription",
"NSLocationAlwaysAndWhenInUseUsageDescription"
)
val serviceEnabled = CLLocationManager.locationServicesEnabled()
if (!serviceEnabled) return PermissionStatus.Disabled
return getLocationManagerStatus(true)
}
override suspend fun request(): PermissionStatus {
if (checkStatus() == PermissionStatus.Granted) {
return PermissionStatus.Granted
}
return requestLocationPermission(true)
}
}
private abstract class IosPermission : Permission {
override fun shouldShowRationale(): Boolean = false
protected fun getMediaTypePermissionStatus(mediaType: AVMediaType): PermissionStatus =
when (AVCaptureDevice.authorizationStatusForMediaType(mediaType)) {
AVAuthorizationStatusAuthorized -> PermissionStatus.Granted
AVAuthorizationStatusDenied -> PermissionStatus.Denied
AVAuthorizationStatusRestricted -> PermissionStatus.Restricted
else -> PermissionStatus.Unknown
}
protected fun getLocationManagerStatus(background: Boolean): PermissionStatus =
when (CLLocationManager.authorizationStatus()) {
kCLAuthorizationStatusAuthorizedAlways -> PermissionStatus.Granted
kCLAuthorizationStatusAuthorizedWhenInUse -> if (background) PermissionStatus.Denied else PermissionStatus.Granted
kCLAuthorizationStatusDenied -> PermissionStatus.Denied
kCLAuthorizationStatusRestricted -> PermissionStatus.Restricted
else -> PermissionStatus.Unknown
}
protected suspend fun requestMediaTypePermission(mediaType: AVMediaType): PermissionStatus =
suspendCoroutine { continuation ->
AVCaptureDevice.requestAccessForMediaType(mediaType, mainContinuation { result ->
continuation.resume(
if (result) PermissionStatus.Granted else PermissionStatus.Denied
)
})
}
protected suspend fun requestLocationPermission(background: Boolean): PermissionStatus {
val locationManager = LocationManager()
suspendCoroutine { continuation ->
if (background) {
locationManager.requestAlwaysAuthorization { continuation.resume(it) }
} else {
locationManager.requestWhenInUseAuthorization { continuation.resume(it) }
}
}
return getLocationManagerStatus(background)
}
protected fun checkPlist(vararg key: String) {
val declaredKeys = NSBundle.mainBundle.infoDictionary?.keys ?: emptySet()
if (!declaredKeys.containsAll(key.toList())) {
error("You need to declare using the permission: `${key.joinToString()}` in your Info.plist")
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy