notify user of invisible chats

Phillip Davis created

Change summary

src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java | 108 
src/main/res/drawable/menu_with_dot_24dp.xml                       |  19 
2 files changed, 103 insertions(+), 24 deletions(-)

Detailed changes

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

@@ -70,6 +70,7 @@ import io.michaelrocks.libphonenumber.android.NumberParseException;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.color.MaterialColors;
 
+import org.jetbrains.annotations.NotNull;
 import org.openintents.openpgp.util.OpenPgpApi;
 
 import java.util.Arrays;
@@ -223,12 +224,24 @@ public class ConversationsActivity extends XmppActivity
             final var items = binding.drawer.getItemAdapter().getAdapterItems();
             final var tags = new TreeMap<Tag, Integer>();
             final var conversations = new ArrayList<Conversation>();
+            final var removedConversations = new ArrayList<Conversation>();
             var totalUnread = 0;
             var dmUnread = 0;
             var channelUnread = 0;
             var chatRequests = 0;
             final var selectedAccount = selectedAccount();
-            populateWithOrderedConversations(conversations, false, false);
+            // What sort of memory footprint does this function typically have?
+            populateWithOrderedConversations(
+                    conversations,
+                    removedConversations,
+                    false
+            );
+
+            final var invisibles = removedConversations.stream().anyMatch(c -> c.unreadCount(xmppConnectionService) > 0);
+
+            // Reconstruct the complete list for counts and tags computation
+            conversations.addAll(removedConversations);
+
             for (final var c : conversations) {
                 final var unread = c.unreadCount(xmppConnectionService);
                 if (selectedAccount == null || selectedAccount.getUuid().equals(c.getAccount().getUuid())) {
@@ -247,6 +260,7 @@ public class ConversationsActivity extends XmppActivity
                 if (accountUnread == null) accountUnread = 0;
                 accountUnreads.put(c.getAccount(), accountUnread + unread);
             }
+
             filterByMainFilter(conversations);
             for (final var c : conversations) {
                 if (selectedAccount == null || selectedAccount.getUuid().equals(c.getAccount().getUuid())) {
@@ -260,6 +274,14 @@ public class ConversationsActivity extends XmppActivity
                 }
             }
 
+            ActionBar supportBar = getSupportActionBar();
+
+            if (invisibles && supportBar != null) {
+               supportBar.setHomeAsUpIndicator(R.drawable.menu_with_dot_24dp);
+            } else if (supportBar != null) {
+                supportBar.setHomeAsUpIndicator(R.drawable.menu_24dp);
+            }
+
             com.mikepenz.materialdrawer.util.MaterialDrawerSliderViewExtensionsKt.updateBadge(
                 binding.drawer,
                 DRAWER_UNREAD_CHATS,
@@ -652,30 +674,49 @@ public class ConversationsActivity extends XmppActivity
 
     @Override
     public void populateWithOrderedConversations(List<Conversation> list) {
-        populateWithOrderedConversations(list, true, true);
+        populateWithOrderedConversations(list, new ArrayList<>(), true);
     }
 
-    public void populateWithOrderedConversations(List<Conversation> list, final boolean filter, final boolean sort) {
-        if (sort) {
-            super.populateWithOrderedConversations(list);
-        } else {
-            list.addAll(xmppConnectionService.getConversations());
-        }
-
-        if (!filter) return;
-        filterByMainFilter(list);
-
-        final var selectedAccount = selectedAccount();
+    public void populateWithOrderedConversations(
+            @NotNull List<Conversation> list,
+            final boolean sort
+    ) {
+        populateWithOrderedConversations(list, new ArrayList<>(), sort);
+    }
 
-        for (final var c : ImmutableList.copyOf(list)) {
-            if (selectedAccount != null && !selectedAccount.getUuid().equals(c.getAccount().getUuid())) {
-                list.remove(c);
-            } else if (!selectedTag.isEmpty()) {
-                final var tags = new HashSet<>(c.getTags(this));
-                tags.retainAll(selectedTag);
-                if (tags.isEmpty()) list.remove(c);
-            }
-        }
+    // @param conversations an initially empty list representing the messages
+    //         to returned as relevant
+    // @return a `boolean` value indicating whether any conversations
+    //         were filtered out and therefore would be invisible to
+    //         the user.
+    public void populateWithOrderedConversations(
+            @NotNull List<Conversation> retained,
+            @NotNull List<Conversation> removed,
+            final boolean sort
+    ) {
+		if (sort) {
+			super.populateWithOrderedConversations(retained);
+		} else {
+			retained.addAll(xmppConnectionService.getConversations());
+		}
+
+		filterByMainFilter(retained, removed);
+
+		final var selectedAccount = selectedAccount();
+
+		for (final var c : ImmutableList.copyOf(retained)) {
+			if (selectedAccount != null && !selectedAccount.getUuid().equals(c.getAccount().getUuid())) {
+				retained.remove(c);
+				removed.add(c);
+			} else if (!selectedTag.isEmpty()) {
+				final var tags = new HashSet<>(c.getTags(this));
+				tags.retainAll(selectedTag);
+				if (tags.isEmpty()) {
+					retained.remove(c);
+					removed.add(c);
+				}
+			}
+		}
     }
 
     protected Account selectedAccount() {
@@ -683,24 +724,43 @@ public class ConversationsActivity extends XmppActivity
         return (Account) accountHeader.getActiveProfile().getTag();
     }
 
-    protected void filterByMainFilter(List<Conversation> list) {
+    // Applies pre-defined filters to specify that only conversations
+    // from certain accounts or types of conversations are displayed.
+    //
+    // @param `retained` an initially-empty list representing the messages
+    //         to eventually be returned as relevant.
+    // @param `removed` an initially-empty list representing the messages
+    //         filtered out by tags and `filterByMainFilter`
+    protected void filterByMainFilter(
+        @NotNull List<Conversation> list,
+        @NotNull List<Conversation> removed
+    ) {
          final var chatRequests = xmppConnectionService.getStringPreference("chat_requests", R.string.default_chat_requests);
          for (final var c : ImmutableList.copyOf(list)) {
             if (mainFilter == DRAWER_CHANNELS && c.getMode() != Conversation.MODE_MULTI) {
                 list.remove(c);
+                removed.add(c);
             } else if (mainFilter == DRAWER_DIRECT_MESSAGES && c.getMode() == Conversation.MODE_MULTI) {
                 list.remove(c);
+                removed.add(c);
             } else if (mainFilter == DRAWER_UNREAD_CHATS && c.unreadCount(xmppConnectionService) < 1) {
                 list.remove(c);
+                removed.add(c);
             } else if (mainFilter == DRAWER_CHAT_REQUESTS && !c.isChatRequest(chatRequests)) {
                 list.remove(c);
+                removed.add(c);
             }
             if (mainFilter != DRAWER_CHAT_REQUESTS && c.isChatRequest(chatRequests)) {
                 list.remove(c);
+                removed.add(c);
             }
         }
     }
 
+    protected void filterByMainFilter(@NotNull List<Conversation> list) {
+        filterByMainFilter(list, new ArrayList<>());
+    }
+
     @Override
     public void launchStartConversation() {
         launchStartConversation(0);
@@ -1260,7 +1320,7 @@ public class ConversationsActivity extends XmppActivity
             }
             case R.id.action_report_spam: {
                 final var list = new ArrayList<Conversation>();
-                populateWithOrderedConversations(list, true, false);
+                populateWithOrderedConversations(list, false);
                 new AlertDialog.Builder(this)
                     .setTitle(R.string.report_spam)
                     .setMessage("Do you really want to block all these users and report as SPAM?")

src/main/res/drawable/menu_with_dot_24dp.xml 🔗

@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+
+    <!-- Hamburger menu icon -->
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M120,720L120,640L840,640L840,720L120,720ZM120,520L120,440L840,440L840,520L120,520ZM120,320L120,240L840,240L840,320L120,320Z"/>
+
+    <!-- Notification badge (dot) - radius 160, moved up -->
+    <path
+        android:fillColor="@color/perpy"
+        android:pathData="M820,100
+                          A160,160 0 1,1 819.9,100
+                          Z"/>
+</vector>