forked from Ivasoft/mattermost-mobile
* initalised * Removed unused packages * Android: Added groupId for supporting both threadId & channelId * Fixed ios condition check * Removed commented code * Removed unwanted condition * Removed unused variable * CRT reduced chunk size to 30, Android global threads showing GlobalThreads & iOS is_crt_enabled field is expected to be a boolean * Update android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java Co-authored-by: Elias Nahum <nahumhbl@gmail.com> * Misc fixes Co-authored-by: Elias Nahum <nahumhbl@gmail.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
472 lines
18 KiB
Java
472 lines
18 KiB
Java
package com.mattermost.rnbeta;
|
|
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffXfermode;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.provider.Settings;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import androidx.core.app.NotificationCompat;
|
|
import androidx.core.app.NotificationManagerCompat;
|
|
import androidx.core.app.Person;
|
|
import androidx.core.app.RemoteInput;
|
|
import androidx.core.graphics.drawable.IconCompat;
|
|
|
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
import com.facebook.react.bridge.ReadableMap;
|
|
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
|
import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Date;
|
|
import java.util.Objects;
|
|
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
|
|
public class CustomPushNotificationHelper {
|
|
public static final String CHANNEL_HIGH_IMPORTANCE_ID = "channel_01";
|
|
public static final String CHANNEL_MIN_IMPORTANCE_ID = "channel_02";
|
|
public static final String KEY_TEXT_REPLY = "CAN_REPLY";
|
|
public static final int MESSAGE_NOTIFICATION_ID = 435345;
|
|
public static final String NOTIFICATION_ID = "notificationId";
|
|
|
|
private static NotificationChannel mHighImportanceChannel;
|
|
private static NotificationChannel mMinImportanceChannel;
|
|
|
|
private static void addMessagingStyleMessages(Context context, NotificationCompat.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) {
|
|
String message = bundle.getString("message", bundle.getString("body"));
|
|
String senderId = bundle.getString("sender_id");
|
|
if (senderId == null) {
|
|
senderId = "sender_id";
|
|
}
|
|
Bundle userInfoBundle = bundle.getBundle("userInfo");
|
|
String senderName = getSenderName(bundle);
|
|
if (userInfoBundle != null) {
|
|
boolean localPushNotificationTest = userInfoBundle.getBoolean("test");
|
|
if (localPushNotificationTest) {
|
|
senderName = "Test";
|
|
}
|
|
}
|
|
|
|
if (conversationTitle == null || !android.text.TextUtils.isEmpty(senderName.trim())) {
|
|
message = removeSenderNameFromMessage(message, senderName);
|
|
}
|
|
|
|
long timestamp = new Date().getTime();
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
messagingStyle.addMessage(message, timestamp, senderName);
|
|
} else {
|
|
Person.Builder sender = new Person.Builder()
|
|
.setKey(senderId)
|
|
.setName(senderName);
|
|
|
|
try {
|
|
Bitmap avatar = userAvatar(context, senderId);
|
|
if (avatar != null) {
|
|
sender.setIcon(IconCompat.createWithBitmap(avatar));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
messagingStyle.addMessage(message, timestamp, sender.build());
|
|
}
|
|
}
|
|
|
|
private static void addNotificationExtras(NotificationCompat.Builder notification, Bundle bundle) {
|
|
Bundle userInfoBundle = bundle.getBundle("userInfo");
|
|
if (userInfoBundle == null) {
|
|
userInfoBundle = new Bundle();
|
|
}
|
|
|
|
String postId = bundle.getString("post_id");
|
|
if (postId != null) {
|
|
userInfoBundle.putString("post_id", postId);
|
|
}
|
|
|
|
String rootId = bundle.getString("root_id");
|
|
if (rootId != null) {
|
|
userInfoBundle.putString("root_id", rootId);
|
|
}
|
|
|
|
String channelId = bundle.getString("channel_id");
|
|
if (channelId != null) {
|
|
userInfoBundle.putString("channel_id", channelId);
|
|
}
|
|
|
|
notification.addExtras(userInfoBundle);
|
|
}
|
|
|
|
private static void addNotificationReplyAction(Context context, NotificationCompat.Builder notification, Bundle bundle, int notificationId) {
|
|
String postId = bundle.getString("post_id");
|
|
|
|
if (android.text.TextUtils.isEmpty(postId)) {
|
|
return;
|
|
}
|
|
|
|
Intent replyIntent = new Intent(context, NotificationReplyBroadcastReceiver.class);
|
|
replyIntent.setAction(KEY_TEXT_REPLY);
|
|
replyIntent.putExtra(NOTIFICATION_ID, notificationId);
|
|
replyIntent.putExtra("pushNotification", bundle);
|
|
|
|
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
|
|
context,
|
|
notificationId,
|
|
replyIntent,
|
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
|
|
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
|
|
.setLabel("Reply")
|
|
.build();
|
|
|
|
int icon = R.drawable.ic_notif_action_reply;
|
|
CharSequence title = "Reply";
|
|
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(icon, title, replyPendingIntent)
|
|
.addRemoteInput(remoteInput)
|
|
.setAllowGeneratedReplies(true)
|
|
.build();
|
|
|
|
notification
|
|
.setShowWhen(true)
|
|
.addAction(replyAction);
|
|
}
|
|
|
|
public static NotificationCompat.Builder createNotificationBuilder(Context context, PendingIntent intent, Bundle bundle, boolean createSummary) {
|
|
final NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_HIGH_IMPORTANCE_ID);
|
|
|
|
String channelId = bundle.getString("channel_id");
|
|
String postId = bundle.getString("post_id");
|
|
String rootId = bundle.getString("root_id");
|
|
int notificationId = postId != null ? postId.hashCode() : MESSAGE_NOTIFICATION_ID;
|
|
NotificationPreferences notificationPreferences = NotificationPreferences.getInstance(context);
|
|
|
|
Boolean is_crt_enabled = bundle.getString("is_crt_enabled") != null && bundle.getString("is_crt_enabled").equals("true");
|
|
String groupId = is_crt_enabled && !android.text.TextUtils.isEmpty(rootId) ? rootId : channelId;
|
|
|
|
addNotificationExtras(notification, bundle);
|
|
setNotificationIcons(context, notification, bundle);
|
|
setNotificationMessagingStyle(context, notification, bundle);
|
|
setNotificationGroup(notification, groupId, createSummary);
|
|
setNotificationBadgeType(notification);
|
|
setNotificationSound(notification, notificationPreferences);
|
|
setNotificationVibrate(notification, notificationPreferences);
|
|
setNotificationBlink(notification, notificationPreferences);
|
|
|
|
setNotificationChannel(notification, bundle);
|
|
setNotificationDeleteIntent(context, notification, bundle, notificationId);
|
|
addNotificationReplyAction(context, notification, bundle, notificationId);
|
|
|
|
notification
|
|
.setContentIntent(intent)
|
|
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
|
|
.setPriority(Notification.PRIORITY_HIGH)
|
|
.setCategory(Notification.CATEGORY_MESSAGE)
|
|
.setAutoCancel(true);
|
|
|
|
return notification;
|
|
}
|
|
|
|
public static void createNotificationChannels(Context context) {
|
|
// Notification channels are not supported in Android Nougat and below
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
return;
|
|
}
|
|
|
|
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
|
|
|
if (mHighImportanceChannel == null) {
|
|
mHighImportanceChannel = new NotificationChannel(CHANNEL_HIGH_IMPORTANCE_ID, "High Importance", NotificationManager.IMPORTANCE_HIGH);
|
|
mHighImportanceChannel.setShowBadge(true);
|
|
notificationManager.createNotificationChannel(mHighImportanceChannel);
|
|
}
|
|
|
|
if (mMinImportanceChannel == null) {
|
|
mMinImportanceChannel = new NotificationChannel(CHANNEL_MIN_IMPORTANCE_ID, "Min Importance", NotificationManager.IMPORTANCE_MIN);
|
|
mMinImportanceChannel.setShowBadge(true);
|
|
notificationManager.createNotificationChannel(mMinImportanceChannel);
|
|
}
|
|
}
|
|
|
|
private static Bitmap getCircleBitmap(Bitmap bitmap) {
|
|
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
|
|
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
|
final Canvas canvas = new Canvas(output);
|
|
|
|
final int color = Color.RED;
|
|
final Paint paint = new Paint();
|
|
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
|
final RectF rectF = new RectF(rect);
|
|
|
|
paint.setAntiAlias(true);
|
|
canvas.drawARGB(0, 0, 0, 0);
|
|
paint.setColor(color);
|
|
canvas.drawOval(rectF, paint);
|
|
|
|
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
|
canvas.drawBitmap(bitmap, rect, rect, paint);
|
|
|
|
bitmap.recycle();
|
|
|
|
return output;
|
|
}
|
|
|
|
private static String getConversationTitle(Bundle bundle) {
|
|
String title = bundle.getString("channel_name");
|
|
|
|
if (android.text.TextUtils.isEmpty(title)) {
|
|
title = bundle.getString("sender_name");
|
|
}
|
|
|
|
return title;
|
|
}
|
|
|
|
private static int getIconResourceId(Context context, String iconName) {
|
|
final Resources res = context.getResources();
|
|
String packageName = context.getPackageName();
|
|
String defType = "mipmap";
|
|
|
|
return res.getIdentifier(iconName, defType, packageName);
|
|
}
|
|
|
|
private static NotificationCompat.MessagingStyle getMessagingStyle(Context context, Bundle bundle) {
|
|
NotificationCompat.MessagingStyle messagingStyle;
|
|
String senderId = "me";
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
messagingStyle = new NotificationCompat.MessagingStyle("Me");
|
|
} else {
|
|
Person.Builder sender = new Person.Builder()
|
|
.setKey(senderId)
|
|
.setName("Me");
|
|
|
|
try {
|
|
Bitmap avatar = userAvatar(context, "me");
|
|
if (avatar != null) {
|
|
sender.setIcon(IconCompat.createWithBitmap(avatar));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
messagingStyle = new NotificationCompat.MessagingStyle(sender.build());
|
|
}
|
|
|
|
String conversationTitle = getConversationTitle(bundle);
|
|
setMessagingStyleConversationTitle(messagingStyle, conversationTitle, bundle);
|
|
addMessagingStyleMessages(context, messagingStyle, conversationTitle, bundle);
|
|
|
|
return messagingStyle;
|
|
}
|
|
|
|
private static String getSenderName(Bundle bundle) {
|
|
String senderName = bundle.getString("sender_name");
|
|
if (senderName != null) {
|
|
return senderName;
|
|
}
|
|
|
|
String channelName = bundle.getString("channel_name");
|
|
if (channelName != null && channelName.startsWith("@")) {
|
|
return channelName;
|
|
}
|
|
|
|
String message = bundle.getString("message");
|
|
if (message != null) {
|
|
String name = message.split(":")[0];
|
|
if (!name.equals(message)) {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
return getConversationTitle(bundle);
|
|
}
|
|
|
|
public static int getSmallIconResourceId(Context context, String iconName) {
|
|
if (iconName == null) {
|
|
iconName = "ic_notification";
|
|
}
|
|
|
|
int resourceId = getIconResourceId(context, iconName);
|
|
|
|
if (resourceId == 0) {
|
|
iconName = "ic_launcher";
|
|
resourceId = getIconResourceId(context, iconName);
|
|
|
|
if (resourceId == 0) {
|
|
resourceId = android.R.drawable.ic_dialog_info;
|
|
}
|
|
}
|
|
|
|
return resourceId;
|
|
}
|
|
|
|
private static String removeSenderNameFromMessage(String message, String senderName) {
|
|
int index = message.indexOf(senderName);
|
|
if (index == 0) {
|
|
message = message.substring(senderName.length());
|
|
}
|
|
|
|
return message.replaceFirst(": ", "").trim();
|
|
}
|
|
|
|
private static void setMessagingStyleConversationTitle(NotificationCompat.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) {
|
|
String channelName = getConversationTitle(bundle);
|
|
String senderName = bundle.getString("sender_name");
|
|
if (TextUtils.isEmpty(senderName)) {
|
|
senderName = getSenderName(bundle);
|
|
}
|
|
|
|
if (conversationTitle != null && !channelName.equals(senderName)) {
|
|
messagingStyle.setConversationTitle(conversationTitle);
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
messagingStyle.setGroupConversation(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void setNotificationBadgeType(NotificationCompat.Builder notification) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
notification.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE);
|
|
}
|
|
}
|
|
|
|
private static void setNotificationBlink(NotificationCompat.Builder notification, NotificationPreferences notificationPreferences) {
|
|
boolean blink = notificationPreferences.getShouldBlink();
|
|
if (blink) {
|
|
notification.setLights(Color.CYAN, 500, 500);
|
|
}
|
|
}
|
|
|
|
private static void setNotificationChannel(NotificationCompat.Builder notification, Bundle bundle) {
|
|
// If Android Oreo or above we need to register a channel
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
return;
|
|
}
|
|
|
|
NotificationChannel notificationChannel = mHighImportanceChannel;
|
|
|
|
boolean testNotification = false;
|
|
boolean localNotification = false;
|
|
Bundle userInfoBundle = bundle.getBundle("userInfo");
|
|
if (userInfoBundle != null) {
|
|
testNotification = userInfoBundle.getBoolean("test");
|
|
localNotification = userInfoBundle.getBoolean("local");
|
|
}
|
|
|
|
if (testNotification || localNotification) {
|
|
notificationChannel = mMinImportanceChannel;
|
|
}
|
|
|
|
notification.setChannelId(notificationChannel.getId());
|
|
}
|
|
|
|
private static void setNotificationDeleteIntent(Context context, NotificationCompat.Builder notification, Bundle bundle, int notificationId) {
|
|
// Let's add a delete intent when the notification is dismissed
|
|
Intent delIntent = new Intent(context, NotificationDismissService.class);
|
|
PushNotificationProps notificationProps = new PushNotificationProps(bundle);
|
|
delIntent.putExtra(NOTIFICATION_ID, notificationId);
|
|
PendingIntent deleteIntent = NotificationIntentAdapter.createPendingNotificationIntent(context, delIntent, notificationProps);
|
|
notification.setDeleteIntent(deleteIntent);
|
|
}
|
|
|
|
private static void setNotificationMessagingStyle(Context context, NotificationCompat.Builder notification, Bundle bundle) {
|
|
NotificationCompat.MessagingStyle messagingStyle = getMessagingStyle(context, bundle);
|
|
notification.setStyle(messagingStyle);
|
|
}
|
|
|
|
private static void setNotificationGroup(NotificationCompat.Builder notification, String channelId, boolean setAsSummary) {
|
|
notification.setGroup(channelId);
|
|
|
|
if (setAsSummary) {
|
|
// if this is the first notification for the channel then set as summary, otherwise skip
|
|
notification.setGroupSummary(true);
|
|
}
|
|
}
|
|
|
|
private static void setNotificationIcons(Context context, NotificationCompat.Builder notification, Bundle bundle) {
|
|
String smallIcon = bundle.getString("smallIcon");
|
|
String channelName = getConversationTitle(bundle);
|
|
String senderName = bundle.getString("sender_name");
|
|
|
|
int smallIconResId = getSmallIconResourceId(context, smallIcon);
|
|
notification.setSmallIcon(smallIconResId);
|
|
|
|
if (channelName.equals(senderName)) {
|
|
try {
|
|
String senderId = bundle.getString("sender_id");
|
|
Bitmap avatar = userAvatar(context, senderId);
|
|
if (avatar != null) {
|
|
notification.setLargeIcon(avatar);
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void setNotificationSound(NotificationCompat.Builder notification, NotificationPreferences notificationPreferences) {
|
|
String soundUri = notificationPreferences.getNotificationSound();
|
|
if (soundUri != null) {
|
|
if (!soundUri.equals("none")) {
|
|
notification.setSound(Uri.parse(soundUri));
|
|
}
|
|
} else {
|
|
Uri defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
|
|
notification.setSound(defaultUri);
|
|
}
|
|
}
|
|
|
|
private static void setNotificationVibrate(NotificationCompat.Builder notification, NotificationPreferences notificationPreferences) {
|
|
boolean vibrate = notificationPreferences.getShouldVibrate();
|
|
if (vibrate) {
|
|
// Use the system default for vibration
|
|
notification.setDefaults(Notification.DEFAULT_VIBRATE);
|
|
}
|
|
}
|
|
|
|
private static Bitmap userAvatar(Context context, final String userId) throws IOException {
|
|
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
|
final ReadableMap credentials = MattermostCredentialsHelper.getCredentialsSync(reactApplicationContext);
|
|
final String serverUrl = credentials.getString("serverUrl");
|
|
final String token = credentials.getString("token");
|
|
|
|
|
|
final OkHttpClient client = new OkHttpClient();
|
|
final String url = String.format("%s/api/v4/users/%s/image", serverUrl, userId);
|
|
Request request = new Request.Builder()
|
|
.header("Authorization", String.format("Bearer %s", token))
|
|
.url(url)
|
|
.build();
|
|
Response response = client.newCall(request).execute();
|
|
if (response.code() == 200) {
|
|
assert response.body() != null;
|
|
byte[] bytes = response.body().bytes();
|
|
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
|
|
|
Log.i("ReactNative", String.format("Fetch profile %s", url));
|
|
return getCircleBitmap(bitmap);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|