Unverified Commit 5d064696 authored by Leonardo Aramaki's avatar Leonardo Aramaki Committed by GitHub

Merge pull request #571 from RocketChat/fix/notifications

Fix broken notifications
parents 839f60b0 68755814
......@@ -150,8 +150,11 @@ dependencies {
compile 'com.github.matrixxun:MaterialBadgeTextView:c5a27e8243'
compile 'com.github.chrisbanes:PhotoView:2.0.0'
testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:2.8.9"
testCompile 'org.robolectric:robolectric:3.3'
testCompile "org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
testCompile "com.nhaarman:mockito-kotlin:1.1.0"
testCompile 'org.amshove.kluent:kluent:1.14'
testCompile "org.jetbrains.kotlin:kotlin-reflect:$rootProject.ext.kotlinVersion"
}
apply plugin: 'com.google.gms.google-services'
......@@ -17,6 +17,7 @@ import java.util.UUID;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.push.PushManager;
import chat.rocket.core.utils.Pair;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
......@@ -104,9 +105,12 @@ public class RocketChatCache {
}
}
public @NonNull String getSiteUrlFor(String hostname) {
public @Nullable String getSiteUrlFor(String hostname) {
try {
String selectedServerHostname = getSelectedServerHostname();
if (getLoginHostnamesJson() == null || getLoginHostnamesJson().isEmpty()) {
return null;
}
return new JSONObject(getLoginHostnamesJson())
.optString(hostname, selectedServerHostname);
} catch (JSONException e) {
......
......@@ -65,14 +65,19 @@ object PushManager {
@Synchronized
fun handle(context: Context, data: Bundle) {
val appContext = context.applicationContext
val message = data["message"] as String
val image = data["image"] as String
val ejson = data["ejson"] as String
val notId = data["notId"] as String
val style = data["style"] as String
val summaryText = data["summaryText"] as String
val count = data["count"] as String
val title = data["title"] as String
val message = data["message"] as String?
val image = data["image"] as String?
val ejson = data["ejson"] as String?
val notId = data["notId"] as String? ?: randomizer.nextInt().toString()
val style = data["style"] as String?
val summaryText = data["summaryText"] as String?
val count = data["count"] as String?
val title = data["title"] as String?
if (ejson == null || message == null || title == null) {
return
}
val lastPushMessage = PushMessage(title, message, image, ejson, count, notId, summaryText, style)
// We should use Timber here
......@@ -123,7 +128,10 @@ object PushManager {
return group
}
private fun showNotification(context: Context, lastPushMessage: PushMessage) {
internal fun showNotification(context: Context, lastPushMessage: PushMessage) {
if (lastPushMessage.host == null || lastPushMessage.message == null || lastPushMessage.title == null) {
return
}
val manager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
......@@ -141,23 +149,37 @@ object PushManager {
if (isAndroidVersionAtLeast(Build.VERSION_CODES.N)) {
val notification = createSingleNotificationForNougatAndAbove(context, lastPushMessage)
val groupNotification = createGroupNotificationForNougatAndAbove(context, lastPushMessage)
manager.notify(notId, notification)
manager.notify(groupTuple.first, groupNotification)
notification?.let {
manager.notify(notId, notification)
}
groupNotification?.let {
manager.notify(groupTuple.first, groupNotification)
}
} else {
val notification = createSingleNotification(context, lastPushMessage)
val pushMessageList = hostToPushMessageList.get(host)
NotificationManagerCompat.from(context).notify(notId, notification)
notification?.let {
NotificationManagerCompat.from(context).notify(notId, notification)
}
pushMessageList?.let {
if (pushMessageList.size > 1) {
val groupNotification = createGroupNotification(context, lastPushMessage)
NotificationManagerCompat.from(context).notify(groupTuple.first, groupNotification)
groupNotification?.let {
NotificationManagerCompat.from(context).notify(groupTuple.first, groupNotification)
}
}
}
}
}
private fun createGroupNotification(context: Context, lastPushMessage: PushMessage): Notification {
internal fun createGroupNotification(context: Context, lastPushMessage: PushMessage): Notification? {
with(lastPushMessage) {
if (host == null || message == null || title == null) {
return null
}
val id = lastPushMessage.notificationId.toInt()
val contentIntent = getContentIntent(context, id, lastPushMessage)
val deleteIntent = getDismissIntent(context, lastPushMessage)
......@@ -176,18 +198,18 @@ object PushManager {
builder.setSubText(subText)
}
if (style == "inbox") {
if (style == null || style == "inbox") {
val pushMessageList = hostToPushMessageList.get(host)
pushMessageList?.let {
val messageCount = pushMessageList.size
val summary = summaryText.replace("%n%", messageCount.toString())
.fromHtml()
val summary = summaryText?.replace("%n%", messageCount.toString())
?.fromHtml() ?: "$messageCount new messages"
builder.setNumber(messageCount)
if (messageCount > 1) {
val firstPush = pushMessageList[0]
val singleConversation = pushMessageList.filter {
firstPush.sender.username != it.sender.username
firstPush.sender?.username != it.sender?.username
}.isEmpty()
val inbox = NotificationCompat.InboxStyle()
......@@ -203,9 +225,13 @@ object PushManager {
builder.setStyle(inbox)
} else {
val firstMsg = pushMessageList[0]
if (firstMsg.host == null || firstMsg.message == null || firstMsg.title == null) {
return null
}
val bigText = NotificationCompat.BigTextStyle()
.bigText(pushMessageList[0].message.fromHtml())
.setBigContentTitle(pushMessageList[0].title.fromHtml())
.bigText(firstMsg.message.fromHtml())
.setBigContentTitle(firstMsg.title.fromHtml())
builder.setStyle(bigText)
}
......@@ -223,8 +249,11 @@ object PushManager {
}
@RequiresApi(Build.VERSION_CODES.N)
private fun createGroupNotificationForNougatAndAbove(context: Context, lastPushMessage: PushMessage): Notification {
internal fun createGroupNotificationForNougatAndAbove(context: Context, lastPushMessage: PushMessage): Notification? {
with(lastPushMessage) {
if (host == null || message == null || title == null) {
return null
}
val manager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val id = notificationId.toInt()
......@@ -256,7 +285,7 @@ object PushManager {
builder.setSubText(subText)
}
if (style == "inbox") {
if (style == null || style == "inbox") {
val pushMessageList = hostToPushMessageList.get(host)
pushMessageList?.let {
......@@ -287,8 +316,11 @@ object PushManager {
}
}
private fun createSingleNotification(context: Context, lastPushMessage: PushMessage): Notification {
internal fun createSingleNotification(context: Context, lastPushMessage: PushMessage): Notification? {
with(lastPushMessage) {
if (host == null || message == null || title == null) {
return null
}
val id = notificationId.toInt()
val contentIntent = getContentIntent(context, id, lastPushMessage)
val deleteIntent = getDismissIntent(context, lastPushMessage)
......@@ -303,7 +335,7 @@ object PushManager {
.setContentIntent(contentIntent)
.setMessageNotification()
val subText = RocketChatCache(context).getHostSiteName(lastPushMessage.host)
val subText = RocketChatCache(context).getHostSiteName(host)
if (subText.isNotEmpty()) {
builder.setSubText(subText)
}
......@@ -311,12 +343,16 @@ object PushManager {
val pushMessageList = hostToPushMessageList.get(host)
pushMessageList?.let {
val lastPushMsg = pushMessageList.last()
if (lastPushMsg.host == null || lastPushMsg.message == null || lastPushMsg.title == null) {
return null
}
if (pushMessageList.isNotEmpty()) {
val messageCount = pushMessageList.size
val bigText = NotificationCompat.BigTextStyle()
.bigText(pushMessageList.last().message.fromHtml())
.setBigContentTitle(pushMessageList.last().title.fromHtml())
.bigText(lastPushMsg.message.fromHtml())
.setBigContentTitle(lastPushMsg.title.fromHtml())
builder.setStyle(bigText).setNumber(messageCount)
}
}
......@@ -326,11 +362,14 @@ object PushManager {
}
@RequiresApi(Build.VERSION_CODES.N)
private fun createSingleNotificationForNougatAndAbove(context: Context, lastPushMessage: PushMessage): Notification {
internal fun createSingleNotificationForNougatAndAbove(context: Context, lastPushMessage: PushMessage): Notification? {
val manager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
with(lastPushMessage) {
if (host == null || message == null || title == null) {
return null
}
val id = notificationId.toInt()
val contentIntent = getContentIntent(context, id, lastPushMessage)
val deleteIntent = getDismissIntent(context, lastPushMessage)
......@@ -356,12 +395,12 @@ object PushManager {
manager.createNotificationChannel(channel)
}
val subText = RocketChatCache(context).getHostSiteName(lastPushMessage.host)
val subText = RocketChatCache(context).getHostSiteName(host)
if (subText.isNotEmpty()) {
builder.setSubText(subText)
}
if ("inbox" == style) {
if (style == null || "inbox" == style) {
val pushMessageList = hostToPushMessageList.get(host)
pushMessageList?.let {
......@@ -470,12 +509,11 @@ object PushManager {
replyIntent.putExtra(EXTRA_PUSH_MESSAGE, pushMessage as Serializable)
val pendingIntent = PendingIntent.getBroadcast(
context, randomizer.nextInt(), replyIntent, 0)
val replyAction =
NotificationCompat.Action.Builder(
R.drawable.ic_reply, REPLY_LABEL, pendingIntent)
.addRemoteInput(replyRemoteInput)
.setAllowGeneratedReplies(true)
.build()
val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, REPLY_LABEL, pendingIntent)
.addRemoteInput(replyRemoteInput)
.setAllowGeneratedReplies(true)
.build()
this.addAction(replyAction)
return this
}
......@@ -488,49 +526,54 @@ object PushManager {
with(this, {
setAutoCancel(true)
setShowWhen(true)
setColor(ctx.resources.getColor(R.color.colorRed400))
color = ctx.resources.getColor(R.color.colorRed400)
setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(smallIcon)
})
return this
}
private data class PushMessage(
val title: String,
val message: String,
val image: String?,
val ejson: String,
val count: String,
internal data class PushMessage(
val title: String? = null,
val message: String? = null,
val image: String? = null,
val ejson: String? = null,
val count: String? = null,
val notificationId: String,
val summaryText: String,
val style: String) : Serializable {
val host: String
val rid: String
val type: String
val name: String?
val sender: Sender
val summaryText: String? = null,
val style: String? = null) : Serializable {
val host: String?
val rid: String?
val type: String?
val channelName: String?
val sender: Sender?
val createdAt: Long
init {
val json = JSONObject(ejson)
host = json.getString("host")
rid = json.getString("rid")
type = json.getString("type")
name = json.optString("name")
sender = Sender(json.getString("sender"))
val json = if (ejson == null) JSONObject() else JSONObject(ejson)
host = json.optString("host", null)
rid = json.optString("rid", null)
type = json.optString("type", null)
channelName = json.optString("name", null)
val senderJson = json.optString("sender", null)
if (senderJson != null && senderJson != "null") {
sender = Sender(senderJson)
} else {
sender = null
}
createdAt = System.currentTimeMillis()
}
data class Sender(val sender: String) : Serializable {
val _id: String
val username: String
val name: String
val _id: String?
val username: String?
val name: String?
init {
val json = JSONObject(sender)
_id = json.optString("_id")
username = json.getString("username")
name = json.optString("name")
_id = json.optString("_id", null)
username = json.optString("username", null)
name = json.optString("name", null)
}
}
}
......@@ -565,13 +608,16 @@ object PushManager {
val message: CharSequence? = extractMessage(intent)
val pushMessage = intent?.extras?.getSerializable(EXTRA_PUSH_MESSAGE) as PushMessage?
pushMessage?.let {
val userNotId = pushMessage.notificationId.toInt()
if (pushMessage?.host == null) {
return
}
pushMessage.let {
val groupTuple = groupMap.get(pushMessage.host)
val pushes = hostToPushMessageList.get(pushMessage.host)
pushes?.let {
val allMessagesFromSameUser = pushes.filter {
it.sender._id == pushMessage.sender._id
it.sender?._id == pushMessage.sender?._id
}
for (msg in allMessagesFromSameUser) {
manager.cancel(msg.notificationId.toInt())
......@@ -586,9 +632,15 @@ object PushManager {
}
}
message?.let {
if (pushMessage.rid == null) {
return
}
val httpUrl = HttpUrl.parse(pushMessage.host)
httpUrl?.let {
sendMessage(RocketChatCache(context).getSiteUrlFor(httpUrl.host()), message, pushMessage.rid)
val siteUrl = RocketChatCache(context).getSiteUrlFor(httpUrl.host())
if (siteUrl != null) {
sendMessage(siteUrl, message, pushMessage.rid)
}
}
}
}
......@@ -629,7 +681,7 @@ object PushManager {
roomUserTuple.flatMap { tuple -> messageInteractor.send(tuple.first, tuple.second, message as String) }
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ success ->
.subscribe({ _ ->
// Empty
}, { throwable ->
throwable.printStackTrace()
......
package chat.rocket.android.push;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.util.SparseArrayCompat;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.helper.ServerPolicyHelper;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.helper.AvatarHelper;
import chat.rocket.core.models.ServerInfo;
public class PushNotificationHandler implements PushConstants {
private static final String LOG_TAG = "PushNotificationHandler";
private static SparseArrayCompat<ArrayList<String>> messageMap = new SparseArrayCompat<>();
private Random random = new Random();
public static synchronized void cleanUpNotificationStack(int notId) {
messageMap.remove(notId);
}
private synchronized void setNotification(int notId, String message) {
ArrayList<String> messageList = messageMap.get(notId);
if (messageList == null) {
messageList = new ArrayList<>();
messageMap.put(notId, messageList);
}
if (message.isEmpty()) {
messageList.clear();
} else {
messageList.add(message);
}
}
private synchronized ArrayList<String> getMessageList(int notId) {
return messageMap.get(notId);
}
public void showNotificationIfPossible(Context context, Bundle extras) {
// Send a notification if there is a message or title, otherwise just send data
String message = extras.getString(MESSAGE);
String title = extras.getString(TITLE);
String contentAvailable = extras.getString(CONTENT_AVAILABLE);
String forceStart = extras.getString(FORCE_START);
Log.d(LOG_TAG, "message =[" + message + "]");
Log.d(LOG_TAG, "title =[" + title + "]");
Log.d(LOG_TAG, "contentAvailable =[" + contentAvailable + "]");
Log.d(LOG_TAG, "forceStart =[" + forceStart + "]");
if ((message != null && message.length() != 0) ||
(title != null && title.length() != 0)) {
Log.d(LOG_TAG, "build notification");
if (title == null || title.isEmpty()) {
extras.putString(TITLE, getAppName(context));
}
createNotification(context, extras);
}
}
public void createNotification(Context context, Bundle extras) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
String appName = getAppName(context);
String packageName = context.getPackageName();
Resources resources = context.getResources();
String hostname = getHostname(extras);
String roomId = getRoomId(extras);
int notId = parseInt(NOT_ID, extras);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notificationIntent.putExtra(PUSH_BUNDLE, extras);
notificationIntent.putExtra(NOT_ID, notId);
if (hostname != null && roomId != null && isValidHostname(context, hostname)) {
notificationIntent.putExtra(HOSTNAME, hostname);
notificationIntent.putExtra(ROOM_ID, roomId);
}
int requestCode = random.nextInt();
PendingIntent contentIntent = PendingIntent
.getActivity(context, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
SharedPreferences prefs = context
.getSharedPreferences(PushConstants.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE);
String localIcon = prefs.getString(ICON, null);
String localIconColor = prefs.getString(ICON_COLOR, null);
boolean soundOption = prefs.getBoolean(SOUND, true);
boolean vibrateOption = prefs.getBoolean(VIBRATE, true);
Log.d(LOG_TAG, "stored icon=" + localIcon);
Log.d(LOG_TAG, "stored iconColor=" + localIconColor);
Log.d(LOG_TAG, "stored sound=" + soundOption);
Log.d(LOG_TAG, "stored vibrate=" + vibrateOption);
if (Build.VERSION.SDK_INT >= 26) {
String channelId = "rocket-chat-channel";
CharSequence name = "RocketChatMessage";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(channelId, name, importance);
channel.enableLights(true);
notificationManager.createNotificationChannel(channel);
Notification.Builder notificationBuilder = new Notification.Builder(context, channelId)
.setWhen(System.currentTimeMillis())
.setContentTitle(fromHtml(extras.getString(TITLE)))
.setTicker(fromHtml(extras.getString(TITLE)))
.setContentIntent(contentIntent)
.setChannelId(channelId)
.setAutoCancel(true);
setNotificationImportance(extras, channel);
setNotificationVibration(extras, vibrateOption, channel);
setNotificationMessage(notId, extras, notificationBuilder);
setNotificationCount(context, extras, notificationBuilder);
setNotificationSmallIcon(extras, packageName, resources, notificationBuilder,
localIcon);
setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder);
setNotificationLedColor(extras, channel);
if (soundOption) {
setNotificationSound(context, extras, channel);
}
createActions(context, extras, notificationBuilder, resources, packageName, notId);
notificationManager.notify(notId, notificationBuilder.build());
} else {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setWhen(System.currentTimeMillis())
.setContentTitle(fromHtml(extras.getString(TITLE)))
.setTicker(fromHtml(extras.getString(TITLE)))
.setContentIntent(contentIntent)
.setAutoCancel(true);
setNotificationCount(context, extras, notificationBuilder);
setNotificationVibration(extras, vibrateOption, notificationBuilder);
setNotificationIconColor(extras.getString("color"), notificationBuilder, localIconColor);
setNotificationSmallIcon(extras, packageName, resources, notificationBuilder,
localIcon);
setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder);
if (soundOption) {
setNotificationSound(context, extras, notificationBuilder);
}
setNotificationLedColor(extras, notificationBuilder);
setNotificationPriority(extras, notificationBuilder);
setNotificationMessage(notId, extras, notificationBuilder);
setVisibility(context, extras, notificationBuilder);
createActions(context, extras, notificationBuilder, resources, packageName, notId);
notificationManager.notify(appName, notId, notificationBuilder.build());
}
}
private void createActions(Context context, Bundle extras, NotificationCompat.Builder builder,
Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "build actions: with in-line");
String actions = extras.getString(ACTIONS);
if (actions == null) {
return;
}
try {
JSONArray actionsArray = new JSONArray(actions);
ArrayList<NotificationCompat.Action> wActions = new ArrayList<>();
for (int i = 0; i < actionsArray.length(); i++) {
int min = 1;
int max = 2000000000;
Random random = new Random();
int uniquePendingIntentRequestCode = random.nextInt((max - min) + 1) + min;
Log.d(LOG_TAG, "adding action");
JSONObject action = actionsArray.getJSONObject(i);
Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK));
boolean foreground = action.optBoolean(FOREGROUND, true);
boolean inline = action.optBoolean("inline", false);
Intent intent;
PendingIntent pIntent;
if (inline) {
Log.d(LOG_TAG, "Version: " + android.os.Build.VERSION.SDK_INT + " = "
+ android.os.Build.VERSION_CODES.M);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity");
intent = new Intent(context, MainActivity.class);
} else {
Log.d(LOG_TAG, "push receiver");
intent = new Intent(context, BackgroundActionButtonHandler.class);
}
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity for notId " + notId);
pIntent =
PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
} else {
Log.d(LOG_TAG, "push receiver for notId " + notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
}
} else if (foreground) {
intent = new Intent(context, MainActivity.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
} else {
intent = new Intent(context, BackgroundActionButtonHandler.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.Builder(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
RemoteInput remoteInput;
if (inline) {
Log.d(LOG_TAG, "build remote input");
String replyLabel = "Enter your reply here";
remoteInput = new RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel)
.build();
actionBuilder.addRemoteInput(remoteInput);
}
NotificationCompat.Action wAction = actionBuilder.build();
wActions.add(actionBuilder.build());
if (inline) {
builder.addAction(wAction);
} else {
builder.addAction(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
}
wAction = null;
pIntent = null;
}
builder.extend(new NotificationCompat.WearableExtender().addActions(wActions));
wActions.clear();
} catch (JSONException e) {
// nope
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
private void createActions(Context context, Bundle extras, Notification.Builder builder,
Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "build actions: with in-line");
String actions = extras.getString(ACTIONS);
if (actions == null) {
return;
}
try {
JSONArray actionsArray = new JSONArray(actions);
ArrayList<Notification.Action> wActions = new ArrayList<>();
for (int i = 0; i < actionsArray.length(); i++) {
int min = 1;
int max = 2000000000;
Random random = new Random();
int uniquePendingIntentRequestCode = random.nextInt((max - min) + 1) + min;
Log.d(LOG_TAG, "adding action");
JSONObject action = actionsArray.getJSONObject(i);
Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK));
boolean foreground = action.optBoolean(FOREGROUND, true);
boolean inline = action.optBoolean("inline", false);
Intent intent;
PendingIntent pIntent;
if (inline) {
Log.d(LOG_TAG, "Version: " + android.os.Build.VERSION.SDK_INT + " = "
+ android.os.Build.VERSION_CODES.M);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity");
intent = new Intent(context, MainActivity.class);
} else {
Log.d(LOG_TAG, "push receiver");
intent = new Intent(context, BackgroundActionButtonHandler.class);
}
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity for notId " + notId);
pIntent =
PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
} else {
Log.d(LOG_TAG, "push receiver for notId " + notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
}
} else if (foreground) {
intent = new Intent(context, MainActivity.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
} else {
intent = new Intent(context, BackgroundActionButtonHandler.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
android.app.RemoteInput remoteInput;
if (inline) {
Log.d(LOG_TAG, "build remote input");
String replyLabel = "Enter your reply here";
remoteInput = new android.app.RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel)
.build();
actionBuilder.addRemoteInput(remoteInput);
}
Notification.Action wAction = actionBuilder.build();
wActions.add(actionBuilder.build());
if (inline) {
builder.addAction(wAction);
} else {
builder.addAction(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
}
wAction = null;
pIntent = null;
}
builder.extend(new Notification.WearableExtender().addActions(wActions));
wActions.clear();
} catch (JSONException e) {
// nope
}
}
private void setNotificationCount(Context context, Bundle extras,
NotificationCompat.Builder builder) {
int count = extractBadgeCount(extras);
if (count >= 0) {
Log.d(LOG_TAG, "count =[" + count + "]");
builder.setNumber(count);
}
}
private void setNotificationCount(Context context, Bundle extras,
Notification.Builder builder) {
int count = extractBadgeCount(extras);
if (count >= 0) {
Log.d(LOG_TAG, "count =[" + count + "]");
builder.setNumber(count);
}
}
private void setVisibility(Context context, Bundle extras, NotificationCompat.Builder builder) {
String visibilityStr = extras.getString(VISIBILITY);
if (visibilityStr == null) {
return;
}
try {
Integer visibility = Integer.parseInt(visibilityStr);
if (visibility >= NotificationCompat.VISIBILITY_SECRET
&& visibility <= NotificationCompat.VISIBILITY_PUBLIC) {
builder.setVisibility(visibility);
} else {
Log.e(LOG_TAG, "Visibility parameter must be between -1 and 1");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
private void setNotificationVibration(Bundle extras, Boolean vibrateOption,
NotificationCompat.Builder builder) {
String vibrationPattern = extras.getString(VIBRATION_PATTERN);
if (vibrationPattern != null) {
String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
long[] results = new long[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Long.parseLong(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
builder.setVibrate(results);
} else {
if (vibrateOption) {
builder.setDefaults(Notification.DEFAULT_VIBRATE);
}
}
}
@RequiresApi(api = 26)
private void setNotificationVibration(Bundle extras, Boolean vibrateOption,
NotificationChannel channel) {
String vibrationPattern = extras.getString(VIBRATION_PATTERN);
if (vibrationPattern != null) {
String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
long[] results = new long[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Long.parseLong(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
channel.setVibrationPattern(results);
} else {
if (vibrateOption) {
channel.enableVibration(true);
}
}
}
private void setNotificationMessage(int notId, Bundle extras,
NotificationCompat.Builder builder) {
String message = extras.getString(MESSAGE);
String style = extras.getString(STYLE, STYLE_TEXT);
if (STYLE_INBOX.equals(style)) {
setNotification(notId, message);
builder.setContentText(fromHtml(message));
ArrayList<String> messageList = getMessageList(notId);
Integer sizeList = messageList.size();
if (sizeList > 1) {
String sizeListMessage = sizeList.toString();
String stacking = sizeList + " more";
if (extras.getString(SUMMARY_TEXT) != null) {
stacking = extras.getString(SUMMARY_TEXT);
stacking = stacking.replace("%n%", sizeListMessage);
}
NotificationCompat.InboxStyle notificationInbox = new NotificationCompat.InboxStyle()
.setBigContentTitle(fromHtml(extras.getString(TITLE)))
.setSummaryText(fromHtml(stacking));
for (int i = messageList.size() - 1; i >= 0; i--) {
notificationInbox.addLine(fromHtml(messageList.get(i)));
}
builder.setStyle(notificationInbox);
} else {
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) {
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
builder.setStyle(bigText);
}
}
} else if (STYLE_PICTURE.equals(style)) {
setNotification(notId, "");
NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE), null, null));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
builder.setContentTitle(fromHtml(extras.getString(TITLE)));
builder.setContentText(fromHtml(message));
builder.setStyle(bigPicture);
} else {
setNotification(notId, "");
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) {
builder.setContentText(fromHtml(message));
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
String summaryText = extras.getString(SUMMARY_TEXT);
if (summaryText != null) {
bigText.setSummaryText(fromHtml(summaryText));
}
builder.setStyle(bigText);
}
}
}
private void setNotificationMessage(int notId, Bundle extras,
Notification.Builder builder) {
String message = extras.getString(MESSAGE);
String style = extras.getString(STYLE, STYLE_TEXT);
if (STYLE_INBOX.equals(style)) {
setNotification(notId, message);
builder.setContentText(fromHtml(message));
ArrayList<String> messageList = getMessageList(notId);
Integer sizeList = messageList.size();
if (sizeList > 1) {
String sizeListMessage = sizeList.toString();
String stacking = sizeList + " more";
if (extras.getString(SUMMARY_TEXT) != null) {
stacking = extras.getString(SUMMARY_TEXT);
stacking = stacking.replace("%n%", sizeListMessage);
}
Notification.InboxStyle notificationInbox = new Notification.InboxStyle()
.setBigContentTitle(fromHtml(extras.getString(TITLE)))
.setSummaryText(fromHtml(stacking));
for (int i = messageList.size() - 1; i >= 0; i--) {
notificationInbox.addLine(fromHtml(messageList.get(i)));
}
builder.setStyle(notificationInbox);
} else {
Notification.BigTextStyle bigText = new Notification.BigTextStyle();
if (message != null) {
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
builder.setStyle(bigText);
}
}
} else if (STYLE_PICTURE.equals(style)) {
setNotification(notId, "");
Notification.BigPictureStyle bigPicture = new Notification.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE), null, null));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
builder.setContentTitle(fromHtml(extras.getString(TITLE)));
builder.setContentText(fromHtml(message));
builder.setStyle(bigPicture);
} else {
setNotification(notId, "");
Notification.BigTextStyle bigText = new Notification.BigTextStyle();
if (message != null) {
builder.setContentText(fromHtml(message));
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
String summaryText = extras.getString(SUMMARY_TEXT);
if (summaryText != null) {
bigText.setSummaryText(fromHtml(summaryText));
}
builder.setStyle(bigText);
}
}
}
private void setNotificationSound(Context context, Bundle extras,
NotificationCompat.Builder builder) {
String soundname = extras.getString(SOUNDNAME);
if (soundname == null) {
soundname = extras.getString(SOUND);
}
if (SOUND_RINGTONE.equals(soundname)) {
builder.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI);
} else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) {
Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + context.getPackageName() + "/raw/" + soundname);
Log.d(LOG_TAG, sound.toString());
builder.setSound(sound);
} else {
builder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI);
}
}
@RequiresApi(api = 26)
private void setNotificationSound(Context context, Bundle extras,
NotificationChannel channel) {
String soundname = extras.getString(SOUNDNAME);
if (soundname == null) {
soundname = extras.getString(SOUND);
}
if (SOUND_RINGTONE.equals(soundname)) {
channel.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) {
Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + context.getPackageName() + "/raw/" + soundname);
Log.d(LOG_TAG, sound.toString());
channel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else {
channel.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
}
}
private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder builder) {
String ledColor = extras.getString(LED_COLOR);
if (ledColor == null) {
return;
}
// Converts parse Int Array from ledColor
String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
int[] results = new int[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Integer.parseInt(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
if (results.length == 4) {
builder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500);
} else {
Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
}
}
@RequiresApi(api = 26)
private void setNotificationLedColor(Bundle extras, NotificationChannel channel) {
String ledColor = extras.getString(LED_COLOR);
if (ledColor == null) {
return;
}
// Converts parse Int Array from ledColor
String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
int[] results = new int[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Integer.parseInt(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
if (results.length == 4) {
channel.setLightColor(Color.argb(results[0], results[1], results[2], results[3]));
} else {
Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
}
}
private void setNotificationPriority(Bundle extras, NotificationCompat.Builder builder) {
String priorityStr = extras.getString(PRIORITY);
if (priorityStr == null) {
return;
}
try {
Integer priority = Integer.parseInt(priorityStr);
if (priority >= NotificationCompat.PRIORITY_MIN
&& priority <= NotificationCompat.PRIORITY_MAX) {
builder.setPriority(priority);
} else {
Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
@RequiresApi(api = 26)
private void setNotificationImportance(Bundle extras, NotificationChannel channel) {
String priorityStr = extras.getString(PRIORITY);
if (priorityStr == null) {
return;
}
try {
Integer priority = Integer.parseInt(priorityStr);
if (priority >= NotificationCompat.PRIORITY_MIN
&& priority <= NotificationCompat.PRIORITY_MAX) {
channel.setImportance(priority);
} else {
Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder builder) {
String hostname = getHostname(extras);
String username = getSenderUsername(extras);
String gcmLargeIcon;
if (username != null && !username.isEmpty()) {
gcmLargeIcon = "https://" + hostname + "/avatar/" + username;
} else {
gcmLargeIcon = extras.getString(IMAGE); // from gcm
}
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return;
}
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon, username, context));
Log.d(LOG_TAG, "using remote large-icon from gcm");
} else {
AssetManager assetManager = context.getAssets();
InputStream istr;
try {
istr = assetManager.open(gcmLargeIcon);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
builder.setLargeIcon(bitmap);
Log.d(LOG_TAG, "using assets large-icon from gcm");
} catch (IOException e) {
int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
if (largeIconId != 0) {
Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
builder.setLargeIcon(largeIconBitmap);
Log.d(LOG_TAG, "using resources large-icon from gcm");
} else {
Log.d(LOG_TAG, "Not setting large icon");
}
}
}
}
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, Notification.Builder builder) {
String hostname = getHostname(extras);
String username = getSenderUsername(extras);
String gcmLargeIcon;
if (username != null && !username.isEmpty()) {
gcmLargeIcon = "https://" + hostname + "/avatar/" + username;
} else {
gcmLargeIcon = extras.getString(IMAGE); // from gcm
}
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return;
}
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon, username, context));
Log.d(LOG_TAG, "using remote large-icon from gcm");
} else {
AssetManager assetManager = context.getAssets();
InputStream istr;
try {
istr = assetManager.open(gcmLargeIcon);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
builder.setLargeIcon(bitmap);
Log.d(LOG_TAG, "using assets large-icon from gcm");
} catch (IOException e) {
int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
if (largeIconId != 0) {
Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
builder.setLargeIcon(largeIconBitmap);
Log.d(LOG_TAG, "using resources large-icon from gcm");
} else {
Log.d(LOG_TAG, "Not setting large icon");
}
}
}
}
private void setNotificationSmallIcon(Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder builder,
String localIcon) {
int iconId = 0;
String icon = extras.getString(ICON);
if (icon != null && !"".equals(icon)) {
iconId = resources.getIdentifier(icon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
} else if (localIcon != null && !"".equals(localIcon)) {
iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
}
if (iconId == 0) {
Log.d(LOG_TAG, "no icon resource found - using default icon");
iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName);
}
builder.setSmallIcon(iconId);
}
private void setNotificationSmallIcon(Bundle extras, String packageName,
Resources resources, Notification.Builder builder,
String localIcon) {
int iconId = 0;
String icon = extras.getString(ICON);
if (icon != null && !"".equals(icon)) {
iconId = resources.getIdentifier(icon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
} else if (localIcon != null && !"".equals(localIcon)) {
iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
}
if (iconId == 0) {
Log.d(LOG_TAG, "no icon resource found - using default icon");
iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName);
}
builder.setSmallIcon(iconId);
}
private void setNotificationIconColor(String color, NotificationCompat.Builder builder,
String localIconColor) {
int iconColor = 0;
if (color != null && !"".equals(color)) {
try {
iconColor = Color.parseColor(color);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "couldn't parse color from android options");
}
} else if (localIconColor != null && !"".equals(localIconColor)) {
try {
iconColor = Color.parseColor(localIconColor);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "couldn't parse color from android options");
}
}
if (iconColor != 0) {
builder.setColor(iconColor);
}
}
private void updateIntent(Intent intent, String callback, Bundle extras, boolean foreground,
int notId) {
intent.putExtra(CALLBACK, callback);
intent.putExtra(PUSH_BUNDLE, extras);
intent.putExtra(FOREGROUND, foreground);
intent.putExtra(NOT_ID, notId);
}
public Bitmap getBitmapFromURL(String strURL, String username, Context context) {
try {
URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(input);
if (bitmap == null && username != null && context != null) {
return AvatarHelper.INSTANCE.getTextBitmap(username, context);
}
return bitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static String getAppName(Context context) {
CharSequence appName = context.getPackageManager()
.getApplicationLabel(context.getApplicationInfo());
return (String) appName;
}
private int parseInt(String value, Bundle extras) {
int retval = 0;
try {
retval = Integer.parseInt(extras.getString(value));
} catch (NumberFormatException e) {
Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
} catch (Exception e) {
Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
}
return retval;
}
private Spanned fromHtml(String source) {
if (source != null) {
return Html.fromHtml(source);
} else {
return null;
}
}
private int extractBadgeCount(Bundle extras) {
int count = -1;
String msgcnt = extras.getString(COUNT);
try {
if (msgcnt != null) {
count = Integer.parseInt(msgcnt);
}
} catch (NumberFormatException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
}
return count;
}
private String getHostname(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
if (!jsonObject.has("host")) {
return null;
}
return ServerPolicyHelper.enforceHostname(jsonObject.getString("host"));
} catch (Exception e) {
return null;
}
}
private String getRoomId(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
if (!jsonObject.has("rid")) {
return null;
}
return jsonObject.getString("rid");
} catch (Exception e) {
return null;
}
}
private String getSenderUsername(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
return jsonObject.getJSONObject("sender").optString("username");
} catch (JSONException e) {
return null;
}
}
private boolean isValidHostname(Context context, String hostname) {
final List<ServerInfo> serverInfoList =
ConnectivityManager.getInstance(context.getApplicationContext()).getServerList();
for (ServerInfo serverInfo : serverInfoList) {
if (serverInfo.getHostname().equals(hostname)) {
return true;
}
}
return false;
}
}
package chat.rocket.android;
import android.content.Context
import chat.rocket.core.utils.Pair
import org.hamcrest.CoreMatchers.equalTo
import org.json.JSONObject
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class RocketChatCacheTest {
lateinit var cache: RocketChatCache
@Before
fun setup() {
val mockedContext = mock(Context::class.java)
val mockAppContext = mock(Context::class.java)
`when`(mockedContext.applicationContext).thenReturn(mockAppContext)
cache = spy(RocketChatCache(mockedContext))
}
@Test
fun getServerList_ShouldReturnHostnameList() {
val hostnameList = JSONObject()
.put("http://open.rocket.chat", "images/logo/logo.png")
.put("http://192.168.0.6:3000", "images/icon.svg")
.toString()
doReturn(hostnameList).`when`(cache).getString("KEY_HOSTNAME_LIST", null)
val expectedServerList = mutableListOf(
Pair("http://192.168.0.6:3000", "http://192.168.0.6:3000/images/icon.svg"),
Pair("http://open.rocket.chat", "http://open.rocket.chat/images/logo/logo.png"))
val serverList = cache.serverList
assertThat(serverList, equalTo(expectedServerList))
}
}
\ No newline at end of file
package chat.rocket.android.push
import android.app.Application
import android.content.Context
import android.content.res.Resources
import android.os.Bundle
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.push.PushManager.PushMessage
import com.nhaarman.mockito_kotlin.*
import org.amshove.kluent.`should equal`
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import kotlin.test.assertTrue
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class,
application = PushManagerTest.StubApplication::class,
sdk = intArrayOf(23))
class PushManagerTest {
val EJSON = """
{
"host":"https://open.rocket.chat/",
"rid":"FaXMyHqbNJbPq6Ym9uWiyfQkgekhXywvKw",
"sender":{
"_id":"uWiFa3adOi0adac",
"username":"jean-luc.picard",
"name":"Jean-Luc Picard"
},
"type":"d",
"name":null
}
"""
val EJSON_NO_SENDER = """
{
"host":"https://open.rocket.chat/",
"rid":"FaXMyHqbNJbPq6Ym9uWiyfQkgekhXywvKw",
"sender":null,
"type":"d",
"name":null
}
"""
lateinit var data: Bundle
lateinit var pushManager: PushManager
lateinit var context: Context
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
data = Bundle()
data.putString("message", "Hello")
data.putString("title", "jean-luc.picard")
data.putString("ejson", EJSON)
data.putString("notId", "1")
context = spy(RuntimeEnvironment.application)
pushManager = spy(PushManager)
val res = mock<Resources> {
on { getColor(any()) } doReturn 0
on { getIdentifier(
anyString(), anyString(), any()) } doReturn R.drawable.notification_background
on { getConfiguration() } doReturn RuntimeEnvironment.application.resources.configuration
}
whenever(context.resources).doReturn(res)
whenever(context.applicationContext).doReturn(context)
}
@Test
fun `should create PushMessage without throwing`() {
PushMessage(null, null, null, null, null, "xxx",
null, null)
}
@Test
fun `given data shoud show notification`() {
pushManager.handle(context, data)
val push = PushMessage(title = data["title"] as String,
message = data["message"] as String, ejson = EJSON, notificationId = "1")
verify(pushManager, times(1)).showNotification(context, push)
}
@Test
fun `given required data is missing do not show notification`() {
val bundle = Bundle()
pushManager.handle(context, bundle)
verify(pushManager, never()).showNotification(any(), any())
bundle.putString("title", "jean-luc.picard")
bundle.putString("message", "Hello!")
pushManager.handle(context, bundle)
verify(pushManager, never()).showNotification(any(), any())
bundle.clear()
bundle.putString("ejson", EJSON)
bundle.putString("message", "Hello!")
pushManager.handle(context, bundle)
verify(pushManager, never()).showNotification(any(), any())
bundle.clear()
bundle.putString("ejson", EJSON)
bundle.putString("title", "jean-luc.picard")
pushManager.handle(context, bundle)
verify(pushManager, never()).showNotification(any(), any())
}
@Test
fun `given data should deserialize correctly`() {
pushManager.handle(context, data)
val push = PushMessage(
data.getString("title"),
data.getString("message"),
null,
EJSON,
null,
data.getString("notId"),
null,
null
)
verify(pushManager, times(1)).showNotification(context, push)
push.title `should equal` "jean-luc.picard"
push.message `should equal` "Hello"
val sender = push.sender
assertTrue(sender != null)
sender?._id `should equal` "uWiFa3adOi0adac"
sender?.name `should equal` "Jean-Luc Picard"
sender?.username `should equal` "jean-luc.picard"
}
@Test
fun `given that only sender is missing show notification`() {
val bundle = Bundle()
bundle.putString("title", "jean-luc.picard")
bundle.putString("message", "Hello")
bundle.putString("ejson", EJSON_NO_SENDER)
pushManager.handle(context, bundle)
verify(pushManager, times(1)).showNotification(any(), any())
}
internal class StubApplication : Application()
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment