diff --git a/app/build.gradle b/app/build.gradle
index c05a7e9d80b966b34d0b22555a6d7d098ddc2bff..372d606482f9a6c2cc533bfe35c9d11eefc0fc5b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -46,6 +46,7 @@ if (versionPropsFile.canRead()) {
   throw new GradleException("Could not read version.properties!"+versionPropsFile.toString())
 }
 
+
 def rawVersionName = "0.2.0"
 
 android {
@@ -61,20 +62,9 @@ android {
     }
     buildTypes {
         release {
-            //minifyEnabled true
-            //shrinkResources true
-            //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-
-            applicationVariants.all { variant ->
-                variant.outputs.each { output ->
-                    if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
-                        def fileName = output.outputFile.name
-                                .replace(".apk", String.format("-%s-build%d.apk", rawVersionName, versionCode))
-                                .replace("app-", "QuasselDroidNG-")
-                        output.outputFile = new File(output.outputFile.parent, fileName)
-                    }
-                }
-            }
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
     compileOptions {
@@ -93,6 +83,31 @@ android {
         exclude 'META-INF/dependencies.txt'
         exclude 'META-INF/LGPL2.1'
     }
+    lintOptions {
+        abortOnError false
+    }
+}
+
+def runTasks = gradle.startParameter.taskNames
+if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
+    android {
+        buildTypes {
+            release {
+                //minifyEnabled true
+                //shrinkResources true
+                //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+                applicationVariants.all { variant ->
+                    variant.outputs.each { output ->
+                        def fileName = output.outputFile.name
+                                .replace(".apk", String.format("-%s-build%d.apk", rawVersionName, versionBuild))
+                                .replace("app-", "QuasselDroidNG-")
+                        output.outputFile = new File(output.outputFile.parent, fileName)
+                    }
+                }
+            }
+        }
+    }
 }
 
 
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 59129d0e12abc8d9b3558d13e31c97b42df76ee9..7f2953eaa185a3bd646f59a8a1be8f7fdb372aca 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -26,13 +26,16 @@
 -dontwarn sun.misc.Unsafe
 -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
 -dontwarn javax.annotation.processing.ProcessingEnvironment
+-dontwarn com.nineoldandroids.view.animation.AnimatorProxy
 
