[notifications] Fix some android issues with local notifications

This commit is contained in:
Chris Bianca 2018-03-07 18:29:53 +00:00
parent aa367e7be8
commit 57ffa9bd3e
7 changed files with 119 additions and 57 deletions

View File

@ -1,5 +1,7 @@
package io.invertase.firebase;
import android.app.ActivityManager;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log;
@ -522,4 +524,26 @@ public class Utils {
}
return deconstructedList;
}
public static boolean isAppInForeground(Context context) {
/**
We need to check if app is in foreground otherwise the app will crash.
http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
**/
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance ==
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
}

View File

@ -23,6 +23,7 @@ import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
@ -36,6 +37,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.BundleJSONConverter;
public class RNFirebaseNotificationManager {
@ -44,10 +46,15 @@ public class RNFirebaseNotificationManager {
private static final String TAG = "RNFNotificationManager";
private AlarmManager alarmManager;
private Context context;
private boolean isForeground = false;
private ReactApplicationContext reactContext;
private NotificationManager notificationManager;
private SharedPreferences preferences;
public RNFirebaseNotificationManager(ReactApplicationContext reactContext) {
this(reactContext.getApplicationContext());
this.reactContext = reactContext;
}
public RNFirebaseNotificationManager(Context context) {
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.context = context;
@ -117,11 +124,6 @@ public class RNFirebaseNotificationManager {
}
public void displayScheduledNotification(Bundle notification) {
// Broadcast the notification to the RN Application
Intent scheduledNotificationEvent = new Intent(SCHEDULED_NOTIFICATION_EVENT);
scheduledNotificationEvent.putExtra("notification", notification);
LocalBroadcastManager.getInstance(context).sendBroadcast(scheduledNotificationEvent);
// If this isn't a repeated notification, clear it from the scheduled notifications list
if (!notification.getBundle("schedule").containsKey("repeated")
|| !notification.getBundle("schedule").getBoolean("repeated")) {
@ -129,9 +131,14 @@ public class RNFirebaseNotificationManager {
preferences.edit().remove(notificationId).apply();;
}
// If the app isn't in the foreground, then we display it
// Otherwise, it is up to the JS to decide whether to send the notification
if (!isForeground) {
if (Utils.isAppInForeground(context)) {
// If the app is in the foregound, broadcast the notification to the RN Application
// It is up to the JS to decide whether to display the notification
Intent scheduledNotificationEvent = new Intent(SCHEDULED_NOTIFICATION_EVENT);
scheduledNotificationEvent.putExtra("notification", notification);
LocalBroadcastManager.getInstance(context).sendBroadcast(scheduledNotificationEvent);
} else {
// If the app is in the background, then we display it automatically
displayNotification(notification, null);
}
}
@ -175,10 +182,6 @@ public class RNFirebaseNotificationManager {
scheduleNotification(notificationBundle, promise);
}
public void setIsForeground(boolean isForeground) {
this.isForeground = isForeground;
}
private void cancelAlarm(String notificationId) {
Intent notificationIntent = new Intent(context, RNFirebaseNotificationManager.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
@ -201,7 +204,6 @@ public class RNFirebaseNotificationManager {
String notificationId = notification.getString("notificationId");
NotificationCompat.Builder nb;
// TODO: Change 27 to 'Build.VERSION_CODES.O_MR1' when using appsupport v27
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
nb = new NotificationCompat.Builder(context, channelId);
} else {
@ -343,20 +345,27 @@ public class RNFirebaseNotificationManager {
nb = nb.setTicker(android.getString("ticker"));
}
if (android.containsKey("timeoutAfter")) {
nb = nb.setTimeoutAfter(android.getLong("timeoutAfter"));
Double timeoutAfter = android.getDouble("timeoutAfter");
nb = nb.setTimeoutAfter(timeoutAfter.longValue());
}
if (android.containsKey("usesChronometer")) {
nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
}
if (android.containsKey("vibrate")) {
nb = nb.setVibrate(android.getLongArray("vibrate"));
double[] vibrate = android.getDoubleArray("vibrate");
long[] vibrateArray = new long[vibrate.length];
for (int i = 0; i < vibrate.length; i++) {
vibrateArray[i] = ((Double)vibrate[i]).longValue();
}
nb = nb.setVibrate(vibrateArray);
}
if (android.containsKey("visibility")) {
Double visibility = android.getDouble("visibility");
nb = nb.setVisibility(visibility.intValue());
}
if (android.containsKey("when")) {
nb = nb.setWhen(android.getLong("when"));
Double when = android.getDouble("when");
nb = nb.setWhen(when.longValue());
}
// TODO: Big text / Big picture
@ -393,6 +402,10 @@ public class RNFirebaseNotificationManager {
// Build the notification and send it
Notification builtNotification = nb.build();
notificationManager.notify(notificationId.hashCode(), builtNotification);
if (reactContext != null) {
Utils.sendEvent(reactContext, "notifications_notification_displayed", Arguments.fromBundle(notification));
}
} catch (Exception e) {
if (promise == null) {
Log.e(TAG, "Failed to send notification", e);
@ -525,7 +538,7 @@ public class RNFirebaseNotificationManager {
String notificationId = notification.getString("notificationId");
Bundle schedule = notification.getBundle("schedule");
long fireDate = schedule.getLong("fireDate");
Double fireDate = schedule.getDouble("fireDate");
// Scheduled alarms are cleared on restart
// We store them so that they can be re-scheduled when the phone restarts in RNFirebaseNotificationsRebootReceiver
@ -567,14 +580,14 @@ public class RNFirebaseNotificationManager {
return;
}
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, fireDate, interval, pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, fireDate.longValue(), interval, pendingIntent);
} else {
if (schedule.containsKey("exact")
&& schedule.getBoolean("exact")
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate.longValue(), pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate.longValue(), pendingIntent);
}
}

View File

@ -30,7 +30,7 @@ import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.RNFirebaseMessagingService;
import me.leolin.shortcutbadger.ShortcutBadger;
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener {
private static final String BADGE_FILE = "BadgeCountFile";
private static final String BADGE_KEY = "BadgeCount";
private static final String TAG = "RNFirebaseNotifications";
@ -41,9 +41,8 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
public RNFirebaseNotifications(ReactApplicationContext context) {
super(context);
context.addActivityEventListener(this);
context.addLifecycleEventListener(this);
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
notificationManager = new RNFirebaseNotificationManager(context);
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
@ -86,13 +85,11 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
@ReactMethod
public void getInitialNotification(Promise promise) {
// TODO
if (getCurrentActivity() == null) {
promise.resolve(null);
} else {
WritableMap notificationOpenMap = parseIntentForRemoteNotification(getCurrentActivity().getIntent());
promise.resolve(notificationOpenMap);
WritableMap notificationOpenMap = null;
if (getCurrentActivity() != null) {
notificationOpenMap = parseIntentForNotification(getCurrentActivity().getIntent());
}
promise.resolve(notificationOpenMap);
}
@ReactMethod
@ -173,37 +170,36 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
@Override
public void onNewIntent(Intent intent) {
WritableMap notificationOpenMap = parseIntentForRemoteNotification(intent);
WritableMap notificationOpenMap = parseIntentForNotification(intent);
if (notificationOpenMap != null) {
Log.d(TAG, "onNewIntent called with new remote notification");
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenMap);
}
}
//////////////////////////////////////////////////////////////////////
// End ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Start LifecycleEventListener methods
//////////////////////////////////////////////////////////////////////
@Override
public void onHostResume() {
notificationManager.setIsForeground(true);
private WritableMap parseIntentForNotification(Intent intent) {
WritableMap notificationOpenMap = parseIntentForRemoteNotification(intent);
if (notificationOpenMap == null) {
notificationOpenMap = parseIntentForLocalNotification(intent);
}
return notificationOpenMap;
}
@Override
public void onHostPause() {
notificationManager.setIsForeground(false);
private WritableMap parseIntentForLocalNotification(Intent intent) {
if (intent.getExtras() == null || !intent.hasExtra("notificationId")) {
return null;
}
@Override
public void onHostDestroy() {
// Do nothing
}
//////////////////////////////////////////////////////////////////////
// End LifecycleEventListener methods
//////////////////////////////////////////////////////////////////////
WritableMap notificationMap = Arguments.makeNativeMap(intent.getExtras());
WritableMap notificationOpenMap = Arguments.createMap();
notificationOpenMap.putString("action", intent.getAction());
notificationOpenMap.putMap("notification", notificationMap);
return notificationOpenMap;
}
private WritableMap parseIntentForRemoteNotification(Intent intent) {
// Check if FCM data exists

View File

@ -293,6 +293,16 @@ export default class AndroidNotification {
return this._notification;
}
/**
*
* @param clickAction
* @returns {Notification}
*/
setClickAction(clickAction: string): Notification {
this._clickAction = clickAction;
return this._notification;
}
/**
*
* @param color

View File

@ -38,7 +38,7 @@ type OnNotificationObserver = {
type OnNotificationOpened = NotificationOpen => any;
type OnNotificationOpenedObserver = {
next: OnNotificationOpen,
next: NotificationOpen,
};
const NATIVE_EVENTS = [
@ -158,12 +158,18 @@ export default class Notifications extends ModuleBase {
return getNativeModule(this).getBadge();
}
getInitialNotification(): Promise<Object> {
getInitialNotification(): Promise<NotificationOpen> {
return getNativeModule(this)
.getInitialNotification()
.then(
notification => (notification ? new Notification(notification) : null)
);
.then((notificationOpen: NativeNotificationOpen) => {
if (notificationOpen) {
return {
action: notificationOpen.action,
notification: new Notification(notificationOpen.notification),
};
}
return null;
});
}
/**

View File

@ -30,6 +30,16 @@
</intent-filter>
</service>
<receiver android:name="io.invertase.firebase.notifications.RNFirebaseNotificationReceiver"/>
<receiver android:enabled="true" android:exported="true" android:name="io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

View File

@ -45,16 +45,19 @@ const init = async () => {
notification
.setTitle('Test title')
.setBody('Test body')
.setNotificationId('displayed')
.android.setChannelId('test')
.android.setClickAction('action')
.android.setPriority(RNfirebase.notifications.Android.Priority.Max);
const date = new Date();
date.setMinutes(date.getMinutes() + 1);
setTimeout(() => {
RNfirebase.notifications().displayNotification(notification);
}, 5);
notification.setNotificationId('scheduled');
RNfirebase.notifications().scheduleNotification(notification, {
fireDate: date.getTime(),
});
}, 5);
} catch (error) {
console.error('messaging init error:', error);
}