use PM on direct reply if last message in notifacation stack is PM

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java          | 11 
src/main/java/eu/siacs/conversations/entities/Message.java               | 27 
src/main/java/eu/siacs/conversations/services/NotificationService.java   | 23 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 11 
4 files changed, 53 insertions(+), 19 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -271,6 +271,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         return null;
     }
 
+    public Message findMessageWithUuid(final String uuid) {
+        synchronized (this.messages) {
+            for (final Message message : this.messages) {
+                if (message.getUuid().equals(uuid)) {
+                    return message;
+                }
+            }
+        }
+        return null;
+    }
+
     public boolean markAsDeleted(final List<String> uuids) {
         boolean deleted = false;
         final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService();

src/main/java/eu/siacs/conversations/entities/Message.java 🔗

@@ -984,13 +984,28 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
         }
         if (conversation.getMode() == Conversation.MODE_MULTI) {
             final Jid nextCounterpart = conversation.getNextCounterpart();
-            if (nextCounterpart != null) {
-                message.setCounterpart(nextCounterpart);
-                message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(nextCounterpart));
-                message.setType(isFile ? Message.TYPE_PRIVATE_FILE : Message.TYPE_PRIVATE);
-                return true;
-            }
+            return configurePrivateMessage(conversation, message, nextCounterpart, isFile);
         }
         return false;
     }
+
+    public static boolean configurePrivateMessage(final Message message, final Jid counterpart) {
+        final Conversation conversation;
+        if (message.conversation instanceof Conversation) {
+            conversation = (Conversation) message.conversation;
+        } else {
+            return false;
+        }
+        return configurePrivateMessage(conversation, message, counterpart, false);
+    }
+
+    private static boolean configurePrivateMessage(final Conversation conversation, final Message message, final Jid counterpart, final boolean isFile) {
+        if (counterpart == null) {
+            return false;
+        }
+        message.setCounterpart(counterpart);
+        message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterpart));
+        message.setType(isFile ? Message.TYPE_PRIVATE_FILE : Message.TYPE_PRIVATE);
+        return true;
+    }
 }

src/main/java/eu/siacs/conversations/services/NotificationService.java 🔗

@@ -35,6 +35,7 @@ import androidx.core.content.ContextCompat;
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
 
 import java.io.File;
 import java.io.IOException;
@@ -407,7 +408,7 @@ public class NotificationService {
             currentInterruptionFilter = 1; //INTERRUPTION_FILTER_ALL
         }
         if (currentInterruptionFilter != 1) {
-            Log.d(Config.LOGTAG,"do not ring or vibrate because interruption filter has been set to "+currentInterruptionFilter);
+            Log.d(Config.LOGTAG, "do not ring or vibrate because interruption filter has been set to " + currentInterruptionFilter);
             return;
         }
         final ScheduledFuture<?> currentVibrationFuture = this.vibrationFuture;
@@ -424,13 +425,13 @@ public class NotificationService {
         final Resources resources = mXmppConnectionService.getResources();
         final String ringtonePreference = preferences.getString("call_ringtone", resources.getString(R.string.incoming_call_ringtone));
         if (Strings.isNullOrEmpty(ringtonePreference)) {
-            Log.d(Config.LOGTAG,"ringtone has been set to none");
+            Log.d(Config.LOGTAG, "ringtone has been set to none");
             return;
         }
         final Uri uri = Uri.parse(ringtonePreference);
         this.currentlyPlayingRingtone = RingtoneManager.getRingtone(mXmppConnectionService, uri);
         if (this.currentlyPlayingRingtone == null) {
-            Log.d(Config.LOGTAG,"unable to find ringtone for uri "+uri);
+            Log.d(Config.LOGTAG, "unable to find ringtone for uri " + uri);
             return;
         }
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -790,17 +791,18 @@ public class NotificationService {
                         .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
                         .setShowsUserInterface(false)
                         .build();
-                String replyLabel = mXmppConnectionService.getString(R.string.reply);
-                NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
+                final String replyLabel = mXmppConnectionService.getString(R.string.reply);
+                final String lastMessageUuid = Iterables.getLast(messages).getUuid();
+                final NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
                         R.drawable.ic_send_text_offline,
                         replyLabel,
-                        createReplyIntent(conversation, false))
+                        createReplyIntent(conversation, lastMessageUuid, false))
                         .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
                         .setShowsUserInterface(false)
                         .addRemoteInput(remoteInput).build();
-                NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply,
+                final NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply,
                         replyLabel,
-                        createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build();
+                        createReplyIntent(conversation, lastMessageUuid, true)).addRemoteInput(remoteInput).build();
                 mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction));
                 int addedActionsCount = 1;
                 mBuilder.addAction(markReadAction);
@@ -1066,13 +1068,14 @@ public class NotificationService {
         return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
     }
 
-    private PendingIntent createReplyIntent(Conversation conversation, boolean dismissAfterReply) {
+    private PendingIntent createReplyIntent(final Conversation conversation, final String lastMessageUuid, final boolean dismissAfterReply) {
         final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
         intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
         intent.putExtra("uuid", conversation.getUuid());
         intent.putExtra("dismiss_notification", dismissAfterReply);
+        intent.putExtra("last_message_uuid", lastMessageUuid);
         final int id = generateRequestCode(conversation, dismissAfterReply ? 12 : 14);
-        return PendingIntent.getService(mXmppConnectionService, id, intent, 0);
+        return PendingIntent.getService(mXmppConnectionService, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     private PendingIntent createReadPendingIntent(Conversation conversation) {

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -726,6 +726,7 @@ public class XmppConnectionService extends Service {
                     }
                     final CharSequence body = remoteInput.getCharSequence("text_reply");
                     final boolean dismissNotification = intent.getBooleanExtra("dismiss_notification", false);
+                    final String lastMessageUuid = intent.getStringExtra("last_message_uuid");
                     if (body == null || body.length() <= 0) {
                         break;
                     }
@@ -734,7 +735,7 @@ public class XmppConnectionService extends Service {
                             restoredFromDatabaseLatch.await();
                             final Conversation c = findConversationByUuid(uuid);
                             if (c != null) {
-                                directReply(c, body.toString(), dismissNotification);
+                                directReply(c, body.toString(), lastMessageUuid, dismissNotification);
                             }
                         } catch (InterruptedException e) {
                             Log.d(Config.LOGTAG, "unable to process direct reply");
@@ -932,8 +933,12 @@ public class XmppConnectionService extends Service {
         }
     }
 
-    private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
-        Message message = new Message(conversation, body, conversation.getNextEncryption());
+    private void directReply(final Conversation conversation, final String body, final String lastMessageUuid, final boolean dismissAfterReply) {
+        final Message inReplyTo = lastMessageUuid == null ? null : conversation.findMessageWithUuid(lastMessageUuid);
+        final Message message = new Message(conversation, body, conversation.getNextEncryption());
+        if (inReplyTo != null && inReplyTo.isPrivateMessage()) {
+            Message.configurePrivateMessage(message, inReplyTo.getCounterpart());
+        }
         message.markUnread();
         if (message.getEncryption() == Message.ENCRYPTION_PGP) {
             getPgpEngine().encrypt(message, new UiCallback<Message>() {