--keepclasseswithmembernames class **.libquassel.** {
+-keepclasseswithmembernames class de.kuschku.** {
     <methods>;
+    <fields>;
 }
 
--keepclassmembers class **.libquassel.** {
+-keepclassmembers class de.kuschku.** {
     <methods>;
+    <fields>;
 }
 
 
diff --git a/app/src/main/java/de/kuschku/libquassel/Client.java b/app/src/main/java/de/kuschku/libquassel/Client.java
index dc24c9aaeecff2d632390d1ea3e6ad8017cc5c3c..62fe02b78d79006e06546d8ddf2d11660b7946fd 100644
--- a/app/src/main/java/de/kuschku/libquassel/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/Client.java
@@ -54,7 +54,7 @@ public class Client {
     @NonNull
     private final BacklogManager backlogManager;
     @NonNull
-    private final NotificationManager notificationManager = new NotificationManager();
+    private final NotificationManager notificationManager = new NotificationManager(this);
     @NonNull
     private final BusProvider busProvider;
     private long lag;
@@ -197,6 +197,7 @@ public class Client {
 
     public void setState(@Nullable SessionState state) {
         this.state = state;
+        Log.e("DEBUG", String.valueOf(this.state));
     }
 
     @NonNull
diff --git a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
index 23387e78151ec62d30090f27dd241bea3e1871b7..71baec5989b2828c747c4ed98d650c764254cef5 100644
--- a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
+++ b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
@@ -281,12 +281,13 @@ public class CoreConnection {
                 assertNotNull(client);
 
                 while (running) {
-                    busProvider.dispatch(new Heartbeat(DateTime.now()));
+                    Heartbeat heartbeat = new Heartbeat(DateTime.now());
+                    Log.e("heartbeat", String.valueOf(heartbeat));
+                    busProvider.dispatch(heartbeat);
 
                     Thread.sleep(30 * 1000);
                 }
             } catch (InterruptedException e) {
-                e.printStackTrace();
             }
         }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/IProtocolHandler.java b/app/src/main/java/de/kuschku/libquassel/IProtocolHandler.java
index ef0fdb7e7dc4966e4f59945fc60cbf2d50f3ddf8..203282cb7e68c5f26b682a080eae83bd896a0329 100644
--- a/app/src/main/java/de/kuschku/libquassel/IProtocolHandler.java
+++ b/app/src/main/java/de/kuschku/libquassel/IProtocolHandler.java
@@ -2,6 +2,8 @@ package de.kuschku.libquassel;
 
 import android.support.annotation.NonNull;
 
+import de.kuschku.libquassel.functions.types.Heartbeat;
+import de.kuschku.libquassel.functions.types.HeartbeatReply;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.functions.types.InitRequestFunction;
 import de.kuschku.libquassel.functions.types.RpcCallFunction;
@@ -31,6 +33,10 @@ public interface IProtocolHandler {
 
     void onEvent(SessionInit message);
 
+    void onEvent(Heartbeat message);
+
+    void onEventMainThread(HeartbeatReply message);
+
     @NonNull
     Client getClient();
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
index 6cafde2d2154c3330db981a51bef135c1b50375b..82bfaae07e81d13b44704e9d273a126fd8d86cfc 100644
--- a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
+++ b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
@@ -5,6 +5,8 @@ import android.util.Log;
 
 import org.joda.time.DateTime;
 
+import java.util.List;
+
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.HandshakeFailedEvent;
@@ -25,6 +27,7 @@ import de.kuschku.libquassel.objects.types.SessionInit;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.SyncableRegistry;
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
+import de.kuschku.libquassel.syncables.types.Identity;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
 import de.kuschku.util.AndroidAssert;
 import de.kuschku.util.ReflectionUtils;
@@ -135,6 +138,9 @@ public class ProtocolHandler implements IProtocolHandler {
         for (int NetworkId : client.getState().NetworkIds) {
             client.sendInitRequest("Network", String.valueOf(NetworkId), true);
         }
+        for (Identity identity : client.getState().Identities) {
+            identity.init(null, busProvider, client);
+        }
         for (BufferInfo info : message.SessionState.BufferInfos) {
             final int initialBacklogCount = 10;
             client.getBacklogManager().requestBacklog(info.id, -1, -1, initialBacklogCount, 0);
@@ -146,6 +152,8 @@ public class ProtocolHandler implements IProtocolHandler {
     }
 
     public void onEventMainThread(@NonNull HeartbeatReply heartbeat) {
+        Log.e("heartbeatreply", String.valueOf(heartbeat));
+
         long roundtrip = DateTime.now().getMillis() - heartbeat.dateTime.getMillis();
         long lag = (long) (roundtrip * 0.5);
 
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatSerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatSerializer.java
index ec2e4c59d9fe07501f98140c2d0d06757fab5755..38d20cd724cc3bbdf21c21e6e6a7408cc00471e1 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatSerializer.java
@@ -9,6 +9,7 @@ import java.util.List;
 
 import de.kuschku.libquassel.functions.FunctionType;
 import de.kuschku.libquassel.functions.types.Heartbeat;
+import de.kuschku.libquassel.primitives.QMetaType;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 import static de.kuschku.util.AndroidAssert.assertTrue;
@@ -30,7 +31,7 @@ public class HeartbeatSerializer implements FunctionSerializer<Heartbeat> {
     public List serialize(@NonNull Heartbeat data) {
         return Arrays.asList(
                 FunctionType.HEARTBEAT.id,
-                new QVariant<>(data.dateTime)
+                new QVariant<>(QMetaType.Type.QDateTime, data.dateTime)
         );
     }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
index 40b7c73c8ebcb36ec2b816e6f0a07f611a457ae0..6c61d0a3f505cb7712d724e479a7b84ff64c70fe 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
@@ -12,5 +12,5 @@ public interface Buffer {
     @Nullable
     String getName();
 
-    boolean isActive();
+    BufferInfo.BufferStatus getStatus();
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
index 2865b1a633a94fc2ed9585888bdd91726dacce53..41f8551214d0f5bdcffe3ff21aec63298112c9b8 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
@@ -30,8 +30,8 @@ public class ChannelBuffer implements Buffer {
     }
 
     @Override
-    public boolean isActive() {
-        return channel != null;
+    public BufferInfo.BufferStatus getStatus() {
+        return channel == null ? BufferInfo.BufferStatus.OFFLINE : BufferInfo.BufferStatus.ONLINE;
     }
 
     @Nullable
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/NotificationManager.java b/app/src/main/java/de/kuschku/libquassel/localtypes/NotificationManager.java
index 5dd652996789696123622267ee512ef194bd4a30..9ae407ba07ce761497817b8c79602d641793cb79 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/NotificationManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/NotificationManager.java
@@ -2,17 +2,82 @@ package de.kuschku.libquassel.localtypes;
 
 import android.util.SparseArray;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.syncables.types.Identity;
+import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 
 public class NotificationManager {
     private SparseArray<ObservableComparableSortedList<Message>> notifications = new SparseArray<>();
+    private List<HighlightRule> highlights = new ArrayList<>();
+    private Client client;
+
+    public NotificationManager(Client client) {
+        this.client = client;
+    }
 
     public ObservableComparableSortedList<Message> getNotifications(int bufferid) {
+        if (notifications.get(bufferid) == null)
+            notifications.put(bufferid, new ObservableComparableSortedList<>(Message.class));
+
         return notifications.get(bufferid);
     }
 
     public void init(int id) {
         notifications.put(id, new ObservableComparableSortedList<>(Message.class));
     }
+
+    public void receiveMessage(Message message) {
+        if (checkMessage(message)) {
+            getNotifications(message.bufferInfo.id).add(message);
+        }
+    }
+
+    public boolean checkMessage(Message message) {
+        Buffer buffer = client.getBuffer(message.bufferInfo.id);
+        if (buffer == null) return false;
+        Network network = client.getNetwork(buffer.getInfo().networkId);
+        if (network == null) return false;
+        Identity identity = client.getIdentity(network.getIdentityId());
+        if (identity == null) return false;
+
+        for (String nick : identity.getNicks()) {
+            if (message.content.contains(nick))
+                return true;
+        }
+        for (HighlightRule rule : highlights) {
+            if (rule.matches(message.content, buffer.getName()))
+                return true;
+        }
+        return false;
+    }
+
+    public void receiveMessages(List<Message> messages) {
+        for (Message message : messages) {
+            receiveMessage(message);
+        }
+    }
+
+    private class HighlightRule {
+        public final Pattern rule;
+        public final Pattern channelRule;
+        public final boolean invertChannelRule;
+        public final boolean caseSensitive;
+
+        public HighlightRule(String rule, String channelRule, boolean invertChannelRule, boolean caseSensitive) {
+            this.rule = rule.isEmpty() ? Pattern.compile(".*") : Pattern.compile(rule);
+            this.channelRule = channelRule.isEmpty() ? Pattern.compile(".*") : Pattern.compile(channelRule);
+            this.invertChannelRule = invertChannelRule;
+            this.caseSensitive = caseSensitive;
+        }
+
+        public boolean matches(String message, String channelName) {
+            return (invertChannelRule ^ channelRule.matcher(channelName).matches() && rule.matcher(message).matches());
+        }
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
index b64c6be507cdaaa63510e23392878626a05a5fdd..c07356b146c540cfc084165109bf8ca9b36bd0f5 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
@@ -30,8 +30,10 @@ public class QueryBuffer implements Buffer {
     }
 
     @Override
-    public boolean isActive() {
-        return user != null;
+    public BufferInfo.BufferStatus getStatus() {
+        return  (user == null) ?    BufferInfo.BufferStatus.OFFLINE :
+                (user.isAway()) ?   BufferInfo.BufferStatus.AWAY :
+                                    BufferInfo.BufferStatus.ONLINE;
     }
 
     @Nullable
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
index bc25d633c16680000372b891447384e02b23783c..c28a09c2524686dd65c6d4f10a93589bc7578289 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
@@ -30,8 +30,8 @@ public class StatusBuffer implements Buffer {
     }
 
     @Override
-    public boolean isActive() {
-        return network.isConnected();
+    public BufferInfo.BufferStatus getStatus() {
+        return network.isConnected() ? BufferInfo.BufferStatus.ONLINE : BufferInfo.BufferStatus.OFFLINE;
     }
 
     @NonNull
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
index 370099559148321b941ca112eba5da04bb7190c3..d36073bf8961dbe73510239a28cef570d3df8a05 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
@@ -1,9 +1,9 @@
 package de.kuschku.libquassel.localtypes.backlogmanagers;
 
 import android.support.annotation.NonNull;
-import android.util.Log;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeUtils;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -47,7 +47,7 @@ public class BacklogFilter implements UICallback {
         int id = -1;
         while (now.isAfter(earliestMessage)) {
             filtered.add(new Message(
-                    id,
+                    (int) DateTimeUtils.toJulianDay(now.getMillis()),
                     now,
                     Message.Type.DayChange,
                     new Message.Flags(false, false, false, false, false),
@@ -79,21 +79,22 @@ public class BacklogFilter implements UICallback {
         updateAdd();
     }
 
-    private void updateRemove() {
+    public void update() {
+        updateAdd();
+        updateRemove();
+    }
+
+    public void updateRemove() {
         for (Message message : unfiltered) {
-            if (filterItem(message) && filtered.contains(message)) {
-                String simpleName = getClass().getSimpleName();
-                Log.e(simpleName, "Filtered: "+message);
+            if (filterItem(message)) {
                 filtered.remove(message);
             }
         }
     }
 
-    private void updateAdd() {
+    public void updateAdd() {
         for (Message message : unfiltered) {
-            if (!filterItem(message) && !filtered.contains(message)) {
-                String simpleName = getClass().getSimpleName();
-                Log.e(simpleName, "Unfiltered: "+message);
+            if (!filterItem(message)) {
                 filtered.add(message);
             }
         }
@@ -134,4 +135,29 @@ public class BacklogFilter implements UICallback {
             notifyItemRemoved(i);
         }
     }
+
+    public void setFilters(int filters) {
+        Set<Message.Type> removed = new HashSet<>();
+        for (Message.Type type : filteredTypes) {
+            if ((filters & type.value) == 0)
+                removed.add(type);
+        }
+        for (Message.Type type : removed) {
+            removeFilter(type);
+        }
+
+        for (Message.Type type : Message.Type.values()) {
+            if ((filters & type.value) != 0) {
+                addFilter(type);
+            }
+        }
+    }
+
+    public int getFilters() {
+        int filters = 0x00000000;
+        for (Message.Type type : filteredTypes) {
+            filters |= type.value;
+        }
+        return filters;
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
index 2abd74a6d6399ed95c775ae004b8a885bbf2cf0f..e8236cc7a9aa5c38c47eb5bb551584edd0545afd 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
@@ -4,7 +4,6 @@ import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
-import android.util.Log;
 import android.util.SparseArray;
 
 import com.google.common.collect.Lists;
@@ -19,13 +18,11 @@ 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.AndroidAssert;
 import de.kuschku.util.observables.AutoScroller;
 import de.kuschku.util.observables.callbacks.wrappers.AdapterUICallbackWrapper;
 import de.kuschku.util.observables.lists.ObservableComparableSortedList;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
-import static de.kuschku.util.AndroidAssert.*;
 import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 public class SimpleBacklogManager extends BacklogManager<SimpleBacklogManager> {
@@ -60,6 +57,7 @@ public class SimpleBacklogManager extends BacklogManager<SimpleBacklogManager> {
 
     public void receiveBacklog(@IntRange(from = 0) int bufferId, int from, int to, int count, int extra, @NonNull List<Message> messages) {
         get(bufferId).addAll(messages);
+        client.getNotificationManager().receiveMessages(messages);
 
         busProvider.sendEvent(new BacklogReceivedEvent(bufferId));
     }
@@ -70,6 +68,7 @@ public class SimpleBacklogManager extends BacklogManager<SimpleBacklogManager> {
         assertNotNull(messages);
 
         messages.add(message);
+        client.getNotificationManager().receiveMessage(message);
     }
 
     public void bind(@IntRange(from = 0) int bufferId, @NonNull RecyclerView.Adapter adapter, @Nullable AutoScroller scroller) {
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 430442535bf36e7dbcb867a02fa3253bb6623e1b..627698d1b1214af53cdf99bfa96b059bc4f4c82b 100644
--- a/app/src/main/java/de/kuschku/libquassel/message/Message.java
+++ b/app/src/main/java/de/kuschku/libquassel/message/Message.java
@@ -52,20 +52,13 @@ public class Message implements ContentComparable<Message> {
     }
 
     @Override
-    public boolean equalsContent(@NonNull Message message) {
-        return messageId == message.messageId &&
-                time.equals(message.time) &&
-                type == message.type &&
-                flags.equals(message.flags) &&
-                bufferInfo.equals(message.bufferInfo) &&
-                sender.equals(message.sender) &&
-                content.equals(message.content);
+    public boolean areContentsTheSame(@NonNull Message message) {
+        return this == message;
     }
 
     @Override
-    public boolean equals(@Nullable Object o) {
-        return this == o || !(o == null || getClass() != o.getClass()) && messageId == ((Message) o).messageId;
-
+    public boolean areItemsTheSame(Message other) {
+        return this.messageId == other.messageId;
     }
 
     @Override
@@ -75,7 +68,10 @@ public class Message implements ContentComparable<Message> {
 
     @Override
     public int compareTo(@NonNull Message another) {
-        return this.time.compareTo(another.time);
+        if (this.type != Type.DayChange && another.type != Type.DayChange)
+            return this.messageId - another.messageId;
+        else
+            return this.time.compareTo(another.time);
     }
 
     public enum Type {
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionStateSerializer.java b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionStateSerializer.java
index dce6e2f247c9506b735032658efa4d3474b40457..ce63ca596e174214458b327f0ac87547e952e80e 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionStateSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/serializers/SessionStateSerializer.java
@@ -13,6 +13,7 @@ import de.kuschku.libquassel.functions.types.UnpackedFunction;
 import de.kuschku.libquassel.objects.types.SessionState;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.primitives.types.QVariant;
+import de.kuschku.libquassel.syncables.types.Identity;
 
 @SuppressWarnings({"unchecked", "ConstantConditions"})
 public class SessionStateSerializer implements ObjectSerializer<SessionState> {
@@ -43,7 +44,7 @@ public class SessionStateSerializer implements ObjectSerializer<SessionState> {
     @Override
     public SessionState fromLegacy(@NonNull Map<String, QVariant> map) {
         return new SessionState(
-                (List<Map<String, QVariant>>) map.get("Identities").or(new ArrayList<>()),
+                (List<Identity>) map.get("Identities").or(new ArrayList<>()),
                 (List<BufferInfo>) map.get("BufferInfos").or(new ArrayList<>()),
                 (List<Integer>) map.get("NetworkIds").or(new ArrayList<>())
         );
diff --git a/app/src/main/java/de/kuschku/libquassel/objects/types/SessionState.java b/app/src/main/java/de/kuschku/libquassel/objects/types/SessionState.java
index 4c3f670247d38940e9d2ce408068972a99b0ef3e..bbd365392d3e60d557042590f6409716b641981a 100644
--- a/app/src/main/java/de/kuschku/libquassel/objects/types/SessionState.java
+++ b/app/src/main/java/de/kuschku/libquassel/objects/types/SessionState.java
@@ -3,20 +3,19 @@ package de.kuschku.libquassel.objects.types;
 import android.support.annotation.NonNull;
 
 import java.util.List;
-import java.util.Map;
 
 import de.kuschku.libquassel.primitives.types.BufferInfo;
-import de.kuschku.libquassel.primitives.types.QVariant;
+import de.kuschku.libquassel.syncables.types.Identity;
 
 public class SessionState {
     @NonNull
-    public final List<Map<String, QVariant>> Identities;
+    public final List<Identity> Identities;
     @NonNull
     public final List<BufferInfo> BufferInfos;
     @NonNull
     public final List<Integer> NetworkIds;
 
-    public SessionState(@NonNull List<Map<String, QVariant>> identities, @NonNull List<BufferInfo> bufferInfos,
+    public SessionState(@NonNull List<Identity> identities, @NonNull List<BufferInfo> bufferInfos,
                         @NonNull List<Integer> networkIds) {
         this.Identities = identities;
         this.BufferInfos = bufferInfos;
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/types/BufferInfo.java b/app/src/main/java/de/kuschku/libquassel/primitives/types/BufferInfo.java
index 28a4ba65837504e3970492de98df84e113cf6e5f..f392d7b94b1e51dbd193c56df19f86a4fb056e8c 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/types/BufferInfo.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/types/BufferInfo.java
@@ -65,4 +65,17 @@ public class BufferInfo {
             }
         }
     }
+
+    public enum BufferStatus {
+        OFFLINE,
+        AWAY,
+        ONLINE
+    }
+
+    public enum ContentStatus {
+        NONE,
+        ACTIVITY,
+        MESSAGES,
+        HIGHLIGHTS
+    }
 }
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 03c997d10608ab72e14f5b6b13f0837e7727cc56..7c7b080569c1ce1ae73249b46b23e9316a77824e 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
@@ -33,7 +33,7 @@ public class BufferViewConfigSerializer implements ObjectSerializer<BufferViewCo
         map.data.put("bufferViewName", new QVariant<>(data.getBufferViewName()));
         map.data.put("TemporarilyRemovedBuffers", new QVariant<>(data.getTemporarilyRemovedBuffers()));
         map.data.put("hideInactiveNetworks", new QVariant<>(data.isHideInactiveNetworks()));
-        map.data.put("BufferList", new QVariant<>(data.getBufferList()));
+        map.data.put("BufferList", new QVariant<>(data.getBuffers()));
         map.data.put("allowedBufferTypes", new QVariant<>(data.getAllowedBufferTypes()));
         map.data.put("sortAlphabetically", new QVariant<>(data.isSortAlphabetically()));
         map.data.put("disableDecoration", new QVariant<>(data.isDisableDecoration()));
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 b0fa90823f1d7f7623e4821afc4a8830079154d3..177f3d8783818a07f07b532ddcad780ddb23e62d 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
@@ -1,7 +1,7 @@
 package de.kuschku.libquassel.syncables.types;
 
-import android.net.*;
 import android.support.annotation.NonNull;
+import android.util.Log;
 
 import java.util.Collections;
 import java.util.List;
@@ -11,20 +11,18 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferViewConfigSerializer;
-import de.kuschku.util.AndroidAssert;
 import de.kuschku.util.observables.callbacks.ElementCallback;
 import de.kuschku.util.observables.lists.IObservableList;
 import de.kuschku.util.observables.lists.ObservableElementList;
 
-import static de.kuschku.util.AndroidAssert.*;
+import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     private String bufferViewName;
-    private List<Integer> TemporarilyRemovedBuffers;
+    private List<Integer> temporarilyRemovedBuffers;
     private boolean hideInactiveNetworks;
-    private IObservableList<ElementCallback<Integer>, Integer> BufferList;
+    private IObservableList<ElementCallback<Integer>, Integer> buffers;
     private IObservableList<ElementCallback<Integer>, Integer> NetworkList = new ObservableElementList<>();
     private int allowedBufferTypes;
     private boolean sortAlphabetically;
@@ -33,14 +31,14 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     private int networkId;
     private int minimumActivity;
     private boolean hideInactiveBuffers;
-    private List<Integer> RemovedBuffers;
+    private List<Integer> removedBuffers;
     private Client client;
 
     public BufferViewConfig(String bufferViewName, List<Integer> temporarilyRemovedBuffers, boolean hideInactiveNetworks, @NonNull List<Integer> bufferList, int allowedBufferTypes, boolean sortAlphabetically, boolean disableDecoration, boolean addNewBuffersAutomatically, int networkId, int minimumActivity, boolean hideInactiveBuffers, List<Integer> removedBuffers) {
         this.bufferViewName = bufferViewName;
-        this.TemporarilyRemovedBuffers = temporarilyRemovedBuffers;
+        this.temporarilyRemovedBuffers = temporarilyRemovedBuffers;
         this.hideInactiveNetworks = hideInactiveNetworks;
-        this.BufferList = new ObservableElementList<>(bufferList);
+        this.buffers = new ObservableElementList<>(bufferList);
         this.allowedBufferTypes = allowedBufferTypes;
         this.sortAlphabetically = sortAlphabetically;
         this.disableDecoration = disableDecoration;
@@ -48,7 +46,7 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         this.networkId = networkId;
         this.minimumActivity = minimumActivity;
         this.hideInactiveBuffers = hideInactiveBuffers;
-        this.RemovedBuffers = removedBuffers;
+        this.removedBuffers = removedBuffers;
     }
 
     public String getBufferViewName() {
@@ -60,11 +58,11 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     }
 
     public List<Integer> getTemporarilyRemovedBuffers() {
-        return TemporarilyRemovedBuffers;
+        return temporarilyRemovedBuffers;
     }
 
     public void setTemporarilyRemovedBuffers(List<Integer> temporarilyRemovedBuffers) {
-        TemporarilyRemovedBuffers = temporarilyRemovedBuffers;
+        this.temporarilyRemovedBuffers = temporarilyRemovedBuffers;
     }
 
     public boolean isHideInactiveNetworks() {
@@ -75,16 +73,23 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         this.hideInactiveNetworks = hideInactiveNetworks;
     }
 
-    public IObservableList<ElementCallback<Integer>, Integer> getBufferList() {
-        return BufferList;
+
+    public void SYNC_setHideInactiveNetworks(boolean hideInactiveNetworks) {
+        if (this.hideInactiveNetworks == hideInactiveNetworks) return;
+        setHideInactiveNetworks(hideInactiveBuffers);
+        sync("setHideInactiveBuffers", new Object[]{hideInactiveNetworks});
+    }
+
+    public IObservableList<ElementCallback<Integer>, Integer> getBuffers() {
+        return buffers;
     }
 
-    public void setBufferList(IObservableList<ElementCallback<Integer>, Integer> bufferList) {
-        BufferList = bufferList;
+    public void setBuffers(IObservableList<ElementCallback<Integer>, Integer> buffers) {
+        this.buffers = buffers;
     }
 
     public void setBufferList(@NonNull List<Integer> bufferList) {
-        BufferList = new ObservableElementList<>(bufferList);
+        buffers = new ObservableElementList<>(bufferList);
     }
 
     public int getAllowedBufferTypes() {
@@ -125,10 +130,11 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
 
     public void setNetworkId(int networkId) {
         this.networkId = networkId;
-        if (this.networkId != -1) {
-            this.NetworkList.addAll(client.getNetworks());
+        if (this.networkId != 0) {
+            if (client.getNetworks().contains(networkId))
+                this.NetworkList.add(networkId);
         } else {
-            this.NetworkList.retainAll(Collections.singletonList(networkId));
+            this.NetworkList.addAll(client.getNetworks());
         }
     }
 
@@ -148,12 +154,18 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         this.hideInactiveBuffers = hideInactiveBuffers;
     }
 
+    public void SYNC_setHideInactiveBuffers(boolean hideInactiveBuffers) {
+        if (this.hideInactiveBuffers == hideInactiveBuffers) return;
+        setHideInactiveBuffers(hideInactiveBuffers);
+        sync("setHideInactiveBuffers", new Object[]{hideInactiveBuffers});
+    }
+
     public List<Integer> getRemovedBuffers() {
-        return RemovedBuffers;
+        return removedBuffers;
     }
 
     public void setRemovedBuffers(List<Integer> removedBuffers) {
-        RemovedBuffers = removedBuffers;
+        this.removedBuffers = removedBuffers;
     }
 
     @NonNull
@@ -161,9 +173,9 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     public String toString() {
         return "BufferViewConfig{" +
                 "bufferViewName='" + bufferViewName + '\'' +
-                ", TemporarilyRemovedBuffers=" + TemporarilyRemovedBuffers +
+                ", temporarilyRemovedBuffers=" + temporarilyRemovedBuffers +
                 ", hideInactiveNetworks=" + hideInactiveNetworks +
-                ", BufferList=" + BufferList +
+                ", buffers=" + buffers +
                 ", allowedBufferTypes=" + allowedBufferTypes +
                 ", sortAlphabetically=" + sortAlphabetically +
                 ", disableDecoration=" + disableDecoration +
@@ -171,7 +183,7 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
                 ", networkId=" + networkId +
                 ", minimumActivity=" + minimumActivity +
                 ", hideInactiveBuffers=" + hideInactiveBuffers +
-                ", RemovedBuffers=" + RemovedBuffers +
+                ", removedBuffers=" + removedBuffers +
                 '}';
     }
 
@@ -186,9 +198,9 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     @Override
     public void update(BufferViewConfig from) {
         this.bufferViewName = from.bufferViewName;
-        this.TemporarilyRemovedBuffers = from.TemporarilyRemovedBuffers;
+        this.temporarilyRemovedBuffers = from.temporarilyRemovedBuffers;
         this.hideInactiveNetworks = from.hideInactiveNetworks;
-        this.BufferList = from.BufferList;
+        this.buffers = from.buffers;
         this.allowedBufferTypes = from.allowedBufferTypes;
         this.sortAlphabetically = from.sortAlphabetically;
         this.disableDecoration = from.disableDecoration;
@@ -196,7 +208,7 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         this.networkId = from.networkId;
         this.minimumActivity = from.minimumActivity;
         this.hideInactiveBuffers = from.hideInactiveBuffers;
-        this.RemovedBuffers = from.RemovedBuffers;
+        this.removedBuffers = from.removedBuffers;
     }
 
     @Override
@@ -204,8 +216,22 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         update(BufferViewConfigSerializer.get().fromDatastream(from));
     }
 
-    public void addBuffer(int bufferId, int position) {
-        BufferList.add(position, bufferId);
+    public void addBuffer(int bufferId, int pos) {
+        if (buffers.contains(bufferId))
+            return;
+
+        if (pos < 0)
+            pos = 0;
+        if (pos > buffers.size())
+            pos = buffers.size();
+
+        if (removedBuffers.contains(bufferId))
+            removedBuffers.remove(removedBuffers.indexOf(bufferId));
+
+        if (temporarilyRemovedBuffers.contains(bufferId))
+            temporarilyRemovedBuffers.remove(temporarilyRemovedBuffers.indexOf(bufferId));
+
+        buffers.add(pos, bufferId);
     }
 
     public void SYNC_addBuffer(int bufferId, int position) {
@@ -213,8 +239,36 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         sync("addBuffer", new Object[]{bufferId, position});
     }
 
+    public void moveBuffer(int bufferId, int pos) {
+        if (!buffers.contains(bufferId))
+            return;
+
+        if (pos < 0)
+            pos = 0;
+        if (pos >= buffers.size())
+            pos = buffers.size() - 1;
+
+        int index = buffers.indexOf(bufferId);
+        if (pos == index)
+            return;
+        if (pos > index)
+            pos--;
+
+        buffers.remove(index);
+        buffers.add(pos, bufferId);
+    }
+
+    public void SYNC_moveBuffer(int bufferId, int position) {
+        moveBuffer(bufferId, position);
+        sync("moveBuffer", new Object[]{bufferId, position});
+    }
+
     public void removeBuffer(int bufferId) {
-        if (BufferList.contains(bufferId)) BufferList.remove(BufferList.indexOf(bufferId));
+        if (buffers.contains(bufferId))
+            buffers.remove(buffers.indexOf(bufferId));
+        if (removedBuffers.contains(bufferId))
+            removedBuffers.remove(removedBuffers.indexOf(bufferId));
+        temporarilyRemovedBuffers.add(bufferId);
     }
 
     public void SYNC_removeBuffer(int bufferId) {
@@ -222,6 +276,21 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
         sync("removeBuffer", new Object[]{bufferId});
     }
 
+    public void removeBufferPermanently(int bufferId) {
+        if (buffers.contains(bufferId))
+            buffers.remove(buffers.indexOf(bufferId));
+        if (temporarilyRemovedBuffers.contains(bufferId))
+            temporarilyRemovedBuffers.remove(temporarilyRemovedBuffers.indexOf(bufferId));
+        removedBuffers.add(bufferId);
+    }
+
+    public void SYNC_removeBufferPermanently(int bufferId) {
+        removeBufferPermanently(bufferId);
+        sync("removeBufferPermanently", new Object[]{bufferId});
+    }
+
+
+
     @NonNull
     public IObservableList<ElementCallback<Integer>, Integer> getNetworkList() {
         return NetworkList;
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
index dd43c8f90888858c3b86f48b6fab8a7841471159..49406768cacd64cf24d4f37ec90b4d6745977bd3 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
@@ -1,6 +1,8 @@
 package de.kuschku.libquassel.syncables.types;
 
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
 
 import java.util.List;
 import java.util.Map;
@@ -258,8 +260,8 @@ public class Identity extends SyncableObject<Identity> {
     }
 
     @Override
-    public void init(@NonNull InitDataFunction function, @NonNull BusProvider provider, @NonNull Client client) {
-        client.addIdentity(Integer.valueOf(function.objectName), this);
+    public void init(@Nullable InitDataFunction function, @NonNull BusProvider provider, @NonNull Client client) {
+        client.addIdentity(getIdentityId(), this);
     }
 
     @Override
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
index 853a559cb580a3ecd7ebb35421d62ee9b0c3fef4..ab47a972839128e5d2759da17dd924742ed0b0c4 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
@@ -44,23 +44,32 @@ public class IrcChannel extends SyncableObject<IrcChannel> {
         this.encrypted = encrypted;
     }
 
+    @NonNull
     public Map<String, List<String>> getA_ChanModes() {
         if (ChanModes.get("A") == null) ChanModes.put("A", new HashMap<>());
         return (Map<String, List<String>>) ChanModes.get("A");
     }
 
+    @NonNull
     public Map<String, String> getB_ChanModes() {
         if (ChanModes.get("B") == null) ChanModes.put("B", new HashMap<>());
         return (Map<String, String>) ChanModes.get("B");
     }
 
+    @NonNull
     public Map<String, String> getC_ChanModes() {
         if (ChanModes.get("C") == null) ChanModes.put("C", new HashMap<>());
         return (Map<String, String>) ChanModes.get("C");
     }
 
+    @NonNull
     public Set<String> getD_ChanModes() {
-        if (ChanModes.get("D") == null) ChanModes.put("D", new HashSet<>());
+        if (ChanModes.get("D") instanceof String) {
+            List<String> list = Arrays.asList(((String) ChanModes.get("D")).split(""));
+            ChanModes.put("D", new HashSet<>(list));
+        } else if (ChanModes.get("D") == null) {
+            ChanModes.put("D", new HashSet<>());
+        }
         return (Set<String>) ChanModes.get("D");
     }
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
index 8349555234c7b0ef0b4798192f42fb74d75d74e6..bb7ed09a311eaf9b929c595694d7391a20091404 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
@@ -18,6 +18,7 @@ import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.objects.types.NetworkServer;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.serializers.NetworkSerializer;
+import de.kuschku.util.irc.IrcUserUtils;
 import de.kuschku.util.observables.ContentComparable;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
@@ -156,7 +157,7 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
     }
 
     public void addIrcUser(String sender) {
-        client.sendInitRequest("IrcUser", getObjectName() + "/" + sender);
+        client.sendInitRequest("IrcUser", getObjectName() + "/" + IrcUserUtils.getNick(sender));
     }
 
     @Nullable
@@ -543,7 +544,12 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
     }
 
     @Override
-    public boolean equalsContent(@NonNull Network other) {
+    public boolean areContentsTheSame(@NonNull Network other) {
+        return this == other;
+    }
+
+    @Override
+    public boolean areItemsTheSame(Network other) {
         return networkId == other.networkId;
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
index 700611bc39eb94b9d6266e989fbdc969e5845d94..38fb5b2e5de6658aa638a7d66c67319a0271c7dc 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
@@ -4,15 +4,14 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
-import android.support.design.widget.CoordinatorLayout;
 import android.support.design.widget.Snackbar;
-import android.support.v4.widget.NestedScrollView;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.ActionMenuView;
@@ -22,17 +21,18 @@ import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.Toolbar;
-import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ScrollView;
-import android.widget.Scroller;
 
 import com.afollestad.materialdialogs.MaterialDialog;
 import com.google.common.base.Splitter;
 import com.mikepenz.fastadapter.FastAdapter;
 import com.mikepenz.fastadapter.IExpandable;
+import com.mikepenz.fastadapter.IIdentifyable;
 import com.mikepenz.fastadapter.IItem;
 import com.mikepenz.fastadapter.adapters.ItemAdapter;
 import com.mikepenz.materialdrawer.AccountHeader;
@@ -42,9 +42,12 @@ import com.mikepenz.materialdrawer.DrawerBuilder;
 import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
 import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
 import com.mikepenz.materialdrawer.model.SecondaryDrawerItem;
-import com.sothree.slidinguppanel.ScrollableViewHelper;
 import com.sothree.slidinguppanel.SlidingUpPanelLayout;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import aspm.annotations.BooleanPreference;
@@ -59,6 +62,8 @@ import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.LagChangedEvent;
 import de.kuschku.libquassel.localtypes.Buffer;
+import de.kuschku.libquassel.localtypes.ChannelBuffer;
+import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
@@ -67,10 +72,14 @@ import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread;
 import de.kuschku.quasseldroid_ng.service.QuasselService;
 import de.kuschku.quasseldroid_ng.ui.chat.chatview.MessageAdapter;
+import de.kuschku.quasseldroid_ng.ui.chat.drawer.BufferViewConfigWrapper;
+import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkItem;
+import de.kuschku.quasseldroid_ng.ui.editor.AdvancedEditor;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.quasseldroid_ng.ui.theme.AppTheme;
 import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
 import de.kuschku.util.ServerAddress;
+import de.kuschku.util.backports.Stream;
 import de.kuschku.util.instancestateutil.Storable;
 import de.kuschku.util.instancestateutil.Store;
 import de.kuschku.util.keyboardutils.DialogKeyboardUtil;
@@ -86,8 +95,10 @@ public class ChatActivity extends AppCompatActivity {
     Toolbar toolbar;
     @Bind(R.id.sliding_layout)
     SlidingUpPanelLayout slidingLayout;
+    @Bind(R.id.sliding_layout_history)
+    SlidingUpPanelLayout slidingLayoutHistory;
 
-    @Bind(R.id.chatlineScroller)
+    @Bind(R.id.chatline_scroller)
     ScrollView chatlineScroller;
     @Bind(R.id.chatline)
     AppCompatEditText chatline;
@@ -102,8 +113,10 @@ public class ChatActivity extends AppCompatActivity {
     @Bind(R.id.messages)
     RecyclerView messages;
 
-    @Bind(R.id.amvMenu)
+    @Bind(R.id.formatting_menu)
     ActionMenuView formattingMenu;
+    @Bind(R.id.formatting_toolbar)
+    Toolbar formattingToolbar;
 
     @PreferenceWrapper(BuildConfig.APPLICATION_ID)
     public static abstract class Settings {
@@ -141,8 +154,8 @@ public class ChatActivity extends AppCompatActivity {
         }
 
         private void disconnect() {
-            if (binder != null) binder.stopBackgroundThread();
-            if (context.getProvider() != null) context.getProvider().event.unregister(this);
+            if (context.getProvider() != null)
+                context.getProvider().event.unregister(this);
             context.setProvider(null);
             context.setClient(null);
         }
@@ -155,6 +168,7 @@ public class ChatActivity extends AppCompatActivity {
     private AccountHeader accountHeader;
     private Drawer drawerLeft;
     private BufferViewConfigWrapper wrapper;
+    private AdvancedEditor editor;
 
     private ServiceConnection serviceConnection = new ServiceConnection() {
         @UiThread
@@ -162,7 +176,16 @@ public class ChatActivity extends AppCompatActivity {
             if (service instanceof QuasselService.LocalBinder) {
                 ChatActivity.this.binder = (QuasselService.LocalBinder) service;
                 if (binder.getBackgroundThread() != null) {
-                    connectToThread(binder.getBackgroundThread());
+                    ClientBackgroundThread backgroundThread = binder.getBackgroundThread();
+                    assertNotNull(backgroundThread);
+
+                    serviceInterface.disconnect();
+
+                    backgroundThread.provider.event.register(ChatActivity.this);
+                    context.setClient(backgroundThread.handler.client);
+                    context.setProvider(backgroundThread.provider);
+                    updateBufferViewConfigs();
+                    updateSubTitle();
                 }
             }
         }
@@ -176,67 +199,77 @@ public class ChatActivity extends AppCompatActivity {
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
-        context.setSettings(new WrappedSettings(this));
-        AppTheme theme = AppTheme.themeFromString(context.getSettings().theme.get());
-        setTheme(theme.themeId);
-        context.setThemeUtil(new ThemeUtil(this, theme));
+        setupContext();
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_chat);
         ButterKnife.bind(this);
         setSupportActionBar(toolbar);
 
-        connectToService();
+        Intent intent = new Intent(this, QuasselService.class);
+        startService(intent);
 
-        accountHeader = new AccountHeaderBuilder()
-                .withActivity(this)
-                .withCompactStyle(true)
-                .withHeaderBackground(R.drawable.bg)
-                .withSavedInstance(savedInstanceState)
-                .withProfileImagesVisible(false)
-                .withOnAccountHeaderListener((view, profile, current) -> {
-                    if (!current) {
-                        selectBufferViewConfig((int) profile.getIdentifier());
-                    }
-                    return true;
-                })
-                .build();
+        setupHeader(savedInstanceState);
+
+        setupDrawer(savedInstanceState);
+
+        setupEditor();
+
+        setupContent();
+
+        setupHistory();
+
+        initLoader();
+    }
+
+    private void setupContext() {
+        context.setSettings(new WrappedSettings(this));
+        AppTheme theme = AppTheme.themeFromString(context.getSettings().theme.get());
+        setTheme(theme.themeId);
+        context.setThemeUtil(new ThemeUtil(this, theme));
+    }
+
+    private void setupEditorLayout() {
+        slidingLayout.setAntiDragView(R.id.card_panel);
+        slidingLayout.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
+            @Override
+            public void onPanelSlide(View panel, float slideOffset) {
 
-        drawerLeft = new DrawerBuilder()
-                .withActivity(this)
-                .withToolbar(toolbar)
-                .withAccountHeader(accountHeader)
-                .withSavedInstance(savedInstanceState)
-                .withTranslucentStatusBar(true)
-                .build();
-        drawerLeft.addStickyFooterItem(new PrimaryDrawerItem().withIcon(R.drawable.ic_server_light).withName("(Re-)Connect").withIdentifier(-1));
-        drawerLeft.addStickyFooterItem(new SecondaryDrawerItem().withName("Settings").withIdentifier(-2));
-        drawerLeft.setOnDrawerItemClickListener((view, position, drawerItem) -> {
-            long identifier = drawerItem.getIdentifier();
-            if (identifier == -1) {
-                showConnectDialog();
-                return false;
-            } else if (identifier == -2) {
-                showThemeDialog();
-                return false;
-            } else {
-                if (((IExpandable) drawerItem).getSubItems() != null) {
-                    drawerLeft.getAdapter().toggleExpandable(position);
-                    return true;
-                } else {
-                    selectBuffer((int) drawerItem.getIdentifier());
-                    return false;
-                }
             }
-        });
 
-        getMenuInflater().inflate(R.menu.formatting,formattingMenu.getMenu());
+            @Override
+            public void onPanelCollapsed(View panel) {
+                setChatlineExpanded(false);
+            }
+
+            @Override
+            public void onPanelExpanded(View panel) {
+                setChatlineExpanded(true);
+            }
 
-        messages.setItemAnimator(new DefaultItemAnimator());
-        messages.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
-        messageAdapter = new MessageAdapter(this, context, new AutoScroller(messages));
-        messages.setAdapter(messageAdapter);
+            @Override
+            public void onPanelAnchored(View panel) {
+
+            }
+
+            @Override
+            public void onPanelHidden(View panel) {
+
+            }
+        });
+        setChatlineExpanded(slidingLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED);
+    }
+
+    private void initLoader() {
+        swipeView.setEnabled(false);
+        swipeView.setColorSchemeColors(context.getThemeUtil().res.colorPrimary);
+        swipeView.setOnRefreshListener(() -> {
+            assertNotNull(context.getClient());
+            context.getClient().getBacklogManager().requestMoreBacklog(status.bufferId, 20);
+        });
+    }
 
+    private void setupHistory() {
         FastAdapter<IItem> fastAdapter = new FastAdapter<>();
         ItemAdapter<IItem> itemAdapter = new ItemAdapter<>();
         itemAdapter.wrap(fastAdapter);
@@ -261,43 +294,175 @@ public class ChatActivity extends AppCompatActivity {
         msgHistory.setAdapter(fastAdapter);
         msgHistory.setLayoutManager(new LinearLayoutManager(this));
         msgHistory.setItemAnimator(new DefaultItemAnimator());
+    }
 
-        swipeView.setColorSchemeColors(context.getThemeUtil().res.colorPrimary);
-        swipeView.setOnRefreshListener(() -> {
-            assertNotNull(context.getClient());
-            context.getClient().getBacklogManager().requestMoreBacklog(status.bufferId, 20);
-        });
+    private void setupContent() {
+        messages.setItemAnimator(new DefaultItemAnimator());
+        messages.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
+        messageAdapter = new MessageAdapter(this, context, new AutoScroller(messages));
+        messages.setAdapter(messageAdapter);
+    }
 
+    private void setupEditor() {
+        getMenuInflater().inflate(R.menu.formatting, formattingMenu.getMenu());
+        formattingMenu.setOnMenuItemClickListener(item -> {
+            switch (item.getItemId()) {
+                case R.id.format_bold:
+                    editor.toggleBold();
+                    return true;
+                case R.id.format_italic:
+                    editor.toggleItalic();
+                    return true;
+                case R.id.format_underline:
+                    editor.toggleUnderline();
+                    return true;
+                case R.id.action_history:
+                    slidingLayoutHistory.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
+                    return true;
+                default:
+                    return false;
+            }
+        });
+        editor = new AdvancedEditor(context, chatline);
         send.setOnClickListener(view -> sendInput());
 
-        slidingLayout.setAntiDragView(R.id.card_panel);
-        slidingLayout.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
-            @Override
-            public void onPanelSlide(View panel, float slideOffset) {
+        setupEditorLayout();
+    }
 
+    private void setupDrawer(@Nullable Bundle savedInstanceState) {
+        drawerLeft = new DrawerBuilder()
+                .withActivity(this)
+                .withToolbar(toolbar)
+                .withAccountHeader(accountHeader)
+                .withSavedInstance(savedInstanceState)
+                .withTranslucentStatusBar(true)
+                .build();
+        drawerLeft.addStickyFooterItem(new PrimaryDrawerItem().withIcon(R.drawable.ic_server_light).withName("(Re-)Connect").withIdentifier(-1));
+        drawerLeft.addStickyFooterItem(new SecondaryDrawerItem().withName("Settings").withIdentifier(-2));
+        drawerLeft.setOnDrawerItemClickListener((view, position, drawerItem) -> {
+            long identifier = drawerItem.getIdentifier();
+            if (identifier == -1) {
+                showConnectDialog();
+                return false;
+            } else if (identifier == -2) {
+                showThemeDialog();
+                return false;
+            } else {
+                if (((IExpandable) drawerItem).getSubItems() != null) {
+                    drawerLeft.getAdapter().toggleExpandable(position);
+                    return true;
+                } else {
+                    selectBuffer((int) drawerItem.getIdentifier());
+                    return false;
+                }
             }
+        });
+    }
 
-            @Override
-            public void onPanelCollapsed(View panel) {
-                setChatlineExpanded(false);
-            }
+    private void setupHeader(@Nullable Bundle savedInstanceState) {
+        accountHeader = new AccountHeaderBuilder()
+                .withActivity(this)
+                .withCompactStyle(true)
+                .withHeaderBackground(R.drawable.bg)
+                .withSavedInstance(savedInstanceState)
+                .withProfileImagesVisible(false)
+                .withOnAccountHeaderListener((view, profile, current) -> {
+                    if (!current) {
+                        selectBufferViewConfig((int) profile.getIdentifier());
+                    }
+                    return true;
+                })
+                .build();
+    }
 
-            @Override
-            public void onPanelExpanded(View panel) {
-                setChatlineExpanded(true);
-            }
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.chat, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
 
-            @Override
-            public void onPanelAnchored(View panel) {
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        // Checks whether a hardware keyboard is available
+        if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
 
-            }
+        } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
 
-            @Override
-            public void onPanelHidden(View panel) {
+        }
+    }
 
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        List<Integer> filterSettings = Arrays.asList(
+                Message.Type.Join.value,
+                Message.Type.Part.value,
+                Message.Type.Quit.value,
+                Message.Type.Nick.value,
+                Message.Type.Mode.value,
+                Message.Type.Topic.value
+        );
+        int[] filterSettingsInts = new int[filterSettings.size()];
+        for (int i = 0; i < filterSettingsInts.length; i++) { filterSettingsInts[i] = filterSettings.get(i); }
+
+        switch (item.getItemId()) {
+            case R.id.action_hide_events: {
+                if (context.getClient() != null) {
+                    BacklogFilter backlogFilter = context.getClient().getBacklogManager().getFilter(status.bufferId);
+                    if (backlogFilter != null) {
+                        int oldFilters = backlogFilter.getFilters();
+                        List<Integer> oldFiltersList = new ArrayList<>();
+                        for (int type : filterSettings) {
+                            if ((type & oldFilters) != 0)
+                                oldFiltersList.add(filterSettings.indexOf(type));
+                        }
+                        Integer[] selectedIndices = oldFiltersList.toArray(new Integer[oldFiltersList.size()]);
+                        new MaterialDialog.Builder(this)
+                                .items(
+                                        "Joins",
+                                        "Parts",
+                                        "Quits",
+                                        "Nick Changes",
+                                        "Mode Changes",
+                                        "Topic Changes"
+                                )
+                                .itemsIds(filterSettingsInts)
+                                .itemsCallbackMultiChoice(
+                                        selectedIndices,
+                                        (dialog, which, text) -> false
+                                )
+                                .positiveText("Select")
+                                .negativeText("Cancel")
+                                .onPositive((dialog, which) -> {
+                                    int filters = 0x00000000;
+                                    if (dialog.getSelectedIndices() != null)
+                                    for (int i : dialog.getSelectedIndices()) {
+                                        filters |= filterSettings.get(i);
+                                    }
+                                    backlogFilter.setFilters(filters);
+                                })
+                                .buttonRippleColorAttr(R.attr.colorAccentFocus)
+                                .build()
+                                .show();
+                    }
+                }
             }
-        });
-        setChatlineExpanded(slidingLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        serviceInterface.disconnect();
+        unbindService(serviceConnection);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Intent intent = new Intent(this, QuasselService.class);
+        bindService(intent, serviceConnection, Context.BIND_IMPORTANT);
     }
 
     public void setChatlineExpanded(boolean expanded) {
@@ -354,20 +519,10 @@ public class ChatActivity extends AppCompatActivity {
         status.onSaveInstanceState(outState);
     }
 
-    private void connectToThread(@NonNull ClientBackgroundThread backgroundThread) {
-        assertNotNull(backgroundThread);
-
-        serviceInterface.disconnect();
-
-        backgroundThread.provider.event.register(this);
-        context.setClient(backgroundThread.handler.client);
-        context.setProvider(backgroundThread.provider);
-        selectBuffer(status.bufferId);
-        selectBufferViewConfig(status.bufferViewConfigId);
-        updateSubTitle();
-    }
-
     private void selectBufferViewConfig(@IntRange(from = -1) int bufferViewConfigId) {
+        status.bufferViewConfigId = bufferViewConfigId;
+        accountHeader.setActiveProfile(bufferViewConfigId, false);
+
         if (wrapper != null) wrapper.setDrawer(null);
         drawerLeft.removeAllItems();
         if (bufferViewConfigId == -1) {
@@ -381,14 +536,19 @@ public class ChatActivity extends AppCompatActivity {
 
             wrapper = new BufferViewConfigWrapper(context, viewConfig, drawerLeft);
             wrapper.updateDrawerItems();
+            String name = viewConfig.getBufferViewName();
         }
     }
 
     private void selectBuffer(@IntRange(from = -1) int bufferId) {
         if (bufferId == -1) {
+            swipeView.setEnabled(false);
+
             messageAdapter.setMessageList(MessageAdapter.emptyList());
             toolbar.setTitle(getResources().getString(R.string.app_name));
         } else {
+            swipeView.setEnabled(true);
+
             status.bufferId = bufferId;
             // Make sure we are actually connected
             ObservableSortedList<Message> list = context.getClient().getBacklogManager().getFiltered(status.bufferId);
@@ -399,12 +559,25 @@ public class ChatActivity extends AppCompatActivity {
 
             messageAdapter.setMessageList(list);
             toolbar.setTitle(buffer.getName());
+            updateNoColor(buffer, formattingMenu.getMenu());
         }
     }
 
-    private void connectToService() {
-        Intent intent = new Intent(this, QuasselService.class);
-        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+    private static void updateNoColor(Buffer buffer, Menu menu) {
+        boolean isNoColor = isNoColor(buffer);
+        menu.findItem(R.id.format_bold).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_italic).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_underline).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_paint).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_fill).setEnabled(!isNoColor);
+    }
+
+    public static boolean isNoColor(Buffer buffer) {
+        if (buffer instanceof ChannelBuffer && ((ChannelBuffer) buffer).getChannel() != null) {
+            return ((ChannelBuffer) buffer).getChannel().getD_ChanModes().contains("c");
+        } else {
+            return false;
+        }
     }
 
     private void onConnectionEstablished() {
@@ -419,8 +592,8 @@ public class ChatActivity extends AppCompatActivity {
         Buffer buffer = context.getClient().getBuffer(status.bufferId);
         assertNotNull(buffer);
 
-        CharSequence text = chatline.getText();
-        context.getClient().sendInput(buffer.getInfo(), text.toString());
+        String text = editor.toFormatString();
+        context.getClient().sendInput(buffer.getInfo(), text);
         chatline.setText("");
     }
 
@@ -447,6 +620,7 @@ public class ChatActivity extends AppCompatActivity {
     }
 
     private void updateBufferViewConfigs() {
+        assertNotNull(context.getClient().getBufferViewManager());
         Map<Integer, BufferViewConfig> bufferViews = context.getClient().getBufferViewManager().BufferViews;
         accountHeader.clear();
         for (Map.Entry<Integer, BufferViewConfig> entry : bufferViews.entrySet()) {
@@ -494,6 +668,8 @@ public class ChatActivity extends AppCompatActivity {
                 .title("Address")
                 .customView(R.layout.dialog_address, false)
                 .onPositive((dialog1, which) -> {
+                    if (binder != null && binder.getBackgroundThread() != null) binder.stopBackgroundThread();
+
                     View parent = dialog1.getCustomView();
                     AppCompatEditText hostField = (AppCompatEditText) parent.findViewById(R.id.host);
                     AppCompatEditText portField = (AppCompatEditText) parent.findViewById(R.id.port);
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
index d8a929f9ca527d77e4532360e7d69afc48221823..cb07ea5ad7138251b714d3e959fd68b3feaf88aa 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
@@ -16,6 +16,7 @@ import de.kuschku.util.annotationbind.AutoBinder;
 import de.kuschku.util.annotationbind.AutoString;
 import de.kuschku.util.irc.IrcFormatHelper;
 import de.kuschku.util.irc.IrcUserUtils;
+import de.kuschku.util.ui.MessageUtil;
 import de.kuschku.util.ui.SpanFormatter;
 import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
 
@@ -23,8 +24,6 @@ import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 @UiThread
 public class ChatMessageRenderer {
-    @NonNull
-    private final FormatStrings strings;
 
     private IrcFormatHelper helper;
     private MessageStyleContainer highlightStyle;
@@ -36,37 +35,36 @@ public class ChatMessageRenderer {
     private AppContext context;
 
     public ChatMessageRenderer(@NonNull Context ctx, @NonNull AppContext context) {
-        this.strings = new FormatStrings(ctx);
         this.context = context;
-        setTheme(context.getThemeUtil());
+        setTheme(context);
     }
 
-    public void setTheme(ThemeUtil themeUtil) {
-        this.helper = new IrcFormatHelper(themeUtil.res);
+    public void setTheme(AppContext context) {
+        this.helper = new IrcFormatHelper(context);
 
         this.highlightStyle = new MessageStyleContainer(
-                themeUtil.res.colorForegroundHighlight,
+                context.getThemeUtil().res.colorForegroundHighlight,
                 Typeface.NORMAL,
-                themeUtil.res.colorForegroundHighlight,
-                themeUtil.res.colorBackgroundHighlight
+                context.getThemeUtil().res.colorForegroundHighlight,
+                context.getThemeUtil().res.colorBackgroundHighlight
         );
         this.serverStyle = new MessageStyleContainer(
-                themeUtil.res.colorForegroundSecondary,
+                context.getThemeUtil().res.colorForegroundSecondary,
                 Typeface.ITALIC,
-                themeUtil.res.colorForegroundSecondary,
-                themeUtil.res.colorBackgroundSecondary
+                context.getThemeUtil().res.colorForegroundSecondary,
+                context.getThemeUtil().res.colorBackgroundSecondary
         );
         this.plainStyle = new MessageStyleContainer(
-                themeUtil.res.colorForeground,
+                context.getThemeUtil().res.colorForeground,
                 Typeface.NORMAL,
-                themeUtil.res.colorForegroundSecondary,
-                themeUtil.res.transparent
+                context.getThemeUtil().res.colorForegroundSecondary,
+                context.getThemeUtil().res.transparent
         );
         this.actionStyle = new MessageStyleContainer(
-                themeUtil.res.colorForegroundAction,
+                context.getThemeUtil().res.colorForegroundAction,
                 Typeface.ITALIC,
-                themeUtil.res.colorForegroundSecondary,
-                themeUtil.res.transparent
+                context.getThemeUtil().res.colorForegroundSecondary,
+                context.getThemeUtil().res.transparent
         );
     }
 
@@ -82,7 +80,7 @@ public class ChatMessageRenderer {
     private CharSequence formatNick(@NonNull String hostmask, boolean full) {
         CharSequence formattedNick = helper.formatUserNick(IrcUserUtils.getNick(hostmask));
         if (full) {
-            return strings.formatUsername(formattedNick, IrcUserUtils.getMask(hostmask));
+            return context.getThemeUtil().translations.formatUsername(formattedNick, IrcUserUtils.getMask(hostmask));
         } else {
             return formattedNick;
         }
@@ -106,7 +104,7 @@ public class ChatMessageRenderer {
     private void onBindPlain(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, plainStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(
-                strings.formatPlain(
+                context.getThemeUtil().translations.formatPlain(
                         formatNick(message.sender, false),
                         helper.formatIrcMessage(message.content)
                 )
@@ -115,7 +113,7 @@ public class ChatMessageRenderer {
 
     private void onBindNotice(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, plainStyle, highlightStyle, message.flags.Highlight);
-        holder.content.setText(strings.formatAction(
+        holder.content.setText(context.getThemeUtil().translations.formatAction(
                 formatNick(message.sender, false),
                 helper.formatIrcMessage(message.content)
         ));
@@ -124,7 +122,7 @@ public class ChatMessageRenderer {
     private void onBindAction(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, actionStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(
-                strings.formatAction(
+                context.getThemeUtil().translations.formatAction(
                         formatNick(message.sender, false),
                         helper.formatIrcMessage(message.content)
                 )
@@ -134,11 +132,11 @@ public class ChatMessageRenderer {
     private void onBindNick(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         if (message.flags.Self)
-            holder.content.setText(strings.formatNick(
+            holder.content.setText(context.getThemeUtil().translations.formatNick(
                     formatNick(message.sender, false)
             ));
         else
-            holder.content.setText(strings.formatNick(
+            holder.content.setText(context.getThemeUtil().translations.formatNick(
                     formatNick(message.sender, false),
                     helper.formatUserNick(message.content)
             ));
@@ -151,7 +149,7 @@ public class ChatMessageRenderer {
 
     private void onBindJoin(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
-        holder.content.setText(strings.formatJoin(
+        holder.content.setText(context.getThemeUtil().translations.formatJoin(
                 formatNick(message.sender),
                 getBufferName(message)
         ));
@@ -159,7 +157,7 @@ public class ChatMessageRenderer {
 
     private void onBindPart(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
-        holder.content.setText(strings.formatPart(
+        holder.content.setText(context.getThemeUtil().translations.formatPart(
                 formatNick(message.sender),
                 getBufferName(message),
                 message.content
@@ -168,7 +166,7 @@ public class ChatMessageRenderer {
 
     private void onBindQuit(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
-        holder.content.setText(strings.formatQuit(
+        holder.content.setText(context.getThemeUtil().translations.formatQuit(
                 formatNick(message.sender),
                 message.content
         ));
@@ -201,7 +199,7 @@ public class ChatMessageRenderer {
 
     private void onBindDayChange(@NonNull MessageViewHolder holder, @NonNull Message message) {
         applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
-        holder.content.setText(strings.formatDayChange(
+        holder.content.setText(context.getThemeUtil().translations.formatDayChange(
                 context.getThemeUtil().formatter.getLongDateFormatter().print(message.time)
         ));
     }
@@ -305,143 +303,4 @@ public class ChatMessageRenderer {
             this.bgColor = bgColor;
         }
     }
-
-    public static class FormatStrings {
-        @AutoString(R.string.username_hostmask)
-        public String username_hostmask;
-
-        @AutoString(R.string.message_plain)
-        public String message_plain;
-
-        @AutoString(R.string.message_join)
-        public String message_join;
-
-        @AutoString(R.string.message_part)
-        public String message_part;
-
-        @AutoString(R.string.message_part_extra)
-        public String message_part_extra;
-
-        @AutoString(R.string.message_quit)
-        public String message_quit;
-
-        @AutoString(R.string.message_quit_extra)
-        public String message_quit_extra;
-
-        @AutoString(R.string.message_kill)
-        public String message_kill;
-
-        @AutoString(R.string.message_kick)
-        public String message_kick;
-
-        @AutoString(R.string.message_kick_extra)
-        public String message_kick_extra;
-
-        @AutoString(R.string.message_mode)
-        public String message_mode;
-
-        @AutoString(R.string.message_nick_self)
-        public String message_nick_self;
-
-        @AutoString(R.string.message_nick_other)
-        public String message_nick_other;
-
-        @AutoString(R.string.message_daychange)
-        public String message_daychange;
-
-        @AutoString(R.string.message_action)
-        public String message_action;
-
-        public FormatStrings(@NonNull Context ctx) {
-            try {
-                AutoBinder.bind(this, ctx);
-            } catch (IllegalAccessException e) {
-                Log.e("ERROR", e.toString());
-                e.printStackTrace();
-            }
-        }
-
-        @NonNull
-        public CharSequence formatUsername(@NonNull CharSequence nick, @NonNull CharSequence hostmask) {
-            return SpanFormatter.format(username_hostmask, nick, hostmask);
-        }
-
-        @NonNull
-        public CharSequence formatJoin(@NonNull CharSequence user, @NonNull CharSequence channel) {
-            return SpanFormatter.format(message_join, user, channel);
-        }
-
-        @NonNull
-        public CharSequence formatPart(@NonNull CharSequence user, @NonNull CharSequence channel) {
-            return SpanFormatter.format(message_part, user, channel);
-        }
-
-        @NonNull
-        public CharSequence formatPart(@NonNull CharSequence user, @NonNull CharSequence channel, @Nullable CharSequence reason) {
-            if (reason == null || reason.length() == 0) return formatPart(user, channel);
-
-            return SpanFormatter.format(message_part_extra, user, channel, reason);
-        }
-
-        @NonNull
-        public CharSequence formatQuit(@NonNull CharSequence user) {
-            return SpanFormatter.format(message_quit, user);
-        }
-
-        @NonNull
-        public CharSequence formatQuit(@NonNull CharSequence user, @Nullable CharSequence reason) {
-            if (reason == null || reason.length() == 0) return formatQuit(user);
-
-            return SpanFormatter.format(message_quit_extra, user, reason);
-        }
-
-        @NonNull
-        public CharSequence formatKill(@NonNull CharSequence user, @NonNull CharSequence channel) {
-            return SpanFormatter.format(message_kill, user, channel);
-        }
-
-        @NonNull
-        public CharSequence formatKick(@NonNull CharSequence user, @NonNull CharSequence kicked) {
-            return SpanFormatter.format(message_kick, user, kicked);
-        }
-
-        @NonNull
-        public CharSequence formatKick(@NonNull CharSequence user, @NonNull CharSequence kicked, @Nullable CharSequence reason) {
-            if (reason == null || reason.length() == 0) return formatKick(user, kicked);
-
-            return SpanFormatter.format(message_kick_extra, user, kicked, reason);
-        }
-
-        @NonNull
-        public CharSequence formatMode(@NonNull CharSequence mode, @NonNull CharSequence user) {
-            return SpanFormatter.format(message_mode, mode, user);
-        }
-
-        @NonNull
-        public CharSequence formatNick(@NonNull CharSequence newNick) {
-            return SpanFormatter.format(message_nick_self, newNick);
-        }
-
-        @NonNull
-        public CharSequence formatNick(@NonNull CharSequence oldNick, @Nullable CharSequence newNick) {
-            if (newNick == null || newNick.length() == 0) return formatNick(oldNick);
-
-            return SpanFormatter.format(message_nick_other, oldNick, newNick);
-        }
-
-        @NonNull
-        public CharSequence formatDayChange(@NonNull CharSequence day) {
-            return SpanFormatter.format(message_daychange, day);
-        }
-
-        @NonNull
-        public CharSequence formatAction(@NonNull CharSequence user, @NonNull CharSequence channel) {
-            return SpanFormatter.format(message_action, user, channel);
-        }
-
-        @NonNull
-        public CharSequence formatPlain(@NonNull CharSequence nick, @NonNull CharSequence message) {
-            return SpanFormatter.format(message_plain, nick, message);
-        }
-    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
index 9ac11f0269e78ef959a9130b59c8a09ad8313d4b..ea259ca33b2f802eaa9d6323170379803372b653 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
@@ -16,6 +16,7 @@ import de.kuschku.libquassel.localtypes.ChannelBuffer;
 import de.kuschku.libquassel.localtypes.QueryBuffer;
 import de.kuschku.libquassel.localtypes.StatusBuffer;
 import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.util.observables.IObservable;
@@ -80,25 +81,28 @@ public class BufferItem extends SecondaryDrawerItem implements IObservable<Gener
 
     @Override
     public StringHolder getName() {
-        return new StringHolder(buffer.getName());
+        if (buffer instanceof StatusBuffer)
+            return new StringHolder(context.getThemeUtil().translations.title_status_buffer);
+        else
+            return new StringHolder(buffer.getName());
     }
 
     @Override
     public ImageHolder getIcon() {
         if (buffer instanceof ChannelBuffer) {
-            if (buffer.isActive()) {
+            if (buffer.getStatus() != BufferInfo.BufferStatus.OFFLINE) {
                 return new ImageHolder(R.drawable.ic_status_channel);
             } else {
                 return new ImageHolder(R.drawable.ic_status_channel_offline);
             }
         } else if (buffer instanceof StatusBuffer) {
-            if (buffer.isActive()) {
+            if (buffer.getStatus() != BufferInfo.BufferStatus.OFFLINE) {
                 return new ImageHolder(R.drawable.ic_status);
             } else {
                 return new ImageHolder(R.drawable.ic_status_offline);
             }
         } else {
-            if (buffer.isActive()) {
+            if (buffer.getStatus() != BufferInfo.BufferStatus.OFFLINE) {
                 return new ImageHolder(R.drawable.ic_status);
             } else {
                 return new ImageHolder(R.drawable.ic_status_offline);
@@ -106,9 +110,21 @@ public class BufferItem extends SecondaryDrawerItem implements IObservable<Gener
         }
     }
 
+    @Override
+    public boolean isIconTinted() {
+        return buffer.getStatus() == BufferInfo.BufferStatus.ONLINE;
+    }
+
     @Override
     public ColorHolder getIconColor() {
-        return super.getIconColor();
+        return buffer.getStatus() == BufferInfo.BufferStatus.ONLINE  ?
+                ColorHolder.fromColor(context.getThemeUtil().res.colorAccent) :
+                new ColorHolder();
+    }
+
+    @Override
+    public ColorHolder getDescriptionTextColor() {
+        return ColorHolder.fromColor(context.getThemeUtil().res.colorForegroundSecondary);
     }
 
     @Override
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigWrapper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
similarity index 88%
rename from app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigWrapper.java
rename to app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
index f6512fed1379403b170e0c6909c0dd624e2b4fe1..903e76bb3465a6e619dc5451ec0a86406cf4fc71 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigWrapper.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
@@ -1,14 +1,22 @@
-package de.kuschku.quasseldroid_ng.ui.chat;
+package de.kuschku.quasseldroid_ng.ui.chat.drawer;
 
+import android.util.Log;
+
+import com.mikepenz.fastadapter.IIdentifyable;
 import com.mikepenz.materialdrawer.Drawer;
 import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkItem;
+import de.kuschku.util.backports.Stream;
 import de.kuschku.util.observables.callbacks.ElementCallback;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
@@ -24,13 +32,13 @@ public class BufferViewConfigWrapper {
         }
 
         @Override
-        public boolean areContentsTheSame(NetworkItem oldItem, NetworkItem newItem) {
-            return oldItem.getNetwork().getNetworkId() == newItem.getNetwork().getNetworkId();
+        public boolean areContentsTheSame(NetworkItem item1, NetworkItem item2) {
+            return item1 == item2;
         }
 
         @Override
         public boolean areItemsTheSame(NetworkItem item1, NetworkItem item2) {
-            return item1 == item2;
+            return item1.getNetwork().getNetworkId() == item2.getNetwork().getNetworkId();
         }
     });
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
index 0ed01812c23ee3eb5ff0d4444cb3dfdb3486ba13..36706986dca3250bf03a16fb10ce7ca95e7c83e6 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
@@ -12,6 +12,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import de.kuschku.libquassel.localtypes.Buffer;
+import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
@@ -21,6 +22,11 @@ import de.kuschku.util.observables.callbacks.GeneralCallback;
 import de.kuschku.util.observables.callbacks.wrappers.GeneralCallbackWrapper;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
+import static de.kuschku.libquassel.primitives.types.BufferInfo.Type.CHANNEL;
+import static de.kuschku.libquassel.primitives.types.BufferInfo.Type.GROUP;
+import static de.kuschku.libquassel.primitives.types.BufferInfo.Type.QUERY;
+import static de.kuschku.libquassel.primitives.types.BufferInfo.Type.STATUS;
+
 public class NetworkItem extends PrimaryDrawerItem implements IObservable<GeneralCallback>, GeneralCallback {
     private final AppContext context;
     private final Network network;
@@ -35,14 +41,13 @@ public class NetworkItem extends PrimaryDrawerItem implements IObservable<Genera
         this.network = network;
         this.config = config;
 
-        for (Integer bufferId : this.config.getBufferList()) {
+        for (Integer bufferId : this.config.getBuffers()) {
             Buffer buffer = context.getClient().getBuffer(bufferId);
             if (buffer != null && buffer.getInfo().networkId == network.getNetworkId()) {
                 this.buffers.add(new BufferItem(buffer, context));
-                Log.e("Drawer", "Buffer can not be null! BufferId: "+ bufferId);
             }
         }
-        this.config.getBufferList().addCallback(new ElementCallback<Integer>() {
+        this.config.getBuffers().addCallback(new ElementCallback<Integer>() {
             @Override
             public void notifyItemInserted(Integer element) {
                 if (network.getBuffers().contains(element)) {
@@ -131,17 +136,40 @@ public class NetworkItem extends PrimaryDrawerItem implements IObservable<Genera
     class AlphabeticalComparator implements ObservableSortedList.ItemComparator<BufferItem> {
         @Override
         public int compare(BufferItem o1, BufferItem o2) {
-            return o1.getName().getText().compareTo(o2.getName().getText());
+            BufferInfo.Type type1 = o1.getBuffer().getInfo().type;
+            BufferInfo.Type type2 = o2.getBuffer().getInfo().type;
+            if (type1 == type2) {
+                return o1.getBuffer().getName().compareTo(o2.getBuffer().getName());
+            } else {
+                // Type1 is status, Type2 isn’t
+                if (type1 == STATUS) return -1;
+                // Type2 is status, Type1 isn’t
+                if (type2 == STATUS) return 1;
+                // Type1 is channel, Type2 isn’t
+                if (type1 == CHANNEL) return -1;
+                // Type2 is channel, Type1 isn’t
+                if (type2 == CHANNEL) return 1;
+                // Type1 is group, Type2 isn’t
+                if (type1 == GROUP) return -1;
+                // Type2 is group, Type1 isn’t
+                if (type2 == GROUP) return 1;
+                // Type1 is query, Type2 isn’t
+                if (type1 == QUERY) return -1;
+                // Type2 is query, Type1 isn’t
+                if (type2 == QUERY) return 1;
+                // Per default, keep order
+                return -1;
+            }
         }
 
         @Override
-        public boolean areContentsTheSame(BufferItem oldItem, BufferItem newItem) {
-            return oldItem.getBuffer().getInfo().id == newItem.getBuffer().getInfo().id;
+        public boolean areContentsTheSame(BufferItem item1, BufferItem item2) {
+            return item1 == item2;
         }
 
         @Override
         public boolean areItemsTheSame(BufferItem item1, BufferItem item2) {
-            return item1 == item2;
+            return item1.getBuffer().getInfo().id == item2.getBuffer().getInfo().id;
         }
     }
     
@@ -152,13 +180,13 @@ public class NetworkItem extends PrimaryDrawerItem implements IObservable<Genera
         }
 
         @Override
-        public boolean areContentsTheSame(BufferItem oldItem, BufferItem newItem) {
-            return oldItem.getBuffer().getInfo().id == newItem.getBuffer().getInfo().id;
+        public boolean areContentsTheSame(BufferItem item1, BufferItem item2) {
+            return item1 == item2;
         }
 
         @Override
         public boolean areItemsTheSame(BufferItem item1, BufferItem item2) {
-            return item1 == item2;
+            return item1.getBuffer().getInfo().id == item2.getBuffer().getInfo().id;
         }
     }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b1ed63848d1ae21f195d4d467bb2ed06fa75b5b
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java
@@ -0,0 +1,116 @@
+package de.kuschku.quasseldroid_ng.ui.editor;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntRange;
+import android.text.Spanned;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.UnderlineSpan;
+import android.widget.EditText;
+
+import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
+
+public class AdvancedEditor {
+    private EditText editText;
+    private FormattingHelper helper;
+
+    public AdvancedEditor(AppContext context, EditText editText) {
+        this.helper = new FormattingHelper(context);
+        this.editText = editText;
+    }
+
+    public void toggleUnderline() {
+        toggleUnderline(editText.getSelectionStart(), editText.getSelectionEnd());
+    }
+    public void toggleUnderline(int start, int end) {
+        boolean isUnderline = false;
+        for (UnderlineSpan span : editText.getText().getSpans(start, end, UnderlineSpan.class)) {
+            if ((editText.getText().getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) continue;
+
+            isUnderline = (editText.getText().getSpanStart(span) == start && editText.getText().getSpanEnd(span) == end);
+            editText.getText().removeSpan(span);
+
+            if (isUnderline) break;
+        }
+        if (!isUnderline) {
+            editText.getText().setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+    }
+
+
+    public void toggleBold() {
+        toggleBold(editText.getSelectionStart(), editText.getSelectionEnd());
+    }
+    public void toggleBold(int start, int end) {
+        boolean isBold = false;
+        for (BoldSpan span : editText.getText().getSpans(start, end, BoldSpan.class)) {
+            if ((editText.getText().getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) continue;
+
+            isBold = (editText.getText().getSpanStart(span) == start && editText.getText().getSpanEnd(span) == end);
+            editText.getText().removeSpan(span);
+
+            if (isBold) break;
+        }
+        if (!isBold) {
+            editText.getText().setSpan(new BoldSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+    }
+
+    public void toggleItalic() {
+        toggleItalic(editText.getSelectionStart(), editText.getSelectionEnd());
+    }
+    public void toggleItalic(int start, int end) {
+        boolean isItalic = false;
+        for (ItalicSpan span : editText.getText().getSpans(start, end, ItalicSpan.class)) {
+            if ((editText.getText().getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) continue;
+
+            isItalic = (editText.getText().getSpanStart(span) == start && editText.getText().getSpanEnd(span) == end);
+            editText.getText().removeSpan(span);
+
+            if (isItalic) break;
+        }
+        if (!isItalic) {
+            editText.getText().setSpan(new ItalicSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+    }
+
+    public void toggleForeground(@IntRange(from = 0, to = 15) int color) {
+        toggleForeground(editText.getSelectionStart(), editText.getSelectionEnd(), color);
+    }
+    public void toggleForeground(int start, int end, @ColorInt int color) {
+        boolean isColored = false;
+        for (ForegroundColorSpan span : editText.getText().getSpans(start, end, ForegroundColorSpan.class)) {
+            if ((editText.getText().getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) continue;
+
+            isColored = span.getForegroundColor() == color && (editText.getText().getSpanStart(span) == start && editText.getText().getSpanEnd(span) == end);
+            editText.getText().removeSpan(span);
+
+            if (isColored) break;
+        }
+        if (!isColored) {
+            editText.getText().setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+    }
+
+    public void toggleBackground(@IntRange(from = 0, to = 15) int color) {
+        toggleBackground(editText.getSelectionStart(), editText.getSelectionEnd(), color);
+    }
+    public void toggleBackground(int start, int end, @ColorInt int color) {
+        boolean isColored = false;
+        for (BackgroundColorSpan span : editText.getText().getSpans(start, end, BackgroundColorSpan.class)) {
+            if ((editText.getText().getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) continue;
+
+            isColored = span.getBackgroundColor() == color && (editText.getText().getSpanStart(span) == start && editText.getText().getSpanEnd(span) == end);
+            editText.getText().removeSpan(span);
+
+            if (isColored) break;
+        }
+        if (!isColored) {
+            editText.getText().setSpan(new BackgroundColorSpan(color), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+    }
+
+    public String toFormatString() {
+        return helper.toEscapeCodes(editText.getText());
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java
new file mode 100644
index 0000000000000000000000000000000000000000..6786a1393a9755d2803a733e258746804e371374
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java
@@ -0,0 +1,10 @@
+package de.kuschku.quasseldroid_ng.ui.editor;
+
+import android.graphics.Typeface;
+import android.text.style.StyleSpan;
+
+public class BoldSpan extends StyleSpan {
+    public BoldSpan() {
+        super(Typeface.BOLD);
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e6be5d2b284051e296e47bec5853ef140a0967d
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java
@@ -0,0 +1,114 @@
+package de.kuschku.quasseldroid_ng.ui.editor;
+
+import android.text.Spanned;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.UnderlineSpan;
+
+import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
+
+public class FormattingHelper {
+    private AppContext context;
+
+    public FormattingHelper(AppContext context) {
+        this.context = context;
+    }
+
+    public String toEscapeCodes(Spanned text) {
+        StringBuilder out = new StringBuilder();
+        withinParagraph(out, text, 0, text.length());
+        return out.toString();
+    }
+
+    public int colorToId(int color) {
+        int[] colors = context.getThemeUtil().res.mircColors;
+        for (int i = 0; i < colors.length; i++) {
+            if (colors[i] == color)
+                return i;
+        }
+        return 0;
+    }
+
+    private void withinParagraph(StringBuilder out, Spanned text,
+                                        int start, int end) {
+        int next;
+        for (int i = start; i < end; i = next) {
+            next = text.nextSpanTransition(i, end, CharacterStyle.class);
+            CharacterStyle[] style = text.getSpans(i, next,
+                    CharacterStyle.class);
+
+            boolean jump = false;
+
+            for (int j = 0; j < style.length; j++) {
+                if (jump) {
+                    jump = false;
+                } else {
+                    if ((text.getSpanFlags(style[j]) & Spanned.SPAN_COMPOSING) != 0)
+                        continue;
+
+                    if (style[j] instanceof BoldSpan) {
+                        out.append((char) 0x02);
+                    } else if (style[j] instanceof ItalicSpan) {
+                        out.append((char) 0x1D);
+                    } else if (style[j] instanceof UnderlineSpan) {
+                        out.append((char) 0x1F);
+                    } else if (style[j] instanceof ForegroundColorSpan) {
+                        int fg;
+                        int bg;
+                        fg = colorToId(((ForegroundColorSpan) style[j]).getForegroundColor());
+
+                        if ((j + 1 < style.length) && (style[j + 1] instanceof BackgroundColorSpan)) {
+                            bg = colorToId(((BackgroundColorSpan) style[j + 1]).getBackgroundColor());
+                        } else {
+                            bg = 99;
+                        }
+
+                        out.append((char) 0x03);
+                        out.append(String.format("%02d,%02d", fg, bg));
+
+                        jump = true;
+                    } else if (style[j] instanceof BackgroundColorSpan) {
+                        int fg;
+                        int bg;
+                        if ((j + 1 < style.length) && (style[j + 1] instanceof ForegroundColorSpan)) {
+                            fg = colorToId(((ForegroundColorSpan) style[j + 1]).getForegroundColor());
+                        } else {
+                            fg = 99;
+                        }
+
+                        bg = colorToId(((BackgroundColorSpan) style[j]).getBackgroundColor());
+
+                        out.append((char) 0x03);
+                        out.append(String.format("%02d,%02d", fg, bg));
+
+                        jump = true;
+                    }
+                }
+            }
+
+            out.append(text.subSequence(i,next));
+
+            for (int j = style.length - 1; j >= 0; j--) {
+                if ((text.getSpanFlags(style[j]) & Spanned.SPAN_COMPOSING) != 0)
+                    continue;
+
+                if (style[j] instanceof ForegroundColorSpan) {
+                    out.append((char) 0x03);
+                }
+                if (style[j] instanceof BackgroundColorSpan) {
+                    out.append((char) 0x03);
+                }
+                if (style[j] instanceof UnderlineSpan) {
+                    out.append((char) 0x1F);
+                }
+                if (style[j] instanceof BoldSpan) {
+                    out.append((char) 0x02);
+                }
+                if (style[j] instanceof ItalicSpan) {
+                    out.append((char) 0x1D);
+                }
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java
new file mode 100644
index 0000000000000000000000000000000000000000..93fed4cb0052bfbe8f3ceaac38ef5bb25713aa69
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java
@@ -0,0 +1,10 @@
+package de.kuschku.quasseldroid_ng.ui.editor;
+
+import android.graphics.Typeface;
+import android.text.style.StyleSpan;
+
+public class ItalicSpan extends StyleSpan {
+    public ItalicSpan() {
+        super(Typeface.ITALIC);
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
index a4295233e5a19151342544b1a5b443ba23c96ac1..fa55f28ff9d7d0bcc102ee57adecbc540cf0fa35 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
@@ -3,18 +3,23 @@ package de.kuschku.quasseldroid_ng.ui.theme;
 import android.content.Context;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 import android.support.v7.view.ContextThemeWrapper;
+import android.util.Log;
 
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.util.annotationbind.AutoBinder;
 import de.kuschku.util.annotationbind.AutoColor;
 import de.kuschku.util.annotationbind.AutoDimen;
+import de.kuschku.util.annotationbind.AutoString;
 import de.kuschku.util.ui.DateTimeFormatHelper;
+import de.kuschku.util.ui.SpanFormatter;
 
 public class ThemeUtil {
     @NonNull
     public final Colors res = new Colors();
+    public final FormatStrings translations = new FormatStrings();
     public DateTimeFormatHelper formatter;
 
     public ThemeUtil(@NonNull Context ctx) {
@@ -31,11 +36,145 @@ public class ThemeUtil {
     public void initColors(@NonNull ContextThemeWrapper wrapper) {
         try {
             AutoBinder.bind(res, wrapper);
+            AutoBinder.bind(translations, wrapper);
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         }
     }
 
+    public static class FormatStrings {
+        @AutoString(R.string.username_hostmask)
+        public String username_hostmask;
+
+        @AutoString(R.string.message_plain)
+        public String message_plain;
+
+        @AutoString(R.string.message_join)
+        public String message_join;
+
+        @AutoString(R.string.message_part)
+        public String message_part;
+
+        @AutoString(R.string.message_part_extra)
+        public String message_part_extra;
+
+        @AutoString(R.string.message_quit)
+        public String message_quit;
+
+        @AutoString(R.string.message_quit_extra)
+        public String message_quit_extra;
+
+        @AutoString(R.string.message_kill)
+        public String message_kill;
+
+        @AutoString(R.string.message_kick)
+        public String message_kick;
+
+        @AutoString(R.string.message_kick_extra)
+        public String message_kick_extra;
+
+        @AutoString(R.string.message_mode)
+        public String message_mode;
+
+        @AutoString(R.string.message_nick_self)
+        public String message_nick_self;
+
+        @AutoString(R.string.message_nick_other)
+        public String message_nick_other;
+
+        @AutoString(R.string.message_daychange)
+        public String message_daychange;
+
+        @AutoString(R.string.message_action)
+        public String message_action;
+
+        @AutoString(R.string.title_status_buffer)
+        public String title_status_buffer;
+
+        @NonNull
+        public CharSequence formatUsername(@NonNull CharSequence nick, @NonNull CharSequence hostmask) {
+            return SpanFormatter.format(username_hostmask, nick, hostmask);
+        }
+
+        @NonNull
+        public CharSequence formatJoin(@NonNull CharSequence user, @NonNull CharSequence channel) {
+            return SpanFormatter.format(message_join, user, channel);
+        }
+
+        @NonNull
+        public CharSequence formatPart(@NonNull CharSequence user, @NonNull CharSequence channel) {
+            return SpanFormatter.format(message_part, user, channel);
+        }
+
+        @NonNull
+        public CharSequence formatPart(@NonNull CharSequence user, @NonNull CharSequence channel, @Nullable CharSequence reason) {
+            if (reason == null || reason.length() == 0) return formatPart(user, channel);
+
+            return SpanFormatter.format(message_part_extra, user, channel, reason);
+        }
+
+        @NonNull
+        public CharSequence formatQuit(@NonNull CharSequence user) {
+            return SpanFormatter.format(message_quit, user);
+        }
+
+        @NonNull
+        public CharSequence formatQuit(@NonNull CharSequence user, @Nullable CharSequence reason) {
+            if (reason == null || reason.length() == 0) return formatQuit(user);
+
+            return SpanFormatter.format(message_quit_extra, user, reason);
+        }
+
+        @NonNull
+        public CharSequence formatKill(@NonNull CharSequence user, @NonNull CharSequence channel) {
+            return SpanFormatter.format(message_kill, user, channel);
+        }
+
+        @NonNull
+        public CharSequence formatKick(@NonNull CharSequence user, @NonNull CharSequence kicked) {
+            return SpanFormatter.format(message_kick, user, kicked);
+        }
+
+        @NonNull
+        public CharSequence formatKick(@NonNull CharSequence user, @NonNull CharSequence kicked, @Nullable CharSequence reason) {
+            if (reason == null || reason.length() == 0) return formatKick(user, kicked);
+
+            return SpanFormatter.format(message_kick_extra, user, kicked, reason);
+        }
+
+        @NonNull
+        public CharSequence formatMode(@NonNull CharSequence mode, @NonNull CharSequence user) {
+            return SpanFormatter.format(message_mode, mode, user);
+        }
+
+        @NonNull
+        public CharSequence formatNick(@NonNull CharSequence newNick) {
+            return SpanFormatter.format(message_nick_self, newNick);
+        }
+
+        @NonNull
+        public CharSequence formatNick(@NonNull CharSequence oldNick, @Nullable CharSequence newNick) {
+            if (newNick == null || newNick.length() == 0) return formatNick(oldNick);
+
+            return SpanFormatter.format(message_nick_other, oldNick, newNick);
+        }
+
+        @NonNull
+        public CharSequence formatDayChange(@NonNull CharSequence day) {
+            return SpanFormatter.format(message_daychange, day);
+        }
+
+        @NonNull
+        public CharSequence formatAction(@NonNull CharSequence user, @NonNull CharSequence channel) {
+            return SpanFormatter.format(message_action, user, channel);
+        }
+
+        @NonNull
+        public CharSequence formatPlain(@NonNull CharSequence nick, @NonNull CharSequence message) {
+            return SpanFormatter.format(message_plain, nick, message);
+        }
+    }
+
     public static class Colors {
         @AutoColor(android.R.color.transparent)
         @ColorInt
diff --git a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
index 077562e98146afd81ede4640ef6b61314d5a0ea9..fc05b3521aa656e4c539524b3f6b6f987a5d8ca6 100644
--- a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
+++ b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
@@ -24,7 +24,9 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
+import de.kuschku.util.ui.MessageUtil;
 
 public class IrcFormatHelper {
     @NonNull
@@ -41,16 +43,16 @@ public class IrcFormatHelper {
     private static final Pattern channelPattern = Pattern.compile("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b", Pattern.CASE_INSENSITIVE);
 
     @NonNull
-    private final ThemeUtil.Colors colors;
+    private final AppContext context;
 
-    public IrcFormatHelper(@NonNull ThemeUtil.Colors colors) {
-        this.colors = colors;
+    public IrcFormatHelper(@NonNull AppContext context) {
+        this.context = context;
     }
 
     @NonNull
     public CharSequence formatUserNick(@NonNull String nick) {
         int colorIndex = IrcUserUtils.getSenderColor(nick);
-        int color = colors.senderColors[colorIndex];
+        int color = context.getThemeUtil().res.senderColors[colorIndex];
 
         SpannableString str = new SpannableString(nick);
         str.setSpan(new ForegroundColorSpan(color), 0, nick.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
@@ -62,7 +64,7 @@ public class IrcFormatHelper {
     public CharSequence formatIrcMessage(@NonNull String message) {
         List<FutureClickableSpan> spans = new LinkedList<>();
 
-        SpannableString str = new SpannableString(message);
+        SpannableString str = new SpannableString(MessageUtil.parseStyleCodes(context.getThemeUtil(), message, context.getSettings().mircColors.get()));
         Matcher urlMatcher = urlPattern.matcher(str);
         while (urlMatcher.find()) {
             spans.add(new FutureClickableSpan(new CustomURLSpan(urlMatcher.toString()), urlMatcher.start(), urlMatcher.end()));
diff --git a/app/src/main/java/de/kuschku/util/observables/ContentComparable.java b/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
index 6295bc245bdf911abb2b6b9332775a3f92cb096b..952ca7feabe7e9ffabf5e7e7372f6103e96f2552 100644
--- a/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
+++ b/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
@@ -1,5 +1,6 @@
 package de.kuschku.util.observables;
 
 public interface ContentComparable<T extends ContentComparable<T>> extends Comparable<T> {
-    boolean equalsContent(T other);
+    boolean areItemsTheSame(T other);
+    boolean areContentsTheSame(T other);
 }
diff --git a/app/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java b/app/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
index bf571f8adc78918435b7d7f9f2010f293d902e8a..7c66e49b9bba1aaa0867c5f29c5e4f6f9d5ffc6e 100644
--- a/app/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
+++ b/app/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
@@ -24,12 +24,12 @@ public class ObservableComparableSortedList<T extends ContentComparable<T>> exte
 
         @Override
         public boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem) {
-            return oldItem.equalsContent(newItem);
+            return oldItem.areContentsTheSame(newItem);
         }
 
         @Override
         public boolean areItemsTheSame(@NonNull T item1, @NonNull T item2) {
-            return item1.equals(item2);
+            return item1.areItemsTheSame(item2);
         }
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
index b6bab5cd4c3c8faee32491c99a85bf9ac4ecd74f..33a0de54b3aa9250cb96d0a6586c99367ea02b9c 100644
--- a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
+++ b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
@@ -7,6 +7,7 @@ import android.support.v7.util.SortedList;
 import com.afollestad.materialdialogs.MaterialDialog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -195,7 +196,11 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
     @NonNull
     @Override
     public Object[] toArray() {
-        throw new MaterialDialog.NotImplementedException("Not implemented");
+        Object[] array = new Object[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
     }
 
     @NonNull
@@ -216,6 +221,11 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
         boolean areItemsTheSame(T item1, T item2);
     }
 
+    @Override
+    public String toString() {
+        return Arrays.toString(toArray());
+    }
+
     class Callback extends SortedList.Callback<T> {
         @Override
         public int compare(T o1, T o2) {
@@ -284,12 +294,12 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
 
         @Override
         public boolean hasNext() {
-            return list.size() > position + 1;
+            return position < list.size();
         }
 
         @Override
         public boolean hasPrevious() {
-            return false;
+            return position >= 0;
         }
 
         @Override
diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml
index 0879d3b13ad04b2be86252a8a55daa038536c77d..8237f72712c3080e2f8cf1d0af1da9f99446f970 100644
--- a/app/src/main/res/layout/activity_chat.xml
+++ b/app/src/main/res/layout/activity_chat.xml
@@ -13,7 +13,7 @@
         android:layout_height="match_parent"
         android:gravity="bottom"
         android:orientation="vertical"
-        app:umanoScrollableView="@+id/chatlineScroller"
+        app:umanoScrollableView="@+id/chatline_scroller"
         app:umanoPanelHeight="?attr/actionBarSize"
         app:umanoShadowHeight="4dp">
 
diff --git a/app/src/main/res/layout/slider_main.xml b/app/src/main/res/layout/slider_main.xml
index c9acedcfed02f2dd4a194458b961b13d166f5e34..f3c374a7445cdaf6561207770a17b60f943c2ff5 100644
--- a/app/src/main/res/layout/slider_main.xml
+++ b/app/src/main/res/layout/slider_main.xml
@@ -1,79 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
 <com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:sothree="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/sliding_layout_history"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
-    xmlns:app="http://schemas.android.com/tools"
     android:background="?attr/colorBackgroundCard"
     android:clickable="false"
     android:focusable="true"
     android:gravity="bottom"
-    sothree:umanoFadeColor="#00000000"
+    sothree:umanoFadeColor="?attr/colorBackgroundCard"
     sothree:umanoOverlay="true"
-    sothree:umanoPanelHeight="72dip"
+    sothree:umanoPanelHeight="@dimen/message_history_panel_height"
     sothree:umanoScrollableView="@+id/msg_history"
     sothree:umanoShadowHeight="0.0dip">
 
 
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:paddingBottom="72dp">
-
-        <ScrollView
-            android:id="@+id/chatlineScroller"
-            android:layout_width="0dip"
-            android:layout_height="match_parent"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentStart="true"
-            android:layout_toLeftOf="@+id/send"
-            android:layout_toStartOf="@+id/send"
-            android:layout_alignParentTop="true"
-            android:layout_above="@+id/formatting_toolbar">
-
-            <android.support.v7.widget.AppCompatEditText
-                android:id="@+id/chatline"
-                android:layout_width="match_parent"
-                android:layout_height="?attr/actionBarSize"
-                android:background="@android:color/transparent"
-                android:gravity="top"
-                android:hint="@string/message_placeholder"
-                android:inputType="textCapSentences|textShortMessage|textAutoCorrect"
-                android:paddingBottom="17dp"
-                android:paddingLeft="20dp"
-                android:paddingRight="20dp"
-                android:paddingTop="17dp"
-                android:textSize="16sp" />
-
-        </ScrollView>
-
-        <android.support.v7.widget.AppCompatImageButton
-            android:id="@+id/send"
-            style="?attr/buttonStyleSmall"
-            android:layout_width="56dp"
-            android:layout_height="?attr/actionBarSize"
-            android:layout_alignParentEnd="true"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentTop="true"
-            android:layout_gravity="top"
-            android:background="?attr/selectableItemBackgroundBorderless"
-            android:padding="12dp"
-            android:src="@drawable/ic_send"
-            android:tint="?attr/colorAccent" />
-
-        <android.support.v7.widget.Toolbar
-            android:id="@+id/formatting_toolbar"
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentStart="true">
-            <android.support.v7.widget.ActionMenuView
-                android:id="@+id/amvMenu"
-                android:layout_width="wrap_content"
-                android:layout_height="?attr/actionBarSize"/>
-        </android.support.v7.widget.Toolbar>
-    </RelativeLayout>
+    <include layout="@layout/widget_editor" />
 
     <FrameLayout
         android:id="@+id/card_panel"
@@ -100,7 +42,7 @@
                     android:gravity="center_vertical"
                     android:paddingLeft="16.0dip"
                     android:paddingRight="16.0dip"
-                    android:text="Message History"
+                    android:text="@string/message_history"
                     android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                     android:textColor="?attr/colorForegroundSecondary" />
 
diff --git a/app/src/main/res/layout/widget_editor.xml b/app/src/main/res/layout/widget_editor.xml
new file mode 100644
index 0000000000000000000000000000000000000000..58e98cb7971df752d8c609d9a1cb830fc8d776be
--- /dev/null
+++ b/app/src/main/res/layout/widget_editor.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/message_history_panel_height">
+
+    <ScrollView
+        android:id="@+id/chatline_scroller"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        android:layout_toLeftOf="@+id/send"
+        android:layout_toStartOf="@+id/send"
+        android:layout_alignParentTop="true"
+        android:layout_above="@+id/formatting_toolbar_container">
+
+        <android.support.v7.widget.AppCompatEditText
+            android:id="@+id/chatline"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="@android:color/transparent"
+            android:gravity="top"
+            android:hint="@string/message_placeholder"
+            android:inputType="textCapSentences|textShortMessage|textAutoCorrect"
+            android:paddingBottom="17dp"
+            android:paddingLeft="20dp"
+            android:paddingRight="20dp"
+            android:paddingTop="17dp"
+            android:textSize="16sp" />
+
+    </ScrollView>
+
+    <android.support.v7.widget.AppCompatImageButton
+        android:id="@+id/send"
+        style="?attr/buttonStyleSmall"
+        android:layout_width="56dp"
+        android:layout_height="?attr/actionBarSize"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_gravity="top"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:padding="12dp"
+        android:src="@drawable/ic_send"
+        android:tint="?attr/colorAccent" />
+
+    <android.support.design.widget.AppBarLayout
+        android:id="@+id/formatting_toolbar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        android:background="?attr/colorBackgroundCard">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/formatting_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize">
+            <android.support.v7.widget.ActionMenuView
+                android:id="@+id/formatting_menu"
+                android:layout_width="wrap_content"
+                android:layout_height="?attr/actionBarSize"/>
+        </android.support.v7.widget.Toolbar>
+    </android.support.design.widget.AppBarLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/menu/chat.xml b/app/src/main/res/menu/chat.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9f5ae5a47ebbfb36f6c1c6005607eca2a6ae44e8
--- /dev/null
+++ b/app/src/main/res/menu/chat.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item
+        android:id="@+id/action_hide_events"
+        android:icon="@android:drawable/ic_menu_sort_by_size"
+        android:orderInCategory="100"
+        android:title="Hide events"
+        app:showAsAction="ifRoom" />
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:title="@string/action_settings"
+        app:showAsAction="never" />
+</menu>
diff --git a/app/src/main/res/menu/formatting.xml b/app/src/main/res/menu/formatting.xml
index 1e05f147149efe417403fecdf1ffe8d76d165779..d2319d915b021a317a40e8e016a334f27a07e90f 100644
--- a/app/src/main/res/menu/formatting.xml
+++ b/app/src/main/res/menu/formatting.xml
@@ -26,4 +26,9 @@
         android:icon="?attr/ic_format_fill"
         app:showAsAction="always"
         android:title="@string/format_fill" />
+    <item
+        android:id="@+id/action_history"
+        android:icon="?attr/ic_history"
+        app:showAsAction="always"
+        android:title="@string/message_history" />
 </menu>
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 55dbf1cf9d6ace5123c836d2196b75aed73e3dc2..c782848f43e16b0644712311f8e42dfcfad1e588 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -65,6 +65,7 @@
     <attr name="ic_format_underline" format="reference" />
     <attr name="ic_format_paint" format="reference" />
     <attr name="ic_format_fill" format="reference" />
+    <attr name="ic_history" format="reference" />
 
     <attr name="cardStyle" format="reference" />
 </resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index a90926a73282e23ea4201b5a8b70b28c25d2ef6c..879ca0f5ae2eec17001921f5fb58d4f1ab574a72 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -11,4 +11,5 @@
     <!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp:
          https://developer.android.com/design/patterns/navigation-drawer.html -->
     <dimen name="navigation_drawer_width">240dp</dimen>
+    <dimen name="message_history_panel_height">0dip</dimen>
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 840c11d7553aaf4f53d21f7b913c5977b65d3e22..a6a06bdd2f44601433e61a61a087fef66c3105f9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,9 +1,6 @@
 <resources>
     <string name="app_name">QuasselDroid</string>
     <string name="action_settings">Settings</string>
-    <string name="default_port">4242</string>
-
-    <string name="username_hostmask">%1$s (%2$s)</string>
 
     <!-- Message format strings -->
     <string name="message_plain">%1$s: %2$s</string>
@@ -30,109 +27,18 @@
 
     <string name="message_action">* %1$s %2$s</string>
 
-
-    <string name="super_long_string">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.     Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.     Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.     Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.     Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.     At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.     Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.     Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.     Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.     Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.     Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo</string>
-    <string name="open_drawer">open</string>
-    <string name="close_drawer">close</string>
+    <!-- Editor UI -->
     <string name="message_placeholder">Write a message…</string>
-
-    <!-- Preferences -->
-    <string name="preference_theme">preference_theme</string>
-    <string name="preference_full_hostmask">preference_full_hostmask</string>
-    <string name="title_activity_scrolling">ScrollingActivity</string>
-    <string name="large_text">
-        "Material is the metaphor.\n\n"
-
-        "A material metaphor is the unifying theory of a rationalized space and a system of motion."
-        "The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
-        "technologically advanced and open to imagination and magic.\n"
-        "Surfaces and edges of the material provide visual cues that are grounded in reality. The "
-        "use of familiar tactile attributes helps users quickly understand affordances. Yet the "
-        "flexibility of the material creates new affordances that supercede those in the physical "
-        "world, without breaking the rules of physics.\n"
-        "The fundamentals of light, surface, and movement are key to conveying how objects move, "
-        "interact, and exist in space and in relation to each other. Realistic lighting shows "
-        "seams, divides space, and indicates moving parts.\n\n"
-
-        "Bold, graphic, intentional.\n\n"
-
-        "The foundational elements of print based design typography, grids, space, scale, color, "
-        "and use of imagery guide visual treatments. These elements do far more than please the "
-        "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
-        "imagery, large scale typography, and intentional white space create a bold and graphic "
-        "interface that immerse the user in the experience.\n"
-        "An emphasis on user actions makes core functionality immediately apparent and provides "
-        "waypoints for the user.\n\n"
-
-        "Motion provides meaning.\n\n"
-
-        "Motion respects and reinforces the user as the prime mover. Primary user actions are "
-        "inflection points that initiate motion, transforming the whole design.\n"
-        "All action takes place in a single environment. Objects are presented to the user without "
-        "breaking the continuity of experience even as they transform and reorganize.\n"
-        "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
-        "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
-
-        "3D world.\n\n"
-
-        "The material environment is a 3D space, which means all objects have x, y, and z "
-        "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
-        "positive z-axis extending towards the viewer. Every sheet of material occupies a single "
-        "position along the z-axis and has a standard 1dp thickness.\n"
-        "On the web, the z-axis is used for layering and not for perspective. The 3D world is "
-        "emulated by manipulating the y-axis.\n\n"
-
-        "Light and shadow.\n\n"
-
-        "Within the material environment, virtual lights illuminate the scene. Key lights create "
-        "directional shadows, while ambient light creates soft shadows from all angles.\n"
-        "Shadows in the material environment are cast by these two light sources. In Android "
-        "development, shadows occur when light sources are blocked by sheets of material at "
-        "various positions along the z-axis. On the web, shadows are depicted by manipulating the "
-        "y-axis only. The following example shows the card with a height of 6dp.\n\n"
-
-        "Resting elevation.\n\n"
-
-        "All material objects, regardless of size, have a resting elevation, or default elevation "
-        "that does not change. If an object changes elevation, it should return to its resting "
-        "elevation as soon as possible.\n\n"
-
-        "Component elevations.\n\n"
-
-        "The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
-        "does not vary from 6dp in one app to 16dp in another app).\n"
-        "Components may have different resting elevations across platforms, depending on the depth "
-        "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
-
-        "Responsive elevation and dynamic elevation offsets.\n\n"
-
-        "Some component types have responsive elevation, meaning they change elevation in response "
-        "to user input (e.g., normal, focused, and pressed) or system events. These elevation "
-        "changes are consistently implemented using dynamic elevation offsets.\n"
-        "Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
-        "to the component’s resting state. They ensure that elevation changes are consistent "
-        "across actions and component types. For example, all components that lift on press have "
-        "the same elevation change relative to their resting elevation.\n"
-        "Once the input event is completed or cancelled, the component will return to its resting "
-        "elevation.\n\n"
-
-        "Avoiding elevation interference.\n\n"
-
-        "Components with responsive elevations may encounter other components as they move between "
-        "their resting elevations and dynamic elevation offsets. Because material cannot pass "
-        "through other material, components avoid interfering with one another any number of ways, "
-        "whether on a per component basis or using the entire app layout.\n"
-        "On a component level, components can move or be removed before they cause interference. "
-        "For example, a floating action button (FAB) can disappear or move off screen before a "
-        "user picks up a card, or it can move if a snackbar appears.\n"
-        "On the layout level, design your app layout to minimize opportunities for interference. "
-        "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
-        "when a user tries to pick up one of cards.\n\n"
-    </string>
-    <string name="title_activity_demo">DemoActivity</string>
+    <string name="message_history">Message History</string>
     <string name="format_bold">Bold</string>
     <string name="format_italic">Italic</string>
     <string name="format_underline">Underline</string>
-    <string name="format_color">Text color</string>
-    <string name="format_fill">Background color</string>
+    <string name="format_color">Text Color</string>
+    <string name="format_fill">Background Color</string>
+
+    <!-- Misc -->
+    <string name="username_hostmask">%1$s (%2$s)</string>
+    <string name="title_status_buffer">Status Buffer</string>
+    <string name="open_drawer">open</string>
+    <string name="close_drawer">close</string>
 </resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 537c5f9432aba362b1d6169c56e2375ed3014416..4e8d94402016e64808582db4a6a733efd3ac1361 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -16,6 +16,7 @@
         <item name="ic_format_underline">@drawable/ic_format_underline_dark</item>
         <item name="ic_format_paint">@drawable/ic_format_paint_dark</item>
         <item name="ic_format_fill">@drawable/ic_format_fill_dark</item>
+        <item name="ic_history">@drawable/ic_history_dark</item>
 
         <item name="cardStyle">@style/CardView.Dark</item>
     </style>
@@ -35,6 +36,7 @@
         <item name="ic_format_underline">@drawable/ic_format_underline_light</item>
         <item name="ic_format_paint">@drawable/ic_format_paint_light</item>
         <item name="ic_format_fill">@drawable/ic_format_fill_light</item>
+        <item name="ic_history">@drawable/ic_history_light</item>
 
         <item name="cardStyle">@style/CardView.Light</item>
     </style>