Show list of recent threads in muc details

Stephen Paul Weber created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java        | 35 
src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java | 37 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java      |  9 
src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java     |  1 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java              |  5 
src/main/res/layout/activity_muc_details.xml                           | 19 
6 files changed, 104 insertions(+), 2 deletions(-)

Detailed changes

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

@@ -659,7 +659,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
             if (mthread != null) {
                 Thread thread = threads.get(mthread.getContent());
                 if (thread == null) {
-                    thread = new Thread();
+                    thread = new Thread(mthread.getContent());
                     threads.put(mthread.getContent(), thread);
                 }
                 if (thread.subject == null && (m.getSubject() != null && (m.getRawBody() == null || m.getRawBody().length() == 0))) {
@@ -684,6 +684,13 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         return threads.get(id);
     }
 
+    public List<Thread> recentThreads() {
+        final ArrayList<Thread> recent = new ArrayList<>();
+        recent.addAll(threads.values());
+        recent.sort((a, b) -> b.getLastTime() == a.getLastTime() ? 0 : (b.getLastTime() > a.getLastTime() ? 1 : -1));
+        return recent.size() < 5 ? recent : recent.subList(0, 5);
+    }
+
     @Override
     public boolean isBlocked() {
         return getContact().isBlocked();
@@ -3312,13 +3319,37 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         protected Message subject = null;
         protected Message first = null;
         protected Message last = null;
+        protected final String threadId;
 
-        protected Thread() {}
+        protected Thread(final String threadId) {
+            this.threadId = threadId;
+        }
+
+        public String getThreadId() {
+            return threadId;
+        }
 
         public String getSubject() {
             if (subject == null) return null;
 
             return subject.getSubject();
         }
+
+        public String getDisplay() {
+            final String s = getSubject();
+            if (s != null) return s;
+
+            if (first != null) {
+                return first.getBody();
+            }
+
+            return "";
+        }
+
+        public long getLastTime() {
+            if (last == null) return 0;
+
+            return last.getTimeSent();
+        }
     }
 }

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

@@ -18,13 +18,17 @@ import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
 
+import com.cheogram.android.Util;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -36,6 +40,7 @@ import java.util.stream.Collectors;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
+import eu.siacs.conversations.databinding.ThreadRowBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Bookmark;
 import eu.siacs.conversations.entities.Contact;
@@ -61,6 +66,7 @@ import eu.siacs.conversations.utils.AccountUtils;
 import eu.siacs.conversations.utils.Compatibility;
 import eu.siacs.conversations.utils.StringUtils;
 import eu.siacs.conversations.utils.StylingHelper;
+import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.XmppConnection;
@@ -227,6 +233,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
         this.binding.users.setAdapter(mUserPreviewAdapter);
         GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
         GridManager.setupLayoutManager(this, this.binding.users, R.dimen.media_size);
+        this.binding.recentThreads.setOnItemClickListener((a0, v, pos, a3) -> {
+            final Conversation.Thread thread = (Conversation.Thread) binding.recentThreads.getAdapter().getItem(pos);
+            switchToConversation(mConversation, null, false, null, false, true, null, thread.getThreadId());
+        });
         this.binding.invite.setOnClickListener(v -> inviteToConversation(mConversation));
         this.binding.showUsers.setOnClickListener(v -> {
             Intent intent = new Intent(this, MucUsersActivity.class);
@@ -650,6 +660,17 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
             return;
         }
 
+        final List<Conversation.Thread> recentThreads = mConversation.recentThreads();
+        if (recentThreads.isEmpty()) {
+            this.binding.recentThreadsWrapper.setVisibility(View.GONE);
+        } else {
+            final ThreadAdapter threads = new ThreadAdapter();
+            threads.addAll(recentThreads);
+            this.binding.recentThreads.setAdapter(threads);
+            this.binding.recentThreadsWrapper.setVisibility(View.VISIBLE);
+            Util.justifyListViewHeightBasedOnChildren(binding.recentThreads);
+        }
+
         List<ListItem.Tag> tagList = bookmark.getTags(this);
         if (tagList.size() == 0 || !showDynamicTags) {
             binding.tags.setVisibility(View.GONE);
@@ -746,4 +767,20 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
         }
     }
 
