From 30600b3ff9d18badc9f34d109433fbd260750996 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Wed, 21 Sep 2016 23:37:21 +0200
Subject: [PATCH] Improved performance a lot, changed settings UI a bit

---
 .../de/kuschku/libquassel/QuasselClient.java  | 10 +--
 .../libquassel/localtypes/BacklogFilter.java  | 64 +++++++++++++++----
 .../backlogstorage/HybridBacklogStorage.java  | 10 ++-
 .../syncables/types/impl/IrcChannel.java      | 12 +++-
 .../types/interfaces/QIrcChannel.java         |  4 +-
 .../ui/chat/fragment/ChatFragment.java        |  2 +-
 .../ui/chat/nicklist/NickListAdapter.java     | 63 +++---------------
 app/src/main/res/layout/activity_settings.xml |  2 +-
 8 files changed, 87 insertions(+), 80 deletions(-)

diff --git a/app/src/main/java/de/kuschku/libquassel/QuasselClient.java b/app/src/main/java/de/kuschku/libquassel/QuasselClient.java
index 7a40061d6..bcfc9b11f 100644
--- a/app/src/main/java/de/kuschku/libquassel/QuasselClient.java
+++ b/app/src/main/java/de/kuschku/libquassel/QuasselClient.java
@@ -72,10 +72,12 @@ public class QuasselClient {
         }
         this.client.bufferManager().bufferIds().clear();
         this.client.bufferManager().buffers().clear();
