Allow filtering by tag in the drawer

Stephen Paul Weber created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java        |  10 
src/main/java/eu/siacs/conversations/entities/ListItem.java            |   8 
src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java     | 122 
src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java |  12 
src/main/res/values/themes.xml                                         |   1 
5 files changed, 132 insertions(+), 21 deletions(-)

Detailed changes

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

@@ -126,6 +126,7 @@ import eu.siacs.conversations.databinding.CommandTextFieldBinding;
 import eu.siacs.conversations.databinding.CommandSliderFieldBinding;
 import eu.siacs.conversations.databinding.CommandWebviewBinding;
 import eu.siacs.conversations.databinding.DialogQuickeditBinding;
+import eu.siacs.conversations.entities.ListItem.Tag;
 import eu.siacs.conversations.http.HttpConnectionManager;
 import eu.siacs.conversations.persistance.DatabaseBackend;
 import eu.siacs.conversations.services.AvatarService;
@@ -906,6 +907,15 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         }
     }
 
+    public List<Tag> getTags(final Context ctx) {
+        if (getMode() == MODE_MULTI) {
+            if (getBookmark() == null) return new ArrayList<>();
+            return getBookmark().getTags(ctx);
+        } else {
+            return getContact().getTags(ctx);
+        }
+    }
+
     public String getAccountUuid() {
         return this.accountUuid;
     }

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

@@ -20,7 +20,7 @@ public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable
 
 	List<Tag> getTags(Context context);
 
-	final class Tag implements Serializable {
+	final class Tag implements Serializable, Comparable {
 		private final String name;
 
 		public Tag(final String name) {
@@ -41,6 +41,12 @@ public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable
 			return name.toLowerCase(Locale.US).equals(ot.getName().toLowerCase(Locale.US));
 		}
 
+		public int compareTo(Object o) {
+			if (!(o instanceof Tag)) return -1;
+			Tag ot = (Tag) o;
+			return name.toLowerCase(Locale.US).compareTo(ot.getName().toLowerCase(Locale.US));
+		}
+
 		public int hashCode() {
 			return name.toLowerCase(Locale.US).hashCode();
 		}

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

@@ -29,7 +29,6 @@
 
 package eu.siacs.conversations.ui;
 
-
 import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;
 
 import android.Manifest;
@@ -43,6 +42,7 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -73,10 +73,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import org.openintents.openpgp.util.OpenPgpApi;
 
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import eu.siacs.conversations.Config;
@@ -87,6 +90,8 @@ import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Conversational;
+import eu.siacs.conversations.entities.ListItem.Tag;
+import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
@@ -136,9 +141,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
     public static final int REQUEST_DOWNLOAD_STICKERS = 0xbf8702;
 
     public static final long DRAWER_ALL_CHATS = 1;
-    public static final long DRAWER_SETTINGS = 2;
-    public static final long DRAWER_MANAGE_ACCOUNT = 3;
-    public static final long DRAWER_MANAGE_PHONE_ACCOUNTS = 4;
+    public static final long DRAWER_DIRECT_MESSAGES = 2;
+    public static final long DRAWER_CHANNELS = 3;
+    public static final long DRAWER_SETTINGS = 4;
+    public static final long DRAWER_MANAGE_ACCOUNT = 5;
+    public static final long DRAWER_MANAGE_PHONE_ACCOUNTS = 6;
 
     //secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
     private static final @IdRes
@@ -153,6 +160,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
     private int mRequestCode = -1;
     private com.mikepenz.materialdrawer.widget.AccountHeaderView accountHeader;
     private Bundle savedState = null;
+    private Tag selectedTag = null;
+    private long mainFilter = DRAWER_ALL_CHATS;
 
     private static boolean isViewOrShareIntent(Intent i) {
         Log.d(Config.LOGTAG, "action: " + (i == null ? null : i.getAction()));
@@ -211,20 +220,62 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
             }
         }
 
-        int id = 101;
+        long id = 101;
         for (final var a : accounts) {
             final var p = new com.mikepenz.materialdrawer.model.ProfileDrawerItem();
             p.setIdentifier(id++);
             p.setTag(a);
             com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(p, a.getDisplayName());
             com.mikepenz.materialdrawer.model.interfaces.DescribableKt.setDescriptionText(p, a.getJid().asBareJid().toString());
-            com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconDrawable(p, xmppConnectionService.getAvatarService().get(a, (int) getResources().getDimension(R.dimen.avatar_on_drawer), false));
+            com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconBitmap(p, FileBackend.drawDrawable(xmppConnectionService.getAvatarService().get(a, (int) getResources().getDimension(R.dimen.avatar_on_drawer), false)).copy(Bitmap.Config.ARGB_8888, false));
             if (inHeader.contains(a)) {
                 accountHeader.updateProfile(p);
             } else {
                 accountHeader.addProfile(p, accountHeader.getProfiles().size() - (hasPhoneAccounts ? 2 : 1));
             }
         }
+
+        final var items = binding.drawer.getItemAdapter().getAdapterItems();
+        final var tags = new TreeMap<Tag, Integer>();
+        final var conversations = new ArrayList<Conversation>();
+        populateWithOrderedConversations(conversations, false);
+        for (final var c : conversations) {
+            for (final var tag : c.getTags(this)) {
+                if ("Channel".equals(tag.getName())) continue;
+                var count = tags.get(tag);
+                if (count == null) count = 0;
+                tags.put(tag, count + c.unreadCount());
+            }
+        }
+
+        id = 1000;
+        final var inDrawer = new HashMap<Tag, Long>();
+        for (final var item : ImmutableList.copyOf(items)) {
+            if (item.getIdentifier() >= 1000 && !tags.containsKey(item.getTag())) {
+                com.mikepenz.materialdrawer.util.MaterialDrawerSliderViewExtensionsKt.removeItems(binding.drawer, item);
+            } else if (item.getIdentifier() >= 1000) {
+                inDrawer.put((Tag)item.getTag(), item.getIdentifier());
+                id = item.getIdentifier() + 1;
+            }
+        }
+
+        for (final var entry : tags.entrySet()) {
+            final var badge = entry.getValue() > 0 ? entry.getValue().toString() : "";
+            if (inDrawer.containsKey(entry.getKey())) {
+                com.mikepenz.materialdrawer.util.MaterialDrawerSliderViewExtensionsKt.updateBadge(
+                    binding.drawer,
+                    inDrawer.get(entry.getKey()),
+                    new com.mikepenz.materialdrawer.holder.StringHolder(badge)
+                );
+            } else {
+                final var item = new com.mikepenz.materialdrawer.model.SecondaryDrawerItem();
+                item.setIdentifier(id++);
+                item.setTag(entry.getKey());
+                com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(item, entry.getKey().getName());
+                com.mikepenz.materialdrawer.model.interfaces.BadgeableKt.setBadgeText(item, badge);
+                binding.drawer.getItemAdapter().add(item);
+            }
+        }
     }
 
     @Override
