From e233dfd693b510671324d2d838e04161a9ae32d0 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sat, 26 Sep 2020 15:56:51 +0200 Subject: [PATCH] rework missed calls * original from https://github.com/iNPUTmice/Conversations/pull/3857 --- .../conversations/entities/Conversation.java | 4 +- .../services/NotificationService.java | 239 +++++++++++++++++- .../services/XmppConnectionService.java | 31 ++- .../xmpp/jingle/JingleRtpConnection.java | 5 +- .../ic_missed_call_notification.png | Bin 0 -> 1231 bytes .../res/drawable-hdpi/ic_notification.png | Bin 825 -> 1197 bytes .../ic_missed_call_notification.png | Bin 0 -> 691 bytes .../res/drawable-mdpi/ic_notification.png | Bin 547 -> 655 bytes .../ic_missed_call_notification.png | Bin 0 -> 1471 bytes .../res/drawable-xhdpi/ic_notification.png | Bin 1104 -> 1409 bytes .../ic_missed_call_notification.png | Bin 0 -> 2845 bytes .../res/drawable-xxhdpi/ic_notification.png | Bin 1735 -> 2752 bytes .../ic_missed_call_notification.png | Bin 0 -> 3220 bytes .../res/drawable-xxxhdpi/ic_notification.png | Bin 3852 -> 2944 bytes src/main/res/values/strings.xml | 5 + 15 files changed, 262 insertions(+), 22 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_missed_call_notification.png create mode 100644 src/main/res/drawable-mdpi/ic_missed_call_notification.png create mode 100644 src/main/res/drawable-xhdpi/ic_missed_call_notification.png create mode 100644 src/main/res/drawable-xxhdpi/ic_missed_call_notification.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_missed_call_notification.png diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index cc2aaef8d..8204411c5 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -247,11 +247,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } - public void findUnreadMessages(OnMessageFound onMessageFound) { + public void findUnreadMessagesAndCalls(OnMessageFound onMessageFound) { final ArrayList results = new ArrayList<>(); synchronized (this.messages) { for (final Message message : this.messages) { - if (message.isRead() || message.getType() == Message.TYPE_RTP_SESSION) { + if (message.isRead()) { continue; } results.add(message); diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 7a7c24aa4..1ef6ddd59 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -86,7 +86,8 @@ public class NotificationService { private static final int CALL_DAT = 120; private static final long[] CALL_PATTERN = {0, 3 * CALL_DAT, CALL_DAT, CALL_DAT, 3 * CALL_DAT, CALL_DAT, CALL_DAT}; - private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations"; + private static final String MESSAGES_GROUP = "eu.siacs.conversations.messages"; + private static final String MISSED_CALLS_GROUP = "eu.siacs.conversations.missed_calls"; private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024; public static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER; public static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4; @@ -95,8 +96,10 @@ public class NotificationService { private static final int INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8; public static final int ONGOING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 10; private static final int DELIVERY_FAILED_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 12; + public static final int MISSED_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 14; private final XmppConnectionService mXmppConnectionService; private final LinkedHashMap> notifications = new LinkedHashMap<>(); + private final LinkedHashMap mMissedCalls = new LinkedHashMap<>(); private final HashMap mBacklogMessageCounter = new HashMap<>(); private Conversation mOpenConversation; private boolean mIsInForeground; @@ -184,6 +187,15 @@ public class NotificationService { ongoingCallsChannel.setGroup("calls"); notificationManager.createNotificationChannel(ongoingCallsChannel); + final NotificationChannel missedCallsChannel = new NotificationChannel("missed_calls", + c.getString(R.string.missed_calls_channel_name), + NotificationManager.IMPORTANCE_HIGH); + missedCallsChannel.setShowBadge(true); + missedCallsChannel.setSound(null, null); + missedCallsChannel.setLightColor(LED_COLOR); + missedCallsChannel.enableLights(true); + missedCallsChannel.setGroup("calls"); + notificationManager.createNotificationChannel(missedCallsChannel); final NotificationChannel messagesChannel = new NotificationChannel("messages", c.getString(R.string.messages_channel_name), @@ -235,12 +247,18 @@ public class NotificationService { notificationManager.createNotificationChannel(deliveryFailedChannel); } - private boolean notify(final Message message) { + public boolean notifyMessage(final Message message) { final Conversation conversation = (Conversation) message.getConversation(); return message.getStatus() == Message.STATUS_RECEIVED && !conversation.isMuted() && (conversation.alwaysNotify() || wasHighlightedOrPrivate(message)) - && (!conversation.isWithStranger() || notificationsFromStrangers()); + && (!conversation.isWithStranger() || notificationsFromStrangers()) + && message.getType() != Message.TYPE_RTP_SESSION; + } + + private boolean notifyMissedCall(final Message message) { + return message.getType() == Message.TYPE_RTP_SESSION + && message.getStatus() == Message.STATUS_RECEIVED; } public boolean notificationsFromStrangers() { @@ -264,11 +282,15 @@ public class NotificationService { } public void pushFromBacklog(final Message message) { - if (notify(message)) { + if (notifyMessage(message)) { synchronized (notifications) { getBacklogMessageCounter((Conversation) message.getConversation()).incrementAndGet(); pushToStack(message); } + } else if (notifyMissedCall(message)) { + synchronized (mMissedCalls) { + pushMissedCall(message); + } } } @@ -303,6 +325,9 @@ public class NotificationService { updateNotification(count > 0, conversations); } } + synchronized (mMissedCalls) { + updateMissedCallNotifications(mMissedCalls.keySet()); + } } private List getBacklogConversations(Account account) { @@ -494,7 +519,7 @@ public class NotificationService { private void pushNow(final Message message) { mXmppConnectionService.updateUnreadCountBadge(); final boolean isScreenOn = mXmppConnectionService.isInteractive(); - if (!notify(message)) { + if (!notifyMessage(message)) { if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) { mXmppConnectionService.vibrate(); } @@ -520,7 +545,29 @@ public class NotificationService { } } - public void clear() { + private void pushMissedCall(final Message message) { + final Conversational conversation = message.getConversation(); + final MissedCallsInfo info = mMissedCalls.get(conversation); + if (info == null) { + mMissedCalls.put(conversation, new MissedCallsInfo(message.getTimeSent())); + } else { + info.newMissedCall(message.getTimeSent()); + } + } + + public void pushMissedCallNow(final Message message) { + synchronized (mMissedCalls) { + pushMissedCall(message); + updateMissedCallNotifications(Collections.singleton(message.getConversation())); + } + } + + public void clear(final Conversation conversation) { + clearMessages(conversation); + clearMissedCalls(conversation); + } + + public void clearMessages() { synchronized (notifications) { for (ArrayList messages : notifications.values()) { markAsReadIfHasDirectReply(messages); @@ -530,7 +577,7 @@ public class NotificationService { } } - public void clear(final Conversation conversation) { + public void clearMessages(final Conversation conversation) { synchronized (this.mBacklogMessageCounter) { this.mBacklogMessageCounter.remove(conversation); } @@ -543,6 +590,25 @@ public class NotificationService { } } + public void clearMissedCalls() { + synchronized (mMissedCalls) { + for (final Conversational conversation : mMissedCalls.keySet()) { + cancel(conversation.getUuid(), MISSED_CALL_NOTIFICATION_ID); + } + mMissedCalls.clear(); + updateMissedCallNotifications(null); + } + } + + public void clearMissedCalls(final Conversation conversation) { + synchronized (mMissedCalls) { + if (mMissedCalls.remove(conversation) != null) { + cancel(conversation.getUuid(), MISSED_CALL_NOTIFICATION_ID); + updateMissedCallNotifications(null); + } + } + } + private void markAsReadIfHasDirectReply(final Conversation conversation) { markAsReadIfHasDirectReply(notifications.get(conversation.getUuid())); } @@ -606,7 +672,7 @@ public class NotificationService { singleBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); } modifyForSoundVibrationAndLight(singleBuilder, notifyThis, quiteHours, preferences); - singleBuilder.setGroup(CONVERSATIONS_GROUP); + singleBuilder.setGroup(MESSAGES_GROUP); setNotificationColor(singleBuilder); notify(entry.getKey(), NOTIFICATION_ID, singleBuilder.build()); } @@ -616,6 +682,31 @@ public class NotificationService { } } + private void updateMissedCallNotifications(final Set update) { + if (mMissedCalls.isEmpty()) { + cancel(MISSED_CALL_NOTIFICATION_ID); + return; + } + if (mMissedCalls.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + final Conversational conversation = mMissedCalls.keySet().iterator().next(); + final MissedCallsInfo info = mMissedCalls.values().iterator().next(); + final Notification notification = missedCall(conversation, info); + notify(MISSED_CALL_NOTIFICATION_ID, notification); + } else { + final Notification summary = missedCallsSummary(); + notify(MISSED_CALL_NOTIFICATION_ID, summary); + if (update != null) { + for (final Conversational conversation : update) { + final MissedCallsInfo info = mMissedCalls.get(conversation); + if (info != null) { + final Notification notification = missedCall(conversation, info); + notify(conversation.getUuid(), MISSED_CALL_NOTIFICATION_ID, notification); + } + } + } + } + } + private void modifyForSoundVibrationAndLight(Builder mBuilder, boolean notify, boolean quietHours, SharedPreferences preferences) { final Resources resources = mXmppConnectionService.getResources(); final String ringtone = preferences.getString("notification_ringtone", resources.getString(R.string.notification_ringtone)); @@ -674,6 +765,101 @@ public class NotificationService { } } + private Notification missedCallsSummary() { + final Builder publicBuilder = buildMissedCallsSummary(true); + final Builder builder = buildMissedCallsSummary(false); + builder.setPublicVersion(publicBuilder.build()); + return builder.build(); + } + + private Builder buildMissedCallsSummary(boolean publicVersion) { + final Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "missed_calls"); + int totalCalls = 0; + final StringBuilder names = new StringBuilder(); + long lastTime = 0; + for (Map.Entry entry : mMissedCalls.entrySet()) { + final Conversational conversation = entry.getKey(); + final MissedCallsInfo missedCallsInfo = entry.getValue(); + names.append(conversation.getContact().getDisplayName()); + names.append(", "); + totalCalls += missedCallsInfo.getNumberOfCalls(); + lastTime = Math.max(lastTime, missedCallsInfo.getLastTime()); + } + if (names.length() >= 2) { + names.delete(names.length() - 2, names.length()); + } + final String title = (totalCalls == 1) ? mXmppConnectionService.getString(R.string.missed_call) : + (mMissedCalls.size() == 1) ? mXmppConnectionService.getString(R.string.n_missed_calls, totalCalls) : + mXmppConnectionService.getString(R.string.n_missed_calls_from_m_contacts, totalCalls, mMissedCalls.size()); + builder.setContentTitle(title); + builder.setTicker(title); + if (!publicVersion) { + builder.setContentText(names.toString()); + } + builder.setSmallIcon(R.drawable.ic_missed_call_notification); + builder.setGroupSummary(true); + builder.setGroup(MISSED_CALLS_GROUP); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN); + builder.setCategory(NotificationCompat.CATEGORY_CALL); + builder.setWhen(lastTime); + if (!mMissedCalls.isEmpty()) { + final Conversational firstConversation = mMissedCalls.keySet().iterator().next(); + builder.setContentIntent(createContentIntent(firstConversation)); + } + builder.setDeleteIntent(createMissedCallsDeleteIntent(null)); + modifyMissedCall(builder); + return builder; + } + + private Notification missedCall(final Conversational conversation, final MissedCallsInfo info) { + final Builder publicBuilder = buildMissedCall(conversation, info, true); + final Builder builder = buildMissedCall(conversation, info, false); + builder.setPublicVersion(publicBuilder.build()); + return builder.build(); + } + + private Builder buildMissedCall(final Conversational conversation, final MissedCallsInfo info, boolean publicVersion) { + final Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "missed_calls"); + final String title = (info.getNumberOfCalls() == 1) ? mXmppConnectionService.getString(R.string.missed_call) : + mXmppConnectionService.getString(R.string.n_missed_calls, info.getNumberOfCalls()); + builder.setContentTitle(title); + final String name = conversation.getContact().getDisplayName(); + if (publicVersion) { + builder.setTicker(title); + } else { + if (info.getNumberOfCalls() == 1) { + builder.setTicker(mXmppConnectionService.getString(R.string.missed_call_from_x, name)); + } else { + builder.setTicker(mXmppConnectionService.getString(R.string.n_missed_calls_from_x, info.getNumberOfCalls(), name)); + } + builder.setContentText(name); + } + builder.setSmallIcon(R.drawable.ic_missed_call_notification); + builder.setGroup(MISSED_CALLS_GROUP); + builder.setCategory(NotificationCompat.CATEGORY_CALL); + builder.setWhen(info.getLastTime()); + builder.setContentIntent(createContentIntent(conversation)); + builder.setDeleteIntent(createMissedCallsDeleteIntent(conversation)); + if (!publicVersion && conversation instanceof Conversation) { + builder.setLargeIcon(mXmppConnectionService.getAvatarService() + .get((Conversation) conversation, AvatarService.getSystemUiAvatarSize(mXmppConnectionService))); + } + modifyMissedCall(builder); + return builder; + } + + private void modifyMissedCall(final Builder builder) { + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); + final Resources resources = mXmppConnectionService.getResources(); + final boolean led = preferences.getBoolean("led", resources.getBoolean(R.bool.led)); + if (led) { + builder.setLights(LED_COLOR, 2000, 3000); + } + builder.setPriority(NotificationCompat.PRIORITY_HIGH); + builder.setSound(null); + setNotificationColor(builder); + } + private Builder buildMultipleConversation(final boolean notify, final boolean quietHours) { final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages")); final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); @@ -710,7 +896,7 @@ public class NotificationService { mBuilder.setContentIntent(createOpenConversationsIntent()); } mBuilder.setGroupSummary(true); - mBuilder.setGroup(CONVERSATIONS_GROUP); + mBuilder.setGroup(MESSAGES_GROUP); mBuilder.setDeleteIntent(createDeleteIntent(null)); mBuilder.setSmallIcon(R.drawable.ic_notification); return mBuilder; @@ -1015,7 +1201,7 @@ public class NotificationService { private PendingIntent createDeleteIntent(Conversation conversation) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); - intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION); + intent.setAction(XmppConnectionService.ACTION_CLEAR_MESSAGE_NOTIFICATION); if (conversation != null) { intent.putExtra("uuid", conversation.getUuid()); return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 20), intent, 0); @@ -1023,6 +1209,16 @@ public class NotificationService { return PendingIntent.getService(mXmppConnectionService, 0, intent, 0); } + private PendingIntent createMissedCallsDeleteIntent(final Conversational conversation) { + final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); + intent.setAction(XmppConnectionService.ACTION_CLEAR_MISSED_CALL_NOTIFICATION); + if (conversation != null) { + intent.putExtra("uuid", conversation.getUuid()); + return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 21), intent, 0); + } + return PendingIntent.getService(mXmppConnectionService, 1, intent, 0); + } + private PendingIntent createReplyIntent(Conversation conversation, boolean dismissAfterReply) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION); @@ -1314,4 +1510,27 @@ public class NotificationService { Log.d(Config.LOGTAG, "unable to cancel notification", e); } } + + private static class MissedCallsInfo { + private int numberOfCalls; + private long lastTime; + + MissedCallsInfo(final long time) { + numberOfCalls = 1; + lastTime = time; + } + + public void newMissedCall(final long time) { + ++numberOfCalls; + lastTime = time; + } + + public int getNumberOfCalls() { + return numberOfCalls; + } + + public long getLastTime() { + return lastTime; + } + } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index b543955ca..3b2a27d4e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -181,7 +181,8 @@ public class XmppConnectionService extends Service { public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations"; public static final String ACTION_MARK_AS_READ = "mark_as_read"; public static final String ACTION_SNOOZE = "snooze"; - public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification"; + public static final String ACTION_CLEAR_MESSAGE_NOTIFICATION = "clear_message_notification"; + public static final String ACTION_CLEAR_MISSED_CALL_NOTIFICATION = "clear_missed_call_notification"; public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error"; public static final String ACTION_TRY_AGAIN = "try_again"; public static final String ACTION_IDLE_PING = "idle_ping"; @@ -661,19 +662,35 @@ public class XmppConnectionService extends Service { case Intent.ACTION_SHUTDOWN: logoutAndSave(true); return START_NOT_STICKY; - case ACTION_CLEAR_NOTIFICATION: + case ACTION_CLEAR_MESSAGE_NOTIFICATION: mNotificationExecutor.execute(() -> { try { final Conversation c = findConversationByUuid(uuid); if (c != null) { - mNotificationService.clear(c); + mNotificationService.clearMessages(c); } else { - mNotificationService.clear(); + mNotificationService.clearMessages(); } restoredFromDatabaseLatch.await(); } catch (InterruptedException e) { - Log.d(Config.LOGTAG, "unable to process clear notification"); + Log.d(Config.LOGTAG, "unable to process clear message notification"); + } + }); + break; + case ACTION_CLEAR_MISSED_CALL_NOTIFICATION: + mNotificationExecutor.execute(() -> { + try { + final Conversation c = findConversationByUuid(uuid); + if (c != null) { + mNotificationService.clearMissedCalls(c); + } else { + mNotificationService.clearMissedCalls(); + } + restoredFromDatabaseLatch.await(); + + } catch (InterruptedException e) { + Log.d(Config.LOGTAG, "unable to process clear missed call notification"); } }); break; @@ -765,7 +782,7 @@ public class XmppConnectionService extends Service { return; } c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000); - mNotificationService.clear(c); + mNotificationService.clearMessages(c); updateConversation(c); }); case AudioManager.RINGER_MODE_CHANGED_ACTION: @@ -2083,7 +2100,7 @@ public class XmppConnectionService extends Service { private void restoreMessages(Conversation conversation) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); - conversation.findUnreadMessages(message -> mNotificationService.pushFromBacklog(message)); + conversation.findUnreadMessagesAndCalls(message -> mNotificationService.pushFromBacklog(message)); } public void loadPhoneContacts() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 993661fa3..5ad0a6ac3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -582,6 +582,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web rejectCallFromSessionInitiate(); break; } + xmppConnectionService.getNotificationService().pushMissedCallNow(message); } private void cancelRingingTimeout() { @@ -624,6 +625,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web final State target = this.state == State.PROCEED ? State.RETRACTED_RACED : State.RETRACTED; if (transition(target)) { xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); + xmppConnectionService.getNotificationService().pushMissedCallNow(message); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted (serverMsgId=" + serverMsgId + ")"); if (serverMsgId != null) { this.message.setServerMsgId(serverMsgId); @@ -1235,9 +1237,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private void writeLogMessageMissed() { this.message.setBody(new RtpSessionStatus(false, 0).toString()); - if (this.state != State.REJECTED) { - xmppConnectionService.getNotificationService().push(message); - } this.writeMessage(); } diff --git a/src/main/res/drawable-hdpi/ic_missed_call_notification.png b/src/main/res/drawable-hdpi/ic_missed_call_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ac22b3414f755fce11542dcdfab9572901f660 GIT binary patch literal 1231 zcmV;=1Tg!FP)~cI%e$4n6Y1ExgtdVa7_uJ6v=WWhMR6#{;-6?b?+oH$t}rJj6cYd5s^J> z?(N<|CWJ^e~`gQ1+1MTaYOe5eJ@C0~YU0odol4{R@{&h{L9;8u*z5;j(GNpLl z4{XEpZ$L|%IZc7dzz^ReXDXb!Xh+Er9kw5B?(S&i#tmr9AhHu1 zSK{{<swBlBKwDs| z-hBe*IBCJ^(i?P=|746Akjh3Gaw;u>gV25k$li8|RDq|FW57`0rKGc(k^!0ZCf826 zVV%e5y3hfDetuNNXnR=mF9X@IftfPI+G;0&9KW{H3y9K1;Uyz^5=iD+vUb~NNNS;{ z?TFAY5V(T=AkYfZ`I(W}a2%=83BGlTH80V?{YoiY-jI%oM@;L`>(;v9rVEF+nqR5s zN@yV8v}ZHIlbm9W(UzLCx(#+akcj}Zb;$KL>rKZ#|1d#&NvfU2vzD0tYXFQ;9Qj4K zYD4A?H&kRNp%T-7G>ed`1h%6LFOGzw(P)1=LW)x%rGr~8YI+UK)A;s^`Vo^IQN`&X z^oRE0ftt8UXs`pJc}l7oN-O1do}}d4NGLIr5uw{6^t&A5FCfcIpfNBUm;tnoL?XQ$ z2o(YMfkBc~A4$sav{%3ssVP^rlbxqs)ciudx=-&FsX%HXnY~uYn4MRx5IpO3dJoAw0hw8; z7WW}*d4T7RP~QPdDDX?>HOR2?tY=x1HF|fTO{zq*fHm0Qg@@r9cE!Suw~*cr>#fk( z?Y`i4u@MPePr6KIZG0!Lo^6VXq8S( zFI>#@B74iLs;cU&TA6(wE{v_Djy4Raz&VgSkA9}v-6=WM@04c8pf**cLd&qBkHB$# zV92$**YWabqe`lKt|B_KL#f^`PURy5Z=;)mySi7gy<4!1C;fi2(=__I(bWI@sG7chIc1saJqI_bN?&j{1O{cGTP?jHx|#LPJp z+zKun60@@CJg^8{3eE;&K~r~k_js8z`hI_Z|3b$90^fq=pfNRJ2O;uww9-ny8GMbV z_JKX%1Mb)5a(}t&=tuee1z?}engOe1`lP~~58B0vl3?0EWJZupYe5g7 zOS(+P5ol$tiJYe8`4I7Ej`{YIg<4*b6}e-9u10{*>E8h3 z(9U8-;u;Gi5ByXFuU1QA><#rye5*J#tqTjx3-&xqs3@Fc%}}(0r++72tn5@9W7wInsH#^D( zYZu=3$-jW>?_xYk$=rOde~65WLu)+r$$$e_3-ZzdpyT33r3m4d%52Jiz=;?)*3qEc z=vWyN{|bH&P00s7>)dLshc*w0O-en%UUQ<=>fAd-UX~-qE%CARvd1>WT7%=I zKB+KG&aWrS0~HdKp7_vIi6gS^=e`rX&$R;H!b~PJ*41uf%$m!rae4A7Nd;QvFP0as z)bV1zP3i6JZE>!wehLNSIGbYvQ0@N_Lhhhn8oIkQ=losTY);Clb>LXQq2u6P|9@bZ z99P$=c#JWn%zdXc;?tqS<15qrWKcEw4EWx4mD(Q)s4?$PbYa-(TCClsl{TKXKw9$! zuk&hR9tFWuRqgd)wKtm<^WF&a?L5zEbOE|Tsf~{f;UPQ7j=288M?0fG1*wiC{C%zl0gCjbBd07*qoM6N<$f~LGvNdN!< delta 803 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 ZKLHQ|cS#oi1Oxy8002ovPDHLkV1hzAza{_x literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_notification.png b/src/main/res/drawable-xhdpi/ic_notification.png index 0c7eba383b4523b8cce52c998d7f40209a0588c2..75d7703b6dbcf0abd5562858c536596f2bc88368 100644 GIT binary patch delta 1391 zcmV-#1(5pC2!RWbBYy=*Nkl5v$^hWc5lA} zXXtU9yZ7$iySuSC@G+a2xo6IqIj@;Xvu5wgf5o=#7C>8|1AovFc#qM%DKp&~m<+5* zBoc>#JMnltirQ8{S=3aSrw>%hb;%d+~S&P!nt$VRRL7O;tSwJ8{K&qyRv6pcnlQjNb0sNFQav9WQU zrf#=@VL>v~+<(KEmcS?2h3`>c03JepUa=9AK|e;@C15ugUPvBFeA^e_9nyq&1WX9Z zNOuAg9s0Xr@>ReSFd4Hw6pU*GLinZvyE9ZR5>N^}5kqT%seV;qKNEWh43+({SZoRy zxo-O_wGd=A>Q+4G2%tvv{{s}JE5KT9vd@)Zn6wq3e@T>*$LAx1n!CMU!3eG z8S|qx-@ZFEj7w~_^Di+fD^XY^b*xbIfez&8N06SWN)-J7#4YoHtvA71DBNmBHMke zf~yx5(gh$U2515tHRUxL9a4d?L4XO`xE&guKMVp;hT6l#Y%5|8_^7_VzK1%iYFnSc zs(J&H-0lJPpx04TKE>Zc^uFA;1k|d_d!8V)7G0MPj+~p>+IY z-uepxs+{z_h;If+@|P+X0g($i3S|jw+-a%FslXgFHI+r>n~_LQwM4gTc633n1T74l+VH9CgPMJv_-hW zzq6z|WQWCmPGvL50Wh(MN~t-UZM< zUwl#?6!YpXvn5n&HlLPkH&;su9itkn{-rS~dvmV2g2azBr(fpjkWvL~0Q!mc0%D4z zy3pPaTcjn2Gtzi9IJ&!w)j z8%=(o@hJ=$;xu3-@U7dmIO(`4S>xUPn2xnF1HkV#z+D!A61~ou403rvM^sL4wuxO> x_%{9@20eH@<@dUxw^4vRem!dUzxofEe*yjYJZhi$)Bykh002ovPDHLkV1hD_lePc= delta 1084 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 krE{*yHIt`2z{JOBUy07*qoM6N<$f^RvDZvX%Q literal 3852 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 O0000Some messages could not be delivered Failed deliveries + Missed calls + Missed call from %s + %1$d missed calls from %2$s + %d missed calls + %1$d missed calls from %2$d contacts