Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Push notification data privacy #2213

Merged
merged 38 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6c05393
[WIP] Notification Service
djorkaeffalexandre Jun 15, 2020
0882844
[WIP] Android push notification privacy
djorkaeffalexandre Jun 16, 2020
5356e58
[WIP] Retry request when it fails (iOS)
djorkaeffalexandre Jun 16, 2020
549f12c
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jun 16, 2020
406ec7b
[WIP] Override notification bundle
djorkaeffalexandre Jun 16, 2020
0a34daa
[CHORE] Remove unnecessary import
djorkaeffalexandre Jun 16, 2020
e389b0f
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jun 17, 2020
738007d
[WIP] Check notification Type (iOS)
djorkaeffalexandre Jun 22, 2020
f1ecc7e
[WIP] Change to notification endpoint
djorkaeffalexandre Jun 23, 2020
54e604b
eof
djorkaeffalexandre Jun 23, 2020
690fa5c
fix unwrap conditional value
djorkaeffalexandre Jun 23, 2020
05c120d
turn run request synchronous
djorkaeffalexandre Jun 24, 2020
872560e
fix bundle info
djorkaeffalexandre Jun 24, 2020
9317795
eof
djorkaeffalexandre Jun 24, 2020
1b1e431
remove extra tab
djorkaeffalexandre Jun 24, 2020
78839b7
undo unnecessary change
djorkaeffalexandre Jun 24, 2020
767ffb9
remove not working code for a while
djorkaeffalexandre Jun 24, 2020
b57aa25
fix notification title
djorkaeffalexandre Jun 24, 2020
a3047f1
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jun 29, 2020
14b8a9d
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jul 21, 2020
5cd7c77
change endpoint and received/sent data
djorkaeffalexandre Jul 21, 2020
c65acbb
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jul 22, 2020
29162ca
message-id-only working properly on android
djorkaeffalexandre Jul 22, 2020
0b69382
notification privacy working on ios
djorkaeffalexandre Jul 22, 2020
13a7f84
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Jul 22, 2020
4a62812
invalidate circleCI yarn cache
djorkaeffalexandre Jul 22, 2020
3be132c
Merge branch 'develop' into new.push-notification
diegolmello Jul 22, 2020
64e08ef
Fix provisioning profiles
diegolmello Jul 23, 2020
43608b8
fix notification service version
djorkaeffalexandre Jul 23, 2020
3506b52
fix unwrap nil
djorkaeffalexandre Jul 23, 2020
467a62b
Merge branch 'develop' into new.push-notification
diegolmello Jul 24, 2020
0804638
compatibility older servers android
djorkaeffalexandre Jul 24, 2020
f7ef355
show received notification when cant fetch content from server
djorkaeffalexandre Jul 24, 2020
3772973
undo some android changes
djorkaeffalexandre Jul 24, 2020
0550fff
prevent group & reply fallback notifications
djorkaeffalexandre Jul 24, 2020
4a22670
dont show more than one fallback notification by server
djorkaeffalexandre Jul 24, 2020
f4ce7cc
Merge branch 'develop' into new.push-notification
diegolmello Jul 27, 2020
265d9c6
Merge branch 'develop' into new.push-notification
diegolmello Jul 27, 2020
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
10 changes: 10 additions & 0 deletions android/app/src/main/java/chat/rocket/reactnative/Callback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package chat.rocket.reactnative;

import android.os.Bundle;
import androidx.annotation.Nullable;

public class Callback {
public void call(@Nullable Bundle bundle) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
import android.os.Build;
import android.os.Bundle;
import android.app.Person;
import androidx.annotation.Nullable;

import com.google.gson.*;
import com.google.gson.Gson;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
Expand All @@ -33,15 +34,18 @@
import java.util.Map;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;

public class CustomPushNotification extends PushNotification {
public static ReactApplicationContext reactApplicationContext;
final NotificationManager notificationManager;

public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
reactApplicationContext = new ReactApplicationContext(context);
notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}

private static Map<String, List<Bundle>> notificationMessages = new HashMap<String, List<Bundle>>();
Expand All @@ -54,29 +58,39 @@ public static void clearMessages(int notId) {

@Override
public void onReceived() throws InvalidNotificationException {
final Bundle bundle = mNotificationProps.asBundle();
Bundle received = mNotificationProps.asBundle();
Ejson receivedEjson = new Gson().fromJson(received.getString("ejson", "{}"), Ejson.class);

if (receivedEjson.notificationType != null && receivedEjson.notificationType.equals("message-id-only")) {
notificationLoad(receivedEjson.serverURL(), receivedEjson.messageId, new Callback() {
@Override
public void call(@Nullable Bundle bundle) {
if (bundle != null) {
mNotificationProps = createProps(bundle);
}
}
});
}

// We should re-read these values since that can be changed by notificationLoad
Bundle bundle = mNotificationProps.asBundle();
Ejson loadedEjson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
String notId = bundle.getString("notId", "1");
String title = bundle.getString("title");

if (notificationMessages.get(notId) == null) {
notificationMessages.put(notId, new ArrayList<Bundle>());
}

Gson gson = new Gson();
Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class);

boolean hasSender = ejson.sender != null;
boolean hasSender = loadedEjson.sender != null;
String title = bundle.getString("title");

bundle.putLong("time", new Date().getTime());
bundle.putString("username", hasSender ? ejson.sender.username : title);
bundle.putString("senderId", hasSender ? ejson.sender._id : "1");
bundle.putString("avatarUri", ejson.getAvatarUri());
bundle.putString("username", hasSender ? loadedEjson.sender.username : title);
bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1");
bundle.putString("avatarUri", loadedEjson.getAvatarUri());

notificationMessages.get(notId).add(bundle);

super.postNotification(Integer.parseInt(notId));

postNotification(Integer.parseInt(notId));
notifyReceivedToJS();
}

Expand All @@ -96,9 +110,11 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
String notId = bundle.getString("notId", "1");
String title = bundle.getString("title");
String message = bundle.getString("message");
Boolean notificationLoaded = bundle.getBoolean("notificationLoaded", false);
Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);

