Long press to see who reacted

Stephen Paul Weber created

Change summary

src/main/java/eu/siacs/conversations/entities/Reaction.java         | 13 
src/main/java/eu/siacs/conversations/ui/BindingAdapters.java        | 22 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java |  2 
src/main/java/eu/siacs/conversations/utils/UIHelper.java            | 17 
4 files changed, 43 insertions(+), 11 deletions(-)

Detailed changes

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

@@ -209,13 +209,12 @@ public class Reaction {
     }
 
     public static Aggregated aggregated(final Collection<Reaction> reactions, Function<Reaction, GetThumbnailForCid> thumbnailer) {
-        final Map<EmojiSearch.Emoji, Integer> aggregatedReactions =
-                Maps.transformValues(
-                        Multimaps.index(reactions, r -> r.cid == null ? new EmojiSearch.Emoji(r.reaction, 0) : new EmojiSearch.CustomEmoji(r.reaction, r.cid.toString(), thumbnailer.apply(r).getThumbnail(r.cid), null)).asMap(), Collection::size);
-        final List<Map.Entry<EmojiSearch.Emoji, Integer>> sortedList =
+        final Map<EmojiSearch.Emoji, Collection<Reaction>> aggregatedReactions =
+                        Multimaps.index(reactions, r -> r.cid == null ? new EmojiSearch.Emoji(r.reaction, 0) : new EmojiSearch.CustomEmoji(r.reaction, r.cid.toString(), thumbnailer.apply(r).getThumbnail(r.cid), null)).asMap();
+        final List<Map.Entry<EmojiSearch.Emoji, Collection<Reaction>>> sortedList =
                 Ordering.from(
                                 Comparator.comparingInt(
-                                        (Map.Entry<EmojiSearch.Emoji, Integer> o) -> o.getValue()))
+                                        (Map.Entry<EmojiSearch.Emoji, Collection<Reaction>> o) -> o.getValue().size()))
                         .reverse()
                         .immutableSortedCopy(aggregatedReactions.entrySet());
         return new Aggregated(
@@ -228,11 +227,11 @@ public class Reaction {
 
     public static final class Aggregated {
 
-        public final List<Map.Entry<EmojiSearch.Emoji, Integer>> reactions;
+        public final List<Map.Entry<EmojiSearch.Emoji, Collection<Reaction>>> reactions;
         public final Set<String> ourReactions;
 
         private Aggregated(
-                final List<Map.Entry<EmojiSearch.Emoji, Integer>> reactions, Set<String> ourReactions) {
+                final List<Map.Entry<EmojiSearch.Emoji, Collection<Reaction>>> reactions, Set<String> ourReactions) {
             this.reactions = reactions;
             this.ourReactions = ourReactions;
         }

src/main/java/eu/siacs/conversations/ui/BindingAdapters.java 🔗

@@ -9,37 +9,43 @@ import com.cheogram.android.EmojiSearch;
 import com.google.android.material.chip.Chip;
 import com.google.android.material.chip.ChipGroup;
 import com.google.android.material.color.MaterialColors;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableSet;
 
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Reaction;
+import eu.siacs.conversations.utils.UIHelper;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 public class BindingAdapters {
 
     public static void setReactionsOnReceived(
             final ChipGroup chipGroup,
+            final Conversation conversation,
             final Reaction.Aggregated reactions,
             final Consumer<Collection<String>> onModifiedReactions,
             final Consumer<EmojiSearch.CustomEmoji> onCustomReaction,
             final Runnable addReaction) {
-        setReactions(chipGroup, reactions, true, onModifiedReactions, onCustomReaction, addReaction);
+        setReactions(chipGroup, conversation, reactions, true, onModifiedReactions, onCustomReaction, addReaction);
     }
 
     public static void setReactionsOnSent(
             final ChipGroup chipGroup,
             final Reaction.Aggregated reactions,
             final Consumer<Collection<String>> onModifiedReactions) {
-        setReactions(chipGroup, reactions, false, onModifiedReactions, null, null);
+        setReactions(chipGroup, null, reactions, false, onModifiedReactions, null, null);
     }
 
     private static void setReactions(
             final ChipGroup chipGroup,
+            final Conversation conversation,
             final Reaction.Aggregated aggregated,
             final boolean onReceived,
             final Consumer<Collection<String>> onModifiedReactions,
@@ -49,7 +55,7 @@ public class BindingAdapters {
         final var size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, context.getResources().getDisplayMetrics());
         final var corner = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, context.getResources().getDisplayMetrics());
         final var layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, size);
-        final List<Map.Entry<EmojiSearch.Emoji, Integer>> reactions = aggregated.reactions;
+        final List<Map.Entry<EmojiSearch.Emoji, Collection<Reaction>>> reactions = aggregated.reactions;
         if (reactions == null || reactions.isEmpty()) {
             chipGroup.setVisibility(View.GONE);
         } else {
@@ -57,7 +63,7 @@ public class BindingAdapters {
             chipGroup.setVisibility(View.VISIBLE);
             for (final var reaction : reactions) {
                 final var emoji = reaction.getKey();
-                final var count = reaction.getValue();
+                final var count = reaction.getValue().size();
                 final Chip chip = new Chip(chipGroup.getContext());
                 //chip.setEnsureMinTouchTargetSize(false);
                 chip.setChipMinHeight(size-32.0f);
@@ -101,6 +107,14 @@ public class BindingAdapters {
                                 }
                             }
                         });
+                chip.setOnLongClickListener(v -> {
+                    final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
+                    builder.setTitle(emoji.toString());
+                    builder.setMessage(reaction.getValue().stream().map(r -> UIHelper.getDisplayName(conversation, r)).collect(Collectors.joining("\n")));
+                    builder.setPositiveButton(context.getResources().getString(R.string.ok), null);
+                    builder.create().show();
+                    return true;
+                });
                 chipGroup.addView(chip);
             }
             if (addReaction != null) {

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -1520,6 +1520,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
             final var aggregatedReactions = conversation instanceof Conversation ? ((Conversation) conversation).aggregatedReactionsFor(message, reactionThumbnailer) : message.getAggregatedReactions();
             BindingAdapters.setReactionsOnReceived(
                     viewHolder.reactions,
+                    conversation instanceof Conversation ? (Conversation) conversation : null,
                     aggregatedReactions,
                     reactions -> sendReactions(message, reactions),
                     emoji -> sendCustomReaction(message, emoji),
@@ -1528,6 +1529,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
             final var aggregatedReactions = conversation instanceof Conversation ? ((Conversation) conversation).aggregatedReactionsFor(message, reactionThumbnailer) : message.getAggregatedReactions();
             BindingAdapters.setReactionsOnReceived(
                     viewHolder.reactions,
+                    conversation instanceof Conversation ? (Conversation) conversation : null,
                     aggregatedReactions,
                     reactions -> sendReactions(message, reactions),
                     emoji -> sendCustomReaction(message, emoji),

src/main/java/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -39,6 +39,7 @@ import eu.siacs.conversations.entities.Conversational;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.MucOptions;
 import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.Reaction;
 import eu.siacs.conversations.entities.RtpSessionStatus;
 import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.services.XmppConnectionService;
@@ -503,6 +504,22 @@ public class UIHelper {
         }
     }
 
+    public static String getDisplayName(final Conversational conversation, final Reaction reaction) {
+        if (conversation.getMode() == Conversation.MODE_MULTI) {
+            if (conversation instanceof Conversation) {
+                MucOptions.User user = ((Conversation) conversation).getMucOptions().findUserByFullJid(reaction.trueJid);
+                if (user == null) user = ((Conversation) conversation).getMucOptions().findUserByOccupantId(reaction.occupantId, reaction.from);
+                if (user != null) {
+                    final String dname = getDisplayName(user);
+                    if (dname != null) return dname;
+                }
+            }
+            return getDisplayedMucCounterpart(reaction.from);
+        }
+
+        return conversation == null ? reaction.from.asBareJid().toString() : conversation.getAccount().getRoster().getContact(reaction.from).getDisplayName();
+    }
+
     public static String getMessageHint(final Context context,final  Conversation conversation) {
         return switch (conversation.getNextEncryption()) {
             case Message.ENCRYPTION_NONE -> {