+    class ThreadAdapter extends ArrayAdapter<Conversation.Thread> {
+        ThreadAdapter() { super(ConferenceDetailsActivity.this, 0); }
+
+        @Override
+        public View getView(int position, View view, @NonNull ViewGroup parent) {
+            final ThreadRowBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.thread_row, parent, false);
+            final Conversation.Thread item = getItem(position);
+
+            binding.threadIdenticon.setColor(UIHelper.getColorForName(item.getThreadId()));
+            binding.threadIdenticon.setHash(UIHelper.identiconHash(item.getThreadId()));
+
+            binding.threadSubject.setText(item.getDisplay());
+
+            return binding.getRoot();
+        }
+    }
 }

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

@@ -3240,6 +3240,15 @@ public class ConversationFragment extends XmppFragment
         final boolean doNotAppend =
                 extras.getBoolean(ConversationsActivity.EXTRA_DO_NOT_APPEND, false);
         final String type = extras.getString(ConversationsActivity.EXTRA_TYPE);
+
+        final String thread = extras.getString(ConversationsActivity.EXTRA_THREAD);
+        if (thread != null) {
+            conversation.setLockThread(true);
+            backPressedLeaveSingleThread.setEnabled(true);
+            setThread(new Element("thread").setContent(thread));
+            refresh();
+        }
+
         final List<Uri> uris = extractUris(extras);
         if (uris != null && uris.size() > 0) {
             if (uris.size() == 1 && "geo".equals(uris.get(0).getScheme())) {

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

@@ -116,6 +116,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
     public static final String EXTRA_DO_NOT_APPEND = "do_not_append";
     public static final String EXTRA_POST_INIT_ACTION = "post_init_action";
     public static final String POST_ACTION_RECORD_VOICE = "record_voice";
+    public static final String EXTRA_THREAD = "threadId";
     public static final String EXTRA_TYPE = "type";
     public static final String EXTRA_NODE = "node";
     public static final String EXTRA_JID = "jid";

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

@@ -593,11 +593,16 @@ public abstract class XmppActivity extends ActionBarActivity {
     }
 
     public void switchToConversation(Conversation conversation, String text, boolean asQuote, String nick, boolean pm, boolean doNotAppend, String postInit) {
+        switchToConversation(conversation, text, asQuote, nick, pm, doNotAppend, postInit, null);
+    }
+
+    public void switchToConversation(Conversation conversation, String text, boolean asQuote, String nick, boolean pm, boolean doNotAppend, String postInit, String thread) {
         if (conversation == null) return;
 
         Intent intent = new Intent(this, ConversationsActivity.class);
         intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
         intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
+        intent.putExtra(ConversationsActivity.EXTRA_THREAD, thread);
         if (text != null) {
             intent.putExtra(Intent.EXTRA_TEXT, text);
             if (asQuote) {

src/main/res/layout/activity_muc_details.xml 🔗

@@ -420,6 +420,25 @@
                             android:textAppearance="@style/TextAppearance.Conversations.Caption"/>
                     </LinearLayout>
                 </androidx.cardview.widget.CardView>
+
+                <androidx.cardview.widget.CardView
+                    android:id="@+id/recent_threads_wrapper"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginBottom="@dimen/activity_vertical_margin"
+                    android:layout_marginLeft="@dimen/activity_horizontal_margin"
+                    android:layout_marginRight="@dimen/activity_horizontal_margin"
+                    android:layout_marginTop="@dimen/activity_vertical_margin">
+
+                    <ListView
+                        android:id="@+id/recent_threads"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:divider="@android:color/transparent"
+                        android:dividerHeight="0dp"></ListView>
+
+                </androidx.cardview.widget.CardView>
+
                 <androidx.cardview.widget.CardView
                     android:id="@+id/media_wrapper"
                     android:layout_width="fill_parent"