@@ -270,14 +321,29 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
         accountHeader = new com.mikepenz.materialdrawer.widget.AccountHeaderView(this);
         final var manageAccount = new com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem();
         manageAccount.setIdentifier(DRAWER_MANAGE_ACCOUNT);
-        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(manageAccount, "Manage Accounts");
+        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(manageAccount, xmppConnectionService.getAccounts().size() > 1 ? "Manage Accounts" : "Manage Account");
         com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(manageAccount, R.drawable.ic_settings_24dp);
         accountHeader.addProfiles(manageAccount);
 
-        final var item = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
-        item.setIdentifier(DRAWER_ALL_CHATS);
-        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(item, "All Chats");
-        binding.drawer.getItemAdapter().add(item);
+        final var allChats = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
+        allChats.setIdentifier(DRAWER_ALL_CHATS);
+        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(allChats, "All Chats");
+
+
+        final var directMessages = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
+        directMessages.setIdentifier(DRAWER_DIRECT_MESSAGES);
+        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(directMessages, "Direct Messages");
+
+        final var channels = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
+        channels.setIdentifier(DRAWER_CHANNELS);
+        com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(channels, "Channels");
+
+        binding.drawer.getItemAdapter().add(
+            allChats,
+            directMessages,
+            channels,
+            new com.mikepenz.materialdrawer.model.DividerDrawerItem()
+        );
 
         final var settings = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
         settings.setIdentifier(DRAWER_SETTINGS);
