Last active
March 17, 2020 20:56
-
-
Save IlyaLavrov97/a711d6d7d33a2df813165a6407a4500f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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