From 56d6694ffdc81d5ce7421b1239ac375b835c9444 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sat, 12 May 2018 03:08:07 +0200
Subject: [PATCH] First implementation of initial configuration UI

---
 app/build.gradle.kts                          |   9 +-
 app/src/main/AndroidManifest.xml              |   8 +
 .../assets/{org/threeten/bp => }/TZDB.dat     | Bin
 app/src/main/assets/networks.json             | 742 ++++++++++++++++++
 .../quasseldroid/dagger/ActivityModule.kt     |   6 +
 .../kuschku/quasseldroid/dagger/AppModule.kt  |   8 +-
 .../quasseldroid/defaults/DefaultNetwork.kt   |  29 +
 .../defaults/DefaultNetworkServer.kt          |  28 +
 .../quasseldroid/defaults/DefaultNetworks.kt  |  41 +
 .../kuschku/quasseldroid/defaults/Defaults.kt |  78 ++
 .../service/QuasselNotificationBackend.kt     |   2 +
 .../quasseldroid/service/QuasselService.kt    |  26 +-
 .../QuasseldroidNotificationManager.kt        |  28 +-
 .../quasseldroid/ui/chat/ChatActivity.kt      |  36 +-
 .../ui/chat/buffers/BufferListAdapter.kt      |   6 +-
 .../ui/clientsettings/crash/CrashFragment.kt  |   9 +-
 .../ui/coresettings/CoreSettingsFragment.kt   |  25 +-
 .../chatlist/ChatListBaseFragment.kt          |  83 +-
 .../chatlist/ChatListCreateFragment.kt        |   5 +-
 .../chatlist/ChatListEditFragment.kt          |   2 +-
 .../identity/IdentityBaseFragment.kt          |  70 +-
 .../identity/IdentityCreateFragment.kt        |   7 +-
 .../identity/IdentityEditFragment.kt          |   2 +-
 .../network/NetworkBaseFragment.kt            |  98 ++-
 .../network/NetworkCreateFragment.kt          |   5 +-
 .../network/NetworkEditFragment.kt            |   2 +-
 .../ui/setup/ServiceBoundSetupActivity.kt     | 362 +++++++++
 .../setup/AccountSetupConnectionSlide.kt      |   2 +-
 .../ui/setup/user/DefaultNetworkAdapter.kt    |  82 ++
 .../ui/setup/user/UserSetupActivity.kt        | 106 +++
 .../ui/setup/user/UserSetupChannelsSlide.kt   |  72 ++
 .../setup/user/UserSetupFragmentProvider.kt   |  40 +
 .../ui/setup/user/UserSetupIdentitySlide.kt   |  96 +++
 .../ui/setup/user/UserSetupNetworkSlide.kt    | 183 +++++
 .../de/kuschku/quasseldroid/util/Patterns.kt  |   4 +
 .../util/backport/AndroidThreeTenBackport.kt  |   2 +-
 .../quasseldroid/util/helper/GsonHelper.kt    |   2 +
 .../util/service/BackendServiceConnection.kt  |  19 +-
 .../util/service/ServiceBoundActivity.kt      |  49 +-
 .../util/service/ServiceBoundFragment.kt      |  13 +-
 .../res/layout/setup_account_connection.xml   |   2 +-
 .../main/res/layout/setup_account_edit.xml    |   2 +-
 .../main/res/layout/setup_user_channels.xml   |  43 +
 .../main/res/layout/setup_user_identity.xml   |  59 ++
 .../main/res/layout/setup_user_network.xml    | 101 +++
 .../main/res/values-de/strings_defaults.xml   |  34 +
 .../main/res/values-de/strings_settings.xml   |   1 +
 app/src/main/res/values-de/strings_setup.xml  |  29 +-
 .../main/res/values-lt/strings_defaults.xml   |  26 +
 .../main/res/values-lt/strings_settings.xml   |   1 +
 app/src/main/res/values-lt/strings_setup.xml  |   4 +-
 .../main/res/values-pt/strings_defaults.xml   |  32 +
 .../main/res/values-pt/strings_settings.xml   |   1 +
 app/src/main/res/values-pt/strings_setup.xml  |   4 +-
 app/src/main/res/values/strings_defaults.xml  |  34 +
 app/src/main/res/values/strings_settings.xml  |   1 +
 app/src/main/res/values/strings_setup.xml     |  22 +-
 app/src/main/res/values/styles_widgets.xml    |   4 +-
 app/src/main/res/values/themes_base.xml       |   9 +
 .../primitive/serializer/MessageSerializer.kt |   6 +-
 .../libquassel/quassel/ExtendedFeature.kt     |   1 +
 .../libquassel/quassel/syncables/IrcUser.kt   |   6 +-
 .../quassel/syncables/interfaces/IIrcUser.kt  |   2 +-
 .../de/kuschku/libquassel/session/Session.kt  |   2 +
 .../libquassel/session/SessionManager.kt      |   2 +
 lint.xml                                      |   3 +
 persistence/build.gradle.kts                  |   8 +-
 .../viewmodel/QuasselViewModel.kt             |   2 +-
 68 files changed, 2608 insertions(+), 220 deletions(-)
 rename app/src/main/assets/{org/threeten/bp => }/TZDB.dat (100%)
 create mode 100644 app/src/main/assets/networks.json
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetwork.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworkServer.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworks.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/DefaultNetworkAdapter.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupChannelsSlide.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupNetworkSlide.kt
 create mode 100644 app/src/main/res/layout/setup_user_channels.xml
 create mode 100644 app/src/main/res/layout/setup_user_identity.xml
 create mode 100644 app/src/main/res/layout/setup_user_network.xml
 create mode 100644 app/src/main/res/values-de/strings_defaults.xml
 create mode 100644 app/src/main/res/values-lt/strings_defaults.xml
 create mode 100644 app/src/main/res/values-pt/strings_defaults.xml
 create mode 100644 app/src/main/res/values/strings_defaults.xml

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 6c9da1322..f304e41bc 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -128,20 +128,19 @@ dependencies {
     implementation("android.arch.lifecycle", "extensions", version)
     implementation("android.arch.lifecycle", "reactivestreams", version)
     kapt("android.arch.lifecycle", "compiler", version)
+    testImplementation("android.arch.core", "core-testing", version)
   }
 
   // App Arch Persistence
