Skip to content

Commit de4e950

Browse files
authored
Merge pull request #107 from Joe3112/feature/notifications
Add local notification permission
2 parents 3818a28 + 7f350e9 commit de4e950

File tree

8 files changed

+137
-37
lines changed

8 files changed

+137
-37
lines changed

calf-permissions/src/androidMain/kotlin/com/mohamedrejeb/calf/permissions/PermissionsUtil.android.kt

+21-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mohamedrejeb.calf.permissions
22

3+
import android.Manifest
34
import android.app.Activity
45
import android.content.Context
56
import android.content.ContextWrapper
@@ -101,31 +102,37 @@ internal fun Activity.shouldShowRationale(permission: String): Boolean {
101102

102103
internal fun Permission.toAndroidPermission(): String {
103104
return when (this) {
104-
Permission.Call -> android.Manifest.permission.CALL_PHONE
105-
Permission.Camera -> android.Manifest.permission.CAMERA
106-
Permission.Gallery -> android.Manifest.permission.READ_EXTERNAL_STORAGE
107-
Permission.ReadStorage -> android.Manifest.permission.READ_EXTERNAL_STORAGE
108-
Permission.WriteStorage -> android.Manifest.permission.WRITE_EXTERNAL_STORAGE
109-
Permission.FineLocation -> android.Manifest.permission.ACCESS_FINE_LOCATION
110-
Permission.CoarseLocation -> android.Manifest.permission.ACCESS_COARSE_LOCATION
111-
Permission.RemoteNotification -> android.Manifest.permission.RECEIVE_BOOT_COMPLETED
112-
Permission.RecordAudio -> android.Manifest.permission.RECORD_AUDIO
113-
Permission.BluetoothLe -> android.Manifest.permission.BLUETOOTH
105+
Permission.Call -> Manifest.permission.CALL_PHONE
106+
Permission.Camera -> Manifest.permission.CAMERA
107+
Permission.Gallery -> Manifest.permission.READ_EXTERNAL_STORAGE
108+
Permission.ReadStorage -> Manifest.permission.READ_EXTERNAL_STORAGE
109+
Permission.WriteStorage -> Manifest.permission.WRITE_EXTERNAL_STORAGE
110+
Permission.FineLocation -> Manifest.permission.ACCESS_FINE_LOCATION
111+
Permission.CoarseLocation -> Manifest.permission.ACCESS_COARSE_LOCATION
112+
Permission.RemoteNotification -> Manifest.permission.RECEIVE_BOOT_COMPLETED
113+
Permission.RecordAudio -> Manifest.permission.RECORD_AUDIO
114+
Permission.BluetoothLe -> Manifest.permission.BLUETOOTH
114115
Permission.BluetoothScan ->
115116
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
116-
android.Manifest.permission.BLUETOOTH_SCAN
117+
Manifest.permission.BLUETOOTH_SCAN
117118
} else {
118119
""
119120
}
120121
Permission.BluetoothConnect ->
121122
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
122-
android.Manifest.permission.BLUETOOTH_CONNECT
123+
Manifest.permission.BLUETOOTH_CONNECT
123124
} else {
124125
""
125126
}
126127
Permission.BluetoothAdvertise ->
127128
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
128-
android.Manifest.permission.BLUETOOTH_ADVERTISE
129+
Manifest.permission.BLUETOOTH_ADVERTISE
130+
} else {
131+
""
132+
}
133+
Permission.Notification ->
134+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
135+
Manifest.permission.POST_NOTIFICATIONS
129136
} else {
130137
""
131138
}
@@ -150,6 +157,7 @@ internal fun getPermissionFromAndroidPermission(androidPermission: String): Perm
150157
android.Manifest.permission.WRITE_EXTERNAL_STORAGE -> Permission.WriteStorage
151158
android.Manifest.permission.ACCESS_FINE_LOCATION -> Permission.FineLocation
152159
android.Manifest.permission.ACCESS_COARSE_LOCATION -> Permission.CoarseLocation
160+
android.Manifest.permission.POST_NOTIFICATIONS -> Permission.Notification
153161
android.Manifest.permission.RECEIVE_BOOT_COMPLETED -> Permission.RemoteNotification
154162
android.Manifest.permission.RECORD_AUDIO -> Permission.RecordAudio
155163
android.Manifest.permission.BLUETOOTH -> Permission.BluetoothLe

calf-permissions/src/commonMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionState.kt

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ enum class Permission {
7676
WriteStorage,
7777
FineLocation,
7878
CoarseLocation,
79+
Notification,
7980
RemoteNotification,
8081
RecordAudio,
8182
BluetoothLe,

calf-permissions/src/iosMain/kotlin/com.mohamedrejeb.calf/permissions/PermissionsUtil.kt

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.mohamedrejeb.calf.permissions.helper.AVCapturePermissionHelper
44
import com.mohamedrejeb.calf.permissions.helper.BluetoothPermissionHelper
55
import com.mohamedrejeb.calf.permissions.helper.GalleryPermissionHelper
66
import com.mohamedrejeb.calf.permissions.helper.GrantedPermissionHelper
7+
import com.mohamedrejeb.calf.permissions.helper.LocalNotificationPermissionHelper
78
import com.mohamedrejeb.calf.permissions.helper.LocationPermissionHelper
89
import com.mohamedrejeb.calf.permissions.helper.PermissionHelper
910
import com.mohamedrejeb.calf.permissions.helper.RemoteNotificationPermissionHelper
@@ -16,10 +17,12 @@ internal fun Permission.getPermissionDelegate(): PermissionHelper {
1617
Permission.Gallery -> GalleryPermissionHelper()
1718
Permission.ReadStorage, Permission.WriteStorage, Permission.Call -> GrantedPermissionHelper()
1819
Permission.FineLocation, Permission.CoarseLocation -> LocationPermissionHelper()
20+
Permission.Notification -> LocalNotificationPermissionHelper()
1921
Permission.RemoteNotification -> RemoteNotificationPermissionHelper()
2022
Permission.RecordAudio -> AVCapturePermissionHelper(AVMediaTypeAudio)
2123
Permission.BluetoothLe, Permission.BluetoothScan,
2224
Permission.BluetoothConnect, Permission.BluetoothAdvertise,
2325
-> BluetoothPermissionHelper()
26+
2427
}
2528
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.mohamedrejeb.calf.permissions.helper
2+
3+
import com.mohamedrejeb.calf.permissions.ExperimentalPermissionsApi
4+
import com.mohamedrejeb.calf.permissions.PermissionStatus
5+
import platform.UserNotifications.UNAuthorizationOptionAlert
6+
import platform.UserNotifications.UNAuthorizationOptionBadge
7+
import platform.UserNotifications.UNAuthorizationOptionSound
8+
import platform.UserNotifications.UNAuthorizationStatusAuthorized
9+
import platform.UserNotifications.UNAuthorizationStatusEphemeral
10+
import platform.UserNotifications.UNAuthorizationStatusNotDetermined
11+
import platform.UserNotifications.UNAuthorizationStatusProvisional
12+
import platform.UserNotifications.UNUserNotificationCenter
13+
14+
internal class LocalNotificationPermissionHelper : PermissionHelper {
15+
16+
override fun launchPermissionRequest(onPermissionResult: (Boolean) -> Unit) {
17+
val notificationCenter = UNUserNotificationCenter.currentNotificationCenter()
18+
19+
notificationCenter.getNotificationSettingsWithCompletionHandler { settings ->
20+
when (settings?.authorizationStatus) {
21+
UNAuthorizationStatusAuthorized,
22+
UNAuthorizationStatusProvisional,
23+
UNAuthorizationStatusEphemeral -> onPermissionResult(true)
24+
UNAuthorizationStatusNotDetermined -> {
25+
notificationCenter.requestAuthorizationWithOptions(
26+
UNAuthorizationOptionSound.or(UNAuthorizationOptionAlert).or(UNAuthorizationOptionBadge)
27+
) { isOk, error ->
28+
if (isOk && error == null) {
29+
onPermissionResult(true)
30+
} else {
31+
onPermissionResult(false)
32+
}
33+
}
34+
}
35+
else -> onPermissionResult(false)
36+
}
37+
}
38+
}
39+
40+
@ExperimentalPermissionsApi
41+
override fun getPermissionStatus(onPermissionResult: (PermissionStatus) -> Unit) {
42+
val notificationCenter = UNUserNotificationCenter.currentNotificationCenter()
43+
notificationCenter.getNotificationSettingsWithCompletionHandler { settings ->
44+
when (settings?.authorizationStatus) {
45+
UNAuthorizationStatusAuthorized,
46+
UNAuthorizationStatusProvisional,
47+
UNAuthorizationStatusEphemeral -> onPermissionResult(PermissionStatus.Granted)
48+
else -> onPermissionResult(PermissionStatus.Denied(false))
49+
}
50+
}
51+
}
52+
}

docs/permissions.md

+24
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,27 @@ On iOS you need to add the following key to your `Info.plist` file:
212212
```
213213

214214
The string value is the message that will be displayed to the user when the permission is requested.
215+
216+
### Post Notifications Permission
217+
218+
To request the post notifications permission, use `Permission.Notification`.
219+
220+
#### Android
221+
222+
On Android API version 33 and up, you need to add the following permission to your `AndroidManifest.xml` file:
223+
224+
```xml
225+
<!-- For Posting Notifications -->
226+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
227+
```
228+
229+
#### iOS
230+
231+
On iOS you need to add the following key to your `Info.plist` file:
232+
233+
```xml
234+
<key>NSUserNotificationsUsageDescription</key>
235+
<string>Notifications permission is required to show notifications</string>
236+
```
237+
238+
The string value is the message that will be displayed to the user when the permission is requested.

sample/android/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
2222
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2323
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
24+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
2425

2526
<application
2627
android:allowBackup="false"

sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/screens/PermissionsScreen.kt

+24-15
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,32 @@ import androidx.compose.material3.IconButtonDefaults
2222
import androidx.compose.material3.MaterialTheme
2323
import androidx.compose.material3.Text
2424
import androidx.compose.runtime.Composable
25+
import androidx.compose.runtime.LaunchedEffect
2526
import androidx.compose.ui.Alignment
2627
import androidx.compose.ui.Modifier
2728
import androidx.compose.ui.unit.dp
2829
import com.mohamedrejeb.calf.permissions.ExperimentalPermissionsApi
2930
import com.mohamedrejeb.calf.permissions.Permission
3031
import com.mohamedrejeb.calf.permissions.isGranted
3132
import com.mohamedrejeb.calf.permissions.rememberPermissionState
33+
import com.mohamedrejeb.calf.permissions.shouldShowRationale
3234

3335
@Composable
3436
fun PermissionScreen(navigateBack: () -> Unit) {
3537
Box(
3638
modifier =
37-
Modifier
38-
.fillMaxSize()
39-
.background(MaterialTheme.colorScheme.background)
40-
.windowInsetsPadding(WindowInsets.systemBars)
41-
.windowInsetsPadding(WindowInsets.ime),
39+
Modifier
40+
.fillMaxSize()
41+
.background(MaterialTheme.colorScheme.background)
42+
.windowInsetsPadding(WindowInsets.systemBars)
43+
.windowInsetsPadding(WindowInsets.ime),
4244
) {
4345
LazyColumn(
4446
horizontalAlignment = Alignment.CenterHorizontally,
4547
modifier =
46-
Modifier
47-
.fillMaxSize()
48-
.padding(16.dp),
48+
Modifier
49+
.fillMaxSize()
50+
.padding(16.dp),
4951
) {
5052
items(Permission.entries) { permission ->
5153
PermissionItem(permission = permission)
@@ -57,14 +59,14 @@ fun PermissionScreen(navigateBack: () -> Unit) {
5759
navigateBack()
5860
},
5961
colors =
60-
IconButtonDefaults.iconButtonColors(
61-
contentColor = MaterialTheme.colorScheme.onBackground,
62-
containerColor = MaterialTheme.colorScheme.surface,
63-
),
62+
IconButtonDefaults.iconButtonColors(
63+
contentColor = MaterialTheme.colorScheme.onBackground,
64+
containerColor = MaterialTheme.colorScheme.surface,
65+
),
6466
modifier =
65-
Modifier
66-
.align(Alignment.TopStart)
67-
.padding(16.dp),
67+
Modifier
68+
.align(Alignment.TopStart)
69+
.padding(16.dp),
6870
) {
6971
Icon(
7072
Icons.Filled.ArrowBackIosNew,
@@ -90,6 +92,13 @@ private fun PermissionItem(permission: Permission) {
9092
text = "Is permission granted: ${permissionState.status.isGranted}",
9193
)
9294

95+
LaunchedEffect(permissionState.status) {
96+
println("${permission.name}: ${permissionState.status}")
97+
if (!permissionState.status.isGranted && permissionState.status.shouldShowRationale) {
98+
println("${permission.name}: Show Rationale")
99+
}
100+
}
101+
93102
Button(
94103
onClick = {
95104
println("Click")

sample/ios/Calf/Info.plist

+11-9
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44
<dict>
55
<key>CADisableMinimumFrameDurationOnPhone</key>
66
<true/>
7+
<key>NSUserNotificationsUsageDescription</key>
8+
<string>Just for tests</string>
79
<key>NSCameraUsageDescription</key>
8-
<string>Just for tests</string>
10+
<string>Just for tests</string>
911
<key>NSPhotoLibraryUsageDescription</key>
10-
<string>Just for tests</string>
12+
<string>Just for tests</string>
1113
<key>NSLocationWhenInUseUsageDescription</key>
12-
<string>Just for tests</string>
13-
<key>NSMicrophoneUsageDescription</key>
14-
<string>Just for tests</string>
15-
<key>NSBluetoothAlwaysUsageDescription</key>
16-
<string>Just for tests</string>
17-
<key>NSBluetoothPeripheralUsageDescription</key>
18-
<string>Just for tests</string>
14+
<string>Just for tests</string>
15+
<key>NSMicrophoneUsageDescription</key>
16+
<string>Just for tests</string>
17+
<key>NSBluetoothAlwaysUsageDescription</key>
18+
<string>Just for tests</string>
19+
<key>NSBluetoothPeripheralUsageDescription</key>
20+
<string>Just for tests</string>
1921
</dict>
2022
</plist>

0 commit comments

Comments
 (0)