From 5a9aae09ccaadd762a09b9d43cacf0b87345c61a Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sat, 27 Aug 2016 19:00:17 +0200
Subject: [PATCH] Fixed Sync during join/part/delete of channels

---
 app/build.gradle                              |  62 +++++-----
 .../kuschku/libquassel/ProtocolHandler.java   |   5 +-
 .../de/kuschku/libquassel/client/Client.java  |  39 ++++--
 .../serializers/IrcUserSerializer.java        |   2 +-
 .../serializers/NetworkSerializer.java        |  43 ++++---
 .../syncables/types/impl/BufferSyncer.java    |   4 +
 .../types/impl/BufferViewConfig.java          |  46 +++++--
 .../syncables/types/impl/IrcChannel.java      |  17 ++-
 .../types/interfaces/QBufferViewConfig.java   |   5 +
 .../quasseldroid_ng/ui/chat/MainActivity.java |  22 +++-
 .../ui/chat/chatview/ChatMessageRenderer.java |   1 -
 .../chat/drawer/BufferViewConfigAdapter.java  |  29 ++++-
 .../ui/chat/drawer/NetworkItem.java           | 112 +++++++++++++++---
 .../wrappers/MultiElementCallbackWrapper.java |   4 +-
 .../util/servicebound/BoundActivity.java      |   2 +-
 app/src/main/res/layout/activity_main.xml     |   4 +-
 app/src/main/res/menu/chatlist.xml            |   9 +-
 app/src/main/res/values/strings.xml           |   3 +
 18 files changed, 305 insertions(+), 104 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index a2fd16b3e..18449596d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -32,40 +32,43 @@ dependencies {
 }
 
 if (project.hasProperty("storeFile")) {
-  android {
-    signingConfigs {
-      release {
-        storeFile file(project.property("storeFile"))
-        storePassword project.property("storePassword")
-        keyAlias project.property("keyAlias")
-        keyPassword project.property("keyPassword")
-      }
-    }
-    
-    buildTypes {
-      release {
-        signingConfig signingConfigs.release
-      }
+    android {
+        signingConfigs {
+            release {
+                storeFile file(project.property("storeFile"))
+                storePassword project.property("storePassword")
+                keyAlias project.property("keyAlias")
+                keyPassword project.property("keyPassword")
+            }
+        }
+
+        buildTypes {
+            release {
+                signingConfig signingConfigs.release
+            }
+            debug {
+                applicationIdSuffix ".debug"
+            }
+        }
     }
-  }
 }
 
 def versionPropsFile = file('version.properties')
 def versionBuild = 0
 if (versionPropsFile.exists() && versionPropsFile.canRead()) {
-  def Properties versionProps = new Properties()
-
-  versionProps.load(new FileInputStream(versionPropsFile))
-  def runTasks = gradle.startParameter.taskNames
-  versionBuild = Integer.valueOf(versionProps['VERSION_BUILD'].toString())
-  if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
-    versionBuild = versionBuild + 1
-  }
-  versionProps['VERSION_BUILD'] = versionBuild.toString()
-  versionProps.store(versionPropsFile.newWriter(), null)
+    def Properties versionProps = new Properties()
+
+    versionProps.load(new FileInputStream(versionPropsFile))
+    def runTasks = gradle.startParameter.taskNames
+    versionBuild = Integer.valueOf(versionProps['VERSION_BUILD'].toString())
+    if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
+        versionBuild = versionBuild + 1
+    }
+    versionProps['VERSION_BUILD'] = versionBuild.toString()
+    versionProps.store(versionPropsFile.newWriter(), null)
 } else {
-  // Get jenkins build number from environment
-  versionBuild = Integer.valueOf(System.getenv("BUILD_NUMBER"))
+    // Get jenkins build number from environment
+    versionBuild = Integer.valueOf(System.getenv("BUILD_NUMBER"))
 }
 
 
