diff --git a/calf-permissions/src/androidMain/kotlin/com/mohamedrejeb/calf/permissions/PermissionsUtil.android.kt b/calf-permissions/src/androidMain/kotlin/com/mohamedrejeb/calf/permissions/PermissionsUtil.android.kt
index 6750a85..252e0ed 100644
--- a/calf-permissions/src/androidMain/kotlin/com/mohamedrejeb/calf/permissions/PermissionsUtil.android.kt
+++ b/calf-permissions/src/androidMain/kotlin/com/mohamedrejeb/calf/permissions/PermissionsUtil.android.kt
@@ -1,5 +1,6 @@
package com.mohamedrejeb.calf.permissions
+import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
@@ -101,31 +102,37 @@ internal fun Activity.shouldShowRationale(permission: String): Boolean {
internal fun Permission.toAndroidPermission(): String {
return when (this) {
- Permission.Call -> android.Manifest.permission.CALL_PHONE
- Permission.Camera -> android.Manifest.permission.CAMERA
- Permission.Gallery -> android.Manifest.permission.READ_EXTERNAL_STORAGE
- Permission.ReadStorage -> android.Manifest.permission.READ_EXTERNAL_STORAGE
- Permission.WriteStorage -> android.Manifest.permission.WRITE_EXTERNAL_STORAGE
- Permission.FineLocation -> android.Manifest.permission.ACCESS_FINE_LOCATION
- Permission.CoarseLocation -> android.Manifest.permission.ACCESS_COARSE_LOCATION
- Permission.RemoteNotification -> android.Manifest.permission.RECEIVE_BOOT_COMPLETED
- Permission.RecordAudio -> android.Manifest.permission.RECORD_AUDIO
- Permission.BluetoothLe -> android.Manifest.permission.BLUETOOTH
+ Permission.Call -> Manifest.permission.CALL_PHONE
+ Permission.Camera -> Manifest.permission.CAMERA
+ Permission.Gallery -> Manifest.permission.READ_EXTERNAL_STORAGE
+ Permission.ReadStorage -> Manifest.permission.READ_EXTERNAL_STORAGE
+ Permission.WriteStorage -> Manifest.permission.WRITE_EXTERNAL_STORAGE
+ Permission.FineLocation -> Manifest.permission.ACCESS_FINE_LOCATION
+ Permission.CoarseLocation -> Manifest.permission.ACCESS_COARSE_LOCATION
+ Permission.RemoteNotification -> Manifest.permission.RECEIVE_BOOT_COMPLETED
+ Permission.RecordAudio -> Manifest.permission.RECORD_AUDIO
+ Permission.BluetoothLe -> Manifest.permission.BLUETOOTH
Permission.BluetoothScan ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- android.Manifest.permission.BLUETOOTH_SCAN
+ Manifest.permission.BLUETOOTH_SCAN
} else {
""
}
Permission.BluetoothConnect ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- android.Manifest.permission.BLUETOOTH_CONNECT
+ Manifest.permission.BLUETOOTH_CONNECT
} else {
""
}
Permission.BluetoothAdvertise ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- android.Manifest.permission.BLUETOOTH_ADVERTISE
+ Manifest.permission.BLUETOOTH_ADVERTISE
+ } else {
+ ""
+ }
+ Permission.Notification ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.POST_NOTIFICATIONS
} else {
""
}
@@ -150,6 +157,7 @@ internal fun getPermissionFromAndroidPermission(androidPermission: String): Perm
android.Manifest.permission.WRITE_EXTERNAL_STORAGE -> Permission.WriteStorage
android.Manifest.permission.ACCESS_FINE_LOCATION -> Permission.FineLocation
android.Manifest.permission.ACCESS_COARSE_LOCATION -> Permission.CoarseLocation
+ android.Manifest.permission.POST_NOTIFICATIONS -> Permission.Notification
android.Manifest.permission.RECEIVE_BOOT_COMPLETED -> Permission.RemoteNotification
android.Manifest.permission.RECORD_AUDIO -> Permission.RecordAudio
android.Manifest.permission.BLUETOOTH -> Permission.BluetoothLe
diff --git a/calf-permissions/src/commonMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionState.kt b/calf-permissions/src/commonMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionState.kt
index c3e5cfa..29bfb90 100644
--- a/calf-permissions/src/commonMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionState.kt
+++ b/calf-permissions/src/commonMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionState.kt
@@ -76,6 +76,7 @@ enum class Permission {
WriteStorage,
FineLocation,
CoarseLocation,
+ Notification,
RemoteNotification,
RecordAudio,
BluetoothLe,
diff --git a/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionsUtil.kt b/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionsUtil.kt
index a7a660a..7d67f2c 100644
--- a/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionsUtil.kt
+++ b/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionsUtil.kt
@@ -4,6 +4,7 @@ import com.mohamedrejeb.calf.permissions.helper.AVCapturePermissionHelper
import com.mohamedrejeb.calf.permissions.helper.BluetoothPermissionHelper
import com.mohamedrejeb.calf.permissions.helper.GalleryPermissionHelper
import com.mohamedrejeb.calf.permissions.helper.GrantedPermissionHelper
+import com.mohamedrejeb.calf.permissions.helper.LocalNotificationPermissionHelper
import com.mohamedrejeb.calf.permissions.helper.LocationPermissionHelper
import com.mohamedrejeb.calf.permissions.helper.PermissionHelper
import com.mohamedrejeb.calf.permissions.helper.RemoteNotificationPermissionHelper
@@ -16,10 +17,12 @@ internal fun Permission.getPermissionDelegate(): PermissionHelper {
Permission.Gallery -> GalleryPermissionHelper()
Permission.ReadStorage, Permission.WriteStorage, Permission.Call -> GrantedPermissionHelper()
Permission.FineLocation, Permission.CoarseLocation -> LocationPermissionHelper()
+ Permission.Notification -> LocalNotificationPermissionHelper()
Permission.RemoteNotification -> RemoteNotificationPermissionHelper()
Permission.RecordAudio -> AVCapturePermissionHelper(AVMediaTypeAudio)
Permission.BluetoothLe, Permission.BluetoothScan,
Permission.BluetoothConnect, Permission.BluetoothAdvertise,
-> BluetoothPermissionHelper()
+
}
}
diff --git a/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/helper/LocalNotificationPermissionHelper.kt b/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/helper/LocalNotificationPermissionHelper.kt
new file mode 100644
index 0000000..d87914e
--- /dev/null
+++ b/calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/helper/LocalNotificationPermissionHelper.kt
@@ -0,0 +1,52 @@
+package com.mohamedrejeb.calf.permissions.helper
+
+import com.mohamedrejeb.calf.permissions.ExperimentalPermissionsApi
+import com.mohamedrejeb.calf.permissions.PermissionStatus
+import platform.UserNotifications.UNAuthorizationOptionAlert
+import platform.UserNotifications.UNAuthorizationOptionBadge
+import platform.UserNotifications.UNAuthorizationOptionSound
+import platform.UserNotifications.UNAuthorizationStatusAuthorized
+import platform.UserNotifications.UNAuthorizationStatusEphemeral
+import platform.UserNotifications.UNAuthorizationStatusNotDetermined
+import platform.UserNotifications.UNAuthorizationStatusProvisional
+import platform.UserNotifications.UNUserNotificationCenter
+
+internal class LocalNotificationPermissionHelper : PermissionHelper {
+
+ override fun launchPermissionRequest(onPermissionResult: (Boolean) -> Unit) {
+ val notificationCenter = UNUserNotificationCenter.currentNotificationCenter()
+
+ notificationCenter.getNotificationSettingsWithCompletionHandler { settings ->
+ when (settings?.authorizationStatus) {
+ UNAuthorizationStatusAuthorized,
+ UNAuthorizationStatusProvisional,
+ UNAuthorizationStatusEphemeral -> onPermissionResult(true)
+ UNAuthorizationStatusNotDetermined -> {
+ notificationCenter.requestAuthorizationWithOptions(
+ UNAuthorizationOptionSound.or(UNAuthorizationOptionAlert).or(UNAuthorizationOptionBadge)
+ ) { isOk, error ->
+ if (isOk && error == null) {
+ onPermissionResult(true)
+ } else {
+ onPermissionResult(false)
+ }
+ }
+ }
+ else -> onPermissionResult(false)
+ }
+ }
+ }
+
+ @ExperimentalPermissionsApi
+ override fun getPermissionStatus(onPermissionResult: (PermissionStatus) -> Unit) {
+ val notificationCenter = UNUserNotificationCenter.currentNotificationCenter()
+ notificationCenter.getNotificationSettingsWithCompletionHandler { settings ->
+ when (settings?.authorizationStatus) {
+ UNAuthorizationStatusAuthorized,
+ UNAuthorizationStatusProvisional,
+ UNAuthorizationStatusEphemeral -> onPermissionResult(PermissionStatus.Granted)
+ else -> onPermissionResult(PermissionStatus.Denied(false))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/permissions.md b/docs/permissions.md
index 271826b..b411666 100644
--- a/docs/permissions.md
+++ b/docs/permissions.md
@@ -212,3 +212,27 @@ On iOS you need to add the following key to your `Info.plist` file:
```
The string value is the message that will be displayed to the user when the permission is requested.
+
+### Post Notifications Permission
+
+To request the post notifications permission, use `Permission.Notification`.
+
+#### Android
+
+On Android API version 33 and up, you need to add the following permission to your `AndroidManifest.xml` file:
+
+```xml
+
+
+```
+
+#### iOS
+
+On iOS you need to add the following key to your `Info.plist` file:
+
+```xml
+NSUserNotificationsUsageDescription
+Notifications permission is required to show notifications
+```
+
+The string value is the message that will be displayed to the user when the permission is requested.
diff --git a/sample/android/src/main/AndroidManifest.xml b/sample/android/src/main/AndroidManifest.xml
index a4b94ef..106e7e8 100644
--- a/sample/android/src/main/AndroidManifest.xml
+++ b/sample/android/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
+
Unit) {
Box(
modifier =
- Modifier
- .fillMaxSize()
- .background(MaterialTheme.colorScheme.background)
- .windowInsetsPadding(WindowInsets.systemBars)
- .windowInsetsPadding(WindowInsets.ime),
+ Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background)
+ .windowInsetsPadding(WindowInsets.systemBars)
+ .windowInsetsPadding(WindowInsets.ime),
) {
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
- Modifier
- .fillMaxSize()
- .padding(16.dp),
+ Modifier
+ .fillMaxSize()
+ .padding(16.dp),
) {
items(Permission.entries) { permission ->
PermissionItem(permission = permission)
@@ -57,14 +59,14 @@ fun PermissionScreen(navigateBack: () -> Unit) {
navigateBack()
},
colors =
- IconButtonDefaults.iconButtonColors(
- contentColor = MaterialTheme.colorScheme.onBackground,
- containerColor = MaterialTheme.colorScheme.surface,
- ),
+ IconButtonDefaults.iconButtonColors(
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ containerColor = MaterialTheme.colorScheme.surface,
+ ),
modifier =
- Modifier
- .align(Alignment.TopStart)
- .padding(16.dp),
+ Modifier
+ .align(Alignment.TopStart)
+ .padding(16.dp),
) {
Icon(
Icons.Filled.ArrowBackIosNew,
@@ -90,6 +92,13 @@ private fun PermissionItem(permission: Permission) {
text = "Is permission granted: ${permissionState.status.isGranted}",
)
+ LaunchedEffect(permissionState.status) {
+ println("${permission.name}: ${permissionState.status}")
+ if (!permissionState.status.isGranted && permissionState.status.shouldShowRationale) {
+ println("${permission.name}: Show Rationale")
+ }
+ }
+
Button(
onClick = {
println("Click")
diff --git a/sample/ios/Calf/Info.plist b/sample/ios/Calf/Info.plist
index 8b323ee..1142d5e 100644
--- a/sample/ios/Calf/Info.plist
+++ b/sample/ios/Calf/Info.plist
@@ -4,17 +4,19 @@
CADisableMinimumFrameDurationOnPhone
+ NSUserNotificationsUsageDescription
+ Just for tests
NSCameraUsageDescription
- Just for tests
+ Just for tests
NSPhotoLibraryUsageDescription
- Just for tests
+ Just for tests
NSLocationWhenInUseUsageDescription
- Just for tests
- NSMicrophoneUsageDescription
- Just for tests
- NSBluetoothAlwaysUsageDescription
- Just for tests
- NSBluetoothPeripheralUsageDescription
- Just for tests
+ Just for tests
+ NSMicrophoneUsageDescription
+ Just for tests
+ NSBluetoothAlwaysUsageDescription
+ Just for tests
+ NSBluetoothPeripheralUsageDescription
+ Just for tests