From dfc17fcfc2d938cb8d2f9cc596a44cc5684da18a Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Mon, 12 Sep 2016 02:20:16 +0200
Subject: [PATCH] Added nick list, optimized sorting behaviour

---
 app/build.gradle                              |  10 +-
 .../de/kuschku/libquassel/CoreConnection.java |   5 +-
 .../de/kuschku/libquassel/client/Client.java  |  16 +-
 .../libquassel/ssl/QuasselTrustManager.java   |  14 +-
 .../syncables/SyncableRegistry.java           |   2 +
 .../serializers/CoreInfoSerializer.java       |  86 +++++++
 .../serializers/NetworkSerializer.java        |  26 +-
 .../syncables/types/abstracts/ANetwork.java   |   6 -
 .../types/impl/BufferViewManager.java         |   2 +-
 .../syncables/types/impl/CoreInfo.java        |  54 ++++
 .../syncables/types/impl/IrcChannel.java      |  34 +--
 .../syncables/types/impl/IrcUser.java         |  26 +-
 .../syncables/types/impl/Network.java         |  63 +++--
 .../syncables/types/interfaces/QNetwork.java  |   5 +-
 .../syncables/types/invokers/INetwork.java    |   3 -
 .../quasseldroid_ng/ui/chat/MainActivity.java |  45 ++--
 .../chat/dialogs/CoreInfoDialogBuilder.java   | 114 +++++++++
 .../chat/drawer/BufferViewConfigAdapter.java  |   2 +-
 .../ui/chat/nicklist/NickListAdapter.java     | 181 +++++++++++++
 .../coresettings/network/AllNetworksItem.java |  10 +-
 .../quasseldroid_ng/ui/theme/ThemeUtil.java   |  23 +-
 .../de/kuschku/util/CompatibilityUtils.java   |   3 +-
 .../util/niohelpers/WrappedChannel.java       |   7 +-
 .../lists/ObservableSortedList.java           |   4 +-
 app/src/main/res/drawable/badge.xml           |   2 +-
 .../main/res/layout-w720dp/activity_main.xml  |  93 -------
 app/src/main/res/layout/activity_main.xml     |  12 +
 app/src/main/res/layout/dialog_coreinfo.xml   | 240 ++++++++++++++++++
 app/src/main/res/layout/widget_actionbar.xml  |  22 +-
 .../main/res/layout/widget_channel_mode.xml   |  23 +-
 app/src/main/res/layout/widget_nick.xml       |  75 ++++++
 .../main/res/layout/widget_settings_alias.xml |   2 +-
 app/src/main/res/menu/chat.xml                |   4 +
 app/src/main/res/values/strings_actions.xml   |   2 +
 .../main/res/values/strings_coresettings.xml  |   9 +
 app/src/main/res/values/styles.xml            |  21 ++
 36 files changed, 1017 insertions(+), 229 deletions(-)
 create mode 100644 app/src/main/java/de/kuschku/libquassel/syncables/serializers/CoreInfoSerializer.java
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/dialogs/CoreInfoDialogBuilder.java
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
 delete mode 100644 app/src/main/res/layout-w720dp/activity_main.xml
 create mode 100644 app/src/main/res/layout/dialog_coreinfo.xml
 create mode 100644 app/src/main/res/layout/widget_nick.xml

