Skip to content

Instantly share code, notes, and snippets.

@IlyaLavrov97
Last active March 17, 2020 20:56
Show Gist options
  • Save IlyaLavrov97/a711d6d7d33a2df813165a6407a4500f to your computer and use it in GitHub Desktop.
Save IlyaLavrov97/a711d6d7d33a2df813165a6407a4500f to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Util;
using Firebase.Messaging;
using Newtonsoft.Json.Linq;
using Notifications.FCM.Android.Activities;
using Notifications.FCM.Android.Receivers;
namespace Notifications.FCM.Android
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
public const string SenderNameTag = "senderName";
public const string SenderIdTag = "senderId";
public const string ChatIdTag = "chatId";
public const string NotificationBodyTag = "body";
public const string NotificationTitleTag = "title";
public const string GROUP_KEY_CHAT_MESSAGE = "GROUP_CHAT_MESSAGE";
// NEW CODE random id
const int _summaryNotificationId = 12369;
const int _maxLinesMinApi = 5;
const string TAG = nameof(MyFirebaseMessagingService);
const string DefaultTitle = "Chat Message";
// NEW CODE
private static NotificationManagerCompat notificationManager = null;
private static Dictionary<int, List<ChatMessage>> ChatsMessagesNotify = new Dictionary<int, List<ChatMessage>>();
private class ChatMessage
{
public string Text { get; set; }
public string SenderName { get; set; }
public long DateTimeMilis { get; set; }
// NEW CODE
public NotificationCompat.MessagingStyle.Message GetNotificationMessage()
{
return new NotificationCompat.MessagingStyle.Message(Text, DateTimeMilis, SenderName);
}
}
public static void RemoveMessagesFromChat(int id)
{
if (ChatsMessagesNotify.ContainsKey(id))
ChatsMessagesNotify[id].Clear();
}
public override void OnNewToken(string p0)
{
base.OnNewToken(p0);
Log.Debug(TAG, "Refreshed token: " + p0);
SendRegistrationToServer(p0);
}
public override void OnMessageReceived(RemoteMessage message)
{
try
{
// TODO get some configs. Check top fragment to decide, show notification or not
}
catch
{
// App in the background. Services cannot be resolved.
}
// TODO rework, when server side will be ready. Decide which approach is better
if (message.Data.ContainsKey("data"))
{
var data = JObject.Parse(message.Data["data"]);
if (message.GetNotification() != null)
{
var notification = message.GetNotification();
SendNotification(notification.Body, string.IsNullOrEmpty(notification.Title) ? DefaultTitle : notification.Title, data.ToObject<Dictionary<string, string>>());
}
else
{
if (!message.Data.ContainsKey("notification"))
return;
var notification = JObject.Parse(message.Data["notification"]);
SendNotification(notification[NotificationBodyTag].ToString(), string.IsNullOrEmpty(notification[NotificationTitleTag]?.ToString()) ? DefaultTitle : notification[NotificationTitleTag].ToString(), data.ToObject<Dictionary<string, string>>());
}
}
else
{
// Probably this approach is better for production. Example server push notification:
// { "message": { "token": "...", "data":{ //content key-value pairs// } } }
if (message.Data.TryGetValue(NotificationBodyTag, out string messageBody) &&
message.Data.TryGetValue(NotificationTitleTag, out string titleBody))
{
SendNotification(messageBody, titleBody, message.Data);
}
}
}
private void SendRegistrationToServer(string token)
{
// Add custom implementation, as needed.
}
private void SendNotification(string messageBody, string messageTitle, IDictionary<string, string> data)
{
Intent intent = null;
int notificationId = GetNewIdForNotification();
try
{
// TODO Update foreground app via services and create intent
intent = GetForegroundIntent();
}
catch
{
// App killed and IoC not initialized. We create intent manually
intent = GetBackgroundIntent();
}
intent.AddFlags(ActivityFlags.SingleTop);
SafePutExtra(SenderNameTag, string.Empty, intent, data);
SafePutExtra(SenderIdTag, string.Empty, intent, data);
SafePutExtra(ChatIdTag, string.Empty, intent, data);
SafePutExtra(NotificationBodyTag, string.Empty, intent, data);
SafePutExtra(NotificationTitleTag, string.Empty, intent, data);
var notificationBuilder = GetBaseChatNotificationBuilder(intent, messageBody, notificationId); // NEW CODE
List<ChatMessage> currentMessages = new List<ChatMessage>();
ChatMessage newMessage = new ChatMessage
{
Text = messageBody,
DateTimeMilis = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond,
SenderName = data[SenderNameTag]?.ToString()
};
if (int.TryParse(data[ChatIdTag].ToString(), out int chatId))
{
notificationId = chatId;
if (ChatsMessagesNotify.ContainsKey(chatId))
{
ChatsMessagesNotify[chatId].Add(newMessage);
foreach (var message in ChatsMessagesNotify[chatId])
currentMessages.Add(message);
}
else
{
ChatsMessagesNotify[chatId] = new List<ChatMessage> { newMessage };
currentMessages.Add(newMessage);
}
}
else
{
currentMessages.Add(newMessage);
}
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(string.Empty);
var conversationTitle = string.Empty;
messagingStyle.SetConversationTitle(messageTitle);
foreach (var message in currentMessages)
messagingStyle.AddMessage(message.GetNotificationMessage());
notificationBuilder.SetStyle(messagingStyle);
}
else
{
if (currentMessages.Count > 1)
{
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
notificationBuilder.SetContentTitle(messageTitle);
for (int i = currentMessages.Count >= _maxLinesMinApi ? currentMessages.Count - _maxLinesMinApi : 0; i < currentMessages.Count; i++)
{
inboxStyle.AddLine(currentMessages[i].SenderName + ": " + currentMessages[i].Text);
}
if (currentMessages.Count - _maxLinesMinApi > 0)
inboxStyle.SetSummaryText($"+{currentMessages.Count - _maxLinesMinApi} more");
notificationBuilder.SetStyle(inboxStyle);
}
else if (currentMessages.Count == 1) // because with one message AddLine doesn't work
{
notificationBuilder.SetContentTitle(messageTitle)
.SetContentText(currentMessages[0].SenderName + ": " + currentMessages[0].Text);
}
}
try
{
SetupIconForNotification(notificationBuilder, data);
}
catch
{
// TODO handle
}
// NEW CODE
if (notificationManager == null)
{
notificationManager = NotificationManagerCompat.From(this);
}
notificationManager.Notify(notificationId, notificationBuilder.Build());
// NEW CODE
if (ChatsMessagesNotify.Count > 1)
{
notificationManager.Notify(_summaryNotificationId, BuildSummaryChatNotification());
}
}
private void SafePutExtra(string key, string defaultValue, Intent intent, IDictionary<string, string> data)
{
if (data.TryGetValue(key, out string value))
{
intent.PutExtra(key, value);
}
else
{
intent.PutExtra(key, defaultValue);
}
}
// NEW CODE
private NotificationCompat.Builder GetBaseBuilder(string channel)
{
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
notificationBuilder.SetChannelId(channel);
}
return notificationBuilder;
}
// NEW CODE
private NotificationCompat.Builder GetBaseChatNotificationBuilder(Intent intent, string message, int notificationId)
{
var notificationBuilder = GetBaseBuilder(GetString(Resource.String.notifications_chat_channel_id));
var pendingIntent = PendingIntent.GetActivity(this, notificationId, intent, PendingIntentFlags.UpdateCurrent);
return notificationBuilder
.SetColor(new Color(ContextCompat.GetColor(this, Resource.Color.primary_material_light)))
.SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_circle)
.SetAutoCancel(true)
.SetShowWhen(true)
.SetTicker(message)
.SetGroup(GROUP_KEY_CHAT_MESSAGE)
.SetDeleteIntent(CreateOnDismissedIntent(this, notificationId))
.SetContentIntent(pendingIntent);
}
// NEW CODE
private Notification BuildSummaryChatNotification()
{
var notificationBuilder = GetBaseBuilder(GetString(Resource.String.notifications_chat_channel_id));
int messagesCount = ChatsMessagesNotify.Values.Select(messages => messages.Count).Sum();
return notificationBuilder
.SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_circle)
.SetStyle(new NotificationCompat.InboxStyle()
.SetSummaryText($"{messagesCount} messages from {ChatsMessagesNotify.Count} chats"))
.SetGroup(GROUP_KEY_CHAT_MESSAGE)
.SetGroupSummary(true)
.Build();
}
private PendingIntent CreateOnDismissedIntent(Context context, int notificationId)
{
Intent intent = new Intent(context, typeof(NotificationDismissedReceiver));
intent.PutExtra(context.GetString(Resource.String.notification_id_dismissed), notificationId);
PendingIntent pendingIntent = PendingIntent.GetBroadcast(context.ApplicationContext, notificationId, intent, 0);
return pendingIntent;
}
private void SetupIconForNotification(NotificationCompat.Builder notificationBuilder, IDictionary<string, string> data)
{
// TODO setup icon following the data type
}
private Intent GetForegroundIntent()
{
Intent intent = new Intent(ApplicationContext, typeof(ChatActivity));
return intent;
}
private Intent GetBackgroundIntent()
{
Intent intent = new Intent(ApplicationContext, typeof(ChatActivity));
return intent;
}
/// <summary>
/// Generate ID for unknown object
/// </summary>
private int GetNewIdForNotification()
{
Random rand = new Random();
return rand.Next(1, int.MaxValue - 1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment