Skip to content

Commit ec70036

Browse files
authored
Merge pull request #1586 from OneSignal/add/notification_permission_prompting
[Add] Android 13 notification permission prompting
2 parents 3911dfd + 3ec1fe0 commit ec70036

13 files changed

+442
-73
lines changed

Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,6 @@ private void setupSubscriptionSwitch() {
546546
boolean isPermissionEnabled = deviceState != null && deviceState.areNotificationsEnabled();
547547
final boolean isSubscribed = deviceState != null && deviceState.isSubscribed();
548548

549-
subscriptionSwitch.setEnabled(isPermissionEnabled);
550549
subscriptionSwitch.setChecked(isSubscribed);
551550

552551
if (isPermissionEnabled) {
@@ -561,6 +560,8 @@ private void setupSubscriptionSwitch() {
561560

562561
subscriptionSwitch.setOnClickListener(v -> {
563562
OneSignal.disablePush(!subscriptionSwitch.isChecked());
563+
if (subscriptionSwitch.isChecked())
564+
OneSignal.promptForPushNotifications(true);
564565
});
565566
}
566567

OneSignalSDK/onesignal/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<!-- OneSignal SDK has runtime checks Android version. -->
55
<!--<uses-sdk tools:overrideLibrary="android.support.customtabs"/>-->
66

7+
<!-- Required runtime permission to display notifications on Android 13 -->
8+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
9+
710
<!-- Required so the device can access the internet. -->
811
<uses-permission android:name="android.permission.INTERNET" />
912

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2022 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal
29+
30+
import android.app.Activity
31+
import android.app.AlertDialog
32+
33+
object AlertDialogPrepromptForAndroidSettings {
34+
35+
interface Callback {
36+
fun onAccept()
37+
fun onDecline()
38+
}
39+
40+
fun show(
41+
activity: Activity,
42+
titlePrefix: String,
43+
previouslyDeniedPostfix: String,
44+
callback: Callback,
45+
) {
46+
val titleTemplate = activity.getString(R.string.permission_not_available_title)
47+
val title = titleTemplate.format(titlePrefix)
48+
49+
val messageTemplate = activity.getString(R.string.permission_not_available_message)
50+
val message = messageTemplate.format(previouslyDeniedPostfix)
51+
52+
AlertDialog.Builder(activity)
53+
.setTitle(title)
54+
.setMessage(message)
55+
.setPositiveButton(R.string.permission_not_available_open_settings_option) { dialog, which ->
56+
callback.onAccept()
57+
}
58+
.setNegativeButton(android.R.string.no) { dialog, which ->
59+
callback.onDecline()
60+
}
61+
.show()
62+
}
63+
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/GMSLocationController.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ private static class GoogleApiClientListener implements GoogleApiClient.Connecti
124124
@Override
125125
public void onConnected(Bundle bundle) {
126126
synchronized (syncLock) {
127-
PermissionsActivity.answered = false;
128-
129127
if (googleApiClient == null || googleApiClient.realInstance() == null) {
130128
OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "GMSLocationController GoogleApiClientListener onConnected googleApiClient not available, returning");
131129
return;

OneSignalSDK/onesignal/src/main/java/com/onesignal/LocationController.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ static void getLocation(Context context, boolean promptLocation, boolean fallbac
252252
//
253253
// For each case, we call the prompt handlers
254254
if (requestPermission != null && promptLocation) {
255-
PermissionsActivity.startPrompt(fallbackToSettings);
255+
LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission);
256256
} else if (locationCoarsePermission == PackageManager.PERMISSION_GRANTED) {
257257
sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED);
258258
startGetLocation();
@@ -289,7 +289,7 @@ private static void backgroundLocationPermissionLogic(Context context, boolean p
289289
}
290290

291291
if (requestPermission != null && promptLocation) {
292-
PermissionsActivity.startPrompt(fallbackToSettings);
292+
LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission);
293293
} else {
294294
// Fine permission already granted
295295
sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED);
@@ -343,8 +343,6 @@ static boolean isHMSAvailable() {
343343
}
344344

345345
static void fireFailedComplete() {
346-
PermissionsActivity.answered = false;
347-
348346
synchronized (syncLock) {
349347
if (isGooglePlayServicesAvailable())
350348
GMSLocationController.fireFailedComplete();
@@ -413,4 +411,4 @@ protected static class LocationHandlerThread extends HandlerThread {
413411
mHandler = new Handler(getLooper());
414412
}
415413
}
416-
}
414+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2022 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal
29+
30+
object LocationPermissionController : PermissionsActivity.PermissionCallback {
31+
private const val PERMISSION_TYPE = "LOCATION"
32+
33+
init {
34+
PermissionsActivity.registerAsCallback(PERMISSION_TYPE, this)
35+
}
36+
37+
fun prompt(
38+
fallbackToSettings: Boolean,
39+
androidPermissionString: String,
40+
) {
41+
PermissionsActivity.startPrompt(
42+
fallbackToSettings,
43+
PERMISSION_TYPE,
44+
androidPermissionString,
45+
this::class.java
46+
)
47+
}
48+
49+
private fun onResponse(result: OneSignal.PromptActionResult) {
50+
LocationController.sendAndClearPromptHandlers(
51+
true,
52+
result
53+
)
54+
}
55+
56+
override fun onAccept() {
57+
onResponse(OneSignal.PromptActionResult.PERMISSION_GRANTED)
58+
LocationController.startGetLocation()
59+
}
60+
61+
override fun onReject(fallbackToSettings: Boolean) {
62+
onResponse(OneSignal.PromptActionResult.PERMISSION_DENIED)
63+
if (fallbackToSettings) showFallbackAlertDialog()
64+
LocationController.fireFailedComplete()
65+
}
66+
67+
private fun showFallbackAlertDialog() {
68+
val activity = OneSignal.getCurrentActivity() ?: return
69+
AlertDialogPrepromptForAndroidSettings.show(
70+
activity,
71+
activity.getString(R.string.location_permission_name_for_title),
72+
activity.getString(R.string.location_permission_settings_message),
73+
object : AlertDialogPrepromptForAndroidSettings.Callback {
74+
override fun onAccept() {
75+
NavigateToAndroidSettingsForLocation.show(activity)
76+
LocationController.sendAndClearPromptHandlers(
77+
true,
78+
OneSignal.PromptActionResult.PERMISSION_DENIED
79+
)
80+
}
81+
override fun onDecline() {
82+
LocationController.sendAndClearPromptHandlers(
83+
true,
84+
OneSignal.PromptActionResult.PERMISSION_DENIED
85+
)
86+
}
87+
}
88+
)
89+
}
90+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2022 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal
29+
30+
import android.content.Context
31+
import android.content.Intent
32+
import android.net.Uri
33+
import android.provider.Settings
34+
35+
object NavigateToAndroidSettingsForLocation {
36+
fun show(context: Context) {
37+
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
38+
intent.data = Uri.parse("package:" + context.packageName)
39+
context.startActivity(intent)
40+
}
41+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2022 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal
29+
30+
import android.content.Context
31+
import android.content.Intent
32+
33+
object NavigateToAndroidSettingsForNotifications {
34+
fun show(context: Context) {
35+
val intent = Intent()
36+
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
37+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
38+
39+
//for Android 5-7
40+
intent.putExtra("app_package", context.getPackageName())
41+
intent.putExtra("app_uid", context.getApplicationInfo().uid)
42+
43+
// for Android 8 and above
44+
intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName())
45+
context.startActivity(intent)
46+
}
47+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2022 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal
29+
30+
import android.os.Build
31+
32+
object NotificationPermissionController : PermissionsActivity.PermissionCallback {
33+
private const val PERMISSION_TYPE = "NOTIFICATION"
34+
private const val ANDROID_PERMISSION_STRING = "android.permission.POST_NOTIFICATIONS"
35+
36+
init {
37+
PermissionsActivity.registerAsCallback(PERMISSION_TYPE, this)
38+
}
39+
40+
fun prompt(fallbackToSettings: Boolean) {
41+
// TODO: Android 13 Beta 1 reports as 32 instead of 33, update to 33 once Google fixes this
42+
if (Build.VERSION.SDK_INT < 32)
43+
return
44+
45+
PermissionsActivity.startPrompt(
46+
fallbackToSettings,
47+
PERMISSION_TYPE,
48+
ANDROID_PERMISSION_STRING,
49+
this::class.java
50+
)
51+
}
52+
53+
override fun onAccept() {
54+
OneSignal.refreshNotificationPermissionState()
55+
}
56+
57+
override fun onReject(fallbackToSettings: Boolean) {
58+
if (fallbackToSettings) showFallbackAlertDialog()
59+
}
60+
61+
private fun showFallbackAlertDialog() {
62+
val activity = OneSignal.getCurrentActivity() ?: return
63+
AlertDialogPrepromptForAndroidSettings.show(
64+
activity,
65+
activity.getString(R.string.notification_permission_name_for_title),
66+
activity.getString(R.string.notification_permission_settings_message),
67+
object : AlertDialogPrepromptForAndroidSettings.Callback {
68+
override fun onAccept() {
69+
NavigateToAndroidSettingsForNotifications.show(activity)
70+
}
71+
override fun onDecline() {
72+
}
73+
}
74+
)
75+
}
76+
}

0 commit comments

Comments
 (0)