From d9b0223e88288badc700b2d07f398805a307aba6 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 16 Feb 2018 23:33:34 +0100 Subject: [PATCH] Improved channel list --- .../quasseldroid_ng/service/QuasselService.kt | 1 + .../ui/chat/BufferListAdapter.kt | 348 ++++++++++++++++-- .../ui/chat/BufferViewConfigFragment.kt | 83 ++++- .../util/helper/ContextHelper.kt | 21 ++ .../util/helper/LiveDataZipHelper.kt | 66 ++++ .../util/helper/ThemeHelper.kt | 16 + app/src/main/res/drawable/ic_chevron_down.xml | 10 + app/src/main/res/drawable/ic_chevron_up.xml | 10 + app/src/main/res/layout/activity_main.xml | 3 +- app/src/main/res/layout/activity_setup.xml | 3 +- .../main/res/layout/fragment_chat_list.xml | 8 +- app/src/main/res/layout/fragment_messages.xml | 4 +- .../res/layout/setup_account_connection.xml | 5 +- .../main/res/layout/setup_account_edit.xml | 4 +- .../main/res/layout/setup_account_name.xml | 5 +- .../main/res/layout/setup_account_user.xml | 5 +- .../main/res/layout/setup_select_account.xml | 5 +- app/src/main/res/layout/setup_slide.xml | 4 +- app/src/main/res/layout/widget_buffer.xml | 52 +++ .../res/layout/widget_chatmessage_action.xml | 5 +- .../res/layout/widget_chatmessage_error.xml | 4 +- .../res/layout/widget_chatmessage_plain.xml | 4 +- .../res/layout/widget_chatmessage_server.xml | 4 +- .../main/res/layout/widget_core_account.xml | 3 +- .../res/layout/widget_core_account_add.xml | 3 +- app/src/main/res/layout/widget_network.xml | 42 +++ .../res/layout/widget_spinner_item_inline.xml | 4 +- .../layout/widget_spinner_item_toolbar.xml | 3 +- .../quassel/syncables/IrcChannel.kt | 7 + .../libquassel/quassel/syncables/IrcUser.kt | 9 + .../libquassel/quassel/syncables/Network.kt | 27 +- .../kuschku/libquassel/session/SignalProxy.kt | 21 ++ .../java/de/kuschku/libquassel/util/Flag.kt | 7 + .../de/kuschku/libquassel/util/LongFlag.kt | 7 + .../de/kuschku/libquassel/util/ShortFlag.kt | 7 + 35 files changed, 748 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataZipHelper.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ThemeHelper.kt create mode 100644 app/src/main/res/drawable/ic_chevron_down.xml create mode 100644 app/src/main/res/drawable/ic_chevron_up.xml create mode 100644 app/src/main/res/layout/widget_buffer.xml create mode 100644 app/src/main/res/layout/widget_network.xml diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt index a3c6e5fca..8df09d4aa 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt @@ -30,6 +30,7 @@ class QuasselService : LifecycleService() { @SuppressLint("TrustAllX509TrustManager") override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) = Unit + override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray() } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt index 499ff06f5..05ea46dc9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt @@ -2,38 +2,75 @@ package de.kuschku.quasseldroid_ng.ui.chat import android.arch.lifecycle.LifecycleOwner import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.Observer +import android.graphics.drawable.Drawable +import android.support.v4.graphics.drawable.DrawableCompat import android.support.v7.util.DiffUtil +import android.support.v7.widget.AppCompatImageButton import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.util.hasFlag +import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.util.helper.getCompatDrawable +import de.kuschku.quasseldroid_ng.util.helper.styledAttributes +import de.kuschku.quasseldroid_ng.util.helper.zip class BufferListAdapter( lifecycleOwner: LifecycleOwner, - liveData: LiveData<List<BufferInfo>?>, + liveData: LiveData<List<BufferProps>?>, runInBackground: (() -> Unit) -> Any, runOnUiThread: (Runnable) -> Any, private val clickListener: ((BufferId) -> Unit)? = null ) : RecyclerView.Adapter<BufferListAdapter.BufferViewHolder>() { - var data = mutableListOf<BufferInfo>() + var data = mutableListOf<BufferListItem>() + + var collapsedNetworks = MutableLiveData<Set<NetworkId>>() + + fun expandListener(networkId: NetworkId) { + if (collapsedNetworks.value.orEmpty().contains(networkId)) + collapsedNetworks.postValue(collapsedNetworks.value.orEmpty() - networkId) + else + collapsedNetworks.postValue(collapsedNetworks.value.orEmpty() + networkId) + } init { - liveData.observe( - lifecycleOwner, Observer { list: List<BufferInfo>? -> + collapsedNetworks.value = emptySet() + + liveData.zip(collapsedNetworks).observe( + lifecycleOwner, Observer { it: Pair<List<BufferProps>?, Set<NetworkId>>? -> runInBackground { - val old = data - val new = list?.sortedBy(BufferInfo::networkId) ?: emptyList() + val list = it?.first ?: emptyList() + val collapsedNetworks = it?.second ?: emptySet() + + val old: List<BufferListItem> = data + val new: List<BufferListItem> = list.sortedBy { props -> + props.network.networkName + }.map { props -> + BufferListItem( + props, + BufferState( + networkExpanded = !collapsedNetworks.contains(props.network.networkId) + ) + ) + }.filter { (props, state) -> + props.info.type.hasFlag(BufferInfo.Type.StatusBuffer) || state.networkExpanded + } + val result = DiffUtil.calculateDiff( object : DiffUtil.Callback() { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) - = old[oldItemPosition].bufferId == new[newItemPosition].bufferId + = old[oldItemPosition].props.info.bufferId == new[newItemPosition].props.info.bufferId override fun getOldListSize() = old.size override fun getNewListSize() = new.size @@ -54,40 +91,287 @@ class BufferListAdapter( ) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = BufferViewHolder( - LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false), - clickListener = clickListener - ) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { + BufferInfo.Type.ChannelBuffer.toInt() -> BufferViewHolder.ChannelBuffer( + LayoutInflater.from(parent.context).inflate( + R.layout.widget_buffer, parent, false + ), + clickListener = clickListener + ) + BufferInfo.Type.QueryBuffer.toInt() -> BufferViewHolder.QueryBuffer( + LayoutInflater.from(parent.context).inflate( + R.layout.widget_buffer, parent, false + ), + clickListener = clickListener + ) + BufferInfo.Type.GroupBuffer.toInt() -> BufferViewHolder.GroupBuffer( + LayoutInflater.from(parent.context).inflate( + R.layout.widget_buffer, parent, false + ), + clickListener = clickListener + ) + BufferInfo.Type.StatusBuffer.toInt() -> BufferViewHolder.StatusBuffer( + LayoutInflater.from(parent.context).inflate( + R.layout.widget_network, parent, false + ), + clickListener = clickListener, + expansionListener = ::expandListener + ) + else -> throw IllegalArgumentException( + "No such viewType: $viewType" + ) + } override fun onBindViewHolder(holder: BufferViewHolder, position: Int) - = holder.bind(data[position]) + = holder.bind(data[position].props, data[position].state) override fun getItemCount() = data.size - class BufferViewHolder( - itemView: View, - private val clickListener: ((BufferId) -> Unit)? = null - ) : RecyclerView.ViewHolder(itemView) { - @BindView(android.R.id.text1) - lateinit var text: TextView - - var bufferId: BufferId? = null - - init { - ButterKnife.bind(this, itemView) - itemView.setOnClickListener { - val buffer = bufferId - if (buffer != null) - clickListener?.invoke(buffer) + override fun getItemViewType(position: Int) = data[position].props.info.type.toInt() + + data class BufferListItem( + val props: BufferProps, + val state: BufferState + ) + + data class BufferProps( + val info: BufferInfo, + val network: INetwork.NetworkInfo, + val bufferStatus: BufferStatus, + val description: String + ) + + data class BufferState( + val networkExpanded: Boolean + ) + + abstract class BufferViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + abstract fun bind(props: BufferProps, state: BufferState) + + fun <T> status(target: T, actual: T) = if (target == actual) { + View.VISIBLE + } else { + View.GONE + } + + class StatusBuffer( + itemView: View, + private val clickListener: ((BufferId) -> Unit)? = null, + private val expansionListener: ((NetworkId) -> Unit)? = null + ) : BufferViewHolder(itemView) { + @BindView(R.id.status) + lateinit var status: AppCompatImageButton + + @BindView(R.id.name) + lateinit var name: TextView + + var bufferId: BufferId? = null + var networkId: NetworkId? = null + + init { + ButterKnife.bind(this, itemView) + itemView.setOnClickListener { + val buffer = bufferId + if (buffer != null) + clickListener?.invoke(buffer) + } + + status.setOnClickListener { + val network = networkId + if (network != null) + expansionListener?.invoke(network) + } + } + + override fun bind(props: BufferProps, state: BufferState) { + name.text = props.network.networkName + bufferId = props.info.bufferId + networkId = props.info.networkId + + if (state.networkExpanded) { + status.setImageDrawable(itemView.context.getCompatDrawable(R.drawable.ic_chevron_up)) + } else { + status.setImageDrawable(itemView.context.getCompatDrawable(R.drawable.ic_chevron_down)) + } + } + } + + class GroupBuffer( + itemView: View, + private val clickListener: ((BufferId) -> Unit)? = null + ) : BufferViewHolder(itemView) { + @BindView(R.id.status) + lateinit var status: ImageView + + @BindView(R.id.name) + lateinit var name: TextView + + @BindView(R.id.description) + lateinit var description: TextView + + var bufferId: BufferId? = null + + private val online: Drawable + private val offline: Drawable + + init { + ButterKnife.bind(this, itemView) + itemView.setOnClickListener { + val buffer = bufferId + if (buffer != null) + clickListener?.invoke(buffer) + } + + online = itemView.context.getCompatDrawable(R.drawable.ic_status) + offline = itemView.context.getCompatDrawable(R.drawable.ic_status_offline) + + itemView.context.theme.styledAttributes(R.attr.colorAccent, R.attr.colorAway) { + DrawableCompat.setTint(online, getColor(0, 0)) + DrawableCompat.setTint(offline, getColor(1, 0)) + } + } + + override fun bind(props: BufferProps, state: BufferState) { + bufferId = props.info.bufferId + + name.text = props.info.bufferName + description.text = props.description + + description.visibility = if (props.description == "") { + View.GONE + } else { + View.VISIBLE + } + + status.setImageDrawable( + when (props.bufferStatus) { + BufferStatus.ONLINE -> online + else -> offline + } + ) + } + } + + class ChannelBuffer( + itemView: View, + private val clickListener: ((BufferId) -> Unit)? = null + ) : BufferViewHolder(itemView) { + @BindView(R.id.status) + lateinit var status: ImageView + + @BindView(R.id.name) + lateinit var name: TextView + + @BindView(R.id.description) + lateinit var description: TextView + + var bufferId: BufferId? = null + + private val online: Drawable + private val offline: Drawable + + init { + ButterKnife.bind(this, itemView) + itemView.setOnClickListener { + val buffer = bufferId + if (buffer != null) + clickListener?.invoke(buffer) + } + + online = itemView.context.getCompatDrawable(R.drawable.ic_status_channel) + offline = itemView.context.getCompatDrawable(R.drawable.ic_status_channel_offline) + + itemView.context.theme.styledAttributes(R.attr.colorAccent, R.attr.colorAway) { + DrawableCompat.setTint(online, getColor(0, 0)) + DrawableCompat.setTint(offline, getColor(1, 0)) + } + } + + override fun bind(props: BufferProps, state: BufferState) { + bufferId = props.info.bufferId + + name.text = props.info.bufferName + description.text = props.description + + description.visibility = if (props.description == "") { + View.GONE + } else { + View.VISIBLE + } + + status.setImageDrawable( + when (props.bufferStatus) { + BufferStatus.ONLINE -> online + else -> offline + } + ) } } - fun bind(info: BufferInfo) { - text.text = when { - info.type.hasFlag(BufferInfo.Type.StatusBuffer) -> "Network ${info.networkId}" - else -> "${info.networkId}/${info.bufferName}" + class QueryBuffer( + itemView: View, + private val clickListener: ((BufferId) -> Unit)? = null + ) : BufferViewHolder(itemView) { + @BindView(R.id.status) + lateinit var status: ImageView + + @BindView(R.id.name) + lateinit var name: TextView + + @BindView(R.id.description) + lateinit var description: TextView + + var bufferId: BufferId? = null + + private val online: Drawable + private val away: Drawable + private val offline: Drawable + + init { + ButterKnife.bind(this, itemView) + itemView.setOnClickListener { + val buffer = bufferId + if (buffer != null) + clickListener?.invoke(buffer) + } + + online = itemView.context.getCompatDrawable(R.drawable.ic_status) + away = itemView.context.getCompatDrawable(R.drawable.ic_status) + offline = itemView.context.getCompatDrawable(R.drawable.ic_status_offline) + + itemView.context.theme.styledAttributes(R.attr.colorAccent, R.attr.colorAway) { + DrawableCompat.setTint(online, getColor(0, 0)) + DrawableCompat.setTint(away, getColor(1, 0)) + DrawableCompat.setTint(offline, getColor(1, 0)) + } + } + + override fun bind(props: BufferProps, state: BufferState) { + bufferId = props.info.bufferId + + name.text = props.info.bufferName + description.text = props.description + + description.visibility = if (props.description == "") { + View.GONE + } else { + View.VISIBLE + } + + status.setImageDrawable( + when (props.bufferStatus) { + BufferStatus.ONLINE -> online + BufferStatus.AWAY -> away + else -> offline + } + ) } - bufferId = info.bufferId } } + + enum class BufferStatus { + ONLINE, + AWAY, + OFFLINE + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigFragment.kt index af325bf39..3deed0c90 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigFragment.kt @@ -12,9 +12,8 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.NetworkId -import de.kuschku.libquassel.quassel.syncables.BufferViewConfig -import de.kuschku.libquassel.quassel.syncables.BufferViewManager -import de.kuschku.libquassel.quassel.syncables.Network +import de.kuschku.libquassel.quassel.BufferInfo +import de.kuschku.libquassel.quassel.syncables.* import de.kuschku.libquassel.session.Backend import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.session.SessionManager @@ -25,6 +24,7 @@ import de.kuschku.quasseldroid_ng.util.helper.or import de.kuschku.quasseldroid_ng.util.helper.switchMap import de.kuschku.quasseldroid_ng.util.helper.switchMapRx import de.kuschku.quasseldroid_ng.util.service.ServiceBoundFragment +import io.reactivex.Observable class BufferViewConfigFragment : ServiceBoundFragment() { private val handlerThread = AndroidHandlerThread("ChatList") @@ -74,11 +74,78 @@ class BufferViewConfigFragment : ServiceBoundFragment() { private val bufferIdList = selectedBufferViewConfig.switchMapRx(BufferViewConfig::live_buffers) - private val bufferList = sessionManager.switchMap { manager -> - bufferIdList.map { ids -> - ids.mapNotNull { - manager.bufferSyncer?.bufferInfo(it) - } + private val bufferList: LiveData<List<BufferListAdapter.BufferProps>?> = sessionManager.switchMap { manager -> + bufferIdList.switchMapRx { ids -> + Observable.combineLatest( + ids.mapNotNull { id -> + manager.bufferSyncer?.bufferInfo( + id + ) + }.mapNotNull { + val network = manager.networks[it.networkId] + if (network == null) { + null + } else { + it to network + } + }.map { (info, network) -> + when (info.type.toInt()) { + BufferInfo.Type.QueryBuffer.toInt() -> { + network.liveIrcUser(info.bufferName).distinctUntilChanged().switchMap { user -> + user.live_away.switchMap { away -> + user.live_realName.map { realName -> + BufferListAdapter.BufferProps( + info = info, + network = network.networkInfo(), + bufferStatus = when { + user == IrcUser.NULL -> BufferListAdapter.BufferStatus.OFFLINE + away -> BufferListAdapter.BufferStatus.AWAY + else -> BufferListAdapter.BufferStatus.ONLINE + }, + description = realName + ) + } + } + } + } + BufferInfo.Type.ChannelBuffer.toInt() -> { + network.liveIrcChannel(info.bufferName).distinctUntilChanged().switchMap { channel -> + channel.live_topic.map { topic -> + BufferListAdapter.BufferProps( + info = info, + network = network.networkInfo(), + bufferStatus = when (channel) { + IrcChannel.NULL -> BufferListAdapter.BufferStatus.OFFLINE + else -> BufferListAdapter.BufferStatus.ONLINE + }, + description = topic + ) + } + } + } + BufferInfo.Type.StatusBuffer.toInt() -> { + network.liveConnectionState.map { + BufferListAdapter.BufferProps( + info = info, + network = network.networkInfo(), + bufferStatus = BufferListAdapter.BufferStatus.OFFLINE, + description = "" + ) + } + } + else -> Observable.just( + BufferListAdapter.BufferProps( + info = info, + network = network.networkInfo(), + bufferStatus = BufferListAdapter.BufferStatus.OFFLINE, + description = "" + ) + ) + } + }, { array: Array<Any> -> + array.toList() as List<BufferListAdapter.BufferProps> + } + ) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ContextHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ContextHelper.kt index 70dab9704..1fe35d03f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ContextHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ContextHelper.kt @@ -1,7 +1,11 @@ package de.kuschku.quasseldroid_ng.util.helper import android.content.Context +import android.graphics.drawable.Drawable import android.os.Build +import android.support.annotation.ColorInt +import android.support.annotation.ColorRes +import android.support.annotation.DrawableRes fun Context.getStatusBarHeight(): Int { var result = 0 @@ -17,3 +21,20 @@ inline fun <reified T> Context.systemService(): T = if (Build.VERSION.SDK_INT >= } else { getSystemService(T::class.java.simpleName) as T } + +fun Context.getCompatDrawable(@DrawableRes id: Int): Drawable { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + this.resources.getDrawable(id, this.theme) + } else { + this.resources.getDrawable(id) + } +} + +@ColorInt +fun Context.getCompatColor(@ColorRes id: Int): Int { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + this.resources.getColor(id, this.theme) + } else { + this.resources.getColor(id) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataZipHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataZipHelper.kt new file mode 100644 index 000000000..c9045e619 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataZipHelper.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Mitchell Skaggs, Keturah Gadson, Ethan Holtgrieve, Nathan Skelton, + * Pattonville School District + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid_ng.util.helper + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MediatorLiveData + +/** + * This function creates a [LiveData] of a [Pair] of the two types provided. The resulting LiveData + * is updated whenever either input LiveData updates and both LiveData have updated at least once + * before. + * + * If the zip of A and B is C, and A and B are updated in this pattern: `AABA`, C would be updated + * twice (once with the second A value and first B value, and once with the third A value and first + * B value). + * + * @param a the first LiveData + * @param b the second LiveData + * @author Mitchell Skaggs + */ +fun <A, B> zipLiveData(a: LiveData<A>, b: LiveData<B>): LiveData<Pair<A, B>> { + return MediatorLiveData<Pair<A, B>>().apply { + var lastA: A? = null + var lastB: B? = null + + fun update() { + val localLastA = lastA + val localLastB = lastB + if (localLastA != null && localLastB != null) + this.value = Pair(localLastA, localLastB) + } + + addSource(a) { + lastA = it + update() + } + addSource(b) { + lastB = it + update() + } + } +} + +/** + * This is merely an extension function for [zipLiveData]. + * + * @see zipLiveData + * @author Mitchell Skaggs + */ +fun <A, B> LiveData<A>.zip(b: LiveData<B>): LiveData<Pair<A, B>> = zipLiveData(this, b) \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ThemeHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ThemeHelper.kt new file mode 100644 index 000000000..452086db1 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/ThemeHelper.kt @@ -0,0 +1,16 @@ +package de.kuschku.quasseldroid_ng.util.helper + +import android.content.res.Resources +import android.content.res.TypedArray + +inline fun Resources.Theme.styledAttributes(vararg attributes: Int, f: TypedArray.() -> Unit) { + this.obtainStyledAttributes(attributes).use { + it.apply(f) + } +} + +inline fun <R> TypedArray.use(block: (TypedArray) -> R): R { + val result = block(this) + recycle() + return result +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chevron_down.xml b/app/src/main/res/drawable/ic_chevron_down.xml new file mode 100644 index 000000000..e86d4f638 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_down.xml @@ -0,0 +1,10 @@ +<!-- drawable/chevron_down.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000" + android:pathData="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chevron_up.xml b/app/src/main/res/drawable/ic_chevron_up.xml new file mode 100644 index 000000000..092abec33 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_up.xml @@ -0,0 +1,10 @@ +<!-- drawable/chevron_up.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000" + android:pathData="M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 1094421ed..98bd94628 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,7 +4,8 @@ android:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml index a0a74dc86..81175f085 100644 --- a/app/src/main/res/layout/activity_setup.xml +++ b/app/src/main/res/layout/activity_setup.xml @@ -2,7 +2,8 @@ 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:layout_height="match_parent" + tools:theme="@style/Theme.SetupTheme"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" diff --git a/app/src/main/res/layout/fragment_chat_list.xml b/app/src/main/res/layout/fragment_chat_list.xml index 228f6f5e9..1dc6351ef 100644 --- a/app/src/main/res/layout/fragment_chat_list.xml +++ b/app/src/main/res/layout/fragment_chat_list.xml @@ -1,8 +1,11 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" @@ -30,5 +33,6 @@ <android.support.v7.widget.RecyclerView android:id="@+id/chatList" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + tools:listitem="@layout/widget_buffer" /> </LinearLayout> diff --git a/app/src/main/res/layout/fragment_messages.xml b/app/src/main/res/layout/fragment_messages.xml index ac1b5a37a..2470d8d7d 100644 --- a/app/src/main/res/layout/fragment_messages.xml +++ b/app/src/main/res/layout/fragment_messages.xml @@ -4,7 +4,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/colorBackground"> + android:background="?attr/colorBackground" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <android.support.v7.widget.RecyclerView android:id="@+id/messages" diff --git a/app/src/main/res/layout/setup_account_connection.xml b/app/src/main/res/layout/setup_account_connection.xml index 499b64e16..162ac4321 100644 --- a/app/src/main/res/layout/setup_account_connection.xml +++ b/app/src/main/res/layout/setup_account_connection.xml @@ -2,10 +2,13 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="32dp"> + android:padding="32dp" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme"> <android.support.design.widget.TextInputLayout android:id="@+id/hostWrapper" diff --git a/app/src/main/res/layout/setup_account_edit.xml b/app/src/main/res/layout/setup_account_edit.xml index 715e30623..bfc7e62d9 100644 --- a/app/src/main/res/layout/setup_account_edit.xml +++ b/app/src/main/res/layout/setup_account_edit.xml @@ -2,7 +2,9 @@ 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:layout_height="match_parent" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme"> <LinearLayout android:layout_width="match_parent" diff --git a/app/src/main/res/layout/setup_account_name.xml b/app/src/main/res/layout/setup_account_name.xml index 591099361..0bf1ecfcd 100644 --- a/app/src/main/res/layout/setup_account_name.xml +++ b/app/src/main/res/layout/setup_account_name.xml @@ -1,9 +1,12 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="32dp"> + android:padding="32dp" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme"> <android.support.design.widget.TextInputLayout android:id="@+id/nameWrapper" diff --git a/app/src/main/res/layout/setup_account_user.xml b/app/src/main/res/layout/setup_account_user.xml index 4468f2c97..e75a654c6 100644 --- a/app/src/main/res/layout/setup_account_user.xml +++ b/app/src/main/res/layout/setup_account_user.xml @@ -1,9 +1,12 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="32dp"> + android:padding="32dp" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme"> <android.support.design.widget.TextInputLayout android:id="@+id/userWrapper" diff --git a/app/src/main/res/layout/setup_select_account.xml b/app/src/main/res/layout/setup_select_account.xml index 7961fd8dd..b2e81ee86 100644 --- a/app/src/main/res/layout/setup_select_account.xml +++ b/app/src/main/res/layout/setup_select_account.xml @@ -1,4 +1,7 @@ <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:id="@+id/account_list" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme" /> diff --git a/app/src/main/res/layout/setup_slide.xml b/app/src/main/res/layout/setup_slide.xml index 54045a751..46f172494 100644 --- a/app/src/main/res/layout/setup_slide.xml +++ b/app/src/main/res/layout/setup_slide.xml @@ -2,7 +2,9 @@ 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:layout_height="match_parent" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.SetupTheme"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" diff --git a/app/src/main/res/layout/widget_buffer.xml b/app/src/main/res/layout/widget_buffer.xml new file mode 100644 index 000000000..4e1e095d1 --- /dev/null +++ b/app/src/main/res/layout/widget_buffer.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="48dp" + android:paddingBottom="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="8dp" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> + + <ImageView + android:id="@+id/status" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center" + android:layout_marginEnd="32dp" + android:layout_marginRight="32dp" + tools:src="@drawable/ic_status_channel" + tools:tint="?attr/colorAccent" /> + + <LinearLayout + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:id="@+id/name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:singleLine="true" + android:textColor="?attr/colorForeground" + android:textSize="13sp" + android:textStyle="bold" + tools:text="#quasseldroid" /> + + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:singleLine="true" + android:textColor="?attr/colorForegroundSecondary" + android:textSize="12sp" + tools:text="QuasselDroid is an Android client for #quassel ♥ justJanne's much improved version: https://dl.kuschku.de/releases/quasseldroid/ ♥ http://github.com/sandsmark/QuasselDroid ♥ Quasseldroid on play https://market.android.com/details?id=com.iskrembilen.quasseldroid ♥ Sign up for beta: https://plus.google.com/communities/104094956084217666662" /> + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_chatmessage_action.xml b/app/src/main/res/layout/widget_chatmessage_action.xml index 754adfd98..13146566a 100644 --- a/app/src/main/res/layout/widget_chatmessage_action.xml +++ b/app/src/main/res/layout/widget_chatmessage_action.xml @@ -20,6 +20,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" @@ -32,7 +33,9 @@ android:paddingRight="@dimen/message_horizontal" android:paddingStart="@dimen/message_horizontal" android:paddingTop="@dimen/message_vertical" - android:textAppearance="?android:attr/textAppearanceListItemSmall"> + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <TextView android:id="@+id/time" diff --git a/app/src/main/res/layout/widget_chatmessage_error.xml b/app/src/main/res/layout/widget_chatmessage_error.xml index a19b7e87f..ac45d3d6e 100644 --- a/app/src/main/res/layout/widget_chatmessage_error.xml +++ b/app/src/main/res/layout/widget_chatmessage_error.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorBackgroundSecondary" @@ -13,7 +14,8 @@ android:paddingRight="@dimen/message_horizontal" android:paddingStart="@dimen/message_horizontal" android:paddingTop="@dimen/message_vertical" - android:textAppearance="?android:attr/textAppearanceListItemSmall"> + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <TextView android:id="@+id/time" diff --git a/app/src/main/res/layout/widget_chatmessage_plain.xml b/app/src/main/res/layout/widget_chatmessage_plain.xml index 705534482..1f52573c3 100644 --- a/app/src/main/res/layout/widget_chatmessage_plain.xml +++ b/app/src/main/res/layout/widget_chatmessage_plain.xml @@ -13,7 +13,9 @@ android:paddingRight="@dimen/message_horizontal" android:paddingStart="@dimen/message_horizontal" android:paddingTop="@dimen/message_vertical" - android:textAppearance="?android:attr/textAppearanceListItemSmall"> + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <TextView android:id="@+id/time" diff --git a/app/src/main/res/layout/widget_chatmessage_server.xml b/app/src/main/res/layout/widget_chatmessage_server.xml index a9ac1112f..48b076cb0 100644 --- a/app/src/main/res/layout/widget_chatmessage_server.xml +++ b/app/src/main/res/layout/widget_chatmessage_server.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorBackgroundSecondary" @@ -13,7 +14,8 @@ android:paddingRight="@dimen/message_horizontal" android:paddingStart="@dimen/message_horizontal" android:paddingTop="@dimen/message_vertical" - android:textAppearance="?android:attr/textAppearanceListItemSmall"> + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <TextView android:id="@+id/time" diff --git a/app/src/main/res/layout/widget_core_account.xml b/app/src/main/res/layout/widget_core_account.xml index 0a8e02ba0..041147810 100644 --- a/app/src/main/res/layout/widget_core_account.xml +++ b/app/src/main/res/layout/widget_core_account.xml @@ -9,7 +9,8 @@ android:focusableInTouchMode="false" android:orientation="horizontal" android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingRight="16dp" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <LinearLayout android:layout_width="48dp" diff --git a/app/src/main/res/layout/widget_core_account_add.xml b/app/src/main/res/layout/widget_core_account_add.xml index f6c839109..464c45b4e 100644 --- a/app/src/main/res/layout/widget_core_account_add.xml +++ b/app/src/main/res/layout/widget_core_account_add.xml @@ -9,7 +9,8 @@ android:focusableInTouchMode="false" android:orientation="horizontal" android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingRight="16dp" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> <ImageView android:layout_width="32dp" diff --git a/app/src/main/res/layout/widget_network.xml b/app/src/main/res/layout/widget_network.xml new file mode 100644 index 000000000..974a81e40 --- /dev/null +++ b/app/src/main/res/layout/widget_network.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="48dp" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> + + <TextView + android:id="@+id/name" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginBottom="8dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginTop="8dp" + android:layout_weight="1" + android:singleLine="true" + android:textColor="?attr/colorForeground" + android:textSize="14sp" + android:textStyle="bold" + tools:text="Freenode" /> + + <android.support.v7.widget.AppCompatImageButton + android:id="@+id/status" + style="?attr/buttonStyleSmall" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="@null" + android:foreground="?attr/selectableItemBackgroundBorderless" + android:minWidth="72dp" + android:paddingBottom="12dp" + android:paddingEnd="16dp" + android:paddingStart="16dp" + android:paddingTop="12dp" + android:scaleType="fitEnd" + android:tint="?attr/colorForegroundSecondary" + app:srcCompat="@drawable/ic_chevron_down" /> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_spinner_item_inline.xml b/app/src/main/res/layout/widget_spinner_item_inline.xml index b21fc8158..bdbc09345 100644 --- a/app/src/main/res/layout/widget_spinner_item_inline.xml +++ b/app/src/main/res/layout/widget_spinner_item_inline.xml @@ -1,7 +1,9 @@ <TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:minHeight="48dp" - android:textAppearance="?android:attr/textAppearanceListItemSmall" /> + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:theme="@style/Theme.ChatTheme.Quassel_Light" /> diff --git a/app/src/main/res/layout/widget_spinner_item_toolbar.xml b/app/src/main/res/layout/widget_spinner_item_toolbar.xml index fa2ad16dc..5c14c6cad 100644 --- a/app/src/main/res/layout/widget_spinner_item_toolbar.xml +++ b/app/src/main/res/layout/widget_spinner_item_toolbar.xml @@ -8,4 +8,5 @@ android:paddingLeft="16dp" android:paddingRight="16dp" android:textAppearance="?android:attr/textAppearanceListItemSmall" - tools:text="All Chats" /> + tools:text="All Chats" + tools:theme="@style/Theme.ChatTheme.Quassel_Light" /> diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt index 9d4a65d46..34acb8ce5 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt @@ -6,6 +6,7 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IIrcChannel import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.helpers.getOr +import io.reactivex.subjects.BehaviorSubject import java.nio.charset.Charset class IrcChannel( @@ -165,6 +166,7 @@ class IrcChannel( if (_topic == topic) return _topic = topic + live_topic.onNext(topic) super.setTopic(topic) } @@ -312,6 +314,7 @@ class IrcChannel( private var _name: String = name private var _topic: String = "" + val live_topic = BehaviorSubject.createDefault("") private var _password: String = "" private var _encrypted: Boolean = false private var _userModes: MutableMap<IrcUser, String> = mutableMapOf() @@ -322,4 +325,8 @@ class IrcChannel( private var _B_channelModes: MutableMap<Char, String> = mutableMapOf() private var _C_channelModes: MutableMap<Char, String> = mutableMapOf() private var _D_channelModes: MutableSet<Char> = mutableSetOf() + + companion object { + val NULL = IrcChannel("", Network.NULL, SignalProxy.NULL) + } } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt index cc0d9d2c8..7069bc149 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt @@ -7,6 +7,7 @@ import de.kuschku.libquassel.protocol.valueOr import de.kuschku.libquassel.quassel.syncables.interfaces.IIrcUser import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.irc.HostmaskHelper +import io.reactivex.subjects.BehaviorSubject import org.threeten.bp.Instant import java.nio.charset.Charset @@ -127,6 +128,7 @@ class IrcUser( override fun setRealName(realName: String) { if (_realName != realName) { _realName = realName + live_realName.onNext(realName) super.setRealName(realName) } } @@ -141,6 +143,7 @@ class IrcUser( override fun setAway(away: Boolean) { if (_away != away) { _away = away + live_away.onNext(away) super.setAway(away) } } @@ -272,9 +275,11 @@ class IrcUser( private var _user: String = HostmaskHelper.user(hostmask) private var _host: String = HostmaskHelper.host(hostmask) private var _realName: String = "" + val live_realName = BehaviorSubject.createDefault("") private var _account: String = "" private var _awayMessage: String = "" private var _away: Boolean = false + val live_away = BehaviorSubject.createDefault(false) private var _server: String = "" private var _idleTime: Instant = Instant.EPOCH private var _idleTimeSet: Instant = Instant.EPOCH @@ -289,4 +294,8 @@ class IrcUser( private var _network: Network = network private var _codecForEncoding: Charset? = null private var _codecForDecoding: Charset? = null + + companion object { + val NULL = IrcUser("", Network.NULL, SignalProxy.NULL) + } } 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 146d07605..2e672826d 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 @@ -9,6 +9,7 @@ import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.helpers.getOr import de.kuschku.libquassel.util.helpers.serializeString import de.kuschku.libquassel.util.irc.HostmaskHelper +import io.reactivex.subjects.BehaviorSubject import java.nio.ByteBuffer import java.nio.charset.Charset import java.util.* @@ -327,13 +328,15 @@ class Network constructor( _ircUsers[nick] = ircUser val mask = ircUser.hostMask() super.addIrcUser(mask) + live_ircUsers.onNext(_ircUsers) ircUser } else { user } } - fun ircUser(nickName: String?) = _ircUsers[nickName] + fun ircUser(nickName: String?) = _ircUsers[nickName?.toLowerCase(Locale.ENGLISH)] + fun liveIrcUser(nickName: String?) = live_ircUsers.map { ircUser(nickName) ?: IrcUser.NULL } fun ircUsers() = _ircUsers.values.toList() fun ircUserCount(): UInt = _ircUsers.size fun newIrcChannel(channelName: String, initData: QVariantMap = emptyMap()): IrcChannel { @@ -347,6 +350,7 @@ class Network constructor( } proxy.synchronize(ircChannel) _ircChannels[channelName.toLowerCase(Locale.ENGLISH)] = ircChannel + live_ircChannels.onNext(_ircChannels) super.addIrcChannel(channelName) return ircChannel } else { @@ -354,9 +358,15 @@ class Network constructor( } } - fun ircChannel(channelName: String) = _ircChannels[channelName] + fun ircChannel(channelName: String?) = _ircChannels[channelName?.toLowerCase(Locale.ENGLISH)] + fun liveIrcChannel(channelName: String?) = live_ircChannels.map { + ircChannel( + channelName + ) ?: IrcChannel.NULL + } + fun ircChannels() = _ircChannels.values.toList() - fun ircChanenlCount(): UInt = _ircChannels.size + fun ircChannelCount(): UInt = _ircChannels.size fun codecForServer(): String = _codecForServer.name() fun codecForEncoding(): String = _codecForEncoding.name() fun codecForDecoding(): String = _codecForDecoding.name() @@ -838,14 +848,18 @@ class Network constructor( fun removeChansAndUsers() { _ircUsers.clear() _ircChannels.clear() + live_ircChannels.onNext(_ircChannels) + live_ircUsers.onNext(_ircUsers) } fun removeIrcUser(user: IrcUser) { _ircUsers.remove(user.nick()) + live_ircUsers.onNext(_ircUsers) } fun removeIrcChannel(channel: IrcChannel) { _ircChannels.remove(channel.name()) + live_ircChannels.onNext(_ircChannels) } private var _networkId: NetworkId = networkId @@ -856,13 +870,16 @@ class Network constructor( private var _currentServer: String = "" private var _connected: Boolean = false private var _connectionState: ConnectionState = ConnectionState.Disconnected + val liveConnectionState = BehaviorSubject.createDefault(ConnectionState.Disconnected) private var _prefixes: Set<Char>? = null private var _prefixModes: Set<Char>? = null private var _channelModes: Map<ChannelModeType, Set<Char>>? = null // stores all known nicks for the server private var _ircUsers: MutableMap<String, IrcUser> = mutableMapOf() + private val live_ircUsers = BehaviorSubject.createDefault(emptyMap<String, IrcUser>()) // stores all known channels private var _ircChannels: MutableMap<String, IrcChannel> = mutableMapOf() + private val live_ircChannels = BehaviorSubject.createDefault(emptyMap<String, IrcChannel>()) // stores results from RPL_ISUPPORT private var _supports: MutableMap<String, String> = mutableMapOf() /** @@ -905,4 +922,8 @@ class Network constructor( private var _codecForDecoding: Charset = Charsets.UTF_8 /** when this is active handle305 and handle306 don't trigger any output */ private var _autoAwayActive: Boolean = false + + companion object { + val NULL = Network(-1, SignalProxy.NULL) + } } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt b/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt index de9118c29..bb83b858d 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt @@ -43,4 +43,25 @@ interface SignalProxy { fun synchronize(syncableObject: ISyncableObject?, baseInit: Boolean) fun synchronize(syncableObject: ISyncableObject?) = synchronize(syncableObject, false) fun stopSynchronize(syncableObject: ISyncableObject?) + + companion object { + val NULL = object : SignalProxy { + override fun dispatch(message: SignalProxyMessage) = Unit + override fun dispatch(message: HandshakeMessage) = Unit + override fun callSync(type: String, instance: String, slot: String, + params: List<QVariant_>) = Unit + + override fun callRpc(slot: String, params: List<QVariant_>) = Unit + override fun shouldSync(type: String, instance: String, slot: String) = false + override fun shouldRpc(slot: String) = false + override fun network(id: NetworkId) = null + override fun identity(id: IdentityId) = null + override fun renameObject(syncableObject: ISyncableObject, newName: String, + oldName: String) = Unit + + override fun renameObject(className: String, newName: String, oldName: String) = Unit + override fun synchronize(syncableObject: ISyncableObject?, baseInit: Boolean) = Unit + override fun stopSynchronize(syncableObject: ISyncableObject?) = Unit + } + } } diff --git a/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt b/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt index aff3f65f3..e670ed8b4 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt @@ -2,6 +2,13 @@ package de.kuschku.libquassel.util interface Flag<T> where T : Enum<T>, T : Flag<T> { val bit: Int + fun toByte() = bit.toByte() + fun toChar() = bit.toChar() + fun toDouble() = bit.toDouble() + fun toFloat() = bit.toFloat() + fun toInt() = bit + fun toLong() = bit.toLong() + fun toShort() = bit.toShort() } data class Flags<E>( diff --git a/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt index 6617ff225..9dd681cb1 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt @@ -2,6 +2,13 @@ package de.kuschku.libquassel.util interface LongFlag<T> where T : Enum<T>, T : LongFlag<T> { val bit: Long + fun toByte() = bit.toByte() + fun toChar() = bit.toChar() + fun toDouble() = bit.toDouble() + fun toFloat() = bit.toFloat() + fun toInt() = bit.toInt() + fun toLong() = bit + fun toShort() = bit.toShort() } data class LongFlags<E>( diff --git a/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt index ec88fd70e..25b668398 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt @@ -6,6 +6,13 @@ import kotlin.experimental.xor interface ShortFlag<T> where T : Enum<T>, T : ShortFlag<T> { val bit: Short + fun toByte() = bit.toByte() + fun toChar() = bit.toChar() + fun toDouble() = bit.toDouble() + fun toFloat() = bit.toFloat() + fun toInt() = bit.toInt() + fun toLong() = bit.toLong() + fun toShort() = bit } data class ShortFlags<E>( -- GitLab