From d599079478e01466136abe6043e8ebc002aafdd3 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Mon, 16 Apr 2018 04:13:01 +0200 Subject: [PATCH] Implement Network create/read/update/delete --- app/src/main/AndroidManifest.xml | 6 +- .../quasseldroid/dagger/ActivityModule.kt | 5 + .../ui/coresettings/CoreSettingsFragment.kt | 3 +- .../identity/IdentityBaseFragment.kt | 26 ++-- .../ignorelist/IgnoreListFragment.kt | 10 +- .../network/NetworkBaseFragment.kt | 88 ++++++++--- .../network/NetworkCreateActivity.kt | 2 +- .../network/NetworkServerAdapter.kt | 23 ++- .../networkconfig/NetworkConfigFragment.kt | 28 ++-- .../networkserver/NetworkServerActivity.kt | 24 +++ .../networkserver/NetworkServerFragment.kt | 133 ++++++++++++++++ .../NetworkServerFragmentProvider.kt | 10 ++ .../networkserver/ProxyTypeAdapter.kt | 67 ++++++++ .../networkserver/ProxyTypeItem.kt | 6 + .../quasseldroid/util/ui/SettingsActivity.kt | 26 +++- app/src/main/res/layout/settings_network.xml | 1 + .../res/layout/settings_networkserver.xml | 147 ++++++++++++++++++ .../main/res/values-de/strings_settings.xml | 20 ++- app/src/main/res/values/strings_settings.xml | 16 ++ .../primitive/serializer/VariantSerializer.kt | 2 +- .../libquassel/quassel/syncables/Network.kt | 18 ++- .../quassel/syncables/RpcHandler.kt | 6 +- .../quassel/syncables/interfaces/INetwork.kt | 75 ++++++++- .../syncables/interfaces/IRpcHandler.kt | 5 +- .../de/kuschku/libquassel/util/Optional.kt | 28 ++-- .../util/helpers/ObservableHelper.kt | 19 ++- .../kuschku/libquassel/ConnectionUnitTest.kt | 25 ++- .../kuschku/libquassel/SerializerUnitTest.kt | 25 +++ 28 files changed, 738 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerActivity.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeAdapter.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeItem.kt create mode 100644 app/src/main/res/layout/settings_networkserver.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5afc1e446..233fbb64f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,7 +51,6 @@ android:name=".ui.chat.topic.TopicActivity" android:exported="false" android:label="@string/label_topic" - android:parentActivityName=".ui.chat.info.channel.ChannelInfoActivity" android:windowSoftInputMode="adjustResize" /> <!-- Core Settings --> @@ -73,6 +72,11 @@ android:label="@string/settings_network_title" android:parentActivityName=".ui.coresettings.CoreSettingsActivity" android:windowSoftInputMode="adjustResize" /> + <activity + android:name=".ui.coresettings.networkserver.NetworkServerActivity" + android:exported="false" + android:label="@string/settings_network_title" + android:windowSoftInputMode="adjustResize" /> <activity android:name=".ui.coresettings.identity.IdentityCreateActivity" android:exported="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 422f9fbd3..ae530d1c2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -40,6 +40,8 @@ import de.kuschku.quasseldroid.ui.coresettings.network.NetworkEditActivity import de.kuschku.quasseldroid.ui.coresettings.network.NetworkEditFragmentProvider import de.kuschku.quasseldroid.ui.coresettings.networkconfig.NetworkConfigActivity import de.kuschku.quasseldroid.ui.coresettings.networkconfig.NetworkConfigFragmentProvider +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.selection.AccountSelectionActivity import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionFragmentProvider @@ -81,6 +83,9 @@ abstract class ActivityModule { @ContributesAndroidInjector(modules = [NetworkEditFragmentProvider::class]) abstract fun bindNetworkEditActivity(): NetworkEditActivity + @ContributesAndroidInjector(modules = [NetworkServerFragmentProvider::class]) + abstract fun bindNetworkServerActivity(): NetworkServerActivity + @ContributesAndroidInjector(modules = [IdentityCreateFragmentProvider::class]) abstract fun bindIdentityCreateActivity(): IdentityCreateActivity 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 9e8ff6544..25fdaaa01 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 @@ -21,6 +21,7 @@ import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistEditActivity import de.kuschku.quasseldroid.ui.coresettings.identity.IdentityCreateActivity import de.kuschku.quasseldroid.ui.coresettings.identity.IdentityEditActivity import de.kuschku.quasseldroid.ui.coresettings.ignorelist.IgnoreListActivity +import de.kuschku.quasseldroid.ui.coresettings.network.NetworkCreateActivity import de.kuschku.quasseldroid.ui.coresettings.network.NetworkEditActivity import de.kuschku.quasseldroid.ui.coresettings.networkconfig.NetworkConfigActivity import de.kuschku.quasseldroid.util.helper.combineLatest @@ -131,7 +132,7 @@ class CoreSettingsFragment : ServiceBoundFragment() { } newNetwork.setOnClickListener { - // + NetworkCreateActivity.launch(requireContext()) } newIdentity.setOnClickListener { 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 4cca29dbf..ec80b42bd 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 @@ -104,18 +104,20 @@ abstract class IdentityBaseFragment : SettingsFragment(), SettingsFragment.Savab .firstElement() .toLiveData().observe(this, Observer { it?.let { - 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 (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() + } } } }) 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 bbdfad785..967ab67c6 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 @@ -57,9 +57,11 @@ class IgnoreListFragment : SettingsFragment(), SettingsFragment.Savable, .map(Optional<IgnoreListManager>::get) .toLiveData().observe(this, Observer { if (it != null) { - this.ignoreListManager = Pair(it, it.copy()) - this.ignoreListManager?.let { (_, data) -> - if (adapter.list.isEmpty()) adapter.list = data.ignoreList() + if (this.ignoreListManager == null) { + this.ignoreListManager = Pair(it, it.copy()) + this.ignoreListManager?.let { (_, data) -> + adapter.list = data.ignoreList() + } } } }) @@ -104,7 +106,7 @@ class IgnoreListFragment : SettingsFragment(), SettingsFragment.Savable, override fun hasChanged() = ignoreListManager?.let { (it, data) -> applyChanges(data) - data != it + data.ignoreList() != it.ignoreList() } ?: false private fun applyChanges(data: IgnoreListManager) { 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 4ac703d09..234002202 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 @@ -1,6 +1,8 @@ package de.kuschku.quasseldroid.ui.coresettings.network +import android.app.Activity import android.arch.lifecycle.Observer +import android.content.Intent import android.os.Bundle import android.support.v4.view.ViewCompat import android.support.v7.widget.LinearLayoutManager @@ -21,6 +23,7 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R 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 @@ -80,6 +83,9 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl @BindView(R.id.autoreconnect_unlimited) lateinit var autoreconnectUnlimited: SwitchCompat + @BindView(R.id.perform) + lateinit var perform: EditText + @BindView(R.id.rejoin_channels) lateinit var rejoinChannels: SwitchCompat @@ -120,7 +126,10 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl helper.attachToRecyclerView(servers) newServer.setOnClickListener { - // TODO: Add server screen + startActivityForResult( + NetworkServerActivity.intent(requireContext()), + REQUEST_CREATE_SERVER + ) } val identityAdapter = IdentityAdapter() @@ -148,33 +157,36 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl .firstElement() .toLiveData().observe(this, Observer { it?.let { - this.network = Pair(it, it.copy()) - this.network?.let { (_, data) -> - networkName.setText(data.networkName()) + 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) + identityAdapter.indexOf(data.identity())?.let(identity::setSelection) - adapter.list = data.serverList() + adapter.list = data.serverList() - saslEnabled.isChecked = data.useSasl() - saslAccount.setText(data.saslAccount()) - saslPassword.setText(data.saslPassword()) + saslEnabled.isChecked = data.useSasl() + saslAccount.setText(data.saslAccount()) + saslPassword.setText(data.saslPassword()) - autoidentifyEnabled.isChecked = data.useAutoIdentify() - autoidentifyService.setText(data.autoIdentifyService()) - autoidentifyPassword.setText(data.autoIdentifyPassword()) + 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() + autoreconnectEnabled.isChecked = data.useAutoReconnect() + autoreconnectInterval.setText(data.autoReconnectInterval().toString()) + autoreconnectRetries.setText(data.autoReconnectRetries().toString()) + autoreconnectUnlimited.isChecked = data.unlimitedReconnectRetries() - rejoinChannels.isChecked = data.rejoinChannels() + 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}") + customratelimitsEnabled.isChecked = data.useCustomMessageRate() + customratelimitsBurstSize.setText(data.messageRateBurstSize().toString()) + customratelimitsUnlimited.isChecked = data.unlimitedMessageRate() + customratelimitsDelay.setText("${data.messageRateDelay() / 1000.0}") + } } } }) @@ -193,14 +205,37 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl return view } - private fun serverClick(index: Int, server: INetwork.Server) { - // TODO: Add server screen + private fun serverClick(server: INetwork.Server) { + startActivityForResult( + NetworkServerActivity.intent(requireContext(), server = server), + REQUEST_UPDATE_SERVER + ) } private fun startDrag(holder: RecyclerView.ViewHolder) { helper.startDrag(holder) } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (resultCode == Activity.RESULT_OK && data != null) { + when (requestCode) { + REQUEST_UPDATE_SERVER -> { + val old = data.getSerializableExtra("old") as? INetwork.Server + val new = data.getSerializableExtra("new") as? INetwork.Server + if (old != null && new != null) { + adapter.replace(old, new) + } + } + REQUEST_CREATE_SERVER -> { + val new = data.getSerializableExtra("new") as? INetwork.Server + if (new != null) { + adapter.add(new) + } + } + } + } + } + override fun hasChanged() = network?.let { (it, data) -> applyChanges(data) @@ -219,6 +254,7 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl data.autoReconnectRetries() != it.autoReconnectRetries() || data.unlimitedReconnectRetries() != it.unlimitedReconnectRetries() || data.rejoinChannels() != it.rejoinChannels() || + data.perform() != it.perform() || data.useCustomMessageRate() != it.useCustomMessageRate() || data.messageRateBurstSize() != it.messageRateBurstSize() || data.unlimitedMessageRate() != it.unlimitedMessageRate() || @@ -247,6 +283,7 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl ?: data.autoReconnectRetries()) data.setUnlimitedReconnectRetries(autoreconnectUnlimited.isChecked) + data.setPerform(perform.text.lines()) data.setRejoinChannels(rejoinChannels.isChecked) data.setUseCustomMessageRate(customratelimitsEnabled.isChecked) @@ -257,4 +294,9 @@ abstract class NetworkBaseFragment : SettingsFragment(), SettingsFragment.Savabl ?.let { (it * 1000).roundToInt() } ?: data.messageRateDelay()) } + + companion object { + private const val REQUEST_UPDATE_SERVER = 1 + private const val REQUEST_CREATE_SERVER = 2 + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateActivity.kt index a8b204e84..25ef7bf72 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateActivity.kt @@ -7,6 +7,6 @@ import de.kuschku.quasseldroid.util.ui.SettingsActivity class NetworkCreateActivity : SettingsActivity(NetworkCreateFragment()) { companion object { fun launch(context: Context) = context.startActivity(intent(context)) - fun intent(context: Context) = Intent(context, NetworkEditActivity::class.java) + fun intent(context: Context) = Intent(context, NetworkCreateActivity::class.java) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkServerAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkServerAdapter.kt index e91a70570..80d697bef 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkServerAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkServerAdapter.kt @@ -18,7 +18,7 @@ import de.kuschku.quasseldroid.util.helper.tint import java.util.* class NetworkServerAdapter( - private val clickListener: (Int, INetwork.Server) -> Unit, + private val clickListener: (INetwork.Server) -> Unit, private val dragListener: (NetworkServerViewHolder) -> Unit ) : RecyclerView.Adapter<NetworkServerAdapter.NetworkServerViewHolder>() { private val data = mutableListOf<INetwork.Server>() @@ -38,9 +38,20 @@ class NetworkServerAdapter( notifyItemInserted(index) } - fun replace(index: Int, item: INetwork.Server) { - data[index] = item - notifyItemChanged(index) + fun indexOf(item: INetwork.Server): Int? { + for ((index, it) in data.withIndex()) { + if (it == item) { + return index + } + } + return null + } + + fun replace(old: INetwork.Server, new: INetwork.Server) { + indexOf(old)?.let { + data[it] = new + notifyItemChanged(it) + } } fun remove(index: Int) { @@ -67,7 +78,7 @@ class NetworkServerAdapter( class NetworkServerViewHolder( itemView: View, - clickListener: (Int, INetwork.Server) -> Unit, + clickListener: (INetwork.Server) -> Unit, dragListener: (NetworkServerViewHolder) -> Unit ) : RecyclerView.ViewHolder(itemView) { @BindView(R.id.host) @@ -92,7 +103,7 @@ class NetworkServerAdapter( ButterKnife.bind(this, itemView) itemView.setOnClickListener { item?.let { - clickListener(adapterPosition, it) + clickListener(it) } } handle.setOnTouchListener { _, event -> 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 e9367b373..e8c697a08 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 @@ -60,19 +60,21 @@ class NetworkConfigFragment : SettingsFragment(), SettingsFragment.Savable, .map(Optional<NetworkConfig>::get) .firstElement() .toLiveData().observe(this, Observer { - if (it != null) { - this.networkConfig = Pair(it, it.copy()) - this.networkConfig?.let { (_, data) -> - pingTimeoutEnabled.isChecked = data.pingTimeoutEnabled() - pingInterval.setText(data.pingInterval().toString()) - maxPingCount.setText(data.maxPingCount().toString()) - - autoWhoEnabled.isChecked = data.autoWhoEnabled() - autoWhoInterval.setText(data.autoWhoInterval().toString()) - autoWhoNickLimit.setText(data.autoWhoNickLimit().toString()) - autoWhoDelay.setText(data.autoWhoDelay().toString()) - - standardCtcp.isChecked = data.standardCtcp() + it?.let { + if (this.networkConfig == null) { + this.networkConfig = Pair(it, it.copy()) + this.networkConfig?.let { (_, data) -> + pingTimeoutEnabled.isChecked = data.pingTimeoutEnabled() + pingInterval.setText(data.pingInterval().toString()) + maxPingCount.setText(data.maxPingCount().toString()) + + autoWhoEnabled.isChecked = data.autoWhoEnabled() + autoWhoInterval.setText(data.autoWhoInterval().toString()) + autoWhoNickLimit.setText(data.autoWhoNickLimit().toString()) + autoWhoDelay.setText(data.autoWhoDelay().toString()) + + standardCtcp.isChecked = data.standardCtcp() + } } } }) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerActivity.kt new file mode 100644 index 000000000..9a9281c6f --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerActivity.kt @@ -0,0 +1,24 @@ +package de.kuschku.quasseldroid.ui.coresettings.networkserver + +import android.content.Context +import android.content.Intent +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork +import de.kuschku.quasseldroid.util.ui.SettingsActivity + +class NetworkServerActivity : SettingsActivity(NetworkServerFragment()) { + companion object { + fun launch( + context: Context, + server: INetwork.Server? = null + ) = context.startActivity(intent(context, server)) + + fun intent( + context: Context, + server: INetwork.Server? = null + ) = Intent(context, NetworkServerActivity::class.java).apply { + if (server != null) { + putExtra("server", server) + } + } + } +} 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 new file mode 100644 index 000000000..9cae167bf --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragment.kt @@ -0,0 +1,133 @@ +package de.kuschku.quasseldroid.ui.coresettings.networkserver + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.support.v7.widget.SwitchCompat +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.Spinner +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork +import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.ui.coresettings.SettingsFragment +import de.kuschku.quasseldroid.util.helper.setDependent + +class NetworkServerFragment : SettingsFragment(), SettingsFragment.Savable, + SettingsFragment.Changeable { + @BindView(R.id.host) + lateinit var host: EditText + + @BindView(R.id.port) + lateinit var port: EditText + + @BindView(R.id.ssl_enabled) + lateinit var sslEnabled: SwitchCompat + + @BindView(R.id.ssl_verify) + lateinit var sslVerify: SwitchCompat + + @BindView(R.id.password) + lateinit var password: EditText + + @BindView(R.id.proxy_enabled) + lateinit var proxyEnabled: SwitchCompat + + @BindView(R.id.proxy_group) + lateinit var proxyGroup: ViewGroup + + @BindView(R.id.proxy_type) + lateinit var proxyType: Spinner + + @BindView(R.id.proxy_host) + lateinit var proxyHost: EditText + + @BindView(R.id.proxy_port) + lateinit var proxyPort: EditText + + @BindView(R.id.proxy_user) + lateinit var proxyUser: EditText + + @BindView(R.id.proxy_pass) + lateinit var proxyPass: EditText + + private var item: INetwork.Server? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.settings_networkserver, container, false) + ButterKnife.bind(this, view) + + (arguments?.getSerializable("server") as? INetwork.Server)?.let { + item = it + } + + val typeAdapter = ProxyTypeAdapter(listOf( + ProxyTypeItem( + value = INetwork.ProxyType.Socks5Proxy, + name = R.string.settings_networkserver_proxy_type_socks5 + ), + ProxyTypeItem( + value = INetwork.ProxyType.HttpProxy, + name = R.string.settings_networkserver_proxy_type_http + ) + )) + proxyType.adapter = typeAdapter + + item?.let { data -> + host.setText(data.host) + port.setText(data.port.toString()) + sslEnabled.isChecked = data.useSsl + sslVerify.isChecked = data.sslVerify + password.setText(data.password) + proxyEnabled.isChecked = data.useProxy + proxyType.setSelection(typeAdapter.indexOf(data.proxyType) ?: 0) + proxyHost.setText(data.proxyHost) + proxyPort.setText(data.proxyPort.toString()) + proxyUser.setText(data.proxyUser) + proxyPass.setText(data.proxyPass) + } + + proxyEnabled.setDependent(proxyGroup) + + return view + } + + override fun onSave() = item.let { data -> + val intent = Intent() + intent.putExtra("old", data) + val new = INetwork.Server( + host = host.text.toString(), + port = port.text.toString().toIntOrNull() ?: data?.port ?: 0, + useSsl = sslEnabled.isChecked, + sslVerify = sslVerify.isChecked, + password = password.text.toString(), + useProxy = proxyEnabled.isChecked, + proxyType = proxyType.selectedItemId.toInt(), + proxyHost = proxyHost.text.toString(), + proxyPort = proxyPort.text.toString().toIntOrNull() ?: data?.proxyPort ?: 0, + proxyUser = proxyUser.text.toString(), + proxyPass = proxyPass.text.toString() + ) + intent.putExtra("new", new) + requireActivity().setResult(Activity.RESULT_OK, intent) + true + } + + override fun hasChanged() = item != INetwork.Server( + host = host.text.toString(), + port = port.text.toString().toIntOrNull() ?: item?.port ?: 0, + useSsl = sslEnabled.isChecked, + sslVerify = sslVerify.isChecked, + password = password.text.toString(), + useProxy = proxyEnabled.isChecked, + proxyType = proxyType.selectedItemId.toInt(), + proxyHost = proxyHost.text.toString(), + proxyPort = proxyPort.text.toString().toIntOrNull() ?: item?.proxyPort ?: 0, + proxyUser = proxyUser.text.toString(), + proxyPass = proxyPass.text.toString() + ) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt new file mode 100644 index 000000000..5c05de822 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt @@ -0,0 +1,10 @@ +package de.kuschku.quasseldroid.ui.coresettings.networkserver + +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module +abstract class NetworkServerFragmentProvider { + @ContributesAndroidInjector + abstract fun bindNetworkServerFragment(): NetworkServerFragment +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeAdapter.kt new file mode 100644 index 000000000..378884151 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeAdapter.kt @@ -0,0 +1,67 @@ +package de.kuschku.quasseldroid.ui.coresettings.networkserver + +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.util.ui.ContextThemeWrapper +import de.kuschku.quasseldroid.util.ui.RecyclerSpinnerAdapter + +class ProxyTypeAdapter(val data: List<ProxyTypeItem>) : + RecyclerSpinnerAdapter<ProxyTypeAdapter.ProxyTypeViewHolder>(), + ThemedSpinnerAdapter { + + override fun isEmpty() = data.isEmpty() + + override fun onBindViewHolder(holder: ProxyTypeViewHolder, position: Int) = + holder.bind(getItem(position)) + + override fun onCreateViewHolder(parent: ViewGroup, dropDown: Boolean) + : ProxyTypeViewHolder { + val inflater = LayoutInflater.from( + if (dropDown) ContextThemeWrapper(parent.context, dropDownViewTheme) + else parent.context + ) + return ProxyTypeViewHolder( + inflater.inflate(R.layout.widget_spinner_item_toolbar, parent, false) + ) + } + + override fun getItem(position: Int) = data[position] + + override fun getItemId(position: Int) = getItem(position).value.value.toLong() + + override fun hasStableIds() = true + + override fun getCount() = data.size + + fun indexOf(value: Int): Int? { + for ((key, item) in data.withIndex()) { + if (item.value.value == value) { + return key + } + } + return null + } + + class ProxyTypeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + @BindView(android.R.id.text1) + lateinit var text: TextView + + init { + ButterKnife.bind(this, itemView) + } + + fun bind(activity: ProxyTypeItem?) { + activity?.let { + text.setText(it.name) + } + } + } +} + diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeItem.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeItem.kt new file mode 100644 index 000000000..b2f7d8ecd --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/ProxyTypeItem.kt @@ -0,0 +1,6 @@ +package de.kuschku.quasseldroid.ui.coresettings.networkserver + +import android.support.annotation.StringRes +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork + +data class ProxyTypeItem(val value: INetwork.ProxyType, @StringRes val name: Int) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/SettingsActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/SettingsActivity.kt index f3ec01f76..86ac9b86f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/SettingsActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/SettingsActivity.kt @@ -2,7 +2,9 @@ package de.kuschku.quasseldroid.util.ui import android.os.Bundle import android.support.v4.app.Fragment +import android.support.v4.app.NavUtils import android.support.v7.widget.Toolbar +import android.view.MenuItem import butterknife.BindView import butterknife.ButterKnife import com.afollestad.materialdialogs.MaterialDialog @@ -39,7 +41,7 @@ abstract class SettingsActivity(private val fragment: Fragment? = null) : Servic this.changeable = fragment as? SettingsFragment.Changeable } - override fun onBackPressed() { + private fun shouldNavigateAway(callback: () -> Unit) { val changeable = this.changeable if (changeable?.hasChanged() == true) { MaterialDialog.Builder(this) @@ -50,10 +52,28 @@ abstract class SettingsActivity(private val fragment: Fragment? = null) : Servic .backgroundColorAttr(R.attr.colorBackgroundCard) .contentColorAttr(R.attr.colorTextPrimary) .onPositive { _, _ -> - super.onBackPressed() + callback() } .build() .show() - } else super.onBackPressed() + } else callback() + } + + override fun onBackPressed() = shouldNavigateAway { + super.onBackPressed() + } + + override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { + android.R.id.home -> { + shouldNavigateAway { + if (supportParentActivityIntent == null) { + super.onBackPressed() + } else { + NavUtils.navigateUpFromSameTask(this) + } + } + true + } + else -> super.onOptionsItemSelected(item) } } diff --git a/app/src/main/res/layout/settings_network.xml b/app/src/main/res/layout/settings_network.xml index 4154566b7..ff2f3b624 100644 --- a/app/src/main/res/layout/settings_network.xml +++ b/app/src/main/res/layout/settings_network.xml @@ -200,6 +200,7 @@ <android.support.design.widget.TextInputEditText android:id="@+id/perform" style="@style/Widget.CoreSettings.EditText" + android:inputType="textMultiLine" tools:text="/mode -x" /> </android.support.design.widget.TextInputLayout> diff --git a/app/src/main/res/layout/settings_networkserver.xml b/app/src/main/res/layout/settings_networkserver.xml new file mode 100644 index 000000000..40fae5168 --- /dev/null +++ b/app/src/main/res/layout/settings_networkserver.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.v4.widget.NestedScrollView 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:scrollbars="vertical"> + + <LinearLayout style="@style/Widget.CoreSettings.Wrapper"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="48dp"> + + <android.support.v7.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_server_network" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/settings_networkserver_connection" /> + </LinearLayout> + + <LinearLayout + style="@style/Widget.CoreSettings.DependentGroup" + android:visibility="visible"> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_host"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/host" + style="@style/Widget.CoreSettings.EditText" + tools:text="irc.freenode.org" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_port"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/port" + style="@style/Widget.CoreSettings.EditText" + android:inputType="number" + tools:text="6667" /> + </android.support.design.widget.TextInputLayout> + + <android.support.v7.widget.SwitchCompat + android:id="@+id/ssl_enabled" + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/settings_networkserver_ssl_enabled" /> + + <android.support.v7.widget.SwitchCompat + android:id="@+id/ssl_verify" + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/settings_networkserver_ssl_verify" /> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_password" + app:passwordToggleEnabled="true"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/password" + style="@style/Widget.CoreSettings.EditText" + android:inputType="textPassword" + tools:text="thisisasecurepassword" /> + </android.support.design.widget.TextInputLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="48dp"> + + <android.support.v7.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_settings" /> + + <android.support.v7.widget.SwitchCompat + android:id="@+id/proxy_enabled" + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/settings_networkserver_proxy_enabled" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/proxy_group" + style="@style/Widget.CoreSettings.DependentGroup" + tools:visibility="visible"> + + <TextView + style="@style/Widget.CoreSettings.EditTextHeader" + android:text="@string/settings_networkserver_proxy_type" /> + + <Spinner + android:id="@+id/proxy_type" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:listitem="@layout/widget_spinner_item_toolbar" /> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_proxy_host"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/proxy_host" + style="@style/Widget.CoreSettings.EditText" + tools:text="localhost" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_proxy_port"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/proxy_port" + style="@style/Widget.CoreSettings.EditText" + android:inputType="number" + tools:text="8080" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_proxy_user"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/proxy_user" + style="@style/Widget.CoreSettings.EditText" + tools:text="justjanne" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + style="@style/Widget.CoreSettings.EditTextLayout" + android:hint="@string/settings_networkserver_proxy_pass" + app:passwordToggleEnabled="true"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/proxy_pass" + style="@style/Widget.CoreSettings.EditText" + android:inputType="textPassword" + tools:text="thisisasecurepassword" /> + </android.support.design.widget.TextInputLayout> + </LinearLayout> + </LinearLayout> +</android.support.v4.widget.NestedScrollView> diff --git a/app/src/main/res/values-de/strings_settings.xml b/app/src/main/res/values-de/strings_settings.xml index a19c1bbcf..1cf6b7cd1 100644 --- a/app/src/main/res/values-de/strings_settings.xml +++ b/app/src/main/res/values-de/strings_settings.xml @@ -14,16 +14,32 @@ <string name="settings_network_perform">Befehle</string> <string name="settings_network_rejoin_channels">Beim Verbinden Kanäle wieder betreten</string> <string name="settings_network_autoreconnect_enabled">Automatisches Wiederverbinden</string> - <string name="settings_network_autoreconnect_interval">Zeit zwischen Verbindungsversuchen</string> + <string name="settings_network_autoreconnect_interval">Intervall</string> <string name="settings_network_autoreconnect_interval_unit">Sekunden</string> <string name="settings_network_autoreconnect_attempts">Maximale Anzahl an Verbindungsversuchen</string> <string name="settings_network_autoreconnect_unlimited">Unbegrenzt</string> <string name="settings_network_customratelimits_enabled">Benutzerdefinierte Nachrichtenrate</string> <string name="settings_network_customratelimits_burstsize">Anzahl Nachrichten pro Übertragung</string> <string name="settings_network_customratelimits_unlimited">Unbegrenzt</string> - <string name="settings_network_customratelimits_delay">Zeit zwischen Übertragungen</string> + <string name="settings_network_customratelimits_delay">Verzögerung</string> <string name="settings_network_customratelimits_interval_unit">Sekunden</string> + <string name="settings_networkserver_title">Netzwerk-Server</string> + <string name="settings_networkserver_connection">Verbindung</string> + <string name="settings_networkserver_host">Adresse</string> + <string name="settings_networkserver_port">Port</string> + <string name="settings_networkserver_ssl_enabled">SSL verwenden</string> + <string name="settings_networkserver_ssl_verify">SSL verifizieren</string> + <string name="settings_networkserver_password">Passwort</string> + <string name="settings_networkserver_proxy_enabled">Proxy verwenden</string> + <string name="settings_networkserver_proxy_type">Typ</string> + <string name="settings_networkserver_proxy_type_http">HTTP</string> + <string name="settings_networkserver_proxy_type_socks5">SOCKS 5</string> + <string name="settings_networkserver_proxy_host">Adresse</string> + <string name="settings_networkserver_proxy_port">Port</string> + <string name="settings_networkserver_proxy_user">Benutzername</string> + <string name="settings_networkserver_proxy_pass">Passwort</string> + <string name="settings_identities_title">Identitäten</string> <string name="settings_identity_title">Identität</string> <string name="settings_identity_names">Namen</string> diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml index 6a69c0736..b4ec66509 100644 --- a/app/src/main/res/values/strings_settings.xml +++ b/app/src/main/res/values/strings_settings.xml @@ -24,6 +24,22 @@ <string name="settings_network_customratelimits_delay">Delay</string> <string name="settings_network_customratelimits_interval_unit">seconds</string> + <string name="settings_networkserver_title">Network Server</string> + <string name="settings_networkserver_connection">Connection</string> + <string name="settings_networkserver_host">Host</string> + <string name="settings_networkserver_port">Port</string> + <string name="settings_networkserver_ssl_enabled">Use SSL</string> + <string name="settings_networkserver_ssl_verify">Verify SSL</string> + <string name="settings_networkserver_password">Password</string> + <string name="settings_networkserver_proxy_enabled">Use Proxy</string> + <string name="settings_networkserver_proxy_type">Type</string> + <string name="settings_networkserver_proxy_type_http">HTTP</string> + <string name="settings_networkserver_proxy_type_socks5">SOCKS 5</string> + <string name="settings_networkserver_proxy_host">Host</string> + <string name="settings_networkserver_proxy_port">Port</string> + <string name="settings_networkserver_proxy_user">Username</string> + <string name="settings_networkserver_proxy_pass">Password</string> + <string name="settings_identities_title">Identities</string> <string name="settings_identity_title">Identity</string> <string name="settings_identity_names">Names</string> diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt index c55df4dac..81bef0d94 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt @@ -10,7 +10,7 @@ object VariantSerializer : Serializer<QVariant_> { IntSerializer.serialize(buffer, data.type.id, features) BoolSerializer.serialize(buffer, false, features) if (data is QVariant.Custom && data.type == Type.UserType) { - StringSerializer.C.serialize(buffer, data.qtype.name, features) + StringSerializer.C.serialize(buffer, data.qtype.typeName, features) } (data.serializer as Serializer<Any?>).serialize(buffer, data.data, features) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt index b44f15101..6e771258a 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt @@ -453,13 +453,15 @@ class Network constructor( } override fun setServerList(serverList: QVariantList) { - val actualServerList = serverList.map { + setActualServerList(serverList.map { it.valueOrThrow<QVariantMap>() - }.map(Server.Companion::fromVariantMap) + }.map(Server.Companion::fromVariantMap)) + } - if (_serverList == actualServerList) + fun setActualServerList(serverList: List<INetwork.Server>) { + if (_serverList == serverList) return - _serverList = actualServerList + _serverList = serverList } override fun setUseRandomServer(randomServer: Boolean) { @@ -670,7 +672,7 @@ class Network constructor( }.toList() override fun initServerList(): QVariantList = _serverList.map { - QVariant.of(it, QType.Network_Server) + QVariant.of(it.toVariantMap(), QType.Network_Server) }.toList() override fun initIrcUsersAndChannels(): QVariantMap { @@ -854,6 +856,12 @@ class Network constructor( live_ircChannels.onNext(_ircChannels) } + fun copy(): Network { + val identity = Network(this.networkId(), SignalProxy.NULL) + identity.fromVariantMap(this.toVariantMap()) + return identity + } + private var _networkId: NetworkId = networkId set(value) { field = value diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt index af3653acd..c520ec738 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt @@ -41,10 +41,10 @@ class RpcHandler( backlogStorage.storeMessages(session, message) } - override fun createIdentity(identity: QVariantMap, additional: QVariantMap) = + override fun createIdentity(identity: Identity, additional: QVariantMap) = RPC( "2createIdentity(Identity,QVariantMap)", - ARG(identity, QType.Identity), + ARG(identity.toVariantMap(), QType.Identity), ARG(additional, Type.QVariantMap) ) @@ -57,7 +57,7 @@ class RpcHandler( override fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String>) = RPC( "2createNetwork(NetworkInfo,QStringList)", - ARG(networkInfo, QType.NetworkInfo), + ARG(networkInfo.toVariantMap(), QType.NetworkInfo), ARG(channels, Type.QStringList) ) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt index 8e0a302c0..889cad377 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt @@ -3,8 +3,11 @@ package de.kuschku.libquassel.quassel.syncables.interfaces import de.kuschku.libquassel.annotations.Slot import de.kuschku.libquassel.annotations.Syncable import de.kuschku.libquassel.protocol.* +import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer import de.kuschku.libquassel.util.flag.Flag import de.kuschku.libquassel.util.flag.Flags +import de.kuschku.libquassel.util.helpers.serializeString +import java.io.Serializable import java.nio.ByteBuffer @Syncable(name = "Network") @@ -220,7 +223,7 @@ interface INetwork : ISyncableObject { val proxyPort: UInt = 8080, val proxyUser: String = "", val proxyPass: String = "" - ) { + ) : Serializable { fun toVariantMap(): QVariantMap = mapOf( "Host" to QVariant.of(host, Type.QString), "Port" to QVariant.of(port, Type.UInt), @@ -229,7 +232,7 @@ interface INetwork : ISyncableObject { "sslVerify" to QVariant.of(sslVerify, Type.Bool), "sslVersion" to QVariant.of(sslVersion, Type.Int), "UseProxy" to QVariant.of(useProxy, Type.Bool), - "ProxyType" to QVariant.of(proxyType, Type.Bool), + "ProxyType" to QVariant.of(proxyType, Type.Int), "ProxyHost" to QVariant.of(proxyHost, Type.QString), "ProxyPort" to QVariant.of(proxyPort, Type.UInt), "ProxyUser" to QVariant.of(proxyUser, Type.QString), @@ -295,7 +298,73 @@ interface INetwork : ISyncableObject { var messageRateBurstSize: Int = 0, var messageRateDelay: Int = 0, var unlimitedMessageRate: Boolean = false - ) + ) { + fun toVariantMap() = mapOf( + "NetworkId" to QVariant.of(networkId, QType.NetworkId), + "NetworkName" to QVariant.of(networkName, Type.QString), + "Identity" to QVariant.of(identity, QType.IdentityId), + "UseCustomEncodings" to QVariant.of(useCustomEncodings, Type.Bool), + "CodecForServer" to QVariant.of( + codecForServer.serializeString(StringSerializer.UTF8), Type.QByteArray + ), + "CodecForEncoding" to QVariant.of( + codecForEncoding.serializeString(StringSerializer.UTF8), Type.QByteArray + ), + "CodecForDecoding" to QVariant.of( + codecForDecoding.serializeString(StringSerializer.UTF8), Type.QByteArray + ), + "ServerList" to QVariant.of(serverList.map { + QVariant.of(it.toVariantMap(), QType.Network_Server) + }, Type.QVariantList), + "UseRandomServer" to QVariant.of(useRandomServer, Type.Bool), + "Perform" to QVariant.of(perform, Type.QStringList), + "UseAutoIdentify" to QVariant.of(useAutoIdentify, Type.Bool), + "AutoIdentifyService" to QVariant.of(autoIdentifyService, Type.QString), + "AutoIdentifyPassword" to QVariant.of(autoIdentifyPassword, Type.QString), + "UseSasl" to QVariant.of(useSasl, Type.Bool), + "SaslAccount" to QVariant.of(saslAccount, Type.QString), + "SaslPassword" to QVariant.of(saslPassword, Type.QString), + "UseAutoReconnect" to QVariant.of(useAutoReconnect, Type.Bool), + "AutoReconnectInterval" to QVariant.of(autoReconnectInterval, Type.UInt), + "AutoReconnectRetries" to QVariant.of(autoReconnectRetries, Type.Int), + "UnlimitedReconnectRetries" to QVariant.of(unlimitedReconnectRetries, Type.Bool), + "RejoinChannels" to QVariant.of(rejoinChannels, Type.Bool), + "UseCustomMessageRate" to QVariant.of(useCustomMessageRate, Type.Bool), + "MessageRateBurstSize" to QVariant.of(messageRateBurstSize, Type.UInt), + "MessageRateDelay" to QVariant.of(messageRateDelay, Type.UInt), + "UnlimitedMessageRate" to QVariant.of(unlimitedMessageRate, Type.Bool) + ) + + fun fromVariantMap(map: QVariantMap) { + networkId = map["NetworkId"].value(networkId) + networkName = map["NetworkName"].value(networkName) + identity = map["Identity"].value(identity) + useCustomEncodings = map["UseCustomEncodings"].value(useCustomEncodings) + codecForServer = map["CodecForServer"].value(codecForServer) + codecForEncoding = map["CodecForEncoding"].value(codecForEncoding) + codecForDecoding = map["CodecForDecoding"].value(codecForDecoding) + serverList = map["ServerList"].value(emptyList<QVariant_>()).map { + INetwork.Server.fromVariantMap(it.value(emptyMap())) + } + useRandomServer = map["UseRandomServer"].value(useRandomServer) + perform = map["Perform"].value(perform) + useAutoIdentify = map["UseAutoIdentify"].value(useAutoIdentify) + autoIdentifyService = map["AutoIdentifyService"].value(autoIdentifyService) + autoIdentifyPassword = map["AutoIdentifyPassword"].value(autoIdentifyPassword) + useSasl = map["UseSasl"].value(useSasl) + saslAccount = map["SaslAccount"].value(saslAccount) + saslPassword = map["SaslPassword"].value(saslPassword) + useAutoReconnect = map["UseAutoReconnect"].value(useAutoReconnect) + autoReconnectInterval = map["AutoReconnectInterval"].value(autoReconnectInterval) + autoReconnectRetries = map["AutoReconnectRetries"].value(autoReconnectRetries) + unlimitedReconnectRetries = map["UnlimitedReconnectRetries"].value(unlimitedReconnectRetries) + rejoinChannels = map["RejoinChannels"].value(rejoinChannels) + useCustomMessageRate = map["UseCustomMessageRate"].value(useCustomMessageRate) + messageRateBurstSize = map["MessageRateBurstSize"].value(messageRateBurstSize) + messageRateDelay = map["MessageRateDelay"].value(messageRateDelay) + unlimitedMessageRate = map["UnlimitedMessageRate"].value(unlimitedMessageRate) + } + } /** * IRCv3 capability names and values diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt index e33a48a00..46116ae6e 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt @@ -7,6 +7,7 @@ import de.kuschku.libquassel.protocol.Message import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.QVariantMap import de.kuschku.libquassel.quassel.BufferInfo +import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.session.Session import java.nio.ByteBuffer @@ -44,9 +45,9 @@ interface IRpcHandler { @Slot("2disconnectFromCore()") fun disconnectFromCore() - fun createIdentity(identity: QVariantMap, additional: QVariantMap) + fun createIdentity(identity: Identity, additional: QVariantMap) fun removeIdentity(identityId: IdentityId) - fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String>) + fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String> = emptyList()) fun removeNetwork(networkId: NetworkId) fun changePassword(peerPtr: Long, user: String, old: String, new: String) fun requestKickClient(id: Int) diff --git a/lib/src/main/java/de/kuschku/libquassel/util/Optional.kt b/lib/src/main/java/de/kuschku/libquassel/util/Optional.kt index 390581dcc..428fd9bdc 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/Optional.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/Optional.kt @@ -2,13 +2,13 @@ package de.kuschku.libquassel.util import java.io.Serializable -interface Optional<T> : Serializable { +interface Optional<T : Any> : Serializable { fun get(): T fun isPresent(): Boolean fun ifPresent(consumer: (T) -> Unit) fun filter(predicate: (T) -> Boolean): Optional<T> - fun <U> map(mapper: (T) -> U): Optional<U> - fun <U> flatMap(mapper: (T) -> Optional<U>): Optional<U> + fun <U : Any> map(mapper: (T) -> U): Optional<U> + fun <U : Any> flatMap(mapper: (T) -> Optional<U>): Optional<U> fun orElse(other: T): T fun orNull(): T? fun <X : Throwable> orElseThrow(supplier: () -> X): T @@ -16,13 +16,13 @@ interface Optional<T> : Serializable { override fun hashCode(): Int override fun toString(): String - private class Present<T>(private val value: T) : Optional<T> { + private class Present<T : Any>(private val value: T) : Optional<T> { override fun get() = value override fun isPresent() = true override fun ifPresent(consumer: (T) -> Unit) = consumer(value) override fun filter(predicate: (T) -> Boolean) = if (predicate(value)) this else empty<T>() - override fun <U> map(mapper: (T) -> U) = ofNullable(mapper(value)) - override fun <U> flatMap(mapper: (T) -> Optional<U>) = mapper(value) + override fun <U : Any> map(mapper: (T) -> U) = ofNullable(mapper(value)) + override fun <U : Any> flatMap(mapper: (T) -> Optional<U>) = mapper(value) override fun orElse(other: T) = value override fun orNull(): T? = value override fun <X : Throwable> orElseThrow(supplier: () -> X) = value @@ -31,13 +31,13 @@ interface Optional<T> : Serializable { override fun toString() = "Optional[$value]" } - private class Absent<T> : Optional<T> { + private class Absent<T : Any> : Optional<T> { override fun get() = throw NoSuchElementException("No value present") override fun isPresent() = false override fun ifPresent(consumer: (T) -> Unit) = Unit override fun filter(predicate: (T) -> Boolean) = this - override fun <U> map(mapper: (T) -> U) = empty<U>() - override fun <U> flatMap(mapper: (T) -> Optional<U>) = empty<U>() + override fun <U : Any> map(mapper: (T) -> U) = empty<U>() + override fun <U : Any> flatMap(mapper: (T) -> Optional<U>) = empty<U>() override fun orElse(other: T) = other override fun orNull(): T? = null override fun <X : Throwable> orElseThrow(supplier: () -> X) = throw supplier() @@ -47,12 +47,12 @@ interface Optional<T> : Serializable { } companion object { - private val absent = Absent<Any?>() + private val absent = Absent<Any>() @Suppress("UNCHECKED_CAST") - fun <T> empty(): Optional<T> = absent as Absent<T> + fun <T : Any> empty(): Optional<T> = absent as Absent<T> - fun <T> of(value: T): Optional<T> = Present(value) - fun <T> ofNullable(value: T?): Optional<T> = value?.let(::Present) ?: empty() + fun <T : Any> of(value: T): Optional<T> = Present(value) + fun <T : Any> ofNullable(value: T?): Optional<T> = value?.let(::Present) ?: empty() } -} \ No newline at end of file +} diff --git a/lib/src/main/java/de/kuschku/libquassel/util/helpers/ObservableHelper.kt b/lib/src/main/java/de/kuschku/libquassel/util/helpers/ObservableHelper.kt index 656e6fa4c..4843ccfa9 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/helpers/ObservableHelper.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/helpers/ObservableHelper.kt @@ -9,21 +9,20 @@ fun <T> Observable<T>.or(default: T): T = try { default } -val <T> Observable<T>.value +val <T : Any> Observable<T>.value get() = this.map { Optional.of(it) }.blockingMostRecent(Optional.empty()).firstOrNull()?.orNull() -fun <T, U> Observable<Optional<T>>.mapMap(mapper: (T) -> U): Observable<Optional<U>> = map { - it.map(mapper) -} +fun <T : Any, U : Any> Observable<Optional<T>>.mapMap(mapper: (T) -> U): Observable<Optional<U>> = + map { it.map(mapper) } -fun <T, U> Observable<Optional<T>>.mapMapNullable( +fun <T : Any, U : Any> Observable<Optional<T>>.mapMapNullable( mapper: (T) -> U?): Observable<Optional<U>> = map { it.flatMap { Optional.ofNullable(mapper(it)) } } -fun <T, U> Observable<Optional<T>>.mapSwitchMap( +fun <T : Any, U : Any> Observable<Optional<T>>.mapSwitchMap( mapper: (T) -> Observable<U>): Observable<Optional<U>> = switchMap { if (it.isPresent()) { it.map(mapper).get().map { Optional.of(it) } @@ -32,7 +31,7 @@ fun <T, U> Observable<Optional<T>>.mapSwitchMap( } } -fun <T, U> Observable<Optional<T>>.mapSwitchMapEmpty( +fun <T : Any, U : Any> Observable<Optional<T>>.mapSwitchMapEmpty( mapper: (T) -> Observable<U>): Observable<U> = switchMap { if (it.isPresent()) { it.map(mapper).get() @@ -41,11 +40,11 @@ fun <T, U> Observable<Optional<T>>.mapSwitchMapEmpty( } } -fun <T, U> Observable<Optional<T>>.flatMapSwitchMap( +fun <T : Any, U : Any> Observable<Optional<T>>.flatMapSwitchMap( mapper: (T) -> Observable<Optional<U>>): Observable<Optional<U>> = switchMap { it.map(mapper).orElse(Observable.just(Optional.empty())) } -fun <T> Observable<Optional<T>>.mapOrElse(orElse: T): Observable<T> = map { +fun <T : Any> Observable<Optional<T>>.mapOrElse(orElse: T): Observable<T> = map { it.orElse(orElse) -} \ No newline at end of file +} diff --git a/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt b/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt index 8d7514cb8..a1292b3b6 100644 --- a/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt @@ -3,7 +3,9 @@ package de.kuschku.libquassel import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.quassel.ProtocolFeature import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.BacklogStorage +import de.kuschku.libquassel.session.ConnectionState import de.kuschku.libquassel.session.Session import de.kuschku.libquassel.session.SocketAddress import de.kuschku.libquassel.util.compatibility.reference.JavaHandlerService @@ -46,12 +48,31 @@ class ConnectionUnitTest { override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray() }, SocketAddress(host, port), JavaHandlerService(), object : BacklogStorage { - override fun storeMessages(vararg messages: Message, initialLoad: Boolean) = Unit - override fun storeMessages(messages: Iterable<Message>, initialLoad: Boolean) = Unit + override fun updateIgnoreRules(session: Session) = Unit + override fun storeMessages(session: Session, vararg messages: Message, + initialLoad: Boolean) = Unit + + override fun storeMessages(session: Session, messages: Iterable<Message>, + initialLoad: Boolean) = Unit + override fun clearMessages(bufferId: BufferId, idRange: IntRange) = Unit override fun clearMessages(bufferId: BufferId) = Unit override fun clearMessages() = Unit }, user to pass, {}, {}) + session.state.subscribe { + if (it == ConnectionState.CONNECTED) { + session.rpcHandler?.createNetwork(INetwork.NetworkInfo( + networkName = "QuakeNet", + identity = session.identities.values.firstOrNull()?.id()!!, + serverList = listOf( + INetwork.Server( + host = "irc.quakenet.org", + port = 6667 + ) + ) + ), emptyList()) + } + } session.join() } } diff --git a/lib/src/test/java/de/kuschku/libquassel/SerializerUnitTest.kt b/lib/src/test/java/de/kuschku/libquassel/SerializerUnitTest.kt index 8f68cdbab..766c4517a 100644 --- a/lib/src/test/java/de/kuschku/libquassel/SerializerUnitTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/SerializerUnitTest.kt @@ -2,6 +2,7 @@ package de.kuschku.libquassel import de.kuschku.libquassel.protocol.primitive.serializer.* import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.util.nio.ChainedByteBuffer import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals @@ -103,6 +104,30 @@ class SerializerUnitTest { assertEquals("Test", roundTrip(StringSerializer.C, "Test")) } + @Test + fun networkInfoSerializer() { + val info = INetwork.NetworkInfo( + networkName = "QuakeNet", + identity = 5, + serverList = listOf( + INetwork.Server( + host = "irc.quakenet.org", + port = 6667 + ) + ) + ) + val info2 = info.copy() + info2.fromVariantMap(roundTrip(VariantMapSerializer, info.toVariantMap())) + assertEquals(info, info2) + } + + @Test + fun captureSerializer() { + val data = byteArrayOf( + + ) + } + companion object { fun <T> roundTrip(serializer: Serializer<T>, value: T, features: QuasselFeatures = QuasselFeatures.all()): T { -- GitLab