diff --git a/QuasselDroidNG/build.gradle b/QuasselDroidNG/build.gradle
index 934d869e4f4c32b627e9a5639bbdfbe791cef6db..3b4284bde9e54b5fe57e7e5e044ce9ff3c54b01d 100644
--- a/QuasselDroidNG/build.gradle
+++ b/QuasselDroidNG/build.gradle
@@ -20,7 +20,7 @@ android {
             minifyEnabled true
             shrinkResources true
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-            
+
             applicationVariants.all { variant ->
                 variant.outputs.each { output ->
                     output.outputFile = new File(output.outputFile.parent, output.outputFile.name.replace(".apk", "-" + defaultConfig.versionName + "-build" + versionCode + ".apk"))
@@ -46,13 +46,25 @@ android {
     }
 }
 
-if(project.hasProperty("Android.signing")
+if (project.hasProperty("Android.signing")
         && new File((String) project.property("Android.signing") + ".gradle").exists()) {
     apply from: project.property("Android.signing") + ".gradle";
 }
 
 dependencies {
     testCompile 'junit:junit:4.12'
+    compile('com.mikepenz:materialdrawer:5.0.0.fastAdapter.b5-SNAPSHOT@aar') {
+        transitive = true
+    }
+    compile('com.mikepenz:fastadapter:0.4.2-SNAPSHOT@aar') {
+        transitive = true
+    }
+    compile('com.github.afollestad.material-dialogs:core:0.8.5.3@aar') {
+        transitive = true
+    }
+    compile('com.github.afollestad.material-dialogs:commons:0.8.5.3@aar') {
+        transitive = true
+    }
     compile 'com.android.support:appcompat-v7:23.1.1'
     compile 'com.android.support:design:23.1.1'
     compile 'com.jakewharton:butterknife:7.0.1'
@@ -61,11 +73,8 @@ dependencies {
     compile 'org.joda:joda-convert:1.8'
     compile 'de.greenrobot:eventbus:2.4.0'
     compile 'com.bignerdranch.android:expandablerecyclerview:2.0.4'
-    compile('com.mikepenz:materialdrawer:5.0.0.fastAdapter.b5-SNAPSHOT@aar') {
-        transitive = true
-    }
-    compile('com.mikepenz:fastadapter:0.4.2-SNAPSHOT@aar') {
-        transitive = true
-    }
     compile 'com.sothree.slidinguppanel:library:3.2.1'
+    compile 'com.android.support:support-v4:23.1.1'
+    compile 'com.android.support:recyclerview-v7:23.1.1'
+    compile 'com.android.support:cardview-v7:23.1.1'
 }
diff --git a/QuasselDroidNG/proguard-rules.pro b/QuasselDroidNG/proguard-rules.pro
index 45dc58a590cb45b0e593be5df3121c9fa250adc1..78c26c21dc92c46a29ceab489426fb18ab9511af 100644
--- a/QuasselDroidNG/proguard-rules.pro
+++ b/QuasselDroidNG/proguard-rules.pro
@@ -15,3 +15,50 @@
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
+
+-dontobfuscate
+
+-dontwarn javax.annotation.**
+-dontwarn javax.lang.model.**
+-dontwarn javax.tools.**
+-dontwarn com.google.j2objc.annotations.**
+-dontwarn java.lang.ClassValue
+-dontwarn sun.misc.Unsafe
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+-dontwarn javax.annotation.processing.ProcessingEnvironment
+
+-keepclasseswithmembernames class **.libquassel.** {
+    <methods>;
+}
+
+-keepclassmembers class **.libquassel.** {
+    <methods>;
+}
+
+
+#########################################
+## Bufferknife                         ##
+#########################################
+-keep class butterknife.** { *; }
+-dontwarn butterknife.internal.**
+-keep class **$$ViewBinder { *; }
+
+-keepclasseswithmembernames class * {
+    @butterknife.* <fields>;
+}
+
+-keepclasseswithmembernames class * {
+    @butterknife.* <methods>;
+}
+
+
+#########################################
+## EventBus                            ##
+#########################################
+-keepclassmembers class ** {
+    public void onEvent*(***);
+}
+
+-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
+    <init>(java.lang.Throwable);
+}
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/AndroidManifest.xml b/QuasselDroidNG/src/main/AndroidManifest.xml
index cff36a31b9a42b6a722a398b9e6806b09528cc2d..fe33a4b07bcfcb6f3a8c89f8a84b4f43a14b72e4 100644
--- a/QuasselDroidNG/src/main/AndroidManifest.xml
+++ b/QuasselDroidNG/src/main/AndroidManifest.xml
@@ -8,19 +8,29 @@
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:supportsRtl="true">
-        <service android:name=".QuasselService" />
+        android:supportsRtl="true"
+        android:theme="@style/Theme.AppCompat.Light">
+        <service android:name=".service.QuasselService" />
 
         <activity
-            android:name=".ui.MainActivity"
+            android:name=".ui.chat.ChatActivity"
             android:label="@string/app_name"
-            android:launchMode="singleTop">
+            android:launchMode="singleTop"
+            android:theme="@style/Quassel">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".ui.coresetup.CoreSetupActivity"
+            android:label="@string/title_activity_core_setup"
+            android:parentActivityName=".ui.chat.ChatActivity">
+            <meta-data
+                android:name="android.support.PARENT_ACTIVITY"
+                android:value=".ui.chat.MainActivity" />
+        </activity>
     </application>
 
 </manifest>
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/Client.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/Client.java
index 70bb59b03ef6ef3dd2e09ffe2c173675e2058cf5..23ea46ad2ffd5ba6b3fabf90b99b3928032f83dc 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/Client.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/Client.java
@@ -23,13 +23,17 @@ import de.kuschku.libquassel.syncables.types.BufferSyncer;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
 import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
-import de.kuschku.util.backports.Optional;
-import de.kuschku.util.backports.Optionals;
+import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkWrapper;
 import de.kuschku.util.backports.Stream;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.lists.IObservableList;
+import de.kuschku.util.observables.lists.ObservableComparableSortedList;
+import de.kuschku.util.observables.lists.ObservableSortedList;
 
 
 public class Client {
     private final Map<Integer, Network> networks = new HashMap<>();
+    private final IObservableList<UICallback, NetworkWrapper> networkList = new ObservableComparableSortedList<>(NetworkWrapper.class);
     private final Map<Integer, Buffer> buffers = new HashMap<>();
     private final List<String> initDataQueue = new ArrayList<>();
     private final BacklogManager backlogManager;
@@ -202,4 +206,8 @@ public class Client {
         this.connectionStatus = connectionStatus;
         busProvider.sendEvent(new ConnectionChangeEvent(connectionStatus));
     }
+
+    public IObservableList<UICallback, NetworkWrapper> getNetworkList() {
+        return networkList;
+    }
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/CoreConnection.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/CoreConnection.java
index 3db6d26a0a3cf87f56c589708f3d88c6aa724760..bc51ea9685aa81f1a13526b7c97512ed1805b1a4 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/CoreConnection.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/CoreConnection.java
@@ -13,8 +13,6 @@ import java.util.Date;
 import java.util.Locale;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
@@ -27,7 +25,7 @@ import de.kuschku.libquassel.primitives.types.Protocol;
 import de.kuschku.libquassel.protocols.DatastreamPeer;
 import de.kuschku.libquassel.protocols.LegacyPeer;
 import de.kuschku.libquassel.protocols.RemotePeer;
-import de.kuschku.quasseldroid_ng.util.ServerAddress;
+import de.kuschku.util.ServerAddress;
 import de.kuschku.util.niohelpers.WrappedChannel;
 
 import static de.kuschku.libquassel.primitives.QMetaType.Type.UInt;
@@ -65,7 +63,7 @@ public class CoreConnection {
      * This method opens a socket to the specified address and starts the connection process.
      *
      * @throws IOException
-     * @param supportsKeepAlive
+     * @param supportsKeepAlive If the connection may use keepAlive
      */
     public void open(boolean supportsKeepAlive) throws IOException {
         // Intialize socket
@@ -214,6 +212,7 @@ public class CoreConnection {
                 }
             } catch (SocketException e) {
                 Log.e("libquassel", "Socket closed while reading");
+                client.setConnectionStatus(ConnectionChangeEvent.Status.DISCONNECTED);
             } catch (Exception e) {
                 busProvider.sendEvent(new GeneralErrorEvent(e));
             }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
index 17eb85768c3a71954055f471c54cb30296297876..f8dbd417a25f82db34f292206276f3b6075ab5c0 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java
@@ -7,8 +7,8 @@ import java.util.List;
 
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
-import de.kuschku.util.observablelists.AutoScroller;
-import de.kuschku.util.observablelists.ObservableSortedList;
+import de.kuschku.util.observables.AutoScroller;
+import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public abstract class BacklogManager extends SyncableObject {
     public abstract void requestBacklog(int bufferId, int from, int to, int count, int extra);
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
index 0360f62bf287d7751a73954aa5c5650936036709..a1c5b2e54544c083dbe7f2cf5ff25f637b939b79 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java
@@ -15,9 +15,10 @@ 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.observablelists.AutoScroller;
-import de.kuschku.util.observablelists.ObservableSortedList;
-import de.kuschku.util.observablelists.RecyclerViewAdapterCallback;
+import de.kuschku.util.observables.AutoScroller;
+import de.kuschku.util.observables.lists.ObservableComparableSortedList;
+import de.kuschku.util.observables.lists.ObservableSortedList;
+import de.kuschku.util.observables.callbacks.wrappers.AdapterUICallbackWrapper;
 
 public class SimpleBacklogManager extends BacklogManager {
     SparseArray<ObservableSortedList<Message>> backlogs = new SparseArray<>();
@@ -49,10 +50,7 @@ public class SimpleBacklogManager extends BacklogManager {
     }
 
     public void bind(int bufferId, @Nullable RecyclerView.Adapter adapter, AutoScroller scroller) {
-        if (adapter == null)
-            get(bufferId).setCallback(null);
-        else
-            get(bufferId).setCallback(new RecyclerViewAdapterCallback(adapter, scroller));
+        get(bufferId).addCallback(new AdapterUICallbackWrapper(adapter, scroller));
     }
 
     @Override
@@ -68,7 +66,7 @@ public class SimpleBacklogManager extends BacklogManager {
 
     public ObservableSortedList<Message> get(int bufferId) {
         if (backlogs.get(bufferId) == null)
-            backlogs.put(bufferId, new ObservableSortedList<>(Message.class, true));
+            backlogs.put(bufferId, new ObservableComparableSortedList<>(Message.class, true));
 
         return backlogs.get(bufferId);
     }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/BufferViewManagerChangedEvent.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/events/BufferViewManagerChangedEvent.java
similarity index 88%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/BufferViewManagerChangedEvent.java
rename to QuasselDroidNG/src/main/java/de/kuschku/libquassel/events/BufferViewManagerChangedEvent.java
index 32a2a562fd34556bec3a3a04bab31b0234a8d703..9ce676ca9a1e0cc2fd7cf91f324c4e7165c810ca 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/BufferViewManagerChangedEvent.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/events/BufferViewManagerChangedEvent.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng;
+package de.kuschku.libquassel.events;
 
 public class BufferViewManagerChangedEvent {
     public final int id;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
index 1e0701a9499781d25a7159933fbaeff1e67db93e..c2ed7cb3f9af919d95a27cd3ec5500730586624d 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/Buffer.java
@@ -8,6 +8,4 @@ public interface Buffer {
     BufferInfo getInfo();
 
     String getName();
-
-    IDrawerItem getDrawerElement();
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
index 96d986a2142cb039f84f3e0b15af0681e256af35..3645fd36365e0298b02e8512ba31b7c35d9a5c04 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java
@@ -1,25 +1,17 @@
 package de.kuschku.libquassel.localtypes;
 
-import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
-
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.IrcChannel;
-import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem;
 
 public class ChannelBuffer implements Buffer {
     private final BufferInfo info;
     private IrcChannel channel;
-    private IDrawerItem drawerElement = new BufferDrawerItem(this);
 
     public ChannelBuffer(BufferInfo info, IrcChannel channel) {
         this.info = info;
         this.channel = channel;
     }
 
-    public IDrawerItem getDrawerElement() {
-        return drawerElement;
-    }
-
     @Override
     public BufferInfo getInfo() {
         return info;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
index 676aa546adab463a24324b16c5efb91ee3c08a4c..6619ddd5c7dd3a3cea72bb7dbe07fa738bd5432d 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
@@ -1,25 +1,17 @@
 package de.kuschku.libquassel.localtypes;
 
-import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
-
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.IrcUser;
-import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem;
 
 public class QueryBuffer implements Buffer {
     private final BufferInfo info;
     private IrcUser user;
-    private IDrawerItem drawerElement = new BufferDrawerItem(this);
 
     public QueryBuffer(BufferInfo info, IrcUser user) {
         this.info = info;
         this.user = user;
     }
 
-    public IDrawerItem getDrawerElement() {
-        return drawerElement;
-    }
-
     @Override
     public BufferInfo getInfo() {
         return info;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
index 80740254066e409dc7e3c6ef41b37dcc7052bda5..8aecd2096870fffbe1194cdd8f45cc2c78691608 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java
@@ -1,25 +1,17 @@
 package de.kuschku.libquassel.localtypes;
 
-import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
-
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.types.Network;
-import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem;
 
 public class StatusBuffer implements Buffer {
     private final BufferInfo info;
     private final Network network;
-    private IDrawerItem drawerElement = new BufferDrawerItem(this);
 
     public StatusBuffer(BufferInfo info, Network network) {
         this.info = info;
         this.network = network;
     }
 
-    public IDrawerItem getDrawerElement() {
-        return drawerElement;
-    }
-
     @Override
     public BufferInfo getInfo() {
         return info;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/message/Message.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/message/Message.java
index e222d3b78fb13c97e8e19144345e216f1b7516ed..f4fdce423b44f7cd504da7fd10621d3e5894e137 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/message/Message.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/message/Message.java
@@ -8,7 +8,7 @@ import java.io.Serializable;
 import java.util.Comparator;
 
 import de.kuschku.libquassel.primitives.types.BufferInfo;
-import de.kuschku.util.observablelists.ContentComparable;
+import de.kuschku.util.observables.ContentComparable;
 
 public class Message implements ContentComparable<Message> {
     public final int messageId;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
index 32816648d438725dfb8c7f739f8e6e3ae371ae8d..2112caeb862000e61c21f820fb5d2959ef70d934 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/objects/serializers/StringObjectMapSerializer.java
@@ -1,5 +1,7 @@
 package de.kuschku.libquassel.objects.serializers;
 
+import android.util.Log;
+
 import java.util.HashMap;
 import java.util.Map;
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
index f582ef782c8c5b3267a0fcd1a39f21eb98eee949..3873b9e0229a0bbe2a5ac5320d9b6ab5b27c870d 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/IrcChannelSerializer.java
@@ -1,5 +1,7 @@
 package de.kuschku.libquassel.syncables.serializers;
 
+import android.util.Log;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -42,7 +44,7 @@ public class IrcChannelSerializer implements ObjectSerializer<IrcChannel> {
                 (String) map.get("topic").data,
                 (String) map.get("password").data,
                 StringObjectMapSerializer.<String>get().fromLegacy(((QVariant<Map<String, QVariant>>) map.get("UserModes")).data),
-                (Map<String, Object>) map.get("ChanModes").data,
+                StringObjectMapSerializer.get().fromLegacy((Map<String, QVariant>) map.get("ChanModes").data),
                 (boolean) map.get("encrypted").data
         );
     }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
index c41bf16da42807f3d9035933f2e81b1efdc9f068..0beb3ccb8770681dbd21fafc1d5623d647fa2920 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
@@ -123,8 +123,8 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
                         (String) channels.get("name").data.get(i),
                         (String) channels.get("topic").data.get(i),
                         (String) channels.get("password").data.get(i),
-                        (Map<String, String>) channels.get("UserModes").data.get(i),
-                        (Map<String, Object>) channels.get("ChanModes").data.get(i),
+                        StringObjectMapSerializer.<String>get().fromLegacy((Map<String, QVariant>) channels.get("UserModes").data.get(i)),
+                        StringObjectMapSerializer.get().fromLegacy((Map<String, QVariant>) channels.get("ChanModes").data.get(i)),
                         (boolean) channels.get("encrypted").data.get(i)
                 ));
             }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
index 2e6e0f2fe5ae7bc9a0fa80501d269f057443bf35..92a3f8ed40da45b9f19885648d9dabed9853d06b 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
@@ -1,16 +1,19 @@
 package de.kuschku.libquassel.syncables.types;
 
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import java.util.Map;
 
 import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
+import de.kuschku.util.observables.ContentComparable;
+import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public class BufferSyncer extends SyncableObject {
-    public final SparseArray<Integer> LastSeenMsg = new SparseArray<>();
-    public final SparseArray<Integer> MarkerLines = new SparseArray<>();
+    private final SparseIntArray LastSeenMsg = new SparseIntArray();
+    private final SparseIntArray MarkerLines = new SparseIntArray();
 
     Client client;
 
@@ -47,4 +50,12 @@ public class BufferSyncer extends SyncableObject {
     public void setMarkerLine(int bufferId, int msgId) {
         MarkerLines.put(bufferId, msgId);
     }
+
+    public int getLastSeenMsg(int bufferId) {
+        return LastSeenMsg.get(bufferId, -1);
+    }
+
+    public int getMarkerLine(int bufferId) {
+        return MarkerLines.get(bufferId, -1);
+    }
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
index 5cc17b730cc7d8d69a8874e6f8e8140d67d3c5fa..39aa571046cf6d431aa848bd236333b39111654a 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
@@ -1,19 +1,20 @@
 package de.kuschku.libquassel.syncables.types;
 
-import android.util.Log;
-
 import java.util.List;
 
 import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
+import de.kuschku.libquassel.events.BufferViewManagerChangedEvent;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
-import de.kuschku.quasseldroid_ng.BufferViewManagerChangedEvent;
+import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.lists.IObservableList;
+import de.kuschku.util.observables.lists.ObservableElementList;
 
 public class BufferViewConfig extends SyncableObject {
     String bufferViewName;
     List<Integer> TemporarilyRemovedBuffers;
     boolean hideInactiveNetworks;
-    List<Integer> BufferList;
+    IObservableList<ElementCallback<Integer>, Integer> BufferList;
     int allowedBufferTypes;
     boolean sortAlphabetically;
     boolean disableDecoration;
@@ -27,7 +28,7 @@ public class BufferViewConfig extends SyncableObject {
         this.bufferViewName = bufferViewName;
         TemporarilyRemovedBuffers = temporarilyRemovedBuffers;
         this.hideInactiveNetworks = hideInactiveNetworks;
-        BufferList = bufferList;
+        BufferList = new ObservableElementList<>(bufferList);
         this.allowedBufferTypes = allowedBufferTypes;
         this.sortAlphabetically = sortAlphabetically;
         this.disableDecoration = disableDecoration;
@@ -62,14 +63,18 @@ public class BufferViewConfig extends SyncableObject {
         this.hideInactiveNetworks = hideInactiveNetworks;
     }
 
-    public List<Integer> getBufferList() {
+    public IObservableList<ElementCallback<Integer>, Integer> getBufferList() {
         return BufferList;
     }
 
-    public void setBufferList(List<Integer> bufferList) {
+    public void setBufferList(IObservableList<ElementCallback<Integer>, Integer> bufferList) {
         BufferList = bufferList;
     }
 
+    public void setBufferList(List<Integer> bufferList) {
+        BufferList = new ObservableElementList<>(bufferList);
+    }
+
     public int getAllowedBufferTypes() {
         return allowedBufferTypes;
     }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
index 13fe475c4acee637684dba3b711ec7a18a92cfa1..109ca187e191490db0ee6dbb42a62565cfb963ca 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
@@ -1,5 +1,7 @@
 package de.kuschku.libquassel.syncables.types;
 
+import android.util.Log;
+
 import java.util.List;
 import java.util.Map;
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
index 37765e277338873771fec027d0344634271703f2..7fbf22bff667325fb53a38fd1493a0ae723072ce 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
@@ -128,8 +128,9 @@ public class IrcUser extends SyncableObject {
     }
 
     public void renameObject(String objectName) {
+        // TODO: Check if this is designed well
         String nick = objectName.split("/")[1];
-        network.renameUser(this.nick, nick);
+        network.renameUser(this.getObjectName(), nick);
         for (String channelName : channels) {
             IrcChannel channel = network.getChannels().get(channelName);
             channel.renameUser(this.nick, nick);
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/Network.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
index a08e3d417a1027d789f73dee8f5e4dda2acd3e8d..e756e797cd16c7abf8d849068733749f9a488f41 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
@@ -1,5 +1,7 @@
 package de.kuschku.libquassel.syncables.types;
 
+import android.support.annotation.NonNull;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -14,8 +16,9 @@ import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.functions.types.InitRequestFunction;
 import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.objects.types.NetworkServer;
+import de.kuschku.util.observables.ContentComparable;
 
-public class Network extends SyncableObject {
+public class Network extends SyncableObject implements ContentComparable<Network> {
     private Map<String, IrcUser> users;
     private Map<String, IrcChannel> channels;
 
@@ -433,6 +436,16 @@ public class Network extends SyncableObject {
         client.putNetwork(this);
     }
 
+    @Override
+    public boolean equalsContent(Network other) {
+        return networkId == other.networkId;
+    }
+
+    @Override
+    public int compareTo(@NonNull Network another) {
+        return networkId - another.networkId;
+    }
+
     public static class IrcMode {
         public final int rank;
         public final String prefix;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
index 318a2e2e7732c51da2f3e585d52b6fc74698ae0d..fe81fcb0c8c0b256fc98867916f54f80bfe0c59a 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
@@ -6,6 +6,7 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.functions.types.SyncFunction;
+import de.kuschku.util.observables.ContentComparable;
 
 public abstract class SyncableObject {
     protected BusProvider provider;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
similarity index 82%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
index 981a3e4354e58e8b1af7c04cd41111081e2715b5..5e3432723a9c024154daf1aa7d831e435865920d 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng;
+package de.kuschku.quasseldroid_ng.service;
 
 import java.io.IOException;
 
@@ -8,12 +8,12 @@ import de.kuschku.libquassel.CoreConnection;
 import de.kuschku.libquassel.ProtocolHandler;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.protocols.RemotePeer;
-import de.kuschku.quasseldroid_ng.util.CompatibilityUtils;
-import de.kuschku.quasseldroid_ng.util.ServerAddress;
+import de.kuschku.util.ui.CompatibilityUtils;
+import de.kuschku.util.ServerAddress;
 
 public class ClientBackgroundThread implements Runnable {
     public static final ClientData CLIENT_DATA = new ClientData(
-            new ClientData.FeatureFlags(false, true),
+            new ClientData.FeatureFlags(false, CompatibilityUtils.deviceSupportsCompression()),
             new int[]{RemotePeer.DATASTREAM, RemotePeer.LEGACY},
             "QuasselDroid-ng 0.1 | libquassel 0.2",
             RemotePeer.PROTOCOL_VERSION_LEGACY
@@ -34,7 +34,7 @@ public class ClientBackgroundThread implements Runnable {
     @Override
     public void run() {
         try {
-            connection.open(!CompatibilityUtils.isChromiumDevice());
+            connection.open(CompatibilityUtils.deviceSupportsKeepAlive());
         } catch (IOException e) {
             provider.sendEvent(new GeneralErrorEvent(e));
         }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
similarity index 92%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
index 22dda9ddab96e95a22cf8672bc73fda5f28f5e5a..2b48c615cdf818b203cb210e8f2a067c94f43006 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng;
+package de.kuschku.quasseldroid_ng.service;
 
 import android.app.Service;
 import android.content.Intent;
@@ -6,7 +6,7 @@ import android.os.Binder;
 import android.os.IBinder;
 
 import de.kuschku.libquassel.BusProvider;
-import de.kuschku.quasseldroid_ng.util.ServerAddress;
+import de.kuschku.util.ServerAddress;
 
 public class QuasselService extends Service {
     private final IBinder binder = new LocalBinder();
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java
deleted file mode 100644
index 2df5843fe2ce808f9a5cfb83fd32c1d47cddeb1a..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.kuschku.quasseldroid_ng.ui;
-
-import com.mikepenz.materialdrawer.holder.ImageHolder;
-import com.mikepenz.materialdrawer.holder.StringHolder;
-import com.mikepenz.materialdrawer.model.SecondaryDrawerItem;
-
-import de.kuschku.libquassel.localtypes.Buffer;
-import de.kuschku.libquassel.localtypes.ChannelBuffer;
-import de.kuschku.quasseldroid_ng.R;
-
-public class BufferDrawerItem extends SecondaryDrawerItem {
-    final Buffer buffer;
-
-    public BufferDrawerItem(Buffer buffer) {
-        this.buffer = buffer;
-    }
-
-    @Override
-    public StringHolder getName() {
-        return new StringHolder(buffer.getName());
-    }
-
-    @Override
-    public ImageHolder getIcon() {
-        if (buffer instanceof ChannelBuffer)
-            return new ImageHolder(R.drawable.ic_status_channel);
-        else
-            return new ImageHolder(R.drawable.ic_status);
-    }
-
-    @Override
-    public int getIdentifier() {
-        return buffer.getInfo().id;
-    }
-}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java
deleted file mode 100644
index 4368fc4dfc5c4175a6da0f9047fde9ea0b3f0b5e..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package de.kuschku.quasseldroid_ng.ui;
-
-import com.mikepenz.materialdrawer.holder.StringHolder;
-import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
-import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
-
-import java.util.List;
-import java.util.Set;
-
-import de.kuschku.libquassel.localtypes.Buffer;
-import de.kuschku.libquassel.syncables.types.Network;
-import de.kuschku.util.backports.Stream;
-
-public class NetworkDrawerItem extends PrimaryDrawerItem {
-    final Network network;
-    final Set<Buffer> buffers;
-
-    public NetworkDrawerItem(Network network, Set<Buffer> buffers) {
-        this.network = network;
-        this.buffers = buffers;
-    }
-
-    @Override
-    public List<IDrawerItem> getSubItems() {
-        return new Stream<>(buffers).map(Buffer::getDrawerElement).list();
-    }
-
-    @Override
-    public StringHolder getName() {
-        return new StringHolder(network.getNetworkName());
-    }
-
-    @Override
-    public int getIdentifier() {
-        return network.getNetworkId() * Short.MAX_VALUE;
-    }
-}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/Themes.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/Themes.java
new file mode 100644
index 0000000000000000000000000000000000000000..950a3ae1703c668d8174b14e611eb174d984f301
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/Themes.java
@@ -0,0 +1,16 @@
+package de.kuschku.quasseldroid_ng.ui;
+
+import android.support.annotation.StyleRes;
+
+import de.kuschku.quasseldroid_ng.R;
+
+public enum Themes {
+    QUASSEL(R.style.Quassel),
+    MATERIAL_DARK(R.style.Material_Dark),
+    MATERIAL_LIGHT(R.style.Material_Light);
+
+    public final int themeId;
+    Themes(@StyleRes int themeId) {
+        this.themeId = themeId;
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..844e39aca3b1e66bc95552686abf05c2e1476c2b
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
@@ -0,0 +1,163 @@
+package de.kuschku.quasseldroid_ng.ui.chat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+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.NavigationView;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.AppCompatEditText;
+import android.support.v7.widget.AppCompatImageButton;
+import android.support.v7.widget.AppCompatSpinner;
+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.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.RelativeLayout;
+
+import com.mikepenz.fastadapter.FastAdapter;
+import com.mikepenz.materialize.util.UIUtils;
+import com.sothree.slidinguppanel.SlidingUpPanelLayout;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import de.kuschku.quasseldroid_ng.BuildConfig;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread;
+import de.kuschku.quasseldroid_ng.service.QuasselService;
+import de.kuschku.util.DrawerUtils;
+import de.kuschku.util.instancestateutil.Storable;
+import de.kuschku.util.instancestateutil.Store;
+
+@UiThread
+public class ChatActivity extends AppCompatActivity {
+    @Bind(R.id.drawer_left)
+    DrawerLayout drawerLeft;
+
+    @Bind(R.id.navigation_left)
+    NavigationView navigationLeft;
+
+    @Bind(R.id.toolbar)
+    Toolbar toolbar;
+
+    @Bind(R.id.sliding_layout)
+    SlidingUpPanelLayout slidingLayout;
+
+    @Bind(R.id.chatline)
+    AppCompatEditText chatline;
+
+    @Bind(R.id.send)
+    AppCompatImageButton send;
+
+    @Bind(R.id.msg_history)
+    RecyclerView msgHistory;
+
+    @Bind(R.id.swipe_view)
+    SwipeRefreshLayout swipeView;
+
+    @Bind(R.id.messages)
+    RecyclerView messages;
+
+    @Bind(R.id.navigation_header_container)
+    RelativeLayout navigationHeaderContainer;
+
+    @Bind(R.id.buffer_view_spinner)
+    AppCompatSpinner bufferViewSpinner;
+
+    private QuasselService.LocalBinder binder;
+    private ClientBackgroundThread backgroundThread;
+
+    private SharedPreferences preferences;
+
+    private Status status = new Status();
+    private static class Status extends Storable {
+        @Store public int bufferId = -1;
+        @Store public int bufferViewConfigId = -1;
+    }
+
+    private ServiceConnection serviceConnection = new ServiceConnection() {
+        @UiThread
+        public void onServiceConnected(@NonNull ComponentName cn, @NonNull IBinder service) {
+            if (service instanceof QuasselService.LocalBinder) {
+                ChatActivity.this.binder = (QuasselService.LocalBinder) service;
+                if (binder.getBackgroundThread() != null) {
+                    connectToThread(binder.getBackgroundThread());
+                }
+            }
+        }
+
+        @UiThread
+        public void onServiceDisconnected(@NonNull ComponentName cn) {
+            backgroundThread = null;
+            binder = null;
+        }
+    };
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        preferences = getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE);
+        setTheme(R.style.Quassel);
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        ButterKnife.bind(this);
+        setSupportActionBar(toolbar);
+
+        DrawerUtils.initDrawer(this, drawerLeft, toolbar, R.string.open_drawer, R.string.close_drawer);
+
+        ViewGroup.LayoutParams lp = navigationHeaderContainer.getLayoutParams();
+        lp.height = lp.height + UIUtils.getStatusBarHeight(this);
+        navigationHeaderContainer.setLayoutParams(lp);
+
+        messages.setLayoutManager(new LinearLayoutManager(this));
+        messages.setItemAnimator(new DefaultItemAnimator());
+        messages.setAdapter(new FastAdapter<>());
+
+        msgHistory.setLayoutManager(new LinearLayoutManager(this));
+        msgHistory.setItemAnimator(new DefaultItemAnimator());
+        msgHistory.setAdapter(new FastAdapter<>());
+
+        bufferViewSpinner.setAdapter(new ArrayAdapter<>(this, R.layout.md_simplelist_item, android.R.id.title, new String[]{"All Chats", "Queries", "Highlights"}));
+        bufferViewSpinner.setPopupBackgroundResource(R.drawable.popup_background_material);
+        bufferViewSpinner.setSelection(0);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        status.onRestoreInstanceState(savedInstanceState);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        status.onSaveInstanceState(outState);
+    }
+
+    private void connectToThread(@NonNull ClientBackgroundThread backgroundThread) {
+        if (this.backgroundThread != null) backgroundThread.provider.event.unregister(this);
+
+        this.backgroundThread = backgroundThread;
+        backgroundThread.provider.event.register(this);
+        selectBuffer(status.bufferId);
+        selectBufferViewConfig(status.bufferViewConfigId);
+    }
+
+    private void selectBufferViewConfig(@IntRange(from=-1) int bufferViewConfigId) {
+
+    }
+
+    private void selectBuffer(@IntRange(from=-1) int bufferId) {
+
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
similarity index 69%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
index bb9376ec38be5014687b3d7af3e0823cb73d3d41..f8f452b52de4a6d6b98ddf6661db2df889f5e6fe 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng.ui;
+package de.kuschku.quasseldroid_ng.ui.chat;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -9,22 +9,26 @@ import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.support.annotation.NonNull;
 import android.support.design.widget.Snackbar;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.AppCompatImageButton;
+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.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.EditText;
 import android.widget.Toast;
 
-import com.google.common.collect.Sets;
+import com.bignerdranch.expandablerecyclerview.Adapter.ExpandableRecyclerAdapter;
+import com.bignerdranch.expandablerecyclerview.Model.ParentListItem;
 import com.mikepenz.fastadapter.FastAdapter;
-import com.mikepenz.fastadapter.ICollapsible;
 import com.mikepenz.fastadapter.adapters.ItemAdapter;
 import com.mikepenz.materialdrawer.AccountHeader;
 import com.mikepenz.materialdrawer.AccountHeaderBuilder;
@@ -32,6 +36,7 @@ import com.mikepenz.materialdrawer.Drawer;
 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.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
 import com.mikepenz.materialdrawer.model.interfaces.IProfile;
 import com.mikepenz.materialdrawer.util.KeyboardUtil;
@@ -39,7 +44,6 @@ import com.sothree.slidinguppanel.SlidingUpPanelLayout;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Map;
 
 import butterknife.Bind;
@@ -57,15 +61,23 @@ import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.objects.types.ClientLogin;
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
-import de.kuschku.libquassel.syncables.types.Network;
-import de.kuschku.quasseldroid_ng.BufferViewManagerChangedEvent;
+import de.kuschku.libquassel.events.BufferViewManagerChangedEvent;
 import de.kuschku.quasseldroid_ng.BuildConfig;
-import de.kuschku.quasseldroid_ng.QuasselService;
+import de.kuschku.quasseldroid_ng.service.QuasselService;
 import de.kuschku.quasseldroid_ng.R;
-import de.kuschku.quasseldroid_ng.util.CompatibilityUtils;
-import de.kuschku.quasseldroid_ng.util.ServerAddress;
-import de.kuschku.util.observablelists.AutoScroller;
-import de.kuschku.util.backports.Stream;
+import de.kuschku.quasseldroid_ng.ui.chat.chatview.MessageAdapter;
+import de.kuschku.quasseldroid_ng.ui.chat.drawer.BufferWrapper;
+import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkWrapper;
+import de.kuschku.quasseldroid_ng.ui.coresetup.CoreSetupActivity;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.lists.IObservableList;
+import de.kuschku.util.observables.callbacks.UIChildCallback;
+import de.kuschku.util.observables.callbacks.UIParentCallback;
+import de.kuschku.util.ui.CompatibilityUtils;
+import de.kuschku.util.ServerAddress;
+import de.kuschku.util.ui.parcelableUtil.ClassLoaderUtils;
+import de.kuschku.util.ui.parcelableUtil.StorageBackendParcelable;
+import de.kuschku.util.observables.AutoScroller;
 
 public class MainActivity extends AppCompatActivity {
     private static final String BUFFER_ID = "BUFFER_ID";
@@ -88,10 +100,10 @@ public class MainActivity extends AppCompatActivity {
     @Bind(R.id.send)
     AppCompatImageButton send;
 
-    @Bind(R.id.swipeview)
+    @Bind(R.id.swipe_view)
     SwipeRefreshLayout swipeView;
 
-    @Bind(R.id.sliding_layout)
+    //@Bind(R.id.sliding_layout)
     SlidingUpPanelLayout slidingLayout;
 
     @Bind(R.id.msg_history)
@@ -182,69 +194,28 @@ public class MainActivity extends AppCompatActivity {
                 .withSavedInstance(savedInstanceState)
                 .withCompactStyle(true)
                 .withProfileImagesVisible(false)
-                // TODO: REWRITE THIS
                 .withOnAccountHeaderListener((view, profile, current) -> {
                     switchBufferView(profile.getIdentifier());
                     return true;
                 })
                 .build();
 
+        RecyclerView rView = new RecyclerView(this);
+        rView.setLayoutManager(new LinearLayoutManager(this));
+        rView.setItemAnimator(new DefaultItemAnimator());
+        rView.setAdapter(new MyAdapter(handler.getClient().getNetworkList()));
+
         drawer = new DrawerBuilder()
                 .withActivity(this)
                 .withToolbar(toolbar)
                 .withAccountHeader(header)
-                // TODO: REWRITE THIS
-                .withOnDrawerItemClickListener((view, position, drawerItem) -> {
-                    if (drawerItem != null) {
-                        if (position == -1) {
-                            binder.stopBackgroundThread();
-                            View coreview = View.inflate(this, R.layout.core_dialog, null);
-                            EditText hostname = ((EditText) coreview.findViewById(R.id.server));
-                            EditText port = ((EditText) coreview.findViewById(R.id.port));
-
-                            hostname.setText(pref.getString(KEY_HOST, ""));
-                            port.setText(String.valueOf(pref.getInt(KEY_PORT, 4242)));
-                            new AlertDialog.Builder(this)
-                                    .setView(coreview)
-                                    .setPositiveButton("Connect", (dialog, which) -> {
-                                        if (provider != null) provider.event.unregister(this);
-                                        binder.stopBackgroundThread();
-                                        provider = new BusProvider();
-                                        provider.event.register(this);
-
-                                        String value_hostname = hostname.getText().toString().trim();
-                                        Integer value_port = Integer.valueOf(port.getText().toString().trim());
-
-                                        SharedPreferences.Editor edit = pref.edit();
-                                        edit.putString(KEY_HOST, value_hostname);
-                                        edit.putInt(KEY_PORT, value_port);
-                                        edit.commit();
-
-                                        binder.startBackgroundThread(provider, new ServerAddress(value_hostname, value_port));
-                                        handler = binder.getBackgroundThread().handler;
-                                    })
-                                    .setNegativeButton("Cancel", (dialog, which) -> {
-                                    })
-                                    .setTitle("Connect")
-                                    .show();
-                            return false;
-                        } else {
-                            if (((ICollapsible) drawerItem).getSubItems() != null) {
-                                drawer.getAdapter().toggleCollapsible(position);
-                            } else {
-                                switchBuffer(drawer.getAdapter().getItem(position).getIdentifier());
-                            }
-                        }
-                        return true;
-                    }
-                    return false;
-                })
                 .withSavedInstance(savedInstanceState)
                 .withShowDrawerOnFirstLaunch(true)
+                .withCustomView(rView)
                 .build();
 
         // TODO: REWRITE THIS
-        if (CompatibilityUtils.isChromiumDevice()) {
+        if (CompatibilityUtils.deviceSupportsKeepAlive()) {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                 getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary, getTheme()));
             } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -255,10 +226,41 @@ public class MainActivity extends AppCompatActivity {
         drawer.addStickyFooterItem(new PrimaryDrawerItem().withName("(Re-)Connect").withIcon(R.drawable.ic_server_light));
     }
 
+    private void reconnect() {
+        binder.stopBackgroundThread();
+        View coreview = View.inflate(this, R.layout.core_dialog, null);
+        EditText hostname = ((EditText) coreview.findViewById(R.id.server));
+        EditText port = ((EditText) coreview.findViewById(R.id.port));
+
+        hostname.setText(pref.getString(KEY_HOST, ""));
+        port.setText(String.valueOf(pref.getInt(KEY_PORT, 4242)));
+        new AlertDialog.Builder(this)
+                .setView(coreview)
+                .setPositiveButton("Connect", (dialog, which) -> {
+                    if (provider != null) provider.event.unregister(this);
+                    binder.stopBackgroundThread();
+                    provider = new BusProvider();
+                    provider.event.register(this);
+
+                    String value_hostname = hostname.getText().toString().trim();
+                    Integer value_port = Integer.valueOf(port.getText().toString().trim());
+
+                    SharedPreferences.Editor edit = pref.edit();
+                    edit.putString(KEY_HOST, value_hostname);
+                    edit.putInt(KEY_PORT, value_port);
+                    edit.commit();
+
+                    binder.startBackgroundThread(provider, new ServerAddress(value_hostname, value_port));
+                    handler = binder.getBackgroundThread().handler;
+                })
+                .setNegativeButton("Cancel", (dialog, which) -> {
+                })
+                .setTitle("Connect")
+                .show();
+    }
+
     private void initSendCallbacks() {
-        send.setOnClickListener(view -> {
-            sendInput();
-        });
+        send.setOnClickListener(view -> sendInput());
         chatline.setOnKeyListener((v, keyCode, event) -> {
             if (event.getAction() == KeyEvent.ACTION_DOWN && (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER))
                 sendInput();
@@ -305,6 +307,7 @@ public class MainActivity extends AppCompatActivity {
 
     // TODO: USE OBSERVABLELIST FOR THIS
     private void switchBufferView(int bufferviewId) {
+        /*
         this.bufferViewId = bufferviewId;
         adapter.setClient(handler.getClient());
         BufferViewConfig config = handler.getClient().getBufferViewManager().BufferViews.get(bufferviewId);
@@ -336,6 +339,7 @@ public class MainActivity extends AppCompatActivity {
         for (int i = 0; i < drawer.getAdapter().getItemCount(); i++) {
             drawer.getAdapter().open(i);
         }
+        */
     }
 
 
@@ -397,41 +401,56 @@ public class MainActivity extends AppCompatActivity {
                     switchBufferView(header.getProfiles().get(0).getIdentifier());
                 break;
             case LOGIN_REQUIRED:
-                View loginview = View.inflate(this, R.layout.login_dialog, null);
-                EditText username = ((EditText) loginview.findViewById(R.id.username));
-                EditText password = ((EditText) loginview.findViewById(R.id.password));
-                username.setText(pref.getString(KEY_USER, ""));
-                password.setText(pref.getString(KEY_PASS, ""));
-                new AlertDialog.Builder(this)
-                        .setView(loginview)
-                        .setPositiveButton("Login", (dialog, which) -> {
-                            String value_user = username.getText().toString();
-                            String value_pass = password.getText().toString();
-
-                            SharedPreferences.Editor edit = pref.edit();
-                            edit.putString(KEY_USER, value_user);
-                            edit.putString(KEY_PASS, value_pass);
-                            edit.commit();
-
-                            binder.getBackgroundThread().provider.dispatch(new HandshakeFunction(new ClientLogin(
-                                    value_user,
-                                    value_pass
-                            )));
-                        })
-                        .setNegativeButton("Cancel", (dialog, which) -> {
-                            binder.stopBackgroundThread();
-                        })
-                        .setTitle("Login")
-                        .show();
+                showLoginDialog();
+                break;
+            case CORE_SETUP_REQUIRED:
+                showCoreSetupDialog();
                 break;
         }
 
         toolbar.setSubtitle(event.status.name());
     }
 
+    private void showCoreSetupDialog() {
+        ClassLoaderUtils.loader = Thread.currentThread().getContextClassLoader();
+        Intent doCoreSetup = new Intent(this, CoreSetupActivity.class);
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList("backends", StorageBackendParcelable.wrap(handler.getClient().getCore().StorageBackends));
+        doCoreSetup.putExtra("bundle", bundle);
+        startActivityForResult(doCoreSetup, 0);
+    }
+
+    private void showLoginDialog() {
+        View loginview = View.inflate(this, R.layout.login_dialog, null);
+        EditText username = ((EditText) loginview.findViewById(R.id.username));
+        EditText password = ((EditText) loginview.findViewById(R.id.password));
+        username.setText(pref.getString(KEY_USER, ""));
+        password.setText(pref.getString(KEY_PASS, ""));
+        new AlertDialog.Builder(this)
+                .setView(loginview)
+                .setPositiveButton("Login", (dialog, which) -> {
+                    String value_user = username.getText().toString();
+                    String value_pass = password.getText().toString();
+
+                    SharedPreferences.Editor edit = pref.edit();
+                    edit.putString(KEY_USER, value_user);
+                    edit.putString(KEY_PASS, value_pass);
+                    edit.commit();
+
+                    binder.getBackgroundThread().provider.dispatch(new HandshakeFunction(new ClientLogin(
+                            value_user,
+                            value_pass
+                    )));
+                })
+                .setNegativeButton("Cancel", (dialog, which) -> {
+                    binder.stopBackgroundThread();
+                })
+                .setTitle("Login")
+                .show();
+    }
+
     // TODO: USE OBSERVABLE LIST FOR THIS SHIT
     public void onEventMainThread(BufferViewManagerChangedEvent event) {
-        IProfile activeProfile = header.getActiveProfile();
         switch (event.action) {
             case ADD:
                 BufferViewConfig add = handler.getClient().getBufferViewManager().BufferViews.get(event.id);
@@ -478,4 +497,33 @@ public class MainActivity extends AppCompatActivity {
             Snackbar.make(messages, event.toString(), Snackbar.LENGTH_LONG).show();
         }
     }
+
+    private class MyAdapter extends ExpandableRecyclerAdapter<NetworkWrapper.ViewHolder, BufferWrapper.ViewHolder> implements UIParentCallback, UIChildCallback {
+        int primaryRes = new PrimaryDrawerItem().getLayoutRes();
+        int secondaryRes = new SecondaryDrawerItem().getLayoutRes();
+
+        public MyAdapter(@NonNull IObservableList<UICallback, NetworkWrapper> wrapperList) {
+            super(wrapperList);
+        }
+
+        @Override
+        public NetworkWrapper.ViewHolder onCreateParentViewHolder(ViewGroup parentViewGroup) {
+            return new NetworkWrapper.ViewHolder(LayoutInflater.from(parentViewGroup.getContext()).inflate(primaryRes, parentViewGroup, false));
+        }
+
+        @Override
+        public BufferWrapper.ViewHolder onCreateChildViewHolder(ViewGroup childViewGroup) {
+            return new BufferWrapper.ViewHolder(LayoutInflater.from(childViewGroup.getContext()).inflate(secondaryRes, childViewGroup, false));
+        }
+
+        @Override
+        public void onBindParentViewHolder(NetworkWrapper.ViewHolder parentViewHolder, int position, ParentListItem parentListItem) {
+            parentViewHolder.bind((NetworkWrapper) parentListItem);
+        }
+
+        @Override
+        public void onBindChildViewHolder(BufferWrapper.ViewHolder childViewHolder, int position, Object childListItem) {
+            childViewHolder.bind((BufferWrapper) childListItem);
+        }
+    }
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
similarity index 61%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
index 13391ed8fe7385b522ef7318d54d161e19656085..72c130db26aeae69d252d351b460e2cc307aa673 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
@@ -1,74 +1,94 @@
-package de.kuschku.quasseldroid_ng.ui;
+package de.kuschku.quasseldroid_ng.ui.chat.chatview;
 
 import android.content.Context;
 import android.graphics.Typeface;
 import android.support.annotation.ColorInt;
-import android.text.SpannableString;
-import android.text.TextUtils;
+import android.support.annotation.UiThread;
+import android.util.Log;
 
 import org.joda.time.format.DateTimeFormatter;
 
-import java.util.Locale;
-
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.quasseldroid_ng.R;
-import de.kuschku.quasseldroid_ng.util.DateFormatHelper;
-import de.kuschku.quasseldroid_ng.util.IrcFormatHelper;
-import de.kuschku.quasseldroid_ng.util.IrcUserUtils;
-import de.kuschku.quasseldroid_ng.util.SpanFormatter;
-import de.kuschku.quasseldroid_ng.util.ThemeUtil;
-
+import de.kuschku.util.ui.DateFormatHelper;
+import de.kuschku.util.irc.IrcFormatHelper;
+import de.kuschku.util.irc.IrcUserUtils;
+import de.kuschku.util.ui.SpanFormatter;
+import de.kuschku.util.ui.ThemeUtil;
+import de.kuschku.util.annotationbind.AutoString;
+import de.kuschku.util.annotationbind.AutoBinder;
+
+@UiThread
 public class ChatMessageRenderer {
-    private final ThemeUtil themeUtil;
     private final DateTimeFormatter format;
     private final FormatStrings strings;
     private final IrcFormatHelper helper;
 
     private Client client;
-    private boolean fullHostmask = true;
+    private boolean fullHostmask = false;
 
     public ChatMessageRenderer(Context ctx) {
-        this.themeUtil = new ThemeUtil(ctx);
+        ThemeUtil themeUtil = new ThemeUtil(ctx);
         this.format = DateFormatHelper.getTimeFormatter(ctx);
         this.strings = new FormatStrings(ctx);
         this.helper = new IrcFormatHelper(themeUtil.colors);
-    }
 
-    public void setClient(Client client) {
-        this.client = client;
+        this.highlightStyle = new MessageStyleContainer(
+                themeUtil.colors.colorForegroundHighlight,
+                Typeface.NORMAL,
+                themeUtil.colors.colorForegroundHighlight,
+                themeUtil.colors.colorBackgroundHighlight
+        );
+        this.serverStyle = new MessageStyleContainer(
+                themeUtil.colors.colorForegroundSecondary,
+                Typeface.ITALIC,
+                themeUtil.colors.colorForegroundSecondary,
+                themeUtil.colors.colorBackgroundSecondary
+        );
+        this.plainStyle = new MessageStyleContainer(
+                themeUtil.colors.colorForeground,
+                Typeface.NORMAL,
+                themeUtil.colors.colorForegroundSecondary,
+                themeUtil.colors.transparent
+        );
+        this.actionStyle = new MessageStyleContainer(
+                themeUtil.colors.colorForegroundAction,
+                Typeface.ITALIC,
+                themeUtil.colors.colorForegroundSecondary,
+                themeUtil.colors.transparent
+        );
     }
 
-    private void setColors(MessageViewHolder holder, boolean highlight, boolean isServerAction) {
-        if (highlight) {
-            applyStyle(holder,
-                    themeUtil.colors.colorForegroundHighlight,
-                    Typeface.NORMAL,
-                    themeUtil.colors.colorForegroundHighlight,
-                    themeUtil.colors.colorBackgroundHighlight
-            );
-        } else if (isServerAction) {
-            applyStyle(holder,
-                    themeUtil.colors.colorForegroundSecondary,
-                    Typeface.ITALIC,
-                    themeUtil.colors.colorForegroundSecondary,
-                    themeUtil.colors.colorBackgroundSecondary
-            );
-        } else {
-            applyStyle(holder,
-                    themeUtil.colors.colorForeground,
-                    Typeface.NORMAL,
-                    themeUtil.colors.colorForegroundSecondary,
-                    themeUtil.colors.transparent
-            );
+    private static class MessageStyleContainer{
+        public final @ColorInt int textColor;
+        public final int fontstyle;
+        public final @ColorInt int timeColor;
+        public final @ColorInt int bgColor;
+
+        public MessageStyleContainer(int textColor, int fontstyle, int timeColor, int bgColor) {
+            this.textColor = textColor;
+            this.fontstyle = fontstyle;
+            this.timeColor = timeColor;
+            this.bgColor = bgColor;
         }
     }
 
-    private void applyStyle(MessageViewHolder holder, @ColorInt int textColor, int style, @ColorInt int timeColor, @ColorInt int bgColor) {
-        holder.content.setTextColor(textColor);
-        holder.content.setTypeface(null, style);
-        holder.time.setTextColor(timeColor);
-        holder.itemView.setBackgroundColor(bgColor);
+    public MessageStyleContainer highlightStyle;
+    public MessageStyleContainer serverStyle;
+    public MessageStyleContainer actionStyle;
+    public MessageStyleContainer plainStyle;
+
+    public void setClient(Client client) {
+        this.client = client;
+    }
+
+    private void applyStyle(MessageViewHolder holder, MessageStyleContainer style, MessageStyleContainer highlightStyle, boolean highlight) {
+        MessageStyleContainer container = highlight ? highlightStyle : style;
+        holder.content.setTextColor(container.textColor);
+        holder.content.setTypeface(null, container.fontstyle);
+        holder.time.setTextColor(container.timeColor);
+        holder.itemView.setBackgroundColor(container.bgColor);
     }
 
     private CharSequence formatNick(String hostmask, boolean full) {
@@ -85,6 +105,7 @@ public class ChatMessageRenderer {
     }
 
     public void onBindPlain(MessageViewHolder holder, Message message) {
+        applyStyle(holder, plainStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(
                 strings.formatPlain(
                         formatNick(message.sender, false),
@@ -94,6 +115,7 @@ public class ChatMessageRenderer {
     }
 
     public void onBindNotice(MessageViewHolder holder, Message message) {
+        applyStyle(holder, plainStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(strings.formatAction(
                 formatNick(message.sender, false),
                 helper.formatIrcMessage(message.content)
@@ -101,10 +123,17 @@ public class ChatMessageRenderer {
     }
 
     public void onBindAction(MessageViewHolder holder, Message message) {
-        holder.content.setText(message.toString());
+        applyStyle(holder, actionStyle, highlightStyle, message.flags.Highlight);
+        holder.content.setText(
+                strings.formatAction(
+                        formatNick(message.sender, false),
+                        helper.formatIrcMessage(message.content)
+                )
+        );
     }
 
     public void onBindNick(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         if (message.flags.Self)
             holder.content.setText(strings.formatNick(
                     formatNick(message.sender, false)
@@ -117,10 +146,12 @@ public class ChatMessageRenderer {
     }
 
     public void onBindMode(MessageViewHolder holder, Message message) {
+        
         holder.content.setText(message.toString());
     }
 
     public void onBindJoin(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(strings.formatJoin(
                 formatNick(message.sender),
                 client.getBuffer(message.bufferInfo.id).getName()
@@ -128,6 +159,7 @@ public class ChatMessageRenderer {
     }
 
     public void onBindPart(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(strings.formatPart(
                 formatNick(message.sender),
                 client.getBuffer(message.bufferInfo.id).getName(),
@@ -136,6 +168,7 @@ public class ChatMessageRenderer {
     }
 
     public void onBindQuit(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(strings.formatQuit(
                 formatNick(message.sender),
                 message.content
@@ -143,42 +176,52 @@ public class ChatMessageRenderer {
     }
 
     public void onBindKick(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindKill(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindServer(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindInfo(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindError(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindDayChange(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindTopic(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindNetsplitJoin(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindNetsplitQuit(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
 
     public void onBindInvite(MessageViewHolder holder, Message message) {
+        applyStyle(holder, serverStyle, highlightStyle, message.flags.Highlight);
         holder.content.setText(message.toString());
     }
     
@@ -186,115 +229,115 @@ public class ChatMessageRenderer {
         holder.time.setText(format.print(message.time));
         switch (message.type) {
             case Plain:
-                setColors(holder, message.flags.Highlight, false);
                 onBindPlain(holder, message);
                 break;
             case Notice:
-                setColors(holder, message.flags.Highlight, false);
                 onBindNotice(holder, message);
                 break;
             case Action:
-                setColors(holder, message.flags.Highlight, false);
                 onBindAction(holder, message);
                 break;
             case Nick:
-                setColors(holder, message.flags.Highlight, true);
                 onBindNick(holder, message);
                 break;
             case Mode:
-                setColors(holder, message.flags.Highlight, true);
                 onBindMode(holder, message);
                 break;
             case Join:
-                setColors(holder, message.flags.Highlight, true);
                 onBindJoin(holder, message);
                 break;
             case Part:
-                setColors(holder, message.flags.Highlight, true);
                 onBindPart(holder, message);
                 break;
             case Quit:
-                setColors(holder, message.flags.Highlight, true);
                 onBindQuit(holder, message);
                 break;
             case Kick:
-                setColors(holder, message.flags.Highlight, true);
                 onBindKick(holder, message);
                 break;
             case Kill:
-                setColors(holder, message.flags.Highlight, true);
                 onBindKill(holder, message);
                 break;
             case Server:
-                setColors(holder, message.flags.Highlight, false);
                 onBindServer(holder, message);
                 break;
             case Info:
-                setColors(holder, message.flags.Highlight, false);
                 onBindInfo(holder, message);
                 break;
             case Error:
-                setColors(holder, message.flags.Highlight, false);
                 onBindError(holder, message);
                 break;
             case DayChange:
-                setColors(holder, message.flags.Highlight, true);
                 onBindDayChange(holder, message);
                 break;
             case Topic:
-                setColors(holder, message.flags.Highlight, true);
                 onBindTopic(holder, message);
                 break;
             case NetsplitJoin:
-                setColors(holder, message.flags.Highlight, true);
                 onBindNetsplitJoin(holder, message);
                 break;
             case NetsplitQuit:
-                setColors(holder, message.flags.Highlight, true);
                 onBindNetsplitQuit(holder, message);
                 break;
             case Invite:
-                setColors(holder, message.flags.Highlight, true);
                 onBindInvite(holder, message);
                 break;
         }
     }
 
-    private static class FormatStrings {
-        private final String username_hostmask;
-
-        private final String message_plain;
-        private final String message_join;
-        private final String message_part;
-        private final String message_part_extra;
-        private final String message_quit;
-        private final String message_quit_extra;
-        private final String message_kill;
-        private final String message_kick;
-        private final String message_kick_extra;
-        private final String message_mode;
-        private final String message_nick_self;
-        private final String message_nick_other;
-        private final String message_daychange;
-        private final String message_action;
+    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(Context ctx) {
-            username_hostmask = ctx.getString(R.string.username_hostmask);
-
-            message_plain = ctx.getString(R.string.message_plain);
-            message_join = ctx.getString(R.string.message_join);
-            message_part = ctx.getString(R.string.message_part);
-            message_part_extra = ctx.getString(R.string.message_part_extra);
-            message_quit = ctx.getString(R.string.message_quit);
-            message_quit_extra = ctx.getString(R.string.message_quit_extra);
-            message_kill = ctx.getString(R.string.message_kill);
-            message_kick = ctx.getString(R.string.message_kick);
-            message_kick_extra = ctx.getString(R.string.message_kick_extra);
-            message_mode = ctx.getString(R.string.message_mode);
-            message_nick_self = ctx.getString(R.string.message_nick_self);
-            message_nick_other = ctx.getString(R.string.message_nick_other);
-            message_daychange = ctx.getString(R.string.message_daychange);
-            message_action = ctx.getString(R.string.message_action);
+            try {
+                AutoBinder.bind(this, ctx);
+            } catch (IllegalAccessException e) {
+                Log.e("ERROR", e.toString());
+                e.printStackTrace();
+            }
         }
 
         public CharSequence formatUsername(CharSequence nick, CharSequence hostmask) {
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
similarity index 59%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
index d4d0369d56f634aee18295cc98fbde8d99ea021d..e7c9a4803e5c172d24f3e6951fe007778734f0e2 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
@@ -1,6 +1,7 @@
-package de.kuschku.quasseldroid_ng.ui;
+package de.kuschku.quasseldroid_ng.ui.chat.chatview;
 
 import android.content.Context;
+import android.support.annotation.UiThread;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -8,20 +9,26 @@ import android.view.ViewGroup;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.quasseldroid_ng.R;
-import de.kuschku.util.observablelists.AutoScroller;
-import de.kuschku.util.observablelists.ObservableSortedList;
-import de.kuschku.util.observablelists.RecyclerViewAdapterCallback;
-
+import de.kuschku.util.observables.AutoScroller;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.lists.IObservableList;
+import de.kuschku.util.observables.lists.ObservableComparableSortedList;
+import de.kuschku.util.observables.lists.ObservableSortedList;
+import de.kuschku.util.observables.callbacks.wrappers.AdapterUICallbackWrapper;
+
+@UiThread
 public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
     private final AutoScroller scroller;
-    private ObservableSortedList<Message> messageList = new ObservableSortedList<>(Message.class);
+    private IObservableList<UICallback, Message> messageList = new ObservableComparableSortedList<>(Message.class);
     private ChatMessageRenderer renderer;
     private LayoutInflater inflater;
+    private UICallback callback;
 
     public MessageAdapter(Context ctx, AutoScroller scroller) {
         this.scroller = scroller;
         this.inflater = LayoutInflater.from(ctx);
         this.renderer = new ChatMessageRenderer(ctx);
+        this.callback = new AdapterUICallbackWrapper(this, scroller);
     }
 
     public void setClient(Client client) {
@@ -29,9 +36,9 @@ public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
     }
 
     public void setMessageList(ObservableSortedList<Message> messageList) {
-        this.messageList.setCallback(null);
+        this.messageList.removeCallback(callback);
         this.messageList = messageList;
-        this.messageList.setCallback(new RecyclerViewAdapterCallback(this, scroller));
+        this.messageList.addCallback(callback);
         notifyDataSetChanged();
     }
 
@@ -40,15 +47,14 @@ public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
         return new MessageViewHolder(inflater.inflate(R.layout.widget_chatmessage, parent, false));
     }
 
-    // TODO: REWRITE THIS
     @Override
     public void onBindViewHolder(MessageViewHolder holder, int position) {
-        Message msg = messageList.list.get(position);
+        Message msg = messageList.get(position);
         renderer.onBind(holder, msg);
     }
 
     @Override
     public int getItemCount() {
-        return messageList.list.size();
+        return messageList.size();
     }
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
similarity index 70%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java
rename to QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
index bd7131ad93b5b4d5786519720c98eee917e3e8b0..1ce428bd8c8ba771da2b0a59ab234221df3df188 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageViewHolder.java
@@ -1,5 +1,6 @@
-package de.kuschku.quasseldroid_ng.ui;
+package de.kuschku.quasseldroid_ng.ui.chat.chatview;
 
+import android.support.annotation.UiThread;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 import android.widget.TextView;
@@ -8,7 +9,8 @@ import butterknife.Bind;
 import butterknife.ButterKnife;
 import de.kuschku.quasseldroid_ng.R;
 
-class MessageViewHolder extends RecyclerView.ViewHolder {
+@UiThread
+public class MessageViewHolder extends RecyclerView.ViewHolder {
     @Bind(R.id.time)
     TextView time;
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..75ec74b9d10b5feca051586fceb82bf5ed5be893
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferWrapper.java
@@ -0,0 +1,71 @@
+package de.kuschku.quasseldroid_ng.ui.chat.drawer;
+
+import android.support.annotation.UiThread;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bignerdranch.expandablerecyclerview.ViewHolder.ChildViewHolder;
+import com.mikepenz.materialdrawer.holder.StringHolder;
+import com.mikepenz.materialdrawer.model.interfaces.Nameable;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import de.kuschku.libquassel.localtypes.Buffer;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.ui.Bindable;
+
+@UiThread
+public class BufferWrapper implements Nameable<BufferWrapper> {
+    private final Buffer buffer;
+
+    public BufferWrapper(Buffer buffer) {
+        this.buffer = buffer;
+    }
+
+    @Override
+    public BufferWrapper withName(String name) {
+        return this;
+    }
+
+    @Override
+    public BufferWrapper withName(int nameRes) {
+        return this;
+    }
+
+    @Override
+    public BufferWrapper withName(StringHolder name) {
+        return this;
+    }
+
+    @Override
+    public StringHolder getName() {
+        return new StringHolder(buffer.getName());
+    }
+
+    public Buffer getBuffer() {
+        return buffer;
+    }
+
+    public static class ViewHolder extends ChildViewHolder implements Bindable<BufferWrapper> {
+        @Bind(R.id.material_drawer_icon)
+        ImageView materialDrawerIcon;
+        @Bind(R.id.material_drawer_name)
+        TextView materialDrawerName;
+        @Bind(R.id.material_drawer_description)
+        TextView materialDrawerDescription;
+        @Bind(R.id.material_drawer_badge_container)
+        View materialDrawerBadgeContainer;
+        @Bind(R.id.material_drawer_badge)
+        TextView materialDrawerBadge;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+            ButterKnife.bind(this, itemView);
+        }
+
+        public void bind(BufferWrapper wrapper) {
+            materialDrawerName.setText(wrapper.getName().getText());
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0cf639dad0e4358132d2b6ed61c7b50afc8f63f7
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkWrapper.java
@@ -0,0 +1,271 @@
+package de.kuschku.quasseldroid_ng.ui.chat.drawer;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bignerdranch.expandablerecyclerview.Model.ParentListItem;
+import com.bignerdranch.expandablerecyclerview.ViewHolder.ParentViewHolder;
+import com.mikepenz.materialdrawer.holder.StringHolder;
+import com.mikepenz.materialdrawer.model.interfaces.Nameable;
+
+import java.util.List;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import de.kuschku.libquassel.Client;
+import de.kuschku.libquassel.localtypes.Buffer;
+import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.syncables.types.BufferViewConfig;
+import de.kuschku.libquassel.syncables.types.Network;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.observables.callbacks.wrappers.ChildUICallbackWrapper;
+import de.kuschku.util.observables.callbacks.wrappers.MultiUIChildCallback;
+import de.kuschku.util.observables.ContentComparable;
+import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.callbacks.UIChildCallback;
+import de.kuschku.util.observables.lists.ObservableSortedList;
+import de.kuschku.util.ui.Bindable;
+
+@UiThread
+public class NetworkWrapper implements ParentListItem, Nameable<NetworkWrapper>,ContentComparable<NetworkWrapper> {
+    private MultiUIChildCallback callback = MultiUIChildCallback.of();
+
+    private final Network network;
+    private final Client client;
+
+    private ObservableSortedList<BufferWrapper> buffers;
+    private ChildUICallbackWrapper wrapper = new ChildUICallbackWrapper(callback);
+    private int groupId;
+
+    public NetworkWrapper(Network network, Client client, BufferViewConfig bufferViewConfig, SortMode sortMode, int groupId) {
+        this.network = network;
+        this.client = client;
+        setSortMode(sortMode);
+        bufferViewConfig.getBufferList().addCallback(new ElementCallback<Integer>() {
+            @Override
+            public void notifyItemInserted(Integer element) {
+                Buffer buffer = client.getBuffer(element);
+                if (buffer.getInfo().networkId != network.getNetworkId()) return;
+
+                buffers.add(new BufferWrapper(buffer));
+            }
+
+            @Override
+            public void notifyItemRemoved(Integer element) {
+                Buffer buffer = client.getBuffer(element);
+                if (buffer.getInfo().networkId != network.getNetworkId()) return;
+
+                buffers.remove(new BufferWrapper(buffer));
+            }
+
+            @Override
+            public void notifyItemChanged(Integer element) {
+                Buffer buffer = client.getBuffer(element);
+                if (buffer.getInfo().networkId != network.getNetworkId()) return;
+
+                callback.notifyChildItemChanged(groupId, buffers.indexOf(new BufferWrapper(buffer)));
+            }
+        });
+        buffers.addCallback(wrapper);
+        wrapper.setGroupPosition(groupId);
+    }
+
+    public void addCallback(UIChildCallback callback) {
+        this.callback.addCallback(callback);
+    }
+
+    public void removeCallback(UIChildCallback callback) {
+        this.callback.removeCallback(callback);
+    }
+
+    public void setGroupId(int groupId) {
+        this.groupId = groupId;
+        this.wrapper.setGroupPosition(groupId);
+    }
+
+    public void setSortMode(SortMode sortMode) {
+        ObservableSortedList.ItemComparator<BufferWrapper> sorter = new BufferWrapperSorterWrapper(getSorter(sortMode));
+        ObservableSortedList<BufferWrapper> newBuffers = new ObservableSortedList<BufferWrapper>(BufferWrapper.class, sorter);
+        if (buffers != null) {
+            if (buffers.size() > newBuffers.size()) {
+                for (int i = newBuffers.size(); i < buffers.size(); i++) {
+                    callback.notifyChildItemRemoved(groupId, i);
+                }
+            } else if (newBuffers.size() > buffers.size()) {
+                for (int i = buffers.size(); i < newBuffers.size(); i++) {
+                    callback.notifyChildItemInserted(groupId, i);
+                }
+            }
+
+            int commonElementCount = Math.min(buffers.size(), newBuffers.size());
+            for (int i = 0; i < commonElementCount; i++) {
+                callback.notifyChildItemChanged(groupId, i);
+            }
+        } else {
+            for (int i = 0; i < newBuffers.size(); i++) {
+                callback.notifyChildItemInserted(groupId, i);
+            }
+        }
+        buffers = newBuffers;
+    }
+
+    private ObservableSortedList.ItemComparator<Buffer> getSorter(SortMode sortMode) {
+        switch (sortMode) {
+            case ALPHABETICAL:
+                return new AlphabeticalBufferSorter();
+            case RECENT_ACTIVITY:
+                return new RecentActivityBufferSorter();
+            case LAST_SEEN:
+                return new LastSeenBufferSorter();
+            default:
+                return new IdBufferSorter();
+        }
+    }
+
+    @Override
+    public List<?> getChildItemList() {
+        return buffers;
+    }
+
+    @Override
+    public boolean isInitiallyExpanded() {
+        return network.isConnected();
+    }
+
+    @Override
+    public NetworkWrapper withName(String name) {
+        return this;
+    }
+
+    @Override
+    public NetworkWrapper withName(int nameRes) {
+        return this;
+    }
+
+    @Override
+    public NetworkWrapper withName(StringHolder name) {
+        return this;
+    }
+
+    @Override
+    public StringHolder getName() {
+        return new StringHolder(network.getNetworkName());
+    }
+
+    @Override
+    public boolean equalsContent(NetworkWrapper other) {
+        return network.equalsContent(other.network);
+    }
+
+    @Override
+    public int compareTo(@NonNull NetworkWrapper another) {
+        return network.compareTo(another.network);
+    }
+
+    public static class ViewHolder extends ParentViewHolder implements Bindable<NetworkWrapper> {
+        @Bind(R.id.material_drawer_icon)
+        ImageView materialDrawerIcon;
+        @Bind(R.id.material_drawer_name)
+        TextView materialDrawerName;
+        @Bind(R.id.material_drawer_description)
+        TextView materialDrawerDescription;
+        @Bind(R.id.material_drawer_badge_container)
+        View materialDrawerBadgeContainer;
+        @Bind(R.id.material_drawer_badge)
+        TextView materialDrawerBadge;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+            ButterKnife.bind(this, itemView);
+        }
+
+        public void bind(NetworkWrapper wrapper) {
+            materialDrawerName.setText(wrapper.getName().getText());
+        }
+    }
+
+    public enum SortMode {
+        NONE,
+        ALPHABETICAL,
+        RECENT_ACTIVITY,
+        LAST_SEEN
+    }
+
+    private static class BufferWrapperSorterWrapper implements ObservableSortedList.ItemComparator<BufferWrapper> {
+        private final ObservableSortedList.ItemComparator<Buffer> wrapped;
+
+        public BufferWrapperSorterWrapper(ObservableSortedList.ItemComparator<Buffer> wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        @Override
+        public int compare(BufferWrapper lhs, BufferWrapper rhs) {
+            return wrapped.compare(lhs.getBuffer(), rhs.getBuffer());
+        }
+
+        @Override
+        public boolean areContentsTheSame(BufferWrapper oldItem, BufferWrapper newItem) {
+            return wrapped.areContentsTheSame(oldItem.getBuffer(), newItem.getBuffer());
+        }
+
+        @Override
+        public boolean areItemsTheSame(BufferWrapper item1, BufferWrapper item2) {
+            return areContentsTheSame(item1, item2);
+        }
+    }
+
+    private class IdBufferSorter extends BasicBufferSorter {
+        @Override
+        public int compare(Buffer lhs, Buffer rhs) {
+            return lhs.getInfo().id - rhs.getInfo().id;
+        }
+    }
+
+    private class AlphabeticalBufferSorter extends BasicBufferSorter {
+        @Override
+        public int compare(Buffer lhs, Buffer rhs) {
+            return lhs.getName().compareTo(rhs.getName());
+        }
+    }
+
+    private class RecentActivityBufferSorter extends BasicBufferSorter {
+        @Override
+        public int compare(Buffer lhs, Buffer rhs) {
+            return getLastMessageId(lhs) - getLastMessageId(rhs);
+        }
+
+        private int getLastMessageId(Buffer buffer) {
+            int bufferId = buffer.getInfo().id;
+            Message message = client.getBacklogManager().get(bufferId).last();
+            return message == null ? -1 : message.messageId;
+        }
+    }
+
+    private class LastSeenBufferSorter extends BasicBufferSorter {
+        @Override
+        public int compare(Buffer lhs, Buffer rhs) {
+            return getLastSeenMessageId(lhs) - getLastSeenMessageId(rhs);
+        }
+
+        private int getLastSeenMessageId(Buffer buffer) {
+            int bufferId = buffer.getInfo().id;
+            return client.getBufferSyncer().getLastSeenMsg(bufferId);
+        }
+    }
+
+    private abstract class BasicBufferSorter implements ObservableSortedList.ItemComparator<Buffer> {
+        @Override
+        public boolean areContentsTheSame(Buffer oldItem, Buffer newItem) {
+            return oldItem.getInfo().id == newItem.getInfo().id;
+        }
+
+        @Override
+        public boolean areItemsTheSame(Buffer item1, Buffer item2) {
+            return areContentsTheSame(item1, item2);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/CoreSetupActivity.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/CoreSetupActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..f07df9656e5c1fc4ada380a4fc788527e53604ae
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/CoreSetupActivity.java
@@ -0,0 +1,81 @@
+package de.kuschku.quasseldroid_ng.ui.coresetup;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.UiThread;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import de.kuschku.libquassel.objects.types.StorageBackend;
+import de.kuschku.libquassel.primitives.types.QVariant;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.ui.parcelableUtil.StorageBackendParcelable;
+import de.kuschku.util.ui.parcelableUtil.ClassLoaderUtils;
+
+@UiThread
+public class CoreSetupActivity extends AppCompatActivity implements StorageBackendFragment.OnListFragmentInteractionListener {
+
+    int page = 0;
+    ArrayList<StorageBackendParcelable> storageBackends;
+    int selectedBackend;
+    Map<String, QVariant> config;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_core_setup);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+        Intent intent = getIntent();
+        Bundle bundle = intent.getBundleExtra("bundle");
+        bundle.setClassLoader(ClassLoaderUtils.loader);
+        storageBackends = bundle.getParcelableArrayList("backends");
+
+        if (savedInstanceState != null) {
+            page = savedInstanceState.getInt("page", 0);
+        }
+
+        switchToPage(page);
+    }
+
+    private void switchToPage(int page) {
+        if (page < 0) page = 0;
+        Fragment fragment = getFragment(page);
+        if (fragment == null) finished();
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.replace(R.id.host, fragment);
+        transaction.commit();
+    }
+
+    private Fragment getFragment(int page) {
+        if (page == 0) {
+            return StorageBackendFragment.newInstance(storageBackends);
+        } else {
+            return null;
+        }
+    }
+
+    private void finished() {
+        Toast.makeText(this, "Selection done!", Toast.LENGTH_LONG).show();
+        Log.e("DONE", String.valueOf(selectedBackend));
+        Log.e("DONE", String.valueOf(config));
+        Intent intent = new Intent("RESULT_SELECTED_BACKEND");
+        intent.putExtra("backend", storageBackends.get(selectedBackend).DisplayName);
+        setResult(Activity.RESULT_OK, intent);
+        finish();
+    }
+
+    @Override
+    public void onListFragmentInteraction(StorageBackend item) {
+        selectedBackend = storageBackends.indexOf(item);
+        switchToPage(page+1);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendFragment.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..52e10d805f074e4863a072924e8370b744aaaa63
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendFragment.java
@@ -0,0 +1,85 @@
+package de.kuschku.quasseldroid_ng.ui.coresetup;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.UiThread;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.kuschku.libquassel.objects.types.StorageBackend;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.ui.parcelableUtil.StorageBackendParcelable;
+
+@UiThread
+public class StorageBackendFragment extends Fragment {
+    public static final String BACKENDS = "backends";
+
+    private OnListFragmentInteractionListener mListener;
+    private StorageBackendRecyclerViewAdapter adapter = new StorageBackendRecyclerViewAdapter(mListener);
+
+    public static StorageBackendFragment newInstance(List<StorageBackend> backends) {
+        return newInstance(StorageBackendParcelable.wrap(backends));
+    }
+
+    public static StorageBackendFragment newInstance(ArrayList<StorageBackendParcelable> backends) {
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList(BACKENDS, backends);
+        StorageBackendFragment fragment = new StorageBackendFragment();
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_storagebackend_list, container, false);
+
+        // Set the adapter
+        if (view instanceof RecyclerView) {
+            Context context = view.getContext();
+            RecyclerView recyclerView = (RecyclerView) view;
+            recyclerView.setLayoutManager(new LinearLayoutManager(context));
+            recyclerView.setAdapter(adapter);
+        }
+        if (getArguments() != null) {
+            List<StorageBackendParcelable> parcelables = getArguments().getParcelableArrayList(BACKENDS);
+            adapter.setBackends(parcelables);
+        }
+        return view;
+    }
+
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        if (context instanceof OnListFragmentInteractionListener) {
+            mListener = (OnListFragmentInteractionListener) context;
+            adapter.setListener(mListener);
+        } else {
+            throw new RuntimeException(context.toString()
+                    + " must implement OnListFragmentInteractionListener");
+        }
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mListener = null;
+    }
+
+    public interface OnListFragmentInteractionListener {
+        void onListFragmentInteraction(StorageBackend item);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendRecyclerViewAdapter.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendRecyclerViewAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8ab6c26dd14badd36de70c715a4f6349f6084fa
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendRecyclerViewAdapter.java
@@ -0,0 +1,78 @@
+package de.kuschku.quasseldroid_ng.ui.coresetup;
+
+import android.support.annotation.UiThread;
+import android.support.v7.widget.AppCompatButton;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.kuschku.libquassel.objects.types.StorageBackend;
+import de.kuschku.quasseldroid_ng.R;
+
+@UiThread
+public class StorageBackendRecyclerViewAdapter extends RecyclerView.Adapter<StorageBackendRecyclerViewAdapter.ViewHolder> {
+
+    private List<? extends StorageBackend> backends = new ArrayList<>();
+    private StorageBackendFragment.OnListFragmentInteractionListener listener;
+
+    public StorageBackendRecyclerViewAdapter(StorageBackendFragment.OnListFragmentInteractionListener listener) {
+        this.listener = listener;
+    }
+
+    public void setBackends(List<? extends StorageBackend> backends) {
+        this.backends = backends;
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.fragment_storagebackend, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final ViewHolder holder, int position) {
+        StorageBackend backend = backends.get(position);
+
+        holder.mItem = backend;
+        holder.mIdView.setText(backend.DisplayName);
+        holder.mContentView.setText(backend.Description);
+
+        holder.mConfigure.setOnClickListener(v -> {
+            if (null != listener) {
+                listener.onListFragmentInteraction(holder.mItem);
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        return backends.size();
+    }
+
+    public void setListener(StorageBackendFragment.OnListFragmentInteractionListener listener) {
+        this.listener = listener;
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        public final View mView;
+        public final TextView mIdView;
+        public final TextView mContentView;
+        public final AppCompatButton mConfigure;
+        public StorageBackend mItem;
+
+        public ViewHolder(View view) {
+            super(view);
+            mView = view;
+            mIdView = (TextView) view.findViewById(R.id.id);
+            mContentView = (TextView) view.findViewById(R.id.content);
+            mConfigure = (AppCompatButton) view.findViewById(R.id.configure);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java b/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java
deleted file mode 100644
index 8a3c9ea7cb67d83ea68856dfbd89f59a746350e5..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.kuschku.quasseldroid_ng.util;
-
-import android.os.Build;
-
-public class CompatibilityUtils {
-    private CompatibilityUtils() {
-
-    }
-
-    public static boolean isChromiumDevice() {
-        return (Build.MANUFACTURER.toLowerCase().contains("chromium") && Build.BRAND.toLowerCase().contains("chromium"));
-    }
-}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/AndroidAssert.java b/QuasselDroidNG/src/main/java/de/kuschku/util/AndroidAssert.java
new file mode 100644
index 0000000000000000000000000000000000000000..3177e9411c8b352fa6684bd943cb6ecdb1a8e582
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/AndroidAssert.java
@@ -0,0 +1,231 @@
+package de.kuschku.util;
+
+import android.support.design.BuildConfig;
+
+@SuppressWarnings("unused")
+public class AndroidAssert {
+    private AndroidAssert() {
+    }
+
+    public static void assertTrue(String message, boolean condition) {
+        if (BuildConfig.DEBUG) {
+            if (!condition) fail(message);
+        }
+    }
+
+    public static void assertTrue(boolean condition) {
+        if (BuildConfig.DEBUG) {
+            if (!condition) fail();
+        }
+    }
+
+    public static void assertFalse(String message, boolean condition) {
+        if (BuildConfig.DEBUG) {
+            if (condition) fail(message);
+        }
+    }
+
+    public static void assertFalse(boolean condition) {
+        if (BuildConfig.DEBUG) {
+            if (condition) fail();
+        }
+    }
+
+    public static void fail(String message) {
+        if (BuildConfig.DEBUG) {
+            throw new AssertionError(message);
+        }
+    }
+
+    public static void fail() {
+        if (BuildConfig.DEBUG) {
+            throw new AssertionError();
+        }
+    }
+
+    public static void assertEquals(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, String expected, String actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String expected, String actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, double expected, double actual, double delta) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(double expected, double actual, double delta) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, float expected, float actual, float delta) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(float expected, float actual, float delta) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, long expected, long actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(long expected, long actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, boolean expected, boolean actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(boolean expected, boolean actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, byte expected, byte actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(byte expected, byte actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, char expected, char actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(char expected, char actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, short expected, short actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(short expected, short actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(String message, int expected, int actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertEquals(int expected, int actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(Objects.equals(expected, actual));
+        }
+    }
+
+    public static void assertNotNull(Object object) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(object != null);
+        }
+    }
+
+    public static void assertNotNull(String message, Object object) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, object != null);
+        }
+    }
+
+    public static void assertNull(Object object) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(object == null);
+        }
+    }
+
+    public static void assertNull(String message, Object object) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, object == null);
+        }
+    }
+
+    public static void assertSame(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, expected == actual);
+        }
+    }
+
+    public static void assertSame(Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(expected == actual);
+        }
+    }
+
+    public static void assertNotSame(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(message, expected != actual);
+        }
+    }
+
+    public static void assertNotSame(Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertTrue(expected != actual);
+        }
+    }
+
+    public static void failSame(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertNotSame(message, expected, actual);
+        }
+    }
+
+    public static void failNotSame(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertSame(message, expected, actual);
+        }
+    }
+
+    public static void failNotEquals(String message, Object expected, Object actual) {
+        if (BuildConfig.DEBUG) {
+            assertEquals(message, expected, actual);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/DrawerUtils.java b/QuasselDroidNG/src/main/java/de/kuschku/util/DrawerUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6ae96ca6df3a86c2fc2d21cb718a5a17d6be123
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/DrawerUtils.java
@@ -0,0 +1,32 @@
+package de.kuschku.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v4.widget.Space;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.LinearLayout;
+
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.ui.MaterialActionBarDrawerToggle;
+
+public class DrawerUtils {
+    @UiThread
+    public static void initDrawer(Activity actvity, DrawerLayout layout, Toolbar toolbar, @StringRes int open_res, @StringRes int close_res) {
+        ActionBarDrawerToggle actionBarDrawerToggle = new MaterialActionBarDrawerToggle(actvity, layout, toolbar, open_res, close_res);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            actvity.getWindow().setStatusBarColor(actvity.getResources().getColor(android.R.color.transparent));
+            layout.setStatusBarBackground(R.color.colorPrimaryDark);
+        }
+        layout.setDrawerListener(actionBarDrawerToggle);
+        actionBarDrawerToggle.syncState();
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/Objects.java b/QuasselDroidNG/src/main/java/de/kuschku/util/Objects.java
new file mode 100644
index 0000000000000000000000000000000000000000..a14c6c64abe9e3e6814248cdd7f7de39394884ed
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/Objects.java
@@ -0,0 +1,113 @@
+package de.kuschku.util;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Utility methods for objects.
+ * @see java.util.Objects
+ * @since 1.7
+ */
+public final class Objects {
+    private Objects() {}
+
+    /**
+     * Returns 0 if {@code a == b}, or {@code c.compare(a, b)} otherwise.
+     * That is, this makes {@code c} null-safe.
+     */
+    public static <T> int compare(T a, T b, Comparator<? super T> c) {
+        if (a == b) {
+            return 0;
+        }
+        return c.compare(a, b);
+    }
+
+    /**
+     * Returns true if both arguments are null,
+     * the result of {@link Arrays#equals} if both arguments are primitive arrays,
+     * the result of {@link Arrays#deepEquals} if both arguments are arrays of reference types,
+     * and the result of {@link #equals} otherwise.
+     */
+    public static boolean deepEquals(Object a, Object b) {
+        if (a == null || b == null) {
+            return a == b;
+        } else if (a instanceof Object[] && b instanceof Object[]) {
+            return Arrays.deepEquals((Object[]) a, (Object[]) b);
+        } else if (a instanceof boolean[] && b instanceof boolean[]) {
+            return Arrays.equals((boolean[]) a, (boolean[]) b);
+        } else if (a instanceof byte[] && b instanceof byte[]) {
+            return Arrays.equals((byte[]) a, (byte[]) b);
+        } else if (a instanceof char[] && b instanceof char[]) {
+            return Arrays.equals((char[]) a, (char[]) b);
+        } else if (a instanceof double[] && b instanceof double[]) {
+            return Arrays.equals((double[]) a, (double[]) b);
+        } else if (a instanceof float[] && b instanceof float[]) {
+            return Arrays.equals((float[]) a, (float[]) b);
+        } else if (a instanceof int[] && b instanceof int[]) {
+            return Arrays.equals((int[]) a, (int[]) b);
+        } else if (a instanceof long[] && b instanceof long[]) {
+            return Arrays.equals((long[]) a, (long[]) b);
+        } else if (a instanceof short[] && b instanceof short[]) {
+            return Arrays.equals((short[]) a, (short[]) b);
+        }
+        return a.equals(b);
+    }
+
+    /**
+     * Null-safe equivalent of {@code a.equals(b)}.
+     */
+    public static boolean equals(Object a, Object b) {
+        return (a == null) ? (b == null) : a.equals(b);
+    }
+
+    /**
+     * Convenience wrapper for {@link Arrays#hashCode}, adding varargs.
+     * This can be used to compute a hash code for an object's fields as follows:
+     * {@code Objects.hash(a, b, c)}.
+     */
+    public static int hash(Object... values) {
+        return Arrays.hashCode(values);
+    }
+
+    /**
+     * Returns 0 for null or {@code o.hashCode()}.
+     */
+    public static int hashCode(Object o) {
+        return (o == null) ? 0 : o.hashCode();
+    }
+
+    /**
+     * Returns {@code o} if non-null, or throws {@code NullPointerException}.
+     */
+    public static <T> T requireNonNull(T o) {
+        if (o == null) {
+            throw new NullPointerException();
+        }
+        return o;
+    }
+
+    /**
+     * Returns {@code o} if non-null, or throws {@code NullPointerException}
+     * with the given detail message.
+     */
+    public static <T> T requireNonNull(T o, String message) {
+        if (o == null) {
+            throw new NullPointerException(message);
+        }
+        return o;
+    }
+
+    /**
+     * Returns "null" for null or {@code o.toString()}.
+     */
+    public static String toString(Object o) {
+        return (o == null) ? "null" : o.toString();
+    }
+
+    /**
+     * Returns {@code nullString} for null or {@code o.toString()}.
+     */
+    public static String toString(Object o, String nullString) {
+        return (o == null) ? nullString : o.toString();
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ReflectionUtils.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ReflectionUtils.java
index 25f4b826fd2eadb962a37b23d621e89f95ee6049..a3a245e53452ba7a99feeb6bdcac82b94ed2d871 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/ReflectionUtils.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ReflectionUtils.java
@@ -9,14 +9,23 @@ import java.util.Arrays;
 import java.util.List;
 
 import de.kuschku.libquassel.exceptions.SyncInvocationException;
+import de.kuschku.libquassel.primitives.types.QVariant;
 
 public class ReflectionUtils {
     private ReflectionUtils() {
 
     }
 
+    private static void unboxList(Object[] list){
+        for (int i = 0; i < list.length; i++) {
+            if (list[i] instanceof QVariant)
+                list[i] = ((QVariant) list[i]).data;
+        }
+    }
+
     public static void invokeMethod(Object o, String name, Object[] argv) throws SyncInvocationException {
         name = stripName(name);
+        unboxList(argv);
 
         Class<?>[] classes = new Class<?>[argv.length];
         for (int i = 0; i < argv.length; i++) {
@@ -24,12 +33,12 @@ public class ReflectionUtils {
         }
         Method m = getMethodFromSignature(name, o.getClass(), classes);
         if (m == null)
-            throw new SyncInvocationException(String.format("Error invoking %s::%s with arguments %s", o.getClass().getSimpleName(), name, Arrays.toString(argv)));
+            throw new SyncInvocationException(String.format("No method %s::%s with argument types %s", o.getClass().getSimpleName(), name, Arrays.toString(classes)));
 
         try {
             m.invoke(o, argv);
         } catch (Exception e) {
-            throw new SyncInvocationException(e, String.format("Error invoking %s::%s with arguments %s", o.getClass().getSimpleName(), name, Arrays.toString(argv)));
+            throw new SyncInvocationException(e, String.format("Error invoking %s::%s with arguments %s and classes %s", o.getClass().getSimpleName(), name, Arrays.toString(argv), Arrays.toString(classes)));
         }
     }
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ServerAddress.java
similarity index 82%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/ServerAddress.java
index 20b2059eae6a19d75fb13f7ceb408788a1e22801..33ba15f123d18d66f4246ade83919162da22cb88 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ServerAddress.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util;
 
 public class ServerAddress {
     public final String host;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoBinder.java b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoBinder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ec3b77228b241412680b9f1c3e9a2db8b1e518d
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoBinder.java
@@ -0,0 +1,72 @@
+package de.kuschku.util.annotationbind;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.support.annotation.ColorInt;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class AutoBinder {
+    private AutoBinder() {
+
+    }
+
+    public static void bind(Object o, Resources.Theme t) throws IllegalAccessException {
+        for (Field f : o.getClass().getFields()) {
+            if (f.isAnnotationPresent(AutoColor.class)) {
+                int[] colors = obtainColors(f.getAnnotation(AutoColor.class).value(), t);
+                if (f.getType().isArray())
+                    f.set(o, colors);
+                else if (colors.length == 1)
+                    f.set(o, colors[0]);
+                else
+                    throw new IllegalAccessException("Field length does not correspond to argument length");
+            }
+        }
+    }
+
+    public static void bind(Object o, Context t) throws IllegalAccessException {
+        Resources.Theme theme = t.getTheme();
+        for (Field f : o.getClass().getFields()) {
+            if (f.isAnnotationPresent(AutoColor.class)) {
+                int[] colors = obtainColors(f.getAnnotation(AutoColor.class).value(), theme);
+                if (f.getType().isArray())
+                    f.set(o, colors);
+                else if (colors.length == 1)
+                    f.set(o, colors[0]);
+                else
+                    throw new IllegalAccessException("Field length does not correspond to argument length");
+            } else if (f.isAnnotationPresent(AutoString.class)) {
+                String[] strings = obtainStrings(f.getAnnotation(AutoString.class).value(), t);
+                if (f.getType().isArray())
+                    f.set(o, strings);
+                else if (strings.length == 1)
+                    f.set(o, strings[0]);
+                else
+                    throw new IllegalAccessException("Field length does not correspond to argument length");
+            }
+        }
+    }
+
+    @ColorInt
+    private static int[] obtainColors(int[] res, Resources.Theme theme) {
+        int[] result = new int[res.length];
+        TypedArray t = theme.obtainStyledAttributes(res);
+        for (int i = 0; i < res.length; i++) {
+            result[i] = t.getColor(i, 0x00000000);
+        }
+        t.recycle();
+        return result;
+    }
+
+    private static String[] obtainStrings(int[] res, Context ctx) {
+        String[] result = new String[res.length];
+        for (int i = 0; i < res.length; i++) {
+            result[i] = ctx.getString(res[i]);
+        }
+        return result;
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Color.java b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoColor.java
similarity index 89%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Color.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoColor.java
index 833948443bb4fe8ff92899a8a4d167f42f84ac29..0d6f895f99623b9d171d6f8b92e1b5569cd9d521 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Color.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoColor.java
@@ -6,7 +6,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 @Retention(RetentionPolicy.RUNTIME)
-public @interface Color {
+public @interface AutoColor {
     @AnyRes
     int[] value() default {};
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoString.java b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoString.java
new file mode 100644
index 0000000000000000000000000000000000000000..66d94944cac967660e1adf981f1434aea995a0bd
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoString.java
@@ -0,0 +1,12 @@
+package de.kuschku.util.annotationbind;
+
+import android.support.annotation.AnyRes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AutoString {
+    @AnyRes
+    int[] value() default {};
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Binder.java b/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Binder.java
deleted file mode 100644
index 067dc9ca1f9b9bad9585eaaf14c547343fff7ea5..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Binder.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package de.kuschku.util.annotationbind;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.support.annotation.ColorInt;
-
-import java.lang.reflect.Field;
-
-public class Binder {
-    private Binder() {
-
-    }
-
-    public static void bind(Object o, Resources.Theme t) throws IllegalAccessException {
-        for (Field f : o.getClass().getFields()) {
-            if (f.isAnnotationPresent(Color.class)) {
-                int[] colors = obtainColors(f.getAnnotation(Color.class).value(), t);
-                if (f.getType().isArray())
-                    f.set(o, colors);
-                else if (colors.length == 1)
-                    f.set(o, colors[0]);
-                else
-                    throw new IllegalAccessException("Field length does not correspond to argument length");
-            }
-        }
-    }
-
-    public static void bind(Object o, Context t) throws IllegalAccessException {
-        bind(o, t.getTheme());
-    }
-
-    @ColorInt
-    private static int[] obtainColors(int[] res, Resources.Theme theme) {
-        int[] result = new int[res.length];
-        TypedArray t = theme.obtainStyledAttributes(res);
-        for (int i = 0; i < res.length; i++) {
-            result[i] = t.getColor(i, 0x00000000);
-        }
-        t.recycle();
-        return result;
-    }
-}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Storable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Storable.java
new file mode 100644
index 0000000000000000000000000000000000000000..77567673afe5477d56037bb9d953ce48cf84eb17
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Storable.java
@@ -0,0 +1,259 @@
+package de.kuschku.util.instancestateutil;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+public class Storable {
+    public boolean onRestoreInstanceState(Bundle in) {
+        try {
+            Field[] fields = getClass().getDeclaredFields();
+            for (Field field : fields) {
+                if (field.isAnnotationPresent(Store.class)) {
+                    Store annotation = field.getAnnotation(Store.class);
+                    String name = annotation.name().isEmpty() ? field.getName() : annotation.name();
+                    Store.Type type = annotation.type() == Store.Type.INVALID ? getTypeFromClass(field.getType()) : annotation.type();
+                    loadField(in, type, name, field);
+                }
+            }
+            return true;
+        } catch (IllegalAccessException e) {
+            return false;
+        }
+    }
+
+    public boolean onSaveInstanceState(Bundle out) {
+        try {
+            Field[] fields = getClass().getDeclaredFields();
+            for (Field field : fields) {
+                if (field.isAnnotationPresent(Store.class)) {
+                    Store annotation = field.getAnnotation(Store.class);
+                    String name = annotation.name().isEmpty() ? field.getName() : annotation.name();
+                    Store.Type type = annotation.type() == Store.Type.INVALID ? getTypeFromClass(field.getType()) : annotation.type();
+                    storeField(out, type, name, field.get(this));
+                }
+            }
+            return true;
+        } catch (IllegalAccessException e) {
+            return false;
+        }
+    }
+
+    private void storeField(Bundle out, Store.Type type, String name, Object data) {
+        switch (type) {
+            case BOOLEAN:
+                out.putBoolean(name, (Boolean) data);
+                break;
+            case BOOLEAN_ARRAY:
+                out.putBooleanArray(name, (boolean[]) data);
+                break;
+            case BYTE:
+                out.putByte(name, (byte) data);
+                break;
+            case BYTE_ARRAY:
+                out.putByteArray(name, (byte[]) data);
+                break;
+            case CHAR:
+                out.putChar(name, (char) data);
+                break;
+            case CHAR_ARRAY:
+                out.putCharArray(name, (char[]) data);
+                break;
+            case SHORT:
+                out.putShort(name, (short) data);
+                break;
+            case SHORT_ARRAY:
+                out.putShortArray(name, (short[]) data);
+                break;
+            case INT:
+                out.putInt(name, (int) data);
+                break;
+            case INT_ARRAY:
+                out.putIntArray(name, (int[]) data);
+                break;
+            case INTEGER_ARRAYLIST:
+                out.putIntegerArrayList(name, (ArrayList<Integer>) data);
+                break;
+            case LONG:
+                out.putLong(name, (long) data);
+                break;
+            case LONG_ARRAY:
+                out.putLongArray(name, (long[]) data);
+                break;
+            case FLOAT:
+                out.putFloat(name, (float) data);
+                break;
+            case FLOAT_ARRAY:
+                out.putFloatArray(name, (float[]) data);
+                break;
+            case DOUBLE:
+                out.putDouble(name, (double) data);
+                break;
+            case DOUBLE_ARRAY:
+                out.putDoubleArray(name, (double[]) data);
+                break;
+            case STRING:
+                out.putString(name, (String) data);
+                break;
+            case STRING_ARRAY:
+                out.putStringArray(name, (String[]) data);
+                break;
+            case STRING_ARRAYLIST:
+                out.putStringArrayList(name, (ArrayList<String>) data);
+                break;
+            case CHARSEQUENCE:
+                out.putCharSequence(name, (CharSequence) data);
+                break;
+            case CHARSEQUENCE_ARRAY:
+                out.putCharSequenceArray(name, (CharSequence[]) data);
+                break;
+            case CHARSEQUENCE_ARRAYLIST:
+                out.putCharSequenceArrayList(name, (ArrayList<CharSequence>) data);
+                break;
+            case PARCELABLE:
+                out.putParcelable(name, (Parcelable) data);
+                break;
+            case PARCELABLE_ARRAY:
+                out.putParcelableArray(name, (Parcelable[]) data);
+                break;
+            case PARCELABLE_ARRAYLIST:
+                out.putParcelableArrayList(name, (ArrayList<? extends Parcelable>) data);
+                break;
+            case SPARSEPARCELABLE_ARRAY:
+                out.putSparseParcelableArray(name, (SparseArray<? extends Parcelable>) data);
+                break;
+            case SERIALIZABLE:
+                out.putSerializable(name, (Serializable) data);
+                break;
+            case BUNDLE:
+                out.putBundle(name, (Bundle) data);
+                break;
+        }
+    }
+
+    private void loadField(Bundle in, Store.Type type, String name, Field field) throws IllegalAccessException {
+        if (!in.containsKey(name)) return;
+
+        switch (type) {
+            case BOOLEAN:
+                field.setBoolean(this, in.getBoolean(name));
+                break;
+            case BOOLEAN_ARRAY:
+                field.set(this, in.getBooleanArray(name));
+                break;
+            case BYTE:
+                field.setByte(this, in.getByte(name));
+                break;
+            case BYTE_ARRAY:
+                field.set(this, in.getByteArray(name));
+                break;
+            case CHAR:
+                field.setChar(this, in.getChar(name));
+                break;
+            case CHAR_ARRAY:
+                field.set(this, in.getCharArray(name));
+                break;
+            case SHORT:
+                field.setShort(this, in.getShort(name));
+                break;
+            case SHORT_ARRAY:
+                field.set(this, in.getShortArray(name));
+                break;
+            case INT:
+                field.setInt(this, in.getInt(name));
+                break;
+            case INT_ARRAY:
+                field.set(this, in.getIntArray(name));
+                break;
+            case INTEGER_ARRAYLIST:
+                field.set(this, in.getIntegerArrayList(name));
+                break;
+            case LONG:
+                field.setLong(this, in.getLong(name));
+                break;
+            case LONG_ARRAY:
+                field.set(this, in.getLongArray(name));
+                break;
+            case FLOAT:
+                field.setFloat(this, in.getFloat(name));
+                break;
+            case FLOAT_ARRAY:
+                field.set(this, in.getFloatArray(name));
+                break;
+            case DOUBLE:
+                field.setDouble(this, in.getDouble(name));
+                break;
+            case DOUBLE_ARRAY:
+                field.set(this, in.getDoubleArray(name));
+                break;
+            case STRING:
+                field.set(this, in.getString(name));
+                break;
+            case STRING_ARRAY:
+                field.set(this, in.getStringArray(name));
+                break;
+            case STRING_ARRAYLIST:
+                field.set(this, in.getStringArrayList(name));
+                break;
+            case CHARSEQUENCE:
+                field.set(this, in.getCharSequence(name));
+                break;
+            case CHARSEQUENCE_ARRAY:
+                field.set(this, in.getCharSequenceArray(name));
+                break;
+            case CHARSEQUENCE_ARRAYLIST:
+                field.set(this, in.getCharSequenceArrayList(name));
+                break;
+            case PARCELABLE:
+                field.set(this, in.getParcelable(name));
+                break;
+            case PARCELABLE_ARRAY:
+                field.set(this, in.getParcelableArray(name));
+                break;
+            case PARCELABLE_ARRAYLIST:
+                field.set(this, in.getParcelableArrayList(name));
+                break;
+            case SPARSEPARCELABLE_ARRAY:
+                field.set(this, in.getSparseParcelableArray(name));
+                break;
+            case SERIALIZABLE:
+                field.set(this, in.getSerializable(name));
+                break;
+            case BUNDLE:
+                field.set(this, in.getBundle(name));
+                break;
+        }
+    }
+
+    private Store.Type getTypeFromClass(Class cl) {
+        if (boolean.class.isAssignableFrom(cl)) return Store.Type.BOOLEAN;
+        if (boolean[].class.isAssignableFrom(cl)) return Store.Type.BOOLEAN_ARRAY;
+        if (byte.class.isAssignableFrom(cl)) return Store.Type.BYTE;
+        if (byte[].class.isAssignableFrom(cl)) return Store.Type.BYTE_ARRAY;
+        if (char.class.isAssignableFrom(cl)) return Store.Type.CHAR;
+        if (char[].class.isAssignableFrom(cl)) return Store.Type.CHAR_ARRAY;
+        if (short.class.isAssignableFrom(cl)) return Store.Type.SHORT;
+        if (short[].class.isAssignableFrom(cl)) return Store.Type.SHORT_ARRAY;
+        if (int.class.isAssignableFrom(cl)) return Store.Type.INT;
+        if (int[].class.isAssignableFrom(cl)) return Store.Type.INT_ARRAY;
+        if (long.class.isAssignableFrom(cl)) return Store.Type.LONG;
+        if (long[].class.isAssignableFrom(cl)) return Store.Type.LONG_ARRAY;
+        if (float.class.isAssignableFrom(cl)) return Store.Type.FLOAT;
+        if (float[].class.isAssignableFrom(cl)) return Store.Type.FLOAT_ARRAY;
+        if (double.class.isAssignableFrom(cl)) return Store.Type.DOUBLE;
+        if (double[].class.isAssignableFrom(cl)) return Store.Type.DOUBLE_ARRAY;
+        if (String.class.isAssignableFrom(cl)) return Store.Type.STRING;
+        if (String[].class.isAssignableFrom(cl)) return Store.Type.STRING_ARRAY;
+        if (CharSequence.class.isAssignableFrom(cl)) return Store.Type.CHARSEQUENCE;
+        if (CharSequence[].class.isAssignableFrom(cl)) return Store.Type.CHARSEQUENCE_ARRAY;
+        if (Parcelable.class.isAssignableFrom(cl)) return Store.Type.PARCELABLE;
+        if (Parcelable[].class.isAssignableFrom(cl)) return Store.Type.PARCELABLE_ARRAY;
+        if (Serializable.class.isAssignableFrom(cl)) return Store.Type.SERIALIZABLE;
+        if (Bundle.class.isAssignableFrom(cl)) return Store.Type.BUNDLE;
+        return Store.Type.INVALID;
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Store.java b/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Store.java
new file mode 100644
index 0000000000000000000000000000000000000000..868d8c4f9bc9aa59b983eeab62a4d0c08fabc821
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Store.java
@@ -0,0 +1,46 @@
+package de.kuschku.util.instancestateutil;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Store {
+    String name() default "";
+    Type type() default Type.INVALID;
+
+    enum Type {
+        INVALID,
+        BOOLEAN,
+        BOOLEAN_ARRAY,
+        BYTE,
+        BYTE_ARRAY,
+        CHAR,
+        CHAR_ARRAY,
+        SHORT,
+        SHORT_ARRAY,
+        INT,
+        INT_ARRAY,
+        INTEGER_ARRAYLIST,
+        LONG,
+        LONG_ARRAY,
+        FLOAT,
+        FLOAT_ARRAY,
+        DOUBLE,
+        DOUBLE_ARRAY,
+        STRING,
+        STRING_ARRAY,
+        STRING_ARRAYLIST,
+        CHARSEQUENCE,
+        CHARSEQUENCE_ARRAY,
+        CHARSEQUENCE_ARRAYLIST,
+        PARCELABLE,
+        PARCELABLE_ARRAY,
+        PARCELABLE_ARRAYLIST,
+        SPARSEPARCELABLE_ARRAY,
+        SERIALIZABLE,
+        BUNDLE,
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
similarity index 85%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
index 8e272224f5c55d3b7c297e6b525fc77b0947ca55..b585c0d85d773bb0ae7f3cf6436a7fe76dd2db51 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util.irc;
 
 
 import android.content.ActivityNotFoundException;
@@ -8,14 +8,13 @@ import android.graphics.Typeface;
 import android.net.Uri;
 import android.os.Parcel;
 import android.provider.Browser;
+import android.support.annotation.NonNull;
 import android.text.ParcelableSpan;
 import android.text.SpannableString;
 import android.text.Spanned;
-import android.text.TextUtils;
 import android.text.style.ClickableSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
-import android.text.style.URLSpan;
 import android.util.Log;
 import android.view.View;
 
@@ -25,6 +24,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.ui.ThemeUtil;
 
 public class IrcFormatHelper {
     private static final String scheme = "(?:(?:mailto:|(?:[+.-]?\\w)+://)|www(?=\\.\\S+\\.))";
@@ -36,11 +36,11 @@ public class IrcFormatHelper {
 
     private final ThemeUtil.Colors colors;
 
-    public IrcFormatHelper(ThemeUtil.Colors colors) {
+    public IrcFormatHelper(@NonNull ThemeUtil.Colors colors) {
         this.colors = colors;
     }
 
-    public CharSequence formatUserNick(String nick) {
+    public CharSequence formatUserNick(@NonNull String nick) {
         int colorIndex = IrcUserUtils.getSenderColor(nick);
         int color = colors.senderColors[colorIndex];
 
@@ -50,7 +50,7 @@ public class IrcFormatHelper {
         return str;
     }
 
-    public CharSequence formatIrcMessage(String message) {
+    public CharSequence formatIrcMessage(@NonNull String message) {
         List<FutureClickableSpan> spans = new LinkedList<>();
 
         SpannableString str = new SpannableString(message);
@@ -69,21 +69,21 @@ public class IrcFormatHelper {
         public final int start;
         public final int end;
 
-        public FutureClickableSpan(ClickableSpan span, int start, int end) {
+        public FutureClickableSpan(@NonNull ClickableSpan span, int start, int end) {
             this.span = span;
             this.start = start;
             this.end = end;
         }
     }
 
-    private class CustomURLSpan extends ClickableSpan implements ParcelableSpan {
+    private static class CustomURLSpan extends ClickableSpan implements ParcelableSpan {
         private final String mURL;
 
-        public CustomURLSpan(String url) {
+        public CustomURLSpan(@NonNull String url) {
             mURL = url;
         }
 
-        public CustomURLSpan(Parcel src) {
+        public CustomURLSpan(@NonNull Parcel src) {
             mURL = src.readString();
         }
 
@@ -95,7 +95,7 @@ public class IrcFormatHelper {
             return 0;
         }
 
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeString(mURL);
         }
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java b/QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcUserUtils.java
similarity index 98%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcUserUtils.java
index 608a35c84eff1294b2d877d5e2aca1d69d420ce7..5cdce767e314b687703662571caaf8534009b82b 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/irc/IrcUserUtils.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util.irc;
 
 import java.nio.charset.Charset;
 import java.util.Locale;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java b/QuasselDroidNG/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
index 5d0fa64ce4e86fe0bdf75fda8f91eba2827e3ec1..6a27c70fd47b742a5a4f62071ce068ed529d1854 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
@@ -1,5 +1,7 @@
 package de.kuschku.util.niohelpers;
 
+import android.annotation.TargetApi;
+import android.os.Build;
 import android.support.annotation.Nullable;
 
 import java.io.DataInputStream;
@@ -40,6 +42,7 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
         return new WrappedChannel(s.getInputStream(), s.getOutputStream());
     }
 
+    @TargetApi(Build.VERSION_CODES.KITKAT)
     public static WrappedChannel withCompression(WrappedChannel channel) throws IOException {
         return new WrappedChannel(
                 new InflaterInputStream(channel.rawIn),
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java
deleted file mode 100644
index fef28bb8fa87e6675779a3b83ee080f2726f99c2..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package de.kuschku.util.observablelists;
-
-import android.support.v7.util.SortedList;
-
-public class ObservableSortedList<T extends ContentComparable<T>> {
-    public final SortedList<T> list;
-    Callback internal = new Callback();
-    UICallback callback;
-    boolean reverse;
-
-    public ObservableSortedList(Class<T> cl) {
-        list = new SortedList<>(cl, internal);
-    }
-
-    public ObservableSortedList(Class<T> cl, boolean reverse) {
-        this(cl);
-        this.reverse = reverse;
-    }
-
-    public void setCallback(UICallback callback) {
-        this.callback = callback;
-    }
-
-    public T last() {
-        if (list.size() == 0) return null;
-
-        return list.get(list.size() - 1);
-    }
-
-    class Callback extends SortedList.Callback<T> {
-        @Override
-        public int compare(T o1, T o2) {
-            return (reverse) ? o2.compareTo(o1): o1.compareTo(o2);
-        }
-
-        @Override
-        public void onInserted(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemInserted(position);
-                else
-                    callback.notifyItemRangeInserted(position, count);
-        }
-
-        @Override
-        public void onRemoved(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemRemoved(position);
-                else
-                    callback.notifyItemRangeRemoved(position, count);
-        }
-
-        @Override
-        public void onMoved(int fromPosition, int toPosition) {
-            if (callback != null)
-                callback.notifyItemMoved(fromPosition, toPosition);
-        }
-
-        @Override
-        public void onChanged(int position, int count) {
-            if (callback != null)
-                if (count == 1)
-                    callback.notifyItemChanged(position);
-                else
-                    callback.notifyItemRangeChanged(position, count);
-        }
-
-        @Override
-        public boolean areContentsTheSame(T oldItem, T newItem) {
-            return oldItem.equalsContent(newItem);
-        }
-
-        @Override
-        public boolean areItemsTheSame(T item1, T item2) {
-            return item1.equals(item2);
-        }
-    }
-}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/AutoScroller.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/AutoScroller.java
similarity index 93%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/AutoScroller.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/observables/AutoScroller.java
index 2ac3aead373c38d34110243e1dcb2435ab46cd9d..5d6171842560dcda907b2df5ac5caa736910fb87 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/AutoScroller.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/AutoScroller.java
@@ -1,4 +1,4 @@
-package de.kuschku.util.observablelists;
+package de.kuschku.util.observables;
 
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ContentComparable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/ContentComparable.java
similarity index 76%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ContentComparable.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/observables/ContentComparable.java
index 818c6152160a83d8457927d4101ba80496a120b8..6295bc245bdf911abb2b6b9332775a3f92cb096b 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ContentComparable.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/ContentComparable.java
@@ -1,4 +1,4 @@
-package de.kuschku.util.observablelists;
+package de.kuschku.util.observables;
 
 public interface ContentComparable<T extends ContentComparable<T>> extends Comparable<T> {
     boolean equalsContent(T other);
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/IObservable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/IObservable.java
new file mode 100644
index 0000000000000000000000000000000000000000..07fe0b644bf96f7394d4f297955c7c1b3d3fb970
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/IObservable.java
@@ -0,0 +1,8 @@
+package de.kuschku.util.observables;
+
+import de.kuschku.util.observables.callbacks.UICallback;
+
+public interface IObservable<T> {
+    void addCallback(T callback);
+    void removeCallback(T callback);
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/ElementCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/ElementCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a2d08f6f1757590763f56f2d3fc5e459de1a01a
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/ElementCallback.java
@@ -0,0 +1,14 @@
+package de.kuschku.util.observables.callbacks;
+
+import android.support.annotation.UiThread;
+
+public interface ElementCallback<T> {
+    @UiThread
+    void notifyItemInserted(T element);
+
+    @UiThread
+    void notifyItemRemoved(T element);
+
+    @UiThread
+    void notifyItemChanged(T element);
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/UICallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UICallback.java
similarity index 66%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/UICallback.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UICallback.java
index 124f9261a360f5569e1078b09c48f4bf77373b0e..64746340f7f78c7bfb1c52861f9a68624dbf1701 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/UICallback.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UICallback.java
@@ -1,17 +1,26 @@
-package de.kuschku.util.observablelists;
+package de.kuschku.util.observables.callbacks;
+
+import android.support.annotation.UiThread;
 
 public interface UICallback {
+    @UiThread
     void notifyItemInserted(int position);
 
+    @UiThread
     void notifyItemChanged(int position);
 
+    @UiThread
     void notifyItemRemoved(int position);
 
+    @UiThread
     void notifyItemMoved(int from, int to);
 
+    @UiThread
     void notifyItemRangeInserted(int position, int count);
 
+    @UiThread
     void notifyItemRangeChanged(int position, int count);
 
+    @UiThread
     void notifyItemRangeRemoved(int position, int count);
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f86cc7ecc5ab2a2c5ae3921484d95685d017c7b
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildCallback.java
@@ -0,0 +1,14 @@
+package de.kuschku.util.observables.callbacks;
+
+import android.support.annotation.UiThread;
+
+public interface UIChildCallback {
+    @UiThread
+    void notifyChildItemInserted(int group, int position);
+
+    @UiThread
+    void notifyChildItemChanged(int group, int position);
+
+    @UiThread
+    void notifyChildItemRemoved(int group, int position);
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildParentCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildParentCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..8056c73d0f56a3d3909f83991a5f22f6dd8592c5
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildParentCallback.java
@@ -0,0 +1,4 @@
+package de.kuschku.util.observables.callbacks;
+
+public interface UIChildParentCallback extends UIChildCallback, UIParentCallback {
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIParentCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIParentCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..1508f170e846fe89eaf19e07b4ce2f9cd260f5a9
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIParentCallback.java
@@ -0,0 +1,17 @@
+package de.kuschku.util.observables.callbacks;
+
+import android.support.annotation.UiThread;
+
+public interface UIParentCallback {
+    @UiThread
+    void notifyParentItemInserted(int position);
+
+    @UiThread
+    void notifyParentItemRemoved(int position);
+
+    @UiThread
+    void notifyParentItemChanged(int position);
+
+    @UiThread
+    void notifyParentItemRangeInserted(int from, int to);
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/AdapterUICallbackWrapper.java
similarity index 64%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/AdapterUICallbackWrapper.java
index 7a59f28235010207eabd2588fa522ac8306d4e9b..dd061ab4756bf1b2599ab911e972ea2e54a11856 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/RecyclerViewAdapterCallback.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/AdapterUICallbackWrapper.java
@@ -1,12 +1,21 @@
-package de.kuschku.util.observablelists;
+package de.kuschku.util.observables.callbacks.wrappers;
 
+import android.support.annotation.UiThread;
 import android.support.v7.widget.RecyclerView;
 
-public class RecyclerViewAdapterCallback implements UICallback {
+import de.kuschku.util.observables.AutoScroller;
+import de.kuschku.util.observables.callbacks.UICallback;
+
+@UiThread
+public class AdapterUICallbackWrapper implements UICallback {
     private final RecyclerView.Adapter adapter;
     private final AutoScroller scroller;
 
-    public RecyclerViewAdapterCallback(RecyclerView.Adapter adapter, AutoScroller scroller) {
+    public AdapterUICallbackWrapper(RecyclerView.Adapter adapter) {
+        this(adapter, null);
+    }
+
+    public AdapterUICallbackWrapper(RecyclerView.Adapter adapter, AutoScroller scroller) {
         this.adapter = adapter;
         this.scroller = scroller;
     }
@@ -14,7 +23,7 @@ public class RecyclerViewAdapterCallback implements UICallback {
     @Override
     public void notifyItemInserted(int position) {
         adapter.notifyItemInserted(position);
-        if (position == 0) scroller.notifyScroll();
+        if (position == 0 && scroller != null) scroller.notifyScroll();
     }
 
     @Override
@@ -35,7 +44,7 @@ public class RecyclerViewAdapterCallback implements UICallback {
     @Override
     public void notifyItemRangeInserted(int position, int count) {
         adapter.notifyItemRangeInserted(position, count);
-        if (position == 0) scroller.notifyScroll();
+        if (position == 0 && scroller != null) scroller.notifyScroll();
     }
 
     @Override
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ChildUICallbackWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ChildUICallbackWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..16beac3933a7f728a257ebd88b1d6e8a83ee87a4
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ChildUICallbackWrapper.java
@@ -0,0 +1,66 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.callbacks.UIChildCallback;
+
+@UiThread
+public class ChildUICallbackWrapper implements UICallback {
+    final UIChildCallback wrapped;
+    int groupPosition;
+
+    public ChildUICallbackWrapper(UIChildCallback wrapped) {
+        this.wrapped = wrapped;
+    }
+
+    public int getGroupPosition() {
+        return groupPosition;
+    }
+
+    public void setGroupPosition(int groupPosition) {
+        this.groupPosition = groupPosition;
+    }
+
+    @Override
+    public void notifyItemInserted(int position) {
+        wrapped.notifyChildItemInserted(groupPosition, position);
+    }
+
+    @Override
+    public void notifyItemChanged(int position) {
+        wrapped.notifyChildItemChanged(groupPosition, position);
+    }
+
+    @Override
+    public void notifyItemRemoved(int position) {
+        wrapped.notifyChildItemRemoved(groupPosition, position);
+    }
+
+    @Override
+    public void notifyItemMoved(int from, int to) {
+        notifyItemRemoved(from);
+        notifyItemInserted(to);
+    }
+
+    @Override
+    public void notifyItemRangeInserted(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemInserted(i);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeChanged(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemChanged(i);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeRemoved(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemRemoved(i);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9c9cad133019c66383cff1b1c6fe771f088e2d5
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
@@ -0,0 +1,53 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.callbacks.UICallback;
+
+@UiThread
+public class MultiElementCallbackWrapper<T> implements ElementCallback<T> {
+    Set<ElementCallback<T>> callbacks = new HashSet<>();
+
+    private MultiElementCallbackWrapper(Collection<ElementCallback<T>> callbacks) {
+        this.callbacks.addAll(callbacks);
+    }
+
+    public static <T> MultiElementCallbackWrapper of(ElementCallback<T>... callbacks) {
+        return new MultiElementCallbackWrapper<>(Arrays.asList(callbacks));
+    }
+
+    public void addCallback(ElementCallback<T> callback) {
+        callbacks.add(callback);
+    }
+
+    public void removeCallback(ElementCallback<T> callback) {
+        callbacks.remove(callback);
+    }
+
+    @Override
+    public void notifyItemInserted(T element) {
+        for (ElementCallback<T> callback : callbacks) {
+            callback.notifyItemInserted(element);
+        }
+    }
+
+    @Override
+    public void notifyItemRemoved(T element) {
+        for (ElementCallback<T> callback : callbacks) {
+            callback.notifyItemInserted(element);
+        }
+    }
+
+    @Override
+    public void notifyItemChanged(T element) {
+        for (ElementCallback<T> callback : callbacks) {
+            callback.notifyItemInserted(element);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUICallbackWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUICallbackWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8947af6dcce67be5aee1e56901f52b511d8fa93
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUICallbackWrapper.java
@@ -0,0 +1,80 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import de.kuschku.util.observables.callbacks.UICallback;
+
+@UiThread
+public class MultiUICallbackWrapper implements UICallback {
+    Set<UICallback> callbacks = new HashSet<>();
+
+    private MultiUICallbackWrapper(Collection<UICallback> callbacks) {
+        this.callbacks.addAll(callbacks);
+    }
+
+    public static MultiUICallbackWrapper of(UICallback... callbacks) {
+        return new MultiUICallbackWrapper(Arrays.asList(callbacks));
+    }
+
+    public void addCallback(UICallback callback) {
+        callbacks.add(callback);
+    }
+
+    public void removeCallback(UICallback callback) {
+        callbacks.remove(callback);
+    }
+
+    @Override
+    public void notifyItemInserted(int position) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemInserted(position);
+        }
+    }
+
+    @Override
+    public void notifyItemChanged(int position) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemChanged(position);
+        }
+    }
+
+    @Override
+    public void notifyItemRemoved(int position) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemRemoved(position);
+        }
+    }
+
+    @Override
+    public void notifyItemMoved(int from, int to) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemMoved(from, to);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeInserted(int position, int count) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemRangeInserted(position, count);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeChanged(int position, int count) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemRangeChanged(position, count);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeRemoved(int position, int count) {
+        for (UICallback callback : callbacks) {
+            callback.notifyItemRangeRemoved(position, count);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..949d0d0e02b8c859b94aa7014d3719f180b42fcd
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildCallback.java
@@ -0,0 +1,50 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import de.kuschku.util.observables.callbacks.UIChildCallback;
+
+@UiThread
+public class MultiUIChildCallback implements UIChildCallback {
+    Set<UIChildCallback> callbacks = new HashSet<>();
+
+    private MultiUIChildCallback(Collection<UIChildCallback> callbacks) {
+        this.callbacks.addAll(callbacks);
+    }
+
+    public static MultiUIChildCallback of(UIChildCallback... callbacks) {
+        return new MultiUIChildCallback(Arrays.asList(callbacks));
+    }
+
+
+    public void addCallback(UIChildCallback callback) {
+        this.callbacks.add(callback);
+    }
+
+    public void removeCallback(UIChildCallback callback) {
+        this.callbacks.remove(callback);
+    }
+
+    @Override
+    public void notifyChildItemInserted(int group, int position) {
+        for (UIChildCallback callback : callbacks)
+            callback.notifyChildItemInserted(group, position);
+    }
+
+    @Override
+    public void notifyChildItemChanged(int group, int position) {
+        for (UIChildCallback callback : callbacks)
+            callback.notifyChildItemChanged(group, position);
+    }
+
+    @Override
+    public void notifyChildItemRemoved(int group, int position) {
+        for (UIChildCallback callback : callbacks)
+            callback.notifyChildItemRemoved(group, position);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildParentCallback.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildParentCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f76de191e386afb190a9f18cec50cb6fe60597c
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildParentCallback.java
@@ -0,0 +1,74 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import de.kuschku.util.observables.callbacks.UIChildParentCallback;
+
+@UiThread
+public class MultiUIChildParentCallback implements UIChildParentCallback {
+    Set<UIChildParentCallback> callbacks = new HashSet<>();
+
+    private MultiUIChildParentCallback(Collection<UIChildParentCallback> callbacks) {
+        this.callbacks.addAll(callbacks);
+    }
+
+    public static MultiUIChildParentCallback of(UIChildParentCallback... callbacks) {
+        return new MultiUIChildParentCallback(Arrays.asList(callbacks));
+    }
+
+
+    public void addCallback(UIChildParentCallback callback) {
+        this.callbacks.add(callback);
+    }
+
+    public void removeCallback(UIChildParentCallback callback) {
+        this.callbacks.remove(callback);
+    }
+
+    @Override
+    public void notifyChildItemInserted(int group, int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyChildItemInserted(group, position);
+    }
+
+    @Override
+    public void notifyChildItemChanged(int group, int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyChildItemChanged(group, position);
+    }
+
+    @Override
+    public void notifyChildItemRemoved(int group, int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyChildItemRemoved(group, position);
+    }
+
+    @Override
+    public void notifyParentItemInserted(int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyParentItemInserted(position);
+    }
+
+    @Override
+    public void notifyParentItemRemoved(int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyParentItemRemoved(position);
+    }
+
+    @Override
+    public void notifyParentItemChanged(int position) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyParentItemChanged(position);
+    }
+
+    @Override
+    public void notifyParentItemRangeInserted(int from, int to) {
+        for (UIChildParentCallback callback : callbacks)
+            callback.notifyParentItemRangeInserted(from, to);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ParentUICallbackWrapper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ParentUICallbackWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..aace79c700ae0de5c6de4f445182528d8796043c
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ParentUICallbackWrapper.java
@@ -0,0 +1,57 @@
+package de.kuschku.util.observables.callbacks.wrappers;
+
+import android.support.annotation.UiThread;
+
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.callbacks.UIParentCallback;
+
+@UiThread
+public class ParentUICallbackWrapper implements UICallback {
+    private final UIParentCallback wrapped;
+
+    public ParentUICallbackWrapper(UIParentCallback wrapped) {
+        this.wrapped = wrapped;
+    }
+
+    @Override
+    public void notifyItemInserted(int position) {
+        wrapped.notifyParentItemInserted(position);
+    }
+
+    @Override
+    public void notifyItemChanged(int position) {
+        wrapped.notifyParentItemChanged(position);
+    }
+
+    @Override
+    public void notifyItemRemoved(int position) {
+        wrapped.notifyParentItemRemoved(position);
+    }
+
+    @Override
+    public void notifyItemMoved(int from, int to) {
+        notifyItemRemoved(from);
+        notifyItemInserted(to);
+    }
+
+    @Override
+    public void notifyItemRangeInserted(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemInserted(i);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeChanged(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemChanged(i);
+        }
+    }
+
+    @Override
+    public void notifyItemRangeRemoved(int position, int count) {
+        for (int i = position; i < position + count; i++) {
+            notifyItemRemoved(i);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/IObservableList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/IObservableList.java
new file mode 100644
index 0000000000000000000000000000000000000000..c433fdb7f605ef3cb4f42c7df3838aa80af476f3
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/IObservableList.java
@@ -0,0 +1,8 @@
+package de.kuschku.util.observables.lists;
+
+import java.util.List;
+
+import de.kuschku.util.observables.IObservable;
+
+public interface IObservableList<O, T> extends IObservable<O>, List<T> {
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
new file mode 100644
index 0000000000000000000000000000000000000000..9829b8b18a7a6aefe4776ce2d9fa24ee2de1135a
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
@@ -0,0 +1,33 @@
+package de.kuschku.util.observables.lists;
+
+import de.kuschku.util.observables.ContentComparable;
+import de.kuschku.util.observables.callbacks.UICallback;
+
+public class ObservableComparableSortedList<T extends ContentComparable<T>> extends ObservableSortedList<T> implements IObservableList<UICallback, T> {
+
+
+    public ObservableComparableSortedList(Class<T> cl) {
+        super(cl, new SimpleItemComparator<>());
+    }
+
+    public ObservableComparableSortedList(Class<T> cl, boolean reverse) {
+        super(cl, new SimpleItemComparator<>(), reverse);
+    }
+
+    public static class SimpleItemComparator<T extends ContentComparable<T>> implements ItemComparator<T> {
+        @Override
+        public int compare(T o1, T o2) {
+            return o1.compareTo(o2);
+        }
+
+        @Override
+        public boolean areContentsTheSame(T oldItem, T newItem) {
+            return oldItem.equalsContent(newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(T item1, T item2) {
+            return item1.equals(item2);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableElementList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableElementList.java
new file mode 100644
index 0000000000000000000000000000000000000000..20ba2fe15153984cf22e398f4271d00358b9fc82
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableElementList.java
@@ -0,0 +1,148 @@
+package de.kuschku.util.observables.lists;
+
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.callbacks.wrappers.MultiElementCallbackWrapper;
+
+public class ObservableElementList<T> extends ArrayList<T> implements IObservableList<ElementCallback<T>, T> {
+    MultiElementCallbackWrapper<T> callback = MultiElementCallbackWrapper.<T>of();
+
+    public ObservableElementList(int capacity) {
+        super(capacity);
+    }
+
+    public ObservableElementList() {
+        super();
+    }
+
+    public ObservableElementList(Collection<? extends T> collection) {
+        super(collection);
+    }
+
+    public void addCallback(ElementCallback<T> callback) {
+        this.callback.addCallback(callback);
+    }
+
+    public void removeCallback(ElementCallback<T> callback) {
+        this.callback.removeCallback(callback);
+    }
+
+    private int getPosition() {
+        return isEmpty() ? 0 : size() - 1;
+    }
+
+    @Override
+    public boolean add(T object) {
+        add(getPosition(), object);
+        return true;
+    }
+
+    @Override
+    public void add(int index, T object) {
+        super.add(index, object);
+        callback.notifyItemInserted(object);
+    }
+
+    @Override
+    public boolean addAll(@NonNull Collection<? extends T> collection) {
+        return addAll(getPosition(), collection);
+    }
+
+    @Override
+    public boolean addAll(int index, @NonNull Collection<? extends T> collection) {
+        boolean result = super.addAll(index, collection);
+        if (result)
+            for (T element : collection)
+                callback.notifyItemInserted(element);
+        return result;
+    }
+
+    @Override
+    public T remove(int index) {
+        T result = super.remove(index);
+        callback.notifyItemRemoved(result);
+        return result;
+    }
+
+    @Override
+    public boolean remove(Object object) {
+        int position = indexOf(object);
+        if (position == -1) {
+            return false;
+        } else {
+            remove(position);
+            callback.notifyItemRemoved((T) object);
+            return true;
+        }
+    }
+
+    @Override
+    protected void removeRange(int fromIndex, int toIndex) {
+        for (int i = fromIndex; i < toIndex; i++) {
+            remove(get(i));
+        }
+    }
+
+    @Override
+    public boolean removeAll(@NonNull Collection<?> collection) {
+        return super.removeAll(collection);
+    }
+
+    @Override
+    public boolean retainAll(@NonNull Collection<?> collection) {
+        return super.retainAll(collection);
+    }
+
+    @Override
+    public int indexOf(Object object) {
+        for (int i = 0; i < size(); i++) {
+            if (get(i) == object) return i;
+        }
+        return -1;
+    }
+
+    @Override
+    public int lastIndexOf(Object object) {
+        for (int i = size() - 1; i >= 0; i--) {
+            if (get(i) == object) return i;
+        }
+        return -1;
+    }
+
+    @NonNull
+    @Override
+    public Iterator<T> iterator() {
+        return new CallbackedArrayListIterator<>(super.iterator());
+    }
+
+    class CallbackedArrayListIterator<E> implements Iterator<E> {
+        final Iterator<E> iterator;
+        E current;
+
+        public CallbackedArrayListIterator(Iterator<E> iterator) {
+            this.iterator = iterator;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
+        public E next() {
+            current = iterator.next();
+            return current;
+        }
+
+        @Override
+        public void remove() {
+            iterator.remove();
+            callback.notifyItemRemoved((T) current);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableList.java
similarity index 64%
rename from QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableList.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableList.java
index f6823c0ee4c425a45c2fd0f87883cd3448e8a5b6..a70f8a2c1ee76ef86dbadb948c7bf865a465da3f 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableList.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableList.java
@@ -1,4 +1,4 @@
-package de.kuschku.util.observablelists;
+package de.kuschku.util.observables.lists;
 
 import android.support.annotation.NonNull;
 
@@ -6,13 +6,35 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
 
-public class ObservableList<T> extends ArrayList<T> {
-    UICallback callback;
+import de.kuschku.util.observables.callbacks.UIChildCallback;
+import de.kuschku.util.observables.callbacks.wrappers.MultiUICallbackWrapper;
+import de.kuschku.util.observables.callbacks.UICallback;
 
-    public void setCallback(UICallback callback) {
+public class ObservableList<T> extends ArrayList<T> implements IObservableList<UICallback, T> {
+    MultiUICallbackWrapper callback = MultiUICallbackWrapper.of();
+
+    public ObservableList(int capacity) {
+        super(capacity);
+        this.callback = callback;
+    }
+
+    public ObservableList() {
+        this.callback = callback;
+    }
+
+    public ObservableList(Collection<? extends T> collection) {
+        super(collection);
         this.callback = callback;
     }
 
+    public void addCallback(UICallback callback) {
+        this.callback.addCallback(callback);
+    }
+
+    public void removeCallback(UICallback callback) {
+        this.callback.removeCallback(callback);
+    }
+
     private int getPosition() {
         return isEmpty() ? 0 : size() - 1;
     }
@@ -37,7 +59,8 @@ public class ObservableList<T> extends ArrayList<T> {
     @Override
     public boolean addAll(int index, Collection<? extends T> collection) {
         boolean result = super.addAll(index, collection);
-        if (result) callback.notifyItemRangeInserted(index, collection.size());
+        if (result)
+            callback.notifyItemRangeInserted(index, collection.size());
         return result;
     }
 
@@ -76,6 +99,22 @@ public class ObservableList<T> extends ArrayList<T> {
         return super.retainAll(collection);
     }
 
+    @Override
+    public int indexOf(Object object) {
+        for (int i = 0; i < size(); i++) {
+            if (get(i) == object) return i;
+        }
+        return -1;
+    }
+
+    @Override
+    public int lastIndexOf(Object object) {
+        for (int i = size() - 1; i >= 0; i--) {
+            if (get(i) == object) return i;
+        }
+        return -1;
+    }
+
     @NonNull
     @Override
     public Iterator<T> iterator() {
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
new file mode 100644
index 0000000000000000000000000000000000000000..79622648e3c8ffc66bc502943c6609e51afb91d6
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
@@ -0,0 +1,290 @@
+package de.kuschku.util.observables.lists;
+
+import android.support.annotation.NonNull;
+import android.support.v7.util.SortedList;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import de.kuschku.util.backports.Stream;
+import de.kuschku.util.observables.callbacks.UICallback;
+import de.kuschku.util.observables.callbacks.wrappers.MultiUICallbackWrapper;
+
+public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
+    public final SortedList<T> list;
+    Callback internal = new Callback();
+    boolean reverse;
+
+    MultiUICallbackWrapper callback = MultiUICallbackWrapper.of();
+    ItemComparator<T> comparator;
+
+    @Override
+    public void addCallback(UICallback callback) {
+        this.callback.addCallback(callback);
+    }
+
+    @Override
+    public void removeCallback(UICallback callback) {
+        this.callback.removeCallback(callback);
+    }
+
+    public ObservableSortedList(Class<T> cl, ItemComparator<T> comparator) {
+        this(cl, comparator, false);
+    }
+
+    public ObservableSortedList(Class<T> cl, ItemComparator<T> comparator, boolean reverse) {
+        this.list = new SortedList<>(cl, internal);
+        this.comparator = comparator;
+        this.reverse = reverse;
+    }
+
+    public T last() {
+        if (list.size() == 0) return null;
+
+        return list.get(list.size() - 1);
+    }
+
+    @Override
+    public void add(int location, T object) {
+        list.add(object);
+    }
+
+    @Override
+    public boolean add(T object) {
+        list.add(object);
+        return true;
+    }
+
+    @Override
+    public boolean addAll(int location, @NonNull Collection<? extends T> collection) {
+        list.addAll((Collection<T>) collection);
+        return true;
+    }
+
+    @Override
+    public boolean addAll(@NonNull Collection<? extends T> collection) {
+        list.addAll((Collection<T>) collection);
+        return false;
+    }
+
+    @Override
+    public void clear() {
+        list.clear();
+    }
+
+    @Override
+    public boolean contains(Object object) {
+        return indexOf((T) object) != SortedList.INVALID_POSITION;
+    }
+
+    @Override
+    public boolean containsAll(@NonNull Collection<?> collection) {
+        return new Stream<>(collection).allMatch(this::contains);
+    }
+
+    @Override
+    public T get(int location) {
+        return list.get(location);
+    }
+
+    @Override
+    public int indexOf(Object object) {
+        return list.indexOf((T) object);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return list.size() == 0;
+    }
+
+    @NonNull
+    @Override
+    public Iterator<T> iterator() {
+        return new CallbackedSortedListIterator();
+    }
+
+    @Override
+    public int lastIndexOf(Object object) {
+        return 0;
+    }
+
+    @Override
+    public ListIterator<T> listIterator() {
+        return new CallbackedSortedListIterator();
+    }
+
+    @NonNull
+    @Override
+    public ListIterator<T> listIterator(int location) {
+        return new CallbackedSortedListIterator(location);
+    }
+
+    @Override
+    public T remove(int location) {
+        return null;
+    }
+
+    @Override
+    public boolean remove(Object object) {
+        return false;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> collection) {
+        return false;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> collection) {
+        return false;
+    }
+
+    @Override
+    public T set(int location, T object) {
+        return null;
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public List<T> subList(int start, int end) {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public Object[] toArray() {
+        throw new MaterialDialog.NotImplementedException("Not implemented");
+    }
+
+    @NonNull
+    @Override
+    public <T1> T1[] toArray(T1[] array) {
+        throw new MaterialDialog.NotImplementedException("Not implemented");
+    }
+
+    class Callback extends SortedList.Callback<T> {
+        @Override
+        public int compare(T o1, T o2) {
+            return (reverse) ? comparator.compare(o2, o1) : comparator.compare(o1, o2);
+        }
+
+        @Override
+        public void onInserted(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemInserted(position);
+                else
+                    callback.notifyItemRangeInserted(position, count);
+        }
+
+        @Override
+        public void onRemoved(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemRemoved(position);
+                else
+                    callback.notifyItemRangeRemoved(position, count);
+        }
+
+        @Override
+        public void onMoved(int fromPosition, int toPosition) {
+            if (callback != null)
+                callback.notifyItemMoved(fromPosition, toPosition);
+        }
+
+        @Override
+        public void onChanged(int position, int count) {
+            if (callback != null)
+                if (count == 1)
+                    callback.notifyItemChanged(position);
+                else
+                    callback.notifyItemRangeChanged(position, count);
+        }
+
+        @Override
+        public boolean areContentsTheSame(T oldItem, T newItem) {
+            return comparator.areContentsTheSame(oldItem, newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(T item1, T item2) {
+            return comparator.areItemsTheSame(item1, item2);
+        }
+    }
+
+    class CallbackedSortedListIterator implements Iterator<T>, ListIterator<T> {
+        int position;
+
+        public CallbackedSortedListIterator() {
+            this(0);
+        }
+
+        public CallbackedSortedListIterator(int position) {
+            this.position = position;
+        }
+
+        @Override
+        public void add(T object) {
+            list.add(object);
+        }
+
+        @Override
+        public boolean hasNext() {
+            return list.size() > position + 1;
+        }
+
+        @Override
+        public boolean hasPrevious() {
+            return false;
+        }
+
+        @Override
+        public T next() {
+            return list.get(position++);
+        }
+
+        @Override
+        public int nextIndex() {
+            return position+1;
+        }
+
+        @Override
+        public T previous() {
+            return list.get(position--);
+        }
+
+        @Override
+        public int previousIndex() {
+            return position-1;
+        }
+
+        @Override
+        public void remove() {
+            list.remove(list.get(position));
+            callback.notifyItemRemoved(position);
+        }
+
+        @Override
+        public void set(T object) {
+            list.remove(list.get(position));
+            list.add(object);
+        }
+    }
+
+    public interface ItemComparator<T> {
+        int compare(T o1, T o2);
+
+        boolean areContentsTheSame(T oldItem, T newItem);
+
+        boolean areItemsTheSame(T item1, T item2);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ui/Bindable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/Bindable.java
new file mode 100644
index 0000000000000000000000000000000000000000..ece79503400b9725d0e4b42bc592236e7dc2ee7e
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/Bindable.java
@@ -0,0 +1,5 @@
+package de.kuschku.util.ui;
+
+public interface Bindable<T> {
+    void bind(T t);
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ui/CompatibilityUtils.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/CompatibilityUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e603dfd14080328fb7d8a991859bdb75e8cfc65
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/CompatibilityUtils.java
@@ -0,0 +1,30 @@
+package de.kuschku.util.ui;
+
+import android.os.Build;
+
+public class CompatibilityUtils {
+    private CompatibilityUtils() {
+
+    }
+
+    /**
+     * This method is used to check if the current device supports Sockets with the KeepAlive flag.
+     *
+     * As that feature is only missing on Chromium devices, we just check for that
+     * @return supports KeepAlive
+     */
+    public static boolean deviceSupportsKeepAlive() {
+        return !(Build.MANUFACTURER.toLowerCase().contains("chromium") && Build.BRAND.toLowerCase().contains("chromium"));
+    }
+
+    /**
+     * This method is used to check if the device supports both @link{DeflaterInputStream}
+     * and @link{DeflaterOutputStream}.
+     *
+     * As that feature was only added in KitKat, we just check for the device version.
+     * @return supports DeflaterStream
+     */
+    public static boolean deviceSupportsCompression() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/DateFormatHelper.java
similarity index 91%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/ui/DateFormatHelper.java
index f5620c835640ac35c7ace5b7b9cb97b37ddb5fc1..279f6ac6383de6d481c5a97e8da3b289ea25db8d 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/DateFormatHelper.java
@@ -1,4 +1,4 @@
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util.ui;
 
 import android.content.Context;
 
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ui/MaterialActionBarDrawerToggle.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/MaterialActionBarDrawerToggle.java
new file mode 100644
index 0000000000000000000000000000000000000000..349d763445131eeecf1f84aebbcd3f42f27dbe64
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/MaterialActionBarDrawerToggle.java
@@ -0,0 +1,33 @@
+package de.kuschku.util.ui;
+
+import android.app.Activity;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+
+@UiThread
+public class MaterialActionBarDrawerToggle extends ActionBarDrawerToggle {
+    public MaterialActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
+                                         Toolbar toolbar, @StringRes int openDrawerContentDescRes,
+                                         @StringRes int closeDrawerContentDescRes) {
+        super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes);
+    }
+
+    @Override
+    public void onDrawerClosed(View drawerView) {
+        super.onDrawerClosed(drawerView);
+    }
+
+    @Override
+    public void onDrawerOpened(View drawerView) {
+        super.onDrawerOpened(drawerView);
+    }
+
+    @Override
+    public void onDrawerSlide(View drawerView, float slideOffset) {
+        super.onDrawerSlide(drawerView, 0);
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/SpanFormatter.java
similarity index 99%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/ui/SpanFormatter.java
index 71c1ba9cb861d6777807d9757ce721366c67bc05..fc80a5076083384e9d8f86ca9cc050716ec054de 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/SpanFormatter.java
@@ -14,7 +14,7 @@
 * limitations under the License.
 */
 
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util.ui;
 
 import java.util.Locale;
 import java.util.regex.Matcher;
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/ThemeUtil.java
similarity index 62%
rename from QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java
rename to QuasselDroidNG/src/main/java/de/kuschku/util/ui/ThemeUtil.java
index 514fdc1954f2d18637a9383075854da741a9d394..dee4fa5332ba66bcb34ca33989314190bdeca861 100644
--- a/QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/ThemeUtil.java
@@ -1,15 +1,13 @@
-package de.kuschku.quasseldroid_ng.util;
+package de.kuschku.util.ui;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.support.annotation.ColorInt;
+import android.support.annotation.UiThread;
 
-import butterknife.BindColor;
-import butterknife.ButterKnife;
 import de.kuschku.quasseldroid_ng.R;
-import de.kuschku.util.annotationbind.Binder;
-import de.kuschku.util.annotationbind.Color;
+import de.kuschku.util.annotationbind.AutoBinder;
+import de.kuschku.util.annotationbind.AutoColor;
 
 public class ThemeUtil {
     public final Colors colors = new Colors();
@@ -18,58 +16,62 @@ public class ThemeUtil {
         initColors(ctx.getTheme());
     }
 
+    @UiThread
     public void initColors(Resources.Theme theme) {
         try {
-            Binder.bind(colors, theme);
+            AutoBinder.bind(colors, theme);
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         }
     }
 
     public static class Colors {
-        @Color(android.R.color.transparent)
+        @AutoColor(android.R.color.transparent)
         @ColorInt public int transparent;
 
-        @Color({R.attr.senderColor0, R.attr.senderColor1, R.attr.senderColor2, R.attr.senderColor3,
+        @AutoColor({R.attr.senderColor0, R.attr.senderColor1, R.attr.senderColor2, R.attr.senderColor3,
                 R.attr.senderColor4, R.attr.senderColor5, R.attr.senderColor6, R.attr.senderColor7,
                 R.attr.senderColor8, R.attr.senderColor9, R.attr.senderColorA, R.attr.senderColorB,
                 R.attr.senderColorC, R.attr.senderColorD, R.attr.senderColorE, R.attr.senderColorF})
         @ColorInt public int[] senderColors;
 
-        @Color({R.attr.mircColor0, R.attr.mircColor1, R.attr.mircColor2, R.attr.mircColor3,
+        @AutoColor({R.attr.mircColor0, R.attr.mircColor1, R.attr.mircColor2, R.attr.mircColor3,
                 R.attr.mircColor4, R.attr.mircColor5, R.attr.mircColor6, R.attr.mircColor7,
                 R.attr.mircColor8, R.attr.mircColor9, R.attr.mircColorA, R.attr.mircColorB,
                 R.attr.mircColorC, R.attr.mircColorD, R.attr.mircColorE, R.attr.mircColorF})
         @ColorInt public int[] mircColors;
 
-        @Color(R.attr.colorForeground)
+        @AutoColor(R.attr.colorForeground)
         @ColorInt public int colorForeground;
 
-        @Color(R.attr.colorForegroundHighlight)
+        @AutoColor(R.attr.colorForegroundHighlight)
         @ColorInt public int colorForegroundHighlight;
 
-        @Color(R.attr.colorForegroundSecondary)
+        @AutoColor(R.attr.colorForegroundSecondary)
         @ColorInt public int colorForegroundSecondary;
 
-        @Color(R.attr.colorBackground)
+        @AutoColor(R.attr.colorForegroundAction)
+        @ColorInt public int colorForegroundAction;
+
+        @AutoColor(R.attr.colorBackground)
         @ColorInt public int colorBackground;
 
-        @Color(R.attr.colorBackgroundHighlight)
+        @AutoColor(R.attr.colorBackgroundHighlight)
         @ColorInt public int colorBackgroundHighlight;
 
-        @Color(R.attr.colorBackgroundSecondary)
+        @AutoColor(R.attr.colorBackgroundSecondary)
         @ColorInt public int colorBackgroundSecondary;
 
-        @Color(R.attr.colorBackgroundCard)
+        @AutoColor(R.attr.colorBackgroundCard)
         @ColorInt public int colorBackgroundCard;
 
-        @Color(R.attr.colorTintActivity)
+        @AutoColor(R.attr.colorTintActivity)
         @ColorInt public int colorTintActivity;
 
-        @Color(R.attr.colorTintMessage)
+        @AutoColor(R.attr.colorTintMessage)
         @ColorInt public int colorTintMessage;
 
-        @Color(R.attr.colorTintHighlight)
+        @AutoColor(R.attr.colorTintHighlight)
         @ColorInt public int colorTintHighlight;
     }
 }
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/QVariantParcelable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/QVariantParcelable.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5c559b76f44bcf5c3ff75bfed4a7cb5efecb20f
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/QVariantParcelable.java
@@ -0,0 +1,75 @@
+package de.kuschku.util.ui.parcelableUtil;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+
+import de.kuschku.libquassel.primitives.QMetaType;
+import de.kuschku.libquassel.primitives.QMetaTypeRegistry;
+import de.kuschku.libquassel.primitives.types.QVariant;
+
+public class QVariantParcelable<T> extends QVariant<T> implements Parcelable {
+    public Creator<QVariantParcelable> CREATOR = new Creator<QVariantParcelable>() {
+        @Override
+        public QVariantParcelable createFromParcel(Parcel source) {
+            try {
+                QMetaType type = QMetaTypeRegistry.getType(QMetaType.Type.fromId(source.readInt()));
+                Object data;
+                switch (type.type) {
+                    case Int:
+                        data = source.readInt();
+                        break;
+                    case QByteArray:
+                    case QString:
+                        data = source.readString();
+                        break;
+                    case Bool:
+                        data = (source.readInt() > 0);
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Can’t deserialize type "+type.name);
+                }
+                return new QVariantParcelable<>(type.name, data);
+            } catch (IOException e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        @Override
+        public QVariantParcelable[] newArray(int size) {
+            return new QVariantParcelable[size];
+        }
+    };
+
+    public QVariantParcelable(String typeName, T data) {
+        super(typeName, data);
+    }
+
+    public QVariantParcelable(QVariant value) {
+        super(value.type, (T) value.data);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(type.type.getValue());
+        switch (type.type) {
+            case Int:
+                dest.writeInt((Integer) data);
+                break;
+            case QString:
+                dest.writeString((String) data);
+                break;
+            case Bool:
+                dest.writeInt(((Boolean) data) ? 1 : 0);
+                break;
+            default:
+                throw new IllegalArgumentException("Can’t serialize type "+type.name);
+        }
+    }
+}
diff --git a/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/StorageBackendParcelable.java b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/StorageBackendParcelable.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad148f35840b049411c2cba4087eef897e0cbf5d
--- /dev/null
+++ b/QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/StorageBackendParcelable.java
@@ -0,0 +1,100 @@
+package de.kuschku.util.ui.parcelableUtil;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.kuschku.libquassel.objects.types.StorageBackend;
+import de.kuschku.libquassel.primitives.QMetaType;
+import de.kuschku.libquassel.primitives.types.QVariant;
+import de.kuschku.util.backports.Stream;
+
+public class StorageBackendParcelable extends StorageBackend implements Parcelable {
+    public static Creator<StorageBackendParcelable> CREATOR = new Creator<StorageBackendParcelable>() {
+        @Override
+        public StorageBackendParcelable createFromParcel(Parcel source) {
+            String DisplayName = source.readString();
+            String Description = source.readString();
+            List<String> SetupKeys = new ArrayList<>();
+            source.readStringList(SetupKeys);
+            Map<String, QVariant> SetupDefaults = new HashMap<>();
+            int size = source.readInt();
+            for (int i = 0; i < size; i++) {
+                String key = source.readString();
+                try {
+                    QMetaType.Type type = QMetaType.Type.fromId(source.readInt());
+                    switch (type) {
+                        case Int:
+                            SetupDefaults.put(key, new QVariant<>(type, source.readInt()));
+                            break;
+                        case QString:
+                            SetupDefaults.put(key, new QVariant<>(type, source.readString()));
+                            break;
+                        case Bool:
+                            SetupDefaults.put(key, new QVariant<>(type, source.readInt() > 0));
+                            break;
+                        default:
+                            throw new IllegalArgumentException("Can’t serialize type "+ type.name());
+                    }
+                } catch (IOException e) {
+                }
+            }
+
+            return new StorageBackendParcelable(DisplayName, SetupDefaults, Description, SetupKeys);
+        }
+
+        @Override
+        public StorageBackendParcelable[] newArray(int size) {
+            return new StorageBackendParcelable[size];
+        }
+    };
+
+    public StorageBackendParcelable(StorageBackend backend) {
+        this(backend.DisplayName, backend.SetupDefaults, backend.Description, backend.SetupKeys);
+    }
+
+    public StorageBackendParcelable(String displayName, Map<String, QVariant> setupDefaults, String description, List<String> setupKeys) {
+        super(displayName, setupDefaults, description, setupKeys);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(DisplayName);
+        dest.writeString(Description);
+        dest.writeStringList(SetupKeys);
+        dest.writeInt(SetupDefaults.size());
+        for (String key : SetupDefaults.keySet()) {
+            QVariant q = SetupDefaults.get(key);
+            QMetaType.Type type = q.type.type;
+            dest.writeString(key);
+            dest.writeInt(type.getValue());
+            switch (type) {
+                case Int:
+                    dest.writeInt((Integer) q.data);
+                    break;
+                case QString:
+                    dest.writeString((String) q.data);
+                    break;
+                case Bool:
+                    dest.writeInt(((Boolean) q.data) ? 1 : 0);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Can’t serialize type "+q.type.name);
+            }
+        }
+    }
+
+    public static ArrayList<StorageBackendParcelable> wrap(List<StorageBackend> backends) {
+        return new ArrayList<>(new Stream<>(backends).map(StorageBackendParcelable::new).list());
+    }
+}
diff --git a/QuasselDroidNG/src/main/res/drawable-hdpi/drawer_shadow.9.png b/QuasselDroidNG/src/main/res/drawable-hdpi/drawer_shadow.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..236bff558af07faa3921ba35e2515edf62d04bb9
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-hdpi/drawer_shadow.9.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-hdpi/ic_drawer.png b/QuasselDroidNG/src/main/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 0000000000000000000000000000000000000000..c59f601ca31dae344d0dc95912713ce54a8fbefe
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-hdpi/ic_drawer.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-mdpi/drawer_shadow.9.png b/QuasselDroidNG/src/main/res/drawable-mdpi/drawer_shadow.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffe3a28d77c72094021013c6442560803b3d344c
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-mdpi/drawer_shadow.9.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-mdpi/ic_drawer.png b/QuasselDroidNG/src/main/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ed2c56ee4239ff2987568d4fdae10166650b120
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-mdpi/ic_drawer.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/QuasselDroidNG/src/main/res/drawable-xhdpi/drawer_shadow.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..fabe9d96563785c7d6b008bb3d8da25e816c343c
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-xhdpi/drawer_shadow.9.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-xhdpi/ic_drawer.png b/QuasselDroidNG/src/main/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5fa74def4b40d7eb6826da05bd5e12b836cb999
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-xhdpi/ic_drawer.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/QuasselDroidNG/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..b91e9d7f285e8110ba3ba4e72cc6f0416eb3a30a
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-xxhdpi/drawer_shadow.9.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable-xxhdpi/ic_drawer.png b/QuasselDroidNG/src/main/res/drawable-xxhdpi/ic_drawer.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c4685d6e046ce6c450c19426dce627a88718bfc
Binary files /dev/null and b/QuasselDroidNG/src/main/res/drawable-xxhdpi/ic_drawer.png differ
diff --git a/QuasselDroidNG/src/main/res/drawable/above_shadow.xml b/QuasselDroidNG/src/main/res/drawable/above_shadow.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5965c971e3c17d05d8e8f2ab0be766ccde3cc5de
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/drawable/above_shadow.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:startColor="#20000000"
+        android:endColor="@android:color/transparent"
+        android:angle="90" >
+    </gradient>
+</shape>
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/res/drawable/popup_background_material.xml b/QuasselDroidNG/src/main/res/drawable/popup_background_material.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7e5b00305c1b06bb864bcc241d01f55315f4275f
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/drawable/popup_background_material.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <corners
+        android:radius="2dp" />
+    <solid
+        android:color="?attr/colorBackground" />
+
+</shape>
diff --git a/QuasselDroidNG/src/main/res/layout/activity_core_setup.xml b/QuasselDroidNG/src/main/res/layout/activity_core_setup.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9ed3b79e2341f7c77da1ced51e6788a9c001fc5f
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/activity_core_setup.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".ui.coresetup.CoreSetupActivity">
+
+    <FrameLayout
+        android:id="@+id/host"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:layout_weight="1" />
+</LinearLayout>
diff --git a/QuasselDroidNG/src/main/res/layout/activity_main.xml b/QuasselDroidNG/src/main/res/layout/activity_main.xml
index f1ddf4d84d552e5a56369621045b223fc6b13176..b3e6a9896cd94505e243f42c3850c72c95cb4f67 100644
--- a/QuasselDroidNG/src/main/res/layout/activity_main.xml
+++ b/QuasselDroidNG/src/main/res/layout/activity_main.xml
@@ -1,38 +1,38 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.sothree.slidinguppanel.SlidingUpPanelLayout
+<android.support.v4.widget.DrawerLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/sliding_layout"
+    android:id="@+id/drawer_left"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="bottom"
-    android:orientation="vertical"
-    app:umanoPanelHeight="?attr/actionBarSize"
-    app:umanoShadowHeight="4dp"
-    tools:context=".ui.MainActivity">
+    android:fitsSystemWindows="true"
+    tools:context=".ui.chat.ChatActivity">
 
-    <LinearLayout
+    <com.sothree.slidinguppanel.SlidingUpPanelLayout
+        android:id="@+id/sliding_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical">
+        android:gravity="bottom"
+        android:orientation="vertical"
+        app:umanoPanelHeight="?attr/actionBarSize"
+        app:umanoShadowHeight="4dp">
 
-        <android.support.design.widget.AppBarLayout
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:theme="@style/AppTheme.AppBarOverlay">
+            android:layout_height="match_parent"
+            android:orientation="vertical">
 
-            <android.support.v7.widget.Toolbar
-                android:id="@+id/toolbar"
-                android:layout_width="match_parent"
-                android:layout_height="?attr/actionBarSize"
-                android:background="?attr/colorPrimary"
-                app:popupTheme="@style/AppTheme.PopupOverlay" />
+            <include layout="@layout/toolbar" />
 
-        </android.support.design.widget.AppBarLayout>
+            <include layout="@layout/content_main" />
 
-        <include layout="@layout/content_main"/>
-    </LinearLayout>
+        </LinearLayout>
 
-    <include layout="@layout/slider_main" />
-</com.sothree.slidinguppanel.SlidingUpPanelLayout>
+        <include layout="@layout/slider_main" />
+
+    </com.sothree.slidinguppanel.SlidingUpPanelLayout>
+
+    <include layout="@layout/drawer" />
+
+</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/res/layout/content_main.xml b/QuasselDroidNG/src/main/res/layout/content_main.xml
index dfda993f9f055c5cd41de62db1a0c6780018dd7e..8c1a3509d40dd6756c4c18d36841a347efb16634 100644
--- a/QuasselDroidNG/src/main/res/layout/content_main.xml
+++ b/QuasselDroidNG/src/main/res/layout/content_main.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <android.support.v4.widget.SwipeRefreshLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/swipeview"
+    android:id="@+id/swipe_view"
     android:layout_width="match_parent"
     android:layout_height="0dip"
     android:layout_weight="1"
diff --git a/QuasselDroidNG/src/main/res/layout/drawer.xml b/QuasselDroidNG/src/main/res/layout/drawer.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ff5a722a788f866cbc588f9bd2b3f0537af36ed6
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/drawer.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.NavigationView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/navigation_left"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_gravity="start">
+
+    <LinearLayout
+        android:layout_width="320dp"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:id="@+id/navigation_header_container"
+            android:layout_width="match_parent"
+            android:layout_height="72dp"
+            android:orientation="vertical">
+
+            <android.support.v7.widget.AppCompatImageView
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:src="@drawable/bg"
+                android:scaleType="centerCrop" />
+
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="72dp"
+                android:layout_alignParentBottom="true"
+                android:theme="@style/AppTheme.Light">
+
+                <android.support.v7.widget.AppCompatSpinner
+                    android:id="@+id/buffer_view_spinner"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent" />
+
+            </android.support.v7.widget.Toolbar>
+
+        </RelativeLayout>
+
+        <android.support.v7.widget.RecyclerView
+            android:layout_width="wrap_content"
+            android:layout_height="0dip"
+            android:layout_weight="1" />
+    </LinearLayout>
+
+</android.support.design.widget.NavigationView>
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/res/layout/fragment_storagebackend.xml b/QuasselDroidNG/src/main/res/layout/fragment_storagebackend.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a5aa57164e3ee1503d363d70f2cebffaaece6a72
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/fragment_storagebackend.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <android.support.v7.widget.CardView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        app:cardElevation="4dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:paddingBottom="20dp"
+                android:paddingLeft="@dimen/text_margin"
+                android:paddingRight="@dimen/text_margin"
+                android:paddingTop="20dp">
+
+                <TextView
+                    android:id="@+id/id"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="SQLite"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="16sp" />
+
+                <TextView
+                    android:id="@+id/content"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="This software is superawesome"
+                    android:textColor="?android:attr/textColorSecondary"
+                    android:textSize="14sp" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="48dp"
+                android:background="#f0f0f0"
+                android:orientation="horizontal">
+
+                <android.support.v7.widget.AppCompatButton
+                    android:id="@+id/configure"
+                    style="?attr/buttonStyleSmall"
+                    android:layout_width="0dip"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"
+                    android:background="?attr/selectableItemBackground"
+                    android:drawableEnd="@drawable/ic_arrow_right_grey600_24dp"
+                    android:drawableRight="@drawable/ic_arrow_right_grey600_24dp"
+                    android:paddingLeft="16dp"
+                    android:paddingRight="16dp"
+                    android:text="Configure" />
+            </LinearLayout>
+        </LinearLayout>
+    </android.support.v7.widget.CardView>
+</LinearLayout>
diff --git a/QuasselDroidNG/src/main/res/layout/fragment_storagebackend_list.xml b/QuasselDroidNG/src/main/res/layout/fragment_storagebackend_list.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e0cd3dd5506e8cc35737276dc16e2499f9927f40
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/fragment_storagebackend_list.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/list"
+    android:name="de.kuschku.quasseldroid_ng.ui.StorageBackendFragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginLeft="16dp"
+    android:layout_marginRight="16dp"
+    app:layoutManager="LinearLayoutManager"
+    tools:context=".ui.coresetup.StorageBackendFragment"
+    tools:listitem="@layout/fragment_storagebackend" />
diff --git a/QuasselDroidNG/src/main/res/layout/header.xml b/QuasselDroidNG/src/main/res/layout/header.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5491266cc3424daae04efe88106a77b53de89ce8
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/header.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="56dp"
+    android:background="@drawable/bg">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/res/layout/slider_main.xml b/QuasselDroidNG/src/main/res/layout/slider_main.xml
index 0b34d0308d901000cceea50dfa745bc32a1e47a2..2fcfa021869c04156d20fb9a0b82a9b0c2f8e3af 100644
--- a/QuasselDroidNG/src/main/res/layout/slider_main.xml
+++ b/QuasselDroidNG/src/main/res/layout/slider_main.xml
@@ -11,7 +11,7 @@
         android:background="?attr/chatlineBackground"
         android:orientation="horizontal">
 
-        <EditText
+        <android.support.v7.widget.AppCompatEditText
             android:id="@+id/chatline"
             android:layout_width="0dip"
             android:layout_height="match_parent"
@@ -27,10 +27,10 @@
         <android.support.v7.widget.AppCompatImageButton
             android:id="@+id/send"
             style="?attr/buttonStyleSmall"
+            android:background="?attr/selectableItemBackgroundBorderless"
             android:layout_width="56dp"
             android:layout_height="match_parent"
             android:layout_gravity="center_vertical"
-            android:background="?attr/selectableItemBackgroundBorderless"
             android:padding="12dp"
             android:src="@drawable/ic_send"
             android:tint="?attr/colorAccent" />
diff --git a/QuasselDroidNG/src/main/res/layout/toolbar.xml b/QuasselDroidNG/src/main/res/layout/toolbar.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bb095bc455ca05792d5f93f64b76eaf90f6f6c0a
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/layout/toolbar.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.AppBarLayout
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:theme="@style/AppTheme.AppBarOverlay">
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/actionBarSize"
+        android:background="?attr/colorPrimary"
+        app:popupTheme="@style/AppTheme.PopupOverlay" />
+</android.support.design.widget.AppBarLayout>
\ No newline at end of file
diff --git a/QuasselDroidNG/src/main/res/menu/global.xml b/QuasselDroidNG/src/main/res/menu/global.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e3f1dfa6ed293aea203fdcad92d22971a337c81d
--- /dev/null
+++ b/QuasselDroidNG/src/main/res/menu/global.xml
@@ -0,0 +1,7 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/action_settings" />
+</menu>
diff --git a/QuasselDroidNG/src/main/res/menu/menu_main2.xml b/QuasselDroidNG/src/main/res/menu/menu_main2.xml
deleted file mode 100644
index ef40e14c1ca1ca9f32688cd39029b6e59f222ae9..0000000000000000000000000000000000000000
--- a/QuasselDroidNG/src/main/res/menu/menu_main2.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:context="de.kuschku.quasseldroid_ng.Main2Activity">
-    <item
-        android:id="@+id/action_settings"
-        android:orderInCategory="100"
-        android:title="@string/action_settings"
-        app:showAsAction="never" />
-</menu>
diff --git a/QuasselDroidNG/src/main/res/values/attrs.xml b/QuasselDroidNG/src/main/res/values/attrs.xml
index 7a2bf40c8086a6b7f417fd852af7467a826e3ed5..7d42d8175985d5cc388a31e327acafca96b9111e 100644
--- a/QuasselDroidNG/src/main/res/values/attrs.xml
+++ b/QuasselDroidNG/src/main/res/values/attrs.xml
@@ -47,6 +47,7 @@
     <attr name="colorForeground" format="color" />
     <attr name="colorForegroundHighlight" format="color" />
     <attr name="colorForegroundSecondary" format="color" />
+    <attr name="colorForegroundAction" />
 
     <attr name="colorBackground" format="color" />
     <attr name="colorBackgroundHighlight" format="color" />
diff --git a/QuasselDroidNG/src/main/res/values/dimens.xml b/QuasselDroidNG/src/main/res/values/dimens.xml
index 0222438f9a46ef8e9a898f4ce3e1d2ca95a93342..a90926a73282e23ea4201b5a8b70b28c25d2ef6c 100644
--- a/QuasselDroidNG/src/main/res/values/dimens.xml
+++ b/QuasselDroidNG/src/main/res/values/dimens.xml
@@ -6,4 +6,9 @@
 
     <dimen name="message_horizontal">8dp</dimen>
     <dimen name="message_vertical">4dp</dimen>
+    <dimen name="text_margin">16dp</dimen>
+
+    <!-- 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>
 </resources>
diff --git a/QuasselDroidNG/src/main/res/values/strings.xml b/QuasselDroidNG/src/main/res/values/strings.xml
index 0adaad08a7944fe8a9381b8a9f87dab6643a75fc..ef89ec13c99550d1f30853fd60d82bc508be6ce1 100644
--- a/QuasselDroidNG/src/main/res/values/strings.xml
+++ b/QuasselDroidNG/src/main/res/values/strings.xml
@@ -32,4 +32,10 @@
 
 
     <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="title_activity_core_setup">CoreSetupActivity</string>
+
+    <!-- Preferences -->
+    <string name="preference_theme">preference_theme</string>
+    <string name="open_drawer">open</string>
+    <string name="close_drawer">close</string>
 </resources>
diff --git a/QuasselDroidNG/src/main/res/values/styles.xml b/QuasselDroidNG/src/main/res/values/styles.xml
index d0b683875daa0a4ab1fc267d7334f7521940c80a..3fc5c10a5dba80f37132e88c992d41b9ace1d8f1 100644
--- a/QuasselDroidNG/src/main/res/values/styles.xml
+++ b/QuasselDroidNG/src/main/res/values/styles.xml
@@ -1,7 +1,7 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="MaterialDrawerTheme">
+    <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
         <!-- Customize your theme here. -->
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
@@ -12,7 +12,7 @@
         <item name="chatlineBackground">@color/md_dark_cards</item>
     </style>
 
-    <style name="AppTheme.Light" parent="MaterialDrawerTheme.Light">
+    <style name="AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar">
         <!-- Customize your theme here. -->
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
diff --git a/QuasselDroidNG/src/main/res/values/themes.xml b/QuasselDroidNG/src/main/res/values/themes.xml
index 35f6fbc2ff72e5c4fc9ec0af0162e2bff7f12f84..25a7fa2da910a3d25ae0a0a8e0bc95a8b7a75937 100644
--- a/QuasselDroidNG/src/main/res/values/themes.xml
+++ b/QuasselDroidNG/src/main/res/values/themes.xml
@@ -21,6 +21,7 @@
         <item name="colorForeground">@color/md_dark_primary_text</item>
         <item name="colorForegroundHighlight">@color/md_dark_primary_text</item>
         <item name="colorForegroundSecondary">@color/md_dark_secondary</item>
+        <item name="colorForegroundAction">#7986cb</item>
 
         <item name="colorBackground">@color/md_dark_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
@@ -53,6 +54,7 @@
         <item name="colorForeground">@color/md_light_primary_text</item>
         <item name="colorForegroundHighlight">@color/md_light_primary_text</item>
         <item name="colorForegroundSecondary">@color/md_light_secondary</item>
+        <item name="colorForegroundAction">#1a237e</item>
 
         <item name="colorBackground">@color/md_light_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
@@ -85,6 +87,7 @@
         <item name="colorForeground">@color/md_light_primary_text</item>
         <item name="colorForegroundHighlight">@color/md_light_primary_text</item>
         <item name="colorForegroundSecondary">@color/md_light_secondary</item>
+        <item name="colorForegroundAction">#1a237e</item>
 
         <item name="colorBackground">@color/md_light_background</item>
         <item name="colorBackgroundHighlight">#ff8811</item>
diff --git a/build.gradle b/build.gradle
index 26a7c8629d43a92b38f6b6dac5e05c19f4663a2e..0e466c1a905e6130da24e1f3399e90b34453c066 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.0.0-alpha3'
+        classpath 'com.android.tools.build:gradle:2.0.0-alpha7'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files