Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Run onNotification() in JS, even when the app isn't running at all. #220

Merged
merged 3 commits into from
Oct 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import com.dieam.reactnativepushnotification.helpers.ApplicationBadgeHelper;
import com.facebook.react.bridge.ActivityEventListener;
Expand All @@ -21,33 +21,30 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class RNPushNotification extends ReactContextBaseJavaModule implements ActivityEventListener {
public static final String LOG_TAG = "RNPushNotification";// all logging should use this tag

private RNPushNotificationHelper mRNPushNotificationHelper;
private final Random mRandomNumberGenerator = new Random(System.currentTimeMillis());
private RNPushNotificationJsDelivery mJsDelivery;

public RNPushNotification(ReactApplicationContext reactContext) {
super(reactContext);

reactContext.addActivityEventListener(this);

Application applicationContext = (Application) reactContext.getApplicationContext();
// The @ReactNative methods use this
mRNPushNotificationHelper = new RNPushNotificationHelper(applicationContext);
// This is used to delivery callbacks to JS
mJsDelivery = new RNPushNotificationJsDelivery(reactContext);

registerNotificationsRegistration();
registerNotificationsReceiveNotification();
registerNotificationsRemoteFetch();
}

@Override
Expand All @@ -62,22 +59,12 @@ public Map<String, Object> getConstants() {
return constants;
}

private void sendEvent(String eventName, Object params) {
ReactContext reactContext = getReactApplicationContext();

if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}

public void onNewIntent(Intent intent) {
if (intent.hasExtra("notification")) {
Bundle bundle = intent.getBundleExtra("notification");
bundle.putBoolean("foreground", false);
intent.putExtra("notification", bundle);
notifyNotification(bundle);
mJsDelivery.notifyNotification(bundle);
}
}

Expand All @@ -91,44 +78,11 @@ public void onReceive(Context context, Intent intent) {
WritableMap params = Arguments.createMap();
params.putString("deviceToken", token);

sendEvent("remoteNotificationsRegistered", params);
mJsDelivery.sendEvent("remoteNotificationsRegistered", params);
}
}, intentFilter);
}

private void registerNotificationsReceiveNotification() {
IntentFilter intentFilter = new IntentFilter(getReactApplicationContext().getPackageName() + ".RNPushNotificationReceiveNotification");
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
notifyNotification(intent.getBundleExtra("notification"));
}
}, intentFilter);
}

private void registerNotificationsRemoteFetch() {
IntentFilter intentFilter = new IntentFilter(getReactApplicationContext().getPackageName() + ".RNPushNotificationRemoteFetch");
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra("notification");
String bundleString = convertJSON(bundle);
WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);
sendEvent("remoteFetch", params);
}
}, intentFilter);
}

private void notifyNotification(Bundle bundle) {
String bundleString = convertJSON(bundle);

WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);

sendEvent("remoteNotificationReceived", params);
}

private void registerNotificationsReceiveNotificationActions(ReadableArray actions) {
IntentFilter intentFilter = new IntentFilter();
// Add filter for each actions.
Expand All @@ -142,7 +96,7 @@ public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra("notification");

// Notify the action.
notifyNotificationAction(bundle);
mJsDelivery.notifyNotificationAction(bundle);

// Dismiss the notification popup.
NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
Expand All @@ -152,32 +106,6 @@ public void onReceive(Context context, Intent intent) {
}, intentFilter);
}

private void notifyNotificationAction(Bundle bundle) {
String bundleString = convertJSON(bundle);

WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);

sendEvent("notificationActionReceived", params);
}

private String convertJSON(Bundle bundle) {
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
json.put(key, JSONObject.wrap(bundle.get(key)));
} else {
json.put(key, bundle.get(key));
}
} catch (JSONException e) {
return null;
}
}
return json.toString();
}