@@ -299,7 +365,15 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
             final var id = drawerItem.getIdentifier();
             if (id == DRAWER_SETTINGS) {
                 startActivity(new Intent(this, eu.siacs.conversations.ui.activity.SettingsActivity.class));
+                return false;
+            } else if (id == DRAWER_ALL_CHATS || id == DRAWER_DIRECT_MESSAGES || id == DRAWER_CHANNELS) {
+                selectedTag = null;
+                mainFilter = id;
+            } else if (id >= 1000) {
+                selectedTag = (Tag) drawerItem.getTag();
             }
+            binding.drawer.getSelectExtension().selectByIdentifier(mainFilter, false, true);
+            refreshUi();
             return false;
         });
 
@@ -374,22 +448,34 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
 
     @Override
     public void populateWithOrderedConversations(List<Conversation> list) {
+        populateWithOrderedConversations(list, true);
+    }
+
+    public void populateWithOrderedConversations(List<Conversation> list, final boolean tagFilter) {
         super.populateWithOrderedConversations(list);
         if (accountHeader == null || accountHeader.getActiveProfile() == null) return;
 
-        if (accountHeader.getActiveProfile().getTag() != null) {
-            final var selected = ((Account) accountHeader.getActiveProfile().getTag()).getUuid();
-            for (final var c : ImmutableList.copyOf(list)) {
-                if (!selected.equals(c.getAccount().getUuid())) {
-                    list.remove(c);
-                }
+        final var selectedAccount =
+            accountHeader.getActiveProfile().getTag() != null ?
+            ((Account) accountHeader.getActiveProfile().getTag()).getUuid() :
+            null;
+
+        for (final var c : ImmutableList.copyOf(list)) {
+            if (mainFilter == DRAWER_CHANNELS && c.getMode() != Conversation.MODE_MULTI) {
+                list.remove(c);
+            } else if (mainFilter == DRAWER_DIRECT_MESSAGES && c.getMode() == Conversation.MODE_MULTI) {
+                list.remove(c);
+            } else if (selectedAccount != null && !selectedAccount.equals(c.getAccount().getUuid())) {
+                list.remove(c);
+            } else if (selectedTag != null && tagFilter && !c.getTags(this).contains(selectedTag)) {
+                list.remove(c);
             }
         }
     }
 
     @Override
     public void launchStartConversation() {
-        StartConversationActivity.launch(this, (Account) accountHeader.getActiveProfile().getTag());
+        StartConversationActivity.launch(this, (Account) accountHeader.getActiveProfile().getTag(), selectedTag == null ? null : selectedTag.getName());
     }
 
     private boolean performRedirectIfNecessary(boolean noAnimation) {

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

@@ -124,6 +124,7 @@ public class StartConversationActivity extends XmppActivity
 
     public static final String EXTRA_INVITE_URI = "eu.siacs.conversations.invite_uri";
     public static final String EXTRA_ACCOUNT_FILTER = "account_filter";
+    public static final String EXTRA_TEXT_FILTER = "text_filter";
 
     private final int REQUEST_SYNC_CONTACTS = 0x28cf;
     private final int REQUEST_CREATE_CONFERENCE = 0x39da;
@@ -279,16 +280,19 @@ public class StartConversationActivity extends XmppActivity
     }
 
     public static void launch(Context context) {
-        launch(context, null);
+        launch(context, null, null);
     }
 
-    public static void launch(Context context, final Account account) {
+    public static void launch(Context context, final Account account, final String q) {
         final Intent intent = new Intent(context, StartConversationActivity.class);
         if (account != null) {
             intent.putExtra(
                 EXTRA_ACCOUNT_FILTER,
                 account.getJid().asBareJid().toEscapedString());
         }
+        if (q != null) {
+            intent.putExtra(EXTRA_TEXT_FILTER, q);
+        }
         context.startActivity(intent);
     }
 
@@ -365,6 +369,10 @@ public class StartConversationActivity extends XmppActivity
         final Intent intent;
         if (savedInstanceState == null) {
             intent = getIntent();
+            final var search = intent.getStringExtra(EXTRA_TEXT_FILTER);
+            if (search != null) {
+                mInitialSearchValue.push(search);
+            }
         } else {
             createdByViewIntent = savedInstanceState.getBoolean("created_by_view_intent", false);
             final String search = savedInstanceState.getString("search");

src/main/res/values/themes.xml 🔗

@@ -53,4 +53,5 @@
         <item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
         <item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
     </style>
+
 </resources>