notification
.setContentTitle(title)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent)
.setPriority(Notification.PRIORITY_HIGH)
Expand All @@ -109,10 +125,34 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
notificationColor(notification);
notificationChannel(notification);
notificationIcons(notification, bundle);
notificationStyle(notification, notificationId, bundle);
notificationReply(notification, notificationId, bundle);
notificationDismiss(notification, notificationId);

// if notificationType is null (RC < 3.5) or notificationType is different of message-id-only or notification was loaded successfully
if (ejson.notificationType == null || !ejson.notificationType.equals("message-id-only") || notificationLoaded) {
notificationStyle(notification, notificationId, bundle);
notificationReply(notification, notificationId, bundle);

// message couldn't be loaded from server (Fallback notification)
} else {
Gson gson = new Gson();
// iterate over the current notification ids to dismiss fallback notifications from same server
for (Map.Entry<String, List<Bundle>> bundleList : notificationMessages.entrySet()) {
// iterate over the notifications with this id (same host + rid)
Iterator iterator = bundleList.getValue().iterator();
while (iterator.hasNext()) {
Bundle not = (Bundle) iterator.next();
// get the notification info
Ejson notEjson = gson.fromJson(not.getString("ejson", "{}"), Ejson.class);
// if already has a notification from same server
if (ejson.serverURL().equals(notEjson.serverURL())) {
String id = not.getString("notId");
// cancel this notification
notificationManager.cancel(Integer.parseInt(id));
}
}
}
}

return notification;
}

Expand Down Expand Up @@ -300,4 +340,7 @@ private void notificationDismiss(Notification.Builder notification, int notifica
notification.setDeleteIntent(dismissPendingIntent);
}

private void notificationLoad(String server, String messageId, Callback callback) {
LoadNotification.load(reactApplicationContext, server, messageId, callback);
}
}
2 changes: 2 additions & 0 deletions android/app/src/main/java/chat/rocket/reactnative/Ejson.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class Ejson {
String rid;
String type;
Sender sender;
String messageId;
String notificationType;

private String TOKEN_KEY = "reactnativemeteor_usertoken-";
private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package chat.rocket.reactnative;

import android.os.Bundle;
import android.content.Context;
import android.content.SharedPreferences;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Interceptor;

import com.google.gson.Gson;
import java.io.IOException;

import com.facebook.react.bridge.ReactApplicationContext;

import chat.rocket.userdefaults.RNUserDefaultsModule;

class JsonResponse {
Data data;

class Data {
Notification notification;

class Notification {
String notId;
String title;
String text;
Payload payload;

class Payload {
String host;
String rid;
String type;
Sender sender;
String messageId;
String notificationType;

class Sender {
String username;
String _id;
}
}
}
}
}

public class LoadNotification {
private static int RETRY_COUNT = 0;
private static int[] TIMEOUT = new int[]{ 0, 1, 3, 5, 10 };
private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
private static SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);

public static void load(ReactApplicationContext reactApplicationContext, final String host, final String msgId, Callback callback) {
final OkHttpClient client = new OkHttpClient();
HttpUrl.Builder url = HttpUrl.parse(host.concat("/api/v1/push.get")).newBuilder();

String userId = sharedPreferences.getString(TOKEN_KEY.concat(host), "");
String token = sharedPreferences.getString(TOKEN_KEY.concat(userId), "");

Request request = new Request.Builder()
.header("x-user-id", userId)
.header("x-auth-token", token)
.url(url.addQueryParameter("id", msgId).build())
.build();

runRequest(client, request, callback);
}

private static void runRequest(OkHttpClient client, Request request, Callback callback) {
try {
Thread.sleep(TIMEOUT[RETRY_COUNT] * 1000);

Response response = client.newCall(request).execute();
String body = response.body().string();
if (!response.isSuccessful()) {
throw new Exception("Error");
}

Gson gson = new Gson();
JsonResponse json = gson.fromJson(body, JsonResponse.class);

Bundle bundle = new Bundle();
bundle.putString("notId", json.data.notification.notId);
bundle.putString("title", json.data.notification.title);
bundle.putString("message", json.data.notification.text);
bundle.putString("ejson", gson.toJson(json.data.notification.payload));
bundle.putBoolean("notificationLoaded", true);

callback.call(bundle);

} catch (Exception e) {
if (RETRY_COUNT <= TIMEOUT.length) {
RETRY_COUNT++;
runRequest(client, request, callback);
} else {
callback.call(null);
}
}
}
}
33 changes: 33 additions & 0 deletions ios/NotificationService/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroup</key>
<string>group.ios.chat.rocket</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>NotificationService</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>
10 changes: 10 additions & 0 deletions ios/NotificationService/NotificationService.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.ios.chat.rocket</string>
</array>
</dict>
</plist>
Loading