-  withVersion("1.1.0-rc1") {
+  withVersion("1.1.0") {
     implementation("android.arch.persistence.room", "runtime", version)
-    implementation("android.arch.persistence.room", "rxjava2", version)
     kapt("android.arch.persistence.room", "compiler", version)
+    implementation("android.arch.persistence.room", "rxjava2", version)
     testImplementation("android.arch.persistence.room", "testing", version)
   }
 
   // App Arch Paging
-  implementation("android.arch.paging", "runtime", "1.0.0-rc1") {
-    exclude(group = "junit", module = "junit")
-  }
+  implementation("android.arch.paging", "runtime", "1.0.0")
 
   // Utility
   implementation("io.reactivex.rxjava2", "rxandroid", "2.0.2")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9d89e04da..c1c016a5a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -224,6 +224,14 @@
       android:parentActivityName=".ui.setup.accounts.selection.AccountSelectionActivity"
       android:windowSoftInputMode="adjustResize" />
 
+    <!-- Core User Setup Flow -->
+    <activity
+      android:name=".ui.setup.user.UserSetupActivity"
+      android:exported="false"
+      android:label="@string/settings_identity_title"
+      android:parentActivityName=".ui.chat.ChatActivity"
+      android:windowSoftInputMode="adjustResize" />
+
     <!-- Services -->
     <service
       android:name=".service.QuasselService"
diff --git a/app/src/main/assets/org/threeten/bp/TZDB.dat b/app/src/main/assets/TZDB.dat
similarity index 100%
rename from app/src/main/assets/org/threeten/bp/TZDB.dat
rename to app/src/main/assets/TZDB.dat
diff --git a/app/src/main/assets/networks.json b/app/src/main/assets/networks.json
new file mode 100644
index 000000000..43fb35140
--- /dev/null
+++ b/app/src/main/assets/networks.json
@@ -0,0 +1,742 @@
+[
+  {
+    "name": "Aniverse",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.aniverse.com",
+        "port": 6661,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "DALnet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.dal.net",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "mesra.kl.my.dal.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "mesra.kl.my.dal.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "hotspeed.sg.as.dal.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "hotspeed.sg.as.dal.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "powertech.no.eu.dal.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "powertech.no.eu.dal.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "rumble.fl.us.dal.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "rumble.fl.us.dal.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "broadway.ny.us.dal.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "broadway.ny.us.dal.net",
+        "port": 7000,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "EFnet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.efnet.info",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.efnet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.inter.net.il",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.igs.ca",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.inet.tele.dk",
+        "port": 6661,
+        "secure": false
+      },
+      {
+        "host": "efnet.cs.hut.fi",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.pte.hu",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.pte.hu",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.pte.hu",
+        "port": 9000,
+        "secure": false
+      },
+      {
+        "host": "efnet.xs4all.nl",
+        "port": 6661,
+        "secure": false
+      },
+      {
+        "host": "irc.efnet.nl",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.homelien.no",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.homelien.no",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.homelien.no",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.daxnet.no",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.daxnet.no",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.efnet.pl",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.du.se",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.du.se",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "efnet.demon.co.uk",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.blackened.com",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.easynews.com",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.easynews.com",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.easynews.com",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.blessed.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.he.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.he.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.prison.net",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.wh.verio.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.colosolutions.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.colosolutions.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.mindspring.com",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.mindspring.com",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.servercentral.net",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.servercentral.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.umich.edu",
+        "port": 6664,
+        "secure": false
+      },
+      {
+        "host": "irc.umn.edu",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.choopa.net",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.choopa.net",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.nac.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.nac.net",
+        "port": 7000,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "EntertheGame",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.enterthegame.com",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "Freenode",
+    "default": true,
+    "defaultChannels": [
+      "#quassel"
+    ],
+    "servers": [
+      {
+        "host": "chat.freenode.net",
+        "port": 6697,
+        "secure": true
+      },
+      {
+        "host": "chat.freenode.net",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "GalaxyNet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.galaxynet.org",
+        "port": 6662,
+        "secure": false
+      },
+      {
+        "host": "irc.galaxynet.org",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "boston.ma.us.galaxynet.org",
+        "port": 6661,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "GameSurge",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.gamesurge.net",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.eu.gamesurge.net",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "GamesNET",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.gamesnet.net",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "IRCnet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.ircnet.com",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "open.ircnet.net",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.us.ircnet.net",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "ircnet.netvision.net.il",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.tokyo.wide.ad.jp",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.freebsd.org.tw",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "linz.irc.at",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "vienna.irc.at",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "ircnet.realroot.be",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.datacomm.ch",
+        "port": 6664,
+        "secure": false
+      },
+      {
+        "host": "irc.felk.cvut.cz",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "random.ircd.de",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.dotsrc.org",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.ircnet.ee",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.cs.hut.fi",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "ircnet.club-internet.fr",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "atw.irc.hu",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.simnet.is",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.eutelia.it",
+        "port": 6664,
+        "secure": false
+      },
+      {
+        "host": "irc.eutelia.it",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.tin.it",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "irc.apollo.lv",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.apollo.lv",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "irc.uunet.nl",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.xs4all.nl",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.snt.utwente.nl",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.ifi.uio.no",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "irc.pvv.ntnu.no",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "lublin.irc.pl",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "warszawa.irc.pl",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.ludd.luth.se",
+        "port": 6661,
+        "secure": false
+      },
+      {
+        "host": "irc.swipnet.se",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "irc.swipnet.se",
+        "port": 8000,
+        "secure": false
+      },
+      {
+        "host": "irc.arnes.si",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.link.si",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "irc.nextra.sk",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "ircnet.demon.co.uk",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "ircnet.eversible.com",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "ircnet.choopa.net",
+        "port": 6665,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "Mozilla",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.mozilla.org",
+        "port": 6697,
+        "secure": true
+      },
+      {
+        "host": "irc.mozilla.org",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "OFTC",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.oftc.net",
+        "port": 6697,
+        "secure": true
+      },
+      {
+        "host": "irc.oftc.net",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "QuakeNet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.quakenet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "de.quakenet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "se.quakenet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "uk.quakenet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "us.quakenet.org",
+        "port": 6667,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "Undernet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "eu.undernet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "us.undernet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "elsene.be.eu.undernet.org",
+        "port": 6667,
+        "secure": false
+      },
+      {
+        "host": "elsene.be.eu.undernet.org",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "helsinki.fi.eu.Undernet.org",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "zagreb.hr.eu.undernet.org",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "zagreb.hr.eu.undernet.org",
+        "port": 9999,
+        "secure": false
+      },
+      {
+        "host": "ede.nl.eu.undernet.org",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "oslo.no.eu.undernet.org",
+        "port": 6666,
+        "secure": false
+      },
+      {
+        "host": "london.uk.eu.undernet.org",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "london.uk.eu.undernet.org",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "mesa.az.us.undernet.org",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "mesa.az.us.undernet.org",
+        "port": 6665,
+        "secure": false
+      },
+      {
+        "host": "mesa.az.us.undernet.org",
+        "port": 7000,
+        "secure": false
+      },
+      {
+        "host": "newyork.ny.us.undernet.org",
+        "port": 6660,
+        "secure": false
+      },
+      {
+        "host": "newyork.ny.us.undernet.org",
+        "port": 7000,
+        "secure": false
+      }
+    ]
+  },
+  {
+    "name": "euIRCnet",
+    "default": false,
+    "defaultChannels": [],
+    "servers": [
+      {
+        "host": "irc.euirc.net",
+        "port": 6697,
+        "secure": true
+      },
+      {
+        "host": "irc.euirc.net",
+        "port": 6665,
+        "secure": false
+      }
+    ]
+  }
+]
diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
index c0b0a6205..ebc96cc51 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
@@ -78,6 +78,8 @@ import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActiv
 import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionFragmentProvider
 import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupActivity
 import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupFragmentProvider
+import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
+import de.kuschku.quasseldroid.ui.setup.user.UserSetupFragmentProvider
 
 @Module
 abstract class ActivityModule {
@@ -189,6 +191,10 @@ abstract class ActivityModule {
   @ContributesAndroidInjector(modules = [AccountEditModule::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
   abstract fun bindAccountEditActivity(): AccountEditActivity
 
+  @ActivityScope
+  @ContributesAndroidInjector(modules = [UserSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
+  abstract fun bindUserSetupActivity(): UserSetupActivity
+
   @ActivityScope
   @ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class])
   abstract fun bindQuasselService(): QuasselService
diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/AppModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/AppModule.kt
index 1c1807f66..171a84ea2 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/AppModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/AppModule.kt
@@ -19,7 +19,13 @@
 
 package de.kuschku.quasseldroid.dagger
 
+import com.google.gson.GsonBuilder
 import dagger.Module
+import dagger.Provides
 
 @Module
-abstract class AppModule
+object AppModule {
+  @Provides
+  @JvmStatic
+  fun provideGson() = GsonBuilder().setPrettyPrinting().create()
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetwork.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetwork.kt
new file mode 100644
index 000000000..e920f6789
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetwork.kt
@@ -0,0 +1,29 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.defaults
+
+import java.io.Serializable
+
+data class DefaultNetwork(
+  val name: String,
+  val default: Boolean = false,
+  val defaultChannels: List<String> = emptyList(),
+  val servers: List<DefaultNetworkServer>
+) : Serializable
diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworkServer.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworkServer.kt
new file mode 100644
index 000000000..aa868a83e
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworkServer.kt
@@ -0,0 +1,28 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.defaults
+
+import java.io.Serializable
+
+data class DefaultNetworkServer(
+  val host: String,
+  val port: Int,
+  val secure: Boolean
+) : Serializable
diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworks.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworks.kt
new file mode 100644
index 000000000..32feeb699
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/DefaultNetworks.kt
@@ -0,0 +1,41 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.defaults
+
+import android.content.Context
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import java.io.IOException
+import javax.inject.Inject
+
+class DefaultNetworks @Inject constructor(context: Context, gson: Gson) {
+  val networks: List<DefaultNetwork> by lazy {
+    try {
+      context.assets.open("networks.json").use {
+        gson.fromJson<List<DefaultNetwork>>(
+          it.bufferedReader(Charsets.UTF_8),
+          object : TypeToken<List<DefaultNetwork>>() {}.type
+        )
+      }
+    } catch (e: IOException) {
+      throw IllegalStateException("networks.json missing from assets.", e)
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
new file mode 100644
index 000000000..f78c18430
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
@@ -0,0 +1,78 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.defaults
+
+import android.content.Context
+import de.kuschku.libquassel.protocol.Buffer_Activity
+import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
+import de.kuschku.libquassel.quassel.syncables.Identity
+import de.kuschku.libquassel.quassel.syncables.Network
+import de.kuschku.libquassel.session.SignalProxy
+import de.kuschku.quasseldroid.R
+import java.util.*
+
+object Defaults {
+  fun bufferViewConfigInitial(context: Context, proxy: SignalProxy = SignalProxy.NULL) =
+    bufferViewConfig(context, proxy).apply {
+      setBufferViewName(context.getString(R.string.default_bufferviewconfig_name))
+    }
+
+  fun bufferViewConfig(context: Context, proxy: SignalProxy = SignalProxy.NULL) =
+    BufferViewConfig(-1, proxy).apply {
+      setAllowedBufferTypes(Buffer_Type.of(
+        Buffer_Type.StatusBuffer, Buffer_Type.ChannelBuffer, Buffer_Type.QueryBuffer
+      ))
+      setHideInactiveBuffers(false)
+      setHideInactiveNetworks(false)
+      setAddNewBuffersAutomatically(true)
+      setSortAlphabetically(true)
+      setShowSearch(true)
+      setDisableDecoration(false)
+      setMinimumActivity(Buffer_Activity.NoActivity.toInt())
+    }
+
+  fun identity(context: Context, proxy: SignalProxy = SignalProxy.NULL) =
+    Identity(proxy).apply {
+      setIdentityName("")
+      setRealName(context.getString(R.string.default_identity_realname))
+      setNicks(listOf(context.getString(R.string.default_identity_nick, Random().nextInt(16))))
+      setAwayNick("")
+      setAwayNickEnabled(false)
+      setAwayReason(context.getString(R.string.default_identity_awayreason))
+      setAwayReasonEnabled(true)
+      setAutoAwayEnabled(false)
+      setAutoAwayTime(10)
+      setAutoAwayReason(context.getString(R.string.default_identity_autoawayreason))
+      setAutoAwayReasonEnabled(false)
+      setDetachAwayEnabled(false)
+      setDetachAwayReason(context.getString(R.string.default_identity_detachawayreason))
+      setDetachAwayReasonEnabled(false)
+      setIdent(context.getString(R.string.default_identity_ident))
+      setKickReason(context.getString(R.string.default_identity_kickreason))
+      setPartReason(context.getString(R.string.default_identity_partreason))
+      setQuitReason(context.getString(R.string.default_identity_quitreason))
+    }
+
+  fun network(context: Context, proxy: SignalProxy = SignalProxy.NULL) =
+    Network(-1, proxy).apply {
+      setNetworkName("")
+    }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
index ad335f07e..e08f4c2e6 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
@@ -136,6 +136,8 @@ class QuasselNotificationBackend @Inject constructor(
   }
 
   fun updateSettings() {
+    notificationHandler.updateTranslation()
+
     notificationSettings = Settings.notification(context)
     appearanceSettings = Settings.appearance(context)
     messageSettings = Settings.message(context)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
index ee1778e9a..a75cd9b4b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
@@ -32,12 +32,14 @@ import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
 import de.kuschku.libquassel.session.Backend
 import de.kuschku.libquassel.session.ISession
+import de.kuschku.libquassel.session.Session
 import de.kuschku.libquassel.session.SessionManager
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.malheur.CrashHandler
 import de.kuschku.quasseldroid.BuildConfig
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.Defaults
 import de.kuschku.quasseldroid.persistence.AccountDatabase
 import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
@@ -53,6 +55,7 @@ import de.kuschku.quasseldroid.util.compatibility.AndroidHandlerService
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer
+import de.kuschku.quasseldroid.util.ui.LocaleHelper
 import io.reactivex.subjects.PublishSubject
 import org.threeten.bp.Instant
 import java.util.concurrent.TimeUnit
@@ -64,8 +67,11 @@ class QuasselService : DaggerLifecycleService(),
   @Inject
   lateinit var connectionSettings: ConnectionSettings
 
+  private lateinit var translatedLocale: Context
+
   override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
     update()
+    translatedLocale = LocaleHelper.setLocale(this)
     notificationBackend.updateSettings()
   }
 
@@ -317,6 +323,8 @@ class QuasselService : DaggerLifecycleService(),
   override fun onCreate() {
     super.onCreate()
 
+    translatedLocale = LocaleHelper.setLocale(this)
+
     certificateManager = QuasselCertificateManager(database.validityWhitelist())
     hostnameVerifier = QuasselHostnameVerifier(QuasselHostnameManager(database.hostnameWhitelist()))
     trustManager = QuasselTrustManager(certificateManager)
@@ -327,6 +335,7 @@ class QuasselService : DaggerLifecycleService(),
       notificationBackend,
       handlerService,
       ::disconnectFromCore,
+      ::initCallback,
       CrashHandler::handle
     )
 
@@ -412,13 +421,28 @@ class QuasselService : DaggerLifecycleService(),
     return QuasselBinder(asyncBackend)
   }
 
+  private fun initCallback(session: Session) {
+    if (session.bufferViewManager.bufferViewConfigs().isEmpty()) {
+      session.bufferViewManager.requestCreateBufferView(
+        Defaults.bufferViewConfigInitial(translatedLocale).apply {
+          for (info in session.bufferSyncer.bufferInfos()) {
+            handleBuffer(info, session.bufferSyncer)
+          }
+        }.toVariantMap()
+      )
+    }
+  }
+
   companion object {
     fun launch(
       context: Context,
       disconnect: Boolean? = null,
       markRead: BufferId? = null,
       markReadMessage: MsgId? = null
-    ): ComponentName = context.startService(intent(context, disconnect, markRead, markReadMessage))
+    ): ComponentName = context.startService(intent(context,
+                                                   disconnect,
+                                                   markRead,
+                                                   markReadMessage))
 
     fun intent(
       context: Context,
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
index d1711f565..f9576ca89 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
@@ -42,10 +42,16 @@ import de.kuschku.quasseldroid.settings.NotificationSettings
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
 import de.kuschku.quasseldroid.util.NotificationMessage
 import de.kuschku.quasseldroid.util.helper.getColorCompat
+import de.kuschku.quasseldroid.util.ui.LocaleHelper
 import javax.inject.Inject
 
 class QuasseldroidNotificationManager @Inject constructor(private val context: Context) {
   private val notificationManagerCompat = NotificationManagerCompat.from(context)
+  private var translatedLocale: Context = LocaleHelper.setLocale(context)
+
+  fun updateTranslation() {
+    translatedLocale = LocaleHelper.setLocale(context)
+  }
 
   fun init() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
@@ -58,13 +64,13 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
     notificationManager.createNotificationChannels(
       listOf(
         NotificationChannel(
-          context.getString(R.string.notification_channel_background),
-          context.getString(R.string.notification_channel_connection_title),
+          translatedLocale.getString(R.string.notification_channel_background),
+          translatedLocale.getString(R.string.notification_channel_connection_title),
           NotificationManager.IMPORTANCE_LOW
         ),
         NotificationChannel(
-          context.getString(R.string.notification_channel_highlight),
-          context.getString(R.string.notification_channel_highlight_title),
+          translatedLocale.getString(R.string.notification_channel_highlight),
+          translatedLocale.getString(R.string.notification_channel_highlight_title),
           NotificationManager.IMPORTANCE_HIGH
         ).apply {
           enableLights(true)
@@ -101,7 +107,7 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
     )
 
     val remoteInput = RemoteInput.Builder("reply_content")
-      .setLabel(context.getString(R.string.label_reply))
+      .setLabel(translatedLocale.getString(R.string.label_reply))
       .build()
 
     val replyPendingIntent = PendingIntent.getService(
@@ -139,7 +145,7 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
 
     val notification = NotificationCompat.Builder(
       context.applicationContext,
-      context.getString(R.string.notification_channel_highlight)
+      translatedLocale.getString(R.string.notification_channel_highlight)
     )
       .setContentIntent(pendingIntentOpen)
       .setDeleteIntent(deletePendingIntent)
@@ -172,11 +178,11 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
                     }
                   }
       )
-      .addAction(0, context.getString(R.string.label_mark_read), markReadPendingIntent)
+      .addAction(0, translatedLocale.getString(R.string.label_mark_read), markReadPendingIntent)
       .addAction(
         NotificationCompat.Action.Builder(
           0,
-          context.getString(R.string.label_reply),
+          translatedLocale.getString(R.string.label_reply),
           replyPendingIntent
         ).addRemoteInput(remoteInput).build()
       )
@@ -210,11 +216,11 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
 
     val notification = NotificationCompat.Builder(
       context.applicationContext,
-      context.getString(R.string.notification_channel_background)
+      translatedLocale.getString(R.string.notification_channel_background)
     )
       .setContentIntent(pendingIntentOpen)
-      .addAction(0, context.getString(R.string.label_open), pendingIntentOpen)
-      .addAction(0, context.getString(R.string.label_disconnect), pendingIntentDisconnect)
+      .addAction(0, translatedLocale.getString(R.string.label_open), pendingIntentOpen)
+      .addAction(0, translatedLocale.getString(R.string.label_disconnect), pendingIntentDisconnect)
       .setSmallIcon(R.mipmap.ic_logo)
       .setColor(context.getColorCompat(R.color.colorPrimary))
       .setPriority(NotificationCompat.PRIORITY_MIN)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 29aaabcee..81a819144 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -20,6 +20,7 @@
 package de.kuschku.quasseldroid.ui.chat
 
 import android.annotation.TargetApi
+import android.app.Activity
 import android.arch.lifecycle.Observer
 import android.content.Context
 import android.content.Intent
@@ -51,16 +52,20 @@ import de.kuschku.libquassel.util.Optional
 import de.kuschku.libquassel.util.flag.and
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.flag.or
+import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.AccountDatabase
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.settings.Settings
 import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteAdapter
 import de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment
 import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity
 import de.kuschku.quasseldroid.ui.coresettings.CoreSettingsActivity
+import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActivity
+import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundActivity
@@ -92,6 +97,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   @Inject
   lateinit var database: QuasselDatabase
 
+  @Inject
+  lateinit var autoCompleteSettings: AutoCompleteSettings
+
   @Inject
   lateinit var accountDatabase: AccountDatabase
 
@@ -447,7 +455,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
             viewModel.buffer.value == Int.MAX_VALUE &&
             isInitialConnect) {
           drawerLayout.openDrawer(Gravity.START)
-          isInitialConnect = true
+          isInitialConnect = false
+          viewModel.session.value?.orNull()?.let {
+            if (it.identities.isEmpty()) {
+              UserSetupActivity.launch(this)
+            }
+          }
         }
       })
 
@@ -678,6 +691,27 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     }
   }
 
+  private var startedSelection = false
+
+  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+    super.onActivityResult(requestCode, resultCode, data)
+
+    if (requestCode == REQUEST_SELECT_ACCOUNT) {
+      startedSelection = false
+
+      if (resultCode == Activity.RESULT_CANCELED) {
+        finish()
+      }
+    }
+  }
+
+  override fun onSelectAccount() {
+    if (!startedSelection) {
+      startActivityForResult(AccountSelectionActivity.intent(this), REQUEST_SELECT_ACCOUNT)
+      startedSelection = true
+    }
+  }
+
   companion object {
     private val KEY_AUTOCOMPLETE_TEXT = "autocomplete_text"
     private val KEY_BUFFER_ID = "buffer_id"
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
index 8e6efe9c7..8e20f0c83 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
@@ -76,13 +76,13 @@ class BufferListAdapter(
   }
 
   fun toggleSelection(buffer: BufferId): Boolean {
-    val next = if (selectedBuffer.value == buffer) -1 else buffer
+    val next = if (selectedBuffer.value == buffer) Int.MAX_VALUE else buffer
     selectedBuffer.onNext(next)
-    return next != -1
+    return next != Int.MAX_VALUE
   }
 
   fun unselectAll() {
-    selectedBuffer.onNext(-1)
+    selectedBuffer.onNext(Int.MAX_VALUE)
   }
 
   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragment.kt
index 2787c784c..f0afe8d2e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragment.kt
@@ -30,12 +30,12 @@ import android.view.*
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.google.gson.Gson
-import com.google.gson.GsonBuilder
 import dagger.android.support.DaggerFragment
 import de.kuschku.malheur.data.Report
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.util.helper.fromJson
 import java.io.File
+import javax.inject.Inject
 
 class CrashFragment : DaggerFragment() {
   @BindView(R.id.list)
@@ -44,7 +44,9 @@ class CrashFragment : DaggerFragment() {
   private lateinit var handlerThread: HandlerThread
   private lateinit var handler: Handler
 
-  private var gson: Gson? = null
+  @Inject
+  lateinit var gson: Gson
+
   private var crashDir: File? = null
   private var adapter: CrashAdapter? = null
 
@@ -69,7 +71,6 @@ class CrashFragment : DaggerFragment() {
 
     this.adapter = CrashAdapter()
     this.crashDir = File(requireContext().cacheDir, "crashes")
-    this.gson = GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create()
 
     list.layoutManager = LinearLayoutManager(context)
     list.adapter = adapter
@@ -80,7 +81,7 @@ class CrashFragment : DaggerFragment() {
       val crashDir = this.crashDir
       val gson = this.gson
 
-      if (crashDir != null && gson != null) {
+      if (crashDir != null) {
         crashDir.mkdirs()
         val list: List<Pair<Report, String>> = crashDir.listFiles()
           .orEmpty()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
index ed2252489..e7353fe1b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
@@ -48,6 +48,7 @@ import de.kuschku.quasseldroid.ui.coresettings.networkconfig.NetworkConfigActivi
 import de.kuschku.quasseldroid.util.helper.combineLatest
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
+import io.reactivex.Observable
 
 class CoreSettingsFragment : ServiceBoundFragment() {
   @BindView(R.id.networks)
@@ -105,10 +106,14 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     ViewCompat.setNestedScrollingEnabled(networks, false)
 
     viewModel.networks.switchMap {
-      combineLatest(it.values.map(Network::liveNetworkInfo)).map {
-        it.map {
-          SettingsItem(it.networkId, it.networkName)
-        }.sortedBy(SettingsItem::name)
+      if (it.isEmpty()) {
+        Observable.just(emptyList())
+      } else {
+        combineLatest(it.values.map(Network::liveNetworkInfo)).map {
+          it.map {
+            SettingsItem(it.networkId, it.networkName)
+          }.sortedBy(SettingsItem::name)
+        }
       }
     }.toLiveData().observe(this, Observer {
       networkAdapter.submitList(it.orEmpty())
@@ -120,10 +125,14 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     ViewCompat.setNestedScrollingEnabled(identities, false)
 
     viewModel.identities.switchMap {
-      combineLatest(it.values.map(Identity::liveUpdates)).map {
-        it.map {
-          SettingsItem(it.id(), it.identityName() ?: "")
-        }.sortedBy(SettingsItem::name)
+      if (it.isEmpty()) {
+        Observable.just(emptyList())
+      } else {
+        combineLatest(it.values.map(Identity::liveUpdates)).map {
+          it.map {
+            SettingsItem(it.id(), it.identityName() ?: "")
+          }.sortedBy(SettingsItem::name)
+        }
       }
     }.toLiveData().observe(this, Observer {
       identityAdapter.submitList(it.orEmpty())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
index 11220b723..efc4390e3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
@@ -35,17 +35,19 @@ import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
+import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.flag.minus
 import de.kuschku.libquassel.util.flag.plus
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.Defaults
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 import de.kuschku.quasseldroid.util.helper.combineLatest
 import de.kuschku.quasseldroid.util.helper.toLiveData
 
-abstract class ChatListBaseFragment : SettingsFragment(), SettingsFragment.Savable,
-                                      SettingsFragment.Changeable {
+abstract class ChatListBaseFragment(private val initDefault: Boolean) :
+  SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable {
   @BindView(R.id.buffer_view_name)
   lateinit var bufferViewName: EditText
 
@@ -127,34 +129,30 @@ abstract class ChatListBaseFragment : SettingsFragment(), SettingsFragment.Savab
       }
     })
 
-    viewModel.bufferViewConfigMap.map { Optional.ofNullable(it[chatlistId]) }
-      .filter(Optional<BufferViewConfig>::isPresent)
-      .map(Optional<BufferViewConfig>::get)
-      .firstElement()
-      .toLiveData().observe(this, Observer {
-        it?.let {
-          this.chatlist = Pair(it, it.copy())
-          this.chatlist?.let { (_, data) ->
-            bufferViewName.setText(data.bufferViewName())
-            showSearch.isChecked = data.showSearch()
-            sortAlphabetically.isChecked = data.sortAlphabetically()
-            addNewBuffersAutomatically.isChecked = data.addNewBuffersAutomatically()
-            showStatusBuffer.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.StatusBuffer)
-
-            minimumActivity.setSelection(
-              minimumActivityAdapter.indexOf(data.minimumActivity()) ?: 0
-            )
-
-            networkAdapter.indexOf(data.networkId())?.let(networkId::setSelection)
-
-            hideInactiveBuffers.isChecked = data.hideInactiveBuffers()
-            hideInactiveNetworks.isChecked = data.hideInactiveNetworks()
-
-            showQueries.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.QueryBuffer)
-            showChannels.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.ChannelBuffer)
+    if (initDefault) {
+      viewModel.session
+        .filter(Optional<ISession>::isPresent)
+        .map(Optional<ISession>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(Defaults.bufferViewConfig(requireContext(), it.proxy),
+                   minimumActivityAdapter,
+                   networkAdapter)
           }
-        }
-      })
+        })
+    } else {
+      viewModel.bufferViewConfigMap.map { Optional.ofNullable(it[chatlistId]) }
+        .filter(Optional<BufferViewConfig>::isPresent)
+        .map(Optional<BufferViewConfig>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(it, minimumActivityAdapter, networkAdapter)
+          }
+        })
+    }
+
     networkId.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
       override fun onNothingSelected(parent: AdapterView<*>?) {
         showStatusBuffer.isChecked = true
@@ -174,6 +172,33 @@ abstract class ChatListBaseFragment : SettingsFragment(), SettingsFragment.Savab
     return view
   }
 
+  private fun update(it: BufferViewConfig,
+                     minimumActivityAdapter: MinimumActivityAdapter,
+                     networkAdapter: NetworkAdapter) {
+    if (this.chatlist == null) {
+      this.chatlist = Pair(it, it.copy())
+      this.chatlist?.let { (_, data) ->
+        bufferViewName.setText(data.bufferViewName())
+        showSearch.isChecked = data.showSearch()
+        sortAlphabetically.isChecked = data.sortAlphabetically()
+        addNewBuffersAutomatically.isChecked = data.addNewBuffersAutomatically()
+        showStatusBuffer.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.StatusBuffer)
+
+        minimumActivity.setSelection(
+          minimumActivityAdapter.indexOf(data.minimumActivity()) ?: 0
+        )
+
+        networkAdapter.indexOf(data.networkId())?.let(networkId::setSelection)
+
+        hideInactiveBuffers.isChecked = data.hideInactiveBuffers()
+        hideInactiveNetworks.isChecked = data.hideInactiveNetworks()
+
+        showQueries.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.QueryBuffer)
+        showChannels.isChecked = data.allowedBufferTypes().hasFlag(Buffer_Type.ChannelBuffer)
+      }
+    }
+  }
+
   override fun hasChanged() = chatlist?.let { (it, data) ->
     applyChanges(data, it)
     it == null || !data.isEqual(it)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListCreateFragment.kt
index 7dbd312ee..1d72629b6 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListCreateFragment.kt
@@ -19,12 +19,11 @@
 
 package de.kuschku.quasseldroid.ui.coresettings.chatlist
 
-import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.util.helpers.value
 
-class ChatListCreateFragment : ChatListBaseFragment() {
+class ChatListCreateFragment : ChatListBaseFragment(true) {
   override fun onSave() = viewModel.session.value?.orNull()?.let { session ->
-    BufferViewConfig(-1, session.proxy).let { data ->
+    chatlist?.let { (_, data) ->
       applyChanges(data, null)
       viewModel.bufferViewManager.value?.orNull()?.requestCreateBufferView(data.toVariantMap())
       true
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListEditFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListEditFragment.kt
index d81d09bce..a8660d872 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListEditFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListEditFragment.kt
@@ -22,7 +22,7 @@ package de.kuschku.quasseldroid.ui.coresettings.chatlist
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 
-class ChatListEditFragment : ChatListBaseFragment(), SettingsFragment.Deletable {
+class ChatListEditFragment : ChatListBaseFragment(false), SettingsFragment.Deletable {
   override fun onSave() = chatlist?.let { (it, data) ->
     applyChanges(data, it)
     it?.requestUpdate(data.toVariantMap())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
index b9f14f30e..d6bf8f292 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
@@ -35,14 +35,16 @@ import butterknife.BindView
 import butterknife.ButterKnife
 import com.afollestad.materialdialogs.MaterialDialog
 import de.kuschku.libquassel.quassel.syncables.Identity
+import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.Defaults
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 import de.kuschku.quasseldroid.util.helper.setDependent
 import de.kuschku.quasseldroid.util.helper.toLiveData
 
-abstract class IdentityBaseFragment : SettingsFragment(), SettingsFragment.Savable,
-                                      SettingsFragment.Changeable {
+abstract class IdentityBaseFragment(private val initDefault: Boolean) :
+  SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable {
 
   @BindView(R.id.identity_name)
   lateinit var identityName: EditText
@@ -116,37 +118,53 @@ abstract class IdentityBaseFragment : SettingsFragment(), SettingsFragment.Savab
         }.build().show()
     }
 
-    viewModel.identities.map { Optional.ofNullable(it[identityId]) }
-      .filter(Optional<Identity>::isPresent)
-      .map(Optional<Identity>::get)
-      .firstElement()
-      .toLiveData().observe(this, Observer {
-        it?.let {
-          if (this.identity == null) {
-            this.identity = Pair(it, it.copy())
-            this.identity?.let { (_, data) ->
-              identityName.setText(data.identityName())
-              realName.setText(data.realName())
-              ident.setText(data.ident())
-              kickReason.setText(data.kickReason())
-              partReason.setText(data.partReason())
-              quitReason.setText(data.quitReason())
-              awayReason.setText(data.awayReason())
-              detachAway.isChecked = data.detachAwayEnabled()
-              detachAwayReason.setText(data.detachAwayReason())
-              adapter.nicks = data.nicks()
-            }
+    if (initDefault) {
+      viewModel.session
+        .filter(Optional<ISession>::isPresent)
+        .map(Optional<ISession>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(Defaults.identity(requireContext(), it.proxy))
           }
-        }
-      })
+        })
+    } else {
+      viewModel.identities.map { Optional.ofNullable(it[identityId]) }
+        .filter(Optional<Identity>::isPresent)
+        .map(Optional<Identity>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(it)
+          }
+        })
+    }
 
     detachAway.setDependent(detachAwayGroup)
 
     return view
   }
 
-  private fun startDrag(holder: IdentityNicksAdapter.IdentityNickViewHolder) = helper.startDrag(
-    holder)
+  private fun update(it: Identity) {
+    if (this.identity == null) {
+      this.identity = Pair(it, it.copy())
+      this.identity?.let { (_, data) ->
+        identityName.setText(data.identityName())
+        realName.setText(data.realName())
+        ident.setText(data.ident())
+        kickReason.setText(data.kickReason())
+        partReason.setText(data.partReason())
+        quitReason.setText(data.quitReason())
+        awayReason.setText(data.awayReason())
+        detachAway.isChecked = data.detachAwayEnabled()
+        detachAwayReason.setText(data.detachAwayReason())
+        adapter.nicks = data.nicks()
+      }
+    }
+  }
+
+  private fun startDrag(holder: IdentityNicksAdapter.IdentityNickViewHolder) =
+    helper.startDrag(holder)
 
   private fun nickClick(index: Int, nick: String) {
     MaterialDialog.Builder(requireContext())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragment.kt
index 65b02bca6..c2473a8a8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragment.kt
@@ -19,15 +19,14 @@
 
 package de.kuschku.quasseldroid.ui.coresettings.identity
 
-import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.util.helpers.value
 
-class IdentityCreateFragment : IdentityBaseFragment() {
+class IdentityCreateFragment : IdentityBaseFragment(true) {
   override fun onSave() = viewModel.session.value?.orNull()?.let { session ->
-    Identity(session.proxy).let { data ->
+    identity?.let { (_, data) ->
       applyChanges(data)
       session.rpcHandler?.createIdentity(data, mapOf())
       true
-    }
+    } ?: false
   } ?: false
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragment.kt
index abfd3757e..c2c3c38d7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragment.kt
@@ -22,7 +22,7 @@ package de.kuschku.quasseldroid.ui.coresettings.identity
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 
-class IdentityEditFragment : IdentityBaseFragment(), SettingsFragment.Deletable {
+class IdentityEditFragment : IdentityBaseFragment(false), SettingsFragment.Deletable {
   override fun onSave() = identity?.let { (it, data) ->
     applyChanges(data)
     it?.requestUpdate(data.toVariantMap())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
index 8fb7b008b..1c25edd89 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
@@ -39,8 +39,10 @@ import butterknife.ButterKnife
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
+import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.Defaults
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 import de.kuschku.quasseldroid.ui.coresettings.networkserver.NetworkServerActivity
 import de.kuschku.quasseldroid.util.helper.combineLatest
@@ -48,8 +50,8 @@ import de.kuschku.quasseldroid.util.helper.setDependent
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import kotlin.math.roundToInt
 
-abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savable,
-                                     SettingsFragment.Changeable {
+abstract class NetworkBaseFragment(private val initDefault: Boolean) :
+  SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable {
   @BindView(R.id.network_name)
   lateinit var networkName: EditText
 
@@ -169,45 +171,27 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl
       }
     })
 
-    viewModel.networks.map { Optional.ofNullable(it[networkId]) }
-      .filter(Optional<Network>::isPresent)
-      .map(Optional<Network>::get)
-      .firstElement()
-      .toLiveData().observe(this, Observer {
-        it?.let {
-          if (this.network == null) {
-            this.network = Pair(it, it.copy())
-            this.network?.let { (_, data) ->
-              networkName.setText(data.networkName())
-
-              identityAdapter.indexOf(data.identity())?.let(identity::setSelection)
-
-              adapter.list = data.serverList()
-
-              saslEnabled.isChecked = data.useSasl()
-              saslAccount.setText(data.saslAccount())
-              saslPassword.setText(data.saslPassword())
-
-              autoidentifyEnabled.isChecked = data.useAutoIdentify()
-              autoidentifyService.setText(data.autoIdentifyService())
-              autoidentifyPassword.setText(data.autoIdentifyPassword())
-
-              autoreconnectEnabled.isChecked = data.useAutoReconnect()
-              autoreconnectInterval.setText(data.autoReconnectInterval().toString())
-              autoreconnectRetries.setText(data.autoReconnectRetries().toString())
-              autoreconnectUnlimited.isChecked = data.unlimitedReconnectRetries()
-
-              perform.setText(data.perform().joinToString("\n"))
-              rejoinChannels.isChecked = data.rejoinChannels()
-
-              customratelimitsEnabled.isChecked = data.useCustomMessageRate()
-              customratelimitsBurstSize.setText(data.messageRateBurstSize().toString())
-              customratelimitsUnlimited.isChecked = data.unlimitedMessageRate()
-              customratelimitsDelay.setText("${data.messageRateDelay() / 1000.0}")
-            }
+    if (initDefault) {
+      viewModel.session
+        .filter(Optional<ISession>::isPresent)
+        .map(Optional<ISession>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(Defaults.network(requireContext(), it.proxy), identityAdapter)
           }
-        }
-      })
+        })
+    } else {
+      viewModel.networks.map { Optional.ofNullable(it[networkId]) }
+        .filter(Optional<Network>::isPresent)
+        .map(Optional<Network>::get)
+        .firstElement()
+        .toLiveData().observe(this, Observer {
+          it?.let {
+            update(it, identityAdapter)
+          }
+        })
+    }
 
     saslEnabled.setDependent(saslGroup)
     autoidentifyEnabled.setDependent(autoidentifyGroup)
@@ -226,6 +210,40 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl
     return view
   }
 
+  private fun update(it: Network, identityAdapter: IdentityAdapter) {
+    if (this.network == null) {
+      this.network = Pair(it, it.copy())
+      this.network?.let { (_, data) ->
+        networkName.setText(data.networkName())
+
+        identityAdapter.indexOf(data.identity())?.let(identity::setSelection)
+
+        adapter.list = data.serverList()
+
+        saslEnabled.isChecked = data.useSasl()
+        saslAccount.setText(data.saslAccount())
+        saslPassword.setText(data.saslPassword())
+
+        autoidentifyEnabled.isChecked = data.useAutoIdentify()
+        autoidentifyService.setText(data.autoIdentifyService())
+        autoidentifyPassword.setText(data.autoIdentifyPassword())
+
+        autoreconnectEnabled.isChecked = data.useAutoReconnect()
+        autoreconnectInterval.setText(data.autoReconnectInterval().toString())
+        autoreconnectRetries.setText(data.autoReconnectRetries().toString())
+        autoreconnectUnlimited.isChecked = data.unlimitedReconnectRetries()
+
+        perform.setText(data.perform().joinToString("\n"))
+        rejoinChannels.isChecked = data.rejoinChannels()
+
+        customratelimitsEnabled.isChecked = data.useCustomMessageRate()
+        customratelimitsBurstSize.setText(data.messageRateBurstSize().toString())
+        customratelimitsUnlimited.isChecked = data.unlimitedMessageRate()
+        customratelimitsDelay.setText("${data.messageRateDelay() / 1000.0}")
+      }
+    }
+  }
+
   private fun serverClick(server: INetwork.Server) {
     startActivityForResult(
       NetworkServerActivity.intent(requireContext(), server = server),
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragment.kt
index e097b5bf9..a611ff3f3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragment.kt
@@ -19,12 +19,11 @@
 
 package de.kuschku.quasseldroid.ui.coresettings.network
 
-import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.util.helpers.value
 
-class NetworkCreateFragment : NetworkBaseFragment() {
+class NetworkCreateFragment : NetworkBaseFragment(true) {
   override fun onSave() = viewModel.session.value?.orNull()?.let { session ->
-    Network(-1, session.proxy).let { data ->
+    network?.let { (_, data) ->
       applyChanges(data)
       session.rpcHandler?.createNetwork(data.networkInfo(), emptyList())
       true
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragment.kt
index 01a0bbccf..bce4c03c9 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragment.kt
@@ -22,7 +22,7 @@ package de.kuschku.quasseldroid.ui.coresettings.network
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment
 
-class NetworkEditFragment : NetworkBaseFragment(), SettingsFragment.Deletable {
+class NetworkEditFragment : NetworkBaseFragment(false), SettingsFragment.Deletable {
   override fun onSave() = network?.let { (it, data) ->
     applyChanges(data)
     it?.requestUpdate(data.toVariantMap())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
new file mode 100644
index 000000000..2b30d7214
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
@@ -0,0 +1,362 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup
+
+import android.arch.lifecycle.MutableLiveData
+import android.arch.lifecycle.Observer
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.Parcelable
+import android.support.annotation.ColorRes
+import android.support.annotation.DrawableRes
+import android.support.design.widget.FloatingActionButton
+import android.support.v4.app.FragmentManager
+import android.support.v4.app.FragmentStatePagerAdapter
+import android.support.v4.view.ViewPager
+import android.support.v7.widget.ActionMenuView
+import android.util.SparseArray
+import android.view.ViewGroup
+import butterknife.BindView
+import butterknife.ButterKnife
+import dagger.android.support.DaggerAppCompatActivity
+import de.kuschku.libquassel.session.Backend
+import de.kuschku.libquassel.util.Optional
+import de.kuschku.libquassel.util.helpers.nullIf
+import de.kuschku.quasseldroid.Keys
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.settings.AppearanceSettings
+import de.kuschku.quasseldroid.settings.ConnectionSettings
+import de.kuschku.quasseldroid.settings.Settings
+import de.kuschku.quasseldroid.ui.clientsettings.about.AboutActivity
+import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity
+import de.kuschku.quasseldroid.ui.clientsettings.crash.CrashActivity
+import de.kuschku.quasseldroid.ui.clientsettings.whitelist.WhitelistActivity
+import de.kuschku.quasseldroid.util.helper.*
+import de.kuschku.quasseldroid.util.service.BackendServiceConnection
+import de.kuschku.quasseldroid.util.ui.LocaleHelper
+import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
+import io.reactivex.subjects.BehaviorSubject
+import javax.inject.Inject
+
+abstract class ServiceBoundSetupActivity :
+  DaggerAppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
+  @BindView(R.id.menu_view)
+  lateinit var menuView: ActionMenuView
+
+  @BindView(R.id.view_pager)
+  lateinit var viewPager: ViewPager
+
+  @BindView(R.id.next_button)
+  lateinit var button: FloatingActionButton
+
+  private lateinit var adapter: SlidePagerAdapter
+
+  protected abstract val fragments: List<SlideFragment>
+
+  private val currentPage = MutableLiveData<SlideFragment?>()
+  private val isValid = currentPage.switchMap(SlideFragment::valid).or(false)
+
+  @DrawableRes
+  protected val icon: Int = R.mipmap.ic_launcher_recents
+  @ColorRes
+  protected val recentsHeaderColor: Int = R.color.colorPrimary
+
+  private val connection = BackendServiceConnection()
+  protected val backend: BehaviorSubject<Optional<Backend>>
+    get() = connection.backend
+
+  protected open val initData: Bundle = Bundle()
+
+  protected fun runInBackground(f: () -> Unit) {
+    connection.backend.value.ifPresent {
+      it.sessionManager().handlerService.backend(f)
+    }
+  }
+
+  protected fun runInBackgroundDelayed(delayMillis: Long, f: () -> Unit) {
+    connection.backend.value.ifPresent {
+      it.sessionManager().handlerService.backendDelayed(delayMillis, f)
+    }
+  }
+
+  @Inject
+  lateinit var connectionSettings: ConnectionSettings
+
+  @Inject
+  lateinit var appearanceSettings: AppearanceSettings
+
+  @Inject
+  lateinit var viewModel: QuasselViewModel
+
+  protected var accountId: Long = -1
+
+  private var startedSelection = false
+
+  class SetupActivityViewPagerPageChangeListener(private val activity: ServiceBoundSetupActivity) :
+    ViewPager.OnPageChangeListener {
+    override fun onPageScrollStateChanged(state: Int) {
+      when (state) {
+        ViewPager.SCROLL_STATE_SETTLING -> activity.pageChanged()
+      }
+    }
+
+    override fun onPageScrolled(position: Int, positionOffset: Float,
+                                positionOffsetPixels: Int) = Unit
+
+    override fun onPageSelected(position: Int) = Unit
+  }
+
+  private lateinit var pageChangeListener: SetupActivityViewPagerPageChangeListener
+
+  private fun pageChanged() {
+    currentPage.value = adapter.getItem(viewPager.currentItem)
+    val drawable = if (viewPager.currentItem == adapter.totalCount - 1)
+      R.drawable.ic_check
+    else
+      R.drawable.ic_arrow_right
+    button.setImageResource(drawable)
+    currentPage.value?.requestFocus()
+  }
+
+  fun updateRecentsHeader() =
+    updateRecentsHeaderIfExisting(title.toString(), icon, recentsHeaderColor)
+
+  override fun setTitle(title: CharSequence?) {
+    super.setTitle(title)
+    updateRecentsHeader()
+  }
+
+  override fun onCreate(savedInstanceState: Bundle?) {
+    setTheme(R.style.Theme_SetupTheme)
+    super.onCreate(savedInstanceState)
+    viewModel.backendWrapper.onNext(this.backend)
+    packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA).labelRes
+      .nullIf { it == 0 }?.let(this::setTitle)
+    setContentView(R.layout.activity_setup)
+    ButterKnife.bind(this)
+
+    menuView.popupTheme = R.style.Widget_PopupOverlay_Light
+    menuInflater.inflate(R.menu.activity_setup, menuView.menu)
+    menuView.setOnMenuItemClickListener {
+      when (it.itemId) {
+        R.id.action_client_settings -> {
+          ClientSettingsActivity.launch(this)
+          true
+        }
+        R.id.action_certificates    -> {
+          WhitelistActivity.launch(this)
+          true
+        }
+        R.id.action_crashes         -> {
+          CrashActivity.launch(this)
+          true
+        }
+        R.id.action_about           -> {
+          AboutActivity.launch(this)
+          true
+        }
+        else                        -> false
+      }
+    }
+
+    adapter = SlidePagerAdapter(supportFragmentManager)
+    fragments.forEach(adapter::addFragment)
+    viewPager.adapter = adapter
+
+    pageChangeListener = SetupActivityViewPagerPageChangeListener(this)
+
+    button.setOnClickListener {
+      if (viewPager.currentItem == adapter.totalCount - 1)
+        onDoneInternal()
+      else
+        viewPager.setCurrentItem(viewPager.currentItem + 1, true)
+    }
+    isValid.observeSticky(
+      this, Observer {
+      if (it == true) {
+        button.show()
+        adapter.lastValidItem = viewPager.currentItem
+      } else {
+        button.hide()
+        adapter.lastValidItem = viewPager.currentItem - 1
+      }
+    })
+    viewPager.addOnPageChangeListener(pageChangeListener)
+    pageChanged()
+    updateRecentsHeader()
+    adapter.result.putAll(initData)
+
+    connection.context = this
+    lifecycle.addObserver(connection)
+    checkConnection()
+  }
+
+  override fun attachBaseContext(newBase: Context) {
+    super.attachBaseContext(LocaleHelper.setLocale(newBase))
+  }
+
+  private fun onDoneInternal() {
+    onDone(adapter.result)
+  }
+
+  abstract fun onDone(data: Bundle)
+
+  override fun onSaveInstanceState(outState: Bundle) {
+    outState.putInt(currentItemKey, viewPager.currentItem)
+    outState.putInt(lastValidItemKey, adapter.lastValidItem)
+    outState.putBundle(resultKey, adapter.result)
+    super.onSaveInstanceState(outState)
+  }
+
+  override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
+    super.onRestoreInstanceState(savedInstanceState)
+    if (savedInstanceState != null) {
+      if (savedInstanceState.containsKey(resultKey))
+        adapter.result.putAll(savedInstanceState.getBundle(resultKey))
+      if (savedInstanceState.containsKey(lastValidItemKey))
+        adapter.lastValidItem = savedInstanceState.getInt(lastValidItemKey)
+      if (savedInstanceState.containsKey(currentItemKey))
+        viewPager.currentItem = savedInstanceState.getInt(currentItemKey)
+      currentPage.value = adapter.getItem(viewPager.currentItem)
+    }
+    pageChanged()
+  }
+
+  private class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
+    FragmentStatePagerAdapter(fragmentManager) {
+    private val retainedFragments = SparseArray<SlideFragment>()
+
+    val result = Bundle()
+      get() {
+        (0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
+          it.getData(field)
+        }
+        return field
+      }
+
+    var lastValidItem = -1
+      set(value) {
+        field = value
+        notifyDataSetChanged()
+      }
+    private val list = mutableListOf<SlideFragment>()
+
+    override fun getItem(position: Int): SlideFragment {
+      return retainedFragments.get(position) ?: list[position]
+    }
+
+    override fun getCount() = Math.min(list.size, lastValidItem + 2)
+    val totalCount get() = list.size
+    fun addFragment(fragment: SlideFragment) {
+      list.add(fragment)
+    }
+
+    override fun instantiateItem(container: ViewGroup, position: Int): Any {
+      val fragment = super.instantiateItem(container, position)
+      storeNewFragment(position, fragment as SlideFragment)
+      return fragment
+    }
+
+    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
+      retainedFragments.get(position)?.getData(result)
+      retainedFragments.remove(position)
+      super.destroyItem(container, position, `object`)
+    }
+
+    override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
+      super.restoreState(state, loader)
+      if (state != null) {
+        val bundle = state as Bundle
+        val keys = bundle.keySet()
+        for (key in keys) {
+          if (key.startsWith("f")) {
+            val index = Integer.parseInt(key.substring(1))
+            val f = fragmentManager.getFragment(bundle, key)
+            if (f != null && f is SlideFragment) {
+              storeNewFragment(index, f)
+            }
+          }
+        }
+      }
+    }
+
+    private fun storeNewFragment(index: Int, fragment: SlideFragment) {
+      fragment.initData = result
+      retainedFragments.put(index, fragment)
+    }
+  }
+
+  override fun onStart() {
+    if (Settings.appearance(this) != appearanceSettings) {
+      recreate()
+    }
+    sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
+      registerOnSharedPreferenceChangeListener(this@ServiceBoundSetupActivity)
+    }
+    checkConnection()
+    super.onStart()
+  }
+
+  override fun onStop() {
+    super.onStop()
+    sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
+      unregisterOnSharedPreferenceChangeListener(this@ServiceBoundSetupActivity)
+    }
+  }
+
+  override fun onDestroy() {
+    lifecycle.removeObserver(connection)
+    super.onDestroy()
+  }
+
+  override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) =
+    checkConnection()
+
+  private fun checkConnection() {
+    accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
+      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+
+    val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
+      getBoolean(Keys.Status.reconnect, false)
+    }
+    val accountIdValid = accountId != -1L
+    if (!reconnect || !accountIdValid) {
+      finish()
+    } else {
+      connection.start()
+      connection.bind()
+    }
+  }
+
+  override fun onBackPressed() {
+    if (viewPager.currentItem == 0)
+      super.onBackPressed()
+    else
+      viewPager.currentItem -= 1
+  }
+
+  companion object {
+    private const val currentItemKey = ":setupActivity:currentItem"
+    private const val lastValidItemKey = ":setupActivity:lastValidItem"
+    private const val resultKey = ":setupActivity:result"
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupConnectionSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupConnectionSlide.kt
index c8d219118..8eeddd2db 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupConnectionSlide.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupConnectionSlide.kt
@@ -51,7 +51,7 @@ class AccountSetupConnectionSlide : SlideFragment() {
   }
 
   override val title = R.string.slide_account_connection_title
-  override val description = R.string.slideAccountConnectionDescription
+  override val description = R.string.slide_account_connection_description
 
   override fun setData(data: Bundle) {
     if (data.containsKey("host"))
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/DefaultNetworkAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/DefaultNetworkAdapter.kt
new file mode 100644
index 000000000..4f0ba7856
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/DefaultNetworkAdapter.kt
@@ -0,0 +1,82 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.support.v7.widget.RecyclerView
+import android.support.v7.widget.ThemedSpinnerAdapter
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.DefaultNetwork
+import de.kuschku.quasseldroid.defaults.DefaultNetworks
+import de.kuschku.quasseldroid.util.ui.ContextThemeWrapper
+import de.kuschku.quasseldroid.util.ui.RecyclerSpinnerAdapter
+import javax.inject.Inject
+
+class DefaultNetworkAdapter @Inject constructor(defaultNetworks: DefaultNetworks) :
+  RecyclerSpinnerAdapter<DefaultNetworkAdapter.DefaultNetworkViewHolder>(), ThemedSpinnerAdapter {
+  private val data: List<DefaultNetwork?> = defaultNetworks.networks + (null as DefaultNetwork?)
+
+  override fun isEmpty() = data.isEmpty()
+
+  override fun onBindViewHolder(holder: DefaultNetworkViewHolder, position: Int) =
+    holder.bind(getItem(position))
+
+  override fun onCreateViewHolder(parent: ViewGroup, dropDown: Boolean)
+    : DefaultNetworkViewHolder {
+    val inflater = LayoutInflater.from(
+      if (dropDown)
+        ContextThemeWrapper(parent.context, dropDownViewTheme)
+      else
+        parent.context
+    )
+    val view = inflater.inflate(R.layout.widget_spinner_item_inline, parent, false)
+    return DefaultNetworkViewHolder(view)
+  }
+
+  override fun getItem(position: Int) = data[position]
+
+  override fun getCount() = data.size
+
+  fun default() = data.filterNotNull().firstOrNull(DefaultNetwork::default)
+
+  fun indexOf(value: DefaultNetwork?) = data.indexOf(value)
+
+  override fun getItemId(position: Int) = position.toLong()
+
+  class DefaultNetworkViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    @BindView(android.R.id.text1)
+    lateinit var text: TextView
+
+    init {
+      ButterKnife.bind(this, itemView)
+    }
+
+    fun bind(network: DefaultNetwork?) {
+      network?.let {
+        text.text = it.name
+      } ?: text.setText(R.string.label_network_custom)
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
new file mode 100644
index 000000000..556db453d
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
@@ -0,0 +1,106 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.app.Activity
+import android.arch.lifecycle.Observer
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.quassel.syncables.Identity
+import de.kuschku.libquassel.quassel.syncables.Network
+import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
+import de.kuschku.libquassel.util.helpers.value
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.DefaultNetwork
+import de.kuschku.quasseldroid.defaults.Defaults
+import de.kuschku.quasseldroid.ui.setup.ServiceBoundSetupActivity
+import de.kuschku.quasseldroid.util.helper.toLiveData
+import java.util.*
+
+class UserSetupActivity : ServiceBoundSetupActivity() {
+  override val initData
+    get() = Bundle().apply {
+      putString("nick", getString(R.string.default_identity_nick, Random().nextInt(16)))
+      putString("realname", getString(R.string.default_identity_realname))
+    }
+
+  override fun onDone(data: Bundle) {
+    runInBackground {
+      val network = data.getSerializable("network") as? DefaultNetwork
+      if (network != null) {
+        viewModel.session.value?.orNull()?.rpcHandler?.apply {
+          createIdentity(Defaults.identity(this@UserSetupActivity).apply {
+            setIdentityName(this@UserSetupActivity.getString(R.string.default_identity_identity_name))
+            setNicks(listOf(data.getString("nick")))
+            setRealName(data.getString("realname"))
+          }, emptyMap())
+
+          viewModel.identities
+            .map(Map<IdentityId, Identity>::values)
+            .filter(Collection<Identity>::isNotEmpty)
+            .map(Collection<Identity>::first)
+            .firstElement()
+            .toLiveData().observe(this@UserSetupActivity, Observer {
+              if (it != null) {
+                createNetwork(INetwork.NetworkInfo(
+                  networkName = network.name,
+                  identity = it.id(),
+                  serverList = network.servers.map {
+                    INetwork.Server(
+                      host = it.host,
+                      port = it.port,
+                      useSsl = it.secure
+                    )
+                  }
+                ), data.getStringArray("channels").toList())
+              }
+            })
+
+          viewModel.networks
+            .map(Map<NetworkId, Network>::values)
+            .filter(Collection<Network>::isNotEmpty)
+            .map(Collection<Network>::first)
+            .firstElement()
+            .toLiveData().observe(this@UserSetupActivity, Observer {
+              it?.requestConnect()
+              runOnUiThread {
+                setResult(Activity.RESULT_OK)
+                finish()
+              }
+            })
+        }
+      }
+    }
+  }
+
+  override val fragments = listOf(
+    UserSetupIdentitySlide(),
+    UserSetupNetworkSlide(),
+    UserSetupChannelsSlide()
+  )
+
+  companion object {
+    fun launch(context: Context) = context.startActivity(intent(context))
+    fun intent(context: Context) = Intent(context, UserSetupActivity::class.java)
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupChannelsSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupChannelsSlide.kt
new file mode 100644
index 000000000..b3e2fd6b5
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupChannelsSlide.kt
@@ -0,0 +1,72 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.os.Bundle
+import android.support.design.widget.TextInputLayout
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.DefaultNetwork
+import de.kuschku.quasseldroid.ui.setup.SlideFragment
+
+class UserSetupChannelsSlide : SlideFragment() {
+  @BindView(R.id.channelsWrapper)
+  lateinit var channelsWrapper: TextInputLayout
+
+  @BindView(R.id.channels)
+  lateinit var channelsField: EditText
+
+  override fun isValid() = true
+
+  override val title = R.string.slide_user_channels_title
+  override val description = R.string.slide_user_channels_description
+
+  override fun setData(data: Bundle) {
+    if (data.containsKey("channels"))
+      channelsField.setText(data.getStringArray("channels").joinToString("\n"))
+    else if (data.containsKey("network"))
+      (data.getSerializable("network") as? DefaultNetwork)?.let {
+        channelsField.setText(it.defaultChannels.joinToString("\n"))
+      }
+    updateValidity()
+  }
+
+  override fun getData(data: Bundle) {
+    data.putStringArray("channels",
+                        channelsField.text.toString()
+                          .split('\n', ' ', ',', ';')
+                          .map(String::trim)
+                          .filter(String::isNotBlank)
+                          .toTypedArray()
+    )
+  }
+
+  override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
+                               savedInstanceState: Bundle?): View {
+    val view = inflater.inflate(R.layout.setup_user_channels, container, false)
+    ButterKnife.bind(this, view)
+    return view
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt
new file mode 100644
index 000000000..676e288fe
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt
@@ -0,0 +1,40 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.support.v4.app.FragmentActivity
+import dagger.Binds
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+
+@Module
+abstract class UserSetupFragmentProvider {
+  @Binds
+  abstract fun bindFragmentActivity(activity: UserSetupActivity): FragmentActivity
+
+  @ContributesAndroidInjector
+  abstract fun bindUserSetupIdentitySlide(): UserSetupIdentitySlide
+
+  @ContributesAndroidInjector
+  abstract fun bindUserSetupNetworkSlide(): UserSetupNetworkSlide
+
+  @ContributesAndroidInjector
+  abstract fun bindUserSetupChannelsSlide(): UserSetupChannelsSlide
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt
new file mode 100644
index 000000000..f997f7dbc
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt
@@ -0,0 +1,96 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.os.Bundle
+import android.support.design.widget.TextInputLayout
+import android.text.Editable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.ui.setup.SlideFragment
+import de.kuschku.quasseldroid.util.Patterns
+import de.kuschku.quasseldroid.util.TextValidator
+import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
+import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer
+import javax.inject.Inject
+
+class UserSetupIdentitySlide : SlideFragment() {
+  @BindView(R.id.nickWrapper)
+  lateinit var nickWrapper: TextInputLayout
+
+  @BindView(R.id.nick)
+  lateinit var nickField: EditText
+
+  @BindView(R.id.realnameWrapper)
+  lateinit var realnameWrapper: TextInputLayout
+
+  @BindView(R.id.realname)
+  lateinit var realnameField: EditText
+
+  @Inject
+  lateinit var ircFormatSerializer: IrcFormatSerializer
+
+  @Inject
+  lateinit var ircFormatDeserializer: IrcFormatDeserializer
+
+  override fun isValid(): Boolean {
+    return nickValidator.isValid
+  }
+
+  override val title = R.string.slide_user_identity_title
+  override val description = R.string.slide_user_identity_description
+
+  override fun setData(data: Bundle) {
+    if (data.containsKey("nick"))
+      nickField.setText(data.getString("nick"))
+    if (data.containsKey("realname"))
+      realnameField.setText(ircFormatDeserializer.formatString(data.getString("realname"), true))
+    updateValidity()
+  }
+
+  override fun getData(data: Bundle) {
+    data.putString("nick", nickField.text.toString())
+    data.putString("realname", ircFormatSerializer.toEscapeCodes(realnameField.text))
+  }
+
+  override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
+                               savedInstanceState: Bundle?): View {
+    val view = inflater.inflate(R.layout.setup_user_identity, container, false)
+    ButterKnife.bind(this, view)
+    nickValidator = object : TextValidator(
+      nickWrapper::setError, resources.getString(R.string.hint_invalid_nick)
+    ) {
+      override fun validate(text: Editable) = text.isNotEmpty() && text.matches(Patterns.IRC_NICK)
+
+      override fun onChanged() = updateValidity()
+    }
+
+    nickField.addTextChangedListener(nickValidator)
+    nickValidator.afterTextChanged(nickField.text)
+    return view
+  }
+
+  private lateinit var nickValidator: TextValidator
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupNetworkSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupNetworkSlide.kt
new file mode 100644
index 000000000..f6dab7357
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupNetworkSlide.kt
@@ -0,0 +1,183 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * 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.ui.setup.user
+
+import android.os.Bundle
+import android.support.design.widget.TextInputLayout
+import android.support.v7.widget.SwitchCompat
+import android.text.Editable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.EditText
+import android.widget.Spinner
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.libquassel.util.helpers.nullIf
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.defaults.DefaultNetwork
+import de.kuschku.quasseldroid.defaults.DefaultNetworkServer
+import de.kuschku.quasseldroid.ui.setup.SlideFragment
+import de.kuschku.quasseldroid.util.Patterns
+import de.kuschku.quasseldroid.util.TextValidator
+import de.kuschku.quasseldroid.util.ui.AnimationHelper
+import javax.inject.Inject
+
+class UserSetupNetworkSlide : SlideFragment() {
+  @BindView(R.id.network)
+  lateinit var network: Spinner
+
+  @BindView(R.id.network_group)
+  lateinit var networkGroup: ViewGroup
+
+  @BindView(R.id.nameWrapper)
+  lateinit var nameWrapper: TextInputLayout
+
+  @BindView(R.id.name)
+  lateinit var nameField: EditText
+
+  @BindView(R.id.hostWrapper)
+  lateinit var hostWrapper: TextInputLayout
+
+  @BindView(R.id.host)
+  lateinit var hostField: EditText
+
+  @BindView(R.id.portWrapper)
+  lateinit var portWrapper: TextInputLayout
+
+  @BindView(R.id.port)
+  lateinit var portField: EditText
+
+  @BindView(R.id.ssl_enabled)
+  lateinit var sslEnabled: SwitchCompat
+
+  @Inject
+  lateinit var networkAdapter: DefaultNetworkAdapter
+
+  override fun isValid(): Boolean {
+    return (this.network.selectedItemPosition != -1 &&
+            networkAdapter.getItem(this.network.selectedItemPosition) != null) ||
+           (nameValidator.isValid && hostValidator.isValid && portValidator.isValid)
+  }
+
+  override val title = R.string.slide_account_connection_title
+  override val description = R.string.slide_account_connection_description
+
+  override fun setData(data: Bundle) {
+    if (data.containsKey("network")) {
+      val network = data.getSerializable("network") as? DefaultNetwork
+                    ?: networkAdapter.default()
+      if (network != null) {
+        val position = networkAdapter.indexOf(network)
+        if (position == -1) {
+          this.network.setSelection(networkAdapter.indexOf(null))
+          network.servers.firstOrNull()?.let {
+            this.hostField.setText(it.host)
+            this.portField.setText(it.port.toString())
+            this.sslEnabled.isChecked = it.secure
+          }
+        }
+      }
+    }
+    updateValidity()
+  }
+
+  override fun getData(data: Bundle) {
+    data.putSerializable(
+      "network",
+      networkAdapter.getItem(this.network.selectedItemPosition)
+      ?: DefaultNetwork(
+        name = nameField.text.toString(),
+        servers = listOf(
+          DefaultNetworkServer(
+            host = hostField.text.toString(),
+            port = portField.text.toString().toIntOrNull()
+                   ?: if (sslEnabled.isChecked) 6697
+                   else 6667,
+            secure = sslEnabled.isChecked
+          )
+        )
+      )
+    )
+  }
+
+  override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
+                               savedInstanceState: Bundle?): View {
+    val view = inflater.inflate(R.layout.setup_user_network, container, false)
+    ButterKnife.bind(this, view)
+    nameValidator = object : TextValidator(
+      nameWrapper::setError, resources.getString(R.string.hint_invalid_name)
+    ) {
+      override fun validate(text: Editable) = text.isNotBlank()
+
+      override fun onChanged() = updateValidity()
+    }
+    hostValidator = object : TextValidator(
+      hostWrapper::setError, resources.getString(R.string.hint_invalid_host)
+    ) {
+      override fun validate(text: Editable) =
+        text.toString().matches(Patterns.DOMAIN_NAME)
+
+      override fun onChanged() = updateValidity()
+    }
+    portValidator = object : TextValidator(
+      portWrapper::setError, resources.getString(R.string.hint_invalid_port)
+    ) {
+      override fun validate(text: Editable) = text.toString().toIntOrNull() in (0 until 65536)
+
+      override fun onChanged() = updateValidity()
+    }
+
+    nameField.addTextChangedListener(nameValidator)
+    hostField.addTextChangedListener(hostValidator)
+    portField.addTextChangedListener(portValidator)
+    nameValidator.afterTextChanged(nameField.text)
+    hostValidator.afterTextChanged(hostField.text)
+    portValidator.afterTextChanged(portField.text)
+
+    network.adapter = networkAdapter
+    network.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+      fun selected(item: DefaultNetwork?) {
+        if (item == null) {
+          AnimationHelper.expand(networkGroup)
+        } else {
+          AnimationHelper.collapse(networkGroup)
+        }
+      }
+
+      override fun onNothingSelected(parent: AdapterView<*>?) = selected(null)
+
+      override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) =
+        selected(networkAdapter.getItem(position))
+    }
+    networkAdapter.default()?.let {
+      networkAdapter.indexOf(it).nullIf { it == -1 }?.let {
+        network.setSelection(it)
+      }
+    }
+
+    return view
+  }
+
+  private lateinit var nameValidator: TextValidator
+  private lateinit var hostValidator: TextValidator
+  private lateinit var portValidator: TextValidator
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt b/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
index 3cab7a984..d6ddd5350 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
@@ -113,4 +113,8 @@ object Patterns {
   @Language("RegExp")
   const val MATRIX_REALNAME_STR = """^@[^:]+:$DOMAIN_NAME_STR$"""
   val MATRIX_REALNAME = Regex(MATRIX_REALNAME_STR)
+
+  @Language("RegExp")
+  const val IRC_NICK_STR = """[A-Za-z\x5b-\x60\x7b-\x7d][A-Za-z0-9\x5b-\x60\x7b-\x7d]*"""
+  val IRC_NICK = Regex(IRC_NICK_STR)
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/AndroidThreeTenBackport.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/AndroidThreeTenBackport.kt
index 2f9dc704e..6f19f2eb7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/backport/AndroidThreeTenBackport.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/AndroidThreeTenBackport.kt
@@ -37,7 +37,7 @@ object AndroidThreeTenBackport {
     val provider: TzdbZoneRulesProvider
     var inputStream: InputStream? = null
     try {
-      inputStream = context.assets.open("org/threeten/bp/TZDB.dat")
+      inputStream = context.assets.open("TZDB.dat")
       provider = TzdbZoneRulesProvider(inputStream)
     } catch (e: IOException) {
       throw IllegalStateException("TZDB.dat missing from assets.", e)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/helper/GsonHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/helper/GsonHelper.kt
index d3f9af263..cd703c201 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/helper/GsonHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/helper/GsonHelper.kt
@@ -21,6 +21,8 @@ package de.kuschku.quasseldroid.util.helper
 
 import com.google.gson.Gson
 import java.io.File
+import java.io.Reader
 
 inline fun <reified T> Gson.fromJson(file: File): T = this.fromJson(file.reader(), T::class.java)
 inline fun <reified T> Gson.fromJson(text: String): T = this.fromJson(text, T::class.java)
+inline fun <reified T> Gson.fromJson(reader: Reader): T = this.fromJson(reader, T::class.java)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
index 111d27191..bda94b131 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
@@ -19,6 +19,10 @@
 
 package de.kuschku.quasseldroid.util.service
 
+import android.arch.lifecycle.Lifecycle
+import android.arch.lifecycle.LifecycleObserver
+import android.arch.lifecycle.LifecycleOwner
+import android.arch.lifecycle.OnLifecycleEvent
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -30,7 +34,7 @@ import de.kuschku.quasseldroid.service.QuasselBinder
 import de.kuschku.quasseldroid.service.QuasselService
 import io.reactivex.subjects.BehaviorSubject
 
-class BackendServiceConnection : ServiceConnection {
+class BackendServiceConnection : ServiceConnection, LifecycleObserver {
   val backend: BehaviorSubject<Optional<Backend>> = BehaviorSubject.createDefault(Optional.empty())
 
   var context: Context? = null
@@ -75,10 +79,6 @@ class BackendServiceConnection : ServiceConnection {
     }
   }
 
-  fun stop(intent: Intent = QuasselService.intent(context!!)) {
-    context?.stopService(intent)
-  }
-
   @Synchronized
   fun unbind() {
     if (state == State.BOUND || state == State.BINDING) {
@@ -86,4 +86,13 @@ class BackendServiceConnection : ServiceConnection {
       context?.unbindService(this)
     }
   }
+
+  @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+  fun onCreate(lifecycleOwner: LifecycleOwner) = start()
+
+  @OnLifecycleEvent(Lifecycle.Event.ON_START)
+  fun onStart(lifecycleOwner: LifecycleOwner) = bind()
+
+  @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+  fun onStop(lifecycleOwner: LifecycleOwner) = unbind()
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
index 4f140b161..db87d4236 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
@@ -19,9 +19,7 @@
 
 package de.kuschku.quasseldroid.util.service
 
-import android.app.Activity
 import android.content.Context
-import android.content.Intent
 import android.content.SharedPreferences
 import android.os.Bundle
 import android.support.annotation.ColorRes
@@ -30,10 +28,8 @@ import de.kuschku.libquassel.session.Backend
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
-import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.ConnectionSettings
 import de.kuschku.quasseldroid.settings.Settings
-import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActivity
 import de.kuschku.quasseldroid.util.helper.sharedPreferences
 import de.kuschku.quasseldroid.util.helper.updateRecentsHeaderIfExisting
 import de.kuschku.quasseldroid.util.ui.ThemedActivity
@@ -41,8 +37,8 @@ import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
 import io.reactivex.subjects.BehaviorSubject
 import javax.inject.Inject
 
-abstract class ServiceBoundActivity : ThemedActivity(),
-                                      SharedPreferences.OnSharedPreferenceChangeListener {
+abstract class ServiceBoundActivity :
+  ThemedActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
   @DrawableRes
   protected val icon: Int = R.mipmap.ic_launcher_recents
   @ColorRes
@@ -64,9 +60,6 @@ abstract class ServiceBoundActivity : ThemedActivity(),
     }
   }
 
-  @Inject
-  lateinit var autoCompleteSettings: AutoCompleteSettings
-
   @Inject
   lateinit var connectionSettings: ConnectionSettings
 
@@ -75,14 +68,13 @@ abstract class ServiceBoundActivity : ThemedActivity(),
 
   protected var accountId: Long = -1
 
-  private var startedSelection = false
-
   override fun onCreate(savedInstanceState: Bundle?) {
-    connection.context = this
-    checkConnection()
     super.onCreate(savedInstanceState)
     viewModel.backendWrapper.onNext(this.backend)
     updateRecentsHeader()
+    connection.context = this
+    lifecycle.addObserver(connection)
+    checkConnection()
   }
 
   fun updateRecentsHeader() =
@@ -106,16 +98,19 @@ abstract class ServiceBoundActivity : ThemedActivity(),
 
   override fun onStop() {
     super.onStop()
-    connection.unbind()
     sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
       unregisterOnSharedPreferenceChangeListener(this@ServiceBoundActivity)
     }
   }
 
-  override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
-    checkConnection()
+  override fun onDestroy() {
+    lifecycle.removeObserver(connection)
+    super.onDestroy()
   }
 
+  override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) =
+    checkConnection()
+
   private fun checkConnection() {
     accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
       ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
@@ -125,32 +120,14 @@ abstract class ServiceBoundActivity : ThemedActivity(),
     }
     val accountIdValid = accountId != -1L
     if (!reconnect || !accountIdValid) {
-      if (!startedSelection) {
-        startActivityForResult(AccountSelectionActivity.intent(this), REQUEST_SELECT_ACCOUNT)
-        startedSelection = true
-      }
+      onSelectAccount()
     } else {
       connection.start()
       connection.bind()
     }
   }
 
-  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-    super.onActivityResult(requestCode, resultCode, data)
-
-    if (requestCode == REQUEST_SELECT_ACCOUNT) {
-      startedSelection = false
-
-      if (resultCode == Activity.RESULT_CANCELED) {
-        finish()
-      }
-    }
-  }
-
-  protected fun stopService() {
-    connection.unbind()
-    connection.stop()
-  }
+  protected open fun onSelectAccount() = Unit
 
   companion object {
     const val REQUEST_SELECT_ACCOUNT = 1
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
index 8f9462423..86f0c7630 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
@@ -56,17 +56,12 @@ abstract class ServiceBoundFragment : DaggerFragment() {
       ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
 
     connection.context = context
+    lifecycle.addObserver(connection)
     super.onCreate(savedInstanceState)
-    connection.start()
   }
 
-  override fun onStart() {
-    connection.bind()
-    super.onStart()
-  }
-
-  override fun onStop() {
-    super.onStop()
-    connection.unbind()
+  override fun onDestroy() {
+    lifecycle.removeObserver(connection)
+    super.onDestroy()
   }
 }
diff --git a/app/src/main/res/layout/setup_account_connection.xml b/app/src/main/res/layout/setup_account_connection.xml
index 8a0359f19..bbe764da0 100644
--- a/app/src/main/res/layout/setup_account_connection.xml
+++ b/app/src/main/res/layout/setup_account_connection.xml
@@ -46,7 +46,7 @@
     android:id="@+id/portWrapper"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:hint="@string/labelConnectionPort"
+    android:hint="@string/label_connection_port"
     app:errorEnabled="true"
     tools:ignore="LabelFor">
 
diff --git a/app/src/main/res/layout/setup_account_edit.xml b/app/src/main/res/layout/setup_account_edit.xml
index 098530b96..4bdf7366a 100644
--- a/app/src/main/res/layout/setup_account_edit.xml
+++ b/app/src/main/res/layout/setup_account_edit.xml
@@ -114,7 +114,7 @@
           android:id="@+id/portWrapper"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
-          android:hint="@string/labelConnectionPort"
+          android:hint="@string/label_connection_port"
           tools:ignore="LabelFor">
 
           <EditText
diff --git a/app/src/main/res/layout/setup_user_channels.xml b/app/src/main/res/layout/setup_user_channels.xml
new file mode 100644
index 000000000..3d8976048
--- /dev/null
+++ b/app/src/main/res/layout/setup_user_channels.xml
@@ -0,0 +1,43 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  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="vertical"
+  android:padding="32dp">
+
+  <android.support.design.widget.TextInputLayout
+    android:id="@+id/channelsWrapper"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:hint="@string/label_channels"
+    tools:ignore="LabelFor">
+
+    <EditText
+      android:id="@+id/channels"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:inputType="textMultiLine"
+      app:errorEnabled="true" />
+  </android.support.design.widget.TextInputLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/setup_user_identity.xml b/app/src/main/res/layout/setup_user_identity.xml
new file mode 100644
index 000000000..38c5eaf83
--- /dev/null
+++ b/app/src/main/res/layout/setup_user_identity.xml
@@ -0,0 +1,59 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  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="vertical"
+  android:padding="32dp">
+
+  <android.support.design.widget.TextInputLayout
+    android:id="@+id/nickWrapper"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:hint="@string/settings_identity_nick"
+    tools:ignore="LabelFor">
+
+    <EditText
+      android:id="@+id/nick"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:inputType="textVisiblePassword|textNoSuggestions"
+      app:errorEnabled="true" />
+  </android.support.design.widget.TextInputLayout>
+
+  <android.support.design.widget.TextInputLayout
+    android:id="@+id/realnameWrapper"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:hint="@string/settings_identity_real_name"
+    app:passwordToggleEnabled="true"
+    tools:ignore="LabelFor">
+
+    <EditText
+      android:id="@+id/realname"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:inputType="textPersonName"
+      app:errorEnabled="true" />
+  </android.support.design.widget.TextInputLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/setup_user_network.xml b/app/src/main/res/layout/setup_user_network.xml
new file mode 100644
index 000000000..9bf02ee51
--- /dev/null
+++ b/app/src/main/res/layout/setup_user_network.xml
@@ -0,0 +1,101 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  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="vertical"
+  android:padding="32dp">
+
+  <TextView
+    style="@style/Widget.CoreSettings.EditTextHeader"
+    android:text="@string/settings_network_title" />
+
+  <Spinner
+    android:id="@+id/network"
+    style="@style/Widget.FullWidthSpinner"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:listitem="@layout/widget_spinner_item_inline" />
+
+  <LinearLayout
+    android:id="@+id/network_group"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.design.widget.TextInputLayout
+      android:id="@+id/nameWrapper"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:hint="@string/settings_network_network_name"
+      tools:ignore="LabelFor">
+
+      <EditText
+        android:id="@+id/name"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textUri|textNoSuggestions"
+        app:errorEnabled="true" />
+    </android.support.design.widget.TextInputLayout>
+
+    <android.support.design.widget.TextInputLayout
+      android:id="@+id/hostWrapper"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:hint="@string/settings_networkserver_host"
+      tools:ignore="LabelFor">
+
+      <EditText
+        android:id="@+id/host"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textUri|textNoSuggestions"
+        app:errorEnabled="true" />
+    </android.support.design.widget.TextInputLayout>
+
+    <android.support.design.widget.TextInputLayout
+      android:id="@+id/portWrapper"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:hint="@string/settings_networkserver_port"
+      app:passwordToggleEnabled="true"
+      tools:ignore="LabelFor">
+
+      <EditText
+        android:id="@+id/port"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="number"
+        android:text="6667"
+        app:errorEnabled="true"
+        tools:ignore="HardcodedText" />
+    </android.support.design.widget.TextInputLayout>
+
+    <android.support.v7.widget.SwitchCompat
+      android:id="@+id/ssl_enabled"
+      style="@style/Widget.CoreSettings.PrimaryItemSwitch"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:text="@string/settings_networkserver_ssl_enabled" />
+  </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/values-de/strings_defaults.xml b/app/src/main/res/values-de/strings_defaults.xml
new file mode 100644
index 000000000..e4e1464d0
--- /dev/null
+++ b/app/src/main/res/values-de/strings_defaults.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+  
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+  
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+  
+  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/>.
+  -->
+
+<resources>
+  <string name="default_bufferviewconfig_name">All Chats</string>
+
+  <string name="default_identity_identity_name">Default Identity</string>
+  <string name="default_identity_name">&lt;empty&gt;</string>
+  <string name="default_identity_realname">Quassel IRC User</string>
+  <string name="default_identity_nick">quassel%1$d</string>
+  <string name="default_identity_ident">quassel</string>
+  <string name="default_identity_awayreason">Gone fishing.</string>
+  <string name="default_identity_autoawayreason">Not here. No, really. not here!</string>
+  <string name="default_identity_detachawayreason">All Quassel clients vanished from the face of the earth…</string>
+  <string name="default_identity_kickreason">Kindergarten is elsewhere!</string>
+  <string name="default_identity_partreason">https://quassel-irc.org - Chat comfortably. Anywhere.</string>
+  <string name="default_identity_quitreason">https://quassel-irc.org - Chat comfortably. Anywhere.</string>
+</resources>
diff --git a/app/src/main/res/values-de/strings_settings.xml b/app/src/main/res/values-de/strings_settings.xml
index 4112e732b..81aca8a33 100644
--- a/app/src/main/res/values-de/strings_settings.xml
+++ b/app/src/main/res/values-de/strings_settings.xml
@@ -64,6 +64,7 @@
   <string name="settings_identity_identity_name">Identitätsname</string>
   <string name="settings_identity_real_name">Realname</string>
   <string name="settings_identity_ident">Ident</string>
+  <string name="settings_identity_nick">Spitzname</string>
   <string name="settings_identity_nicks">Spitznamen</string>
   <string name="settings_identity_messages">Meldungen</string>
   <string name="settings_identity_kick_reason">Rauswurfgrund</string>
diff --git a/app/src/main/res/values-de/strings_setup.xml b/app/src/main/res/values-de/strings_setup.xml
index cd6f80087..abe7857ca 100644
--- a/app/src/main/res/values-de/strings_setup.xml
+++ b/app/src/main/res/values-de/strings_setup.xml
@@ -25,10 +25,10 @@
 
   <!-- Account Connection -->
   <string name="slide_account_connection_title">Verbindung</string>
-  <string name="slideAccountConnectionDescription">Bitte gebe an die Adresse des Servers an, auf dem dein Core läuft</string>
+  <string name="slide_account_connection_description">Bitte gebe an die Adresse des Servers an, auf dem dein Core läuft</string>
 
   <string name="label_connection_host">Hostname</string>
-  <string name="labelConnectionPort">Port</string>
+  <string name="label_connection_port">Port</string>
 
   <string name="hint_invalid_host">Ungültiger Hostname</string>
   <string name="hint_invalid_port">Ungültiger Port</string>
@@ -52,13 +52,32 @@
 
   <!-- Core Authenticator Select -->
   <string name="slide_core_authenticator_select_title">Authentifizierungsbackend auswählen</string>
-  <string name="slide_core_authenticator_select_description">Bitte wähle aus, welches Authentifizierungsbackend der Quassel Core verwenden soll</string>
+  <string name="slide_core_authenticator_select_description">Bitte wähle aus, welches Authentifizierungsbackend der Quassel Core verwenden soll.</string>
 
   <!-- Core Backend Select -->
   <string name="slide_core_backend_select_title">Datenbank auswählen</string>
-  <string name="slide_core_backend_select_description">Bitte wähle aus, in welchem Datenbankbackend der Quassel Core Nachrichten und andere Daten speichern soll</string>
+  <string name="slide_core_backend_select_description">Bitte wähle aus, in welchem Datenbankbackend der Quassel Core Nachrichten und andere Daten speichern soll.</string>
 
   <!-- Core Backend Config -->
   <string name="slide_core_backend_setup_title">Datenbank konfigurieren</string>
-  <string name="slide_core_backend_setup_description">Bitte konfiguriere das ausgewählte Datenbankbackend</string>
+  <string name="slide_core_backend_setup_description">Bitte konfiguriere das ausgewählte Datenbankbackend.</string>
+
+
+  <!-- User Identity -->
+  <string name="slide_user_identity_title">Identität einrichten</string>
+  <string name="slide_user_identity_description">Bitte wähle einen Spitznamen.</string>
+
+  <string name="hint_invalid_nick">Kein valider Spitzname</string>
+
+  <!-- User Network -->
+  <string name="slide_user_network_title">Netzwerk einrichten</string>
+  <string name="slide_user_network_description">Wähle ein Netzwerk aus, zu dem du dich verbinden möchtest.</string>
+
+  <string name="label_network_custom">Benutzerdefiniertes Netzwerk…</string>
+
+  <!-- User Channels -->
+  <string name="slide_user_channels_title">Räume einrichten</string>
+  <string name="slide_user_channels_description">Wähle aus, welche Räume du betreten möchtest.</string>
+
+  <string name="label_channels">Channels</string>
 </resources>
diff --git a/app/src/main/res/values-lt/strings_defaults.xml b/app/src/main/res/values-lt/strings_defaults.xml
new file mode 100644
index 000000000..740fb6ff6
--- /dev/null
+++ b/app/src/main/res/values-lt/strings_defaults.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+  
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+  
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+  
+  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/>.
+  -->
+
+<resources>
+  <string name="default_bufferviewconfig_name">Visi pokalbiai</string>
+
+  <string name="default_identity_identity_name">Numatyta tapatybė</string>
+  <string name="default_identity_partreason">https://quassel-irc.org - Bendrauk patogiai bet kur.</string>
+  <string name="default_identity_quitreason">https://quassel-irc.org - Bendrauk patogiai bet kur.</string>
+</resources>
diff --git a/app/src/main/res/values-lt/strings_settings.xml b/app/src/main/res/values-lt/strings_settings.xml
index a7d1ca553..4729e6730 100644
--- a/app/src/main/res/values-lt/strings_settings.xml
+++ b/app/src/main/res/values-lt/strings_settings.xml
@@ -64,6 +64,7 @@
   <string name="settings_identity_identity_name">Tapatybės pavadinimas</string>
   <string name="settings_identity_real_name">Tikras Vardas</string>
   <string name="settings_identity_ident">Ident</string>
+  <string name="settings_identity_nick">Slapyvardis</string>
   <string name="settings_identity_nicks">Slapyvardžiai</string>
   <string name="settings_identity_messages">Žinutės</string>
   <string name="settings_identity_kick_reason">Išspyrimo Priežastis</string>
diff --git a/app/src/main/res/values-lt/strings_setup.xml b/app/src/main/res/values-lt/strings_setup.xml
index b72703af4..8b8532f88 100644
--- a/app/src/main/res/values-lt/strings_setup.xml
+++ b/app/src/main/res/values-lt/strings_setup.xml
@@ -25,10 +25,10 @@
 
   <!-- Account Connection -->
   <string name="slide_account_connection_title">Ryšys</string>
-  <string name="slideAccountConnectionDescription">Pirma, pasirinkite prie kurio serverio prisijungęs jūsų branduolys.</string>
+  <string name="slide_account_connection_description">Pirma, pasirinkite prie kurio serverio prisijungęs jūsų branduolys.</string>
 
   <string name="label_connection_host">Host</string>
-  <string name="labelConnectionPort">Port</string>
+  <string name="label_connection_port">Port</string>
 
   <string name="hint_invalid_host">Netinkamas hostname</string>
   <string name="hint_invalid_port">Netinkamas port</string>
diff --git a/app/src/main/res/values-pt/strings_defaults.xml b/app/src/main/res/values-pt/strings_defaults.xml
new file mode 100644
index 000000000..9fdc70ba1
--- /dev/null
+++ b/app/src/main/res/values-pt/strings_defaults.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+  
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+  
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+  
+  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/>.
+  -->
+
+<resources>
+  <string name="default_bufferviewconfig_name">Todas as Conversas</string>
+
+  <string name="default_identity_identity_name">Identidade padrão</string>
+  <string name="default_identity_name">&lt;vazio&gt;</string>
+  <string name="default_identity_realname">Utilizador Quassel IRC</string>
+  <string name="default_identity_awayreason">Fui pescar</string>
+  <string name="default_identity_autoawayreason">Não aqui. Não, realmente. não aqui!</string>
+  <string name="default_identity_detachawayreason">Todos os clientes quassel desapareceram da face da terra…</string>
+  <string name="default_identity_kickreason">Jardim de Infância está em outro lugar!</string>
+  <string name="default_identity_partreason">https://quassel-irc.org - Converse confortavelmente. Em qualquer lugar.</string>
+  <string name="default_identity_quitreason">https://quassel-irc.org - Converse confortavelmente. Em qualquer lugar.</string>
+</resources>
diff --git a/app/src/main/res/values-pt/strings_settings.xml b/app/src/main/res/values-pt/strings_settings.xml
index fa9283247..6613c10a1 100644
--- a/app/src/main/res/values-pt/strings_settings.xml
+++ b/app/src/main/res/values-pt/strings_settings.xml
@@ -64,6 +64,7 @@
   <string name="settings_identity_identity_name">Nome da identidade</string>
   <string name="settings_identity_real_name">Nome Real</string>
   <string name="settings_identity_ident">Identidade</string>
+  <string name="settings_identity_nick">Alcunha</string>
   <string name="settings_identity_nicks">Alcunhas</string>
   <string name="settings_identity_messages">Mensagens</string>
   <string name="settings_identity_kick_reason">Razão de Kick</string>
diff --git a/app/src/main/res/values-pt/strings_setup.xml b/app/src/main/res/values-pt/strings_setup.xml
index bb3afd39c..a177b2ecd 100644
--- a/app/src/main/res/values-pt/strings_setup.xml
+++ b/app/src/main/res/values-pt/strings_setup.xml
@@ -25,10 +25,10 @@
 
   <!-- Account Connection -->
   <string name="slide_account_connection_title">Ligação</string>
-  <string name="slideAccountConnectionDescription">Primeiro, por favor, escolha em qual servidor seu núcleo está hospedado.</string>
+  <string name="slide_account_connection_description">Primeiro, por favor, escolha em qual servidor seu núcleo está hospedado.</string>
 
   <string name="label_connection_host">Anfiriao</string>
-  <string name="labelConnectionPort">Porta</string>
+  <string name="label_connection_port">Porta</string>
 
   <string name="hint_invalid_host">Não é um nome de anfitrião válido</string>
   <string name="hint_invalid_port">Não é uma porta válida</string>
diff --git a/app/src/main/res/values/strings_defaults.xml b/app/src/main/res/values/strings_defaults.xml
new file mode 100644
index 000000000..e4e1464d0
--- /dev/null
+++ b/app/src/main/res/values/strings_defaults.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+  
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+  
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+  
+  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/>.
+  -->
+
+<resources>
+  <string name="default_bufferviewconfig_name">All Chats</string>
+
+  <string name="default_identity_identity_name">Default Identity</string>
+  <string name="default_identity_name">&lt;empty&gt;</string>
+  <string name="default_identity_realname">Quassel IRC User</string>
+  <string name="default_identity_nick">quassel%1$d</string>
+  <string name="default_identity_ident">quassel</string>
+  <string name="default_identity_awayreason">Gone fishing.</string>
+  <string name="default_identity_autoawayreason">Not here. No, really. not here!</string>
+  <string name="default_identity_detachawayreason">All Quassel clients vanished from the face of the earth…</string>
+  <string name="default_identity_kickreason">Kindergarten is elsewhere!</string>
+  <string name="default_identity_partreason">https://quassel-irc.org - Chat comfortably. Anywhere.</string>
+  <string name="default_identity_quitreason">https://quassel-irc.org - Chat comfortably. Anywhere.</string>
+</resources>
diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml
index 1dafcf5ca..ffdf83bd6 100644
--- a/app/src/main/res/values/strings_settings.xml
+++ b/app/src/main/res/values/strings_settings.xml
@@ -64,6 +64,7 @@
   <string name="settings_identity_identity_name">Identity name</string>
   <string name="settings_identity_real_name">Real Name</string>
   <string name="settings_identity_ident">Ident</string>
+  <string name="settings_identity_nick">Nickname</string>
   <string name="settings_identity_nicks">Nicknames</string>
   <string name="settings_identity_messages">Messages</string>
   <string name="settings_identity_kick_reason">Kick Reason</string>
diff --git a/app/src/main/res/values/strings_setup.xml b/app/src/main/res/values/strings_setup.xml
index 81cb31541..a89bfac8b 100644
--- a/app/src/main/res/values/strings_setup.xml
+++ b/app/src/main/res/values/strings_setup.xml
@@ -25,10 +25,10 @@
 
   <!-- Account Connection -->
   <string name="slide_account_connection_title">Connection</string>
-  <string name="slideAccountConnectionDescription">First, please choose which server your core is hosted on.</string>
+  <string name="slide_account_connection_description">First, please choose which server your core is hosted on.</string>
 
   <string name="label_connection_host">Host</string>
-  <string name="labelConnectionPort">Port</string>
+  <string name="label_connection_port">Port</string>
 
   <string name="hint_invalid_host">Not a valid hostname</string>
   <string name="hint_invalid_port">Not a valid port</string>
@@ -61,4 +61,22 @@
   <!-- Core Backend Config -->
   <string name="slide_core_backend_setup_title">Configure Storage Backend</string>
   <string name="slide_core_backend_setup_description">Please configure the selected database backend.</string>
+
+  <!-- User Identity -->
+  <string name="slide_user_identity_title">Setup Identity</string>
+  <string name="slide_user_identity_description">Please choose a nickname</string>
+
+  <string name="hint_invalid_nick">Not a valid nick</string>
+
+  <!-- User Network -->
+  <string name="slide_user_network_title">Setup Network</string>
+  <string name="slide_user_network_description">Select a network to connect to.</string>
+
+  <string name="label_network_custom">Custom Network…</string>
+
+  <!-- User Channels -->
+  <string name="slide_user_channels_title">Setup Channels</string>
+  <string name="slide_user_channels_description">Select what channels to join.</string>
+
+  <string name="label_channels">Channels</string>
 </resources>
diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml
index 48d423dfc..b8cf9da06 100644
--- a/app/src/main/res/values/styles_widgets.xml
+++ b/app/src/main/res/values/styles_widgets.xml
@@ -27,7 +27,9 @@
     <item name="android:textColor">?attr/colorTextPrimary</item>
   </style>
 
-  <style name="Widget.Button.Borderless.Colored" parent="Widget.AppCompat.Button.Borderless.Colored" />
+  <style name="Widget.Button.Borderless.Colored" parent="Widget.AppCompat.Button.Borderless.Colored">
+    <item name="android:textColor">?attr/colorAccent</item>
+  </style>
 
   <style name="Widget.Button" parent="Widget.AppCompat.Button">
     <item name="backgroundTint">?attr/colorBackgroundCard</item>
diff --git a/app/src/main/res/values/themes_base.xml b/app/src/main/res/values/themes_base.xml
index b13b69e61..9aed501fe 100644
--- a/app/src/main/res/values/themes_base.xml
+++ b/app/src/main/res/values/themes_base.xml
@@ -60,6 +60,15 @@
   <style name="Theme.SetupTheme" parent="Theme.AppTheme.Light.NoActionBar">
     <item name="actionBarTheme">@style/Widget.AppBarOverlay</item>
     <item name="actionBarPopupTheme">@style/Widget.PopupOverlay.Light</item>
+
+    <item name="colorTextPrimary">#ffffffff</item>
+    <item name="colorTextPrimaryInverse">#de000000</item>
+    <item name="colorTextSecondary">#b3ffffff</item>
+    <item name="colorTextSecondaryInverse">#8a000000</item>
+    <item name="colorDivider">#1FFFFFFF</item>
+    <item name="android:textColorHint">?colorTextSecondary</item>
+
+    <item name="colorBackground">#FAFAFA</item>
   </style>
 
   <style name="Theme.SplashTheme" parent="Theme.AppTheme.Light.NoActionBar">
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
index 9b3381170..115533fcc 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
@@ -29,7 +29,8 @@ import java.nio.ByteBuffer
 object MessageSerializer : Serializer<Message> {
   override fun serialize(buffer: ChainedByteBuffer, data: Message, features: QuasselFeatures) {
     SignedId64Serializer.serialize(buffer, data.messageId, features)
-    if (features.hasFeature(ExtendedFeature.LongMessageTime))
+    if (features.hasFeature(ExtendedFeature.LongMessageTime) ||
+        features.hasFeature(ExtendedFeature.LongTime))
       LongSerializer.serialize(buffer, data.time.toEpochMilli(), features)
     else
       IntSerializer.serialize(buffer, data.time.epochSecond.toInt(), features)
@@ -49,7 +50,8 @@ object MessageSerializer : Serializer<Message> {
   override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): Message {
     return Message(
       messageId = SignedId64Serializer.deserialize(buffer, features),
-      time = if (features.hasFeature(ExtendedFeature.LongMessageTime))
+      time = if (features.hasFeature(ExtendedFeature.LongMessageTime) ||
+                 features.hasFeature(ExtendedFeature.LongTime))
         Instant.ofEpochMilli(LongSerializer.deserialize(buffer, features))
       else
         Instant.ofEpochSecond(IntSerializer.deserialize(buffer, features).toLong()),
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/ExtendedFeature.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/ExtendedFeature.kt
index 7b58f874b..a61f6d262 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/ExtendedFeature.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/ExtendedFeature.kt
@@ -48,6 +48,7 @@ enum class ExtendedFeature {
   /** Transmit features as list of strings */
   ExtendedFeatures,
   /** Serialize message time as 64-bit */
+  LongTime,
   LongMessageTime,
   /** Real Name and Avatar URL in backlog */
   RichMessages,
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
index 88f2483fd..d3b5cc552 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
@@ -57,7 +57,7 @@ class IrcUser(
     "loginTime" to QVariant.of(loginTime(), Type.QDateTime),
     "server" to QVariant.of(server(), Type.QString),
     "ircOperator" to QVariant.of(ircOperator(), Type.QString),
-    "lastAwayMessage" to QVariant.of(lastAwayMessage(), Type.Int),
+    "lastAwayMessage" to QVariant.of(lastAwayMessage(), Type.Long),
     "whoisServiceReply" to QVariant.of(whoisServiceReply(), Type.QString),
     "suserHost" to QVariant.of(suserHost(), Type.QString),
     "encrypted" to QVariant.of(encrypted(), Type.Bool),
@@ -183,7 +183,7 @@ class IrcUser(
     }
   }
 
-  override fun setLastAwayMessage(lastAwayMessage: Int) {
+  override fun setLastAwayMessage(lastAwayMessage: Long) {
     if (lastAwayMessage > _lastAwayMessage) {
       _lastAwayMessage = lastAwayMessage
     }
@@ -331,7 +331,7 @@ class IrcUser(
       field = value
       hasChangedNotification.onNext(Unit)
     }
-  private var _lastAwayMessage: Int = 0
+  private var _lastAwayMessage: Long = 0L
     set(value) {
       field = value
       hasChangedNotification.onNext(Unit)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
index 6b6b10a89..12c91cf25 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
@@ -68,7 +68,7 @@ interface IIrcUser : ISyncableObject {
   fun setIrcOperator(ircOperator: String)
 
   @Slot
-  fun setLastAwayMessage(lastAwayMessage: Int)
+  fun setLastAwayMessage(lastAwayMessage: Long)
 
   @Slot
   fun setLoginTime(loginTime: Instant)
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
index d33bc2dc0..98f9f522d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
@@ -44,6 +44,7 @@ class Session(
   private val notificationManager: NotificationManager?,
   private var userData: Pair<String, String>,
   val disconnectFromCore: () -> Unit,
+  private val initCallback: (Session) -> Unit,
   exceptionHandler: (Throwable) -> Unit
 ) : ProtocolHandler(exceptionHandler), ISession {
   override val objectStorage: ObjectStorage = ObjectStorage(this)
@@ -222,6 +223,7 @@ class Session(
   }
 
   override fun onInitDone() {
+    initCallback(this)
     for (config in bufferViewManager.bufferViewConfigs()) {
       for (info in bufferSyncer.bufferInfos()) {
         config.handleBuffer(info, bufferSyncer)
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
index fe5e45922..2d01dfd78 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
@@ -41,6 +41,7 @@ class SessionManager(
   val notificationManager: NotificationManager?,
   val handlerService: HandlerService,
   private val disconnectFromCore: () -> Unit,
+  private val initCallback: (Session) -> Unit,
   private val exceptionHandler: (Throwable) -> Unit
 ) {
   fun close() = session.or(lastSession).close()
@@ -138,6 +139,7 @@ class SessionManager(
         notificationManager,
         userData,
         disconnectFromCore,
+        initCallback,
         exceptionHandler
       )
     )
diff --git a/lint.xml b/lint.xml
index 8b2fdc8a9..38019fbca 100644
--- a/lint.xml
+++ b/lint.xml
@@ -33,4 +33,7 @@
 
   <!-- Because this doesn’t work when using splash themes -->
   <issue id="Overdraw" severity="ignore" />
+
+  <!-- Can’t request a translation without a release, can’t release without translation -->
+  <issue id="MissingTranslation" severity="warning" />
 </lint>
diff --git a/persistence/build.gradle.kts b/persistence/build.gradle.kts
index fe7de2be2..d4ea0b3af 100644
--- a/persistence/build.gradle.kts
+++ b/persistence/build.gradle.kts
@@ -79,17 +79,15 @@ dependencies {
   }
 
   // App Arch Persistence
-  withVersion("1.1.0-rc1") {
+  withVersion("1.1.0") {
     implementation("android.arch.persistence.room", "runtime", version)
-    implementation("android.arch.persistence.room", "rxjava2", version)
     kapt("android.arch.persistence.room", "compiler", version)
+    implementation("android.arch.persistence.room", "rxjava2", version)
     testImplementation("android.arch.persistence.room", "testing", version)
   }
 
   // App Arch Paging
-  implementation("android.arch.paging", "runtime", "1.0.0-rc1") {
-    exclude(group = "junit", module = "junit")
-  }
+  implementation("android.arch.paging", "runtime", "1.0.0")
 
   // Utility
   implementation("org.threeten", "threetenbp", "1.3.6", classifier = "no-tzdb")
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
index 7d4af7d05..3b802adc7 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
@@ -252,7 +252,7 @@ class QuasselViewModel : ViewModel() {
 
   val showHidden = BehaviorSubject.createDefault(false)
   val collapsedNetworks = BehaviorSubject.createDefault(emptySet<NetworkId>())
-  val selectedBufferId = BehaviorSubject.createDefault(-1)
+  val selectedBufferId = BehaviorSubject.createDefault(Int.MAX_VALUE)
   val selectedBuffer = combineLatest(session, selectedBufferId, bufferViewConfig)
     .switchMap { (sessionOptional, buffer, bufferViewConfigOptional) ->
       val session = sessionOptional.orNull()
-- 
GitLab