-        for (QBufferViewConfig config : client.bufferViewManager().bufferViewConfigs()) {
-            config.networkList().clear();
+        if (client.bufferViewManager() != null) {
+            for (QBufferViewConfig config : client.bufferViewManager().bufferViewConfigs()) {
+                config.networkList().clear();
+            }
+            client.bufferViewManager().bufferViewConfigs().clear();
+            client.bufferViewManager().bufferViewConfigMap().clear();
         }
-        client.bufferViewManager().bufferViewConfigs().clear();
-        client.bufferViewManager().bufferViewConfigMap().clear();
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
index af6edeae3..441cdabdf 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/BacklogFilter.java
@@ -24,6 +24,8 @@ package de.kuschku.libquassel.localtypes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
+import com.raizlabs.android.dbflow.sql.language.SQLite;
+
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
@@ -34,6 +36,7 @@ import java.util.List;
 
 import de.kuschku.libquassel.client.Client;
 import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.message.Message_Table;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.util.observables.callbacks.ElementCallback;
@@ -82,6 +85,10 @@ public class BacklogFilter implements UICallback {
         updateDayChangeMessages();
     }
 
+    public void loadBackload() {
+        bus.post(new LoadBacklogEvent());
+    }
+
     private Message createMarkerlineMessage(int id) {
         return Message.create(
                 id,
@@ -193,20 +200,24 @@ public class BacklogFilter implements UICallback {
 
     @Subscribe(threadMode = ThreadMode.ASYNC)
     public void onEventAsync(UpdateAddEvent event) {
+        List<Message> filteredMessages = new ArrayList<>();
         for (Message message : unfiltered) {
-            if (!filterItem(message)) {
-                bus.post(new MessageInsertEvent(message));
-            }
+            if (!filterItem(message))
+                filteredMessages.add(message);
         }
+
+        bus.post(new MessageInsertEvent(filteredMessages));
     }
 
     @Subscribe(threadMode = ThreadMode.ASYNC)
     public void onEventAsync(UpdateRemoveEvent event) {
+        List<Message> removedMessages = new ArrayList<>();
         for (Message message : unfiltered) {
-            if (filterItem(message)) {
-                bus.post(new MessageRemoveEvent(message));
-            }
+            if (filterItem(message))
+                removedMessages.add(message);
         }
+
+        bus.post(new MessageRemoveEvent(removedMessages));
     }
 
     @Subscribe(threadMode = ThreadMode.ASYNC)
@@ -227,7 +238,18 @@ public class BacklogFilter implements UICallback {
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onEventMainThread(@NonNull MessageRemoveEvent event) {
-        filtered.remove(event.msg);
+        filtered.removeAll(event.msgs);
+    }
+
+    @Subscribe(threadMode = ThreadMode.ASYNC)
+    public void onEventAsync(@NonNull LoadBacklogEvent event) {
+        List<Message> messageList = SQLite.select().from(Message.class).where(Message_Table.bufferInfo_id.eq(bufferId)).queryList();
+        bus.post(new BacklogLoadedEvent(messageList));
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onEventMainThread(@NonNull BacklogLoadedEvent event) {
+        unfiltered.addAll(event.messageList);
     }
 
     @Override
@@ -249,8 +271,8 @@ public class BacklogFilter implements UICallback {
 
     @Override
     public void notifyItemRangeInserted(int position, int count) {
-        List<Message> message = unfiltered.subList(position, position + count);
-        bus.post(new MessageFilterEvent(message));
+        List<Message> messages = unfiltered.subList(position, position + count);
+        bus.post(new MessageFilterEvent(messages));
     }
 
     @Override
@@ -262,9 +284,8 @@ public class BacklogFilter implements UICallback {
 
     @Override
     public void notifyItemRangeRemoved(int position, int count) {
-        for (int i = position; i < position + count; i++) {
-            notifyItemRemoved(i);
-        }
+        List<Message> messages = unfiltered.subList(position, position + count);
+        bus.post(new MessageRemoveEvent(messages));
     }
 
     public void onDestroy() {
@@ -286,10 +307,14 @@ public class BacklogFilter implements UICallback {
     }
 
     private class MessageRemoveEvent {
-        public final Message msg;
+        public final List<Message> msgs;
 
         public MessageRemoveEvent(Message msg) {
-            this.msg = msg;
+            this.msgs = Collections.singletonList(msg);
+        }
+
+        public MessageRemoveEvent(List<Message> msgs) {
+            this.msgs = msgs;
         }
     }
 
@@ -310,4 +335,15 @@ public class BacklogFilter implements UICallback {
 
     private class UpdateRemoveEvent {
     }
+
+    private class LoadBacklogEvent {
+    }
+
+    private class BacklogLoadedEvent {
+        private final List<Message> messageList;
+
+        public BacklogLoadedEvent(List<Message> messageList) {
+            this.messageList = messageList;
+        }
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/HybridBacklogStorage.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/HybridBacklogStorage.java
index a902a2068..0779ed071 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/HybridBacklogStorage.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogstorage/HybridBacklogStorage.java
@@ -33,6 +33,8 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import de.kuschku.libquassel.client.Client;
 import de.kuschku.libquassel.localtypes.BacklogFilter;
@@ -55,6 +57,8 @@ public class HybridBacklogStorage implements BacklogStorage {
     @NonNull
     private final Set<BacklogFilter> filterSet = new HashSet<>();
 
+    private ExecutorService executor = Executors.newCachedThreadPool();
+
     private Client client;
 
     @NonNull
@@ -233,10 +237,12 @@ public class HybridBacklogStorage implements BacklogStorage {
             }
             messages.addCallback(backlogFilter);
             synchronized (backlogs) {
-                List<Message> messageList = SQLite.select().from(Message.class).where(Message_Table.bufferInfo_id.eq(bufferId)).queryList();
-                messages.addAll(messageList);
                 backlogs.put(bufferId, messages);
             }
+            executor.submit((Runnable) () -> {
+                List<Message> messageList = SQLite.select().from(Message.class).where(Message_Table.bufferInfo_id.eq(bufferId)).queryList();
+                messages.addAll(messageList);
+            });
             filteredBacklogs.put(bufferId, filteredMessages);
             synchronized (filterSet) {
                 filters.put(bufferId, backlogFilter);
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
index fcabcef88..aea9713ec 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
@@ -48,7 +48,7 @@ import de.kuschku.libquassel.syncables.types.interfaces.QIrcChannel;
 import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.util.irc.ModeUtils;
-import de.kuschku.util.observables.lists.ObservableSet;
+import de.kuschku.util.observables.lists.ObservableSortedList;
 
 import static de.kuschku.util.AndroidAssert.assertEquals;
 
@@ -62,13 +62,19 @@ public class IrcChannel extends AIrcChannel {
     private final String name;
     @NonNull
     private final Map<String, Set<Character>> userModes = new HashMap<>();
-    private final ObservableSet<String> users = new ObservableSet<>();
     @NonNull
     public Set<Character> D_channelModes = new HashSet<>();
     private String topic;
     private String password;
     private boolean encrypted;
     private WeakReference<QNetwork> network = new WeakReference<>(null);
+    private final ObservableSortedList<String> users = new ObservableSortedList<>((o1, o2) -> {
+        if (userModes(o1).equals(userModes(o2))) {
+            return o1.compareToIgnoreCase(o2);
+        } else {
+            return network().lowestModeIndex(userModes(o1)) - network().lowestModeIndex(userModes(o2));
+        }
+    });
     private String codecForEncoding;
     private String codecForDecoding;
     // Because we don’t have networks at the beginning yet
@@ -516,7 +522,7 @@ public class IrcChannel extends AIrcChannel {
     }
 
     @NonNull
-    public ObservableSet<String> users() {
+    public ObservableSortedList<String> users() {
         return users;
     }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QIrcChannel.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QIrcChannel.java
index 5e3d1d8c3..e210f60c8 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QIrcChannel.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QIrcChannel.java
@@ -28,7 +28,7 @@ import java.util.List;
 
 import de.kuschku.libquassel.client.Client;
 import de.kuschku.libquassel.syncables.Synced;
-import de.kuschku.util.observables.lists.ObservableSet;
+import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public interface QIrcChannel extends QObservable<QIrcChannel> {
     boolean isKnownUser(QIrcUser ircuser);
@@ -153,7 +153,7 @@ public interface QIrcChannel extends QObservable<QIrcChannel> {
     String getObjectName();
 
     @NonNull
-    ObservableSet<String> users();
+    ObservableSortedList<String> users();
 
     void _ircUserNickChanged(String oldNick, String newNick);
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java
index 84d9bfa68..e70c56769 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/fragment/ChatFragment.java
@@ -111,7 +111,7 @@ public class ChatFragment extends BoundFragment {
         };
         messages.addOnScrollListener(listener);
 
-        scrollDown.setOnClickListener(view1 -> messages.smoothScrollToPosition(0));
+        scrollDown.setOnClickListener(view1 -> messages.scrollToPosition(0));
 
         return view;
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
index f5102ad57..f17b57e14 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
@@ -28,8 +28,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import java.util.Comparator;
-
 import butterknife.Bind;
 import butterknife.ButterKnife;
 import de.kuschku.libquassel.syncables.types.interfaces.QIrcChannel;
@@ -37,71 +35,26 @@ import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.util.CompatibilityUtils;
-import de.kuschku.util.observables.callbacks.ElementCallback;
 import de.kuschku.util.observables.callbacks.wrappers.AdapterUICallbackWrapper;
-import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public class NickListAdapter extends RecyclerView.Adapter<NickListAdapter.NickViewHolder> {
     public static final int TYPE_NORMAL = 0;
     public static final int TYPE_AWAY = 1;
     private final AppContext context;
+    private final AdapterUICallbackWrapper callback;
     QIrcChannel channel;
 
-    ObservableSortedList<QIrcUser> users = new ObservableSortedList<>(new Comparator<QIrcUser>() {
-        @Override
-        public int compare(QIrcUser o1, QIrcUser o2) {
-            if (channel.userModes(o1).equals(channel.userModes(o2))) {
-                return o1.nick().compareToIgnoreCase(o2.nick());
-            } else {
-                return channel.network().lowestModeIndex(channel.userModes(o1)) - channel.network().lowestModeIndex(channel.userModes(o2));
-            }
-        }
-    });
-    private ElementCallback<String> callback = new ElementCallback<String>() {
-        @Override
-        public void notifyItemInserted(String element) {
-            QIrcUser qIrcUser = channel.network().ircUser(element);
-            users.add(qIrcUser);
-        }
-
-        @Override
-        public void notifyItemRemoved(String element) {
-            for (int i = 0; i < users.size(); i++) {
-                QIrcUser user = users.get(i);
-                if (user.nick().equals(element)) {
-                    users.remove(i);
-                }
-            }
-        }
-
-        @Override
-        public void notifyItemChanged(String element) {
-            QIrcUser user = channel.network().ircUser(element);
-            if (user != null) {
-                users.notifyItemChanged(user);
-            }
-        }
-    };
-
     public NickListAdapter(AppContext context) {
         this.context = context;
-        users.addCallback(new AdapterUICallbackWrapper(this));
+        callback = new AdapterUICallbackWrapper(this);
     }
 
     public void setChannel(QIrcChannel channel) {
         if (this.channel != null)
             this.channel.users().removeCallback(callback);
         this.channel = channel;
-        this.users.clear();
-        if (this.channel != null) {
-            for (String nick : channel.users()) {
-                QIrcUser ircUser = channel.network().ircUser(nick);
-                if (ircUser != null) {
-                    users.add(ircUser);
-                }
-            }
+        if (this.channel != null)
             this.channel.users().addCallback(callback);
-        }
         notifyDataSetChanged();
     }
 
@@ -124,12 +77,16 @@ public class NickListAdapter extends RecyclerView.Adapter<NickListAdapter.NickVi
 
     @Override
     public void onBindViewHolder(NickViewHolder holder, int position) {
-        holder.bind(users.get(position));
+        holder.bind(getItem(position));
     }
 
     @Override
     public int getItemCount() {
-        return users.size();
+        return this.channel != null ? this.channel.users().size() : 0;
+    }
+
+    public QIrcUser getItem(int position) {
+        return this.channel != null ? this.channel.network().ircUser(this.channel.users().get(position)) : null;
     }
 
     @NonNull
@@ -143,7 +100,7 @@ public class NickListAdapter extends RecyclerView.Adapter<NickListAdapter.NickVi
 
     @Override
     public int getItemViewType(int position) {
-        return users.get(position).isAway() ? TYPE_AWAY : TYPE_NORMAL;
+        return getItem(position).isAway() ? TYPE_AWAY : TYPE_NORMAL;
     }
 
     class NickViewHolder extends RecyclerView.ViewHolder {
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 099972e36..e40c9bbf8 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -44,5 +44,5 @@
     <FrameLayout
         android:id="@+id/content_host"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="match_parent"/>
 </LinearLayout>
-- 
GitLab