[NEW] E2E Encryption push (Android) (#2481)
* poc push encryption android * eof * format code * react-native-simple-crypto update * prevent find sub twice * remove storage and use ejson storage * invalidate yarn cache * Bump crypto and fix db path * Fix google-services path Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
8643f17fc0
commit
0195506549
|
@ -170,7 +170,7 @@ jobs:
|
||||||
if [[ $KEYSTORE ]]; then
|
if [[ $KEYSTORE ]]; then
|
||||||
echo $GOOGLE_SERVICES_ANDROID | base64 --decode > google-services.json
|
echo $GOOGLE_SERVICES_ANDROID | base64 --decode > google-services.json
|
||||||
fi
|
fi
|
||||||
working_directory: android/app
|
working_directory: android/app/src/play
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Config variables
|
name: Config variables
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package chat.rocket.reactnative;
|
package chat.rocket.reactnative;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
public class Callback {
|
public class Callback {
|
||||||
|
|
|
@ -14,12 +14,14 @@ import android.graphics.drawable.Icon;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.app.Person;
|
import android.app.Person;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.lang.InterruptedException;
|
import java.lang.InterruptedException;
|
||||||
|
|
||||||
|
@ -84,6 +86,15 @@ public class CustomPushNotification extends PushNotification {
|
||||||
boolean hasSender = loadedEjson.sender != null;
|
boolean hasSender = loadedEjson.sender != null;
|
||||||
String title = bundle.getString("title");
|
String title = bundle.getString("title");
|
||||||
|
|
||||||
|
// If it has a encrypted message
|
||||||
|
if (loadedEjson.msg != null) {
|
||||||
|
// Override message with the decrypted content
|
||||||
|
String decrypted = Encryption.shared.decryptMessage(loadedEjson, reactApplicationContext);
|
||||||
|
if (decrypted != null) {
|
||||||
|
bundle.putString("message", decrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bundle.putLong("time", new Date().getTime());
|
bundle.putLong("time", new Date().getTime());
|
||||||
bundle.putString("username", hasSender ? loadedEjson.sender.username : title);
|
bundle.putString("username", hasSender ? loadedEjson.sender.username : title);
|
||||||
bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1");
|
bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1");
|
||||||
|
@ -114,12 +125,12 @@ public class CustomPushNotification extends PushNotification {
|
||||||
Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
|
Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
|
||||||
|
|
||||||
notification
|
notification
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setContentText(message)
|
.setContentText(message)
|
||||||
.setContentIntent(intent)
|
.setContentIntent(intent)
|
||||||
.setPriority(Notification.PRIORITY_HIGH)
|
.setPriority(Notification.PRIORITY_HIGH)
|
||||||
.setDefaults(Notification.DEFAULT_ALL)
|
.setDefaults(Notification.DEFAULT_ALL)
|
||||||
.setAutoCancel(true);
|
.setAutoCancel(true);
|
||||||
|
|
||||||
Integer notificationId = Integer.parseInt(notId);
|
Integer notificationId = Integer.parseInt(notId);
|
||||||
notificationColor(notification);
|
notificationColor(notification);
|
||||||
|
@ -132,7 +143,7 @@ public class CustomPushNotification extends PushNotification {
|
||||||
notificationStyle(notification, notificationId, bundle);
|
notificationStyle(notification, notificationId, bundle);
|
||||||
notificationReply(notification, notificationId, bundle);
|
notificationReply(notification, notificationId, bundle);
|
||||||
|
|
||||||
// message couldn't be loaded from server (Fallback notification)
|
// message couldn't be loaded from server (Fallback notification)
|
||||||
} else {
|
} else {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
// iterate over the current notification ids to dismiss fallback notifications from same server
|
// iterate over the current notification ids to dismiss fallback notifications from same server
|
||||||
|
@ -163,11 +174,11 @@ public class CustomPushNotification extends PushNotification {
|
||||||
private Bitmap getAvatar(String uri) {
|
private Bitmap getAvatar(String uri) {
|
||||||
try {
|
try {
|
||||||
return Glide.with(mContext)
|
return Glide.with(mContext)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.apply(RequestOptions.bitmapTransform(new RoundedCorners(10)))
|
.apply(RequestOptions.bitmapTransform(new RoundedCorners(10)))
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.submit(100, 100)
|
.submit(100, 100)
|
||||||
.get();
|
.get();
|
||||||
} catch (final ExecutionException | InterruptedException e) {
|
} catch (final ExecutionException | InterruptedException e) {
|
||||||
return largeIcon();
|
return largeIcon();
|
||||||
}
|
}
|
||||||
|
@ -203,8 +214,8 @@ public class CustomPushNotification extends PushNotification {
|
||||||
String CHANNEL_NAME = "All";
|
String CHANNEL_NAME = "All";
|
||||||
|
|
||||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
||||||
CHANNEL_NAME,
|
CHANNEL_NAME,
|
||||||
NotificationManager.IMPORTANCE_DEFAULT);
|
NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
|
||||||
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
@ -253,9 +264,9 @@ public class CustomPushNotification extends PushNotification {
|
||||||
messageStyle = new Notification.MessagingStyle("");
|
messageStyle = new Notification.MessagingStyle("");
|
||||||
} else {
|
} else {
|
||||||
Person sender = new Person.Builder()
|
Person sender = new Person.Builder()
|
||||||
.setKey("")
|
.setKey("")
|
||||||
.setName("")
|
.setName("")
|
||||||
.build();
|
.build();
|
||||||
messageStyle = new Notification.MessagingStyle(sender);
|
messageStyle = new Notification.MessagingStyle(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,9 +290,14 @@ public class CustomPushNotification extends PushNotification {
|
||||||
} else {
|
} else {
|
||||||
Bitmap avatar = getAvatar(avatarUri);
|
Bitmap avatar = getAvatar(avatarUri);
|
||||||
|
|
||||||
|
String name = username;
|
||||||
|
if (ejson.senderName != null) {
|
||||||
|
name = ejson.senderName;
|
||||||
|
}
|
||||||
|
|
||||||
Person.Builder sender = new Person.Builder()
|
Person.Builder sender = new Person.Builder()
|
||||||
.setKey(senderId)
|
.setKey(senderId)
|
||||||
.setName(username);
|
.setName(name);
|
||||||
|
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
sender.setIcon(Icon.createWithBitmap(avatar));
|
sender.setIcon(Icon.createWithBitmap(avatar));
|
||||||
|
@ -317,18 +333,18 @@ public class CustomPushNotification extends PushNotification {
|
||||||
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
|
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
|
||||||
.setLabel(label)
|
.setLabel(label)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
CharSequence title = label;
|
CharSequence title = label;
|
||||||
Notification.Action replyAction = new Notification.Action.Builder(smallIconResId, title, replyPendingIntent)
|
Notification.Action replyAction = new Notification.Action.Builder(smallIconResId, title, replyPendingIntent)
|
||||||
.addRemoteInput(remoteInput)
|
.addRemoteInput(remoteInput)
|
||||||
.setAllowGeneratedReplies(true)
|
.setAllowGeneratedReplies(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
notification
|
notification
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.addAction(replyAction);
|
.addAction(replyAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notificationDismiss(Notification.Builder notification, int notificationId) {
|
private void notificationDismiss(Notification.Builder notification, int notificationId) {
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
public class DismissNotification extends BroadcastReceiver {
|
public class DismissNotification extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
int notId = intent.getExtras().getInt(CustomPushNotification.NOTIFICATION_ID);
|
int notId = intent.getExtras().getInt(CustomPushNotification.NOTIFICATION_ID);
|
||||||
CustomPushNotification.clearMessages(notId);
|
CustomPushNotification.clearMessages(notId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ public class Ejson {
|
||||||
Sender sender;
|
Sender sender;
|
||||||
String messageId;
|
String messageId;
|
||||||
String notificationType;
|
String notificationType;
|
||||||
|
String senderName;
|
||||||
|
String msg;
|
||||||
|
|
||||||
private MMKV mmkv;
|
private MMKV mmkv;
|
||||||
|
|
||||||
|
@ -82,6 +84,14 @@ public class Ejson {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String privateKey() {
|
||||||
|
String serverURL = serverURL();
|
||||||
|
if (mmkv != null && serverURL != null) {
|
||||||
|
return mmkv.decodeString(serverURL.concat("-RC_E2E_PRIVATE_KEY"));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public String serverURL() {
|
public String serverURL() {
|
||||||
String url = this.host;
|
String url = this.host;
|
||||||
if (url != null && url.endsWith("/")) {
|
if (url != null && url.endsWith("/")) {
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
package chat.rocket.reactnative;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.pedrouid.crypto.RSA;
|
||||||
|
import com.pedrouid.crypto.RCTAes;
|
||||||
|
import com.pedrouid.crypto.RCTRsaUtils;
|
||||||
|
import com.pedrouid.crypto.Util;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Promise;
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
|
||||||
|
import com.nozbe.watermelondb.Database;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
class Message {
|
||||||
|
String _id;
|
||||||
|
String userId;
|
||||||
|
String text;
|
||||||
|
|
||||||
|
Message(String id, String userId, String text) {
|
||||||
|
this._id = id;
|
||||||
|
this.userId = userId;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrivateKey {
|
||||||
|
String d;
|
||||||
|
String dp;
|
||||||
|
String dq;
|
||||||
|
String e;
|
||||||
|
String n;
|
||||||
|
String p;
|
||||||
|
String q;
|
||||||
|
String qi;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomKey {
|
||||||
|
String k;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Room {
|
||||||
|
String e2eKey;
|
||||||
|
Boolean encrypted;
|
||||||
|
|
||||||
|
Room(String e2eKey, Boolean encrypted) {
|
||||||
|
this.e2eKey = e2eKey;
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Encryption {
|
||||||
|
private Gson gson = new Gson();
|
||||||
|
private String E2ERoomKey;
|
||||||
|
private String keyId;
|
||||||
|
|
||||||
|
public static Encryption shared = new Encryption();
|
||||||
|
private ReactApplicationContext reactContext;
|
||||||
|
|
||||||
|
public Room readRoom(final Ejson ejson) {
|
||||||
|
Database database = new Database(ejson.serverURL().replace("https://", "") + "-experimental.db", reactContext);
|
||||||
|
String[] query = {ejson.rid};
|
||||||
|
Cursor cursor = database.rawQuery("select * from subscriptions where id == ? limit 1", query);
|
||||||
|
|
||||||
|
// Room not found
|
||||||
|
if (cursor.getCount() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.moveToFirst();
|
||||||
|
String e2eKey = cursor.getString(cursor.getColumnIndex("e2e_key"));
|
||||||
|
Boolean encrypted = cursor.getInt(cursor.getColumnIndex("encrypted")) > 0;
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
return new Room(e2eKey, encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String readUserKey(final Ejson ejson) throws Exception {
|
||||||
|
String privateKey = ejson.privateKey();
|
||||||
|
if (privateKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKey privKey = gson.fromJson(privateKey, PrivateKey.class);
|
||||||
|
|
||||||
|
WritableMap jwk = Arguments.createMap();
|
||||||
|
jwk.putString("n", privKey.n);
|
||||||
|
jwk.putString("e", privKey.e);
|
||||||
|
jwk.putString("d", privKey.d);
|
||||||
|
jwk.putString("p", privKey.p);
|
||||||
|
jwk.putString("q", privKey.q);
|
||||||
|
jwk.putString("dp", privKey.dp);
|
||||||
|
jwk.putString("dq", privKey.dq);
|
||||||
|
jwk.putString("qi", privKey.qi);
|
||||||
|
|
||||||
|
return new RCTRsaUtils().jwkToPrivatePkcs1(jwk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String decryptRoomKey(final String e2eKey, final Ejson ejson) throws Exception {
|
||||||
|
String key = e2eKey.substring(12, e2eKey.length());
|
||||||
|
keyId = e2eKey.substring(0, 12);
|
||||||
|
|
||||||
|
String userKey = readUserKey(ejson);
|
||||||
|
if (userKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSA rsa = new RSA();
|
||||||
|
rsa.setPrivateKey(userKey);
|
||||||
|
String decrypted = rsa.decrypt(key);
|
||||||
|
|
||||||
|
RoomKey roomKey = gson.fromJson(decrypted, RoomKey.class);
|
||||||
|
byte[] decoded = Base64.decode(roomKey.k, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE);
|
||||||
|
|
||||||
|
return Util.bytesToHex(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String decryptMessage(final Ejson ejson, final ReactApplicationContext reactContext) {
|
||||||
|
try {
|
||||||
|
this.reactContext = reactContext;
|
||||||
|
|
||||||
|
Room room = readRoom(ejson);
|
||||||
|
if (room == null || room.e2eKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String e2eKey = decryptRoomKey(room.e2eKey, ejson);
|
||||||
|
if (e2eKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = ejson.msg;
|
||||||
|
String msg = message.substring(12, message.length());
|
||||||
|
byte[] msgData = Base64.decode(msg, Base64.NO_WRAP);
|
||||||
|
|
||||||
|
String b64 = Base64.encodeToString(Arrays.copyOfRange(msgData, 16, msgData.length), Base64.DEFAULT);
|
||||||
|
|
||||||
|
String decrypted = RCTAes.decrypt(b64, e2eKey, Util.bytesToHex(Arrays.copyOfRange(msgData, 0, 16)));
|
||||||
|
byte[] data = Base64.decode(decrypted, Base64.NO_WRAP);
|
||||||
|
Message m = gson.fromJson(new String(data, "UTF-8"), Message.class);
|
||||||
|
|
||||||
|
return m.text;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("[ROCKETCHAT][ENCRYPTION]", Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encryptMessage(final String message, final String id, final Ejson ejson) {
|
||||||
|
try {
|
||||||
|
Room room = readRoom(ejson);
|
||||||
|
if (room == null || !room.encrypted || room.e2eKey == null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
String e2eKey = decryptRoomKey(room.e2eKey, ejson);
|
||||||
|
if (e2eKey == null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message m = new Message(id, ejson.userId(), message);
|
||||||
|
String cypher = gson.toJson(m);
|
||||||
|
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
byte[] bytes = new byte[16];
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
|
||||||
|
String encrypted = RCTAes.encrypt(Base64.encodeToString(cypher.getBytes("UTF-8"), Base64.NO_WRAP), e2eKey, Util.bytesToHex(bytes));
|
||||||
|
byte[] data = Base64.decode(encrypted, Base64.NO_WRAP);
|
||||||
|
|
||||||
|
return keyId + Base64.encodeToString(concat(bytes, data), Base64.NO_WRAP);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("[ROCKETCHAT][ENCRYPTION]", Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] concat(byte[]... arrays) {
|
||||||
|
// Determine the length of the result array
|
||||||
|
int totalLength = 0;
|
||||||
|
for (int i = 0; i < arrays.length; i++) {
|
||||||
|
totalLength += arrays[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the result array
|
||||||
|
byte[] result = new byte[totalLength];
|
||||||
|
|
||||||
|
// copy the source arrays into the result array
|
||||||
|
int currentIndex = 0;
|
||||||
|
for (int i = 0; i < arrays.length; i++) {
|
||||||
|
System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
|
||||||
|
currentIndex += arrays[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,89 +11,90 @@ import okhttp3.Response;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
|
||||||
class JsonResponse {
|
class JsonResponse {
|
||||||
Data data;
|
Data data;
|
||||||
|
|
||||||
class Data {
|
class Data {
|
||||||
Notification notification;
|
Notification notification;
|
||||||
|
|
||||||
class Notification {
|
class Notification {
|
||||||
String notId;
|
String notId;
|
||||||
String title;
|
String title;
|
||||||
String text;
|
String text;
|
||||||
Payload payload;
|
Payload payload;
|
||||||
|
|
||||||
class Payload {
|
class Payload {
|
||||||
String host;
|
String host;
|
||||||
String rid;
|
String rid;
|
||||||
String type;
|
String type;
|
||||||
Sender sender;
|
Sender sender;
|
||||||
String messageId;
|
String messageId;
|
||||||
String notificationType;
|
String notificationType;
|
||||||
String name;
|
String name;
|
||||||
String messageType;
|
String messageType;
|
||||||
|
|
||||||
class Sender {
|
class Sender {
|
||||||
String _id;
|
String _id;
|
||||||
String username;
|
String username;
|
||||||
String name;
|
String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoadNotification {
|
public class LoadNotification {
|
||||||
private static int RETRY_COUNT = 0;
|
private static int RETRY_COUNT = 0;
|
||||||
private static int[] TIMEOUT = new int[]{ 0, 1, 3, 5, 10 };
|
private static int[] TIMEOUT = new int[]{0, 1, 3, 5, 10};
|
||||||
private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
|
private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
|
||||||
|
|
||||||
public static void load(ReactApplicationContext reactApplicationContext, final Ejson ejson, Callback callback) {
|
public static void load(ReactApplicationContext reactApplicationContext, final Ejson ejson, Callback callback) {
|
||||||
final OkHttpClient client = new OkHttpClient();
|
final OkHttpClient client = new OkHttpClient();
|
||||||
HttpUrl.Builder url = HttpUrl.parse(ejson.serverURL().concat("/api/v1/push.get")).newBuilder();
|
HttpUrl.Builder url = HttpUrl.parse(ejson.serverURL().concat("/api/v1/push.get")).newBuilder();
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.header("x-user-id", ejson.userId())
|
.header("x-user-id", ejson.userId())
|
||||||
.header("x-auth-token", ejson.token())
|
.header("x-auth-token", ejson.token())
|
||||||
.url(url.addQueryParameter("id", ejson.messageId).build())
|
.url(url.addQueryParameter("id", ejson.messageId).build())
|
||||||
.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);
|
runRequest(client, request, callback);
|
||||||
} else {
|
|
||||||
callback.call(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -64,7 +66,7 @@ public class ReplyBroadcast extends BroadcastReceiver {
|
||||||
final OkHttpClient client = new OkHttpClient();
|
final OkHttpClient client = new OkHttpClient();
|
||||||
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
|
||||||
String json = buildMessage(rid, message.toString());
|
String json = buildMessage(rid, message.toString(), ejson);
|
||||||
|
|
||||||
CustomPushNotification.clearMessages(notId);
|
CustomPushNotification.clearMessages(notId);
|
||||||
|
|
||||||
|
@ -101,25 +103,32 @@ public class ReplyBroadcast extends BroadcastReceiver {
|
||||||
int count = 17;
|
int count = 17;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
while (count-- != 0) {
|
while (count-- != 0) {
|
||||||
int character = (int)(Math.random()*ALPHA_NUMERIC_STRING.length());
|
int character = (int) (Math.random() * ALPHA_NUMERIC_STRING.length());
|
||||||
builder.append(ALPHA_NUMERIC_STRING.charAt(character));
|
builder.append(ALPHA_NUMERIC_STRING.charAt(character));
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String buildMessage(String rid, String message) {
|
protected String buildMessage(String rid, String message, Ejson ejson) {
|
||||||
Gson gsonBuilder = new GsonBuilder().create();
|
Gson gsonBuilder = new GsonBuilder().create();
|
||||||
|
|
||||||
|
String id = getMessageId();
|
||||||
|
|
||||||
|
String msg = Encryption.shared.encryptMessage(message, id, ejson);
|
||||||
|
|
||||||
Map msgMap = new HashMap();
|
Map msgMap = new HashMap();
|
||||||
msgMap.put("_id", getMessageId());
|
msgMap.put("_id", id);
|
||||||
msgMap.put("rid", rid);
|
msgMap.put("rid", rid);
|
||||||
msgMap.put("msg", message);
|
msgMap.put("msg", msg);
|
||||||
|
if (msg != message) {
|
||||||
|
msgMap.put("t", "e2e");
|
||||||
|
}
|
||||||
msgMap.put("tmid", null);
|
msgMap.put("tmid", null);
|
||||||
|
|
||||||
Map msg = new HashMap();
|
Map m = new HashMap();
|
||||||
msg.put("message", msgMap);
|
m.put("message", msgMap);
|
||||||
|
|
||||||
String json = gsonBuilder.toJson(msg);
|
String json = gsonBuilder.toJson(m);
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codler/react-native-keyboard-aware-scroll-view": "^1.0.1",
|
"@codler/react-native-keyboard-aware-scroll-view": "^1.0.1",
|
||||||
"@nozbe/watermelondb": "^0.19.0",
|
"@nozbe/watermelondb": "0.19.0",
|
||||||
"@react-native-community/art": "^1.2.0",
|
"@react-native-community/art": "^1.2.0",
|
||||||
"@react-native-community/async-storage": "1.11.0",
|
"@react-native-community/async-storage": "1.11.0",
|
||||||
"@react-native-community/cameraroll": "4.0.0",
|
"@react-native-community/cameraroll": "4.0.0",
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
"react-native-keycommands": "2.0.3",
|
"react-native-keycommands": "2.0.3",
|
||||||
"react-native-localize": "1.4.0",
|
"react-native-localize": "1.4.0",
|
||||||
"react-native-mime-types": "2.3.0",
|
"react-native-mime-types": "2.3.0",
|
||||||
"react-native-mmkv-storage": "^0.3.5",
|
"react-native-mmkv-storage": "0.3.5",
|
||||||
"react-native-modal": "11.5.6",
|
"react-native-modal": "11.5.6",
|
||||||
"react-native-navigation-bar-color": "2.0.1",
|
"react-native-navigation-bar-color": "2.0.1",
|
||||||
"react-native-notifications": "2.1.7",
|
"react-native-notifications": "2.1.7",
|
||||||
|
|
|
@ -1,3 +1,25 @@
|
||||||
|
diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt
|
||||||
|
index 2217222..5b2eb73 100644
|
||||||
|
--- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt
|
||||||
|
+++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt
|
||||||
|
@@ -5,7 +5,7 @@ import android.database.Cursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
-class Database(private val name: String, private val context: Context) {
|
||||||
|
+public class Database(private val name: String, private val context: Context) {
|
||||||
|
|
||||||
|
private val db: SQLiteDatabase by lazy {
|
||||||
|
SQLiteDatabase.openOrCreateDatabase(
|
||||||
|
@@ -41,7 +41,7 @@ class Database(private val name: String, private val context: Context) {
|
||||||
|
|
||||||
|
fun delete(query: SQL, args: QueryArgs) = db.execSQL(query, args)
|
||||||
|
|
||||||
|
- fun rawQuery(query: SQL, args: RawQueryArgs = emptyArray()): Cursor = db.rawQuery(query, args)
|
||||||
|
+ public fun rawQuery(query: SQL, args: RawQueryArgs = emptyArray()): Cursor = db.rawQuery(query, args)
|
||||||
|
|
||||||
|
fun count(query: SQL, args: RawQueryArgs = emptyArray()): Int =
|
||||||
|
rawQuery(query, args).use {
|
||||||
diff --git a/node_modules/@nozbe/watermelondb/decorators/date/index.js b/node_modules/@nozbe/watermelondb/decorators/date/index.js
|
diff --git a/node_modules/@nozbe/watermelondb/decorators/date/index.js b/node_modules/@nozbe/watermelondb/decorators/date/index.js
|
||||||
index 65690af..ce71aa0 100644
|
index 65690af..ce71aa0 100644
|
||||||
--- a/node_modules/@nozbe/watermelondb/decorators/date/index.js
|
--- a/node_modules/@nozbe/watermelondb/decorators/date/index.js
|
||||||
|
|
|
@ -1922,7 +1922,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@nozbe/sqlite/-/sqlite-3.31.1.tgz#ffd394ad7c188c6b73f89fd6e1ccb849a1b96dba"
|
resolved "https://registry.yarnpkg.com/@nozbe/sqlite/-/sqlite-3.31.1.tgz#ffd394ad7c188c6b73f89fd6e1ccb849a1b96dba"
|
||||||
integrity sha512-z5+GdcHZl9OQ1g0pnygORAnwCYUlYw/gQxdW/8rS0HxD2Gnn/k3DBQOvqQIH4Z3Z3KWVMbGUYhcH1v4SqTAdwg==
|
integrity sha512-z5+GdcHZl9OQ1g0pnygORAnwCYUlYw/gQxdW/8rS0HxD2Gnn/k3DBQOvqQIH4Z3Z3KWVMbGUYhcH1v4SqTAdwg==
|
||||||
|
|
||||||
"@nozbe/watermelondb@^0.19.0":
|
"@nozbe/watermelondb@0.19.0":
|
||||||
version "0.19.0"
|
version "0.19.0"
|
||||||
resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.19.0.tgz#70dae4bfca9dde8d61819a6092846e178a50e2f7"
|
resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.19.0.tgz#70dae4bfca9dde8d61819a6092846e178a50e2f7"
|
||||||
integrity sha512-x7sclLu/4RDmLzANIYQioKjWdmoIxgqYw7OJnS7UtWEtJGn28l5Z69SWojll/RM0X2KOEem0BIcjqM46CA53GA==
|
integrity sha512-x7sclLu/4RDmLzANIYQioKjWdmoIxgqYw7OJnS7UtWEtJGn28l5Z69SWojll/RM0X2KOEem0BIcjqM46CA53GA==
|
||||||
|
@ -12848,7 +12848,7 @@ react-native-mime-types@2.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "~1.37.0"
|
mime-db "~1.37.0"
|
||||||
|
|
||||||
react-native-mmkv-storage@^0.3.5:
|
react-native-mmkv-storage@0.3.5:
|
||||||
version "0.3.5"
|
version "0.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.3.5.tgz#9c2f064c0efdaf960e9646c68dc6ae7f11640fa5"
|
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.3.5.tgz#9c2f064c0efdaf960e9646c68dc6ae7f11640fa5"
|
||||||
integrity sha512-xp0E55Qdi81k8CTeq3PUrXGwT2tMmfNfmYvZ7Emq9qWpvg3ko1/M6B1kXEXOgEou/hgqB503TGcsR/mpN5HSMA==
|
integrity sha512-xp0E55Qdi81k8CTeq3PUrXGwT2tMmfNfmYvZ7Emq9qWpvg3ko1/M6B1kXEXOgEou/hgqB503TGcsR/mpN5HSMA==
|
||||||
|
@ -12958,8 +12958,8 @@ react-native-scrollable-tab-view@^1.0.0:
|
||||||
react-timer-mixin "^0.13.3"
|
react-timer-mixin "^0.13.3"
|
||||||
|
|
||||||
react-native-simple-crypto@RocketChat/react-native-simple-crypto:
|
react-native-simple-crypto@RocketChat/react-native-simple-crypto:
|
||||||
version "0.3.1"
|
version "0.4.0"
|
||||||
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/b3deaf18e83cd4acc418fe65adb2d837164c2a20"
|
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/21d36ccc8c75771428239f9dcca19afe0c95e95f"
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js "^1.3.0"
|
base64-js "^1.3.0"
|
||||||
hex-lite "^1.5.0"
|
hex-lite "^1.5.0"
|
||||||
|
|
Loading…
Reference in New Issue