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"><empty></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"><vazio></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"><empty></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