@@ -74,7 +77,7 @@ def rawVersionName = "0.2.0"
 android {
     compileSdkVersion 24
     buildToolsVersion "24.0.0"
-    
+
     defaultConfig {
         applicationId "com.iskrembilen.quasseldroid"
         minSdkVersion 16
@@ -148,7 +151,7 @@ dependencies {
 
     // UI Libs
     compile 'com.bignerdranch.android:expandablerecyclerview:2.0.4'
-    compile(name:'library-release', ext:'aar')
+    compile(name: 'library-release', ext: 'aar')
     // This dependency can be removed as soon as the requires ressources are copied over.
     compile('com.mikepenz:materialdrawer:5.0.3@aar') { transitive = true }
     compile('com.github.afollestad.material-dialogs:core:0.8.5.3@aar') { transitive = true }
@@ -159,7 +162,6 @@ dependencies {
     compile "com.github.Raizlabs.DBFlow:dbflow-core:3.1.1"
     compile "com.github.Raizlabs.DBFlow:dbflow:3.1.1"
 
-
     // Appcompat
     compile 'com.android.support:appcompat-v7:24.0.0'
     compile 'com.android.support:design:24.0.0'
diff --git a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
index d626b07bd..3da4609ce 100644
--- a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
+++ b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
@@ -22,6 +22,7 @@
 package de.kuschku.libquassel;
 
 import android.support.annotation.NonNull;
+import android.util.Log;
 
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
@@ -95,7 +96,9 @@ public class ProtocolHandler implements IProtocolHandler {
             final Object syncable = client.unsafe_getObjectByIdentifier(packedFunc.className, packedFunc.objectName);
 
             if (syncable == null) {
-                client.bufferSync(packedFunc);
+                Log.d("ProtocolHandler", String.format("Sync Failed: %s::%s(%s, %s)", packedFunc.className, packedFunc.methodName, packedFunc.objectName, packedFunc.params));
+                if (client.connectionStatus() == ConnectionChangeEvent.Status.INITIALIZING_DATA)
+                    client.bufferSync(packedFunc);
             } else {
                 if (syncable instanceof SyncableObject && !((SyncableObject) syncable).initialized()) {
                     client.initObject(packedFunc.className, packedFunc.objectName, (SyncableObject) syncable);
diff --git a/app/src/main/java/de/kuschku/libquassel/client/Client.java b/app/src/main/java/de/kuschku/libquassel/client/Client.java
index fe51a75ae..e11005cd4 100644
--- a/app/src/main/java/de/kuschku/libquassel/client/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/client/Client.java
@@ -364,16 +364,21 @@ public class Client extends AClient {
             }
         }
 
-        // Execute cached sync requests
-        if (bufferedSyncs.size() > 0) {
-            String key = hashName(className, objectName);
-            if (bufferedSyncs.containsKey(key)) {
-                Log.d("libquassel", "Unqueueing syncs: " + className + ":" + objectName);
-                List<SyncFunction> functions = bufferedSyncs.get(key);
-                for (SyncFunction function : functions)
-                    provider.handle(function);
-                bufferedSyncs.remove(key);
+        synchronized (bufferedSyncs) {
+            if (r > 0) r--;
+            else throw new RuntimeException();
+            // Execute cached sync requests
+            if (bufferedSyncs.size() > 0) {
+                String key = hashName(className, objectName);
+                if (bufferedSyncs.containsKey(key)) {
+                    Log.d("libquassel", "Unqueueing syncs: " + className + ":" + objectName);
+                    List<SyncFunction> functions = bufferedSyncs.get(key);
+                    for (SyncFunction function : functions)
+                        provider.handle(function);
+                    bufferedSyncs.remove(key);
+                }
             }
+            r++;
         }
     }
 
@@ -445,10 +450,16 @@ public class Client extends AClient {
         if (connectionStatus() == ConnectionChangeEvent.Status.CONNECTED) {
             Log.d("libquassel", "Queueing sync: " + packedFunc);
         }
-        if (!bufferedSyncs.containsKey(key))
-            bufferedSyncs.put(key, new LinkedList<>());
-        bufferedSyncs.get(key).add(packedFunc);
-        Log.d("libquassel", "Queued syncs: " + bufferedSyncs.keySet());
+
+        synchronized (bufferedSyncs) {
+            if (r > 0) r--;
+            else throw new RuntimeException();
+            if (!bufferedSyncs.containsKey(key))
+                bufferedSyncs.put(key, new LinkedList<>());
+            bufferedSyncs.get(key).add(packedFunc);
+            Log.d("libquassel", "Queued syncs: " + bufferedSyncs.keySet());
+            r++;
+        }
     }
 
     public void bufferBuffer(QBufferViewConfig bufferViewConfig, int bufferId, int pos) {
@@ -481,4 +492,6 @@ public class Client extends AClient {
     public String coreId() {
         return coreId;
     }
+
+    private int r = 1;
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcUserSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcUserSerializer.java
index 498c5701e..7ff0ab262 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcUserSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcUserSerializer.java
@@ -96,7 +96,7 @@ public class IrcUserSerializer implements ObjectSerializer<IrcUser> {
                 (String) map.get("suserHost").data,
                 (String) map.get("nick").data,
                 (String) map.get("realName").data,
-                (String) map.get("account").data,
+                (map.get("account") == null) ? "": (String) map.get("account").data,
                 (String) map.get("awayMessage").data,
                 (DateTime) map.get("loginTime").data,
                 (boolean) map.get("encrypted").data,
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
index f68c1ed38..20bf3c4fd 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
@@ -27,6 +27,7 @@ import android.support.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -48,6 +49,7 @@ import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
 public class NetworkSerializer implements ObjectSerializer<Network> {
     @NonNull
     private static final NetworkSerializer serializer = new NetworkSerializer();
+    public static final DateTime UNIX_EPOCH = new DateTime(0L);
 
     private NetworkSerializer() {
     }
@@ -106,29 +108,36 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
             ircUsers = new ArrayList<>(max);
             for (int i = 0; i < max; i++) {
                 ircUsers.add(new IrcUser(
-                        (String) users.get("server").data.get(i),
-                        (String) users.get("ircOperator").data.get(i),
-                        (boolean) users.get("away").data.get(i),
-                        (int) users.get("lastAwayMessage").data.get(i),
-                        (DateTime) users.get("idleTime").data.get(i),
-                        (String) users.get("whoisServiceReply").data.get(i),
-                        (String) users.get("suserHost").data.get(i),
-                        (String) users.get("nick").data.get(i),
-                        (String) users.get("realName").data.get(i),
-                        (String) users.get("account").data.get(i),
-                        (String) users.get("awayMessage").data.get(i),
-                        (DateTime) users.get("loginTime").data.get(i),
-                        (boolean) users.get("encrypted").data.get(i),
-                        (List<String>) users.get("channels").data.get(i),
-                        (String) users.get("host").data.get(i),
-                        (String) users.get("userModes").data.get(i),
-                        (String) users.get("user").data.get(i)
+                        getAtPosition(users, "server", i, ""),
+                        getAtPosition(users, "ircOperator", i, ""),
+                        getAtPosition(users, "away", i, false),
+                        getAtPosition(users, "lastAwayMessage", i, 0),
+                        getAtPosition(users, "idleTime", i, UNIX_EPOCH),
+                        getAtPosition(users, "whoisServiceReply", i, ""),
+                        getAtPosition(users, "suserHost", i, ""),
+                        getAtPosition(users, "nick", i, ""),
+                        getAtPosition(users, "realName", i, ""),
+                        getAtPosition(users, "account", i, ""),
+                        getAtPosition(users, "awayMessage", i, ""),
+                        getAtPosition(users, "loginTime", i, UNIX_EPOCH),
+                        getAtPosition(users, "encrypted", i, false),
+                        getAtPosition(users, "channels", i, Collections.emptyList()),
+                        getAtPosition(users, "host", i, ""),
+                        getAtPosition(users, "userModes", i, ""),
+                        getAtPosition(users, "user", i, "")
                 ));
             }
         }
         return ircUsers;
     }
 
+    private <T> T getAtPosition(@NonNull Map<String, QVariant<List>> users, String field, int index, T or) {
+        if (users.containsKey(field) && users.get(field) != null && users.get(field).data != null && users.get(field).data.size() > index)
+            return (T) users.get(field).data.get(index);
+        else
+            return or;
+    }
+
     @NonNull
     private List<QIrcChannel> extractChannels(@Nullable Map<String, QVariant<List>> channels) {
         final List<QIrcChannel> ircChannels;
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferSyncer.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferSyncer.java
index 23176613b..acc57b43b 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferSyncer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferSyncer.java
@@ -35,6 +35,7 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.types.abstracts.ABufferSyncer;
 import de.kuschku.libquassel.syncables.types.interfaces.QBacklogManager;
+import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewConfig;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
@@ -123,6 +124,9 @@ public class BufferSyncer extends ABufferSyncer<BufferSyncer> {
     public void _removeBuffer(int buffer) {
         assertNotNull(client);
 
+        for (QBufferViewConfig config : client.bufferViewManager().bufferViewConfigs()) {
+            config.deleteBuffer(buffer);
+        }
         markerLines.removeAt(markerLines.indexOfKey(buffer));
         lastSeenMsgs.removeAt(lastSeenMsgs.indexOfKey(buffer));
         client.bufferManager().removeBuffer(buffer);
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewConfig.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewConfig.java
index 9ec26017d..e21f21901 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewConfig.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewConfig.java
@@ -22,6 +22,7 @@
 package de.kuschku.libquassel.syncables.types.impl;
 
 import android.support.annotation.NonNull;
+import android.util.Log;
 
 import java.util.Collections;
 import java.util.List;
@@ -43,7 +44,9 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
     @NonNull
     private final ObservableList<Integer> buffers;
     @NonNull
-    private final ObservableSet<Integer> bufferIds;
+    private final ObservableSet<Integer> visibleBufferIds;
+    @NonNull
+    private final ObservableSet<Integer> allBufferIds;
     @NonNull
     private final ObservableSet<Integer> removedBuffers;
     @NonNull
@@ -67,8 +70,12 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
         this.buffers = new ObservableList<>(buffers);
         buffers.removeAll(removedBuffers);
         buffers.removeAll(temporarilyRemovedBuffers);
-        this.bufferIds = new ObservableSet<>();
-        bufferIds.addAll(buffers);
+        this.visibleBufferIds = new ObservableSet<>();
+        visibleBufferIds.addAll(buffers);
+        this.allBufferIds = new ObservableSet<>();
+        allBufferIds.addAll(buffers);
+        allBufferIds.addAll(removedBuffers);
+        allBufferIds.addAll(temporarilyRemovedBuffers);
         this.allowedBufferTypes = allowedBufferTypes;
         this.sortAlphabetically = sortAlphabetically;
         this.disableDecoration = disableDecoration;
@@ -145,7 +152,7 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
                 (networkId == 0 || (networkId == buffer.getInfo().networkId))
                 ) {
             int bufferid = buffer.getInfo().id;
-            if (bufferIds.contains(bufferid) && !temporarilyRemovedBuffers.contains(bufferid) && !removedBuffers.contains(bufferid))
+            if (visibleBufferIds.contains(bufferid) && !temporarilyRemovedBuffers.contains(bufferid) && !removedBuffers.contains(bufferid))
                 return DisplayType.ALWAYS;
             else if (temporarilyRemovedBuffers.contains(bufferid) && !removedBuffers.contains(bufferid))
                 return DisplayType.TEMP_HIDDEN;
@@ -247,7 +254,13 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
     @NonNull
     @Override
     public ObservableSet<Integer> bufferIds() {
-        return bufferIds;
+        return visibleBufferIds;
+    }
+
+    @NonNull
+    @Override
+    public ObservableSet<Integer> allBufferIds() {
+        return allBufferIds;
     }
 
     @NonNull
@@ -284,6 +297,8 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
             temporarilyRemovedBuffers.remove(bufferId);
 
         buffers.add(pos, bufferId);
+        visibleBufferIds.add(bufferId);
+        allBufferIds.add(bufferId);
     }
 
     @Override
@@ -324,10 +339,9 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
 
     @Override
     public void _removeBuffer(int bufferId) {
-        int index;
-        if ((index = buffers.indexOf(bufferId)) != -1) {
-            buffers.remove(index);
-        }
+        visibleBufferIds.remove(bufferId);
+        if (buffers.contains(bufferId))
+            buffers.remove((Integer) bufferId);
 
         if (removedBuffers.contains(bufferId))
             removedBuffers.remove(bufferId);
@@ -343,6 +357,7 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
 
     @Override
     public void _removeBufferPermanently(int bufferId) {
+        visibleBufferIds.remove(bufferId);
         if (buffers.contains(bufferId))
             buffers.remove((Integer) bufferId);
 
@@ -377,6 +392,15 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
         return networkList;
     }
 
+    @Override
+    public void deleteBuffer(int bufferId) {
+        visibleBufferIds.remove(bufferId);
+        allBufferIds.remove(bufferId);
+        buffers.remove(buffers.indexOf(bufferId));
+        temporarilyRemovedBuffers.remove(bufferId);
+        removedBuffers.remove(bufferId);
+    }
+
     @Override
     public void _update(@NonNull Map<String, QVariant> from) {
         _update(BufferViewConfigSerializer.get().fromLegacy(from));
@@ -396,8 +420,8 @@ public class BufferViewConfig extends ABufferViewConfig<BufferViewConfig> {
         this.hideInactiveNetworks = from.hideInactiveNetworks;
         this.buffers.clear();
         this.buffers.addAll(from.buffers);
-        this.bufferIds.retainAll(from.bufferIds);
-        this.bufferIds.addAll(from.bufferIds);
+        this.visibleBufferIds.retainAll(from.visibleBufferIds);
+        this.visibleBufferIds.addAll(from.visibleBufferIds);
         this.removedBuffers.retainAll(from.removedBuffers);
         this.removedBuffers.addAll(from.removedBuffers);
         this.temporarilyRemovedBuffers.retainAll(from.temporarilyRemovedBuffers);
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 5e3fd2cac..201f33605 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
@@ -37,9 +37,12 @@ import java.util.Set;
 
 import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.client.Client;
+import de.kuschku.libquassel.localtypes.buffers.ChannelBuffer;
+import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.serializers.IrcChannelSerializer;
 import de.kuschku.libquassel.syncables.types.abstracts.AIrcChannel;
+import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewConfig;
 import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.util.irc.ModeUtils;
@@ -257,6 +260,7 @@ public class IrcChannel extends AIrcChannel<IrcChannel> {
     @Override
     public void _setTopic(String topic) {
         this.topic = topic;
+        _update();
     }
 
     @Override
@@ -524,4 +528,15 @@ public class IrcChannel extends AIrcChannel<IrcChannel> {
         modes.addAll(A_channelModes.keySet());
         return modes;
     }
-}
+
+    @Override
+    public void _update() {
+        super._update();
+        ChannelBuffer buffer = client.bufferManager().channel(this);
+        if (buffer != null) {
+            for (QBufferViewConfig qBufferViewConfig : client.bufferViewManager().bufferViewConfigs()) {
+                qBufferViewConfig.bufferIds().notifyItemChanged(buffer.getInfo().id);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBufferViewConfig.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBufferViewConfig.java
index 36eec92cf..919a482df 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBufferViewConfig.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QBufferViewConfig.java
@@ -156,10 +156,15 @@ public interface QBufferViewConfig extends QObservable {
 
     ObservableSet<QNetwork> networkList();
 
+    void deleteBuffer(int bufferId);
+
     void updateNetworks();
 
     DisplayType mayDisplay(Buffer buffer);
 
+    @NonNull
+    ObservableSet<Integer> allBufferIds();
+
     enum DisplayType {
         NONE,
         ALWAYS,
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
index 3ad162032..31134ad77 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
@@ -154,6 +154,18 @@ public class MainActivity extends BoundActivity {
         chatList.setAdapter(chatListAdapter);
 
         chatListToolbar.inflateMenu(R.menu.chatlist);
+        chatListToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                switch (item.getItemId()) {
+                    case R.id.action_show_all: {
+                        Log.d("QD-NG", "Toggling Show/Hide All");
+                        chatListAdapter.toggleShowAll();
+                    } break;
+                }
+                return false;
+            }
+        });
 
         DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
         if (drawerLayout != null) {
@@ -347,6 +359,11 @@ public class MainActivity extends BoundActivity {
         }
     }
 
+    protected void reconnect() {
+        binder.stopBackgroundThread();
+        connectToServer(manager.account(context.settings().preferenceLastAccount.get()));
+    }
+
     @Override
     protected void onConnectToThread(@Nullable ClientBackgroundThread thread) {
         super.onConnectToThread(thread);
@@ -448,7 +465,10 @@ public class MainActivity extends BoundActivity {
         new MaterialDialog.Builder(this)
                 .content(context.themeUtil().translations.warningCertificate + "\n" + CertificateUtils.certificateToFingerprint(event.certificate, ""))
                 .title("Unknown Certificate")
-                .onPositive((dialog, which) -> new SQLiteCertificateManager(this).addCertificate(event.certificate, event.address))
+                .onPositive((dialog, which) -> {
+                    new SQLiteCertificateManager(this).addCertificate(event.certificate, event.address);
+                    reconnect();
+                })
                 .negativeColor(context.themeUtil().res.colorForeground)
                 .positiveText("Yes")
                 .negativeText("No")
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
index 683444a26..41e97b704 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
@@ -254,7 +254,6 @@ public class ChatMessageRenderer {
     }
 
     private void onBindTopic(@NonNull MessageViewHolder holder, @NonNull Message message) {
-        System.out.println(message);
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.content);
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
index 1e1be5585..90f0c3388 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
@@ -21,6 +21,7 @@
 
 package de.kuschku.quasseldroid_ng.ui.chat.drawer;
 
+import android.databinding.ObservableField;
 import android.os.Parcelable;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.LinearLayoutManager;
@@ -35,7 +36,6 @@ import java.lang.ref.WeakReference;
 import java.util.Map;
 import java.util.WeakHashMap;
 
-import de.kuschku.libquassel.events.BufferChangeEvent;
 import de.kuschku.libquassel.localtypes.buffers.Buffer;
 import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewConfig;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
@@ -51,15 +51,14 @@ public class BufferViewConfigAdapter extends ExpandableRecyclerAdapter<NetworkVi
     private final Map<Integer, BufferViewHolder> bufferViewHolderMap = new WeakHashMap<>();
     private QBufferViewConfig config;
     private WeakReference<RecyclerView> recyclerView = new WeakReference<>(null);
-    private int selectedFrom;
-    private int selectedTo;
     private int open;
     private OnBufferClickListener bufferClickListener;
+    private ObservableField<Boolean> showAll = new ObservableField<>(false);
 
     private ElementCallback<QNetwork> callback = new ElementCallback<QNetwork>() {
         @Override
         public void notifyItemInserted(QNetwork network) {
-            NetworkItem networkItem = new NetworkItem(context, config, network);
+            NetworkItem networkItem = new NetworkItem(context, config, network, BufferViewConfigAdapter.this);
             itemMap.put(network, networkItem);
             items.add(networkItem);
         }
@@ -75,6 +74,18 @@ public class BufferViewConfigAdapter extends ExpandableRecyclerAdapter<NetworkVi
         }
     };
 
+    public void notifyChildItemInserted(NetworkItem parentItem, int childPosition) {
+        super.notifyChildItemInserted(items.indexOf(parentItem), childPosition);
+    }
+
+    public void notifyChildItemRemoved(NetworkItem parentItem, int childPosition) {
+        super.notifyChildItemRemoved(items.indexOf(parentItem), childPosition);
+    }
+
+    public void notifyChildItemChanged(NetworkItem parentItem, int childPosition) {
+        super.notifyChildItemChanged(items.indexOf(parentItem), childPosition);
+    }
+
     private BufferViewConfigAdapter(AppContext context, ObservableSortedList<NetworkItem> items) {
         super(items);
         this.context = context;
@@ -192,7 +203,7 @@ public class BufferViewConfigAdapter extends ExpandableRecyclerAdapter<NetworkVi
         items.clear();
         itemMap.clear();
         for (QNetwork network : config.networkList()) {
-            NetworkItem networkItem = new NetworkItem(context, config, network);
+            NetworkItem networkItem = new NetworkItem(context, config, network, this);
             itemMap.put(network, networkItem);
             items.add(networkItem);
         }
@@ -243,4 +254,12 @@ public class BufferViewConfigAdapter extends ExpandableRecyclerAdapter<NetworkVi
         }
         return -1;
     }
+
+    public ObservableField<Boolean> showAll() {
+        return showAll;
+    }
+
+    public void toggleShowAll() {
+        showAll.set(!showAll.get());
+    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
index e528083d0..27c981ef8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
@@ -21,6 +21,9 @@
 
 package de.kuschku.quasseldroid_ng.ui.chat.drawer;
 
+import android.databinding.Observable;
+import android.util.Log;
+
 import com.bignerdranch.expandablerecyclerview.Model.ParentListItem;
 
 import java.util.List;
@@ -32,10 +35,15 @@ import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.util.irc.IrcCaseMapper;
 import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.lists.ObservableSet;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public class NetworkItem implements ParentListItem {
+    private final AppContext context;
+    private final QBufferViewConfig config;
     private final QNetwork network;
+    private final BufferViewConfigAdapter bufferViewConfigAdapter;
     private final ObservableSortedList<Buffer> buffers = new ObservableSortedList<>(Buffer.class, new ObservableSortedList.ItemComparator<Buffer>() {
         @Override
         public int compare(Buffer o1, Buffer o2) {
@@ -69,34 +77,108 @@ public class NetworkItem implements ParentListItem {
             return item1.getInfo().id == item2.getInfo().id;
         }
     });
-
-    public NetworkItem(AppContext context, QBufferViewConfig config, QNetwork network) {
-        this.network = network;
-        for (int id : config.bufferList()) {
-            Buffer buffer = context.client().bufferManager().buffer(id);
-            if (context.bufferDisplayTypes().contains(config.mayDisplay(buffer)) && buffer.getInfo().networkId == network.networkId())
+    private ElementCallback<Integer> callback = new ElementCallback<Integer>() {
+        @Override
+        public void notifyItemInserted(Integer element) {
+            Buffer buffer = context.client().bufferManager().buffer(element);
+            if (buffer != null && buffer.getInfo().networkId == network.networkId()) {
                 buffers.add(buffer);
+            }
+        }
+
+        @Override
+        public void notifyItemRemoved(Integer element) {
+            Buffer buffer = context.client().bufferManager().buffer(element);
+            if (buffer != null && buffer.getInfo().networkId == network.networkId()) {
+                buffers.remove(buffer);
+            }
         }
-        config.bufferIds().addCallback(new ElementCallback<Integer>() {
+
+        @Override
+        public void notifyItemChanged(Integer element) {
+            Buffer buffer = context.client().bufferManager().buffer(element);
+            if (buffer != null && buffer.getInfo().networkId == network.networkId()) {
+                buffers.notifyItemChanged(buffers.indexOf(buffer));
+            }
+        }
+    };
+    private ObservableSet<Integer> backingSet;
+
+    public NetworkItem(AppContext context, QBufferViewConfig config, QNetwork network, BufferViewConfigAdapter bufferViewConfigAdapter) {
+        this.context = context;
+        this.config = config;
+        this.network = network;
+        this.bufferViewConfigAdapter = bufferViewConfigAdapter;
+        bufferViewConfigAdapter.showAll().addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
             @Override
-            public void notifyItemInserted(Integer id) {
-                Buffer buffer = context.client().bufferManager().buffer(id);
-                if (context.bufferDisplayTypes().contains(config.mayDisplay(buffer)) && buffer.getInfo().networkId == network.networkId())
-                    buffers.add(buffer);
+            public void onPropertyChanged(Observable sender, int propertyId) {
+                setShowAll(bufferViewConfigAdapter.showAll().get());
+            }
+        });
+        setShowAll(bufferViewConfigAdapter.showAll().get());
+        this.buffers.addCallback(new UICallback() {
+            @Override
+            public void notifyItemInserted(int position) {
+                bufferViewConfigAdapter.notifyChildItemInserted(NetworkItem.this, position);
             }
 
             @Override
-            public void notifyItemRemoved(Integer id) {
-                buffers.remove(context.client().bufferManager().buffer(id));
+            public void notifyItemChanged(int position) {
+                bufferViewConfigAdapter.notifyChildItemChanged(NetworkItem.this, position);
             }
 
             @Override
-            public void notifyItemChanged(Integer id) {
-                buffers.notifyItemChanged(buffers.indexOf(context.client().bufferManager().buffer(id)));
+            public void notifyItemRemoved(int position) {
+                bufferViewConfigAdapter.notifyChildItemRemoved(NetworkItem.this, position);
+            }
+
+            @Override
+            public void notifyItemMoved(int from, int to) {
+                this.notifyItemRemoved(from);
+                this.notifyItemInserted(to);
+            }
+
+            @Override
+            public void notifyItemRangeInserted(int position, int count) {
+                for (int i = position; i < position + count; i++) {
+                    this.notifyItemInserted(i);
+                }
+            }
+
+            @Override
+            public void notifyItemRangeChanged(int position, int count) {
+                for (int i = position; i < position + count; i++) {
+                    this.notifyItemChanged(i);
+                }
+            }
+
+            @Override
+            public void notifyItemRangeRemoved(int position, int count) {
+                for (int i = position; i < position + count; i++) {
+                    this.notifyItemRemoved(position);
+                }
             }
         });
     }
 
+    public void populateList(ObservableSet<Integer> backingSet) {
+        if (this.backingSet != null)
+            this.backingSet.removeCallback(callback);
+        buffers.clear();
+
+        backingSet.addCallback(callback);
+        for (int id : backingSet) {
+            Buffer buffer = context.client().bufferManager().buffer(id);
+            if (buffer != null && buffer.getInfo().networkId == network.networkId())
+                buffers.add(buffer);
+        }
+        this.backingSet = backingSet;
+    }
+
+    public void setShowAll(boolean showAll) {
+        populateList(showAll ? config.allBufferIds() : config.bufferIds());
+    }
+
     @Override
     public List<?> getChildItemList() {
         return buffers;
diff --git a/app/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java b/app/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
index 2d49b099f..706884a1b 100644
--- a/app/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
+++ b/app/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
@@ -65,14 +65,14 @@ public class MultiElementCallbackWrapper<T> implements ElementCallback<T> {
     @Override
     public void notifyItemRemoved(@Nullable T element) {
         for (ElementCallback<T> callback : callbacks) {
-            callback.notifyItemInserted(element);
+            callback.notifyItemRemoved(element);
         }
     }
 
     @Override
     public void notifyItemChanged(@Nullable T element) {
         for (ElementCallback<T> callback : callbacks) {
-            callback.notifyItemInserted(element);
+            callback.notifyItemChanged(element);
         }
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java b/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java
index 1ca1e97be..c39787416 100644
--- a/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java
+++ b/app/src/main/java/de/kuschku/util/servicebound/BoundActivity.java
@@ -41,7 +41,7 @@ public abstract class BoundActivity extends AppCompatActivity {
     protected AppContext context = new AppContext();
     @StyleRes
     private int themeId;
-    private QuasselService.LocalBinder binder;
+    protected QuasselService.LocalBinder binder;
     private ServiceConnection connection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 64bf92f02..00183c8bf 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -44,13 +44,13 @@
     </LinearLayout>
 
     <android.support.design.widget.NavigationView
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="start"
         android:background="?attr/colorBackground">
 
         <LinearLayout
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical">
 
diff --git a/app/src/main/res/menu/chatlist.xml b/app/src/main/res/menu/chatlist.xml
index fefc30cbf..e2b37c7b7 100644
--- a/app/src/main/res/menu/chatlist.xml
+++ b/app/src/main/res/menu/chatlist.xml
@@ -22,13 +22,16 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <item
+        android:id="@+id/action_join_channel"
         android:icon="?attr/iconAdd"
-        android:title="Join Channel"
+        android:title="@string/action_join_channel"
         app:showAsAction="ifRoom" />
     <item
-        android:title="Show/Hide Hidden"
+        android:id="@+id/action_show_all"
+        android:title="@string/action_show_hide_hidden"
         app:showAsAction="never" />
     <item
-        android:title="Manage Chat Lists"
+        android:id="@+id/action_manage_chat_lists"
+        android:title="@string/action_manage_chat_lists"
         app:showAsAction="never" />
 </menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ae9968bb3..8ee6c11f0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -84,4 +84,7 @@
     <string name="label_settings">Settings</string>
     <string name="label_search">Find in Buffer</string>
     <string name="label_disconnect">Disconnect</string>
+    <string name="action_manage_chat_lists">Manage Chat Lists</string>
+    <string name="action_show_hide_hidden">Show/Hide Hidden</string>
+    <string name="action_join_channel">Join Channel</string>
 </resources>
-- 
GitLab