From 1fcfd90e4188b6b49a703514be07c3600bdfbb12 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Mon, 21 Jan 2019 21:11:06 +0100 Subject: [PATCH] Added functionality to require SSL for certain connections --- app/build.gradle.kts | 4 +- .../debug/res/values/strings_constants.xml | 20 +- .../de/kuschku/quasseldroid/Quasseldroid.kt | 1 + .../quasseldroid/dagger/ActivityModule.kt | 4 +- .../quasseldroid/service/AsyncBackend.kt | 9 +- .../quasseldroid/service/QuasselService.kt | 10 +- .../quasseldroid/ui/chat/ChatActivity.kt | 235 +++++++++--------- .../ui/chat/topic/TopicFragment.kt | 5 +- .../whitelist/WhitelistFragment.kt | 8 +- .../ui/coresettings/SettingsFragment.kt | 87 ------- .../aliasitem/AliasItemFragment.kt | 8 +- .../aliaslist/AliasListFragment.kt | 7 +- .../chatlist/ChatListBaseFragment.kt | 6 +- .../chatlist/ChatListEditFragment.kt | 4 +- .../highlightlist/HighlightListFragment.kt | 7 +- .../highlightrule/HighlightRuleFragment.kt | 8 +- .../identity/IdentityBaseFragment.kt | 6 +- .../identity/IdentityEditFragment.kt | 4 +- .../ignoreitem/IgnoreItemFragment.kt | 8 +- .../ignorelist/IgnoreListFragment.kt | 8 +- .../network/NetworkBaseFragment.kt | 6 +- .../network/NetworkEditFragment.kt | 4 +- .../networkconfig/NetworkConfigFragment.kt | 8 +- .../networkserver/NetworkServerFragment.kt | 8 +- .../accounts/edit/AccountEditActivity.kt | 181 +------------- .../accounts/edit/AccountEditFragment.kt | 200 +++++++++++++++ ...dule.kt => AccountEditFragmentProvider.kt} | 6 +- .../accounts/setup/AccountSetupActivity.kt | 9 +- .../setup/AccountSetupConnectionSlide.kt | 7 + .../util/ShortcutCreationHelper.kt | 6 +- .../settings/ServiceBoundSettingsActivity.kt | 6 +- .../util/ui/settings/SettingsActivity.kt | 6 +- .../util/ui/settings/fragment/Changeable.kt | 24 ++ .../util/ui/settings/fragment/Deletable.kt | 24 ++ .../util/ui/settings/fragment/Savable.kt | 24 ++ .../fragment/ServiceBoundSettingsFragment.kt | 48 ++++ .../ui/settings/fragment/SettingsFragment.kt | 48 ++++ .../fragment/SettingsFragmentHelper.kt | 68 +++++ .../res/layout/setup_account_connection.xml | 7 + .../main/res/layout/setup_account_edit.xml | 7 + .../main/res/layout/widget_core_backend.xml | 6 +- app/src/main/res/values/strings_constants.xml | 20 +- app/src/main/res/values/strings_error.xml | 2 + app/src/main/res/values/strings_setup.xml | 1 + .../libquassel/connection/CoreConnection.kt | 4 + .../connection/QuasselSecurityException.kt | 4 +- .../de/kuschku/libquassel/session/Backend.kt | 7 +- .../de/kuschku/libquassel/session/Session.kt | 2 + .../libquassel/session/SessionManager.kt | 2 + .../util/helpers/CollectionHelper.kt | 19 ++ .../libquassel/quassel/BufferTypeTest.kt | 19 ++ malheur/build.gradle.kts | 2 +- persistence/build.gradle.kts | 2 +- .../persistence/AccountDatabase.kt | 8 +- viewmodel/build.gradle.kts | 2 +- 55 files changed, 789 insertions(+), 457 deletions(-) delete mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragment.kt rename app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/{AccountEditModule.kt => AccountEditFragmentProvider.kt} (83%) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Changeable.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Deletable.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Savable.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/ServiceBoundSettingsFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragmentHelper.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3a5ba7e54..3c5f51e32 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -104,13 +104,13 @@ android { } compileOptions { - setSourceCompatibility(JavaVersion.VERSION_1_8) + sourceCompatibility = JavaVersion.VERSION_1_8 setTargetCompatibility(JavaVersion.VERSION_1_8) } lintOptions { isWarningsAsErrors = true - lintConfig = file("../lint.xml") + setLintConfig(file("../lint.xml")) } } diff --git a/app/src/debug/res/values/strings_constants.xml b/app/src/debug/res/values/strings_constants.xml index a53dfac10..fb13b0aa1 100644 --- a/app/src/debug/res/values/strings_constants.xml +++ b/app/src/debug/res/values/strings_constants.xml @@ -1,4 +1,22 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 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="package_name" translatable="false">com.iskrembilen.quasseldroid.debug</string> </resources> diff --git a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt index ddcfb2d31..a054d1368 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt @@ -83,6 +83,7 @@ class Quasseldroid : DaggerApplication() { host = it.host, port = it.port, user = it.user, + requireSsl = false, pass = it.pass, name = it.name, lastUsed = 0, 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 1f03c03c0..8613fdf86 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -77,7 +77,7 @@ import de.kuschku.quasseldroid.ui.coresettings.networkconfig.NetworkConfigFragme import de.kuschku.quasseldroid.ui.coresettings.networkserver.NetworkServerActivity import de.kuschku.quasseldroid.ui.coresettings.networkserver.NetworkServerFragmentProvider import de.kuschku.quasseldroid.ui.setup.accounts.edit.AccountEditActivity -import de.kuschku.quasseldroid.ui.setup.accounts.edit.AccountEditModule +import de.kuschku.quasseldroid.ui.setup.accounts.edit.AccountEditFragmentProvider import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActivity import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionFragmentProvider import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupActivity @@ -204,7 +204,7 @@ abstract class ActivityModule { abstract fun bindAccountSelectionActivity(): AccountSelectionActivity @ActivityScope - @ContributesAndroidInjector(modules = [AccountEditModule::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) + @ContributesAndroidInjector(modules = [AccountEditFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) abstract fun bindAccountEditActivity(): AccountEditActivity @ActivityScope diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt index 56bd24f3b..1068e7be8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt @@ -40,15 +40,16 @@ class AsyncBackend( } override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, - reconnect: Boolean) { + requireSsl: Boolean, reconnect: Boolean) { handler.backend { - backend.connectUnlessConnected(address, user, pass, reconnect) + backend.connectUnlessConnected(address, user, pass, requireSsl, reconnect) } } - override fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { + override fun connect(address: SocketAddress, user: String, pass: String, requireSsl: Boolean, + reconnect: Boolean) { handler.backend { - backend.connect(address, user, pass, reconnect) + backend.connect(address, user, pass, requireSsl, reconnect) } } 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 70419e7a9..8a990c698 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt @@ -117,6 +117,7 @@ class QuasselService : DaggerLifecycleService(), SocketAddress(account.host, account.port), account.user, account.pass, + account.requireSsl, true ) } @@ -273,19 +274,20 @@ class QuasselService : DaggerLifecycleService(), override fun sessionManager() = service?.sessionManager override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, - reconnect: Boolean) { + requireSsl: Boolean, reconnect: Boolean) { service?.apply { sessionManager.ifDisconnected { - connect(address, user, pass, reconnect) + connect(address, user, pass, requireSsl, reconnect) } } } - override fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { + override fun connect(address: SocketAddress, user: String, pass: String, requireSsl: Boolean, + reconnect: Boolean) { service?.apply { disconnect() sessionManager.connect( - clientData, trustManager, hostnameVerifier, address, user to pass, reconnect + clientData, trustManager, hostnameVerifier, address, user to pass, requireSsl, reconnect ) } } 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 0caa7d3f7..660e893e0 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 @@ -417,12 +417,11 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } is Error.SslError -> { it.exception.let { - val leafCertificate = it.certificateChain?.firstOrNull() - if (leafCertificate == null) { - // No certificate exists in the chain + if (it == QuasselSecurityException.NoSsl) { + // Ssl is required but not available MaterialDialog.Builder(this) - .title(R.string.label_error_certificate) - .content(R.string.label_error_certificate_no_certificate) + .title(R.string.label_error_ssl) + .content(R.string.label_error_ssl_required_unavailable) .neutralText(R.string.label_close) .titleColorAttr(R.attr.colorTextPrimary) .backgroundColorAttr(R.attr.colorBackgroundCard) @@ -430,132 +429,146 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .build() .show() } else { - when { - // Certificate has expired - it is QuasselSecurityException.Certificate && - (it.cause is CertificateNotYetValidException || - it.cause is CertificateExpiredException) -> { - MaterialDialog.Builder(this) - .title(R.string.label_error_certificate) - .content( - Html.fromHtml( - getString( - R.string.label_error_certificate_invalid, - leafCertificate.fingerprint, - dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notBefore.time) - .atZone(ZoneId.systemDefault())), - dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notAfter.time) - .atZone(ZoneId.systemDefault())) + val leafCertificate = it.certificateChain?.firstOrNull() + if (leafCertificate == null) { + // No certificate exists in the chain + MaterialDialog.Builder(this) + .title(R.string.label_error_certificate) + .content(R.string.label_error_certificate_no_certificate) + .neutralText(R.string.label_close) + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .build() + .show() + } else { + when { + // Certificate has expired + it is QuasselSecurityException.Certificate && + (it.cause is CertificateNotYetValidException || + it.cause is CertificateExpiredException) -> { + MaterialDialog.Builder(this) + .title(R.string.label_error_certificate) + .content( + Html.fromHtml( + getString( + R.string.label_error_certificate_invalid, + leafCertificate.fingerprint, + dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notBefore.time) + .atZone(ZoneId.systemDefault())), + dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notAfter.time) + .atZone(ZoneId.systemDefault())) + ) ) ) - ) - .negativeText(R.string.label_disconnect) - .positiveText(R.string.label_whitelist) - .onNegative { _, _ -> - disconnect() - } - .onPositive { _, _ -> - runInBackground { - database.validityWhitelist().save( - QuasselDatabase.SslValidityWhitelistEntry( - fingerprint = leafCertificate.fingerprint, - ignoreDate = true + .negativeText(R.string.label_disconnect) + .positiveText(R.string.label_whitelist) + .onNegative { _, _ -> + disconnect() + } + .onPositive { _, _ -> + runInBackground { + database.validityWhitelist().save( + QuasselDatabase.SslValidityWhitelistEntry( + fingerprint = leafCertificate.fingerprint, + ignoreDate = true + ) ) - ) - runOnUiThread { - backend.value.orNull()?.reconnect() + runOnUiThread { + backend.value.orNull()?.reconnect() + } } } - } - .titleColorAttr(R.attr.colorTextPrimary) - .backgroundColorAttr(R.attr.colorBackgroundCard) - .contentColorAttr(R.attr.colorTextPrimary) - .build() - .show() - } - // Certificate is in any other way invalid - it is QuasselSecurityException.Certificate -> { - MaterialDialog.Builder(this) - .title(R.string.label_error_certificate) - .content( - Html.fromHtml( - getString( - R.string.label_error_certificate_untrusted, - leafCertificate.fingerprint - ) - ) - ) - .negativeText(R.string.label_disconnect) - .positiveText(R.string.label_whitelist) - .onNegative { _, _ -> - disconnect() - } - .onPositive { _, _ -> - runInBackground { - database.validityWhitelist().save( - QuasselDatabase.SslValidityWhitelistEntry( - fingerprint = leafCertificate.fingerprint, - ignoreDate = !leafCertificate.isValid + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .build() + .show() + } + // Certificate is in any other way invalid + it is QuasselSecurityException.Certificate -> { + MaterialDialog.Builder(this) + .title(R.string.label_error_certificate) + .content( + Html.fromHtml( + getString( + R.string.label_error_certificate_untrusted, + leafCertificate.fingerprint ) ) - accountDatabase.accounts().findById(accountId)?.let { - database.hostnameWhitelist().save( - QuasselDatabase.SslHostnameWhitelistEntry( + ) + .negativeText(R.string.label_disconnect) + .positiveText(R.string.label_whitelist) + .onNegative { _, _ -> + disconnect() + } + .onPositive { _, _ -> + runInBackground { + database.validityWhitelist().save( + QuasselDatabase.SslValidityWhitelistEntry( fingerprint = leafCertificate.fingerprint, - hostname = it.host + ignoreDate = !leafCertificate.isValid ) ) - } + accountDatabase.accounts().findById(accountId)?.let { + database.hostnameWhitelist().save( + QuasselDatabase.SslHostnameWhitelistEntry( + fingerprint = leafCertificate.fingerprint, + hostname = it.host + ) + ) + } - runOnUiThread { - backend.value.orNull()?.reconnect() + runOnUiThread { + backend.value.orNull()?.reconnect() + } } } - } - .titleColorAttr(R.attr.colorTextPrimary) - .backgroundColorAttr(R.attr.colorBackgroundCard) - .contentColorAttr(R.attr.colorTextPrimary) - .build() - .show() - } - // Certificate not valid for this hostname - it is QuasselSecurityException.Hostname -> { - MaterialDialog.Builder(this) - .title(R.string.label_error_certificate) - .content( - Html.fromHtml( - getString( - R.string.label_error_certificate_no_match, - leafCertificate.fingerprint, - it.address.host + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .build() + .show() + } + // Certificate not valid for this hostname + it is QuasselSecurityException.Hostname -> { + MaterialDialog.Builder(this) + .title(R.string.label_error_certificate) + .content( + Html.fromHtml( + getString( + R.string.label_error_certificate_no_match, + leafCertificate.fingerprint, + it.address.host + ) ) ) - ) - .negativeText(R.string.label_disconnect) - .positiveText(R.string.label_whitelist) - .onNegative { _, _ -> - disconnect() - } - .onPositive { _, _ -> - runInBackground { - database.hostnameWhitelist().save( - QuasselDatabase.SslHostnameWhitelistEntry( - fingerprint = leafCertificate.fingerprint, - hostname = it.address.host + .negativeText(R.string.label_disconnect) + .positiveText(R.string.label_whitelist) + .onNegative { _, _ -> + disconnect() + } + .onPositive { _, _ -> + runInBackground { + database.hostnameWhitelist().save( + QuasselDatabase.SslHostnameWhitelistEntry( + fingerprint = leafCertificate.fingerprint, + hostname = it.address.host + ) ) - ) - runOnUiThread { - backend.value.orNull()?.reconnect() + runOnUiThread { + backend.value.orNull()?.reconnect() + } } } - } - .titleColorAttr(R.attr.colorTextPrimary) - .backgroundColorAttr(R.attr.colorBackgroundCard) - .contentColorAttr(R.attr.colorTextPrimary) - .build() - .show() + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .build() + .show() + } } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt index 6084ccdfa..48a4dd845 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt @@ -35,15 +35,16 @@ import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.ui.chat.input.* -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.helper.invoke import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import de.kuschku.quasseldroid.viewmodel.EditorViewModel import javax.inject.Inject -class TopicFragment : SettingsFragment(), SettingsFragment.Savable { +class TopicFragment : ServiceBoundSettingsFragment(), Savable { @BindView(R.id.chatline) lateinit var chatline: RichEditText diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragment.kt index f4fc55739..82c89cade 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragment.kt @@ -34,12 +34,14 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.persistence.QuasselDatabase -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.helper.visibleIf +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.SettingsFragment import javax.inject.Inject -class WhitelistFragment : SettingsFragment(), SettingsFragment.Changeable, - SettingsFragment.Savable { +class WhitelistFragment : SettingsFragment(), Changeable, + Savable { @BindView(R.id.certificate_whitelist) lateinit var certificateList: RecyclerView diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsFragment.kt deleted file mode 100644 index 127067224..000000000 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsFragment.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Quasseldroid - Quassel client for Android - * - * Copyright (c) 2019 Janne Koschinski - * Copyright (c) 2019 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.coresettings - -import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import com.afollestad.materialdialogs.MaterialDialog -import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.util.service.ServiceBoundFragment - -abstract class SettingsFragment : ServiceBoundFragment() { - private var saveable: Savable? = null - private var deletable: Deletable? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - saveable = this as? Savable - deletable = this as? Deletable - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.context_setting, menu) - menu?.findItem(R.id.action_save)?.isVisible = saveable != null - menu?.findItem(R.id.action_delete)?.isVisible = deletable != null - super.onCreateOptionsMenu(menu, inflater) - } - - override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { - R.id.action_save -> { - saveable?.let { - if (it.onSave()) activity?.finish() - } - true - } - R.id.action_delete -> { - deletable?.let { - MaterialDialog.Builder(activity!!) - .content(R.string.delete_confirmation) - .positiveText(R.string.label_yes) - .negativeText(R.string.label_no) - .negativeColorAttr(R.attr.colorTextPrimary) - .backgroundColorAttr(R.attr.colorBackgroundCard) - .contentColorAttr(R.attr.colorTextPrimary) - .onPositive { _, _ -> - it.onDelete() - requireActivity().finish() - } - .build() - .show() - } - true - } - else -> super.onOptionsItemSelected(item) - } - - interface Changeable { - fun hasChanged(): Boolean - } - - interface Savable { - fun onSave(): Boolean - } - - interface Deletable { - fun onDelete() - } -} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt index fb2f45e10..1dfa2e2f9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt @@ -38,15 +38,15 @@ import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.ui.chat.input.* -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import de.kuschku.quasseldroid.viewmodel.EditorViewModel import javax.inject.Inject -class AliasItemFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { - +class AliasItemFragment : ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.name) lateinit var name: EditText diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt index 828546f43..433e9f017 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt @@ -34,13 +34,14 @@ import de.kuschku.libquassel.quassel.syncables.AliasManager import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.ui.coresettings.aliasitem.AliasItemActivity import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import javax.inject.Inject -class AliasListFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class AliasListFragment : ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.list) lateinit var list: RecyclerView 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 557b87499..9070b0af0 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 @@ -42,12 +42,14 @@ 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 +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment abstract class ChatListBaseFragment(private val initDefault: Boolean) : - SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable { + ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.buffer_view_name) lateinit var bufferViewName: EditText 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 a42d68596..2c7cc4067 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 @@ -20,9 +20,9 @@ package de.kuschku.quasseldroid.ui.coresettings.chatlist import de.kuschku.libquassel.util.helpers.value -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment +import de.kuschku.quasseldroid.util.ui.settings.fragment.Deletable -class ChatListEditFragment : ChatListBaseFragment(false), SettingsFragment.Deletable { +class ChatListEditFragment : ChatListBaseFragment(false), 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/highlightlist/HighlightListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt index cba85bc75..a7e1d7646 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt @@ -40,13 +40,14 @@ import de.kuschku.libquassel.quassel.syncables.HighlightRuleManager import de.kuschku.libquassel.quassel.syncables.interfaces.IHighlightRuleManager import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.ui.coresettings.highlightrule.HighlightRuleActivity import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.ui.WarningBarView +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class HighlightListFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class HighlightListFragment : ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.feature_context_coresidehighlights) lateinit var featureContextCoreSideHighlights: WarningBarView diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragment.kt index 586fefb22..e509d1b1f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragment.kt @@ -31,10 +31,12 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.quassel.syncables.HighlightRuleManager import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class HighlightRuleFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class HighlightRuleFragment : ServiceBoundSettingsFragment(), Savable, + Changeable { @BindView(R.id.enabled) lateinit var enabled: SwitchCompat 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 7ff7e243f..9b2bfb294 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 @@ -39,12 +39,14 @@ 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 +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment abstract class IdentityBaseFragment(private val initDefault: Boolean) : - SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable { + ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.identity_name) lateinit var identityName: EditText 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 8b624eae1..88d913fc8 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 @@ -20,9 +20,9 @@ package de.kuschku.quasseldroid.ui.coresettings.identity import de.kuschku.libquassel.util.helpers.value -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment +import de.kuschku.quasseldroid.util.ui.settings.fragment.Deletable -class IdentityEditFragment : IdentityBaseFragment(false), SettingsFragment.Deletable { +class IdentityEditFragment : IdentityBaseFragment(false), 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/ignoreitem/IgnoreItemFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragment.kt index 7c4287ed6..4c9bfda8f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragment.kt @@ -33,11 +33,13 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.quassel.syncables.IgnoreListManager import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.ui.AnimationHelper +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class IgnoreItemFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class IgnoreItemFragment : ServiceBoundSettingsFragment(), Savable, + Changeable { @BindView(R.id.enabled) lateinit var enabled: SwitchCompat diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt index 02addb034..88632f990 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt @@ -36,12 +36,14 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton import de.kuschku.libquassel.quassel.syncables.IgnoreListManager import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.ui.coresettings.ignoreitem.IgnoreItemActivity import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class IgnoreListFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class IgnoreListFragment : ServiceBoundSettingsFragment(), Savable, + Changeable { @BindView(R.id.list) lateinit var list: RecyclerView 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 402dbf473..8c550485e 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 @@ -44,15 +44,17 @@ import de.kuschku.libquassel.util.Optional import de.kuschku.libquassel.util.helpers.nullIf 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 import de.kuschku.quasseldroid.util.helper.setDependent import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import kotlin.math.roundToInt abstract class NetworkBaseFragment(private val initDefault: Boolean) : - SettingsFragment(), SettingsFragment.Savable, SettingsFragment.Changeable { + ServiceBoundSettingsFragment(), Savable, Changeable { @BindView(R.id.network_name) lateinit var networkName: EditText 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 92b15ea34..6924fe05c 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 @@ -20,9 +20,9 @@ package de.kuschku.quasseldroid.ui.coresettings.network import de.kuschku.libquassel.util.helpers.value -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment +import de.kuschku.quasseldroid.util.ui.settings.fragment.Deletable -class NetworkEditFragment : NetworkBaseFragment(false), SettingsFragment.Deletable { +class NetworkEditFragment : NetworkBaseFragment(false), Deletable { override fun onSave() = network?.let { (it, data) -> applyChanges(data) it?.requestSetNetworkInfo(data.networkInfo()) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt index f1ac6b8ed..7b1d1d5db 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt @@ -31,12 +31,14 @@ import butterknife.ButterKnife import de.kuschku.libquassel.quassel.syncables.NetworkConfig import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.helper.setDependent import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class NetworkConfigFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class NetworkConfigFragment : ServiceBoundSettingsFragment(), Savable, + Changeable { @BindView(R.id.ping_timeout_enabled) lateinit var pingTimeoutEnabled: SwitchCompat diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt index 2ad61605c..af9838270 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt @@ -34,11 +34,13 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_SSL import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.helper.setDependent +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -class NetworkServerFragment : SettingsFragment(), SettingsFragment.Savable, - SettingsFragment.Changeable { +class NetworkServerFragment : ServiceBoundSettingsFragment(), Savable, + Changeable { @BindView(R.id.host) lateinit var host: EditText diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditActivity.kt index 6c688f675..f65e8d9cf 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditActivity.kt @@ -19,188 +19,11 @@ package de.kuschku.quasseldroid.ui.setup.accounts.edit -import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Bundle -import android.text.Editable -import android.view.Menu -import android.view.MenuItem -import android.widget.EditText -import butterknife.BindView -import butterknife.ButterKnife -import com.afollestad.materialdialogs.MaterialDialog -import com.google.android.material.textfield.TextInputLayout -import dagger.android.support.DaggerAppCompatActivity -import de.kuschku.quasseldroid.Keys -import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.persistence.AccountDatabase -import de.kuschku.quasseldroid.util.AndroidHandlerThread -import de.kuschku.quasseldroid.util.Patterns -import de.kuschku.quasseldroid.util.TextValidator -import de.kuschku.quasseldroid.util.helper.editCommit -import javax.inject.Inject - -class AccountEditActivity : DaggerAppCompatActivity() { - @BindView(R.id.nameWrapper) - lateinit var nameWrapper: TextInputLayout - @BindView(R.id.name) - lateinit var name: EditText - - @BindView(R.id.hostWrapper) - lateinit var hostWrapper: TextInputLayout - @BindView(R.id.host) - lateinit var host: EditText - - @BindView(R.id.portWrapper) - lateinit var portWrapper: TextInputLayout - @BindView(R.id.port) - lateinit var port: EditText - - @BindView(R.id.userWrapper) - lateinit var userWrapper: TextInputLayout - @BindView(R.id.user) - lateinit var user: EditText - - @BindView(R.id.passWrapper) - lateinit var passWrapper: TextInputLayout - @BindView(R.id.pass) - lateinit var pass: EditText - - @Inject - lateinit var database: AccountDatabase - - private var accountId: Long = -1 - private var account: AccountDatabase.Account? = null - - private val handler = AndroidHandlerThread("AccountEdit") - - override fun onCreate(savedInstanceState: Bundle?) { - handler.onCreate() - setTheme(R.style.Theme_AppTheme_Light) - super.onCreate(savedInstanceState) - setContentView(R.layout.setup_account_edit) - ButterKnife.bind(this) - - handler.post { - accountId = intent.getLongExtra("account", -1) - if (accountId == -1L) { - setResult(Activity.RESULT_CANCELED) - finish() - } - account = database.accounts().findById(accountId) - if (account == null) { - setResult(Activity.RESULT_CANCELED) - finish() - } - - name.setText(account?.name) - host.setText(account?.host) - port.setText(account?.port?.toString()) - user.setText(account?.user) - pass.setText(account?.pass) - } - - nameValidator = object : TextValidator( - this, nameWrapper::setError, resources.getString(R.string.hint_invalid_name) - ) { - override fun validate(text: Editable) = text.isNotBlank() - } - - hostValidator = object : TextValidator( - this, hostWrapper::setError, resources.getString(R.string.hint_invalid_host) - ) { - override fun validate(text: Editable) = text.toString().matches(Patterns.DOMAIN_NAME) - } - - portValidator = object : TextValidator( - this, portWrapper::setError, resources.getString(R.string.hint_invalid_port) - ) { - override fun validate(text: Editable) = text.toString().toIntOrNull() in (0 until 65536) - } - - userValidator = object : TextValidator( - this, userWrapper::setError, resources.getString(R.string.hint_invalid_user) - ) { - override fun validate(text: Editable) = text.isNotBlank() - } - - name.addTextChangedListener(nameValidator) - host.addTextChangedListener(hostValidator) - port.addTextChangedListener(portValidator) - user.addTextChangedListener(userValidator) - nameValidator.afterTextChanged(name.text) - hostValidator.afterTextChanged(host.text) - portValidator.afterTextChanged(port.text) - userValidator.afterTextChanged(user.text) - } - - private lateinit var nameValidator: TextValidator - private lateinit var hostValidator: TextValidator - private lateinit var portValidator: TextValidator - private lateinit var userValidator: TextValidator - - private val isValid - get() = nameValidator.isValid && hostValidator.isValid && portValidator.isValid - && userValidator.isValid - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.setup_edit_account, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onDestroy() { - handler.onDestroy() - super.onDestroy() - } - - override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { - R.id.action_delete -> { - MaterialDialog.Builder(this) - .content(R.string.delete_confirmation) - .positiveText(R.string.label_yes) - .negativeText(R.string.label_no) - .negativeColorAttr(android.R.attr.textColorPrimary) - .onPositive { _, _ -> - val it = account - if (it != null) - handler.post { - val preferences = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - if (preferences.getLong(Keys.Status.selectedAccount, -1) == it.id) { - preferences.editCommit { - remove(Keys.Status.selectedAccount) - } - } - database.accounts().delete(it) - } - setResult(Activity.RESULT_OK) - finish() - } - .build() - .show() - true - } - R.id.action_save -> { - if (isValid) { - val it = account - if (it != null) { - it.name = name.text.toString() - it.host = host.text.toString() - it.port = port.text.toString().toIntOrNull() ?: 4242 - it.user = user.text.toString() - it.pass = pass.text.toString() - handler.post { - database.accounts().save(it) - setResult(Activity.RESULT_OK) - finish() - } - } - } - true - } - else -> super.onOptionsItemSelected(item) - } +import de.kuschku.quasseldroid.util.ui.settings.SettingsActivity +class AccountEditActivity : SettingsActivity(AccountEditFragment()) { companion object { fun launch( context: Context, diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragment.kt new file mode 100644 index 000000000..3f6907436 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragment.kt @@ -0,0 +1,200 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.accounts.edit + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.text.Editable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import androidx.appcompat.widget.SwitchCompat +import butterknife.BindView +import butterknife.ButterKnife +import com.google.android.material.textfield.TextInputLayout +import de.kuschku.quasseldroid.Keys +import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.persistence.AccountDatabase +import de.kuschku.quasseldroid.util.Patterns +import de.kuschku.quasseldroid.util.TextValidator +import de.kuschku.quasseldroid.util.helper.editCommit +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Deletable +import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable +import de.kuschku.quasseldroid.util.ui.settings.fragment.SettingsFragment +import javax.inject.Inject + +class AccountEditFragment : SettingsFragment(), Changeable, Savable, Deletable { + @BindView(R.id.nameWrapper) + lateinit var nameWrapper: TextInputLayout + @BindView(R.id.name) + lateinit var name: EditText + + @BindView(R.id.hostWrapper) + lateinit var hostWrapper: TextInputLayout + @BindView(R.id.host) + lateinit var host: EditText + + @BindView(R.id.portWrapper) + lateinit var portWrapper: TextInputLayout + @BindView(R.id.port) + lateinit var port: EditText + + @BindView(R.id.require_ssl) + lateinit var requireSsl: SwitchCompat + + @BindView(R.id.userWrapper) + lateinit var userWrapper: TextInputLayout + @BindView(R.id.user) + lateinit var user: EditText + + @BindView(R.id.passWrapper) + lateinit var passWrapper: TextInputLayout + @BindView(R.id.pass) + lateinit var pass: EditText + + @Inject + lateinit var database: AccountDatabase + + private var account: AccountDatabase.Account? = null + private var accountId: Long = -1L + + private lateinit var handlerThread: HandlerThread + private lateinit var handler: Handler + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + handlerThread = HandlerThread("accountEdit") + handlerThread.start() + handler = Handler(handlerThread.looper) + } + + override fun onDestroy() { + super.onDestroy() + handlerThread.quit() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.setup_account_edit, container, false) + ButterKnife.bind(this, view) + + setHasOptionsMenu(true) + + handler.post { + accountId = arguments?.getLong("account", -1) ?: -1 + if (accountId == -1L) { + activity?.setResult(Activity.RESULT_CANCELED) + activity?.finish() + } + account = database.accounts().findById(accountId) + if (account == null) { + activity?.setResult(Activity.RESULT_CANCELED) + activity?.finish() + } + + name.setText(account?.name) + host.setText(account?.host) + port.setText(account?.port?.toString()) + requireSsl.isChecked = account?.requireSsl == true + user.setText(account?.user) + pass.setText(account?.pass) + } + + nameValidator = object : TextValidator( + activity, nameWrapper::setError, resources.getString(R.string.hint_invalid_name) + ) { + override fun validate(text: Editable) = text.isNotBlank() + } + + hostValidator = object : TextValidator( + activity, hostWrapper::setError, resources.getString(R.string.hint_invalid_host) + ) { + override fun validate(text: Editable) = text.toString().matches(Patterns.DOMAIN_NAME) + } + + portValidator = object : TextValidator( + activity, portWrapper::setError, resources.getString(R.string.hint_invalid_port) + ) { + override fun validate(text: Editable) = text.toString().toIntOrNull() in (0 until 65536) + } + + userValidator = object : TextValidator( + activity, userWrapper::setError, resources.getString(R.string.hint_invalid_user) + ) { + override fun validate(text: Editable) = text.isNotBlank() + } + + name.addTextChangedListener(nameValidator) + host.addTextChangedListener(hostValidator) + port.addTextChangedListener(portValidator) + user.addTextChangedListener(userValidator) + nameValidator.afterTextChanged(name.text) + hostValidator.afterTextChanged(host.text) + portValidator.afterTextChanged(port.text) + userValidator.afterTextChanged(user.text) + return view + } + + private lateinit var nameValidator: TextValidator + private lateinit var hostValidator: TextValidator + private lateinit var portValidator: TextValidator + private lateinit var userValidator: TextValidator + + private val isValid + get() = nameValidator.isValid && hostValidator.isValid && portValidator.isValid + && userValidator.isValid + + fun applyChanges() = account?.copy( + name = name.text.toString(), + host = host.text.toString(), + port = port.text.toString().toIntOrNull() ?: 4242, + requireSsl = requireSsl.isChecked, + user = user.text.toString(), + pass = pass.text.toString() + ) + + override fun onSave() = applyChanges()?.let { + handler.post { + database.accounts().save(it) + } + true + } ?: false + + override fun onDelete() { + handler.post { + account?.let { + val preferences = activity?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) + if (preferences?.getLong(Keys.Status.selectedAccount, -1) == it.id) { + preferences.editCommit { + remove(Keys.Status.selectedAccount) + } + } + database.accounts().delete(it) + } + } + } + + override fun hasChanged() = account != applyChanges() +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditModule.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt similarity index 83% rename from app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditModule.kt rename to app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt index 224129789..b87c9ba42 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt @@ -22,9 +22,13 @@ package de.kuschku.quasseldroid.ui.setup.accounts.edit import androidx.fragment.app.FragmentActivity import dagger.Binds import dagger.Module +import dagger.android.ContributesAndroidInjector @Module -abstract class AccountEditModule { +abstract class AccountEditFragmentProvider { @Binds abstract fun bindFragmentActivity(activity: AccountEditActivity): FragmentActivity + + @ContributesAndroidInjector + abstract fun bindAccountEditFragment(): AccountEditFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupActivity.kt index 73240e0a5..9a89d2fa8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupActivity.kt @@ -38,11 +38,12 @@ class AccountSetupActivity : SetupActivity() { override fun onDone(data: Bundle) { val account = AccountDatabase.Account( id = 0, - host = data.getString("host"), + host = data.getString("host", ""), port = data.getInt("port"), - user = data.getString("user"), - pass = data.getString("pass"), - name = data.getString("name"), + requireSsl = data.getBoolean("require_ssl"), + user = data.getString("user", ""), + pass = data.getString("pass", ""), + name = data.getString("name", ""), lastUsed = Instant.now().epochSecond, acceptedMissingFeatures = false, defaultFiltered = 0 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 138ad6a37..446d7b95a 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 @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText +import androidx.appcompat.widget.SwitchCompat import butterknife.BindView import butterknife.ButterKnife import com.google.android.material.textfield.TextInputLayout @@ -46,6 +47,9 @@ class AccountSetupConnectionSlide : SlideFragment() { @BindView(R.id.port) lateinit var portField: EditText + @BindView(R.id.require_ssl) + lateinit var requireSsl: SwitchCompat + override fun isValid(): Boolean { return hostValidator.isValid && portValidator.isValid } @@ -58,12 +62,15 @@ class AccountSetupConnectionSlide : SlideFragment() { hostField.setText(data.getString("host")) if (data.containsKey("port")) portField.setText(data.getInt("port").toString()) + if (data.containsKey("require_ssl")) + requireSsl.isChecked = data.getBoolean("require_ssl", false) updateValidity() } override fun getData(data: Bundle) { data.putString("host", hostField.text.toString()) data.putInt("port", portField.text.toString().toIntOrNull() ?: -1) + data.putBoolean("require_ssl", requireSsl.isChecked) } override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt index 81f14ce67..810f85bac 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt @@ -4,17 +4,17 @@ * Copyright (c) 2019 Janne Koschinski * Copyright (c) 2019 The Quassel Project * - * context program is free software: you can redistribute it and/or modify it + * 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. * - * context program is distributed in the hope that it will be useful, + * 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 context program. If not, see <http://www.gnu.org/licenses/>. + * with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.kuschku.quasseldroid.util diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/ServiceBoundSettingsActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/ServiceBoundSettingsActivity.kt index 59208565c..af6a225b0 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/ServiceBoundSettingsActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/ServiceBoundSettingsActivity.kt @@ -27,14 +27,14 @@ import butterknife.BindView import butterknife.ButterKnife import com.afollestad.materialdialogs.MaterialDialog import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.service.ServiceBoundActivity +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable abstract class ServiceBoundSettingsActivity(private val fragment: Fragment? = null) : ServiceBoundActivity() { protected open fun fragment(): Fragment? = null - private var changeable: SettingsFragment.Changeable? = null + private var changeable: Changeable? = null @BindView(R.id.toolbar) lateinit var toolbar: Toolbar @@ -56,7 +56,7 @@ abstract class ServiceBoundSettingsActivity(private val fragment: Fragment? = nu transaction.commit() } - this.changeable = fragment as? SettingsFragment.Changeable + this.changeable = fragment as? Changeable } private fun shouldNavigateAway(callback: () -> Unit) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/SettingsActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/SettingsActivity.kt index dad6e087c..a9ec61804 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/SettingsActivity.kt @@ -27,13 +27,13 @@ import butterknife.BindView import butterknife.ButterKnife import com.afollestad.materialdialogs.MaterialDialog import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment import de.kuschku.quasseldroid.util.ui.ThemedActivity +import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable abstract class SettingsActivity(protected val fragment: Fragment? = null) : ThemedActivity() { protected open fun fragment(): Fragment? = null - private var changeable: SettingsFragment.Changeable? = null + private var changeable: Changeable? = null @BindView(R.id.toolbar) lateinit var toolbar: Toolbar @@ -55,7 +55,7 @@ abstract class SettingsActivity(protected val fragment: Fragment? = null) : Them transaction.commit() } - this.changeable = fragment as? SettingsFragment.Changeable + this.changeable = fragment as? Changeable } private fun shouldNavigateAway(callback: () -> Unit) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Changeable.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Changeable.kt new file mode 100644 index 000000000..f3c78a532 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Changeable.kt @@ -0,0 +1,24 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +interface Changeable { + fun hasChanged(): Boolean +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Deletable.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Deletable.kt new file mode 100644 index 000000000..1ca5ca743 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Deletable.kt @@ -0,0 +1,24 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +interface Deletable { + fun onDelete() +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Savable.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Savable.kt new file mode 100644 index 000000000..69990788a --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/Savable.kt @@ -0,0 +1,24 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +interface Savable { + fun onSave(): Boolean +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/ServiceBoundSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/ServiceBoundSettingsFragment.kt new file mode 100644 index 000000000..82093d630 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/ServiceBoundSettingsFragment.kt @@ -0,0 +1,48 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import de.kuschku.quasseldroid.util.service.ServiceBoundFragment + +abstract class ServiceBoundSettingsFragment : ServiceBoundFragment() { + private lateinit var helper: SettingsFragmentHelper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + helper = SettingsFragmentHelper( + this as? Savable, + this as? Deletable + ) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + helper.onCreateOptionsMenu(menu, inflater) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem?) = + helper.onOptionsItemSelected(activity, item) + ?: super.onOptionsItemSelected(item) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragment.kt new file mode 100644 index 000000000..ed27924e4 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragment.kt @@ -0,0 +1,48 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import dagger.android.support.DaggerFragment + +abstract class SettingsFragment : DaggerFragment() { + private lateinit var helper: SettingsFragmentHelper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + helper = SettingsFragmentHelper( + this as? Savable, + this as? Deletable + ) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + helper.onCreateOptionsMenu(menu, inflater) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem?) = + helper.onOptionsItemSelected(activity, item) + ?: super.onOptionsItemSelected(item) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragmentHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragmentHelper.kt new file mode 100644 index 000000000..d54c8515e --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/fragment/SettingsFragmentHelper.kt @@ -0,0 +1,68 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.util.ui.settings.fragment + +import android.app.Activity +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import com.afollestad.materialdialogs.MaterialDialog +import de.kuschku.quasseldroid.R + +class SettingsFragmentHelper( + private val saveable: Savable?, + private val deletable: Deletable? +) { + fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + inflater?.inflate(R.menu.context_setting, menu) + menu?.findItem(R.id.action_save)?.isVisible = saveable != null + menu?.findItem(R.id.action_delete)?.isVisible = deletable != null + } + + fun onOptionsItemSelected(activity: Activity?, item: MenuItem?) = when (item?.itemId) { + R.id.action_save -> { + saveable?.let { + if (it.onSave()) activity?.finish() + } + true + } + R.id.action_delete -> { + activity?.let { + deletable?.let { + MaterialDialog.Builder(activity) + .content(R.string.delete_confirmation) + .positiveText(R.string.label_yes) + .negativeText(R.string.label_no) + .negativeColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .onPositive { _, _ -> + it.onDelete() + activity.finish() + } + .build() + .show() + } + } + true + } + else -> null + } +} diff --git a/app/src/main/res/layout/setup_account_connection.xml b/app/src/main/res/layout/setup_account_connection.xml index 3ab65382f..a559d3928 100644 --- a/app/src/main/res/layout/setup_account_connection.xml +++ b/app/src/main/res/layout/setup_account_connection.xml @@ -58,4 +58,11 @@ android:text="@string/defaultConnectionPort" /> </com.google.android.material.textfield.TextInputLayout> + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/require_ssl" + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/label_connection_ssl" /> + </LinearLayout> diff --git a/app/src/main/res/layout/setup_account_edit.xml b/app/src/main/res/layout/setup_account_edit.xml index a5104672a..96860d854 100644 --- a/app/src/main/res/layout/setup_account_edit.xml +++ b/app/src/main/res/layout/setup_account_edit.xml @@ -125,6 +125,13 @@ android:text="@string/defaultConnectionPort" app:errorEnabled="true" /> </com.google.android.material.textfield.TextInputLayout> + + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/require_ssl" + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/label_connection_ssl" /> </LinearLayout> </LinearLayout> diff --git a/app/src/main/res/layout/widget_core_backend.xml b/app/src/main/res/layout/widget_core_backend.xml index 85324d285..4228a1b6f 100644 --- a/app/src/main/res/layout/widget_core_backend.xml +++ b/app/src/main/res/layout/widget_core_backend.xml @@ -3,16 +3,16 @@ Copyright (c) 2019 Janne Koschinski Copyright (c) 2019 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/>. --> diff --git a/app/src/main/res/values/strings_constants.xml b/app/src/main/res/values/strings_constants.xml index 171da6849..92b980cb5 100644 --- a/app/src/main/res/values/strings_constants.xml +++ b/app/src/main/res/values/strings_constants.xml @@ -1,4 +1,22 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 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="package_name" translatable="false">com.iskrembilen.quasseldroid</string> diff --git a/app/src/main/res/values/strings_error.xml b/app/src/main/res/values/strings_error.xml index 2e8fd0884..66ad90d7b 100644 --- a/app/src/main/res/values/strings_error.xml +++ b/app/src/main/res/values/strings_error.xml @@ -21,6 +21,8 @@ <string name="label_error_login">Login Error</string> <string name="label_error_setup">Setup Error</string> <string name="label_error_init">Connection Error</string> + <string name="label_error_ssl">SSL Error</string> + <string name="label_error_ssl_required_unavailable">SSL is required for this connection but the core does not support it.</string> <string name="label_error_certificate">Certificate Error</string> <string name="label_error_certificate_no_certificate">No certificate available</string> <string name="label_error_certificate_no_hostname">No hostname available</string> diff --git a/app/src/main/res/values/strings_setup.xml b/app/src/main/res/values/strings_setup.xml index 728420f94..e6cbc99dd 100644 --- a/app/src/main/res/values/strings_setup.xml +++ b/app/src/main/res/values/strings_setup.xml @@ -31,6 +31,7 @@ <string name="label_connection_host">Host</string> <string name="label_connection_port">Port</string> + <string name="label_connection_ssl">Require SSL</string> <string name="hint_invalid_host">Not a valid hostname</string> <string name="hint_invalid_port">Not a valid port</string> diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt index fd50ab783..668c0546f 100644 --- a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt +++ b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt @@ -55,6 +55,7 @@ import javax.net.ssl.X509TrustManager class CoreConnection( private val address: SocketAddress, private val clientData: ClientData = ClientData.DEFAULT, + private val requireSsl: Boolean = false, private val features: Features = Features(clientData.clientFeatures, QuasselFeatures.empty()), private val handlerService: HandlerService = JavaHandlerService(), private val trustManager: X509TrustManager = TrustManagers.default(), @@ -139,6 +140,9 @@ class CoreConnection( // Wrap socket in SSL context if ssl is enabled if (protocol.flags.hasFlag(ProtocolFeature.TLS)) { channel = channel?.withSSL(trustManager, hostnameVerifier, address) + } else if (requireSsl) { + securityExceptionCallback?.invoke(QuasselSecurityException.NoSsl) + close() } // Wrap socket in deflater if compression is enabled diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/QuasselSecurityException.kt b/lib/src/main/java/de/kuschku/libquassel/connection/QuasselSecurityException.kt index 6cfc13ae5..d358ba924 100644 --- a/lib/src/main/java/de/kuschku/libquassel/connection/QuasselSecurityException.kt +++ b/lib/src/main/java/de/kuschku/libquassel/connection/QuasselSecurityException.kt @@ -24,7 +24,7 @@ import java.security.cert.X509Certificate sealed class QuasselSecurityException( val certificateChain: Array<out X509Certificate>?, - cause: Throwable + cause: Throwable? ) : GeneralSecurityException(cause) { class Certificate( certificateChain: Array<out X509Certificate>?, @@ -36,4 +36,6 @@ sealed class QuasselSecurityException( val address: SocketAddress, cause: Exception ) : QuasselSecurityException(certificateChain, cause) + + object NoSsl : QuasselSecurityException(emptyArray(), null) } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt index eaea5680e..1ebfc9b92 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt @@ -22,8 +22,11 @@ package de.kuschku.libquassel.session import de.kuschku.libquassel.connection.SocketAddress interface Backend { - fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, reconnect: Boolean) - fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) + fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, + requireSsl: Boolean, reconnect: Boolean) + + fun connect(address: SocketAddress, user: String, pass: String, requireSsl: Boolean, + reconnect: Boolean) fun reconnect() fun disconnect(forever: Boolean = false) fun sessionManager(): SessionManager? 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 f454b3c97..6d9cbaae6 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -42,6 +42,7 @@ import javax.net.ssl.X509TrustManager class Session( address: SocketAddress, private var userData: Pair<String, String>, + requireSsl: Boolean = false, trustManager: X509TrustManager = TrustManagers.default(), hostnameVerifier: HostnameVerifier = BrowserCompatibleHostnameVerifier(), clientData: ClientData = ClientData.DEFAULT, @@ -64,6 +65,7 @@ class Session( private val coreConnection = CoreConnection( address, clientData, + requireSsl, features, handlerService, trustManager, 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 d5f18920c..c300b207f 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -116,6 +116,7 @@ class SessionManager( hostnameVerifier: HostnameVerifier, address: SocketAddress, userData: Pair<String, String>, + requireSsl: Boolean, shouldReconnect: Boolean = false ) { log(DEBUG, "SessionManager", "Connecting") @@ -131,6 +132,7 @@ class SessionManager( Session( address, userData, + requireSsl, trustManager, hostnameVerifier, clientData, diff --git a/lib/src/main/java/de/kuschku/libquassel/util/helpers/CollectionHelper.kt b/lib/src/main/java/de/kuschku/libquassel/util/helpers/CollectionHelper.kt index 47cc6701a..762ee9808 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/helpers/CollectionHelper.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/helpers/CollectionHelper.kt @@ -1,3 +1,22 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.libquassel.util.helpers diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt index e8e4389d4..b770c14f7 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt @@ -1,3 +1,22 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 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.libquassel.quassel import de.kuschku.libquassel.protocol.Buffer_Type diff --git a/malheur/build.gradle.kts b/malheur/build.gradle.kts index e3a0236a6..985a0aa87 100644 --- a/malheur/build.gradle.kts +++ b/malheur/build.gradle.kts @@ -39,7 +39,7 @@ android { lintOptions { isWarningsAsErrors = true - lintConfig = file("../lint.xml") + setLintConfig(file("../lint.xml")) } } diff --git a/persistence/build.gradle.kts b/persistence/build.gradle.kts index b6f8fcd14..e2b8209df 100644 --- a/persistence/build.gradle.kts +++ b/persistence/build.gradle.kts @@ -46,7 +46,7 @@ android { lintOptions { isWarningsAsErrors = true - lintConfig = file("../lint.xml") + setLintConfig(file("../lint.xml")) } } diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/AccountDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/AccountDatabase.kt index 08d2df171..2c1392a3b 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/AccountDatabase.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/AccountDatabase.kt @@ -25,7 +25,7 @@ import androidx.room.* import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -@Database(entities = [(AccountDatabase.Account::class)], version = 3) +@Database(entities = [(AccountDatabase.Account::class)], version = 4) abstract class AccountDatabase : RoomDatabase() { abstract fun accounts(): AccountDao @@ -35,6 +35,7 @@ abstract class AccountDatabase : RoomDatabase() { var id: Long, var host: String, var port: Int, + var requireSsl: Boolean, var user: String, var pass: String, var name: String, @@ -93,6 +94,11 @@ abstract class AccountDatabase : RoomDatabase() { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE account ADD COLUMN defaultFiltered INTEGER NOT NULL DEFAULT 0;") } + }, + object : Migration(3, 4) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE account ADD COLUMN requireSsl INTEGER NOT NULL DEFAULT 0;") + } } ).allowMainThreadQueries().build() } diff --git a/viewmodel/build.gradle.kts b/viewmodel/build.gradle.kts index 8e917ce39..e7d57a266 100644 --- a/viewmodel/build.gradle.kts +++ b/viewmodel/build.gradle.kts @@ -39,7 +39,7 @@ android { lintOptions { isWarningsAsErrors = true - lintConfig = file("../lint.xml") + setLintConfig(file("../lint.xml")) } } -- GitLab