Page MenuHomePhabricator

D7106.diff
No OneTemporary

D7106.diff

diff --git a/native/android/app/src/main/java/app/comm/android/MainActivity.java b/native/android/app/src/main/java/app/comm/android/MainActivity.java
--- a/native/android/app/src/main/java/app/comm/android/MainActivity.java
+++ b/native/android/app/src/main/java/app/comm/android/MainActivity.java
@@ -1,13 +1,18 @@
package app.comm.android;
+import android.Manifest;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import androidx.core.app.ActivityCompat;
+import app.comm.android.notifications.CommAndroidNotifications;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import expo.modules.ReactActivityDelegateWrapper;
-public class MainActivity extends ReactActivity {
+public class MainActivity extends ReactActivity
+ implements ActivityCompat.OnRequestPermissionsResultCallback {
/**
* Returns the name of the main component registered from JavaScript.
@@ -66,4 +71,24 @@
public void invokeDefaultOnBackPressed() {
moveTaskToBack(true);
}
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+
+ for (int permissionIndex = 0; permissionIndex < grantResults.length;
+ permissionIndex++) {
+ String permissionName = permissions[permissionIndex];
+ if (requestCode ==
+ CommAndroidNotifications
+ .COMM_ANDROID_NOTIFICATIONS_REQUEST_CODE &&
+ permissionName.equals(Manifest.permission.POST_NOTIFICATIONS)) {
+ CommAndroidNotifications.resolveNotificationsPermissionRequestPromise(
+ this,
+ grantResults[permissionIndex] == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+ }
}
diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java b/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
@@ -1,10 +1,13 @@
package app.comm.android.notifications;
+import android.Manifest;
+import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
+import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.Promise;
@@ -17,11 +20,21 @@
import com.google.firebase.messaging.RemoteMessage;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import me.leolin.shortcutbadger.ShortcutBadger;
public class CommAndroidNotifications extends ReactContextBaseJavaModule {
+ private static AtomicReference<Promise>
+ notificationsPermissionRequestPromise = new AtomicReference(null);
+
private NotificationManager notificationManager;
+ // Application can request multiple permission, but all
+ // will be resolved via `onRequestPermissionsResult` callback
+ // in MainActivity. Therefore we use custom request codes
+ // to differentiate between different permission requests
+ public static final int COMM_ANDROID_NOTIFICATIONS_REQUEST_CODE = 11111;
+
CommAndroidNotifications(ReactApplicationContext reactContext) {
super(reactContext);
Context context = reactContext.getApplicationContext();
@@ -105,10 +118,7 @@
@ReactMethod
public void hasPermission(Promise promise) {
- boolean enabled =
- NotificationManagerCompat.from(getReactApplicationContext())
- .areNotificationsEnabled();
- promise.resolve(enabled);
+ promise.resolve(hasPermissionInternal());
}
@ReactMethod
@@ -122,6 +132,45 @@
});
}
+ @ReactMethod
+ public void canRequestNotificationsPermissionFromUser(Promise promise) {
+ promise.resolve(canRequestNotificationsPermissionInternal());
+ }
+
+ @ReactMethod
+ public void requestNotificationsPermission(Promise promise) {
+ if (!canRequestNotificationsPermissionInternal()) {
+ // If this method is erroneously called on older Android we should
+ // resolve to the value from notifications settings.
+ promise.resolve(hasPermissionInternal());
+ return;
+ }
+
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ getCurrentActivity(), Manifest.permission.POST_NOTIFICATIONS)) {
+ // Since Android 13 user has to deny notifications permission
+ // twice to remove possibility to show the prompt again. Details:
+ // https://developer.android.com/training/permissions/requesting#handle-denial
+ // On iOS user has to deny notifications permission once to disable the
+ // prompt. We use the method above to mimic iOS behavior on Android.
+ // Details:
+ // https://developer.android.com/topic/performance/vitals/permissions#explain
+ promise.resolve(hasPermissionInternal());
+ return;
+ }
+
+ if (!notificationsPermissionRequestPromise.compareAndSet(null, promise)) {
+ promise.reject(
+ "Programmer error: permission request already in progress.");
+ return;
+ }
+
+ ActivityCompat.requestPermissions(
+ getCurrentActivity(),
+ new String[] {Manifest.permission.POST_NOTIFICATIONS},
+ CommAndroidNotifications.COMM_ANDROID_NOTIFICATIONS_REQUEST_CODE);
+ }
+
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
@@ -129,4 +178,28 @@
"NOTIFICATIONS_IMPORTANCE_HIGH", NotificationManager.IMPORTANCE_HIGH);
return constants;
}
+
+ public static void resolveNotificationsPermissionRequestPromise(
+ Activity mainActivity,
+ boolean isPermissionGranted) {
+ if (notificationsPermissionRequestPromise.get() == null) {
+ return;
+ }
+
+ notificationsPermissionRequestPromise.getAndSet(null).resolve(
+ isPermissionGranted);
+ }
+
+ private boolean hasPermissionInternal() {
+ return NotificationManagerCompat.from(getReactApplicationContext())
+ .areNotificationsEnabled();
+ }
+
+ private boolean canRequestNotificationsPermissionInternal() {
+ // Application has to explicitly request notifications permission from
+ // user since Android 13. Older versions grant them by default. Details:
+ // https://developer.android.com/develop/ui/views/notifications/notification-permission
+ return android.os.Build.VERSION.SDK_INT >=
+ android.os.Build.VERSION_CODES.TIRAMISU;
+ }
}
diff --git a/native/push/android.js b/native/push/android.js
--- a/native/push/android.js
+++ b/native/push/android.js
@@ -18,6 +18,8 @@
+removeAllDeliveredNotifications: () => void,
+hasPermission: () => Promise<boolean>,
+getToken: () => Promise<string>,
+ +requestNotificationsPermission: () => Promise<boolean>,
+ +canRequestNotificationsPermissionFromUser: () => Promise<boolean>,
+NOTIFICATIONS_IMPORTANCE_HIGH: string,
};
export type AndroidForegroundMessage = {

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 22, 12:47 AM (20 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690105
Default Alt Text
D7106.diff (7 KB)

Event Timeline