diff --git a/app/src/main/java/de/kuschku/libquassel/Client.java b/app/src/main/java/de/kuschku/libquassel/Client.java
index 5d01acbcfe66b425263fa4d3fbf9a76c0f6e8502..70bb59b03ef6ef3dd2e09ffe2c173675e2058cf5 100644
--- a/app/src/main/java/de/kuschku/libquassel/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/Client.java
@@ -23,6 +23,8 @@ import de.kuschku.libquassel.syncables.types.BufferSyncer;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
 import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
+import de.kuschku.util.backports.Optional;
+import de.kuschku.util.backports.Optionals;
 import de.kuschku.util.backports.Stream;
 
 
@@ -101,7 +103,13 @@ public class Client {
     }
 
     public void __objectRenamed__(String className, String newName, String oldName) {
-        getObjectByIdentifier(className, oldName).renameObject(newName);
+        safeGetObjectByIdentifier(className, oldName).renameObject(newName);
+    }
+
+    private SyncableObject safeGetObjectByIdentifier(String className, String oldName) {
+        SyncableObject val = getObjectByIdentifier(className, oldName);
+        if (val == null) throw new IllegalArgumentException(String.format("Object %s::%s does not exist", className, oldName));
+        else return val;
     }
 
     public SyncableObject getObjectByIdentifier(final String className, final String objectName) {
diff --git a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
index 0a4970bf0e0c6500b2d353083532776a3191e54e..3db6d26a0a3cf87f56c589708f3d88c6aa724760 100644
--- a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
+++ b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
@@ -1,6 +1,8 @@
 package de.kuschku.libquassel;
 
 
+import android.util.Log;
+
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
@@ -15,6 +17,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
+import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.HandshakeFailedEvent;
 import de.kuschku.libquassel.functions.types.HandshakeFunction;
 import de.kuschku.libquassel.objects.types.ClientInit;
@@ -175,7 +178,7 @@ public class CoreConnection {
                         final ByteBuffer buffer = ByteBuffer.allocate(4);
                         getChannel().read(buffer);
 
-                        final Protocol protocol = new ProtocolSerializer().deserialize(buffer);
+                        final Protocol protocol = ProtocolSerializer.get().deserialize(buffer);
 
                         // Wrap socket in deflater if compression is enabled
                         setCompression(protocol.protocolFlags.supportsCompression);
@@ -210,9 +213,9 @@ public class CoreConnection {
                     }
                 }
             } catch (SocketException e) {
-                Logger.getLogger("libquassel").log(Level.FINEST, "Socket closed while reading");
-            } catch (IOException e) {
-                e.printStackTrace();
+                Log.e("libquassel", "Socket closed while reading");
+            } catch (Exception e) {
+                busProvider.sendEvent(new GeneralErrorEvent(e));
             }
         }
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
index 7cd7ad5b16aafb5acbc62b8fde619063b9e6deb7..17eb85768c3a71954055f471c54cb30296297876 100644
--- a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
@@ -7,7 +7,8 @@ import java.util.List;
 
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
-import de.kuschku.util.ObservableList;
+import de.kuschku.util.observablelists.AutoScroller;
+import de.kuschku.util.observablelists.ObservableSortedList;
 
 public abstract class BacklogManager extends SyncableObject {
     public abstract void requestBacklog(int bufferId, int from, int to, int count, int extra);
@@ -16,9 +17,9 @@ public abstract class BacklogManager extends SyncableObject {
 
     public abstract void displayMessage(int bufferId, Message message);
 
-    public abstract ObservableList<Message> get(int bufferId);
+    public abstract ObservableSortedList<Message> get(int bufferId);
 
-    public abstract void bind(int bufferId, @Nullable RecyclerView.Adapter adapter);
+    public abstract void bind(int bufferId, @Nullable RecyclerView.Adapter adapter, AutoScroller scroller);
 
     public abstract void requestMoreBacklog(int bufferId, int count);
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
index 1a1d85eadf83bbd19728c8ed4d5855bc3477f109..0360f62bf287d7751a73954aa5c5650936036709 100644
--- a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
@@ -15,10 +15,12 @@ import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.functions.types.SyncFunction;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.util.ObservableList;
+import de.kuschku.util.observablelists.AutoScroller;
+import de.kuschku.util.observablelists.ObservableSortedList;
+import de.kuschku.util.observablelists.RecyclerViewAdapterCallback;
 
 public class SimpleBacklogManager extends BacklogManager {
-    SparseArray<ObservableList<Message>> backlogs = new SparseArray<>();
+    SparseArray<ObservableSortedList<Message>> backlogs = new SparseArray<>();
     private BusProvider busProvider;
 
     public SimpleBacklogManager(BusProvider busProvider) {
@@ -46,27 +48,27 @@ public class SimpleBacklogManager extends BacklogManager {
         get(bufferId).list.add(message);
     }
 
-    public void bind(int bufferId, @Nullable RecyclerView.Adapter adapter) {
+    public void bind(int bufferId, @Nullable RecyclerView.Adapter adapter, AutoScroller scroller) {
         if (adapter == null)
             get(bufferId).setCallback(null);
         else
-            get(bufferId).setCallback(new ObservableList.RecyclerViewAdapterCallback(adapter));
+            get(bufferId).setCallback(new RecyclerViewAdapterCallback(adapter, scroller));
     }
 
     @Override
     public void requestMoreBacklog(int bufferId, int count) {
-        ObservableList<Message> backlog = backlogs.get(bufferId);
+        ObservableSortedList<Message> backlog = backlogs.get(bufferId);
         int messageId =
                 (backlog == null) ? -1 :
-                (backlog.first() == null) ? -1 :
-                backlog.first().messageId;
+                (backlog.last() == null) ? -1 :
+                backlog.last().messageId;
 
         requestBacklog(bufferId, -1, messageId, count, 0);
     }
 
-    public ObservableList<Message> get(int bufferId) {
+    public ObservableSortedList<Message> get(int bufferId) {
         if (backlogs.get(bufferId) == null)
-            backlogs.put(bufferId, new ObservableList<>(Message.class));
+            backlogs.put(bufferId, new ObservableSortedList<>(Message.class, true));
 
         return backlogs.get(bufferId);
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitDataFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitDataFunctionSerializer.java
index 8d6ec1380d0bcc777009960d178a377adadeb495..4170ce6058c635045585e8cba54bd07938bd4362 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitDataFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitDataFunctionSerializer.java
@@ -7,12 +7,21 @@ import de.kuschku.libquassel.functions.types.PackedInitDataFunction;
 import de.kuschku.libquassel.functions.types.UnpackedInitDataFunction;
 
 public class InitDataFunctionSerializer implements FunctionSerializer<InitDataFunction> {
+    private static final InitDataFunctionSerializer serializer = new InitDataFunctionSerializer();
+
+    private InitDataFunctionSerializer() {
+    }
+
+    public static InitDataFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final InitDataFunction data) {
         if (data instanceof UnpackedInitDataFunction) {
-            return new PackedInitDataFunctionSerializer().serialize((UnpackedInitDataFunction) data);
+            return PackedInitDataFunctionSerializer.get().serialize((UnpackedInitDataFunction) data);
         } else if (data instanceof PackedInitDataFunction) {
-            return new UnpackedInitDataFunctionSerializer().serialize((PackedInitDataFunction) data);
+            return UnpackedInitDataFunctionSerializer.get().serialize((PackedInitDataFunction) data);
         } else {
             throw new IllegalArgumentException("Can not be applied to these arguments");
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitRequestFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitRequestFunctionSerializer.java
index cdf3e32b61d6d05738fd6a020bb55031d680bee2..bfde1a1f56dc0c14705dc9285fe21ab404cb3723 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitRequestFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/InitRequestFunctionSerializer.java
@@ -11,6 +11,15 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import static de.kuschku.libquassel.primitives.QMetaType.Type.QByteArray;
 
 public class InitRequestFunctionSerializer implements FunctionSerializer<InitRequestFunction> {
+    private static final InitRequestFunctionSerializer serializer = new InitRequestFunctionSerializer();
+
+    private InitRequestFunctionSerializer() {
+    }
+
+    public static InitRequestFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final InitRequestFunction data) {
         return Lists.newArrayList(
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedInitDataFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedInitDataFunctionSerializer.java
index 540466fba4c85d5d2d9dccf430ce82d30461c8cf..3584efc5ff7a8f1d4f85d7c2157ffad6358aff02 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedInitDataFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedInitDataFunctionSerializer.java
@@ -9,6 +9,15 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.protocols.DatastreamPeer;
 
 public class PackedInitDataFunctionSerializer implements FunctionSerializer<UnpackedInitDataFunction> {
+    private static final PackedInitDataFunctionSerializer serializer = new PackedInitDataFunctionSerializer();
+
+    private PackedInitDataFunctionSerializer() {
+    }
+
+    public static PackedInitDataFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final UnpackedInitDataFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedRpcCallFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedRpcCallFunctionSerializer.java
index 3ff34ceaadd3f5c6acf0fe5ff9c16b9d5994ef41..9bbb06d103e9f46d10bdd88e47bb164ec037fddf 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedRpcCallFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedRpcCallFunctionSerializer.java
@@ -10,6 +10,15 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.protocols.DatastreamPeer;
 
 public class PackedRpcCallFunctionSerializer implements FunctionSerializer<RpcCallFunction> {
+    private static final PackedRpcCallFunctionSerializer serializer = new PackedRpcCallFunctionSerializer();
+
+    private PackedRpcCallFunctionSerializer() {
+    }
+
+    public static PackedRpcCallFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final RpcCallFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedSyncFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedSyncFunctionSerializer.java
index 2a5878c8d392be7a51df7026b396713250023d8f..a76a559f0b01c67bb60836ce23bbaef6ee5c7120 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedSyncFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/PackedSyncFunctionSerializer.java
@@ -10,6 +10,15 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.protocols.DatastreamPeer;
 
 public class PackedSyncFunctionSerializer implements FunctionSerializer<SyncFunction> {
+    private static final PackedSyncFunctionSerializer serializer = new PackedSyncFunctionSerializer();
+
+    private PackedSyncFunctionSerializer() {
+    }
+
+    public static PackedSyncFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final SyncFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedInitDataFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedInitDataFunctionSerializer.java
index 5de08d549b9cbe80991d99c4583625fe39f55c0b..601dcb67ebe7c6a15718cd7bd0de5a13ad09aeb2 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedInitDataFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedInitDataFunctionSerializer.java
@@ -9,6 +9,15 @@ import de.kuschku.libquassel.functions.types.PackedInitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class UnpackedInitDataFunctionSerializer implements FunctionSerializer<PackedInitDataFunction> {
+    private static final UnpackedInitDataFunctionSerializer serializer = new UnpackedInitDataFunctionSerializer();
+
+    private UnpackedInitDataFunctionSerializer() {
+    }
+
+    public static UnpackedInitDataFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final PackedInitDataFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedRpcCallFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedRpcCallFunctionSerializer.java
index 6c492926576b33c63e73eba9f444f69557af532e..ab09225054386969759fe221fe12760a2a418aac 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedRpcCallFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedRpcCallFunctionSerializer.java
@@ -9,6 +9,15 @@ import de.kuschku.libquassel.primitives.QMetaType;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class UnpackedRpcCallFunctionSerializer implements FunctionSerializer<RpcCallFunction> {
+    private static final UnpackedRpcCallFunctionSerializer serializer = new UnpackedRpcCallFunctionSerializer();
+
+    private UnpackedRpcCallFunctionSerializer() {
+    }
+
+    public static UnpackedRpcCallFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final RpcCallFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedSyncFunctionSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedSyncFunctionSerializer.java
index 920cd379184e7dee32e6dbe789378819109faf9c..f77e189c9275028b0e536240222ed44edb875a34 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedSyncFunctionSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/UnpackedSyncFunctionSerializer.java
@@ -9,6 +9,15 @@ import de.kuschku.libquassel.primitives.QMetaType;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class UnpackedSyncFunctionSerializer implements FunctionSerializer<SyncFunction> {
+    private static final UnpackedSyncFunctionSerializer serializer = new UnpackedSyncFunctionSerializer();
+
+    private UnpackedSyncFunctionSerializer() {
+    }
+
+    public static UnpackedSyncFunctionSerializer get() {
+        return serializer;
+    }
+
     @Override
     public List serialize(final SyncFunction data) {
         final List func = new ArrayList<>();
diff --git a/app/src/main/java/de/kuschku/libquassel/message/Message.java b/app/src/main/java/de/kuschku/libquassel/message/Message.java
index b3a80ef6c48b7042145d3e46300167d69993ad21..e222d3b78fb13c97e8e19144345e216f1b7516ed 100644
--- a/app/src/main/java/de/kuschku/libquassel/message/Message.java
+++ b/app/src/main/java/de/kuschku/libquassel/message/Message.java
@@ -8,7 +8,7 @@ import java.io.Serializable;
 import java.util.Comparator;
 
 import de.kuschku.libquassel.primitives.types.BufferInfo;
-import de.kuschku.util.ContentComparable;
+import de.kuschku.util.observablelists.ContentComparable;
 
 public class Message implements ContentComparable<Message> {
     public final int messageId;
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/MessageTypeRegistry.java b/app/src/main/java/de/kuschku/libquassel/objects/MessageTypeRegistry.java
index 3b7cc16dd1e229d016fc35f788c21aafcc6389a6..38e34a81708cb69f4b03291f7ef05e6bd68b4781 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/MessageTypeRegistry.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/MessageTypeRegistry.java
@@ -20,16 +20,16 @@ public class MessageTypeRegistry {
     private static Map<String, ObjectSerializer> serializerMap = new HashMap<String, ObjectSerializer>();
 
     static {
-        serializerMap.put("ClientInit", new ClientInitSerializer());
-        serializerMap.put("ClientInitAck", new ClientInitAckSerializer());
-        serializerMap.put("ClientInitReject", new ClientInitRejectSerializer());
-        serializerMap.put("ClientLogin", new ClientLoginSerializer());
-        serializerMap.put("ClientLoginAck", new ClientLoginAckSerializer());
-        serializerMap.put("ClientLoginReject", new ClientLoginRejectSerializer());
-        serializerMap.put("CoreSetupData", new CoreSetupDataSerializer());
-        serializerMap.put("CoreSetupAck", new CoreSetupAckSerializer());
-        serializerMap.put("CoreSetupReject", new CoreSetupRejectSerializer());
-        serializerMap.put("SessionInit", new SessionInitSerializer());
+        serializerMap.put("ClientInit", ClientInitSerializer.get());
+        serializerMap.put("ClientInitAck", ClientInitAckSerializer.get());
+        serializerMap.put("ClientInitReject", ClientInitRejectSerializer.get());
+        serializerMap.put("ClientLogin", ClientLoginSerializer.get());
+        serializerMap.put("ClientLoginAck", ClientLoginAckSerializer.get());
+        serializerMap.put("ClientLoginReject", ClientLoginRejectSerializer.get());
+        serializerMap.put("CoreSetupData", CoreSetupDataSerializer.get());
+        serializerMap.put("CoreSetupAck", CoreSetupAckSerializer.get());
+        serializerMap.put("CoreSetupReject", CoreSetupRejectSerializer.get());
+        serializerMap.put("SessionInit", SessionInitSerializer.get());
     }
 
     // Disable Constructor
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitAckSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitAckSerializer.java
index ca0e2e7c2d1206ad2e60d82a53db20fc576a3160..dd5d31eade9a08bdf565471078251a953d8ae841 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitAckSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitAckSerializer.java
@@ -15,15 +15,24 @@ import de.kuschku.libquassel.objects.types.StorageBackend;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientInitAckSerializer implements ObjectSerializer<ClientInitAck> {
+    private static ClientInitAckSerializer serializer = new ClientInitAckSerializer();
+
+    private ClientInitAckSerializer() {
+    }
+
+    public static ClientInitAckSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientInitAck data) {
         final List<Map<String, QVariant>> storageBackends = new ArrayList<>();
-        final StorageBackendSerializer storageBackendSerializer = new StorageBackendSerializer();
+        final StorageBackendSerializer storageBackendSerializer = StorageBackendSerializer.get();
         for (StorageBackend backend : data.StorageBackends) {
             storageBackends.add((Map<String, QVariant>) storageBackendSerializer.toVariantMap(backend));
         }
 
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("Configured", new QVariant<>(data.Configured));
         map.data.put("LoginEnabled", new QVariant<>(data.LoginEnabled));
         map.data.put("StorageBackends", new QVariant<>(storageBackends));
@@ -40,7 +49,7 @@ public class ClientInitAckSerializer implements ObjectSerializer<ClientInitAck>
     public ClientInitAck fromLegacy(Map<String, QVariant> map) {
         final List<StorageBackend> storageBackends = new ArrayList<StorageBackend>();
         if (map.containsKey("StorageBackends")) {
-            final StorageBackendSerializer storageBackendSerializer = new StorageBackendSerializer();
+            final StorageBackendSerializer storageBackendSerializer = StorageBackendSerializer.get();
             for (Map<String, QVariant> backend : (List<Map<String, QVariant>>) map.get("StorageBackends").data) {
                 storageBackends.add(storageBackendSerializer.fromLegacy(backend));
             }
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitRejectSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitRejectSerializer.java
index ec20c44a5faa7f9343a74938fbc994f109fd0b02..d026936649994cc34c322577b2969a4402b872b2 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitRejectSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitRejectSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.ClientInitReject;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientInitRejectSerializer implements ObjectSerializer<ClientInitReject> {
+    private static final ClientInitRejectSerializer serializer = new ClientInitRejectSerializer();
+
+    private ClientInitRejectSerializer() {
+    }
+
+    public static ClientInitRejectSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientInitReject data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("Error", new QVariant<>(data.Error));
         return map;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitSerializer.java
index 6f48f7e59ced601383af4c55e41c9cafbaab7d15..24dd5178251182ac2545a176d3b726d660490563 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientInitSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.ClientInit;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientInitSerializer implements ObjectSerializer<ClientInit> {
+    private static final ClientInitSerializer serializer = new ClientInitSerializer();
+
+    private ClientInitSerializer() {
+    }
+
+    public static ClientInitSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientInit data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("ClientDate", new QVariant<>(data.ClientDate));
         map.data.put("UseSsl", new QVariant<>(data.UseSsl));
         map.data.put("ClientVersion", new QVariant<>(data.ClientVersion));
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginAckSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginAckSerializer.java
index ab877f95435f8580c98e1e3944f1de5e7a62ceac..f8bbcde710a861e6cd58d9bdef13eacd3eb5e547 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginAckSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginAckSerializer.java
@@ -10,10 +10,18 @@ import de.kuschku.libquassel.objects.types.ClientLoginAck;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientLoginAckSerializer implements ObjectSerializer<ClientLoginAck> {
+    private static final ClientLoginAckSerializer serializer = new ClientLoginAckSerializer();
+
+    private ClientLoginAckSerializer() {
+    }
+
+    public static ClientLoginAckSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientLoginAck data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
-        return map;
+        return new QVariant<>(new HashMap<>());
     }
 
     @Override
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginRejectSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginRejectSerializer.java
index d1af697c98b43a7b6b98179c0ab67ffb244edbf7..3426462b27e33e24ab4fb37759f596919930e89a 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginRejectSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginRejectSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.ClientLoginReject;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientLoginRejectSerializer implements ObjectSerializer<ClientLoginReject> {
+    private static final ClientLoginRejectSerializer serializer = new ClientLoginRejectSerializer();
+
+    private ClientLoginRejectSerializer() {
+    }
+
+    public static ClientLoginRejectSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientLoginReject data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("Error", new QVariant<>(data.Error));
         return map;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginSerializer.java
index 7daf5ba6ab236afdade8f9fc4981493c5a06b195..05d347ca10118fba677d38e702d3d3cc4f74ae46 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/ClientLoginSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.ClientLogin;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ClientLoginSerializer implements ObjectSerializer<ClientLogin> {
+    private static final ClientLoginSerializer serializer = new ClientLoginSerializer();
+
+    private ClientLoginSerializer() {
+    }
+
+    public static ClientLoginSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final ClientLogin data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("User", new QVariant<>(data.User));
         map.data.put("Password", new QVariant<>(data.Password));
         return map;
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupAckSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupAckSerializer.java
index 0c70b1e85651022afb04345654b73c208b0810d5..dce2df1dee80fac136ab8d82f677907388925b2a 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupAckSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupAckSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.CoreSetupAck;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class CoreSetupAckSerializer implements ObjectSerializer<CoreSetupAck> {
+    private static final CoreSetupAckSerializer serializer = new CoreSetupAckSerializer();
+
+    private CoreSetupAckSerializer() {
+    }
+
+    public static CoreSetupAckSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final CoreSetupAck data) {
-        return new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        return new QVariant<>(new HashMap<>());
     }
 
     @Override
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupDataSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupDataSerializer.java
index 90ef2db828a0c2d5e29296a90c254c545364e6cb..ba140d3576b12d106089d995bca0e19f9d3bd16a 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupDataSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupDataSerializer.java
@@ -10,10 +10,19 @@ import de.kuschku.libquassel.objects.types.CoreSetupData;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class CoreSetupDataSerializer implements ObjectSerializer<CoreSetupData> {
+    private static final CoreSetupDataSerializer serializer = new CoreSetupDataSerializer();
+
+    private CoreSetupDataSerializer() {
+    }
+
+    public static CoreSetupDataSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final CoreSetupData data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
-        map.data.put("SetupData", new SetupDataInitializer().toVariantMap(data.SetupData));
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
+        map.data.put("SetupData", SetupDataInitializer.get().toVariantMap(data.SetupData));
         return map;
     }
 
@@ -25,7 +34,7 @@ public class CoreSetupDataSerializer implements ObjectSerializer<CoreSetupData>
     @Override
     public CoreSetupData fromLegacy(Map<String, QVariant> map) {
         return new CoreSetupData(
-                new SetupDataInitializer().fromLegacy((Map<String, QVariant>) map.get("SetupData").data)
+                SetupDataInitializer.get().fromLegacy((Map<String, QVariant>) map.get("SetupData").data)
         );
     }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupRejectSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupRejectSerializer.java
index c38d1ec84ac0db5621fd733fa4dbf15c8fc01456..864b91fefcf9bf3a3cc3b5e3e447fcec69955808 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupRejectSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/CoreSetupRejectSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.CoreSetupReject;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class CoreSetupRejectSerializer implements ObjectSerializer<CoreSetupReject> {
+    private static final CoreSetupRejectSerializer serializer = new CoreSetupRejectSerializer();
+
+    private CoreSetupRejectSerializer() {
+    }
+
+    public static CoreSetupRejectSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final CoreSetupReject data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("Error", new QVariant<>(data.Error));
         return map;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/NetworkServerSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/NetworkServerSerializer.java
index 79e2e1cfb8fbd67adf4f67e4fa2e17517e769efa..501499a0daac1b241600154e13135736d7dba408 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/NetworkServerSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/NetworkServerSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.NetworkServer;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class NetworkServerSerializer implements ObjectSerializer<NetworkServer> {
+    private static final NetworkServerSerializer serializer = new NetworkServerSerializer();
+
+    private NetworkServerSerializer() {
+    }
+
+    public static NetworkServerSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(NetworkServer data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("UseSSL", new QVariant<>(data.UseSSL));
         map.data.put("sslVersion", new QVariant<>(data.sslVersion));
         map.data.put("Host", new QVariant<>(data.Host));
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionInitSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionInitSerializer.java
index 62819608edbf0578c1215a4347a26d80421174be..be0d63808172cdf6eca6e388aeee2e2e76735c42 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionInitSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionInitSerializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.SessionInit;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class SessionInitSerializer implements ObjectSerializer<SessionInit> {
+    private static final SessionInitSerializer serializer = new SessionInitSerializer();
+
+    private SessionInitSerializer() {
+    }
+
+    public static SessionInitSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final SessionInit data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("SessionState", new SessionStateSerializer().toVariantMap(data.SessionState));
         return map;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SetupDataInitializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SetupDataInitializer.java
index 25abecbf3f6844b8fe2da9724f3e01e2d977d91c..a8b83a50591d467469a67d7cd2eaf67d434033ef 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SetupDataInitializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SetupDataInitializer.java
@@ -10,9 +10,18 @@ import de.kuschku.libquassel.objects.types.SetupData;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class SetupDataInitializer implements ObjectSerializer<SetupData> {
+    private static final SetupDataInitializer serializer = new SetupDataInitializer();
+
+    private SetupDataInitializer() {
+    }
+
+    public static SetupDataInitializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final SetupData data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("AdminPasswd", new QVariant<>(data.AdminPasswd));
         map.data.put("AdminUser", new QVariant<>(data.AdminUser));
         map.data.put("Backend", new QVariant<>(data.Backend));
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/StorageBackendSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/StorageBackendSerializer.java
index f1a8e169b2ca7cb2c05af6ab6fc347fd70ae62a1..98e82cf39c3c39e520d848795333aace6fd173fa 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/StorageBackendSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/StorageBackendSerializer.java
@@ -11,9 +11,18 @@ import de.kuschku.libquassel.objects.types.StorageBackend;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class StorageBackendSerializer implements ObjectSerializer<StorageBackend> {
+    private static final StorageBackendSerializer serializer = new StorageBackendSerializer();
+
+    private StorageBackendSerializer() {
+    }
+
+    public static StorageBackendSerializer get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(final StorageBackend data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         map.data.put("DisplayName", new QVariant<>(data.DisplayName));
         map.data.put("SetupDefaults", new QVariant<>(data.SetupDefaults));
         map.data.put("Description", new QVariant<>(data.Description));
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
index 4bb47dfbb12fa397f843027bf795119df918d766..32816648d438725dfb8c7f739f8e6e3ae371ae8d 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
@@ -9,9 +9,18 @@ import de.kuschku.libquassel.functions.types.UnpackedFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class StringObjectMapSerializer<T> implements ObjectSerializer<Map<String, T>> {
+    private static final StringObjectMapSerializer serializer = new StringObjectMapSerializer();
+
+    private StringObjectMapSerializer() {
+    }
+
+    public static <T> StringObjectMapSerializer<T> get() {
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(Map<String, T> data) {
-        final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
+        final QVariant<Map<String, QVariant>> map = new QVariant<>(new HashMap<>());
         for (Map.Entry<String, T> entry : data.entrySet()) {
             map.data.put(entry.getKey(), new QVariant<>(entry.getValue()));
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java b/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java
index 27c87e26da259da1d049ebf9ebe36ff9824abba7..3ee192ccfcb2a17fb08272026dd12e4c378b6044 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java
@@ -43,35 +43,35 @@ public class QMetaTypeRegistry {
     private static final Map<String, QMetaType> stringSerializerMap = new HashMap<>();
 
     static {
-        addType(Void.class, QMetaType.Type.Void, new VoidSerializer());
-        addType(boolean.class, QMetaType.Type.Bool, new BoolSerializer());
-        addType(int.class, QMetaType.Type.Int, new IntSerializer());
-        addType(int.class, QMetaType.Type.UserType, "BufferId", new IntSerializer());
-        addType(int.class, QMetaType.Type.UserType, "NetworkId", new IntSerializer());
-        addType(int.class, QMetaType.Type.UserType, "IdentityId", new IntSerializer());
-        addType(int.class, QMetaType.Type.UserType, "MsgId", new IntSerializer());
-        addType(BufferInfo.class, QMetaType.Type.UserType, "BufferInfo", new BufferInfoSerializer());
-        addType(Message.class, QMetaType.Type.UserType, "Message", new MessageSerializer());
-        addType(Identity.class, QMetaType.Type.UserType, "Identity", new UserTypeSerializer<>(new IdentitySerializer()));
-        addType(NetworkServer.class, QMetaType.Type.UserType, "Network::Server", new UserTypeSerializer<>(new NetworkServerSerializer()));
-        addType(int.class, QMetaType.Type.UInt, new IntSerializer());
-        addType(short.class, QMetaType.Type.UShort, new ShortSerializer());
+        addType(Void.class, QMetaType.Type.Void, VoidSerializer.get());
+        addType(boolean.class, QMetaType.Type.Bool, BoolSerializer.get());
+        addType(int.class, QMetaType.Type.Int, IntSerializer.get());
+        addType(int.class, QMetaType.Type.UserType, "BufferId", IntSerializer.get());
+        addType(int.class, QMetaType.Type.UserType, "NetworkId", IntSerializer.get());
+        addType(int.class, QMetaType.Type.UserType, "IdentityId", IntSerializer.get());
+        addType(int.class, QMetaType.Type.UserType, "MsgId", IntSerializer.get());
+        addType(BufferInfo.class, QMetaType.Type.UserType, "BufferInfo", BufferInfoSerializer.get());
+        addType(Message.class, QMetaType.Type.UserType, "Message", MessageSerializer.get());
+        addType(Identity.class, QMetaType.Type.UserType, "Identity", new UserTypeSerializer<>(IdentitySerializer.get()));
+        addType(NetworkServer.class, QMetaType.Type.UserType, "Network::Server", new UserTypeSerializer<>(NetworkServerSerializer.get()));
+        addType(int.class, QMetaType.Type.UInt, IntSerializer.get());
+        addType(short.class, QMetaType.Type.UShort, ShortSerializer.get());
 
         // TODO: Implement more custom quassel types
 
-        addType(DateTime.class, QMetaType.Type.QTime, new TimeSerializer());
+        addType(DateTime.class, QMetaType.Type.QTime, TimeSerializer.get());
         addType(BigDecimal.class, QMetaType.Type.LongLong);
         addType(BigDecimal.class, QMetaType.Type.ULongLong);
         addType(double.class, QMetaType.Type.Double);
-        addType(char.class, QMetaType.Type.QChar, new CharSerializer());
-        addType(List.class, QMetaType.Type.QVariantList, new VariantListSerializer());
-        addType(Map.class, QMetaType.Type.QVariantMap, new VariantMapSerializer());
-        addType(List.class, QMetaType.Type.QStringList, new StringListSerializer());
-        addType(String.class, QMetaType.Type.QString, new StringSerializer());
-        addType(String.class, QMetaType.Type.QByteArray, new ByteArraySerializer());
+        addType(char.class, QMetaType.Type.QChar, CharSerializer.get());
+        addType(List.class, QMetaType.Type.QVariantList, VariantListSerializer.get());
+        addType(Map.class, QMetaType.Type.QVariantMap, VariantMapSerializer.get());
+        addType(List.class, QMetaType.Type.QStringList, StringListSerializer.get());
+        addType(String.class, QMetaType.Type.QString, StringSerializer.get());
+        addType(String.class, QMetaType.Type.QByteArray, ByteArraySerializer.get());
         addType(void.class, QMetaType.Type.QBitArray);
         addType(void.class, QMetaType.Type.QDate);
-        addType(DateTime.class, QMetaType.Type.QDateTime, new DateTimeSerializer());
+        addType(DateTime.class, QMetaType.Type.QDateTime, DateTimeSerializer.get());
         addType(void.class, QMetaType.Type.QUrl);
         addType(void.class, QMetaType.Type.QLocale);
         addType(void.class, QMetaType.Type.QRect);
@@ -113,15 +113,15 @@ public class QMetaTypeRegistry {
         addType(void.class, QMetaType.Type.QQuaternion);
 
         addType(void.class, QMetaType.Type.VoidStar, "void*");
-        addType(long.class, QMetaType.Type.Long, new LongSerializer());
-        addType(short.class, QMetaType.Type.Short, new ShortSerializer());
-        addType(byte.class, QMetaType.Type.Char, new ByteSerializer());
-        addType(long.class, QMetaType.Type.ULong, new LongSerializer());
-        addType(byte.class, QMetaType.Type.UChar, new ByteSerializer());
+        addType(long.class, QMetaType.Type.Long, LongSerializer.get());
+        addType(short.class, QMetaType.Type.Short, ShortSerializer.get());
+        addType(byte.class, QMetaType.Type.Char, ByteSerializer.get());
+        addType(long.class, QMetaType.Type.ULong, LongSerializer.get());
+        addType(byte.class, QMetaType.Type.UChar, ByteSerializer.get());
         addType(void.class, QMetaType.Type.Float);
         addType(void.class, QMetaType.Type.QObjectStar, "QObject*");
         addType(void.class, QMetaType.Type.QWidgetStar, "QWidget*");
-        addType(QVariant.class, QMetaType.Type.QVariant, new VariantSerializer());
+        addType(QVariant.class, QMetaType.Type.QVariant, VariantSerializer.get());
     }
 
     // Disable Constructor
@@ -158,11 +158,11 @@ public class QMetaTypeRegistry {
     }
 
     public static <T> T deserialize(final QMetaType.Type type, final ByteBuffer buffer) throws IOException {
-        return deserialize(((QMetaType<T>) typeSerializerMap.get(type)), buffer);
+        return deserialize((getMetaTypeByType(type)), buffer);
     }
 
     public static <T> T deserialize(final String type, final ByteBuffer buffer) throws IOException {
-        return deserialize(((QMetaType<T>) stringSerializerMap.get(type)), buffer);
+        return deserialize((getMetaTypeByString(type)), buffer);
     }
 
     public static <T> T deserialize(final QMetaType<T> type, final ByteBuffer buffer) throws IOException {
@@ -191,42 +191,58 @@ public class QMetaTypeRegistry {
     }
 
     public static <T> QMetaType<T> getType(String typeName) {
-        return ((QMetaType<T>) stringSerializerMap.get(typeName));
+        return getMetaTypeByString(typeName);
+    }
+
+    private static <T> QMetaType<T> getMetaTypeByString(String typeName) {
+        QMetaType<T> result = stringSerializerMap.get(typeName);
+        if (result == null || result.serializer == null) {
+            throw new RuntimeException("Unknown type: " + typeName);
+        }
+        return result;
     }
 
     public static <T> QMetaType<T> getType(QMetaType.Type type) {
-        return ((QMetaType<T>) typeSerializerMap.get(type));
+        return getMetaTypeByType(type);
+    }
+
+    private static <T> QMetaType<T> getMetaTypeByType(QMetaType.Type type) {
+        QMetaType<T> result = typeSerializerMap.get(type);
+        if (result == null || result.serializer == null) {
+            throw new RuntimeException("Unknown type: " + type.toString());
+        }
+        return result;
     }
 
     public static <T> QMetaType<T> getTypeByObject(T type) {
-        if (type instanceof Void) return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Void);
+        if (type instanceof Void) return getMetaTypeByType(QMetaType.Type.Void);
         else if (type instanceof Boolean)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Bool);
+            return getMetaTypeByType(QMetaType.Type.Bool);
         else if (type instanceof Integer)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Int);
+            return getMetaTypeByType(QMetaType.Type.Int);
         else if (type instanceof Short)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Short);
+            return getMetaTypeByType(QMetaType.Type.Short);
         else if (type instanceof DateTime)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QDateTime);
+            return getMetaTypeByType(QMetaType.Type.QDateTime);
         else if (type instanceof Character)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QChar);
+            return getMetaTypeByType(QMetaType.Type.QChar);
         else if (type instanceof List) {
             if (((List) type).size() > 0 && ((List) type).get(0) instanceof String)
-                return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QStringList);
+                return getMetaTypeByType(QMetaType.Type.QStringList);
             else if (((List) type).size() > 0 && ((List) type).get(0) instanceof QVariant)
-                return new QMetaType<>((Class) type.getClass(), QMetaType.Type.QVariantList, new VariantVariantListSerializer());
+                return new QMetaType<>((Class) type.getClass(), QMetaType.Type.QVariantList, (PrimitiveSerializer<T>) VariantVariantListSerializer.get());
             else
-                return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QVariantList);
+                return getMetaTypeByType(QMetaType.Type.QVariantList);
         } else if (type instanceof Map)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QVariantMap);
+            return getMetaTypeByType(QMetaType.Type.QVariantMap);
         else if (type instanceof String)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QString);
+            return getMetaTypeByType(QMetaType.Type.QString);
         else if (type instanceof Long)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Long);
+            return getMetaTypeByType(QMetaType.Type.Long);
         else if (type instanceof Byte)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.Char);
+            return getMetaTypeByType(QMetaType.Type.Char);
         else if (type instanceof QVariant)
-            return (QMetaType<T>) typeSerializerMap.get(QMetaType.Type.QVariant);
+            return getMetaTypeByType(QMetaType.Type.QVariant);
         else if (type instanceof Message) return stringSerializerMap.get("Message");
         else if (type instanceof BufferInfo) return stringSerializerMap.get("BufferInfo");
         else
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BoolSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BoolSerializer.java
index fb98e4c247a97ae109f161ac1f6163457a3dae09..6c0da695f71e5b9d367db9eab23f68268506e4a6 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BoolSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BoolSerializer.java
@@ -5,6 +5,12 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class BoolSerializer implements PrimitiveSerializer<Boolean> {
+    private static final BoolSerializer serializer = new BoolSerializer();
+    private BoolSerializer() {}
+    public static BoolSerializer get(){
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Boolean data) throws IOException {
         final ByteBuffer buffer = ByteBuffer.allocate(1);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BufferInfoSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BufferInfoSerializer.java
index 043ebec841467bad8f95c09067c4a14f9a4c633f..798d137885e569d7ff391557d96243f9fa5afe0d 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BufferInfoSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/BufferInfoSerializer.java
@@ -7,23 +7,29 @@ import java.nio.channels.ByteChannel;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 
 public class BufferInfoSerializer implements PrimitiveSerializer<BufferInfo> {
+    private static final BufferInfoSerializer serializer = new BufferInfoSerializer();
+    private BufferInfoSerializer() {}
+    public static BufferInfoSerializer get(){
+        return serializer;
+    }
+
     @Override
     public void serialize(ByteChannel channel, BufferInfo data) throws IOException {
-        new IntSerializer().serialize(channel, data.id);
-        new IntSerializer().serialize(channel, data.networkId);
-        new ShortSerializer().serialize(channel, data.type.id);
-        new IntSerializer().serialize(channel, data.groupId);
-        new ByteArraySerializer().serialize(channel, data.name);
+        IntSerializer.get().serialize(channel, data.id);
+        IntSerializer.get().serialize(channel, data.networkId);
+        ShortSerializer.get().serialize(channel, data.type.id);
+        IntSerializer.get().serialize(channel, data.groupId);
+        ByteArraySerializer.get().serialize(channel, data.name);
     }
 
     @Override
     public BufferInfo deserialize(final ByteBuffer buffer) throws IOException {
         return new BufferInfo(
-                new IntSerializer().deserialize(buffer),
-                new IntSerializer().deserialize(buffer),
-                BufferInfo.Type.fromId(new ShortSerializer().deserialize(buffer)),
-                new IntSerializer().deserialize(buffer),
-                new ByteArraySerializer().deserialize(buffer)
+                IntSerializer.get().deserialize(buffer),
+                IntSerializer.get().deserialize(buffer),
+                BufferInfo.Type.fromId(ShortSerializer.get().deserialize(buffer)),
+                IntSerializer.get().deserialize(buffer),
+                ByteArraySerializer.get().deserialize(buffer)
         );
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteArraySerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteArraySerializer.java
index f04bd7e30a69b391881c8057c826e3aeaf6d4e4c..e953fd5ba7d71991800cbd61f978979f6c70a986 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteArraySerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteArraySerializer.java
@@ -1,6 +1,7 @@
 package de.kuschku.libquassel.primitives.serializers;
 
 import android.support.annotation.Nullable;
+import android.util.Log;
 
 import com.google.common.base.Charsets;
 
@@ -9,23 +10,30 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class ByteArraySerializer implements PrimitiveSerializer<String> {
-    public final boolean trimLastByte;
+    private static final ByteArraySerializer serializerFalse = new ByteArraySerializer(false);
+    private static final ByteArraySerializer serializerTrue = new ByteArraySerializer(true);
 
-    public ByteArraySerializer() {
-        this(false);
+    public static ByteArraySerializer get() {
+        return get(false);
+    }
+    public static ByteArraySerializer get(boolean trimLastByte) {
+        if (trimLastByte) return serializerTrue;
+        else return serializerFalse;
     }
 
-    public ByteArraySerializer(boolean trimLastByte) {
+    public final boolean trimLastByte;
+
+    private ByteArraySerializer(boolean trimLastByte) {
         this.trimLastByte = trimLastByte;
     }
 
     @Override
     public void serialize(final ByteChannel channel, final String data) throws IOException {
         if (data == null) {
-            new IntSerializer().serialize(channel, 0xffffffff);
+            IntSerializer.get().serialize(channel, 0xffffffff);
         } else {
             final ByteBuffer contentBuffer = Charsets.ISO_8859_1.encode(data);
-            new IntSerializer().serialize(channel, contentBuffer.limit() + (trimLastByte ? 1 : 0));
+            IntSerializer.get().serialize(channel, contentBuffer.limit() + (trimLastByte ? 1 : 0));
             channel.write(contentBuffer);
             if (trimLastByte) channel.write(ByteBuffer.allocate(1));
         }
@@ -34,7 +42,7 @@ public class ByteArraySerializer implements PrimitiveSerializer<String> {
     @Nullable
     @Override
     public String deserialize(final ByteBuffer buffer) throws IOException {
-        final int len = new IntSerializer().deserialize(buffer);
+        final int len = IntSerializer.get().deserialize(buffer);
         if (len == 0xffffffff)
             return null;
         else if (len == 0)
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteSerializer.java
index 044b5776de57c32309e7d9ebb299006534fe0b90..600f790d2000021b355fba2d81e2f5bf7e7a54b2 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ByteSerializer.java
@@ -5,6 +5,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class ByteSerializer implements PrimitiveSerializer<Byte> {
+    private static final ByteSerializer serializer = new ByteSerializer();
+
+    private ByteSerializer() {
+    }
+
+    public static ByteSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Byte data) throws IOException {
         final ByteBuffer buffer = ByteBuffer.allocate(1);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/CharSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/CharSerializer.java
index 6843196f8bbe982ad09b28c236387e10e5190ec7..817343e6655987e227b497f618f60afc0b9d47b3 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/CharSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/CharSerializer.java
@@ -6,6 +6,15 @@ import java.nio.channels.ByteChannel;
 import java.nio.charset.Charset;
 
 public class CharSerializer implements PrimitiveSerializer<Character> {
+    private static final CharSerializer serializer = new CharSerializer();
+
+    private CharSerializer() {
+    }
+
+    public static CharSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Character data) throws IOException {
         final ByteBuffer buffer = Charset.forName("UTF-16BE").encode(String.valueOf(data.charValue()));
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/DateTimeSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/DateTimeSerializer.java
index 78dbd4dc78da60eb6782d1cdebeb48a9a9e7b5cf..8b4217e1746c24aa58299e9a275262cbb3a37b6f 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/DateTimeSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/DateTimeSerializer.java
@@ -9,6 +9,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class DateTimeSerializer implements PrimitiveSerializer<DateTime> {
+    private static final DateTimeSerializer serializer = new DateTimeSerializer();
+
+    private DateTimeSerializer() {
+    }
+
+    public static DateTimeSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final DateTime data) throws IOException {
         final boolean isUTC;
@@ -20,17 +29,17 @@ public class DateTimeSerializer implements PrimitiveSerializer<DateTime> {
             throw new IllegalArgumentException("Serialization of timezones except for local and UTC is not supported");
 
 
-        new IntSerializer().serialize(channel, (int) DateTimeUtils.toJulianDayNumber(data.getMillis()));
-        new IntSerializer().serialize(channel, data.getMillisOfDay());
-        new BoolSerializer().serialize(channel, isUTC);
+        IntSerializer.get().serialize(channel, (int) DateTimeUtils.toJulianDayNumber(data.getMillis()));
+        IntSerializer.get().serialize(channel, data.getMillisOfDay());
+        BoolSerializer.get().serialize(channel, isUTC);
     }
 
     @Override
     public DateTime deserialize(final ByteBuffer buffer) throws IOException {
-        final long julianDay = new IntSerializer().deserialize(buffer);
-        final int millisSinceMidnight = new IntSerializer().deserialize(buffer);
+        final long julianDay = IntSerializer.get().deserialize(buffer);
+        final int millisSinceMidnight = IntSerializer.get().deserialize(buffer);
 
-        final short zone = new ByteSerializer().deserialize(buffer);
+        final short zone = ByteSerializer.get().deserialize(buffer);
 
         if (millisSinceMidnight == 0x73007300 && julianDay == 0x50006100 || millisSinceMidnight == -1 || julianDay == -1)
             return new DateTime(0);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/IntSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/IntSerializer.java
index 88e60c9127a5519c7c54cd456f893711ddf97846..6499f2241d7babf8c62e152d3e87b01244118d70 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/IntSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/IntSerializer.java
@@ -5,6 +5,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class IntSerializer implements PrimitiveSerializer<Integer> {
+    private static final IntSerializer serializer = new IntSerializer();
+
+    private IntSerializer() {
+    }
+
+    public static IntSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Integer data) throws IOException {
         final ByteBuffer buffer = ByteBuffer.allocate(4);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/LongSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/LongSerializer.java
index ae329f8b496f0ef31e292494f2234e78b43cbfdd..2776cb185ab93000c0d7a213e68aea89243a2020 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/LongSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/LongSerializer.java
@@ -5,6 +5,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class LongSerializer implements PrimitiveSerializer<Long> {
+    private static final LongSerializer serializer = new LongSerializer();
+
+    private LongSerializer() {
+    }
+
+    public static LongSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Long data) throws IOException {
         final ByteBuffer buffer = ByteBuffer.allocate(8);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
index 6dffba06775c03781cffe2da951c1dd858b6c379..1a7cf26a32c58a2d22bce9a47536736ea2dc0c52 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
@@ -9,27 +9,36 @@ import java.nio.channels.ByteChannel;
 import de.kuschku.libquassel.message.Message;
 
 public class MessageSerializer implements PrimitiveSerializer<Message> {
+    private static final MessageSerializer serializer = new MessageSerializer();
+
+    private MessageSerializer() {
+    }
+
+    public static MessageSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(ByteChannel channel, Message data) throws IOException {
-        new IntSerializer().serialize(channel, data.messageId);
-        new IntSerializer().serialize(channel, (int) (data.time.getMillis() / 1000));
-        new IntSerializer().serialize(channel, data.type.value);
-        new ByteSerializer().serialize(channel, data.flags.flags);
-        new BufferInfoSerializer().serialize(channel, data.bufferInfo);
-        new ByteArraySerializer().serialize(channel, data.sender);
-        new ByteArraySerializer().serialize(channel, data.content);
+        IntSerializer.get().serialize(channel, data.messageId);
+        IntSerializer.get().serialize(channel, (int) (data.time.getMillis() / 1000));
+        IntSerializer.get().serialize(channel, data.type.value);
+        ByteSerializer.get().serialize(channel, data.flags.flags);
+        BufferInfoSerializer.get().serialize(channel, data.bufferInfo);
+        ByteArraySerializer.get().serialize(channel, data.sender);
+        ByteArraySerializer.get().serialize(channel, data.content);
     }
 
     @Override
     public Message deserialize(final ByteBuffer buffer) throws IOException {
         return new Message(
-                new IntSerializer().deserialize(buffer),
-                new DateTime(((long) new IntSerializer().deserialize(buffer)) * 1000),
-                Message.Type.fromId(new IntSerializer().deserialize(buffer)),
-                new Message.Flags(new ByteSerializer().deserialize(buffer)),
-                new BufferInfoSerializer().deserialize(buffer),
-                new ByteArraySerializer().deserialize(buffer),
-                new ByteArraySerializer().deserialize(buffer)
+                IntSerializer.get().deserialize(buffer),
+                new DateTime(((long) IntSerializer.get().deserialize(buffer)) * 1000),
+                Message.Type.fromId(IntSerializer.get().deserialize(buffer)),
+                new Message.Flags(ByteSerializer.get().deserialize(buffer)),
+                BufferInfoSerializer.get().deserialize(buffer),
+                ByteArraySerializer.get().deserialize(buffer),
+                ByteArraySerializer.get().deserialize(buffer)
         );
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ProtocolSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ProtocolSerializer.java
index c7e667ec603a5b7c6ba6bf61a23f00b524c12675..63a8adead28e075c757401bae7a7d162be8c0cb3 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ProtocolSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ProtocolSerializer.java
@@ -8,19 +8,28 @@ import de.kuschku.libquassel.ClientData;
 import de.kuschku.libquassel.primitives.types.Protocol;
 
 public class ProtocolSerializer implements PrimitiveSerializer<Protocol> {
+    private static final ProtocolSerializer serializer = new ProtocolSerializer();
+
+    private ProtocolSerializer() {
+    }
+
+    public static ProtocolSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(ByteChannel channel, Protocol data) throws IOException {
-        new ByteSerializer().serialize(channel, data.protocolFlags.flags);
-        new ShortSerializer().serialize(channel, data.protocolData);
-        new ByteSerializer().serialize(channel, data.protocolVersion);
+        ByteSerializer.get().serialize(channel, data.protocolFlags.flags);
+        ShortSerializer.get().serialize(channel, data.protocolData);
+        ByteSerializer.get().serialize(channel, data.protocolVersion);
     }
 
     @Override
     public Protocol deserialize(ByteBuffer buffer) throws IOException {
         return new Protocol(
-                new ClientData.FeatureFlags(new ByteSerializer().deserialize(buffer)),
-                new ShortSerializer().deserialize(buffer),
-                new ByteSerializer().deserialize(buffer)
+                new ClientData.FeatureFlags(ByteSerializer.get().deserialize(buffer)),
+                ShortSerializer.get().deserialize(buffer),
+                ByteSerializer.get().deserialize(buffer)
         );
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ShortSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ShortSerializer.java
index c9ff182e7b968bff1e5a054d6ad5ded30ed83d22..207b82e5fc217c5e33b3f47b28383ed7f1c49673 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ShortSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/ShortSerializer.java
@@ -5,6 +5,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class ShortSerializer implements PrimitiveSerializer<Short> {
+    private static final ShortSerializer serializer = new ShortSerializer();
+
+    private ShortSerializer() {
+    }
+
+    public static ShortSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Short data) throws IOException {
         final ByteBuffer buffer = ByteBuffer.allocate(2);
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringListSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringListSerializer.java
index 271be194351b0e8abbe21caad2329f2ed9e6ce1e..6ea6cd5fa5d36069e5e11fb12e8e446e49660d2e 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringListSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringListSerializer.java
@@ -7,20 +7,29 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class StringListSerializer implements PrimitiveSerializer<List<String>> {
+    private static final StringListSerializer serializer = new StringListSerializer();
+
+    private StringListSerializer() {
+    }
+
+    public static StringListSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final List<String> data) throws IOException {
-        new IntSerializer().serialize(channel, data.size());
+        IntSerializer.get().serialize(channel, data.size());
         for (String element : data) {
-            new StringSerializer().serialize(channel, element);
+            StringSerializer.get().serialize(channel, element);
         }
     }
 
     @Override
     public List<String> deserialize(final ByteBuffer buffer) throws IOException {
-        final int size = new IntSerializer().deserialize(buffer);
+        final int size = IntSerializer.get().deserialize(buffer);
         final List<String> list = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {
-            list.add(new StringSerializer().deserialize(buffer));
+            list.add(StringSerializer.get().deserialize(buffer));
         }
         return list;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringSerializer.java
index 087b6eaac40f6bc54bbbb698d784f846604039b9..5c7199f622ef8fd76faf92ea54d0e25739eda243 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/StringSerializer.java
@@ -3,6 +3,7 @@ package de.kuschku.libquassel.primitives.serializers;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.util.Log;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -10,13 +11,22 @@ import java.nio.channels.ByteChannel;
 import java.nio.charset.Charset;
 
 public class StringSerializer implements PrimitiveSerializer<String> {
+    private static final StringSerializer serializer = new StringSerializer();
+
+    private StringSerializer() {
+    }
+
+    public static StringSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(@NonNull final ByteChannel channel, @Nullable final String data) throws IOException {
         if (data == null) {
-            new IntSerializer().serialize(channel, 0xffffffff);
+            IntSerializer.get().serialize(channel, 0xffffffff);
         } else {
             final ByteBuffer contentBuffer = Charset.forName("UTF-16BE").encode(data);
-            new IntSerializer().serialize(channel, contentBuffer.limit());
+            IntSerializer.get().serialize(channel, contentBuffer.limit());
             channel.write(contentBuffer);
         }
     }
@@ -24,7 +34,7 @@ public class StringSerializer implements PrimitiveSerializer<String> {
     @Nullable
     @Override
     public String deserialize(final ByteBuffer buffer) throws IOException {
-        final int len = new IntSerializer().deserialize(buffer);
+        final int len = IntSerializer.get().deserialize(buffer);
         if (len == 0xffffffff)
             return null;
         else {
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/TimeSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/TimeSerializer.java
index 7dcb9294110d66c0d8753ec0672f6c75d9d2b8ed..20dea6276a160aba0d96ab77bbdca25daae511e2 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/TimeSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/TimeSerializer.java
@@ -7,13 +7,22 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class TimeSerializer implements PrimitiveSerializer<DateTime> {
+    private static final TimeSerializer serializer = new TimeSerializer();
+
+    private TimeSerializer() {
+    }
+
+    public static TimeSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final DateTime data) throws IOException {
-        new IntSerializer().serialize(channel, data.millisOfDay().get());
+        IntSerializer.get().serialize(channel, data.millisOfDay().get());
     }
 
     @Override
     public DateTime deserialize(final ByteBuffer buffer) throws IOException {
-        return DateTime.now().millisOfDay().setCopy(new IntSerializer().deserialize(buffer));
+        return DateTime.now().millisOfDay().setCopy(IntSerializer.get().deserialize(buffer));
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/UserTypeSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/UserTypeSerializer.java
index 9532e6f8fd62184aed024389c9f35c4c9c9169b6..dddf88442d5ac74dead6ef9bbf93469e7dd23321 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/UserTypeSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/UserTypeSerializer.java
@@ -17,11 +17,11 @@ public class UserTypeSerializer<T> implements PrimitiveSerializer<T> {
 
     @Override
     public void serialize(ByteChannel channel, T data) throws IOException {
-        new VariantSerializer<Map<String, QVariant>>().serialize(channel, objectSerializer.toVariantMap(data));
+        VariantSerializer.<Map<String, QVariant>>get().serialize(channel, objectSerializer.toVariantMap(data));
     }
 
     @Override
     public T deserialize(ByteBuffer buffer) throws IOException {
-        return (T) objectSerializer.fromLegacy(new VariantMapSerializer().deserialize(buffer));
+        return (T) objectSerializer.fromLegacy(((VariantMapSerializer) VariantMapSerializer.get()).deserialize(buffer));
     }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantListSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantListSerializer.java
index cad66b2167bf1f52eabcd3e34f46371a3be1ca6c..25195552c7b1c02bb31738805159fc16e8737906 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantListSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantListSerializer.java
@@ -9,11 +9,20 @@ import java.util.List;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class VariantListSerializer<T> implements PrimitiveSerializer<List<T>> {
+    private static final VariantListSerializer serializer = new VariantListSerializer();
+
+    private VariantListSerializer() {
+    }
+
+    public static <T> VariantListSerializer<T> get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final List<T> data) throws IOException {
-        new IntSerializer().serialize(channel, data.size());
+        IntSerializer.get().serialize(channel, data.size());
 
-        final VariantSerializer<T> variantSerializer = new VariantSerializer<>();
+        final VariantSerializer<T> variantSerializer = VariantSerializer.get();
         for (T element : data) {
             variantSerializer.serialize(channel, new QVariant<>(element));
         }
@@ -21,10 +30,10 @@ public class VariantListSerializer<T> implements PrimitiveSerializer<List<T>> {
 
     @Override
     public List<T> deserialize(final ByteBuffer buffer) throws IOException {
-        final int length = new IntSerializer().deserialize(buffer);
+        final int length = IntSerializer.get().deserialize(buffer);
         final List<T> list = new ArrayList<>(length);
 
-        final VariantSerializer<T> variantSerializer = new VariantSerializer<>();
+        final VariantSerializer<T> variantSerializer = VariantSerializer.get();
         for (int i = 0; i < length; i++) {
             list.add(variantSerializer.deserialize(buffer).data);
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantMapSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantMapSerializer.java
index 1a86121b5777f2dbd3f45c53b7e52b5188439dd7..cea4e0b713de237064336ffb9b6badef39dd720f 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantMapSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantMapSerializer.java
@@ -9,13 +9,21 @@ import java.util.Map;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class VariantMapSerializer<T> implements PrimitiveSerializer<Map<String, QVariant<T>>> {
+    private static final VariantMapSerializer serializer = new VariantMapSerializer();
 
-    private PrimitiveSerializer<String> stringSerializer = new StringSerializer();
-    private VariantSerializer<T> variantSerializer = new VariantSerializer<>();
+    private VariantMapSerializer() {
+    }
+
+    public static <T> VariantMapSerializer<T> get() {
+        return serializer;
+    }
+
+    private PrimitiveSerializer<String> stringSerializer = StringSerializer.get();
+    private VariantSerializer<T> variantSerializer = VariantSerializer.get();
 
     @Override
     public void serialize(final ByteChannel channel, final Map<String, QVariant<T>> data) throws IOException {
-        new IntSerializer().serialize(channel, data.size());
+        IntSerializer.get().serialize(channel, data.size());
 
         for (Map.Entry<String, QVariant<T>> element : data.entrySet()) {
             stringSerializer.serialize(channel, element.getKey());
@@ -25,7 +33,7 @@ public class VariantMapSerializer<T> implements PrimitiveSerializer<Map<String,
 
     @Override
     public Map<String, QVariant<T>> deserialize(final ByteBuffer buffer) throws IOException {
-        final int length = new IntSerializer().deserialize(buffer);
+        final int length = IntSerializer.get().deserialize(buffer);
         final Map<String, QVariant<T>> map = new HashMap<>(length);
 
         for (int i = 0; i < length; i++) {
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantSerializer.java
index 92666dadb77b11dfcebe7b826173c1351585d417..b8ff42371ca194ffef8c067c3938da275b3b9304 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantSerializer.java
@@ -9,12 +9,24 @@ import de.kuschku.libquassel.primitives.QMetaTypeRegistry;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class VariantSerializer<T> implements PrimitiveSerializer<QVariant<T>> {
+    private static final VariantSerializer serializer = new VariantSerializer();
+
+    private VariantSerializer() {
+    }
+
+    public static <T> VariantSerializer<T> get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final QVariant<T> data) throws IOException {
-        new IntSerializer().serialize(channel, data.type.type.getValue());
-        new BoolSerializer().serialize(channel, data.data == null);
+        IntSerializer.get().serialize(channel, data.type.type.getValue());
+        BoolSerializer.get().serialize(channel, data.data == null);
         if (data.type.type == QMetaType.Type.UserType) {
-            new ByteArraySerializer(true).serialize(channel, data.type.name);
+            ByteArraySerializer.get(true).serialize(channel, data.type.name);
+        }
+        if (data.type.serializer == null) {
+            throw new IOException("Unknown type: " + data.type.name);
         }
         data.type.serializer.serialize(channel, data.data);
     }
@@ -22,11 +34,11 @@ public class VariantSerializer<T> implements PrimitiveSerializer<QVariant<T>> {
     @Override
     public QVariant<T> deserialize(final ByteBuffer buffer) throws IOException {
         // Read original type
-        final QMetaType.Type type = QMetaType.Type.fromId(new IntSerializer().deserialize(buffer));
+        final QMetaType.Type type = QMetaType.Type.fromId(IntSerializer.get().deserialize(buffer));
 
         // Read if the data is defined or null
         // TODO: For some reason, this is completely ignored. Figure out why and document.
-        final boolean isNull = new BoolSerializer().deserialize(buffer);
+        final boolean isNull = BoolSerializer.get().deserialize(buffer);
 
         // Get the actual serialized type
         final QMetaType<T> mtype;
@@ -34,7 +46,7 @@ public class VariantSerializer<T> implements PrimitiveSerializer<QVariant<T>> {
             // If the type is a user-defined type, read type name
             // WARNING: This ByteArray has a trailing null byte, which we can’t deserialize.
             //          Therefore we have to pass a flag to make sure the serializer removes it.
-            final String typeName = new ByteArraySerializer(true).deserialize(buffer);
+            final String typeName = ByteArraySerializer.get(true).deserialize(buffer);
             mtype = QMetaTypeRegistry.getType(typeName);
 
             if (mtype == null || mtype.serializer == null) {
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantVariantListSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantVariantListSerializer.java
index d6c68d59633567bb431f1dd62826f7c33c1b57f3..bcfeba97cfcd274be8c60fc37058fbf4670de053 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantVariantListSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VariantVariantListSerializer.java
@@ -9,11 +9,20 @@ import java.util.List;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class VariantVariantListSerializer<T> implements PrimitiveSerializer<List<QVariant<T>>> {
+    private static final VariantVariantListSerializer serializer = new VariantVariantListSerializer();
+
+    private VariantVariantListSerializer() {
+    }
+
+    public static <T> VariantVariantListSerializer<T> get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final List<QVariant<T>> data) throws IOException {
-        new IntSerializer().serialize(channel, data.size());
+        IntSerializer.get().serialize(channel, data.size());
 
-        final VariantSerializer<T> variantSerializer = new VariantSerializer<>();
+        final VariantSerializer<T> variantSerializer = VariantSerializer.get();
         for (QVariant<T> element : data) {
             variantSerializer.serialize(channel, element);
         }
@@ -21,10 +30,10 @@ public class VariantVariantListSerializer<T> implements PrimitiveSerializer<List
 
     @Override
     public List<QVariant<T>> deserialize(final ByteBuffer buffer) throws IOException {
-        final int length = new IntSerializer().deserialize(buffer);
+        final int length = IntSerializer.get().deserialize(buffer);
         final List<QVariant<T>> list = new ArrayList<>(length);
 
-        final VariantSerializer<T> variantSerializer = new VariantSerializer<>();
+        final VariantSerializer<T> variantSerializer = VariantSerializer.get();
         for (int i = 0; i < length; i++) {
             list.add(variantSerializer.deserialize(buffer));
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VoidSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VoidSerializer.java
index 18e49c17c338bbdc123435ba109090801bfaa2e4..66161a343bac1c160d7108dad5bbe9b8f4d4cff3 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VoidSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/VoidSerializer.java
@@ -8,6 +8,15 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 public class VoidSerializer implements PrimitiveSerializer<Void> {
+    private static final VoidSerializer serializer = new VoidSerializer();
+
+    private VoidSerializer() {
+    }
+
+    public static VoidSerializer get() {
+        return serializer;
+    }
+
     @Override
     public void serialize(final ByteChannel channel, final Void data) throws IOException {
 
diff --git a/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java b/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
index 77eb1cfd17a85649ee92d9f9423ea35ea103d83b..7787c038e8ba3f7b837a25de9c909819c8c134ab 100644
--- a/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
+++ b/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
@@ -1,6 +1,7 @@
 package de.kuschku.libquassel.protocols;
 
 import android.support.annotation.NonNull;
+import android.util.Log;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
@@ -35,6 +36,7 @@ import de.kuschku.libquassel.objects.MessageTypeRegistry;
 import de.kuschku.libquassel.primitives.QMetaType;
 import de.kuschku.libquassel.primitives.serializers.IntSerializer;
 import de.kuschku.libquassel.primitives.serializers.PrimitiveSerializer;
+import de.kuschku.libquassel.primitives.serializers.VariantListSerializer;
 import de.kuschku.libquassel.primitives.serializers.VariantVariantListSerializer;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.util.niohelpers.WrappedChannel;
@@ -46,9 +48,6 @@ import de.kuschku.util.niohelpers.WrappedChannel;
  * @author Janne Koschinski
  */
 public class DatastreamPeer implements RemotePeer {
-    private final IntSerializer intSerializer = new IntSerializer();
-    private final VariantVariantListSerializer variantListSerializer = new VariantVariantListSerializer<>();
-
     private ByteBuffer buffer;
     private CoreConnection connection;
     private BusProvider busProvider;
@@ -59,8 +58,8 @@ public class DatastreamPeer implements RemotePeer {
         this.busProvider.dispatch.register(this);
     }
 
-    public static List<QVariant> mapToList(Map<String, QVariant> data) {
-        final List<QVariant> list = new ArrayList<>(data.size() * 2);
+    public static List<QVariant<Object>> mapToList(Map<String, QVariant> data) {
+        final List<QVariant<Object>> list = new ArrayList<>(data.size() * 2);
         for (Map.Entry<String, QVariant> entry : data.entrySet()) {
             list.add(new QVariant<>(QMetaType.Type.QByteArray, entry.getKey()));
             list.add(entry.getValue());
@@ -96,40 +95,40 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(SyncFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantVariantListSerializer(),
-                new UnpackedSyncFunctionSerializer().serialize(func))
-        );
+                VariantVariantListSerializer.<SyncFunction>get(),
+                UnpackedSyncFunctionSerializer.get().serialize(func)
+        ));
     }
 
     public void onEventBackgroundThread(RpcCallFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantVariantListSerializer(),
-                new UnpackedRpcCallFunctionSerializer().serialize(func))
-        );
+                VariantVariantListSerializer.<RpcCallFunction>get(),
+                UnpackedRpcCallFunctionSerializer.get().serialize(func)
+        ));
     }
 
     public void onEventBackgroundThread(InitRequestFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantVariantListSerializer(),
-                new InitRequestFunctionSerializer().serializePacked(func)
+                VariantVariantListSerializer.<InitRequestFunction>get(),
+                InitRequestFunctionSerializer.get().serializePacked(func)
         ));
     }
 
     public void onEventBackgroundThread(InitDataFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantVariantListSerializer(),
-                new InitDataFunctionSerializer().serialize(func))
-        );
+                VariantVariantListSerializer.<InitDataFunction>get(),
+                InitDataFunctionSerializer.get().serialize(func)
+        ));
     }
 
     public void onEventBackgroundThread(HandshakeFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantVariantListSerializer(),
-                DatastreamPeer.mapToList(MessageTypeRegistry.toVariantMap(func.data).data))
-        );
+                VariantVariantListSerializer.get(),
+                DatastreamPeer.mapToList(MessageTypeRegistry.toVariantMap(func.data).data)
+        ));
     }
 
-    private void handleHandshakeMessage(List<QVariant> data) {
+    private void handleHandshakeMessage(List data) {
         busProvider.handle(MessageTypeRegistry.from(DatastreamPeer.listToMap(data)));
     }
 
@@ -137,16 +136,16 @@ public class DatastreamPeer implements RemotePeer {
         final FunctionType type = FunctionType.fromId((Integer) data.remove(0).data);
         switch (type) {
             case SYNC:
-                busProvider.handle(new PackedSyncFunctionSerializer().deserialize(data));
+                busProvider.handle(PackedSyncFunctionSerializer.get().deserialize(data));
                 break;
             case RPCCALL:
-                busProvider.handle(new PackedRpcCallFunctionSerializer().deserialize(data));
+                busProvider.handle(PackedRpcCallFunctionSerializer.get().deserialize(data));
                 break;
             case INITREQUEST:
-                busProvider.handle(new InitRequestFunctionSerializer().deserialize(data));
+                busProvider.handle(InitRequestFunctionSerializer.get().deserialize(data));
                 break;
             case INITDATA:
-                busProvider.handle(new PackedInitDataFunctionSerializer().deserialize(data));
+                busProvider.handle( PackedInitDataFunctionSerializer.get().deserialize(data));
                 break;
             case HEARTBEAT:
             case HEARTBEATREPLY:
@@ -160,14 +159,15 @@ public class DatastreamPeer implements RemotePeer {
         buffer = ByteBuffer.allocate(4);
         connection.getChannel().read(buffer);
 
-        final int size = intSerializer.deserialize(buffer);
+        final int size = IntSerializer.get().deserialize(buffer);
+
         if (size == 0) return;
 
         buffer = ByteBuffer.allocate(size);
         connection.getChannel().read(buffer);
 
         // TODO: Put this into a future with a time limit, and parallelize it.
-        final List<QVariant> data = variantListSerializer.deserialize(buffer);
+        final List data = VariantVariantListSerializer.get().deserialize(buffer);
         if (connection.getStatus() == ConnectionChangeEvent.Status.CONNECTING
                 || connection.getStatus() == ConnectionChangeEvent.Status.HANDSHAKE
                 || connection.getStatus() == ConnectionChangeEvent.Status.CORE_SETUP_REQUIRED
@@ -211,7 +211,7 @@ public class DatastreamPeer implements RemotePeer {
                 // Serialize the object into the buffer-channel
                 serializer.serialize(fakeChannel, data);
                 // Write the size of the buffer over the network
-                new IntSerializer().serialize(connection.getChannel(), out.size());
+                IntSerializer.get().serialize(connection.getChannel(), out.size());
                 // Write the content of the buffer over the network
                 connection.getChannel().write(ByteBuffer.wrap(out.toByteArray()));
                 // Flush the deflater, if existing
diff --git a/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java b/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
index 0a8d6f8a5880ef61aabcbee225a11fac10c0ac62..531384873c4e60fff16a243f78eb4bf785f9fcfb 100644
--- a/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
+++ b/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
@@ -40,9 +40,6 @@ import static de.kuschku.libquassel.primitives.QMetaType.Type.QVariantMap;
  * @author Janne Koschinski
  */
 public class LegacyPeer implements RemotePeer {
-    private final IntSerializer intSerializer = new IntSerializer();
-    private final VariantSerializer variantSerializer = new VariantSerializer();
-
     private ByteBuffer buffer;
     private CoreConnection connection;
     private BusProvider busProvider;
@@ -54,60 +51,60 @@ public class LegacyPeer implements RemotePeer {
     }
 
     public final void onEventBackgroundThread(SyncFunction func) {
-        final List serialize = new UnpackedSyncFunctionSerializer().serialize(func);
-        connection.getOutputExecutor().submit(new OutputRunnable<>(new VariantSerializer<List>(),
-                new QVariant<>(new QMetaType<List>(List.class, QMetaType.Type.QVariantList, new VariantVariantListSerializer()),
+        final List serialize = UnpackedSyncFunctionSerializer.get().serialize(func);
+        connection.getOutputExecutor().submit(new OutputRunnable<>(VariantSerializer.get(),
+                new QVariant(new QMetaType(List.class, QMetaType.Type.QVariantList, VariantVariantListSerializer.get()),
                         serialize)));
     }
 
     public void onEventBackgroundThread(RpcCallFunction func) {
-        connection.getOutputExecutor().submit(new OutputRunnable<>(new VariantSerializer<List>(),
-                new QVariant<>(new UnpackedRpcCallFunctionSerializer().serialize(func))));
+        connection.getOutputExecutor().submit(new OutputRunnable<>(VariantSerializer.get(),
+                new QVariant<>(UnpackedRpcCallFunctionSerializer.get().serialize(func))));
     }
 
     public void onEventBackgroundThread(InitRequestFunction func) {
-        connection.getOutputExecutor().submit(new OutputRunnable<>(new VariantSerializer<List>(),
-                new QVariant<>(new InitRequestFunctionSerializer().serialize(func))));
+        connection.getOutputExecutor().submit(new OutputRunnable<>(VariantSerializer.get(),
+                new QVariant<>(InitRequestFunctionSerializer.get().serialize(func))));
     }
 
     public void onEventBackgroundThread(InitDataFunction func) {
-        connection.getOutputExecutor().submit(new OutputRunnable<>(new VariantSerializer<List>(),
-                new QVariant<>(new InitDataFunctionSerializer().serialize(func))));
+        connection.getOutputExecutor().submit(new OutputRunnable<>(VariantSerializer.get(),
+                new QVariant<>(InitDataFunctionSerializer.get().serialize(func))));
     }
 
     public void onEventBackgroundThread(HandshakeFunction func) {
         connection.getOutputExecutor().submit(new OutputRunnable<>(
-                new VariantSerializer<>(), MessageTypeRegistry.toVariantMap(func.data)));
+                VariantSerializer.get(), MessageTypeRegistry.toVariantMap(func.data)));
     }
 
     public void processMessage() throws IOException {
         buffer = ByteBuffer.allocate(4);
         connection.getChannel().read(buffer);
 
-        final int size = intSerializer.deserialize(buffer);
+        final int size = IntSerializer.get().deserialize(buffer);
         if (size == 0) return;
 
         buffer = ByteBuffer.allocate(size);
         connection.getChannel().read(buffer);
 
         // TODO: Put this into a future with a time limit, and parallelize it.
-        final QVariant data = variantSerializer.deserialize(buffer);
+        final QVariant data = VariantSerializer.get().deserialize(buffer);
         if (data.type.type == QVariantMap) {
             busProvider.handle(MessageTypeRegistry.from((Map<String, QVariant>) data.data));
         } else if (data.type.type == QVariantList) {
             final FunctionType type = FunctionType.fromId((Integer) ((List<Object>) data.data).remove(0));
             switch (type) {
                 case SYNC:
-                    busProvider.handle(new UnpackedSyncFunctionSerializer().deserialize((List<QVariant>) data.data));
+                    busProvider.handle(UnpackedSyncFunctionSerializer.get().deserialize((List<QVariant>) data.data));
                     break;
                 case RPCCALL:
-                    busProvider.handle(new UnpackedRpcCallFunctionSerializer().deserialize((List<QVariant>) data.data));
+                    busProvider.handle(UnpackedRpcCallFunctionSerializer.get().deserialize((List<QVariant>) data.data));
                     break;
                 case INITREQUEST:
-                    busProvider.handle(new InitRequestFunctionSerializer().deserialize((List<QVariant>) data.data));
+                    busProvider.handle(InitRequestFunctionSerializer.get().deserialize((List<QVariant>) data.data));
                     break;
                 case INITDATA:
-                    busProvider.handle(new UnpackedInitDataFunctionSerializer().deserialize((List<QVariant>) data.data));
+                    busProvider.handle(UnpackedInitDataFunctionSerializer.get().deserialize((List<QVariant>) data.data));
                     break;
                 case HEARTBEAT:
                 case HEARTBEATREPLY:
@@ -150,7 +147,7 @@ public class LegacyPeer implements RemotePeer {
                 // Serialize the object into the buffer-channel
                 serializer.serialize(fakeChannel, data);
                 // Write the size of the buffer over the network
-                new IntSerializer().serialize(connection.getChannel(), out.size());
+                IntSerializer.get().serialize(connection.getChannel(), out.size());
                 // Write the content of the buffer over the network
                 connection.getChannel().write(ByteBuffer.wrap(out.toByteArray()));
                 // Flush the deflater, if existing
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java b/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
index fb17008e7159fe69ec8eadf123913e249316963d..f2985142bbde1d1690c151a6e36e2094c7031783 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
@@ -19,13 +19,13 @@ public class SyncableRegistry {
     private static final Map<String, ObjectSerializer<? extends SyncableObject>> map = new HashMap<>();
 
     static {
-        map.put("BufferSyncer", new BufferSyncerSerializer());
-        map.put("BufferViewConfig", new BufferViewConfigSerializer());
-        map.put("BufferViewManager", new BufferViewManagerSerializer());
-        map.put("Identity", new IdentitySerializer());
-        map.put("IrcChannel", new IrcChannelSerializer());
-        map.put("IrcUser", new IrcUserSerializer());
-        map.put("Network", new NetworkSerializer());
+        map.put("BufferSyncer", BufferSyncerSerializer.get());
+        map.put("BufferViewConfig", BufferViewConfigSerializer.get());
+        map.put("BufferViewManager", BufferViewManagerSerializer.get());
+        map.put("Identity", IdentitySerializer.get());
+        map.put("IrcChannel", IrcChannelSerializer.get());
+        map.put("IrcUser", IrcUserSerializer.get());
+        map.put("Network", NetworkSerializer.get());
     }
 
     private SyncableRegistry() {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferSyncerSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferSyncerSerializer.java
index 4fa8403f86fc14a481bb99759ef7927d667dce89..5dee487e398e08e28fd29849f55feb63ff8c9a5c 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferSyncerSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferSyncerSerializer.java
@@ -12,6 +12,12 @@ import de.kuschku.libquassel.protocols.DatastreamPeer;
 import de.kuschku.libquassel.syncables.types.BufferSyncer;
 
 public class BufferSyncerSerializer implements ObjectSerializer<BufferSyncer> {
+    private static final BufferSyncerSerializer serializer = new BufferSyncerSerializer();
+    private BufferSyncerSerializer() {}
+    public static BufferSyncerSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(BufferSyncer data) {
         // TODO: Implement this
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewConfigSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewConfigSerializer.java
index c90a5c5a90ad64d1b01e15ae36b2ab87ee97d3b2..6c6e91f8bfc57a528c5d9c98d28d1673f7e568e0 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewConfigSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewConfigSerializer.java
@@ -12,6 +12,12 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 
 public class BufferViewConfigSerializer implements ObjectSerializer<BufferViewConfig> {
+    private static final BufferViewConfigSerializer serializer = new BufferViewConfigSerializer();
+    private BufferViewConfigSerializer() {}
+    public static BufferViewConfigSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(BufferViewConfig data) {
         final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<>());
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewManagerSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewManagerSerializer.java
index cfc32897cf400eceb7e49d3c78376c9226a3040f..54a7b217bce78691ff99963af6cef9f5b0925e0c 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewManagerSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/BufferViewManagerSerializer.java
@@ -11,6 +11,12 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
 
 public class BufferViewManagerSerializer implements ObjectSerializer<BufferViewManager> {
+    private static final BufferViewManagerSerializer serializer = new BufferViewManagerSerializer();
+    private BufferViewManagerSerializer() {}
+    public static BufferViewManagerSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(BufferViewManager data) {
         return null;
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IdentitySerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IdentitySerializer.java
index 8af94495610bc9c82d3ed0c46dd8764b4004b87e..9e0de026ef59ce1d075f3fd9c8a3af3890953ff3 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IdentitySerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IdentitySerializer.java
@@ -12,6 +12,12 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.Identity;
 
 public class IdentitySerializer implements ObjectSerializer<Identity> {
+    private static final IdentitySerializer serializer = new IdentitySerializer();
+    private IdentitySerializer() {}
+    public static IdentitySerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(Identity data) {
         final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
index 489bcf9bbf8d8d1b64f599270cf73cbcc0d807b3..f582ef782c8c5b3267a0fcd1a39f21eb98eee949 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
@@ -12,13 +12,19 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.IrcChannel;
 
 public class IrcChannelSerializer implements ObjectSerializer<IrcChannel> {
+    private static final IrcChannelSerializer serializer = new IrcChannelSerializer();
+    private IrcChannelSerializer() {}
+    public static IrcChannelSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(IrcChannel data) {
         final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
         map.data.put("name", new QVariant<>(data.name));
         map.data.put("topic", new QVariant<>(data.topic));
         map.data.put("password", new QVariant<>(data.password));
-        map.data.put("UserModes", new StringObjectMapSerializer<String>().toVariantMap(data.UserModes));
+        map.data.put("UserModes", StringObjectMapSerializer.<String>get().toVariantMap(data.UserModes));
         map.data.put("ChanModes", new QVariant<>(data.ChanModes));
         map.data.put("encrypted", new QVariant<>(data.encrypted));
         return map;
@@ -35,7 +41,7 @@ public class IrcChannelSerializer implements ObjectSerializer<IrcChannel> {
                 (String) map.get("name").data,
                 (String) map.get("topic").data,
                 (String) map.get("password").data,
-                new StringObjectMapSerializer<String>().fromLegacy(((QVariant<Map<String, QVariant>>) map.get("UserModes")).data),
+                StringObjectMapSerializer.<String>get().fromLegacy(((QVariant<Map<String, QVariant>>) map.get("UserModes")).data),
                 (Map<String, Object>) map.get("ChanModes").data,
                 (boolean) map.get("encrypted").data
         );
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 bd32e1b35fc3a27bef94743e7374851a6424e366..bcfc2de7d218fb8ea61c6306181fe7e0b65d192b 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
@@ -14,6 +14,12 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.IrcUser;
 
 public class IrcUserSerializer implements ObjectSerializer<IrcUser> {
+    private static final IrcUserSerializer serializer = new IrcUserSerializer();
+    private IrcUserSerializer() {}
+    public static IrcUserSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(IrcUser data) {
         final QVariant<Map<String, QVariant>> map = new QVariant<Map<String, QVariant>>(new HashMap<String, QVariant>());
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 22308e1a55625d4ba65b1effd48de3977c58eb57..c41bf16da42807f3d9035933f2e81b1efdc9f068 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
@@ -19,6 +19,12 @@ import de.kuschku.libquassel.syncables.types.IrcUser;
 import de.kuschku.libquassel.syncables.types.Network;
 
 public class NetworkSerializer implements ObjectSerializer<Network> {
+    private static final NetworkSerializer serializer = new NetworkSerializer();
+    private NetworkSerializer() {}
+    public static NetworkSerializer get(){
+        return serializer;
+    }
+
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(Network data) {
         // TODO: Implement this
@@ -42,7 +48,7 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
                 channelMap,
                 userMap,
                 (List<NetworkServer>) map.get("ServerList").data,
-                (Map<String, String>) new StringObjectMapSerializer().fromLegacy((Map<String, QVariant>) map.get("Supports").data),
+                (Map<String, String>) StringObjectMapSerializer.<String>get().fromLegacy((Map<String, QVariant>) map.get("Supports").data),
                 (String) map.get("autoIdentifyPassword").data,
                 (String) map.get("autoIdentifyService").data,
                 (int) map.get("autoReconnectInterval").data,
@@ -133,11 +139,11 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
         final Map<String, QVariant<Map<String, QVariant>>> wrappedUsers = usersAndChannels.get("users").data;
         final Map<String, IrcChannel> channels = new HashMap<>(wrappedChannels.size());
         for (Map.Entry<String, QVariant<Map<String, QVariant>>> entry : wrappedChannels.entrySet()) {
-            final IrcChannel ircChannel = new IrcChannelSerializer().fromLegacy(entry.getValue().data);
+            final IrcChannel ircChannel = IrcChannelSerializer.get().fromLegacy(entry.getValue().data);
             channels.put(ircChannel.name, ircChannel);
         }
         final Map<String, IrcUser> users = new HashMap<>(wrappedUsers.size());
-        final Map<String, String> supports = (Map<String, String>) new StringObjectMapSerializer().fromLegacy((Map<String, QVariant>) map.get("Supports").data);
+        final Map<String, String> supports = (Map<String, String>) StringObjectMapSerializer.<String>get().fromLegacy((Map<String, QVariant>) map.get("Supports").data);
         Network network = new Network(
                 channels,
                 new HashMap<>(wrappedUsers.size()),
@@ -168,7 +174,7 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
                 (boolean) map.get("useSasl").data
         );
         for (Map.Entry<String, QVariant<Map<String, QVariant>>> entry : wrappedUsers.entrySet()) {
-            final IrcUser ircUser = new IrcUserSerializer().fromLegacy(entry.getValue().data);
+            final IrcUser ircUser = IrcUserSerializer.get().fromLegacy(entry.getValue().data);
             ircUser.setNetwork(network);
         }
         return network;
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
index 19b1de12c2f38cb2a9c966f4ac1a8ef8c6cefb56..79f102c7d46ca983f8325ae55717a2ab27d63ff6 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
@@ -156,4 +156,13 @@ public class BufferViewConfig extends SyncableObject {
         client.getBufferViewManager().BufferViews.put(Integer.valueOf(function.objectName), this);
         provider.sendEvent(new BufferViewManagerChangedEvent(Integer.valueOf(function.objectName), BufferViewManagerChangedEvent.Action.ADD));
     }
+
+    public void addBuffer(int bufferId, int position) {
+        BufferList.add(position, bufferId);
+    }
+
+    public void SYNC_addBuffer(int bufferId, int position) {
+        addBuffer(bufferId, position);
+        sync("addBuffer", new Object[] {bufferId, position});
+    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java
index 0551f08d6f9391cfee83d6218905bd71a7dabb36..13391ed8fe7385b522ef7318d54d161e19656085 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java
@@ -1,6 +1,8 @@
 package de.kuschku.quasseldroid_ng.ui;
 
 import android.content.Context;
+import android.graphics.Typeface;
+import android.support.annotation.ColorInt;
 import android.text.SpannableString;
 import android.text.TextUtils;
 
@@ -24,7 +26,7 @@ public class ChatMessageRenderer {
     private final IrcFormatHelper helper;
 
     private Client client;
-    private boolean fullHostmask = false;
+    private boolean fullHostmask = true;
 
     public ChatMessageRenderer(Context ctx) {
         this.themeUtil = new ThemeUtil(ctx);
@@ -37,18 +39,38 @@ public class ChatMessageRenderer {
         this.client = client;
     }
 
-    private void setColors(MessageViewHolder holder, boolean highlight) {
+    private void setColors(MessageViewHolder holder, boolean highlight, boolean isServerAction) {
         if (highlight) {
-            holder.content.setTextColor(themeUtil.colors.colorForegroundHighlight);
-            holder.time.setTextColor(themeUtil.colors.colorForegroundHighlight);
-            holder.itemView.setBackgroundColor(themeUtil.colors.colorBackgroundHighlight);
+            applyStyle(holder,
+                    themeUtil.colors.colorForegroundHighlight,
+                    Typeface.NORMAL,
+                    themeUtil.colors.colorForegroundHighlight,
+                    themeUtil.colors.colorBackgroundHighlight
+            );
+        } else if (isServerAction) {
+            applyStyle(holder,
+                    themeUtil.colors.colorForegroundSecondary,
+                    Typeface.ITALIC,
+                    themeUtil.colors.colorForegroundSecondary,
+                    themeUtil.colors.colorBackgroundSecondary
+            );
         } else {
-            holder.content.setTextColor(themeUtil.colors.colorForeground);
-            holder.time.setTextColor(themeUtil.colors.colorForegroundSecondary);
-            holder.itemView.setBackgroundColor(themeUtil.colors.transparent);
+            applyStyle(holder,
+                    themeUtil.colors.colorForeground,
+                    Typeface.NORMAL,
+                    themeUtil.colors.colorForegroundSecondary,
+                    themeUtil.colors.transparent
+            );
         }
     }
 
+    private void applyStyle(MessageViewHolder holder, @ColorInt int textColor, int style, @ColorInt int timeColor, @ColorInt int bgColor) {
+        holder.content.setTextColor(textColor);
+        holder.content.setTypeface(null, style);
+        holder.time.setTextColor(timeColor);
+        holder.itemView.setBackgroundColor(bgColor);
+    }
+
     private CharSequence formatNick(String hostmask, boolean full) {
         CharSequence formattedNick = helper.formatUserNick(IrcUserUtils.getNick(hostmask));
         if (full) {
@@ -161,61 +183,78 @@ public class ChatMessageRenderer {
     }
     
     public void onBind(MessageViewHolder holder, Message message) {
-        setColors(holder, message.flags.Highlight);
         holder.time.setText(format.print(message.time));
         switch (message.type) {
             case Plain:
+                setColors(holder, message.flags.Highlight, false);
                 onBindPlain(holder, message);
                 break;
             case Notice:
+                setColors(holder, message.flags.Highlight, false);
                 onBindNotice(holder, message);
                 break;
             case Action:
+                setColors(holder, message.flags.Highlight, false);
                 onBindAction(holder, message);
                 break;
             case Nick:
+                setColors(holder, message.flags.Highlight, true);
                 onBindNick(holder, message);
                 break;
             case Mode:
+                setColors(holder, message.flags.Highlight, true);
                 onBindMode(holder, message);
                 break;
             case Join:
+                setColors(holder, message.flags.Highlight, true);
                 onBindJoin(holder, message);
                 break;
             case Part:
+                setColors(holder, message.flags.Highlight, true);
                 onBindPart(holder, message);
                 break;
             case Quit:
+                setColors(holder, message.flags.Highlight, true);
                 onBindQuit(holder, message);
                 break;
             case Kick:
+                setColors(holder, message.flags.Highlight, true);
                 onBindKick(holder, message);
                 break;
             case Kill:
+                setColors(holder, message.flags.Highlight, true);
                 onBindKill(holder, message);
                 break;
             case Server:
+                setColors(holder, message.flags.Highlight, false);
                 onBindServer(holder, message);
                 break;
             case Info:
+                setColors(holder, message.flags.Highlight, false);
                 onBindInfo(holder, message);
                 break;
             case Error:
+                setColors(holder, message.flags.Highlight, false);
                 onBindError(holder, message);
                 break;
             case DayChange:
+                setColors(holder, message.flags.Highlight, true);
                 onBindDayChange(holder, message);
                 break;
             case Topic:
+                setColors(holder, message.flags.Highlight, true);
                 onBindTopic(holder, message);
                 break;
             case NetsplitJoin:
+                setColors(holder, message.flags.Highlight, true);
                 onBindNetsplitJoin(holder, message);
                 break;
             case NetsplitQuit:
+                setColors(holder, message.flags.Highlight, true);
                 onBindNetsplitQuit(holder, message);
                 break;
             case Invite:
+                setColors(holder, message.flags.Highlight, true);
                 onBindInvite(holder, message);
                 break;
         }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java
index 446c930005fb5e3ba256fc13bafdfe4ac74f3fa5..bb9376ec38be5014687b3d7af3e0823cb73d3d41 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java
@@ -17,11 +17,9 @@ import android.support.v7.widget.AppCompatImageButton;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.Toolbar;
-import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.EditText;
-import android.widget.ScrollView;
 import android.widget.Toast;
 
 import com.google.common.collect.Sets;
@@ -66,6 +64,7 @@ import de.kuschku.quasseldroid_ng.QuasselService;
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.util.CompatibilityUtils;
 import de.kuschku.quasseldroid_ng.util.ServerAddress;
+import de.kuschku.util.observablelists.AutoScroller;
 import de.kuschku.util.backports.Stream;
 
 public class MainActivity extends AppCompatActivity {
@@ -77,8 +76,6 @@ public class MainActivity extends AppCompatActivity {
     private static final String KEY_USER = "beta_username";
     private static final String KEY_PASS = "beta_password";
 
-    SharedPreferences pref;
-
     @Bind(R.id.toolbar)
     Toolbar toolbar;
 
@@ -104,6 +101,7 @@ public class MainActivity extends AppCompatActivity {
     AccountHeader header;
     MessageAdapter adapter;
 
+    SharedPreferences pref;
     QuasselService.LocalBinder binder;
 
     private ServiceConnection serviceConnection = new ServiceConnection() {
@@ -112,12 +110,14 @@ public class MainActivity extends AppCompatActivity {
                 MainActivity.this.binder = (QuasselService.LocalBinder) service;
                 if (binder.getBackgroundThread() != null) {
                     handler = binder.getBackgroundThread().handler;
+                    provider = binder.getBackgroundThread().provider;
+                    provider.event.register(MainActivity.this);
 
                     toolbar.setSubtitle(binder.getBackgroundThread().connection.getStatus().name());
                     if (bufferId != -1) switchBuffer(bufferId);
                     if (bufferViewId != -1) switchBufferView(bufferViewId);
 
-                    // Horrible hack to load bufferviews back, should use ObservableList
+                    // Horrible hack to load bufferviews back, should use ObservableSortedList
                     Client client = handler == null ? null : handler.getClient();
                     BufferViewManager bufferViewManager = client == null ? null : client.getBufferViewManager();
                     Map<Integer, BufferViewConfig> bufferViews = bufferViewManager == null ? null : bufferViewManager.BufferViews;
@@ -134,17 +134,20 @@ public class MainActivity extends AppCompatActivity {
     };
 
     private IProtocolHandler handler;
+    private BusProvider provider;
+
     private int bufferId;
     private int bufferViewId;
-    private BusProvider provider;
 
     private ItemAdapter<IDrawerItem> hackyHistoryAdapter = new ItemAdapter<>();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        pref = getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
 
         // TODO: ADD THEME SELECTION
         setTheme(R.style.Quassel);
+
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
@@ -152,16 +155,27 @@ public class MainActivity extends AppCompatActivity {
 
         setSupportActionBar(toolbar);
 
-        pref = getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
-        adapter = new MessageAdapter(this);
-
         // This fixes a horrible bug android has where opening the keyboard doesn’t resize the layout
         KeyboardUtil keyboardUtil = new KeyboardUtil(this, slidingLayout);
         keyboardUtil.enable();
 
+        initServive();
+
+        initDrawer(savedInstanceState);
+
+        initMessageList();
+
+        initSendCallbacks();
+
+        initMsgHistory();
+    }
+
+    private void initServive() {
         startService(new Intent(this, QuasselService.class));
         bindService(new Intent(this, QuasselService.class), serviceConnection, BIND_AUTO_CREATE);
+    }
 
+    private void initDrawer(Bundle savedInstanceState) {
         header = new AccountHeaderBuilder()
                 .withActivity(this)
                 .withHeaderBackground(R.drawable.bg)
@@ -239,14 +253,9 @@ public class MainActivity extends AppCompatActivity {
         }
 
         drawer.addStickyFooterItem(new PrimaryDrawerItem().withName("(Re-)Connect").withIcon(R.drawable.ic_server_light));
+    }
 
-        messages.setAdapter(adapter);
-        messages.setLayoutManager(new LinearLayoutManager(this));
-        swipeView.setOnRefreshListener(() -> {
-            if (handler != null) handler.getClient().getBacklogManager().requestMoreBacklog(bufferId, 20);
-            else swipeView.setRefreshing(false);
-        });
-
+    private void initSendCallbacks() {
         send.setOnClickListener(view -> {
             sendInput();
         });
@@ -256,7 +265,9 @@ public class MainActivity extends AppCompatActivity {
 
             return false;
         });
+    }
 
+    private void initMsgHistory() {
         slidingLayout.setScrollableView(msgHistory);
         FastAdapter<IDrawerItem> adapter = new FastAdapter<>();
         hackyHistoryAdapter.wrap(adapter);
@@ -264,6 +275,17 @@ public class MainActivity extends AppCompatActivity {
         msgHistory.setLayoutManager(new LinearLayoutManager(this));
     }
 
+    private void initMessageList() {
+        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true);
+        adapter = new MessageAdapter(this, new AutoScroller(messages, layoutManager));
+        messages.setAdapter(adapter);
+        messages.setLayoutManager(layoutManager);
+        swipeView.setOnRefreshListener(() -> {
+            if (handler != null) handler.getClient().getBacklogManager().requestMoreBacklog(bufferId, 20);
+            else swipeView.setRefreshing(false);
+        });
+    }
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java
index c95fac8ab873026b753a5f6185747aaff3d1a8fd..d4d0369d56f634aee18295cc98fbde8d99ea021d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java
@@ -8,14 +8,18 @@ import android.view.ViewGroup;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.quasseldroid_ng.R;
-import de.kuschku.util.ObservableList;
+import de.kuschku.util.observablelists.AutoScroller;
+import de.kuschku.util.observablelists.ObservableSortedList;
+import de.kuschku.util.observablelists.RecyclerViewAdapterCallback;
 
 public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
-    private ObservableList<Message> messageList = new ObservableList<>(Message.class);
+    private final AutoScroller scroller;
+    private ObservableSortedList<Message> messageList = new ObservableSortedList<>(Message.class);
     private ChatMessageRenderer renderer;
     private LayoutInflater inflater;
 
-    public MessageAdapter(Context ctx) {
+    public MessageAdapter(Context ctx, AutoScroller scroller) {
+        this.scroller = scroller;
         this.inflater = LayoutInflater.from(ctx);
         this.renderer = new ChatMessageRenderer(ctx);
     }
@@ -24,10 +28,10 @@ public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
         renderer.setClient(client);
     }
 
-    public void setMessageList(ObservableList<Message> messageList) {
+    public void setMessageList(ObservableSortedList<Message> messageList) {
         this.messageList.setCallback(null);
         this.messageList = messageList;
-        this.messageList.setCallback(new ObservableList.RecyclerViewAdapterCallback(this));
+        this.messageList.setCallback(new RecyclerViewAdapterCallback(this, scroller));
         notifyDataSetChanged();
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java
index 51e506ee7145f5a46bd54a9f88c07c9125d672d0..8e272224f5c55d3b7c297e6b525fc77b0947ca55 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java
@@ -1,13 +1,39 @@
 package de.kuschku.quasseldroid_ng.util;
 
 
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
 import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Parcel;
+import android.provider.Browser;
+import android.text.ParcelableSpan;
 import android.text.SpannableString;
 import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.ClickableSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.view.View;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.kuschku.quasseldroid_ng.R;
 
 public class IrcFormatHelper {
+    private static final String scheme = "(?:(?:mailto:|(?:[+.-]?\\w)+://)|www(?=\\.\\S+\\.))";
+    private static final String authority = "(?:(?:[,.;@:]?[-\\w]+)+\\.?|\\[[0-9a-f:.]+\\])(?::\\d+)?";
+    private static final String urlChars = "(?:[,.;:]*[\\w~@/?&=+$()!%#*-])";
+    private static final String urlEnd = "(?:>|[,.;:\"]*\\s|\\b|$)";
+    private static final Pattern urlPattern = Pattern.compile(String.format("\\b(%s%s(?:/%s*)?)%s", scheme, authority, urlChars, urlEnd), Pattern.CASE_INSENSITIVE);
+    private static final Pattern channelPattern = Pattern.compile("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b", Pattern.CASE_INSENSITIVE);
+
     private final ThemeUtil.Colors colors;
 
     public IrcFormatHelper(ThemeUtil.Colors colors) {
@@ -25,7 +51,71 @@ public class IrcFormatHelper {
     }
 
     public CharSequence formatIrcMessage(String message) {
+        List<FutureClickableSpan> spans = new LinkedList<>();
+
         SpannableString str = new SpannableString(message);
+        Matcher urlMatcher = urlPattern.matcher(str);
+        while (urlMatcher.find()) {
+            spans.add(new FutureClickableSpan(new CustomURLSpan(urlMatcher.toString()), urlMatcher.start(), urlMatcher.end()));
+        }
+        for (FutureClickableSpan span : spans) {
+            str.setSpan(span.span, span.start, span.end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
         return str;
     }
+
+    private static class FutureClickableSpan {
+        public final ClickableSpan span;
+        public final int start;
+        public final int end;
+
+        public FutureClickableSpan(ClickableSpan span, int start, int end) {
+            this.span = span;
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    private class CustomURLSpan extends ClickableSpan implements ParcelableSpan {
+        private final String mURL;
+
+        public CustomURLSpan(String url) {
+            mURL = url;
+        }
+
+        public CustomURLSpan(Parcel src) {
+            mURL = src.readString();
+        }
+
+        public int getSpanTypeId() {
+            return R.id.custom_url_span;
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mURL);
+        }
+
+        public String getURL() {
+            return mURL;
+        }
+
+        @Override
+        public void onClick(View widget) {
+            Log.e("TEST", "THIS IS A TEST");
+
+            Uri uri = Uri.parse(getURL());
+            Context context = widget.getContext();
+            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+            intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+            try {
+                context.startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
+            }
+        }
+    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java
index 10927d4998c72fdf55ca1338ef1b70b44a764339..514fdc1954f2d18637a9383075854da741a9d394 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java
@@ -5,116 +5,71 @@ import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.support.annotation.ColorInt;
 
+import butterknife.BindColor;
+import butterknife.ButterKnife;
 import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.annotationbind.Binder;
+import de.kuschku.util.annotationbind.Color;
 
 public class ThemeUtil {
     public final Colors colors = new Colors();
 
-    private static final int[] ATTRS_SENDER = {
-            // sender colors
-            R.attr.senderColor0,
-            R.attr.senderColor1,
-            R.attr.senderColor2,
-            R.attr.senderColor3,
-            R.attr.senderColor4,
-            R.attr.senderColor5,
-            R.attr.senderColor6,
-            R.attr.senderColor7,
-            R.attr.senderColor8,
-            R.attr.senderColor9,
-            R.attr.senderColorA,
-            R.attr.senderColorB,
-            R.attr.senderColorC,
-            R.attr.senderColorD,
-            R.attr.senderColorE,
-            R.attr.senderColorF,
-    };
-
-    private static final int[] ATTRS_MIRC = {
-            // mirc colors
-            R.attr.mircColor0,
-            R.attr.mircColor1,
-            R.attr.mircColor2,
-            R.attr.mircColor3,
-            R.attr.mircColor4,
-            R.attr.mircColor5,
-            R.attr.mircColor6,
-            R.attr.mircColor7,
-            R.attr.mircColor8,
-            R.attr.mircColor9,
-            R.attr.mircColorA,
-            R.attr.mircColorB,
-            R.attr.mircColorC,
-            R.attr.mircColorD,
-            R.attr.mircColorE,
-            R.attr.mircColorF,
-    };
-
-    private static final int[] ATTRS_GENERAL = {
-            // General UI colors
-            R.attr.colorForeground,
-            R.attr.colorForegroundHighlight,
-            R.attr.colorForegroundSecondary,
-
-            R.attr.colorBackground,
-            R.attr.colorBackgroundHighlight,
-            R.attr.colorBackgroundCard,
-    };
-
-    private static final int[] ATTRS_TINT = {
-            // Tint colors
-            R.attr.colorTintActivity,
-            R.attr.colorTintMessage,
-            R.attr.colorTintHighlight
-    };
-
     public ThemeUtil(Context ctx) {
         initColors(ctx.getTheme());
     }
 
     public void initColors(Resources.Theme theme) {
-        TypedArray arr;
-
-        arr = theme.obtainStyledAttributes(ATTRS_SENDER);
-        for (int i = 0; i < colors.senderColors.length;i++) {
-            colors.senderColors[i] = arr.getColor(i, colors.transparent);
-        }
-        arr.recycle();
-
-        arr = theme.obtainStyledAttributes(ATTRS_MIRC);
-        for (int i = 0; i < colors.senderColors.length;i++) {
-            colors.mircColors[i] = arr.getColor(i, colors.transparent);
+        try {
+            Binder.bind(colors, theme);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
         }
-        arr.recycle();
-
-        arr = theme.obtainStyledAttributes(ATTRS_GENERAL);
-        colors.colorForeground = arr.getColor(0, colors.transparent);
-        colors.colorForegroundHighlight = arr.getColor(1, colors.transparent);
-        colors.colorForegroundSecondary = arr.getColor(2, colors.transparent);
-        colors.colorBackground = arr.getColor(3, colors.transparent);
-        colors.colorBackgroundHighlight = arr.getColor(4, colors.transparent);
-        colors.colorBackgroundCard = arr.getColor(5, colors.transparent);
-        arr.recycle();
-
-        arr = theme.obtainStyledAttributes(ATTRS_TINT);
-        colors.colorTintActivity = arr.getColor(0, colors.transparent);
-        colors.colorTintMessage = arr.getColor(1, colors.transparent);
-        colors.colorTintHighlight = arr.getColor(2, colors.transparent);
-        arr.recycle();
     }
 
     public static class Colors {
-        @ColorInt public int transparent = 0x00000000;
-        @ColorInt public int[] senderColors = new int[16];
-        @ColorInt public int[] mircColors = new int[16];
-        @ColorInt public int colorForeground = 0x00000000;
-        @ColorInt public int colorForegroundHighlight = 0x00000000;
-        @ColorInt public int colorForegroundSecondary = 0x00000000;
-        @ColorInt public int colorBackground = 0x00000000;
-        @ColorInt public int colorBackgroundHighlight = 0x00000000;
-        @ColorInt public int colorBackgroundCard = 0x00000000;
-        @ColorInt public int colorTintActivity = 0x00000000;
-        @ColorInt public int colorTintMessage = 0x00000000;
-        @ColorInt public int colorTintHighlight = 0x00000000;
+        @Color(android.R.color.transparent)
+        @ColorInt public int transparent;
+
+        @Color({R.attr.senderColor0, R.attr.senderColor1, R.attr.senderColor2, R.attr.senderColor3,
+                R.attr.senderColor4, R.attr.senderColor5, R.attr.senderColor6, R.attr.senderColor7,
+                R.attr.senderColor8, R.attr.senderColor9, R.attr.senderColorA, R.attr.senderColorB,
+                R.attr.senderColorC, R.attr.senderColorD, R.attr.senderColorE, R.attr.senderColorF})
+        @ColorInt public int[] senderColors;
+
+        @Color({R.attr.mircColor0, R.attr.mircColor1, R.attr.mircColor2, R.attr.mircColor3,
+                R.attr.mircColor4, R.attr.mircColor5, R.attr.mircColor6, R.attr.mircColor7,
+                R.attr.mircColor8, R.attr.mircColor9, R.attr.mircColorA, R.attr.mircColorB,
+                R.attr.mircColorC, R.attr.mircColorD, R.attr.mircColorE, R.attr.mircColorF})
+        @ColorInt public int[] mircColors;
+
+        @Color(R.attr.colorForeground)
+        @ColorInt public int colorForeground;
+
+        @Color(R.attr.colorForegroundHighlight)
+        @ColorInt public int colorForegroundHighlight;
+
+        @Color(R.attr.colorForegroundSecondary)
+        @ColorInt public int colorForegroundSecondary;
+
+        @Color(R.attr.colorBackground)
+        @ColorInt public int colorBackground;
+
+        @Color(R.attr.colorBackgroundHighlight)
+        @ColorInt public int colorBackgroundHighlight;
+
+        @Color(R.attr.colorBackgroundSecondary)
+        @ColorInt public int colorBackgroundSecondary;
+
+        @Color(R.attr.colorBackgroundCard)
+        @ColorInt public int colorBackgroundCard;
+
+        @Color(R.attr.colorTintActivity)
+        @ColorInt public int colorTintActivity;
+
+        @Color(R.attr.colorTintMessage)
+        @ColorInt public int colorTintMessage;
+
+        @Color(R.attr.colorTintHighlight)
+        @ColorInt public int colorTintHighlight;
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/ObservableList.java b/app/src/main/java/de/kuschku/util/ObservableList.java
deleted file mode 100644
index b3c37813fc342d26a37d10c98ecb0c50b718be6a..0000000000000000000000000000000000000000
--- a/app/src/main/java/de/kuschku/util/ObservableList.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package de.kuschku.util;
-
-import android.support.v7.util.SortedList;
-import android.support.v7.widget.RecyclerView;
-
-public class ObservableList<T extends ContentComparable<T>> {
-    public final SortedList<T> list;
-    Callback internal = new Callback();
-    UICallback callback;
-    RecyclerView.Adapter x;
-
-    public ObservableList(Class<T> cl) {
-        list = new SortedList<>(cl, internal);
-    }
-
-    public ObservableList(Class<T> cl, int initialcapacity) {
-        list = new SortedList<>(cl, internal, initialcapacity);
-    }
-
-    public void setCallback(UICallback callback) {
-        this.callback = callback;
-    }
-
-    public T last() {
-        if (list.size() == 0) return null;
-
-        return list.get(list.size() - 1);
-    }
-
-    public T first() {
-        if (list.size() == 0) return null;
-
-        return list.get(0);
-    }
-
-    public interface UICallback {
-        void notifyItemInserted(int position);
-
-        void notifyItemChanged(int position);
-
-        void notifyItemRemoved(int position);
-
-        void notifyItemMoved(int from, int to);
-
-        void notifyItemRangeInserted(int position, int count);
-
-        void notifyItemRangeChanged(int position, int count);
-
-        void notifyItemRangeRemoved(int position, int count);
-    }
-
-    public static class RecyclerViewAdapterCallback implements UICallback {
-        private final RecyclerView.Adapter adapter;
-
-        public RecyclerViewAdapterCallback(RecyclerView.Adapter adapter) {
-            this.adapter = adapter;
-        }
-
-        @Override
-        public void notifyItemInserted(int position) {
-            adapter.notifyItemInserted(position);
-        }
-
-        @Override
-        public void notifyItemChanged(int position) {
-            adapter.notifyItemChanged(position);
-        }
-
-        @Override
-        public void notifyItemRemoved(int position) {
-            adapter.notifyItemRemoved(position);
-        }
-
-        @Override
-        public void notifyItemMoved(int from, int to) {
-            adapter.notifyItemMoved(from, to);
-        }
-
-        @Override
-        public void notifyItemRangeInserted(int position, int count) {
-            adapter.notifyItemRangeInserted(position, count);
-        }
-
-        @Override
-        public void notifyItemRangeChanged(int position, int count) {
-            adapter.notifyItemRangeChanged(position, count);
-        }
-
-        @Override
-        public void notifyItemRangeRemoved(int position, int count) {
-            adapter.notifyItemRangeRemoved(position, count);
-        }
-    }
-
-    class Callback extends SortedList.Callback<T> {
-        @Override
-        public int compare(T o1, T o2) {
-            return o1.compareTo(o2);
-        }
-
-        @Override
-        public void onInserted(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemInserted(position);
-                else
-                    callback.notifyItemRangeInserted(position, count);
-        }
-
-        @Override
-        public void onRemoved(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemRemoved(position);
-                else
-                    callback.notifyItemRangeRemoved(position, count);
-        }
-
-        @Override
-        public void onMoved(int fromPosition, int toPosition) {
-            if (callback != null)
-                callback.notifyItemMoved(fromPosition, toPosition);
-        }
-
-        @Override
-        public void onChanged(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemChanged(position);
-                else
-                    callback.notifyItemRangeChanged(position, count);
-        }
-
-        @Override
-        public boolean areContentsTheSame(T oldItem, T newItem) {
-            return oldItem.equalsContent(newItem);
-        }
-
-        @Override
-        public boolean areItemsTheSame(T item1, T item2) {
-            return item1.equals(item2);
-        }
-    }
-}
diff --git a/app/src/main/java/de/kuschku/util/SortedListWrapper.java b/app/src/main/java/de/kuschku/util/SortedListWrapper.java
deleted file mode 100644
index a50824ce1e6b0847d422a6119881b361f9f8f8ac..0000000000000000000000000000000000000000
--- a/app/src/main/java/de/kuschku/util/SortedListWrapper.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package de.kuschku.util;
-
-import android.support.annotation.NonNull;
-import android.support.v7.util.SortedList;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-import de.kuschku.util.backports.Stream;
-
-public class SortedListWrapper<T> implements List<T> {
-    final SortedList<T> list;
-
-    public SortedListWrapper(SortedList<T> list) {
-        this.list = list;
-    }
-
-
-    @Override
-    public void add(int location, T object) {
-        list.add(object);
-    }
-
-    @Override
-    public boolean add(T object) {
-        list.add(object);
-        return false;
-    }
-
-    @Override
-    public boolean addAll(int location, @NonNull Collection<? extends T> collection) {
-        list.addAll((Collection<T>) collection);
-        return false;
-    }
-
-    @Override
-    public boolean addAll(@NonNull Collection<? extends T> collection) {
-        list.addAll((Collection<T>) collection);
-        return false;
-    }
-
-    @Override
-    public void clear() {
-        list.clear();
-    }
-
-    @Override
-    public boolean contains(Object object) {
-        return indexOf(object) != SortedList.INVALID_POSITION;
-    }
-
-    @Override
-    public boolean containsAll(@NonNull Collection<?> collection) {
-        return new Stream<>(collection).allMatch(this::contains);
-    }
-
-    @Override
-    public T get(int location) {
-        return get(location);
-    }
-
-    @Override
-    public int indexOf(Object object) {
-        return list.indexOf((T) object);
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return list.size() == 0;
-    }
-
-    @NonNull
-    @Override
-    public Iterator<T> iterator() {
-        return null;
-    }
-
-    @Override
-    public int lastIndexOf(Object object) {
-        return indexOf(object);
-    }
-
-    @Override
-    public ListIterator<T> listIterator() {
-        return null;
-    }
-
-    @Override
-    public ListIterator<T> listIterator(int location) {
-        return null;
-    }
-
-    @Override
-    public T remove(int location) {
-        T val = get(location);
-        remove(val);
-        return val;
-    }
-
-    @Override
-    public boolean remove(Object object) {
-        return list.remove((T) object);
-    }
-
-    @Override
-    public boolean removeAll(Collection<?> collection) {
-        return new Stream<>(collection).anyMatch(this::remove);
-    }
-
-    @Override
-    public boolean retainAll(Collection<?> collection) {
-        return false;
-    }
-
-    @Override
-    public T set(int location, T object) {
-        return null;
-    }
-
-    @Override
-    public int size() {
-        return list.size();
-    }
-
-    @NonNull
-    @Override
-    public List<T> subList(int start, int end) {
-        return null;
-    }
-
-    @NonNull
-    @Override
-    public Object[] toArray() {
-        return new Object[0];
-    }
-
-    @NonNull
-    @Override
-    public <T1> T1[] toArray(T1[] array) {
-        return null;
-    }
-}
diff --git a/app/src/main/java/de/kuschku/util/annotationbind/Binder.java b/app/src/main/java/de/kuschku/util/annotationbind/Binder.java
new file mode 100644
index 0000000000000000000000000000000000000000..067dc9ca1f9b9bad9585eaaf14c547343fff7ea5
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/annotationbind/Binder.java
@@ -0,0 +1,43 @@
+package de.kuschku.util.annotationbind;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.support.annotation.ColorInt;
+
+import java.lang.reflect.Field;
+
+public class Binder {
+    private Binder() {
+
+    }
+
+    public static void bind(Object o, Resources.Theme t) throws IllegalAccessException {
+        for (Field f : o.getClass().getFields()) {
+            if (f.isAnnotationPresent(Color.class)) {
+                int[] colors = obtainColors(f.getAnnotation(Color.class).value(), t);
+                if (f.getType().isArray())
+                    f.set(o, colors);
+                else if (colors.length == 1)
+                    f.set(o, colors[0]);
+                else
+                    throw new IllegalAccessException("Field length does not correspond to argument length");
+            }
+        }
+    }
+
+    public static void bind(Object o, Context t) throws IllegalAccessException {
+        bind(o, t.getTheme());
+    }
+
+    @ColorInt
+    private static int[] obtainColors(int[] res, Resources.Theme theme) {
+        int[] result = new int[res.length];
+        TypedArray t = theme.obtainStyledAttributes(res);
+        for (int i = 0; i < res.length; i++) {
+            result[i] = t.getColor(i, 0x00000000);
+        }
+        t.recycle();
+        return result;
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/annotationbind/Color.java b/app/src/main/java/de/kuschku/util/annotationbind/Color.java
new file mode 100644
index 0000000000000000000000000000000000000000..833948443bb4fe8ff92899a8a4d167f42f84ac29
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/annotationbind/Color.java
@@ -0,0 +1,12 @@
+package de.kuschku.util.annotationbind;
+
+import android.support.annotation.AnyRes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Color {
+    @AnyRes
+    int[] value() default {};
+}
diff --git a/app/src/main/java/de/kuschku/util/observablelists/AutoScroller.java b/app/src/main/java/de/kuschku/util/observablelists/AutoScroller.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ac3aead373c38d34110243e1dcb2435ab46cd9d
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/observablelists/AutoScroller.java
@@ -0,0 +1,19 @@
+package de.kuschku.util.observablelists;
+
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+
+public class AutoScroller {
+    private final RecyclerView recyclerView;
+    private final LinearLayoutManager manager;
+
+    public AutoScroller(RecyclerView recyclerView, LinearLayoutManager manager) {
+        this.recyclerView = recyclerView;
+        this.manager = manager;
+    }
+
+    public void notifyScroll() {
+        if (manager.findFirstVisibleItemPosition() == 0)
+            manager.smoothScrollToPosition(recyclerView, null, 0);
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/ContentComparable.java b/app/src/main/java/de/kuschku/util/observablelists/ContentComparable.java
similarity index 76%
rename from app/src/main/java/de/kuschku/util/ContentComparable.java
rename to app/src/main/java/de/kuschku/util/observablelists/ContentComparable.java
index b6142b656fdc13da32b00fa2dfac914d7f136401..818c6152160a83d8457927d4101ba80496a120b8 100644
--- a/app/src/main/java/de/kuschku/util/ContentComparable.java
+++ b/app/src/main/java/de/kuschku/util/observablelists/ContentComparable.java
@@ -1,4 +1,4 @@
-package de.kuschku.util;
+package de.kuschku.util.observablelists;
 
 public interface ContentComparable<T extends ContentComparable<T>> extends Comparable<T> {
     boolean equalsContent(T other);
diff --git a/app/src/main/java/de/kuschku/util/observablelists/ObservableList.java b/app/src/main/java/de/kuschku/util/observablelists/ObservableList.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6823c0ee4c425a45c2fd0f87883cd3448e8a5b6
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/observablelists/ObservableList.java
@@ -0,0 +1,110 @@
+package de.kuschku.util.observablelists;
+
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class ObservableList<T> extends ArrayList<T> {
+    UICallback callback;
+
+    public void setCallback(UICallback callback) {
+        this.callback = callback;
+    }
+
+    private int getPosition() {
+        return isEmpty() ? 0 : size() - 1;
+    }
+
+    @Override
+    public boolean add(T object) {
+        add(getPosition(), object);
+        return true;
+    }
+
+    @Override
+    public void add(int index, T object) {
+        super.add(index, object);
+        callback.notifyItemInserted(index);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends T> collection) {
+        return addAll(getPosition(), collection);
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends T> collection) {
+        boolean result = super.addAll(index, collection);
+        if (result) callback.notifyItemRangeInserted(index, collection.size());
+        return result;
+    }
+
+    @Override
+    public T remove(int index) {
+        T result = super.remove(index);
+        callback.notifyItemRemoved(index);
+        return result;
+    }
+
+    @Override
+    public boolean remove(Object object) {
+        int position = indexOf(object);
+        if (position == -1) {
+            return false;
+        } else {
+            remove(position);
+            callback.notifyItemRemoved(position);
+            return true;
+        }
+    }
+
+    @Override
+    protected void removeRange(int fromIndex, int toIndex) {
+        super.removeRange(fromIndex, toIndex);
+        callback.notifyItemRangeRemoved(fromIndex, toIndex - fromIndex);
+    }
+
+    @Override
+    public boolean removeAll(@NonNull Collection<?> collection) {
+        return super.removeAll(collection);
+    }
+
+    @Override
+    public boolean retainAll(@NonNull Collection<?> collection) {
+        return super.retainAll(collection);
+    }
+
+    @NonNull
+    @Override
+    public Iterator<T> iterator() {
+        return new CallbackedArrayListIterator<>(super.iterator());
+    }
+
+    class CallbackedArrayListIterator<E> implements Iterator<E> {
+        final Iterator<E> iterator;
+        int position = 0;
+
+        public CallbackedArrayListIterator(Iterator<E> iterator) {
+            this.iterator = iterator;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
+        public E next() {
+            position++;
+            return iterator.next();
+        }
+
+        @Override
+        public void remove() {
+            iterator.remove();
+            callback.notifyItemRemoved(position);
+        }
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java b/app/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java
new file mode 100644
index 0000000000000000000000000000000000000000..fef28bb8fa87e6675779a3b83ee080f2726f99c2
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java
@@ -0,0 +1,79 @@
+package de.kuschku.util.observablelists;
+
+import android.support.v7.util.SortedList;
+
+public class ObservableSortedList<T extends ContentComparable<T>> {
+    public final SortedList<T> list;
+    Callback internal = new Callback();
+    UICallback callback;
+    boolean reverse;
+
+    public ObservableSortedList(Class<T> cl) {
+        list = new SortedList<>(cl, internal);
+    }
+
+    public ObservableSortedList(Class<T> cl, boolean reverse) {
+        this(cl);
+        this.reverse = reverse;
+    }
+
+    public void setCallback(UICallback callback) {
+        this.callback = callback;
+    }
+
+    public T last() {
+        if (list.size() == 0) return null;
+
+        return list.get(list.size() - 1);
+    }
+
+    class Callback extends SortedList.Callback<T> {
+        @Override
+        public int compare(T o1, T o2) {
+            return (reverse) ? o2.compareTo(o1): o1.compareTo(o2);
+        }
+
+        @Override
+        public void onInserted(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemInserted(position);
+                else
+                    callback.notifyItemRangeInserted(position, count);
+        }
+
+        @Override
+        public void onRemoved(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemRemoved(position);
+                else
+                    callback.notifyItemRangeRemoved(position, count);
+        }
+
+        @Override
+        public void onMoved(int fromPosition, int toPosition) {
+            if (callback != null)
+                callback.notifyItemMoved(fromPosition, toPosition);
+        }
+
+        @Override
+        public void onChanged(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemChanged(position);
+                else
+                    callback.notifyItemRangeChanged(position, count);
+        }
+
+        @Override
+        public boolean areContentsTheSame(T oldItem, T newItem) {
+            return oldItem.equalsContent(newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(T item1, T item2) {
+            return item1.equals(item2);
+        }
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java b/app/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a59f28235010207eabd2588fa522ac8306d4e9b
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java
@@ -0,0 +1,50 @@
+package de.kuschku.util.observablelists;
+
+import android.support.v7.widget.RecyclerView;
+
+public class RecyclerViewAdapterCallback implements UICallback {
+    private final RecyclerView.Adapter adapter;
+    private final AutoScroller scroller;
+
+    public RecyclerViewAdapterCallback(RecyclerView.Adapter adapter, AutoScroller scroller) {
+        this.adapter = adapter;
+        this.scroller = scroller;
+    }
+
+    @Override
+    public void notifyItemInserted(int position) {
+        adapter.notifyItemInserted(position);
+        if (position == 0) scroller.notifyScroll();
+    }
+
+    @Override
+    public void notifyItemChanged(int position) {
+        adapter.notifyItemChanged(position);
+    }
+
+    @Override
+    public void notifyItemRemoved(int position) {
+        adapter.notifyItemRemoved(position);
+    }
+
+    @Override
+    public void notifyItemMoved(int from, int to) {
+        adapter.notifyItemMoved(from, to);
+    }
+
+    @Override
+    public void notifyItemRangeInserted(int position, int count) {
+        adapter.notifyItemRangeInserted(position, count);
+        if (position == 0) scroller.notifyScroll();
+    }
+
+    @Override
+    public void notifyItemRangeChanged(int position, int count) {
+        adapter.notifyItemRangeChanged(position, count);
+    }
+
+    @Override
+    public void notifyItemRangeRemoved(int position, int count) {
+        adapter.notifyItemRangeRemoved(position, count);
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/observablelists/UICallback.java b/app/src/main/java/de/kuschku/util/observablelists/UICallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..124f9261a360f5569e1078b09c48f4bf77373b0e
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/observablelists/UICallback.java
@@ -0,0 +1,17 @@
+package de.kuschku.util.observablelists;
+
+public interface UICallback {
+    void notifyItemInserted(int position);
+
+    void notifyItemChanged(int position);
+
+    void notifyItemRemoved(int position);
+
+    void notifyItemMoved(int from, int to);
+
+    void notifyItemRangeInserted(int position, int count);
+
+    void notifyItemRangeChanged(int position, int count);
+
+    void notifyItemRangeRemoved(int position, int count);
+}
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index bb2d26152e8c13ab4d1d0a16e3f8cb86de7e9fe5..dfda993f9f055c5cd41de62db1a0c6780018dd7e 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -9,6 +9,7 @@
 
     <android.support.v7.widget.RecyclerView
         android:id="@+id/messages"
+        android:clickable="true"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 </android.support.v4.widget.SwipeRefreshLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/slider_main.xml b/app/src/main/res/layout/slider_main.xml
index a09fc45a4b3f4f53420394837e7b042b644d0aed..0b34d0308d901000cceea50dfa745bc32a1e47a2 100644
--- a/app/src/main/res/layout/slider_main.xml
+++ b/app/src/main/res/layout/slider_main.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
-    android:clickable="true"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
diff --git a/app/src/main/res/layout/widget_chatmessage.xml b/app/src/main/res/layout/widget_chatmessage.xml
index 877bceca382d9a5f15f5c37fd37ef40f3df0e75d..f58de9f1e73b40374ca6883733b4923fd834299c 100644
--- a/app/src/main/res/layout/widget_chatmessage.xml
+++ b/app/src/main/res/layout/widget_chatmessage.xml
@@ -6,21 +6,25 @@
     android:layout_height="match_parent"
     android:textAppearance="?android:attr/textAppearanceListItemSmall"
     android:gravity="top"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-    android:paddingTop="4dp"
-    android:paddingBottom="4dp" >
-    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:clickable="true"
+    android:paddingStart="@dimen/message_horizontal"
+    android:paddingLeft="@dimen/message_horizontal"
+    android:paddingEnd="@dimen/message_horizontal"
+    android:paddingRight="@dimen/message_horizontal"
+    android:paddingTop="@dimen/message_vertical"
+    android:paddingBottom="@dimen/message_vertical" >
+    <TextView
         android:id="@+id/time"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:typeface="monospace"
-        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:paddingRight="?android:attr/listPreferredItemPaddingRight" />
-    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:text="10:08"
+        android:layout_marginEnd="@dimen/message_horizontal"
+        android:layout_marginRight="@dimen/message_horizontal" />
+    <TextView
         android:id="@+id/content"
+        android:clickable="true"
+        android:text="NK33 well, the issue is I need the main engines to tilt 90 degrees for vertical takeoff and landing"
         android:layout_width="0dip"
         android:layout_height="wrap_content"
         android:layout_weight="1" />
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 2e9ab05cbd7148030dd57c6efb9eeb9d4408234b..7a2bf40c8086a6b7f417fd852af7467a826e3ed5 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -50,6 +50,7 @@
 
     <attr name="colorBackground" format="color" />
     <attr name="colorBackgroundHighlight" format="color" />
+    <attr name="colorBackgroundSecondary" format="color" />
     <attr name="colorBackgroundCard" format="color" />
 
     <!-- Tint colors for drawer -->
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 812cb7be0a1565a849a29c08fa07fb9901ccb347..0222438f9a46ef8e9a898f4ce3e1d2ca95a93342 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -3,4 +3,7 @@
     <dimen name="activity_horizontal_margin">16dp</dimen>
     <dimen name="activity_vertical_margin">16dp</dimen>
     <dimen name="fab_margin">16dp</dimen>
+
+    <dimen name="message_horizontal">8dp</dimen>
+    <dimen name="message_vertical">4dp</dimen>
 </resources>
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1194555c60b5e25bd88dcc85705c3316b32bd690
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item name="custom_url_span" type="id" />
+    <item name="custom_channel_span" type="id" />
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index af5d1ca069b7d6d07cbb660ee4f58799ba308e40..35f6fbc2ff72e5c4fc9ec0af0162e2bff7f12f84 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -24,6 +24,7 @@
 
         <item name="colorBackground">@color/md_dark_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
+        <item name="colorBackgroundSecondary">@android:color/transparent</item>
         <item name="colorBackgroundCard">@color/md_dark_cards</item>
 
         <item name="colorTintActivity">#88cc33</item>
@@ -55,6 +56,7 @@
 
         <item name="colorBackground">@color/md_light_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
+        <item name="colorBackgroundSecondary">@android:color/transparent</item>
         <item name="colorBackgroundCard">@color/md_light_cards</item>
 
         <item name="colorTintActivity">#88cc33</item>
@@ -86,6 +88,7 @@
 
         <item name="colorBackground">@color/md_light_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
+        <item name="colorBackgroundSecondary">@android:color/transparent</item>
         <item name="colorBackgroundCard">@color/md_light_cards</item>
 
         <item name="colorTintActivity">#88cc33</item>