diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt index bd40477de6a564e9e8556a35d19a5bd20db96e93..7831ba4c5b0f135d8d24ca62e9c54e67bb31524a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt @@ -11,13 +11,21 @@ import de.kuschku.libquassel.protocol.Message_Type import de.kuschku.libquassel.protocol.Message_Types import de.kuschku.libquassel.util.hasFlag import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase +import de.kuschku.quasseldroid_ng.ui.settings.data.RenderingSettings import de.kuschku.quasseldroid_ng.util.helper.getOrPut class MessageAdapter(context: Context) : PagedListAdapter<QuasselDatabase.DatabaseMessage, QuasselMessageViewHolder>( QuasselDatabase.DatabaseMessage.MessageDiffCallback ) { - private val messageRenderer: MessageRenderer = QuasselMessageRenderer(context) + private val messageRenderer: MessageRenderer = QuasselMessageRenderer( + context, + RenderingSettings( + showPrefix = RenderingSettings.ShowPrefixMode.FIRST, + colorizeNicknames = RenderingSettings.ColorizeNicknamesMode.ALL_BUT_MINE, + timeFormat = "" + ) + ) private val messageCache = LruCache<Int, FormattedMessage>(512) @@ -42,7 +50,11 @@ class MessageAdapter(context: Context) : } private fun viewType(type: Message_Types, flags: Message_Flags): Int { - return (if (flags.hasFlag(Message_Flag.Highlight)) 0x8000 else 0x0000) or (type.value and 0x7FF) + if (flags.hasFlag(Message_Flag.Highlight)) { + return -type.value + } else { + return type.value + } } override fun getItemId(position: Int): Long { @@ -50,10 +62,10 @@ class MessageAdapter(context: Context) : } private fun messageType(viewType: Int): Message_Type? - = Message_Type.of(viewType and 0x7FF).enabledValues().firstOrNull() + = Message_Type.of(Math.abs(viewType)).enabledValues().firstOrNull() private fun hasHiglight(viewType: Int) - = viewType and 0x8000 != 0 + = viewType < 0 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuasselMessageViewHolder { val messageType = messageType(viewType) diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/QuasselMessageRenderer.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/QuasselMessageRenderer.kt index 0e0f2998c5e8f0eff822835a38ae8d2e06f06527..3cc00924fd6628059dd6f2cdd0acfa9740d53ab8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/QuasselMessageRenderer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/QuasselMessageRenderer.kt @@ -7,9 +7,14 @@ import android.text.format.DateFormat import android.text.style.ForegroundColorSpan import android.text.style.StyleSpan import de.kuschku.libquassel.protocol.Message.MessageType.* +import de.kuschku.libquassel.protocol.Message_Flag import de.kuschku.libquassel.protocol.Message_Type +import de.kuschku.libquassel.util.hasFlag import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase +import de.kuschku.quasseldroid_ng.ui.settings.data.RenderingSettings +import de.kuschku.quasseldroid_ng.ui.settings.data.RenderingSettings.ColorizeNicknamesMode +import de.kuschku.quasseldroid_ng.ui.settings.data.RenderingSettings.ShowPrefixMode import de.kuschku.quasseldroid_ng.util.helper.styledAttributes import de.kuschku.quasseldroid_ng.util.quassel.IrcUserUtils import de.kuschku.quasseldroid_ng.util.ui.SpanFormatter @@ -17,9 +22,16 @@ import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter import java.text.SimpleDateFormat -class QuasselMessageRenderer(context: Context) : MessageRenderer { +class QuasselMessageRenderer( + private val context: Context, + private val renderingSettings: RenderingSettings +) : MessageRenderer { private val timeFormatter = DateTimeFormatter.ofPattern( - (DateFormat.getTimeFormat(context) as SimpleDateFormat).toLocalizedPattern() + if (renderingSettings.timeFormat.isNotBlank()) { + renderingSettings.timeFormat + } else { + (DateFormat.getTimeFormat(context) as SimpleDateFormat).toLocalizedPattern() + } ) private lateinit var senderColors: IntArray @@ -38,16 +50,15 @@ class QuasselMessageRenderer(context: Context) : MessageRenderer { } } - override fun layout(type: Message_Type?, hasHighlight: Boolean) - = when (type) { - Nick, Mode, Join, Part, Quit, Kick, Kill, Info, DayChange, Topic, NetsplitJoin, - NetsplitQuit, Invite -> R.layout.widget_chatmessage_info - Notice -> R.layout.widget_chatmessage_notice - Server -> R.layout.widget_chatmessage_server - Error -> R.layout.widget_chatmessage_error - Action -> R.layout.widget_chatmessage_action - Plain -> R.layout.widget_chatmessage_plain - else -> R.layout.widget_chatmessage_plain + override fun layout(type: Message_Type?, hasHighlight: Boolean) = when (type) { + Notice -> R.layout.widget_chatmessage_notice + Server -> R.layout.widget_chatmessage_server + Error -> R.layout.widget_chatmessage_error + Action -> R.layout.widget_chatmessage_action + Plain -> R.layout.widget_chatmessage_plain + Nick, Mode, Join, Part, Quit, Kick, Kill, Info, DayChange, Topic, NetsplitJoin, NetsplitQuit, + Invite -> R.layout.widget_chatmessage_info + else -> R.layout.widget_chatmessage_placeholder } override fun init(viewHolder: QuasselMessageViewHolder, @@ -67,119 +78,141 @@ class QuasselMessageRenderer(context: Context) : MessageRenderer { } override fun render(message: QuasselDatabase.DatabaseMessage): FormattedMessage { - return when (message.type) { - Message_Type.Plain.bit -> FormattedMessage( + return when (Message_Type.of(message.type).enabledValues().firstOrNull()) { + Message_Type.Plain -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "%s%s: %s", - message.senderPrefixes, - formatNick(message.sender), + context.getString(R.string.message_format_plain), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), message.content ) ) - Message_Type.Action.bit -> FormattedMessage( + Message_Type.Action -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "* %s%s %s", - message.senderPrefixes, - formatNick(message.sender), + context.getString(R.string.message_format_action), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), message.content ) ) - Message_Type.Notice.bit -> FormattedMessage( + Message_Type.Notice -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "[%s%s] %s", - message.senderPrefixes, - formatNick(message.sender), + context.getString(R.string.message_format_notice), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), message.content ) ) - Message_Type.Nick.bit -> FormattedMessage( + Message_Type.Nick -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "%s%s is now known as %s%s", - message.senderPrefixes, - formatNick(message.sender), - message.senderPrefixes, - formatNick(message.content) + context.getString(R.string.message_format_nick), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), + formatPrefix(message.senderPrefixes), + formatNick(message.content, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)) ) ) - Message_Type.Join.bit -> FormattedMessage( + Message_Type.Mode -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "%s%s joined", - message.senderPrefixes, - formatNick(message.sender) + context.getString(R.string.message_format_mode), + message.content, + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)) ) ) - Message_Type.Part.bit -> FormattedMessage( + Message_Type.Join -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( - "%s%s left: %s", - message.senderPrefixes, - formatNick(message.sender), - message.content + context.getString(R.string.message_format_join), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)) ) ) - Message_Type.Quit.bit -> FormattedMessage( + Message_Type.Part -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), - SpanFormatter.format( - "%s%s quit: %s", - message.senderPrefixes, - formatNick(message.sender), - message.content - ) + if (message.content.isBlank()) { + SpanFormatter.format( + context.getString(R.string.message_format_part_1), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)) + ) + } else { + SpanFormatter.format( + context.getString(R.string.message_format_part_2), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), + message.content + ) + } ) - Message_Type.Server.bit, - Message_Type.Info.bit, - Message_Type.Error.bit -> FormattedMessage( + Message_Type.Quit -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), - SpanFormatter.format( - "%s", - message.content - ) + if (message.content.isBlank()) { + SpanFormatter.format( + context.getString(R.string.message_format_quit_1), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)) + ) + } else { + SpanFormatter.format( + context.getString(R.string.message_format_quit_2), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), + message.content + ) + } ) - Message_Type.Topic.bit -> FormattedMessage( + Message_Type.Server, + Message_Type.Info, + Message_Type.Error -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), - SpanFormatter.format( - "%s", - message.content - ) + message.content ) - else -> FormattedMessage( + Message_Type.Topic -> FormattedMessage( + message.messageId, + timeFormatter.format(message.time.atZone(zoneId)), + message.content + ) + else -> FormattedMessage( message.messageId, timeFormatter.format(message.time.atZone(zoneId)), SpanFormatter.format( "[%d] %s%s: %s", message.type, - message.senderPrefixes, - formatNick(message.sender), + formatPrefix(message.senderPrefixes), + formatNick(message.sender, Message_Flag.of(message.flag).hasFlag(Message_Flag.Self)), message.content ) ) } } - private fun formatNick(sender: String): CharSequence { + private fun formatNickImpl(sender: String, colorize: Boolean): CharSequence { val nick = IrcUserUtils.nick(sender) - val senderColor = IrcUserUtils.senderColor(nick) val spannableString = SpannableString(nick) - spannableString.setSpan( - ForegroundColorSpan(senderColors[senderColor % senderColors.size]), - 0, - nick.length, - SpannableString.SPAN_INCLUSIVE_EXCLUSIVE - ) + if (colorize) { + val senderColor = IrcUserUtils.senderColor(nick) + spannableString.setSpan( + ForegroundColorSpan(senderColors[senderColor % senderColors.size]), + 0, + nick.length, + SpannableString.SPAN_INCLUSIVE_EXCLUSIVE + ) + } spannableString.setSpan( StyleSpan(Typeface.BOLD), 0, @@ -188,4 +221,18 @@ class QuasselMessageRenderer(context: Context) : MessageRenderer { ) return spannableString } + + private fun formatNick(sender: String, self: Boolean) + = when (renderingSettings.colorizeNicknames) { + ColorizeNicknamesMode.ALL -> formatNickImpl(sender, true) + ColorizeNicknamesMode.ALL_BUT_MINE -> formatNickImpl(sender, !self) + ColorizeNicknamesMode.NONE -> formatNickImpl(sender, false) + } + + private fun formatPrefix(prefix: String) + = when (renderingSettings.showPrefix) { + ShowPrefixMode.ALL -> prefix + ShowPrefixMode.FIRST -> prefix.substring(0, Math.min(prefix.length, 1)) + ShowPrefixMode.NONE -> "" + } } \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarFragment.kt index ac243f448aedfe740c60b0a74a63ccbca156969f..61b8f123b6468308e2087eaa5e3853a8e612f22b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ToolbarFragment.kt @@ -11,12 +11,17 @@ import android.widget.TextView import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.BufferInfo +import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.Backend import de.kuschku.libquassel.session.SessionManager +import de.kuschku.libquassel.util.hasFlag import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.ui.settings.data.DisplaySettings import de.kuschku.quasseldroid_ng.util.helper.* import de.kuschku.quasseldroid_ng.util.service.ServiceBoundFragment +import io.reactivex.Observable class ToolbarFragment : ServiceBoundFragment() { @BindView(R.id.toolbar_title) @@ -31,10 +36,83 @@ class ToolbarFragment : ServiceBoundFragment() { private val sessionManager: LiveData<SessionManager?> = backend.map(Backend::sessionManager) - private val currentBufferInfo: LiveData<BufferInfo?> - = sessionManager.switchMapRx(SessionManager::session).switchMap { session -> - buffer.switchMapRx { - session.bufferSyncer?.liveBufferInfo(it) + private val lag: LiveData<Long?> + = sessionManager.switchMapRx { it.session.switchMap { it.lag } } + + private val displaySettings = DisplaySettings( + showLag = true + ) + + private val bufferData: LiveData<BufferData?> = sessionManager.switchMap { manager -> + buffer.switchMapRx { id -> + manager.session.switchMap { + val bufferSyncer = it.bufferSyncer + if (bufferSyncer != null) { + bufferSyncer.live_bufferInfos.switchMap { + val info = bufferSyncer.bufferInfo(id) + val network = manager.networks[info?.networkId] + if (info == null) { + Observable.just( + BufferData( + description = "Info was null" + ) + ) + } else if (network == null) { + Observable.just( + BufferData( + description = "Network was null" + ) + ) + } else { + when (info.type.toInt()) { + BufferInfo.Type.QueryBuffer.toInt() -> { + network.liveIrcUser(info.bufferName).switchMap { user -> + user.live_realName.map { realName -> + BufferData( + info = info, + network = network.networkInfo(), + description = realName + ) + } + } + } + BufferInfo.Type.ChannelBuffer.toInt() -> { + network.liveIrcChannel( + info.bufferName + ).switchMap { channel -> + channel.live_topic.map { topic -> + BufferData( + info = info, + network = network.networkInfo(), + description = topic + ) + } + } + } + BufferInfo.Type.StatusBuffer.toInt() -> { + network.liveConnectionState.map { + BufferData( + info = info, + network = network.networkInfo() + ) + } + } + else -> Observable.just( + BufferData( + description = "type is unknown: ${info.type.toInt()}" + ) + ) + } + } + } + } else { + Observable.just( + BufferData( + description = "buffersyncer was null" + ) + ) + } + } } } @@ -46,17 +124,20 @@ class ToolbarFragment : ServiceBoundFragment() { } } - var title: CharSequence + var title: CharSequence? get() = toolbarTitle.text set(value) { - toolbarTitle.text = value + if (value != null) + toolbarTitle.text = value + else + toolbarTitle.setText(R.string.app_name) } - var subtitle: CharSequence + var subtitle: CharSequence? get() = toolbarTitle.text set(value) { - toolbarSubtitle.text = value - toolbarSubtitle.visibleIf(value.isNotEmpty()) + toolbarSubtitle.text = value ?: "" + toolbarSubtitle.visibleIf(value?.isNotEmpty() == true) } override fun onCreateView(inflater: LayoutInflater, @@ -65,17 +146,37 @@ class ToolbarFragment : ServiceBoundFragment() { val view = inflater.inflate(R.layout.fragment_toolbar, container, false) ButterKnife.bind(this, view) - currentBufferInfo.zip(isSecure).observe( + bufferData.zip(isSecure, lag).observe( this, Observer { if (it != null) { - val (info, isSecure) = it - this.title = info?.bufferName ?: resources.getString( - R.string.app_name - ) + val (data, isSecure, lag) = it + if (data?.info?.type?.hasFlag(Buffer_Type.StatusBuffer) == true) { + this.title = data.network?.networkName + } else { + this.title = data?.info?.bufferName + } + + if (lag == 0L || !displaySettings.showLag) { + this.subtitle = data?.description + } else { + val description = data?.description + if (description.isNullOrBlank()) { + this.subtitle = "Lag: ${lag}ms" + } else { + this.subtitle = "Lag: ${lag}ms ${description}" + } + } } } ) return view } + + data class BufferData( + val info: BufferInfo? = null, + val network: INetwork.NetworkInfo? = null, + val description: String? = null + ) + } \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/DisplaySettings.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/DisplaySettings.kt new file mode 100644 index 0000000000000000000000000000000000000000..bfd333313d06b4c792d9560135b58038ed7e0b3d --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/DisplaySettings.kt @@ -0,0 +1,5 @@ +package de.kuschku.quasseldroid_ng.ui.settings.data + +data class DisplaySettings( + val showLag: Boolean = true +) \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/RenderingSettings.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/RenderingSettings.kt new file mode 100644 index 0000000000000000000000000000000000000000..a0a86d5c02772380f7304ab0098b2dfd25a7a108 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/settings/data/RenderingSettings.kt @@ -0,0 +1,37 @@ +package de.kuschku.quasseldroid_ng.ui.settings.data + +data class RenderingSettings( + val showPrefix: ShowPrefixMode = ShowPrefixMode.FIRST, + val colorizeNicknames: ColorizeNicknamesMode = ColorizeNicknamesMode.ALL_BUT_MINE, + val timeFormat: String = "" +) { + enum class ColorizeNicknamesMode(val value: Int) { + ALL(0), + ALL_BUT_MINE(1), + NONE(2); + + companion object { + fun of(value: Int) = when (value) { + 0 -> ALL + 1 -> ALL_BUT_MINE + 2 -> NONE + else -> ALL_BUT_MINE + } + } + } + + enum class ShowPrefixMode(val value: Int) { + ALL(0), + FIRST(1), + NONE(2); + + companion object { + fun of(value: Int) = when (value) { + 0 -> ALL + 1 -> FIRST + 2 -> NONE + else -> FIRST + } + } + } +} \ No newline at end of file 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 index c9045e61926e25620fd4ad4c75ffa7255c54d149..47836fb3064218d4a88728fee4053b4b4112ffcf 100644 --- 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 @@ -57,10 +57,49 @@ fun <A, B> zipLiveData(a: LiveData<A>, b: LiveData<B>): LiveData<Pair<A, B>> { } } +fun <A, B, C> zipLiveData(a: LiveData<A>, b: LiveData<B>, + c: LiveData<C>): LiveData<Triple<A, B, C>> { + return MediatorLiveData<Triple<A, B, C>>().apply { + var lastA: A? = null + var lastB: B? = null + var lastC: C? = null + + fun update() { + val localLastA = lastA + val localLastB = lastB + val localLastC = lastC + if (localLastA != null && localLastB != null && localLastC != null) + this.value = Triple(localLastA, localLastB, localLastC) + } + + addSource(a) { + lastA = it + update() + } + addSource(b) { + lastB = it + update() + } + addSource(c) { + lastC = 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) + /** * 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 +fun <A, B, C> LiveData<A>.zip(b: LiveData<B>, + c: LiveData<C>): LiveData<Triple<A, B, C>> = zipLiveData(this, b, c) \ No newline at end of file diff --git a/app/src/main/res/layout/widget_chatmessage_error.xml b/app/src/main/res/layout/widget_chatmessage_error.xml index 53233ad2500fd04413285f1af00ef380d738e9dc..55547dc3a88513ba4e67b636e9b5237f0167348f 100644 --- a/app/src/main/res/layout/widget_chatmessage_error.xml +++ b/app/src/main/res/layout/widget_chatmessage_error.xml @@ -35,6 +35,5 @@ android:layout_weight="1" android:textColor="?attr/colorForegroundError" android:textIsSelectable="true" - android:textStyle="italic" tools:text="everyone: deserves a chance to fly. No such channel" /> </LinearLayout> diff --git a/app/src/main/res/layout/widget_chatmessage_placeholder.xml b/app/src/main/res/layout/widget_chatmessage_placeholder.xml new file mode 100644 index 0000000000000000000000000000000000000000..be8f116d729846e556f570e405e21c59faf94446 --- /dev/null +++ b/app/src/main/res/layout/widget_chatmessage_placeholder.xml @@ -0,0 +1,22 @@ +<?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="48dp" + android:gravity="top" + android:orientation="horizontal" + tools:background="@android:color/background_light" + tools:theme="@style/Theme.ChatTheme.Quassel_Light"> + + <TextView + android:id="@+id/time" + android:layout_width="0dip" + android:layout_height="0dip" + android:visibility="gone" /> + + <TextView + android:id="@+id/content" + android:layout_width="0dip" + android:layout_height="0dip" + android:visibility="gone" /> +</LinearLayout> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7771d68ff8bcf33b558189658cf98c63c3f88d81..3527a83de05bf7267fbd872e57a2e405f44db842 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,4 +7,15 @@ <string name="crash_text">QD-NG has crashed</string> <string name="drawer_open">Open</string> <string name="drawer_close">Close</string> + + <string name="message_format_plain">%1$s%2$s: %3$s</string> + <string name="message_format_action">* %1$s%2$s %3$s</string> + <string name="message_format_notice">[%1$s%2$s] %3$s</string> + <string name="message_format_nick">%1$s%2$s is now known as %3$s%4$s</string> + <string name="message_format_mode">%1$s by %2$s%3$s</string> + <string name="message_format_join">%1$s%2$s joined</string> + <string name="message_format_part_1">%1$s%2$s left</string> + <string name="message_format_part_2">%1$s%2$s left: %3$s</string> + <string name="message_format_quit_1">%1$s%2$s quit</string> + <string name="message_format_quit_2">%1$s%2$s quit (%3$s)</string> </resources> diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt index 4cf9c05f5bc1a111722ffa591ed66d326add2d6c..862b13a4d955b8a15b2ef64c5b575a14e85074fe 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt @@ -30,6 +30,8 @@ interface ISession : Closeable { val rpcHandler: RpcHandler? val initStatus: Observable<Pair<Int, Int>> + val lag: Observable<Long> + companion object { val NULL = object : ISession { override val state = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED) @@ -51,6 +53,7 @@ interface ISession : Closeable { override val networks: Map<NetworkId, Network> = emptyMap() override val networkConfig: NetworkConfig? = null override val initStatus: Observable<Pair<Int, Int>> = Observable.just(0 to 0) + override val lag: Observable<Long> = Observable.just(0L) override fun close() = Unit } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt index 9f729068d4844548193269f5342001ce366b7489..af8384cd523a2433b9405bf77adfd0ccf84f0db9 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -50,6 +50,8 @@ class Session( override val initStatus = BehaviorSubject.createDefault(0 to 0) + override val lag = BehaviorSubject.createDefault(0L) + init { coreConnection.start() } @@ -62,6 +64,8 @@ class Session( password = userData.second ) ) + + dispatch(SignalProxyMessage.HeartBeat(Instant.now())) return true } @@ -101,6 +105,8 @@ class Session( synchronize(backlogManager) + dispatch(SignalProxyMessage.HeartBeat(Instant.now())) + return true } @@ -111,12 +117,15 @@ class Session( override fun onInitDone() { coreConnection.setState(ConnectionState.CONNECTED) log(INFO, "Session", "Initialization finished") + + dispatch(SignalProxyMessage.HeartBeat(Instant.now())) } override fun handle(f: SignalProxyMessage.HeartBeatReply): Boolean { val now = Instant.now() val latency = now.toEpochMilli() - f.timestamp.toEpochMilli() log(INFO, "Session", "Latency of $latency ms") + lag.onNext(latency) return true } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index 0f5829de0381342842dddeae59b4e52ec7985983..2732d93f1e98f0a93a1a35af1b24c9d1442de5d9 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -48,6 +48,8 @@ class SessionManager(offlineSession: ISession, val backlogStorage: BacklogStorag get() = session.or(lastSession).networkConfig override val rpcHandler: RpcHandler? get() = session.or(lastSession).rpcHandler + override val lag: Observable<Long> + get() = session.or(lastSession).lag override fun close() = session.or(lastSession).close()