@ReactMethod
public void requestPermissions(String senderID) {
ReactContext reactContext = getReactApplicationContext();
Expand Down Expand Up @@ -217,7 +145,7 @@ public void getInitialNotification(Promise promise) {
Bundle bundle = intent.getBundleExtra("notification");
if (bundle != null) {
bundle.putBoolean("foreground", false);
String bundleString = convertJSON(bundle);
String bundleString = mJsDelivery.convertJSON(bundle);
params.putString("dataJSON", bundleString);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void sendNotification(Bundle bundle) {
}

if (bundle.getString("message") == null) {
Log.e(LOG_TAG, "No message specified for the notification");
Log.e(LOG_TAG, "No message specified for the notification: " + bundle);
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.dieam.reactnativepushnotification.modules;

import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.Set;

import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;

/**
* Created by lambert on 2016/10/09.
*/

public class RNPushNotificationJsDelivery {
private ReactApplicationContext mReactContext;

public RNPushNotificationJsDelivery(ReactApplicationContext reactContext) {
mReactContext = reactContext;
}

void sendEvent(String eventName, Object params) {
if (mReactContext.hasActiveCatalystInstance()) {
mReactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}

void notifyRemoteFetch(Bundle bundle) {
String bundleString = convertJSON(bundle);
WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);
sendEvent("remoteFetch", params);
}

void notifyNotification(Bundle bundle) {
String bundleString = convertJSON(bundle);

WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);

sendEvent("remoteNotificationReceived", params);
}

void notifyNotificationAction(Bundle bundle) {
String bundleString = convertJSON(bundle);

WritableMap params = Arguments.createMap();
params.putString("dataJSON", bundleString);

sendEvent("notificationActionReceived", params);
}

String convertJSON(Bundle bundle) {
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
json.put(key, JSONObject.wrap(bundle.get(key)));
} else {
json.put(key, bundle.get(key));
}
} catch (JSONException e) {
return null;
}
}
return json.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.dieam.reactnativepushnotification.helpers.ApplicationBadgeHelper;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.google.android.gms.gcm.GcmListenerService;

import org.json.JSONObject;

import java.util.List;
import java.util.Random;

import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;

public class RNPushNotificationListenerService extends GcmListenerService {

@Override
public void onMessageReceived(String from, Bundle bundle) {
public void onMessageReceived(String from, final Bundle bundle) {
JSONObject data = getPushData(bundle.getString("data"));
if (data != null) {
if (!bundle.containsKey("message")) {
Expand All @@ -35,7 +38,32 @@ public void onMessageReceived(String from, Bundle bundle) {
}
}

sendNotification(bundle);
// We need to run this on the main thread, as the React code assumes that is true.
// Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
// "Can't create handler inside thread that has not called Looper.prepare()"
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
// Construct and load our normal React JS code bundle
ReactInstanceManager mReactInstanceManager = ((ReactApplication)getApplication()).getReactNativeHost().getReactInstanceManager();
ReactContext context = mReactInstanceManager.getCurrentReactContext();
// If it's constructed, send a notification
if (context != null) {
sendNotification((ReactApplicationContext)context, bundle);
} else {
// Otherwise wait for construction, then send the notification
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
sendNotification((ReactApplicationContext)context, bundle);
}
});
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
// Construct it in the background
mReactInstanceManager.createReactContextInBackground();
}
}
}
});
}

private JSONObject getPushData(String dataString) {
Expand All @@ -46,7 +74,7 @@ private JSONObject getPushData(String dataString) {
}
}

private void sendNotification(Bundle bundle) {
private void sendNotification(ReactApplicationContext context, Bundle bundle) {

// If notification ID is not provided by the user for push notification, generate one at random
if (bundle.getString("id") == null) {
Expand All @@ -56,22 +84,14 @@ private void sendNotification(Bundle bundle) {

Boolean isRunning = isApplicationRunning();

Intent intent = new Intent(this.getPackageName() + ".RNPushNotificationReceiveNotification");
RNPushNotificationJsDelivery jsDelivery = new RNPushNotificationJsDelivery(context);
bundle.putBoolean("foreground", isRunning);
bundle.putBoolean("userInteraction", false);
intent.putExtra("notification", bundle);
sendBroadcast(intent);
jsDelivery.notifyNotification(bundle);

// If contentAvailable is set to true, then send out a remote fetch event
if (bundle.getString("contentAvailable", "false").equalsIgnoreCase("true")) {
Log.d(LOG_TAG, "Received a notification with remote fetch enabled");
Intent remoteFetchIntent = new Intent(this.getPackageName() + ".RNPushNotificationRemoteFetch");
remoteFetchIntent.putExtra("notification", bundle);
sendBroadcast(remoteFetchIntent);
}

if (!isRunning) {
new RNPushNotificationHelper(getApplication()).sendNotification(bundle);
jsDelivery.notifyRemoteFetch(bundle);
}
}

Expand Down
20 changes: 11 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var Notifications = {
onNotification: false,
onRemoteFetch: false,
isLoaded: false,
hasPoppedInitialNotification: false,

isPermissionsRequestPending: false,

Expand Down Expand Up @@ -84,18 +85,19 @@ Notifications.configure = function(options: Object) {
this.callNative( 'addEventListener', [ 'localNotification', this._onNotification ] );
Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null

if ( typeof options.popInitialNotification === 'undefined' ||
options.popInitialNotification === true ) {
this.popInitialNotification(function(firstNotification) {
if ( firstNotification !== null ) {
this._onNotification(firstNotification, true);
}
}.bind(this));
}

this.isLoaded = true;
}

if ( this.hasPoppedInitialNotification === false &&
( options.popInitialNotification === undefined || options.popInitialNotification === true ) ) {
this.popInitialNotification(function(firstNotification) {
if ( firstNotification !== null ) {
this._onNotification(firstNotification, true);
}
}.bind(this));
this.hasPoppedInitialNotification = true;
}

if ( options.requestPermissions !== false ) {
this._requestPermissions();
}
Expand Down