From 0f64e2dfd51ccce34482aa3bb4eb4a3b8780de92 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Mon, 25 Jan 2016 20:36:16 +0100
Subject: [PATCH] Updated and refactored code

---
 QuasselDroidNG/build.gradle                   |  25 +-
 QuasselDroidNG/proguard-rules.pro             |  47 +++
 QuasselDroidNG/src/main/AndroidManifest.xml   |  18 +-
 .../java/de/kuschku/libquassel/Client.java    |  12 +-
 .../de/kuschku/libquassel/CoreConnection.java |   7 +-
 .../backlogmanagers/BacklogManager.java       |   4 +-
 .../backlogmanagers/SimpleBacklogManager.java |  14 +-
 .../BufferViewManagerChangedEvent.java        |   2 +-
 .../kuschku/libquassel/localtypes/Buffer.java |   2 -
 .../libquassel/localtypes/ChannelBuffer.java  |   8 -
 .../libquassel/localtypes/QueryBuffer.java    |   8 -
 .../libquassel/localtypes/StatusBuffer.java   |   8 -
 .../kuschku/libquassel/message/Message.java   |   2 +-
 .../StringObjectMapSerializer.java            |   2 +
 .../serializers/IrcChannelSerializer.java     |   4 +-
 .../serializers/NetworkSerializer.java        |   4 +-
 .../syncables/types/BufferSyncer.java         |  15 +-
 .../syncables/types/BufferViewConfig.java     |  19 +-
 .../syncables/types/IrcChannel.java           |   2 +
 .../libquassel/syncables/types/IrcUser.java   |   3 +-
 .../libquassel/syncables/types/Network.java   |  15 +-
 .../syncables/types/SyncableObject.java       |   1 +
 .../{ => service}/ClientBackgroundThread.java |  10 +-
 .../{ => service}/QuasselService.java         |   4 +-
 .../quasseldroid_ng/ui/BufferDrawerItem.java  |  35 ---
 .../quasseldroid_ng/ui/NetworkDrawerItem.java |  37 ---
 .../de/kuschku/quasseldroid_ng/ui/Themes.java |  16 +
 .../quasseldroid_ng/ui/chat/ChatActivity.java | 163 ++++++++++
 .../ui/{ => chat}/MainActivity.java           | 230 ++++++++------
 .../chatview}/ChatMessageRenderer.java        | 235 ++++++++------
 .../{ => chat/chatview}/MessageAdapter.java   |  28 +-
 .../chatview}/MessageViewHolder.java          |   6 +-
 .../ui/chat/drawer/BufferWrapper.java         |  71 +++++
 .../ui/chat/drawer/NetworkWrapper.java        | 271 ++++++++++++++++
 .../ui/coresetup/CoreSetupActivity.java       |  81 +++++
 .../ui/coresetup/StorageBackendFragment.java  |  85 +++++
 .../StorageBackendRecyclerViewAdapter.java    |  78 +++++
 .../util/CompatibilityUtils.java              |  13 -
 .../java/de/kuschku/util/AndroidAssert.java   | 231 ++++++++++++++
 .../java/de/kuschku/util/DrawerUtils.java     |  32 ++
 .../main/java/de/kuschku/util/Objects.java    | 113 +++++++
 .../java/de/kuschku/util/ReflectionUtils.java |  13 +-
 .../util/ServerAddress.java                   |   2 +-
 .../util/annotationbind/AutoBinder.java       |  72 +++++
 .../{Color.java => AutoColor.java}            |   2 +-
 .../util/annotationbind/AutoString.java       |  12 +
 .../kuschku/util/annotationbind/Binder.java   |  43 ---
 .../util/instancestateutil/Storable.java      | 259 ++++++++++++++++
 .../kuschku/util/instancestateutil/Store.java |  46 +++
 .../util => util/irc}/IrcFormatHelper.java    |  22 +-
 .../util => util/irc}/IrcUserUtils.java       |   2 +-
 .../util/niohelpers/WrappedChannel.java       |   3 +
 .../observablelists/ObservableSortedList.java |  79 -----
 .../AutoScroller.java                         |   2 +-
 .../ContentComparable.java                    |   2 +-
 .../kuschku/util/observables/IObservable.java |   8 +
 .../callbacks/ElementCallback.java            |  14 +
 .../callbacks}/UICallback.java                |  11 +-
 .../callbacks/UIChildCallback.java            |  14 +
 .../callbacks/UIChildParentCallback.java      |   4 +
 .../callbacks/UIParentCallback.java           |  17 +
 .../wrappers/AdapterUICallbackWrapper.java}   |  19 +-
 .../wrappers/ChildUICallbackWrapper.java      |  66 ++++
 .../wrappers/MultiElementCallbackWrapper.java |  53 ++++
 .../wrappers/MultiUICallbackWrapper.java      |  80 +++++
 .../wrappers/MultiUIChildCallback.java        |  50 +++
 .../wrappers/MultiUIChildParentCallback.java  |  74 +++++
 .../wrappers/ParentUICallbackWrapper.java     |  57 ++++
 .../observables/lists/IObservableList.java    |   8 +
 .../lists/ObservableComparableSortedList.java |  33 ++
 .../lists/ObservableElementList.java          | 148 +++++++++
 .../lists}/ObservableList.java                |  49 ++-
 .../lists/ObservableSortedList.java           | 290 ++++++++++++++++++
 .../java/de/kuschku/util/ui/Bindable.java     |   5 +
 .../kuschku/util/ui/CompatibilityUtils.java   |  30 ++
 .../util => util/ui}/DateFormatHelper.java    |   2 +-
 .../ui/MaterialActionBarDrawerToggle.java     |  33 ++
 .../util => util/ui}/SpanFormatter.java       |   2 +-
 .../util => util/ui}/ThemeUtil.java           |  42 +--
 .../ui/parcelableUtil/QVariantParcelable.java |  75 +++++
 .../StorageBackendParcelable.java             | 100 ++++++
 .../res/drawable-hdpi/drawer_shadow.9.png     | Bin 0 -> 161 bytes
 .../src/main/res/drawable-hdpi/ic_drawer.png  | Bin 0 -> 2829 bytes
 .../res/drawable-mdpi/drawer_shadow.9.png     | Bin 0 -> 142 bytes
 .../src/main/res/drawable-mdpi/ic_drawer.png  | Bin 0 -> 2820 bytes
 .../res/drawable-xhdpi/drawer_shadow.9.png    | Bin 0 -> 174 bytes
 .../src/main/res/drawable-xhdpi/ic_drawer.png | Bin 0 -> 2836 bytes
 .../res/drawable-xxhdpi/drawer_shadow.9.png   | Bin 0 -> 208 bytes
 .../main/res/drawable-xxhdpi/ic_drawer.png    | Bin 0 -> 202 bytes
 .../src/main/res/drawable/above_shadow.xml    |   8 +
 .../drawable/popup_background_material.xml    |  25 ++
 .../main/res/layout/activity_core_setup.xml   |  14 +
 .../src/main/res/layout/activity_main.xml     |  46 +--
 .../src/main/res/layout/content_main.xml      |   2 +-
 QuasselDroidNG/src/main/res/layout/drawer.xml |  49 +++
 .../res/layout/fragment_storagebackend.xml    |  65 ++++
 .../layout/fragment_storagebackend_list.xml   |  13 +
 QuasselDroidNG/src/main/res/layout/header.xml |   7 +
 .../src/main/res/layout/slider_main.xml       |   4 +-
 .../src/main/res/layout/toolbar.xml           |  14 +
 QuasselDroidNG/src/main/res/menu/global.xml   |   7 +
 .../src/main/res/menu/menu_main2.xml          |  10 -
 QuasselDroidNG/src/main/res/values/attrs.xml  |   1 +
 QuasselDroidNG/src/main/res/values/dimens.xml |   5 +
 .../src/main/res/values/strings.xml           |   6 +
 QuasselDroidNG/src/main/res/values/styles.xml |   4 +-
 QuasselDroidNG/src/main/res/values/themes.xml |   3 +
 build.gradle                                  |   2 +-
 108 files changed, 3515 insertions(+), 574 deletions(-)
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng => libquassel/events}/BufferViewManagerChangedEvent.java (88%)
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/{ => service}/ClientBackgroundThread.java (82%)
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/{ => service}/QuasselService.java (92%)
 delete mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java
 delete mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/Themes.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/{ => chat}/MainActivity.java (69%)
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/{ => chat/chatview}/ChatMessageRenderer.java (61%)
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/{ => chat/chatview}/MessageAdapter.java (59%)
 rename QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/{ => chat/chatview}/MessageViewHolder.java (70%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/CoreSetupActivity.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendFragment.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/ui/coresetup/StorageBackendRecyclerViewAdapter.java
 delete mode 100644 QuasselDroidNG/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/AndroidAssert.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/DrawerUtils.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/Objects.java
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng => }/util/ServerAddress.java (82%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoBinder.java
 rename QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/{Color.java => AutoColor.java} (89%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/AutoString.java
 delete mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/annotationbind/Binder.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Storable.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/instancestateutil/Store.java
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng/util => util/irc}/IrcFormatHelper.java (85%)
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng/util => util/irc}/IrcUserUtils.java (98%)
 delete mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observablelists/ObservableSortedList.java
 rename QuasselDroidNG/src/main/java/de/kuschku/util/{observablelists => observables}/AutoScroller.java (93%)
 rename QuasselDroidNG/src/main/java/de/kuschku/util/{observablelists => observables}/ContentComparable.java (76%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/IObservable.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/ElementCallback.java
 rename QuasselDroidNG/src/main/java/de/kuschku/util/{observablelists => observables/callbacks}/UICallback.java (66%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildCallback.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIChildParentCallback.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/UIParentCallback.java
 rename QuasselDroidNG/src/main/java/de/kuschku/util/{observablelists/RecyclerViewAdapterCallback.java => observables/callbacks/wrappers/AdapterUICallbackWrapper.java} (64%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ChildUICallbackWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiElementCallbackWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUICallbackWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildCallback.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/MultiUIChildParentCallback.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/callbacks/wrappers/ParentUICallbackWrapper.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/IObservableList.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableComparableSortedList.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableElementList.java
 rename QuasselDroidNG/src/main/java/de/kuschku/util/{observablelists => observables/lists}/ObservableList.java (64%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/ui/Bindable.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/ui/CompatibilityUtils.java
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng/util => util/ui}/DateFormatHelper.java (91%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/ui/MaterialActionBarDrawerToggle.java
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng/util => util/ui}/SpanFormatter.java (99%)
 rename QuasselDroidNG/src/main/java/de/kuschku/{quasseldroid_ng/util => util/ui}/ThemeUtil.java (62%)
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/QVariantParcelable.java
 create mode 100644 QuasselDroidNG/src/main/java/de/kuschku/util/ui/parcelableUtil/StorageBackendParcelable.java
 create mode 100644 QuasselDroidNG/src/main/res/drawable-hdpi/drawer_shadow.9.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-hdpi/ic_drawer.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-mdpi/drawer_shadow.9.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-mdpi/ic_drawer.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-xhdpi/drawer_shadow.9.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-xhdpi/ic_drawer.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable-xxhdpi/ic_drawer.png
 create mode 100644 QuasselDroidNG/src/main/res/drawable/above_shadow.xml
 create mode 100644 QuasselDroidNG/src/main/res/drawable/popup_background_material.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/activity_core_setup.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/drawer.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/fragment_storagebackend.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/fragment_storagebackend_list.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/header.xml
 create mode 100644 QuasselDroidNG/src/main/res/layout/toolbar.xml
 create mode 100644 QuasselDroidNG/src/main/res/menu/global.xml
 delete mode 100644 QuasselDroidNG/src/main/res/menu/menu_main2.xml

diff --git a/QuasselDroidNG/build.gradle b/QuasselDroidNG/build.gradle
index 934d869e4..3b4284bde 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 45dc58a59..78c26c21d 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 cff36a31b..fe33a4b07 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 70bb59b03..23ea46ad2 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 3db6d26a0..bc51ea968 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 17eb85768..f8dbd417a 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 0360f62bf..a1c5b2e54 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 32a2a562f..9ce676ca9 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 1e0701a94..c2ed7cb3f 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 96d986a21..3645fd363 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 676aa546a..6619ddd5c 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 807402540..8aecd2096 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 e222d3b78..f4fdce423 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 32816648d..2112caeb8 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 f582ef782..3873b9e02 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 c41bf16da..0beb3ccb8 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 2e6e0f2fe..92a3f8ed4 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 5cc17b730..39aa57104 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 13fe475c4..109ca187e 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 37765e277..7fbf22bff 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 a08e3d417..e756e797c 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 318a2e2e7..fe81fcb0c 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 981a3e435..5e3432723 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 22dda9dda..2b48c615c 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 2df5843fe..000000000
--- 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 4368fc4df..000000000
--- 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 000000000..950a3ae17
--- /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 000000000..844e39aca
--- /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 bb9376ec3..f8f452b52 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 13391ed8f..72c130db2 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 d4d0369d5..e7c9a4803 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 bd7131ad9..1ce428bd8 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 000000000..75ec74b9d
--- /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 000000000..0cf639dad
--- /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 000000000..f07df9656
--- /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 000000000..52e10d805
--- /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 000000000..b8ab6c26d
--- /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 8a3c9ea7c..000000000
--- 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 000000000..3177e9411
--- /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 000000000..c6ae96ca6
--- /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 000000000..a14c6c64a
--- /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 25f4b826f..a3a245e53 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 20b2059ea..33ba15f12 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 000000000..0ec3b7722
--- /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 833948443..0d6f895f9 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 000000000..66d94944c
--- /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 067dc9ca1..000000000
--- 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 000000000..77567673a
--- /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 000000000..868d8c4f9
--- /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 8e272224f..b585c0d85 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 608a35c84..5cdce767e 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 5d0fa64ce..6a27c70fd 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 fef28bb8f..000000000
--- 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 2ac3aead3..5d6171842 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 818c61521..6295bc245 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 000000000..07fe0b644
--- /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 000000000..8a2d08f6f
--- /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 124f9261a..64746340f 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 000000000..8f86cc7ec
--- /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 000000000..8056c73d0
--- /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 000000000..1508f170e
--- /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 7a59f2823..dd061ab47 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 000000000..16beac393
--- /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 000000000..d9c9cad13
--- /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 000000000..b8947af6d
--- /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 000000000..949d0d0e0
--- /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 000000000..5f76de191
--- /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 000000000..aace79c70
--- /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 000000000..c433fdb7f
--- /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 000000000..9829b8b18
--- /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 000000000..20ba2fe15
--- /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 f6823c0ee..a70f8a2c1 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 000000000..79622648e
--- /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 000000000..ece795034
--- /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 000000000..1e603dfd1
--- /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 f5620c835..279f6ac63 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 000000000..349d76344
--- /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 71c1ba9cb..fc80a5076 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 514fdc195..dee4fa533 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 000000000..e5c559b76
--- /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 000000000..ad148f358
--- /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
GIT binary patch
literal 161
zcmeAS@N?(olHy`uVBq!ia0vp^JV0#3!3HEVSgovp6icy_X9x!n)NrJ90QsB+9+AZi
z4BVX{%xHe{^je^xv!{z=h{y5dAOHW`Gf#YA@1xt%!<wY8u#F*!S791MlAJ=C({+~Q
zo-B#02CEW74zFkn<XtS~X4)gdd%^8;eUgEPF2nk)2F-U{Cp-ii$KdJe=d#Wzp$Pz&
C=q+CW

literal 0
HcmV?d00001

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
GIT binary patch
literal 2829
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

literal 0
HcmV?d00001

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
GIT binary patch
literal 142
zcmeAS@N?(olHy`uVBq!ia0vp^96+qZ!3HFgEN0vWQY^(zo*^7SP{WbZ0pxQQctjR6
zFmQK*Fr)d&(`$i(2A(dCAr_~TfBgS%&&>BhUX5L;x$y=|hic;t)=0q~!&M0(2Uj#r
iT+R^X@#lD(V-Z7IzK5^$<X3h;y$qhNelF{r5}E)8ASbo}

literal 0
HcmV?d00001

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
GIT binary patch
literal 2820
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
W<h@@biNGcR0000<MNUMnLSTXz*F3fW

literal 0
HcmV?d00001

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
GIT binary patch
literal 174
zcmeAS@N?(olHy`uVBq!ia0vp^{6Or)!3HEd1bTh|DVAa<&kznEsNqQI0P;BtJR*x3
z7`Qt@n9=;?>9s(?08bak5Rc<;uP@|nU=UzFz%6x@#ly{E6Z5RU$2Hhw*i>Gs`(~~C
zr~2_>(e4Ka`gpa)&de}Ks*u{@U5E@m-v9X3<$3NT3r3p*`<7|@n1Ecw;OXk;vd$@?
F2>^8yH@N@+

literal 0
HcmV?d00001

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
GIT binary patch
literal 2836
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

literal 0
HcmV?d00001

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
GIT binary patch
literal 208
zcmeAS@N?(olHy`uVBq!ia0vp^VnCe4!3HEl*p=S^DajJoh?3y^w370~qErUQl>DSr
z1<%~X^wgl##FWaylc_d9MMa)2jv*QM-d<4Ta$*#5x!8O#VbcvCx78OjjCM19-`|n`
zlcQ;yJWqK-!X?h+v^9}Es?F+hJ07=b>sdT*QRcgm+^%b8|A7C`|A*gYPjm3$2miRv
cpZdzQt0C%<%j~>EK-(ESUHx3vIVCg!0CP)0sQ>@~

literal 0
HcmV?d00001

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
GIT binary patch
literal 202
zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oVGw3ym^DWND5#L^
z5#-Cjkf+YTP`sXjk!=$LL!~YQgWnqlM%L3n1JW26goK+9UQq_B4e)ev49U3n_PU`U
zg8>io#t-TVJM=pEGT)onF!&ZOd;8}O149hcq!N%?hK6(oZAK7-f#HS|n9?%@Qw{w*
cAUc7wfPvd9>y+w~O{pNhr>mdKI;Vst074Kf@&Et;

literal 0
HcmV?d00001

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 000000000..5965c971e
--- /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 000000000..7e5b00305
--- /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 000000000..9ed3b79e2
--- /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 f1ddf4d84..b3e6a9896 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 dfda993f9..8c1a3509d 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 000000000..ff5a722a7
--- /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 000000000..a5aa57164
--- /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 000000000..e0cd3dd55
--- /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 000000000..5491266cc
--- /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 0b34d0308..2fcfa0218 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 000000000..bb095bc45
--- /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 000000000..e3f1dfa6e
--- /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 ef40e14c1..000000000
--- 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 7a2bf40c8..7d42d8175 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 0222438f9..a90926a73 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 0adaad08a..ef89ec13c 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 d0b683875..3fc5c10a5 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 35f6fbc2f..25a7fa2da 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 26a7c8629..0e466c1a9 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
-- 
GitLab