diff --git a/app/build.gradle b/app/build.gradle
index 38fd8549e..e5b043e51 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -117,6 +117,10 @@ android {
 
         debug {
             applicationIdSuffix ".debug"
+
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
     compileOptions {
@@ -173,14 +177,14 @@ dependencies {
     compile 'com.android.support:preference-v14:24.2.0'
     compile 'com.android.support:cardview-v7:24.2.0'
 
+    // SSL
+    compile 'org.cryptacular:cryptacular:1.1.0'
+
     // Reactive Libs
     compile 'io.reactivex:rxandroid:1.2.1'
     compile 'io.reactivex:rxjava:1.1.6'
     compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
-    compile 'com.jakewharton.rxbinding:rxbinding-support-v4:0.4.0'
     compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
-    compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
-    compile 'com.jakewharton.rxbinding:rxbinding-recyclerview-v7:0.4.0'
 
     // Crashreports
     compile 'ch.acra:acra:4.9.0'
diff --git a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
index eead77323..251b900be 100644
--- a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
+++ b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
@@ -40,6 +40,8 @@ import java.util.Locale;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import javax.security.cert.X509Certificate;
+
 import de.kuschku.libquassel.client.Client;
 import de.kuschku.libquassel.client.ClientData;
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
@@ -95,6 +97,7 @@ public class CoreConnection {
     private Socket socket;
     @NonNull
     private ConnectionChangeEvent.Status status = ConnectionChangeEvent.Status.DISCONNECTED;
+    private X509Certificate[] peerCertificateChain;
 
     public CoreConnection(@NonNull final ServerAddress address,
                           @NonNull final ClientData clientData,
@@ -234,7 +237,7 @@ public class CoreConnection {
     private void setSSL(boolean supportsSSL) {
         if (supportsSSL) {
             try {
-                channel = WrappedChannel.withSSL(getChannel(), certificateManager, address);
+                channel = WrappedChannel.withSSL(getChannel(), certificateManager, address, client::setCertificateChain);
             } catch (Exception e) {
                 if (e.getCause() instanceof UnknownCertificateException) {
                     busProvider.sendEvent(new UnknownCertificateEvent((UnknownCertificateException) e.getCause()));
diff --git a/app/src/main/java/de/kuschku/libquassel/client/Client.java b/app/src/main/java/de/kuschku/libquassel/client/Client.java
index 1da25b58c..f616da732 100644
--- a/app/src/main/java/de/kuschku/libquassel/client/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/client/Client.java
@@ -26,6 +26,7 @@ import android.support.annotation.Nullable;
 import android.util.Log;
 import android.util.Pair;
 
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -58,6 +59,7 @@ import de.kuschku.libquassel.syncables.types.interfaces.QBufferViewManager;
 import de.kuschku.libquassel.syncables.types.interfaces.QIgnoreListManager;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetwork;
 import de.kuschku.libquassel.syncables.types.interfaces.QNetworkConfig;
+import de.kuschku.libquassel.syncables.types.interfaces.QSyncableObject;
 import de.kuschku.util.buffermetadata.BufferMetaDataManager;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
@@ -90,6 +92,7 @@ public class Client extends AClient {
     private long latency;
     private ConnectionChangeEvent.Status connectionStatus;
     private int r = 1;
+    private X509Certificate[] certificateChain;
 
     public Client(@NonNull BusProvider provider, @NonNull BacklogStorage backlogStorage, @NonNull BufferMetaDataManager metaDataManager, String coreId) {
         this.coreId = coreId;
@@ -178,8 +181,8 @@ public class Client extends AClient {
     }
 
     @Override
-    public void ___objectRenamed__(String type, String oldName, String newName) {
-
+    public void ___objectRenamed__(String type, String newName, String oldName) {
+        ((QSyncableObject) unsafe_getObjectByIdentifier(type, oldName)).setObjectName(newName);
     }
 
     public synchronized ConnectionChangeEvent.Status connectionStatus() {
@@ -312,6 +315,7 @@ public class Client extends AClient {
         requestInitObject("AliasManager", "");
         requestInitObject("NetworkConfig", "GlobalNetworkConfig");
         requestInitObject("IgnoreListManager", "");
+        requestInitObject("CoreInfo", "");
         //sendInitRequest("TransferManager", "");
         // This thing never gets sent...
 
@@ -480,4 +484,12 @@ public class Client extends AClient {
     public String coreId() {
         return coreId;
     }
+
+    public X509Certificate[] certificateChain() {
+        return certificateChain;
+    }
+
+    public void setCertificateChain(X509Certificate[] certificateChain) {
+        this.certificateChain = certificateChain;
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java b/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
index 461d2c5eb..3a6edbe89 100644
--- a/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
@@ -34,6 +34,7 @@ import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
 import de.kuschku.util.accounts.ServerAddress;
+import de.kuschku.util.backports.Consumer;
 import de.kuschku.util.certificates.CertificateUtils;
 
 public class QuasselTrustManager implements X509TrustManager {
@@ -43,29 +44,31 @@ public class QuasselTrustManager implements X509TrustManager {
     private final CertificateManager certificateManager;
     @NonNull
     private final ServerAddress address;
+    private final Consumer<X509Certificate[]> callback;
 
-    public QuasselTrustManager(@NonNull X509TrustManager wrapped, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) {
+    public QuasselTrustManager(@NonNull X509TrustManager wrapped, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address, Consumer<X509Certificate[]> callback) {
         this.wrapped = wrapped;
         this.certificateManager = certificateManager;
         this.address = address;
+        this.callback = callback;
     }
 
     @NonNull
-    public static QuasselTrustManager fromFactory(@NonNull TrustManagerFactory factory, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
+    public static QuasselTrustManager fromFactory(@NonNull TrustManagerFactory factory, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address, Consumer<X509Certificate[]> callback) throws GeneralSecurityException {
         TrustManager[] managers = factory.getTrustManagers();
         for (TrustManager manager : managers) {
             if (manager instanceof X509TrustManager) {
-                return new QuasselTrustManager((X509TrustManager) manager, certificateManager, address);
+                return new QuasselTrustManager((X509TrustManager) manager, certificateManager, address, callback);
             }
         }
         throw new GeneralSecurityException("Couldn’t find trustmanager provided by factory");
     }
 
     @NonNull
-    public static QuasselTrustManager fromDefault(@NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
+    public static QuasselTrustManager fromDefault(@NonNull CertificateManager certificateManager, @NonNull ServerAddress address, Consumer<X509Certificate[]> callback) throws GeneralSecurityException {
         TrustManagerFactory factory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
         factory.init((KeyStore) null);
-        return fromFactory(factory, certificateManager, address);
+        return fromFactory(factory, certificateManager, address, callback);
     }
 
     @Override
@@ -83,6 +86,7 @@ public class QuasselTrustManager implements X509TrustManager {
         } catch (CertificateException e) {
             certificateManager.checkTrusted(chain[0], address);
         }
+        callback.apply(chain);
     }
 
     @Override
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java b/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
index a5ba19693..cbfa0d5a9 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/SyncableRegistry.java
@@ -35,6 +35,7 @@ import de.kuschku.libquassel.syncables.serializers.AliasManagerSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferViewConfigSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferViewManagerSerializer;
+import de.kuschku.libquassel.syncables.serializers.CoreInfoSerializer;
 import de.kuschku.libquassel.syncables.serializers.IdentitySerializer;
 import de.kuschku.libquassel.syncables.serializers.IgnoreListManagerSerializer;
 import de.kuschku.libquassel.syncables.serializers.IrcChannelSerializer;
@@ -59,6 +60,7 @@ public class SyncableRegistry {
         map.put("Network", NetworkSerializer.get());
         map.put("NetworkConfig", NetworkConfigSerializer.get());
         map.put("AliasManager", AliasManagerSerializer.get());
+        map.put("CoreInfo", CoreInfoSerializer.get());
     }
 
     private SyncableRegistry() {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/CoreInfoSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/CoreInfoSerializer.java
new file mode 100644
index 000000000..7adba85fb
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/CoreInfoSerializer.java
@@ -0,0 +1,86 @@
+/*
+ * QuasselDroid - Quassel client for Android
+ * Copyright (C) 2016 Janne Koschinski
+ * Copyright (C) 2016 Ken Børge Viktil
+ * Copyright (C) 2016 Magnus Fjell
+ * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.syncables.serializers;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import de.kuschku.libquassel.functions.types.PackedFunction;
+import de.kuschku.libquassel.functions.types.SerializedFunction;
+import de.kuschku.libquassel.functions.types.UnpackedFunction;
+import de.kuschku.libquassel.objects.serializers.ObjectSerializer;
+import de.kuschku.libquassel.primitives.types.QVariant;
+import de.kuschku.libquassel.syncables.types.impl.CoreInfo;
+
+import static de.kuschku.util.AndroidAssert.assertNotNull;
+
+@SuppressWarnings("unchecked")
+public class CoreInfoSerializer implements ObjectSerializer<CoreInfo> {
+    @NonNull
+    private static final CoreInfoSerializer serializer = new CoreInfoSerializer();
+
+    private CoreInfoSerializer() {
+
+    }
+
+    @NonNull
+    public static CoreInfoSerializer get() {
+        return serializer;
+    }
+
+    @Nullable
+    @Override
+    public Map<String, QVariant<Object>> toVariantMap(@NonNull CoreInfo data) {
+        Map<String, QVariant<Object>> map = new HashMap<>();
+        map.put("coreData", (QVariant<Object>) data.coreData());
+        return map;
+    }
+
+    @NonNull
+    @Override
+    public CoreInfo fromDatastream(@NonNull Map<String, QVariant> map) {
+        return fromLegacy(map);
+    }
+
+    @NonNull
+    @Override
+    public CoreInfo fromLegacy(@NonNull Map<String, QVariant> map) {
+        QVariant<Map<String, QVariant>> data = map.get("coreData");
+        assertNotNull(data);
+        return new CoreInfo(
+                data.data
+        );
+    }
+
+    @Nullable
+    @Override
+    public CoreInfo from(@NonNull SerializedFunction function) {
+        if (function instanceof PackedFunction)
+            return fromLegacy(((PackedFunction) function).getData());
+        else if (function instanceof UnpackedFunction)
+            return fromDatastream(((UnpackedFunction) function).getData());
+        else throw new IllegalArgumentException();
+    }
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
index 9eda5e7dd..ed67d9c89 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkSerializer.java
@@ -28,7 +28,6 @@ import org.joda.time.DateTime;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -71,22 +70,9 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
     public Network fromDatastream(@NonNull Map<String, QVariant> map) {
         final Map<String, QVariant<Map<String, QVariant<List>>>> usersAndChannels = ((Map<String, QVariant<Map<String, QVariant<List>>>>) map.get("IrcUsersAndChannels").data);
 
-        final List<QIrcChannel> channels = extractChannels(QVariant.orNull(usersAndChannels.get("Channels")));
-        final List<QIrcUser> users = extractUsers(QVariant.orNull(usersAndChannels.get("Users")));
-
-        final Map<String, QIrcChannel> channelMap = new HashMap<>(channels.size());
-        for (QIrcChannel channel : channels) {
-            channelMap.put(channel.name(), channel);
-        }
-
-        final Map<String, QIrcUser> userMap = new HashMap<>(users.size());
-        for (QIrcUser user : users) {
-            userMap.put(user.nick(), user);
-        }
-
         return new Network(
-                channelMap,
-                userMap,
+                extractChannels(QVariant.orNull(usersAndChannels.get("Channels"))),
+                extractUsers(QVariant.orNull(usersAndChannels.get("Users"))),
                 (List<NetworkServer>) map.get("ServerList").data,
                 StringObjectMapSerializer.<String>get().fromLegacy((Map<String, QVariant>) map.get("Supports").data),
                 (int) map.get("connectionState").data,
@@ -166,15 +152,15 @@ public class NetworkSerializer implements ObjectSerializer<Network> {
         final Map<String, QVariant<Map<String, QVariant<Map<String, QVariant>>>>> usersAndChannels = ((QVariant<Map<String, QVariant<Map<String, QVariant<Map<String, QVariant>>>>>>) map.get("IrcUsersAndChannels")).data;
         final Map<String, QVariant<Map<String, QVariant>>> wrappedChannels = usersAndChannels.get("channels").data;
         final Map<String, QVariant<Map<String, QVariant>>> wrappedUsers = usersAndChannels.get("users").data;
-        final Map<String, QIrcChannel> channels = new HashMap<>(wrappedChannels.size());
+        final List<QIrcChannel> channels = new ArrayList<>(wrappedChannels.size());
         for (Map.Entry<String, QVariant<Map<String, QVariant>>> entry : wrappedChannels.entrySet()) {
             final QIrcChannel ircChannel = IrcChannelSerializer.get().fromLegacy(entry.getValue().data);
-            channels.put(ircChannel.name(), ircChannel);
+            channels.add(ircChannel);
         }
-        final Map<String, QIrcUser> users = new HashMap<>(wrappedUsers.size());
+        final List<QIrcUser> users = new ArrayList<>(wrappedUsers.size());
         for (Map.Entry<String, QVariant<Map<String, QVariant>>> entry : wrappedUsers.entrySet()) {
             final QIrcUser ircUser = IrcUserSerializer.get().fromLegacy(entry.getValue().data);
-            users.put(ircUser.nick(), ircUser);
+            users.add(ircUser);
         }
         final Map<String, String> supports = StringObjectMapSerializer.<String>get().fromLegacy((Map<String, QVariant>) map.get("Supports").data);
         return new Network(
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ANetwork.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ANetwork.java
index 4d8cda108..0e75c5e03 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ANetwork.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/abstracts/ANetwork.java
@@ -207,12 +207,6 @@ public abstract class ANetwork extends SyncableObject<QNetwork> implements QNetw
         syncVar("addIrcChannel", channel);
     }
 
-    @Override
-    public void ircUserNickChanged(String oldnick, String newnick) {
-        _ircUserNickChanged(oldnick, newnick);
-        syncVar("ircUserNickChanged", newnick);
-    }
-
     @Override
     public void connect() {
         _connect();
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewManager.java
index b210b665a..28d536f3d 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/BufferViewManager.java
@@ -45,7 +45,7 @@ public class BufferViewManager extends ABufferViewManager {
     final ObservableSortedList<QBufferViewConfig> list = new ObservableSortedList<>(QBufferViewConfig.class, new ObservableSortedList.ItemComparator<QBufferViewConfig>() {
         @Override
         public int compare(QBufferViewConfig o1, QBufferViewConfig o2) {
-            return o1.bufferViewName().compareTo(o2.bufferViewName());
+            return o1.bufferViewName().compareToIgnoreCase(o2.bufferViewName());
         }
 
         @Override
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/CoreInfo.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/CoreInfo.java
index 3a773ddc1..97160a8e3 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/CoreInfo.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/CoreInfo.java
@@ -23,6 +23,8 @@ package de.kuschku.libquassel.syncables.types.impl;
 
 import android.support.annotation.NonNull;
 
+import org.joda.time.DateTime;
+
 import java.util.Map;
 
 import de.kuschku.libquassel.BusProvider;
@@ -34,6 +36,18 @@ import de.kuschku.libquassel.syncables.types.interfaces.QCoreInfo;
 public class CoreInfo extends ACoreInfo {
     private Map<String, QVariant> coreData;
 
+    private int sessionConnectedClients;
+
+    private String quasselVersion;
+
+    private String quasselBuildDate;
+
+    private DateTime startTime;
+
+    public CoreInfo(Map<String, QVariant> coreData) {
+        _setCoreData(coreData);
+    }
+
     @Override
     public Map<String, QVariant> coreData() {
         return coreData;
@@ -42,6 +56,19 @@ public class CoreInfo extends ACoreInfo {
     @Override
     public void _setCoreData(Map<String, QVariant> coreData) {
         this.coreData = coreData;
+
+        QVariant sessionConnectedClients1 = coreData.remove("sessionConnectedClients");
+        this.sessionConnectedClients = sessionConnectedClients1 != null ? (int) sessionConnectedClients1.data : -1;
+
+        QVariant quasselVersion1 = coreData.remove("quasselVersion");
+        this.quasselVersion = quasselVersion1 != null ? (String) quasselVersion1.data : null;
+
+        QVariant quasselBuildDate1 = coreData.remove("quasselBuildDate");
+        this.quasselBuildDate = quasselBuildDate1 != null ? (String) quasselBuildDate1.data : null;
+
+        QVariant startTime1 = coreData.remove("startTime");
+        this.startTime = startTime1 != null ? (DateTime) startTime1.data : null;
+
         _update();
     }
 
@@ -60,4 +87,31 @@ public class CoreInfo extends ACoreInfo {
         super.init(objectName, provider, client);
         client.setCoreInfo(this);
     }
+
+    public int sessionConnectedClients() {
+        return sessionConnectedClients;
+    }
+
+    public String quasselVersion() {
+        return quasselVersion;
+    }
+
+    public String quasselBuildDate() {
+        return quasselBuildDate;
+    }
+
+    public DateTime startTime() {
+        return startTime;
+    }
+
+    @Override
+    public String toString() {
+        return "CoreInfo{" +
+                "sessionConnectedClients=" + sessionConnectedClients +
+                ", quasselVersion='" + quasselVersion + '\'' +
+                ", quasselBuildDate='" + quasselBuildDate + '\'' +
+                ", startTime=" + startTime +
+                ", coreData=" + coreData +
+                '}';
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
index 2c8398e0f..fcabcef88 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcChannel.java
@@ -142,7 +142,7 @@ public class IrcChannel extends AIrcChannel {
 
     @Override
     public String userModes(String nick) {
-        if (userModes.containsKey(nick))
+        if (userModes.get(nick) != null)
             return Joiner.on("").join(userModes.get(nick));
         else
             return "";
@@ -327,12 +327,14 @@ public class IrcChannel extends AIrcChannel {
 
     @Override
     public void _setUserModes(@NonNull QIrcUser ircuser, String modes) {
-        if (isKnownUser(ircuser)) {
+        if (!isKnownUser(ircuser))
+            return;
 
-            userModes.put(ircuser.nick(), ModeUtils.toModes(modes));
-            users.add(ircuser.nick());
-            _update();
-        }
+        String nick = ircuser.nick();
+        userModes.put(nick, ModeUtils.toModes(modes));
+        users.add(nick);
+        users.notifyItemChanged(nick);
+        _update();
     }
 
     @Override
@@ -345,9 +347,10 @@ public class IrcChannel extends AIrcChannel {
         if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
             return;
 
-        if (!userModes.get(ircuser.nick()).contains(ModeUtils.toMode(mode))) {
-            userModes.get(ircuser.nick()).add(ModeUtils.toMode(mode));
-            users.notifyItemChanged(ircuser.nick());
+        String nick = ircuser.nick();
+        if (!userModes.get(nick).contains(ModeUtils.toMode(mode))) {
+            userModes.get(nick).add(ModeUtils.toMode(mode));
+            users.notifyItemChanged(nick);
             _update();
         }
     }
@@ -362,8 +365,10 @@ public class IrcChannel extends AIrcChannel {
         if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
             return;
 
-        if (userModes.get(ircuser.nick()).contains(ModeUtils.toMode(mode))) {
-            userModes.get(ircuser.nick()).remove(ModeUtils.toMode(mode));
+        String nick = ircuser.nick();
+        if (userModes.get(nick).contains(ModeUtils.toMode(mode))) {
+            userModes.get(nick).remove(ModeUtils.toMode(mode));
+            users.notifyItemChanged(nick);
             _update();
         }
     }
@@ -438,11 +443,8 @@ public class IrcChannel extends AIrcChannel {
         /* TODO: Use just the nick in userModes and users instead – that should make sync things a lot easier */
         if (cachedUserModes != null) {
             for (String username : cachedUserModes.keySet()) {
-                QIrcUser ircUser = network().ircUser(username);
-                if (ircUser != null) {
-                    userModes.put(ircUser.nick(), ModeUtils.toModes(cachedUserModes.get(username)));
-                    users.add(ircUser.nick());
-                }
+                userModes.put(username, ModeUtils.toModes(cachedUserModes.get(username)));
+                users.add(username);
             }
         }
         if (cachedChanModes != null) {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcUser.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcUser.java
index 9c3840c4d..bb22de2bf 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcUser.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/IrcUser.java
@@ -269,12 +269,6 @@ public class IrcUser extends AIrcUser {
     @Override
     public void _setNick(String nick) {
         this.nick = nick;
-        updateObjectName();
-        _update();
-    }
-
-    private void updateObjectName() {
-        setObjectName(getObjectName());
         _update();
     }
 
@@ -284,10 +278,25 @@ public class IrcUser extends AIrcUser {
         return String.format(Locale.US, "%d/%s", network().networkId(), nick());
     }
 
+    @Override
+    public void setObjectName(@Nullable String objectName) {
+        network().ircUserNickChanged(nick, objectName.split("/")[1]);
+        super.setObjectName(objectName);
+    }
+
     @Override
     public void _setRealName(String realName) {
         this.realName = realName;
         _update();
+        for (String channel : this.channels) {
+            QNetwork network = this.network();
+            if (network != null) {
+                QIrcChannel channel1 = network.ircChannel(channel);
+                if (channel1 != null) {
+                    channel1.users().notifyItemChanged(nick);
+                }
+            }
+        }
     }
 
     @Override
@@ -481,6 +490,11 @@ public class IrcUser extends AIrcUser {
     public void _update(QIrcUser from) {
     }
 
+    @Override
+    public void _update() {
+        super._update();
+    }
+
     @NonNull
     @Override
     public String toString() {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/Network.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/Network.java
index d1b2e780c..e21263dca 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/Network.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/impl/Network.java
@@ -26,6 +26,7 @@ import android.support.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -75,13 +76,11 @@ public class Network extends ANetwork implements Observer {
     private IrcModeProvider modeProvider;
     private IrcCaseMappers.IrcCaseMapper caseMapper;
 
-    public Network(Map<String, QIrcChannel> channels,
-                   Map<String, QIrcUser> nicks,
+    public Network(Collection<QIrcChannel> channels,
+                   Collection<QIrcUser> nicks,
                    List<NetworkServer> serverList, Map<String, String> supports,
                    int connectionState, String currentServer, boolean isConnected, int latency,
                    String myNick, NetworkInfo networkInfo) {
-        this.channels = new HashMap<>(channels);
-        this.nicks = new HashMap<>(nicks);
         this.supports = new HashMap<>(supports);
         this.connectionState = ConnectionState.of(connectionState);
         this.currentServer = currentServer;
@@ -92,15 +91,22 @@ public class Network extends ANetwork implements Observer {
         this.networkInfo._setServerList(serverList);
 
         updateCaseMapper();
+
+        this.channels = new HashMap<>();
+        for (QIrcChannel qIrcChannel : channels) {
+            this.channels.put(caseMapper.toLowerCase(qIrcChannel.name()), qIrcChannel);
+        }
+        this.nicks = new HashMap<>();
+        for (QIrcUser qIrcUser : nicks) {
+            this.nicks.put(caseMapper.toLowerCase(qIrcUser.nick()), qIrcUser);
+        }
     }
 
-    public Network(Map<String, QIrcChannel> channels, Map<String, QIrcUser> users, Map<String, String> supports, int connectionState, String currentServer, boolean isConnected, int latency, String myNick, NetworkInfo networkInfo) {
+    public Network(Collection<QIrcChannel> channels, Collection<QIrcUser> users, Map<String, String> supports, int connectionState, String currentServer, boolean isConnected, int latency, String myNick, NetworkInfo networkInfo) {
         this(channels, users, Collections.emptyList(), supports, connectionState, currentServer, isConnected, latency, myNick, networkInfo);
     }
 
-    public Network(Map<String, QIrcChannel> channels, Map<String, QIrcUser> nicks, List<NetworkServer> serverList, Map<String, String> supports, ConnectionState connectionState, String currentServer, boolean isConnected, int latency, String myNick, NetworkInfo networkInfo) {
-        this.channels = new HashMap<>(channels);
-        this.nicks = new HashMap<>(nicks);
+    public Network(Collection<QIrcChannel> channels, Collection<QIrcUser> nicks, List<NetworkServer> serverList, Map<String, String> supports, ConnectionState connectionState, String currentServer, boolean isConnected, int latency, String myNick, NetworkInfo networkInfo) {
         this.supports = new HashMap<>(supports);
         this.connectionState = connectionState;
         this.currentServer = currentServer;
@@ -111,13 +117,22 @@ public class Network extends ANetwork implements Observer {
         this.networkInfo._setServerList(serverList);
 
         updateCaseMapper();
+
+        this.channels = new HashMap<>();
+        for (QIrcChannel qIrcChannel : channels) {
+            this.channels.put(caseMapper.toLowerCase(qIrcChannel.name()), qIrcChannel);
+        }
+        this.nicks = new HashMap<>();
+        for (QIrcUser qIrcUser : nicks) {
+            this.nicks.put(caseMapper.toLowerCase(qIrcUser.nick()), qIrcUser);
+        }
     }
 
     @NonNull
     public static QNetwork create(int network) {
         return new Network(
-                Collections.emptyMap(),
-                Collections.emptyMap(),
+                Collections.emptyList(),
+                Collections.emptyList(),
                 Collections.emptyList(),
                 Collections.emptyMap(),
                 ConnectionState.Disconnected,
@@ -197,6 +212,17 @@ public class Network extends ANetwork implements Observer {
         return index == -1 ? Integer.MAX_VALUE : index;
     }
 
+    @Override
+    public int lowestModeIndex(String mode) {
+        int lowestIndex = Integer.MAX_VALUE;
+        for (String m : CompatibilityUtils.partStringByChar(mode)) {
+            int index = modeToIndex(m);
+            if (index < lowestIndex)
+                lowestIndex = index;
+        }
+        return lowestIndex;
+    }
+
     @NonNull
     @Override
     public ChannelModeType channelModeType(char mode) {
@@ -418,7 +444,7 @@ public class Network extends ANetwork implements Observer {
 
     @Override
     public QIrcUser ircUser(String nickname) {
-        return nicks.get(nickname);
+        return nicks.get(caseMapper.toLowerCase(nickname));
     }
 
     @NonNull
@@ -716,15 +742,15 @@ public class Network extends ANetwork implements Observer {
         IrcUser user = IrcUser.create(mask);
         user.init(this, client);
         client.requestInitObject("IrcUser", user.getObjectName());
-        nicks.put(user.nick(), user);
+        nicks.put(caseMapper.toLowerCase(user.nick()), user);
         _update();
         return user;
     }
 
     @Override
-    public void _ircUserNickChanged(@NonNull String oldNick, @NonNull String newNick) {
+    public void ircUserNickChanged(@NonNull String oldNick, @NonNull String newNick) {
         if (!caseMapper.equalsIgnoreCase(oldNick, newNick)) {
-            nicks.put(newNick, nicks.remove(oldNick));
+            nicks.put(caseMapper.toLowerCase(newNick), nicks.remove(caseMapper.toLowerCase(oldNick)));
             for (QIrcChannel channel : channels.values()) {
                 channel._ircUserNickChanged(oldNick, newNick);
             }
@@ -793,7 +819,7 @@ public class Network extends ANetwork implements Observer {
 
     @Override
     public void _addIrcChannel(@NonNull IrcChannel ircChannel) {
-        channels.put(ircChannel.name(), ircChannel);
+        channels.put(caseMapper.toLowerCase(ircChannel.name()), ircChannel);
     }
 
     @Override
@@ -810,8 +836,11 @@ public class Network extends ANetwork implements Observer {
     @Override
     public void _update() {
         super._update();
-        if (client != null)
-            client.networkManager().networks().notifyItemChanged(client.networkManager().networks().indexOf(this));
+        if (client != null) {
+            int position = client.networkManager().networks().indexOf(this);
+            if (position != -1)
+                client.networkManager().networks().notifyItemChanged(position);
+        }
     }
 
     private void updateDisplay() {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QNetwork.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QNetwork.java
index 9e4fa1af3..5174f07ce 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QNetwork.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/interfaces/QNetwork.java
@@ -59,6 +59,8 @@ public interface QNetwork extends QObservable<QNetwork> {
 
     int modeToIndex(String mode);
 
+    int lowestModeIndex(String mode);
+
     @NonNull
     ChannelModeType channelModeType(final char mode);
 
@@ -314,11 +316,8 @@ public interface QNetwork extends QObservable<QNetwork> {
 
     QIrcUser _updateNickFromMask(final String mask);
 
-    @Synced
     void ircUserNickChanged(String oldNick, String newnick);
 
-    void _ircUserNickChanged(String oldNick, String newnick);
-
     @Synced
     void connect();
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/invokers/INetwork.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/invokers/INetwork.java
index f5d797014..477dd548a 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/invokers/INetwork.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/invokers/INetwork.java
@@ -141,9 +141,6 @@ public class INetwork implements Invoker<QNetwork> {
             case "setNetworkInfo": {
                 obj._setNetworkInfo((NetworkInfo) function.params.get(0));
             } break;
-            case "ircUserNickChanged": {
-                obj._ircUserNickChanged((String) function.params.get(0), (String) function.params.get(1));
-            } break;
             case "connect": {
                 obj._connect();
             } break;
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
index 4b46c6976..a168e595d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MainActivity.java
@@ -80,10 +80,12 @@ import de.kuschku.libquassel.syncables.types.interfaces.QIrcChannel;
 import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread;
+import de.kuschku.quasseldroid_ng.ui.chat.dialogs.CoreInfoDialogBuilder;
 import de.kuschku.quasseldroid_ng.ui.chat.drawer.ActionModeHandler;
 import de.kuschku.quasseldroid_ng.ui.chat.drawer.BufferViewConfigAdapter;
 import de.kuschku.quasseldroid_ng.ui.chat.fragment.ChatFragment;
 import de.kuschku.quasseldroid_ng.ui.chat.fragment.LoadingFragment;
+import de.kuschku.quasseldroid_ng.ui.chat.nicklist.NickListAdapter;
 import de.kuschku.quasseldroid_ng.ui.chat.util.Status;
 import de.kuschku.quasseldroid_ng.ui.coresettings.aliases.AliasListActivity;
 import de.kuschku.quasseldroid_ng.ui.coresettings.chatlist.ChatListListActivity;
@@ -98,6 +100,7 @@ import de.kuschku.util.annotationbind.AutoBinder;
 import de.kuschku.util.certificates.CertificateUtils;
 import de.kuschku.util.certificates.SQLiteCertificateManager;
 import de.kuschku.util.servicebound.BoundActivity;
+import de.kuschku.util.ui.DividerItemDecoration;
 import de.kuschku.util.ui.MenuTint;
 import rx.android.schedulers.AndroidSchedulers;
 
@@ -109,23 +112,32 @@ public class MainActivity extends BoundActivity {
      * This object encapsulates the current status of the activity – opened bufferview, for example
      */
     private final Status status = new Status();
+
     /**
      * Host layout for content fragment, for example showing a loader or the chat
      */
     @Bind(R.id.chatList)
     RecyclerView chatList;
+
+    @Bind(R.id.nickList)
+    RecyclerView nickList;
+
     @Bind(R.id.chatListSpinner)
     AppCompatSpinner chatListSpinner;
+
     @Bind(R.id.chatListToolbar)
     Toolbar chatListToolbar;
+
     @Nullable
     @Bind(R.id.drawer_layout)
     DrawerLayout drawerLayout;
+
     /**
      * Main ActionBar
      */
     @Bind(R.id.toolbar)
     Toolbar toolbar;
+
     private AccountManager manager;
 
     private ToolbarWrapper toolbarWrapper;
@@ -135,6 +147,8 @@ public class MainActivity extends BoundActivity {
 
     private Bundle coreSetupResult;
     private boolean coreSetupCancelled;
+    private CoreInfoDialogBuilder coreInfoDialogBuilder;
+    private NickListAdapter nickListAdapter;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -168,6 +182,8 @@ public class MainActivity extends BoundActivity {
         chatList.setLayoutManager(new LinearLayoutManager(this));
         chatList.setAdapter(chatListAdapter);
 
+        coreInfoDialogBuilder = new CoreInfoDialogBuilder(this);
+
         chatListToolbar.inflateMenu(R.menu.chatlist);
         MenuTint.colorIcons(chatListToolbar.getMenu(), AutoBinder.obtainColor(R.attr.colorFill, chatListToolbar.getContext().getTheme()));
         chatListToolbar.setOnMenuItemClickListener(item -> {
@@ -196,6 +212,12 @@ public class MainActivity extends BoundActivity {
             status.onRestoreInstanceState(savedInstanceState);
 
         manager = new AccountManager(this);
+
+        nickListAdapter = new NickListAdapter(context);
+        nickList.setAdapter(nickListAdapter);
+        nickList.setLayoutManager(new LinearLayoutManager(this));
+        nickList.setItemAnimator(new DefaultItemAnimator());
+        nickList.addItemDecoration(new DividerItemDecoration(this));
     }
 
     @Override
@@ -290,6 +312,10 @@ public class MainActivity extends BoundActivity {
             case R.id.action_aliaslist:
                 startActivity(new Intent(this, AliasListActivity.class));
                 return true;
+            case R.id.action_coreinfo:
+                if (context.client() != null && context.client().coreInfo() != null)
+                    coreInfoDialogBuilder.build(manager.account(context.settings().preferenceLastAccount.get()), context.client().coreInfo(), context.client().certificateChain()).show();
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -301,23 +327,6 @@ public class MainActivity extends BoundActivity {
         finish();
     }
 
-    /*
-
-    private AccountHeader buildAccountHeader() {
-        return new AccountHeaderBuilder()
-                .withActivity(this)
-                .withCompactStyle(true)
-                .withHeaderBackground(R.drawable.bg1)
-                .withProfileImagesVisible(false)
-                .withOnAccountHeaderListener((view, profile, current) -> {
-                    selectBufferViewConfig((int) profile.getIdentifier());
-                    return true;
-                })
-                .build();
-    }
-
-    */
-
     @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
     public void onEventMainThread(ConnectionChangeEvent event) {
         onConnectionChange(event.status);
@@ -350,6 +359,7 @@ public class MainActivity extends BoundActivity {
     }
 
     private void updateBuffer(int id) {
+        nickListAdapter.setChannel(null);
         Client client = context.client();
         if (client != null) {
             Buffer buffer = client.bufferManager().buffer(id);
@@ -364,6 +374,7 @@ public class MainActivity extends BoundActivity {
                     }
                 } else if (buffer instanceof ChannelBuffer) {
                     QIrcChannel channel = ((ChannelBuffer) buffer).getChannel();
+                    nickListAdapter.setChannel(channel);
                     if (channel == null) {
                         toolbarWrapper.setSubtitle(null);
                     } else {
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/dialogs/CoreInfoDialogBuilder.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/dialogs/CoreInfoDialogBuilder.java
new file mode 100644
index 000000000..90d06a355
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/dialogs/CoreInfoDialogBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * QuasselDroid - Quassel client for Android
+ * Copyright (C) 2016 Janne Koschinski
+ * Copyright (C) 2016 Ken Børge Viktil
+ * Copyright (C) 2016 Magnus Fjell
+ * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid_ng.ui.chat.dialogs;
+
+import android.content.Context;
+import android.text.Html;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import org.cryptacular.x509.dn.NameReader;
+import org.cryptacular.x509.dn.StandardAttributeType;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Locale;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import de.kuschku.libquassel.syncables.types.impl.CoreInfo;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.util.accounts.Account;
+import de.kuschku.util.certificates.CertificateUtils;
+
+public class CoreInfoDialogBuilder {
+    private Context context;
+
+    public CoreInfoDialogBuilder(Context context) {
+        this.context = context;
+    }
+
+    public MaterialDialog build(Account account, CoreInfo coreInfo, X509Certificate[] certificateChain) {
+        View view = LayoutInflater.from(context).inflate(R.layout.dialog_coreinfo, null);
+        CoreInfoViewHolder holder = new CoreInfoViewHolder(view);
+        holder.bind(account, coreInfo, certificateChain);
+
+        return new MaterialDialog.Builder(context)
+                .customView(view, true)
+                .backgroundColorAttr(R.attr.colorBackgroundDialog)
+                .positiveColorAttr(R.attr.colorAccent)
+                .positiveText(R.string.actionClose)
+                .build();
+    }
+
+    private String issuerCN(X509Certificate certificate) {
+        return new NameReader(certificate).readIssuer().getValue(StandardAttributeType.CommonName);
+    }
+
+    class CoreInfoViewHolder {
+        @Bind(R.id.address)
+        TextView address;
+
+        @Bind(R.id.verified)
+        TextView verified;
+
+        @Bind(R.id.fingerprint)
+        TextView fingerprint;
+
+        @Bind(R.id.coreVersion)
+        TextView coreVersion;
+
+        @Bind(R.id.coreBuildDate)
+        TextView coreBuildDate;
+
+        @Bind(R.id.uptime)
+        TextView uptime;
+
+        @Bind(R.id.connected)
+        TextView connected;
+
+        public CoreInfoViewHolder(View view) {
+            ButterKnife.bind(this, view);
+        }
+
+        public void bind(Account account, CoreInfo coreInfo, X509Certificate[] certificateChain) {
+            address.setText(String.format(Locale.US, "%s:%d", account.host, account.port));
+            if (certificateChain != null) {
+                verified.setText(context.getString(R.string.labelCoreVerifier, issuerCN(certificateChain[0])));
+                try {
+                    fingerprint.setText(CertificateUtils.certificateToFingerprint(certificateChain[0]));
+                } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+                    fingerprint.setVisibility(View.GONE);
+                }
+            }
+            coreVersion.setText(Html.fromHtml(coreInfo.quasselVersion()));
+            coreBuildDate.setText(coreInfo.quasselBuildDate());
+            uptime.setText(context.getString(R.string.labelCoreUptimeValue, DateUtils.getRelativeTimeSpanString(context, coreInfo.startTime().getMillis())));
+            connected.setText(context.getString(R.string.labelCoreConnectedCientsValue, coreInfo.sessionConnectedClients()));
+        }
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
index 444821be6..b58eacf18 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigAdapter.java
@@ -161,7 +161,7 @@ public class BufferViewConfigAdapter extends ExpandableRecyclerAdapter<NetworkVi
                     } else if (name2 == null) {
                         return -1;
                     } else {
-                        return name1.compareTo(name2);
+                        return name1.compareToIgnoreCase(name2);
                     }
                 }
             }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
new file mode 100644
index 000000000..f4f4f606f
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/nicklist/NickListAdapter.java
@@ -0,0 +1,181 @@
+/*
+ * QuasselDroid - Quassel client for Android
+ * Copyright (C) 2016 Janne Koschinski
+ * Copyright (C) 2016 Ken Børge Viktil
+ * Copyright (C) 2016 Magnus Fjell
+ * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid_ng.ui.chat.nicklist;
+
+import android.support.annotation.NonNull;
+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 butterknife.Bind;
+import butterknife.ButterKnife;
+import de.kuschku.libquassel.syncables.types.interfaces.QIrcChannel;
+import de.kuschku.libquassel.syncables.types.interfaces.QIrcUser;
+import de.kuschku.quasseldroid_ng.R;
+import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
+import de.kuschku.util.CompatibilityUtils;
+import de.kuschku.util.backports.Objects;
+import de.kuschku.util.observables.callbacks.ElementCallback;
+import de.kuschku.util.observables.callbacks.wrappers.AdapterUICallbackWrapper;
+import de.kuschku.util.observables.lists.ObservableSortedList;
+
+public class NickListAdapter extends RecyclerView.Adapter<NickListAdapter.NickViewHolder> {
+    private final AppContext context;
+    QIrcChannel channel;
+
+    List<QIrcUser> list = new ArrayList<>();
+    ObservableSortedList<QIrcUser> users = new ObservableSortedList<>(QIrcUser.class, new ObservableSortedList.ItemComparator<QIrcUser>() {
+        @Override
+        public int compare(QIrcUser o1, QIrcUser o2) {
+            if (channel.userModes(o1).equals(channel.userModes(o2))) {
+                return o1.nick().compareToIgnoreCase(o2.nick());
+            } else {
+                return channel.network().lowestModeIndex(channel.userModes(o1)) - channel.network().lowestModeIndex(channel.userModes(o2));
+            }
+        }
+
+        @Override
+        public boolean areContentsTheSame(QIrcUser oldItem, QIrcUser newItem) {
+            return Objects.equals(oldItem.userModes(), newItem.userModes()) && Objects.equals(oldItem.realName(), newItem.realName());
+        }
+
+        @Override
+        public boolean areItemsTheSame(QIrcUser item1, QIrcUser item2) {
+            return Objects.equals(item1.nick(), item2.nick());
+        }
+    });
+    private ElementCallback<String> callback = new ElementCallback<String>() {
+        @Override
+        public void notifyItemInserted(String element) {
+            QIrcUser qIrcUser = channel.network().ircUser(element);
+            users.add(qIrcUser);
+            list.add(users.indexOf(qIrcUser), qIrcUser);
+        }
+
+        @Override
+        public void notifyItemRemoved(String element) {
+            for (int i = 0; i < users.size(); i++) {
+                QIrcUser user = users.get(i);
+                if (user.nick().equals(element)) {
+                    users.remove(i);
+                    list.remove(user);
+                }
+            }
+        }
+
+        @Override
+        public void notifyItemChanged(String element) {
+            QIrcUser object = channel.network().ircUser(element);
+            if (object != null) {
+                users.notifyItemChanged(list.indexOf(object));
+                list.remove(object);
+                list.add(users.indexOf(object), object);
+            }
+        }
+    };
+
+    public NickListAdapter(AppContext context) {
+        this.context = context;
+        users.addCallback(new AdapterUICallbackWrapper(this));
+    }
+
+    public void setChannel(QIrcChannel channel) {
+        if (this.channel != null)
+            this.channel.users().removeCallback(callback);
+        this.channel = channel;
+        this.users.clear();
+        this.list.clear();
+        if (this.channel != null) {
+            for (String nick : channel.users()) {
+                QIrcUser ircUser = channel.network().ircUser(nick);
+                if (ircUser != null) {
+                    users.add(ircUser);
+                    list.add(users.indexOf(ircUser), ircUser);
+                }
+            }
+            this.channel.users().addCallback(callback);
+        }
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public NickViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        View view = inflater.inflate(R.layout.widget_nick, parent, false);
+        return new NickViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(NickViewHolder holder, int position) {
+        holder.bind(users.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return users.size();
+    }
+
+    @NonNull
+    public String getPrefixedFromMode(String text) {
+        StringBuilder builder = new StringBuilder();
+        for (String s : CompatibilityUtils.partStringByChar(text)) {
+            builder.append(channel.network().modeToPrefix(s));
+        }
+        return builder.toString();
+    }
+
+    class NickViewHolder extends RecyclerView.ViewHolder {
+        @Bind(R.id.mode)
+        TextView mode;
+
+        @Bind(R.id.nick)
+        TextView nick;
+
+        @Bind(R.id.realname)
+        TextView realname;
+
+        public NickViewHolder(View itemView) {
+            super(itemView);
+            ButterKnife.bind(this, itemView);
+        }
+
+        public void bind(QIrcUser qIrcUser) {
+            nick.setText(qIrcUser.nick());
+            realname.setText(qIrcUser.realName());
+            String text = channel.userModes(qIrcUser);
+            if (text.isEmpty()) {
+                mode.setVisibility(View.INVISIBLE);
+                mode.setText(null);
+            } else {
+                mode.setVisibility(View.VISIBLE);
+                String prefixes = getPrefixedFromMode(text);
+                mode.setBackground(context.themeUtil().res.badge(channel.network().lowestModeIndex(text)));
+                mode.setText(prefixes);
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/coresettings/network/AllNetworksItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/coresettings/network/AllNetworksItem.java
index c8fca222c..db4e40448 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/coresettings/network/AllNetworksItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/coresettings/network/AllNetworksItem.java
@@ -105,6 +105,11 @@ class AllNetworksItem implements QNetwork {
         return 0;
     }
 
+    @Override
+    public int lowestModeIndex(String mode) {
+        return 0;
+    }
+
     @NonNull
     @Override
     public ChannelModeType channelModeType(char mode) {
@@ -662,11 +667,6 @@ class AllNetworksItem implements QNetwork {
 
     }
 
-    @Override
-    public void _ircUserNickChanged(String oldNick, String newnick) {
-
-    }
-
     @Override
     public void connect() {
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
index 11c2f7910..f9f801aae 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
@@ -28,8 +28,10 @@ import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
+import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v7.view.ContextThemeWrapper;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
@@ -84,6 +86,7 @@ public class ThemeUtil {
         try {
             res.colors = null;
             AutoBinder.bind(res, wrapper);
+            res.bind(wrapper);
             AutoBinder.bind(translations, wrapper);
             AutoBinder.bind(chanModes, wrapper);
         } catch (IllegalAccessException e) {
@@ -935,16 +938,32 @@ public class ThemeUtil {
         public int actionBarSize;
 
         private SparseIntArray colors;
+        private SparseArray<Drawable> badges;
 
         public int colorToId(int foregroundColor) {
+            return colors.get(foregroundColor, -1);
+        }
+
+        public Drawable badge(int sendercolor) {
+            return badges.get(sendercolor, null);
+        }
+
+        public void bind(ContextThemeWrapper wrapper) {
+            if (badges == null) {
+                badges = new SparseArray<>(16);
+                for (int i = 0; i < senderColors.length; i++) {
+                    Drawable drawable = ResourcesCompat.getDrawable(wrapper.getResources(), R.drawable.badge, wrapper.getTheme());
+                    DrawableCompat.setTint(drawable, senderColors[i]);
+                    badges.put(i, drawable);
+                }
+            }
+
             if (colors == null) {
                 colors = new SparseIntArray(16);
                 for (int i = 0; i < mircColors.length; i++) {
                     colors.put(mircColors[i], i);
                 }
             }
-
-            return colors.get(foregroundColor, -1);
         }
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/CompatibilityUtils.java b/app/src/main/java/de/kuschku/util/CompatibilityUtils.java
index f5eacb925..ecb049d45 100644
--- a/app/src/main/java/de/kuschku/util/CompatibilityUtils.java
+++ b/app/src/main/java/de/kuschku/util/CompatibilityUtils.java
@@ -96,8 +96,9 @@ public class CompatibilityUtils {
     @NonNull
     public static String[] partStringByChar(@NonNull String str) {
         String[] chars = new String[str.length()];
+        char[] charArray = str.toCharArray();
         for (int i = 0; i < chars.length; i++) {
-            chars[i] = str.substring(i, i + 1);
+            chars[i] = new String(charArray, i, 1);
         }
         return chars;
     }
diff --git a/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java b/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
index 829c65f8d..7f73372ce 100644
--- a/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
+++ b/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
@@ -36,6 +36,7 @@ import java.nio.channels.ByteChannel;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.InterruptibleChannel;
 import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.InflaterInputStream;
 
@@ -48,6 +49,7 @@ import de.kuschku.libquassel.ssl.CertificateManager;
 import de.kuschku.libquassel.ssl.QuasselTrustManager;
 import de.kuschku.util.CompatibilityUtils;
 import de.kuschku.util.accounts.ServerAddress;
+import de.kuschku.util.backports.Consumer;
 
 public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChannel {
     @Nullable
@@ -95,9 +97,10 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
 
     public static WrappedChannel withSSL(@NonNull WrappedChannel channel,
                                          @NonNull CertificateManager certificateManager,
-                                         @NonNull ServerAddress address) throws GeneralSecurityException, IOException {
+                                         @NonNull ServerAddress address,
+                                         @NonNull Consumer<X509Certificate[]> callback) throws GeneralSecurityException, IOException {
         SSLContext context = SSLContext.getInstance("TLSv1.2");
-        TrustManager[] managers = new TrustManager[]{QuasselTrustManager.fromDefault(certificateManager, address)};
+        TrustManager[] managers = new TrustManager[]{QuasselTrustManager.fromDefault(certificateManager, address, callback)};
         context.init(null, managers, null);
         SSLSocketFactory factory = context.getSocketFactory();
         SSLSocket socket = (SSLSocket) factory.createSocket(channel.socket, address.host, address.port, true);
diff --git a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
index eb8155929..68ad2a8f3 100644
--- a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
+++ b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
@@ -246,7 +246,9 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
     }
 
     public void notifyItemChanged(int position) {
-        callback.notifyItemChanged(position);
+        T obj = get(position);
+        list.recalculatePositionOfItemAt(position);
+        callback.notifyItemChanged(indexOf(obj));
     }
 
     @NonNull
diff --git a/app/src/main/res/drawable/badge.xml b/app/src/main/res/drawable/badge.xml
index 8d3a3640f..de1160f36 100644
--- a/app/src/main/res/drawable/badge.xml
+++ b/app/src/main/res/drawable/badge.xml
@@ -20,7 +20,7 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@color/md_red_500"/>
+    <solid/>
     <padding
         android:bottom="10dp"
         android:left="10dp"
diff --git a/app/src/main/res/layout-w720dp/activity_main.xml b/app/src/main/res/layout-w720dp/activity_main.xml
deleted file mode 100644
index 8c95f4d18..000000000
--- a/app/src/main/res/layout-w720dp/activity_main.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ QuasselDroid - Quassel client for Android
-  ~ Copyright (C) 2016 Janne Koschinski
-  ~ Copyright (C) 2016 Ken Børge Viktil
-  ~ Copyright (C) 2016 Magnus Fjell
-  ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
-  ~
-  ~ This program is free software: you can redistribute it and/or modify it
-  ~ under the terms of the GNU General Public License as published by the Free
-  ~ Software Foundation, either version 3 of the License, or (at your option)
-  ~ any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License along
-  ~ with this program.  If not, see <http://www.gnu.org/licenses/>.
-  -->
-
-<LinearLayout
-    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:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="horizontal"
-    tools:context=".ui.chat.MainActivity">
-
-    <LinearLayout
-        android:layout_width="300dp"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-        <android.support.design.widget.AppBarLayout
-            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:theme="?attr/actionBarTheme">
-
-            <FrameLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-
-                <android.support.v7.widget.Toolbar
-                    android:id="@+id/chatListToolbar"
-                    android:layout_width="match_parent"
-                    android:layout_height="?attr/actionBarSize"
-                    android:background="?attr/colorPrimary"
-                    app:popupTheme="@style/AppTheme.PopupOverlay">
-
-                    <android.support.v7.widget.AppCompatSpinner
-                        android:id="@+id/chatListSpinner"
-                        android:layout_width="fill_parent"
-                        android:layout_height="match_parent"
-                        app:popupTheme="@style/AppTheme.PopupOverlay"/>
-
-                </android.support.v7.widget.Toolbar>
-
-                <ViewStub
-                    android:id="@+id/cab_stub"
-                    android:layout_width="match_parent"
-                    android:layout_height="?actionBarSize"/>
-
-            </FrameLayout>
-
-        </android.support.design.widget.AppBarLayout>
-
-        <android.support.v7.widget.RecyclerView
-            android:id="@+id/chatList"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-        <include layout="@layout/widget_actionbar"/>
-
-        <FrameLayout
-            android:id="@+id/content_host"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 867045ed9..3a8dbc9c0 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -43,6 +43,18 @@
 
     </LinearLayout>
 
+    <android.support.design.widget.NavigationView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="end">
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/nickList"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+    </android.support.design.widget.NavigationView>
+
     <android.support.design.widget.NavigationView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/dialog_coreinfo.xml b/app/src/main/res/layout/dialog_coreinfo.xml
new file mode 100644
index 000000000..e4dcc51e2
--- /dev/null
+++ b/app/src/main/res/layout/dialog_coreinfo.xml
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ QuasselDroid - Quassel client for Android
+  ~ Copyright (C) 2016 Janne Koschinski
+  ~ Copyright (C) 2016 Ken Børge Viktil
+  ~ Copyright (C) 2016 Magnus Fjell
+  ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
+  ~
+  ~ This program is free software: you can redistribute it and/or modify it
+  ~ under the terms of the GNU General Public License as published by the Free
+  ~ Software Foundation, either version 3 of the License, or (at your option)
+  ~ any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License along
+  ~ with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<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="wrap_content"
+    android:orientation="vertical"
+    android:paddingBottom="16dp"
+    android:paddingTop="16dp">
+
+    <android.support.v7.widget.CardView
+        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:layout_margin="4dp"
+        app:cardBackgroundColor="?colorBackgroundCard"
+        app:cardUseCompatPadding="true"
+        app:contentPaddingBottom="16dp"
+        app:contentPaddingLeft="16dp"
+        app:contentPaddingRight="16dp"
+        app:contentPaddingTop="16dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/labelCoreAddress"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
+                android:textColor="?colorForeground"/>
+
+            <TextView
+                android:id="@+id/address"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
+                android:textColor="?colorForeground"
+                tools:text="example.com:4242"/>
+
+        </LinearLayout>
+
+    </android.support.v7.widget.CardView>
+
+    <android.support.v7.widget.CardView
+        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:layout_margin="4dp"
+        app:cardBackgroundColor="?colorBackgroundCard"
+        app:cardUseCompatPadding="true"
+        app:contentPaddingBottom="16dp"
+        app:contentPaddingLeft="16dp"
+        app:contentPaddingRight="16dp"
+        app:contentPaddingTop="16dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/labelCoreCertificate"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
+                android:textColor="?colorForeground"/>
+
+            <TextView
+                android:id="@+id/verified"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
+                android:textColor="?colorForeground"
+                tools:text="The connection is verified by Let's Encrypt"/>
+
+            <TextView
+                android:id="@+id/fingerprint"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+                android:textColor="?colorForegroundSecondary"
+                tools:text="25:EF:C0:88:42:96:6C:66:2C:72:01:0E:2B:61:84:B2:D8:9A:AB:4D"/>
+
+        </LinearLayout>
+
+    </android.support.v7.widget.CardView>
+
+    <android.support.v7.widget.CardView
+        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:layout_margin="4dp"
+        app:cardBackgroundColor="?colorBackgroundCard"
+        app:cardUseCompatPadding="true"
+        app:contentPaddingBottom="16dp"
+        app:contentPaddingLeft="16dp"
+        app:contentPaddingRight="16dp"
+        app:contentPaddingTop="16dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/labelCoreVersion"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
+                android:textColor="?colorForeground"/>
+
+            <TextView
+                android:id="@+id/coreVersion"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
+                android:textColor="?colorForeground"
+                tools:text="v0.13-pre (0.12.0+245 git-d6129e6)"/>
+
+            <TextView
+                android:id="@+id/coreBuildDate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+                android:textColor="?colorForegroundSecondary"
+                tools:text="Wed Sep 7 20:52:53 2016"/>
+
+        </LinearLayout>
+
+    </android.support.v7.widget.CardView>
+
+    <android.support.v7.widget.CardView
+        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:layout_margin="4dp"
+        app:cardBackgroundColor="?colorBackgroundCard"
+        app:cardUseCompatPadding="true"
+        app:contentPaddingBottom="16dp"
+        app:contentPaddingLeft="16dp"
+        app:contentPaddingRight="16dp"
+        app:contentPaddingTop="16dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/labelCoreUptime"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
+                android:textColor="?colorForeground"/>
+
+            <TextView
+                android:id="@+id/uptime"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
+                android:textColor="?colorForeground"
+                tools:text="The core has been last restarted on 07.09.2016 19:23:31"/>
+
+        </LinearLayout>
+
+    </android.support.v7.widget.CardView>
+
+    <android.support.v7.widget.CardView
+        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:layout_margin="4dp"
+        app:cardBackgroundColor="?colorBackgroundCard"
+        app:cardUseCompatPadding="true"
+        app:contentPaddingBottom="16dp"
+        app:contentPaddingLeft="16dp"
+        app:contentPaddingRight="16dp"
+        app:contentPaddingTop="16dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/labelCoreConnectedClients"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
+                android:textColor="?colorForeground"/>
+
+            <TextView
+                android:id="@+id/connected"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
+                android:textColor="?colorForeground"
+                tools:text="At the moment, 2 clients are connected under your account."/>
+
+        </LinearLayout>
+
+    </android.support.v7.widget.CardView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_actionbar.xml b/app/src/main/res/layout/widget_actionbar.xml
index f8f3e6b0d..4ab110165 100644
--- a/app/src/main/res/layout/widget_actionbar.xml
+++ b/app/src/main/res/layout/widget_actionbar.xml
@@ -34,17 +34,17 @@
         android:background="?attr/colorPrimary"
         app:popupTheme="@style/AppTheme.PopupOverlay">
 
-        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                      android:id="@+id/toolbar_action_area"
-                      android:layout_width="fill_parent"
-                      android:layout_height="fill_parent"
-                      android:background="?attr/selectableItemBackgroundBorderless"
-                      android:clickable="true"
-                      android:gravity="center_vertical|start"
-                      android:minHeight="?attr/actionBarSize"
-                      android:orientation="vertical"
-                      android:paddingLeft="@dimen/action_bar_default_padding_start_material"
-                      android:paddingStart="@dimen/action_bar_default_padding_start_material">
+        <LinearLayout
+            android:id="@+id/toolbar_action_area"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:background="?attr/selectableItemBackgroundBorderless"
+            android:clickable="true"
+            android:gravity="center_vertical|start"
+            android:minHeight="?attr/actionBarSize"
+            android:orientation="vertical"
+            android:paddingLeft="@dimen/action_bar_default_padding_start_material"
+            android:paddingStart="@dimen/action_bar_default_padding_start_material">
 
             <LinearLayout
                 android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/widget_channel_mode.xml b/app/src/main/res/layout/widget_channel_mode.xml
index 672ddc397..405cd25ef 100644
--- a/app/src/main/res/layout/widget_channel_mode.xml
+++ b/app/src/main/res/layout/widget_channel_mode.xml
@@ -19,17 +19,18 @@
   ~ with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<android.support.v7.widget.CardView 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:layout_margin="4dp"
-                                    app:cardBackgroundColor="?attr/colorBackgroundCard"
-                                    app:cardUseCompatPadding="true"
-                                    app:contentPaddingBottom="16dp"
-                                    app:contentPaddingLeft="16dp"
-                                    app:contentPaddingRight="16dp"
-                                    app:contentPaddingTop="16dp">
+<android.support.v7.widget.CardView
+    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:layout_margin="4dp"
+    app:cardBackgroundColor="?attr/colorBackgroundCard"
+    app:cardUseCompatPadding="true"
+    app:contentPaddingBottom="16dp"
+    app:contentPaddingLeft="16dp"
+    app:contentPaddingRight="16dp"
+    app:contentPaddingTop="16dp">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/widget_nick.xml b/app/src/main/res/layout/widget_nick.xml
new file mode 100644
index 000000000..d3fd352a9
--- /dev/null
+++ b/app/src/main/res/layout/widget_nick.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ QuasselDroid - Quassel client for Android
+  ~ Copyright (C) 2016 Janne Koschinski
+  ~ Copyright (C) 2016 Ken Børge Viktil
+  ~ Copyright (C) 2016 Magnus Fjell
+  ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org>
+  ~
+  ~ This program is free software: you can redistribute it and/or modify it
+  ~ under the terms of the GNU General Public License as published by the Free
+  ~ Software Foundation, either version 3 of the License, or (at your option)
+  ~ any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License along
+  ~ with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<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="56dp"
+              android:background="?selectableItemBackground"
+              android:clickable="true"
+              android:orientation="horizontal"
+              android:paddingLeft="?listPreferredItemPaddingLeft"
+              android:paddingRight="?listPreferredItemPaddingRight">
+
+    <TextView
+        android:id="@+id/mode"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:layout_gravity="center_vertical"
+        android:background="@drawable/badge"
+        android:gravity="center"
+        android:textColor="?colorFill"
+        tools:text="\@"
+        />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_weight="1"
+        android:gravity="center_vertical|start"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/nick"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-medium"
+            android:gravity="center_vertical|start"
+            android:lines="1"
+            android:singleLine="true"
+            android:textSize="@dimen/material_drawer_item_primary_text"
+            tools:text="justJanne"/>
+
+        <TextView
+            android:id="@+id/realname"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif"
+            android:gravity="center_vertical|start"
+            android:lines="1"
+            android:singleLine="true"
+            android:textSize="@dimen/material_drawer_item_primary_description"
+            tools:text="Janne Koschinski: https://kuschku.de/"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_settings_alias.xml b/app/src/main/res/layout/widget_settings_alias.xml
index 5a2706480..61adbd59b 100644
--- a/app/src/main/res/layout/widget_settings_alias.xml
+++ b/app/src/main/res/layout/widget_settings_alias.xml
@@ -32,7 +32,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center_vertical"
+        android:layout_gravity="center_vertical"
         android:orientation="vertical">
 
         <TextView
diff --git a/app/src/main/res/menu/chat.xml b/app/src/main/res/menu/chat.xml
index 3fd95e90d..85f7aca9c 100644
--- a/app/src/main/res/menu/chat.xml
+++ b/app/src/main/res/menu/chat.xml
@@ -51,6 +51,10 @@
         android:id="@+id/action_aliaslist"
         android:title="@string/actionAlias"
         app:showAsAction="never"/>
+    <item
+        android:id="@+id/action_coreinfo"
+        android:title="@string/actionCore"
+        app:showAsAction="never"/>
     <item
         android:id="@+id/action_reauth"
         android:title="@string/actionDisconnect"
diff --git a/app/src/main/res/values/strings_actions.xml b/app/src/main/res/values/strings_actions.xml
index b00ff6fe2..ab5ab0d3b 100644
--- a/app/src/main/res/values/strings_actions.xml
+++ b/app/src/main/res/values/strings_actions.xml
@@ -25,6 +25,8 @@
     <string name="actionAddAccount">Add Account</string>
     <string name="actionAlias">Aliases</string>
     <string name="actionCancel">Cancel</string>
+    <string name="actionClose">Close</string>
+    <string name="actionCore">About Core…</string>
     <string name="actionConnect">Connect</string>
     <string name="actionDelete">Delete</string>
     <string name="actionDisconnect">Disconnect</string>
diff --git a/app/src/main/res/values/strings_coresettings.xml b/app/src/main/res/values/strings_coresettings.xml
index c61ad23b7..6797493ad 100644
--- a/app/src/main/res/values/strings_coresettings.xml
+++ b/app/src/main/res/values/strings_coresettings.xml
@@ -102,4 +102,13 @@
     <string name="labelNetworkServerProxyDefault">Default Proxy</string>
     <string name="labelNetworkServerProxySocks5">Socks5</string>
     <string name="labelNetworkServerProxyHttp">Http</string>
+
+    <string name="labelCoreUptimeValue">This core has been online since %1$s</string>
+    <string name="labelCoreUptime">Uptime</string>
+    <string name="labelCoreConnectedClients">Connected Clients</string>
+    <string name="labelCoreConnectedCientsValue">At the moment, %1d clients are logged in as you.</string>
+    <string name="labelCoreVersion">Core Version</string>
+    <string name="labelCoreCertificate">Certificate</string>
+    <string name="labelCoreAddress">Address</string>
+    <string name="labelCoreVerifier">The connection is verified by %1$s</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7c1e417cc..f645b9635 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -128,6 +128,27 @@
         <item name="colorAway">@color/colorAwayDark</item>
     </style>
 
+    <style name="AppTheme.Light.Dialog" parent="MaterialBaseTheme.Light.AlertDialog">
+        <item name="colorForeground">#DE000000</item>
+        <item name="colorForegroundHighlight">#DE000000</item>
+        <item name="colorForegroundSecondary">#8A000000</item>
+        <item name="colorForegroundAction">#1a237e</item>
+        <item name="colorForegroundError">#800000</item>
+
+        <item name="colorForegroundMirc">0x1</item>
+
+        <item name="colorBackground">#FAFAFA</item>
+        <item name="android:windowBackground">@color/quasselLight_background</item>
+        <item name="colorBackgroundHighlight">#ff8811</item>
+        <item name="colorBackgroundSecondary">@null</item>
+        <item name="colorBackgroundCard">#FFFFFF</item>
+        <item name="colorBackgroundDialog">#FAFAFA</item>
+
+        <item name="colorTintActivity">#88cc33</item>
+        <item name="colorTintMessage">#2277dd</item>
+        <item name="colorTintHighlight">#ff8811</item>
+    </style>
+
     <style name="AppTheme.AppBarOverlay.Light" parent="ThemeOverlay.AppCompat.ActionBar">
         <item name="colorControlNormal">@color/colorFillLight</item>
         <item name="android:textColorPrimary">@color/colorFillLight</item>
-- 
GitLab