From eb1bd550e9d794c1e017456dd63b0d3a5ecb81b1 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Wed, 17 Apr 2019 15:07:26 +0200 Subject: [PATCH] Improve state saving/restoring --- .../quasseldroid/service/BacklogRequester.kt | 2 +- .../quasseldroid/service/QuasselService.kt | 4 +- .../quasseldroid/ui/chat/ChatActivity.kt | 25 +++-- .../ui/chat/input/ChatlineFragment.kt | 4 +- .../ui/chat/messages/MessageListFragment.kt | 16 ++-- .../ui/chat/topic/TopicActivity.kt | 2 +- .../ui/chat/topic/TopicFragment.kt | 7 +- .../quasseldroid/util/backport/OsConstants.kt | 2 +- .../util/backport/ReadableWrappedChannel.kt | 8 +- .../util/backport/WritableWrappedChannel.kt | 4 +- .../quasseldroid/util/backport/codec/Hex.kt | 22 ++--- .../ui/fastscroll/views/FastScroller.java | 2 +- .../kuschku/libquassel/protocol/SignedId.kt | 10 +- .../de/kuschku/libquassel/util/flag/Flag.kt | 17 ++-- .../kuschku/libquassel/util/flag/LongFlag.kt | 17 ++-- .../kuschku/libquassel/util/flag/ShortFlag.kt | 17 ++-- .../persistence/models/MessageData.kt | 3 +- .../quasseldroid/viewmodel/ChatViewModel.kt | 92 ++++++++++++++++++- .../quasseldroid/viewmodel/data/Avatar.kt | 4 +- .../viewmodel/data/FormattedMessage.kt | 3 +- .../viewmodel/helper/ChatViewModelHelper.kt | 8 +- .../viewmodel/helper/EditorViewModelHelper.kt | 2 +- 22 files changed, 181 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt index 0302c6cfd..40bfc7d69 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt @@ -45,7 +45,7 @@ class BacklogRequester( finishCallback: () -> Unit) { log(DEBUG, "BacklogRequester", - "requested(buffer: $buffer, amount: $amount, pageSize: $pageSize, lastMessageId: $lastMessageId, untilAllVisible: $untilAllVisible)") + "requested(bufferId: $buffer, amount: $amount, pageSize: $pageSize, lastMessageId: $lastMessageId, untilAllVisible: $untilAllVisible)") var missing = amount session.value?.orNull()?.let { session: ISession -> session.backlogManager.let { diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt index f4e038cac..e615f8ac7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt @@ -176,7 +176,7 @@ class QuasselService : DaggerLifecycleService(), } } - val bufferId = BufferId(intent.getIntExtra("buffer", -1)) + val bufferId = BufferId(intent.getIntExtra("bufferId", -1)) val inputResults = RemoteInput.getResultsFromIntent(intent)?.getCharSequence("reply_content") if (inputResults != null && bufferId.isValidId()) { @@ -550,7 +550,7 @@ class QuasselService : DaggerLifecycleService(), putExtra("disconnect", disconnect) } if (bufferId != null) { - putExtra("buffer", bufferId.id) + putExtra("bufferId", bufferId.id) } if (markReadMessage != null) { putExtra("mark_read_message", markReadMessage.id) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 72f906dc2..708d27060 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -179,7 +179,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } intent.hasExtra(KEY_BUFFER_ID) -> { - chatViewModel.buffer.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1))) + chatViewModel.bufferId.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1))) chatViewModel.bufferOpened.onNext(Unit) if (intent.hasExtra(KEY_ACCOUNT_ID)) { val accountId = intent.getLongExtra(ChatActivity.KEY_ACCOUNT_ID, -1) @@ -404,8 +404,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } // If we connect to a new network without statusbuffer, the bufferid may be -networkId. - // In that case, once we’re connected (and a status buffer exists), we want to switch to it. - combineLatest(modelHelper.allBuffers, chatViewModel.buffer).map { (buffers, current) -> + // In that case, once we’re connected (and a status bufferId exists), we want to switch to it. + combineLatest(modelHelper.allBuffers, chatViewModel.bufferId).map { (buffers, current) -> if (current.isValidId()) Optional.empty() else Optional.ofNullable(buffers.firstOrNull { it.networkId == NetworkId(-current.id) && it.type.hasFlag(Buffer_Type.StatusBuffer) @@ -727,7 +727,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .observe(this, Observer { if (connectedAccount != accountId) { if (resources.getBoolean(R.bool.buffer_drawer_exists) && - chatViewModel.buffer.value == BufferId.MAX_VALUE && + chatViewModel.bufferId.value == BufferId.MAX_VALUE && !restoredDrawerState) { drawerLayout.openDrawer(GravityCompat.START) } @@ -812,7 +812,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } }) - // Only show nick list when we’re in a channel buffer + // Only show nick list when we’re in a channel bufferId modelHelper.bufferDataThrottled.toLiveData().observe(this, Observer { bufferData = it if (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) { @@ -889,9 +889,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - // TODO: store entire chatviewmodel - outState.putInt(KEY_OPEN_BUFFER, chatViewModel.buffer.value.id) - outState.putInt(KEY_OPEN_BUFFERVIEWCONFIG, chatViewModel.bufferViewConfigId.value ?: -1) + chatViewModel.onSaveInstanceState(outState) + outState.putLong(KEY_CONNECTED_ACCOUNT, connectedAccount) outState.putBoolean(KEY_OPEN_DRAWER_START, drawerLayout.isDrawerOpen(GravityCompat.START)) outState.putBoolean(KEY_OPEN_DRAWER_END, drawerLayout.isDrawerOpen(GravityCompat.END)) @@ -899,10 +898,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc override fun onRestoreInstanceState(savedInstanceState: Bundle?) { super.onRestoreInstanceState(savedInstanceState) - // TODO: restore entire chatviewmodel - chatViewModel.buffer.onNext(BufferId(savedInstanceState?.getInt(KEY_OPEN_BUFFER, -1) ?: -1)) - chatViewModel.bufferViewConfigId.onNext(savedInstanceState?.getInt(KEY_OPEN_BUFFERVIEWCONFIG, -1) - ?: -1) + if (savedInstanceState != null) { + chatViewModel.onRestoreInstanceState(savedInstanceState) + } + connectedAccount = savedInstanceState?.getLong(KEY_CONNECTED_ACCOUNT, -1L) ?: -1L if (savedInstanceState?.getBoolean(KEY_OPEN_DRAWER_START) == true && @@ -952,7 +951,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } R.id.action_filter_messages -> { runInBackground { - chatViewModel.buffer { buffer -> + chatViewModel.bufferId { buffer -> val filteredRaw = database.filtered().get( accountId, buffer, diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt index 90052c05e..10c866aff 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt @@ -42,8 +42,6 @@ import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer import de.kuschku.quasseldroid.util.service.ServiceBoundFragment -import de.kuschku.quasseldroid.viewmodel.ChatViewModel -import de.kuschku.quasseldroid.viewmodel.EditorViewModel import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper import javax.inject.Inject @@ -185,7 +183,7 @@ class ChatlineFragment : ServiceBoundFragment() { } modelHelper.session { sessionOptional -> val session = sessionOptional.orNull() - modelHelper.chat.buffer { bufferId -> + modelHelper.chat.bufferId { bufferId -> session?.bufferSyncer?.bufferInfo(bufferId)?.also { bufferInfo -> val output = mutableListOf<IAliasManager.Command>() for ((_, formatted) in lines) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index 3a5a2fedc..554da02b7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -369,7 +369,7 @@ class MessageListFragment : ServiceBoundFragment() { } } - val data = combineLatest(modelHelper.chat.buffer, + val data = combineLatest(modelHelper.chat.bufferId, modelHelper.chat.selectedMessages, modelHelper.chat.expandedMessages, modelHelper.markerLine) @@ -395,11 +395,11 @@ class MessageListFragment : ServiceBoundFragment() { } } - val lastMessageId = modelHelper.chat.buffer.toLiveData().switchMapNotNull { + val lastMessageId = modelHelper.chat.bufferId.toLiveData().switchMapNotNull { database.message().lastMsgId(it) } - modelHelper.chat.buffer.toLiveData().observe(this, Observer { bufferId -> + modelHelper.chat.bufferId.toLiveData().observe(this, Observer { bufferId -> swipeRefreshLayout.isEnabled = (bufferId != null || bufferId?.isValidId() == true) }) @@ -407,12 +407,12 @@ class MessageListFragment : ServiceBoundFragment() { this, Observer { if (it?.orNull() == ConnectionState.CONNECTED) { runInBackgroundDelayed(16) { - modelHelper.chat.buffer { bufferId -> + modelHelper.chat.bufferId { bufferId -> val filtered = database.filtered().get(accountId, bufferId, accountDatabase.accounts().findById(accountId)?.defaultFiltered ?: 0) - // Try loading messages when switching to isEmpty buffer + // Try loading messages when switching to isEmpty bufferId val hasVisibleMessages = database.message().hasVisibleMessages(bufferId, filtered) if (!hasVisibleMessages) { if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) { @@ -504,7 +504,7 @@ class MessageListFragment : ServiceBoundFragment() { list?.let(adapter::submitList) } - val buffer = modelHelper.chat.buffer.value + val buffer = modelHelper.chat.bufferId.value ?: BufferId(-1) if (buffer != lastBuffer) { adapter.clearCache() @@ -539,7 +539,7 @@ class MessageListFragment : ServiceBoundFragment() { if (previous != null && lastMessageId != null) { bufferSyncer.requestSetMarkerLine(previous, lastMessageId) } - // Try loading messages when switching to isEmpty buffer + // Try loading messages when switching to isEmpty bufferId val filtered = database.filtered().get(accountId, current, accountDatabase.accounts().findById(accountId)?.defaultFiltered @@ -567,7 +567,7 @@ class MessageListFragment : ServiceBoundFragment() { private fun loadMore(initial: Boolean = false, lastMessageId: MsgId? = null) { // This can be called *after* we’re already detached from the activity activity?.runOnUiThread { - modelHelper.chat.buffer { bufferId -> + modelHelper.chat.bufferId { bufferId -> if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) { if (initial) swipeRefreshLayout.isRefreshing = true runInBackground { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicActivity.kt index cc1a45d3f..9c8dc9d96 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicActivity.kt @@ -36,7 +36,7 @@ class TopicActivity : ServiceBoundSettingsActivity(TopicFragment()) { context: Context, buffer: Int ) = Intent(context, TopicActivity::class.java).apply { - putExtra("buffer", buffer) + putExtra("bufferId", buffer) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt index 6180fea5a..4684d6211 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt @@ -42,7 +42,6 @@ import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment -import de.kuschku.quasseldroid.viewmodel.EditorViewModel import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper import javax.inject.Inject @@ -117,8 +116,8 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { } } - val bufferId = BufferId(arguments?.getInt("buffer", -1) ?: -1) - modelHelper.chat.buffer.onNext(bufferId) + val bufferId = BufferId(arguments?.getInt("bufferId", -1) ?: -1) + modelHelper.chat.bufferId.onNext(bufferId) modelHelper.bufferData.filter { it.info != null }.firstElement().toLiveData().observe(this, Observer { @@ -131,7 +130,7 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { override fun onSave(): Boolean { modelHelper.session { sessionOptional -> val session = sessionOptional.orNull() - modelHelper.chat.buffer { bufferId -> + modelHelper.chat.bufferId { bufferId -> session?.bufferSyncer?.bufferInfo(bufferId)?.also { bufferInfo -> val topic = formatSerializer.toEscapeCodes(chatline.safeText) session.rpcHandler.sendInput(bufferInfo, "/topic $topic") diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt index 1f92f436e..e98d479fa 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt @@ -400,7 +400,7 @@ object OsConstants { ENETRESET -> "Network dropped connection on reset" ECONNABORTED -> "Software caused connection abort" ECONNRESET -> "Connection reset by peer" - ENOBUFS -> "No buffer space available" + ENOBUFS -> "No bufferId space available" EISCONN -> "Transport endpoint is already connected" ENOTCONN -> "Transport endpoint is not connected" ESHUTDOWN -> "Cannot send after transport endpoint shutdown" diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/ReadableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/ReadableWrappedChannel.kt index 89775f695..9cf6929e5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/backport/ReadableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/ReadableWrappedChannel.kt @@ -50,11 +50,11 @@ class ReadableWrappedChannel( try { // begin blocking operation, this handles interruption etc. properly begin() - // prepare buffer for reading by resetting position and limit + // prepare bufferId for reading by resetting position and limit buffer.clear() - // read data into buffer + // read data into bufferId readData = backingStream.read(buffer.array(), 0, toReadOnce) - // accurately set buffer info + // accurately set bufferId info buffer.position(readData) } finally { // end blocking operation, this handles interruption etc. properly @@ -74,7 +74,7 @@ class ReadableWrappedChannel( // mark that we’ve read data (to only block once) hasRead = true - // flip buffer to prepare for reading + // flip bufferId to prepare for reading buffer.flip() dst.put(buffer) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/WritableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/WritableWrappedChannel.kt index 599163c31..5773792f3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/backport/WritableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/WritableWrappedChannel.kt @@ -39,10 +39,10 @@ class WritableWrappedChannel( // Data to be written, always the minimum of available data and the page size val writtenData = Math.min(remainingData, PAGE_SIZE) - // Set new buffer info + // Set new bufferId info buffer.clear() buffer.limit(writtenData) - // Read data into buffer + // Read data into bufferId buffer.put(src) try { diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/codec/Hex.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/codec/Hex.kt index 840b00d15..aebe1fd5e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/backport/codec/Hex.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/codec/Hex.kt @@ -98,7 +98,7 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. + * Converts a bufferId of character bytes representing hexadecimal values into an array of bytes of those same values. * The returned array will be half the length of the passed array, as it takes two characters to represent any given * byte. An exception is thrown if the passed char array has an odd number of elements. * @@ -160,7 +160,7 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each + * Converts byte bufferId into an array of bytes for the characters representing the hexadecimal values of each * byte in order. The returned array will be double the length of the passed array, as it takes two characters to * represent any given byte. * @@ -170,7 +170,7 @@ class Hex : BinaryEncoder, BinaryDecoder { * * * @param array - * a byte buffer to convert to Hex characters + * a byte bufferId to convert to Hex characters * @return A byte[] containing the bytes of the lower-case hexadecimal characters * @see .encodeHex * @since 1.11 @@ -343,12 +343,12 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. + * Converts a byte bufferId into an array of characters representing the hexadecimal values of each byte in order. * The returned array will be double the length of the passed array, as it takes two characters to represent any * given byte. * * @param data - * a byte buffer to convert to Hex characters + * a byte bufferId to convert to Hex characters * @param toLowerCase * `true` converts to lowercase, `false` to uppercase * @return A char[] containing hexadecimal characters in the selected case @@ -387,12 +387,12 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. + * Converts a byte bufferId into an array of characters representing the hexadecimal values of each byte in order. * The returned array will be double the length of the passed array, as it takes two characters to represent any * given byte. * * @param data - * a byte buffer to convert to Hex characters + * a byte bufferId to convert to Hex characters * @param toDigits * the output alphabet (must be at least 16 characters) * @return A char[] containing the appropriate characters from the alphabet @@ -432,11 +432,11 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned + * Converts a byte bufferId into a String representing the hexadecimal values of each byte in order. The returned * String will be double the length of the passed array, as it takes two characters to represent any given byte. * * @param data - * a byte buffer to convert to Hex characters + * a byte bufferId to convert to Hex characters * @return A String containing lower-case hexadecimal characters * @since 1.11 */ @@ -445,11 +445,11 @@ class Hex : BinaryEncoder, BinaryDecoder { } /** - * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned + * Converts a byte bufferId into a String representing the hexadecimal values of each byte in order. The returned * String will be double the length of the passed array, as it takes two characters to represent any given byte. * * @param data - * a byte buffer to convert to Hex characters + * a byte bufferId to convert to Hex characters * @param toLowerCase * `true` converts to lowercase, `false` to uppercase * @return A String containing lower-case hexadecimal characters diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/fastscroll/views/FastScroller.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/fastscroll/views/FastScroller.java index d510abbca..670cf106f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/fastscroll/views/FastScroller.java +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/fastscroll/views/FastScroller.java @@ -57,7 +57,7 @@ public class FastScroller { private Rect mTmpRect = new Rect(); private Rect mInvalidateRect = new Rect(); private Rect mInvalidateTmpRect = new Rect(); - // The inset is the buffer around which a point will still register as a click on the scrollbar + // The inset is the bufferId around which a point will still register as a click on the scrollbar private int mTouchInset; // This is the offset from the top of the scrollbar when the user first starts touching. To // prevent jumping, this offset is applied as the user scrolls. diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt index 317cc7cd8..861466bd4 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt @@ -19,12 +19,14 @@ @file:Suppress("NOTHING_TO_INLINE") package de.kuschku.libquassel.protocol +import java.io.Serializable + typealias SignedId = Int typealias SignedId64 = Long typealias MsgId_Type = SignedId64 -inline class MsgId(val id: MsgId_Type) : Comparable<MsgId> { +inline class MsgId(val id: MsgId_Type) : Comparable<MsgId>, Serializable { override fun compareTo(other: MsgId) = id.compareTo(other.id) inline fun isValidId() = id > 0 @@ -40,7 +42,7 @@ inline class MsgId(val id: MsgId_Type) : Comparable<MsgId> { typealias NetworkId_Type = SignedId -inline class NetworkId(val id: NetworkId_Type) : Comparable<NetworkId> { +inline class NetworkId(val id: NetworkId_Type) : Comparable<NetworkId>, Serializable { override fun compareTo(other: NetworkId) = id.compareTo(other.id) inline fun isValidId() = id > 0 @@ -56,7 +58,7 @@ inline class NetworkId(val id: NetworkId_Type) : Comparable<NetworkId> { typealias BufferId_Type = SignedId -inline class BufferId(val id: BufferId_Type) : Comparable<BufferId> { +inline class BufferId(val id: BufferId_Type) : Comparable<BufferId>, Serializable { override fun compareTo(other: BufferId) = id.compareTo(other.id) inline fun isValidId() = id > 0 @@ -72,7 +74,7 @@ inline class BufferId(val id: BufferId_Type) : Comparable<BufferId> { typealias IdentityId_Type = SignedId -inline class IdentityId(val id: IdentityId_Type) : Comparable<IdentityId> { +inline class IdentityId(val id: IdentityId_Type) : Comparable<IdentityId>, Serializable { override fun compareTo(other: IdentityId) = id.compareTo(other.id) inline fun isValidId() = id > 0 diff --git a/lib/src/main/java/de/kuschku/libquassel/util/flag/Flag.kt b/lib/src/main/java/de/kuschku/libquassel/util/flag/Flag.kt index ab5ce92ea..c1b40a8a4 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/flag/Flag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/flag/Flag.kt @@ -20,8 +20,9 @@ package de.kuschku.libquassel.util.flag import de.kuschku.libquassel.util.helpers.sum +import java.io.Serializable -interface Flag<T> where T : Enum<T>, T : Flag<T> { +interface Flag<T> : Serializable where T : Enum<T>, T : Flag<T> { val bit: UInt fun toByte() = bit.toByte() fun toChar() = bit.toLong().toChar() @@ -39,7 +40,7 @@ interface Flag<T> where T : Enum<T>, T : Flag<T> { data class Flags<E>( val value: UInt, val values: Array<E>? = null -) : Number(), Comparable<UInt> where E : Enum<E>, E : Flag<E> { +) : Number(), Serializable, Comparable<UInt> where E : Enum<E>, E : Flag<E> { override fun compareTo(other: UInt) = value.compareTo(other) override fun toByte() = value.toByte() @@ -109,27 +110,27 @@ infix fun <T> Flags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Flag<T> } infix fun <T> Flags<T>.or(other: UInt): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value or other) infix fun <T> Flags<T>.or(other: Flag<T>): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value or other.bit) infix fun <T> Flags<T>.or(other: Flags<T>): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value or other.value) infix fun <T> Flags<T>.and(other: UInt): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value and other) infix fun <T> Flags<T>.and(other: Flag<T>): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value and other.bit) infix fun <T> Flags<T>.and(other: Flags<T>): Flags<T> - where T : kotlin.Enum<T>, T : Flag<T> = + where T : Enum<T>, T : Flag<T> = Flags(value and other.value) infix operator fun <T> Flags<T>.plus(other: UInt): Flags<T> diff --git a/lib/src/main/java/de/kuschku/libquassel/util/flag/LongFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/flag/LongFlag.kt index 5aa32e182..ce5deff1e 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/flag/LongFlag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/flag/LongFlag.kt @@ -20,8 +20,9 @@ package de.kuschku.libquassel.util.flag import de.kuschku.libquassel.util.helpers.sum +import java.io.Serializable -interface LongFlag<T> where T : Enum<T>, T : LongFlag<T> { +interface LongFlag<T> : Serializable where T : Enum<T>, T : LongFlag<T> { val bit: ULong fun toByte() = bit.toByte() fun toChar() = bit.toLong().toChar() @@ -39,7 +40,7 @@ interface LongFlag<T> where T : Enum<T>, T : LongFlag<T> { data class LongFlags<E>( val value: ULong, val values: Array<E>? = null -) : Number(), Comparable<ULong> where E : Enum<E>, E : LongFlag<E> { +) : Number(), Serializable, Comparable<ULong> where E : Enum<E>, E : LongFlag<E> { override fun compareTo(other: ULong) = value.compareTo(other) override fun toByte() = value.toByte() @@ -109,27 +110,27 @@ infix fun <T> LongFlags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Lon } infix fun <T> LongFlags<T>.or(other: ULong): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other) infix fun <T> LongFlags<T>.or(other: LongFlag<T>): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other.bit) infix fun <T> LongFlags<T>.or(other: LongFlags<T>): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other.value) infix fun <T> LongFlags<T>.and(other: ULong): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other) infix fun <T> LongFlags<T>.and(other: LongFlag<T>): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other.bit) infix fun <T> LongFlags<T>.and(other: LongFlags<T>): LongFlags<T> - where T : kotlin.Enum<T>, T : LongFlag<T> = + where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other.value) infix operator fun <T> LongFlags<T>.plus(other: ULong): LongFlags<T> diff --git a/lib/src/main/java/de/kuschku/libquassel/util/flag/ShortFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/flag/ShortFlag.kt index 0eacf94f3..8369662f7 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/flag/ShortFlag.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/flag/ShortFlag.kt @@ -20,8 +20,9 @@ package de.kuschku.libquassel.util.flag import de.kuschku.libquassel.util.helpers.sum +import java.io.Serializable -interface ShortFlag<T> where T : Enum<T>, T : ShortFlag<T> { +interface ShortFlag<T> : Serializable where T : Enum<T>, T : ShortFlag<T> { val bit: UShort fun toByte() = bit.toByte() fun toChar() = bit.toLong().toChar() @@ -39,7 +40,7 @@ interface ShortFlag<T> where T : Enum<T>, T : ShortFlag<T> { data class ShortFlags<E>( val value: UShort, val values: Array<E>? = null -) : Number(), Comparable<UShort> where E : Enum<E>, E : ShortFlag<E> { +) : Number(), Serializable, Comparable<UShort> where E : Enum<E>, E : ShortFlag<E> { override fun compareTo(other: UShort) = value.compareTo(other) override fun toByte() = value.toByte() @@ -109,27 +110,27 @@ infix fun <T> ShortFlags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Sh } infix fun <T> ShortFlags<T>.or(other: UShort): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other) infix fun <T> ShortFlags<T>.or(other: ShortFlag<T>): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.bit) infix fun <T> ShortFlags<T>.or(other: ShortFlags<T>): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.value) infix fun <T> ShortFlags<T>.and(other: UShort): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other) infix fun <T> ShortFlags<T>.and(other: ShortFlag<T>): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.bit) infix fun <T> ShortFlags<T>.and(other: ShortFlags<T>): ShortFlags<T> - where T : kotlin.Enum<T>, T : ShortFlag<T> = + where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.value) infix operator fun <T> ShortFlags<T>.plus(other: UShort): ShortFlags<T> diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/models/MessageData.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/models/MessageData.kt index 3233e80b6..7e59e425f 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/models/MessageData.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/models/MessageData.kt @@ -26,6 +26,7 @@ import androidx.room.Index import androidx.room.PrimaryKey import de.kuschku.libquassel.protocol.* import org.threeten.bp.Instant +import java.io.Serializable @Entity(tableName = "message", indices = [Index("bufferId"), Index("ignored")]) data class MessageData( @@ -45,7 +46,7 @@ data class MessageData( var avatarUrl: String, var content: String, var ignored: Boolean -) { +) : Serializable { inline val messageId get() = MsgId(rawMessageId) inline val bufferId diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt index f48c096f3..00dcdc85e 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.viewmodel +import android.os.Bundle import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.MsgId import de.kuschku.libquassel.protocol.NetworkId @@ -27,12 +28,10 @@ import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject open class ChatViewModel : QuasselViewModel() { - val stateReset = BehaviorSubject.create<Unit>() val selectedMessages = BehaviorSubject.createDefault(emptyMap<MsgId, FormattedMessage>()) val bufferSearch = BehaviorSubject.createDefault("") val expandedMessages = BehaviorSubject.createDefault(emptySet<MsgId>()) - val buffer = BehaviorSubject.createDefault(BufferId.MAX_VALUE) - val bufferOpened = PublishSubject.create<Unit>() + val bufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE) val bufferViewConfigId = BehaviorSubject.createDefault(-1) val recentlySentMessages = BehaviorSubject.createDefault(emptyList<CharSequence>()) val showHidden = BehaviorSubject.createDefault(false) @@ -40,6 +39,82 @@ open class ChatViewModel : QuasselViewModel() { val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>()) val selectedBufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE) + val stateReset = BehaviorSubject.create<Unit>() + val bufferOpened = PublishSubject.create<Unit>() + + fun onSaveInstanceState(outState: Bundle) { + outState.putSerializable( + KEY_SELECTED_MESSAGES, + HashMap(selectedMessages.value)) + outState.putString( + KEY_BUFFER_SEARCH, + bufferSearch.value) + outState.putLongArray( + KEY_EXPANDED_MESSAGES, + expandedMessages.value.map(MsgId::id).toLongArray()) + outState.putInt( + KEY_BUFFER_ID, + bufferId.value.id) + outState.putInt( + KEY_BUFFER_VIEW_CONFIG_ID, + bufferViewConfigId.value) + outState.putCharSequenceArray( + KEY_RECENTLY_SENT_MESSAGES, + recentlySentMessages.value.toTypedArray()) + outState.putBoolean( + KEY_SHOW_HIDDEN, + showHidden.value) + outState.putBoolean( + KEY_BUFFER_SEARCH_TEMPORARILY_VISIBLE, + bufferSearchTemporarilyVisible.value) + outState.putSerializable( + KEY_EXPANDED_NETWORKS, + HashMap(expandedNetworks.value)) + outState.putInt( + KEY_SELECTED_BUFFER_ID, + selectedBufferId.value.id) + } + + fun onRestoreInstanceState(savedInstanceState: Bundle) { + if (savedInstanceState.containsKey(KEY_SELECTED_MESSAGES)) { + selectedMessages.onNext( + savedInstanceState.getSerializable(KEY_SELECTED_MESSAGES) as? HashMap<MsgId, FormattedMessage> + ?: emptyMap() + ) + } + + if (savedInstanceState.containsKey(KEY_BUFFER_SEARCH)) + bufferSearch.onNext(savedInstanceState.getString(KEY_BUFFER_SEARCH, null)) + + if (savedInstanceState.containsKey(KEY_EXPANDED_MESSAGES)) + expandedMessages.onNext(savedInstanceState.getLongArray(KEY_EXPANDED_MESSAGES)?.map(::MsgId)?.toSet().orEmpty()) + + if (savedInstanceState.containsKey(KEY_BUFFER_ID)) + bufferId.onNext(BufferId(savedInstanceState.getInt(KEY_BUFFER_ID))) + + if (savedInstanceState.containsKey(KEY_BUFFER_VIEW_CONFIG_ID)) + bufferViewConfigId.onNext(savedInstanceState.getInt(KEY_BUFFER_VIEW_CONFIG_ID)) + + if (savedInstanceState.containsKey(KEY_RECENTLY_SENT_MESSAGES)) + recentlySentMessages.onNext(savedInstanceState.getCharSequenceArray(KEY_RECENTLY_SENT_MESSAGES)?.toList().orEmpty()) + + if (savedInstanceState.containsKey(KEY_SHOW_HIDDEN)) + showHidden.onNext(savedInstanceState.getBoolean(KEY_SHOW_HIDDEN)) + + if (savedInstanceState.containsKey(KEY_BUFFER_SEARCH_TEMPORARILY_VISIBLE)) + bufferSearchTemporarilyVisible.onNext(savedInstanceState.getBoolean(KEY_BUFFER_SEARCH_TEMPORARILY_VISIBLE)) + + if (savedInstanceState.containsKey(KEY_EXPANDED_NETWORKS)) { + expandedNetworks.onNext( + savedInstanceState.getSerializable(KEY_EXPANDED_NETWORKS) as? HashMap<NetworkId, Boolean> + ?: emptyMap() + ) + } + + if (savedInstanceState.containsKey(KEY_SELECTED_BUFFER_ID)) + selectedBufferId.onNext(BufferId(savedInstanceState.getInt(KEY_SELECTED_BUFFER_ID))) + } + fun resetAccount() { bufferViewConfigId.onNext(-1) selectedMessages.onNext(emptyMap()) @@ -65,6 +140,17 @@ open class ChatViewModel : QuasselViewModel() { } companion object { + const val KEY_SELECTED_MESSAGES = "model_chat_selectedMessages" + const val KEY_BUFFER_SEARCH = "model_chat_bufferSearch" + const val KEY_EXPANDED_MESSAGES = "model_chat_expandedMessages" + const val KEY_BUFFER_ID = "model_chat_bufferId" + const val KEY_BUFFER_VIEW_CONFIG_ID = "model_chat_bufferViewConfigId" + const val KEY_RECENTLY_SENT_MESSAGES = "model_chat_recentlySentMessages" + const val KEY_SHOW_HIDDEN = "model_chat_showHidden" + const val KEY_BUFFER_SEARCH_TEMPORARILY_VISIBLE = "model_chat_bufferSearchTemporarilyVisible" + const val KEY_EXPANDED_NETWORKS = "model_chat_expandedNetworks" + const val KEY_SELECTED_BUFFER_ID = "model_chat_selectedBufferId" + const val MAX_RECENT_MESSAGES = 20 } } diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt index 99d2097b4..9c70afe32 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt @@ -19,7 +19,9 @@ package de.kuschku.quasseldroid.viewmodel.data -sealed class Avatar { +import java.io.Serializable + +sealed class Avatar : Serializable { data class NativeAvatar(val url: String) : Avatar() data class IRCCloudAvatar(val url: String) : Avatar() data class GravatarAvatar(val url: String) : Avatar() diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt index 106fd6e16..548320653 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt @@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.viewmodel.data import android.graphics.drawable.Drawable import de.kuschku.quasseldroid.persistence.models.MessageData +import java.io.Serializable class FormattedMessage( val original: MessageData, @@ -38,7 +39,7 @@ class FormattedMessage( val isExpanded: Boolean, val isMarkerLine: Boolean, val hasSpoilers: Boolean -) { +) : Serializable { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt index 720b65592..c17c10dc1 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt @@ -36,7 +36,7 @@ open class ChatViewModelHelper @Inject constructor( }.mapSwitchMap(BufferViewConfig::liveUpdates) } - val network = combineLatest(bufferSyncer, networks, chat.buffer) + val network = combineLatest(bufferSyncer, networks, chat.bufferId) .map { (syncer, networks, buffer) -> Optional.ofNullable(syncer.orNull()?.bufferInfo(buffer)?.let { networks[it.networkId] }) } @@ -45,13 +45,13 @@ open class ChatViewModelHelper @Inject constructor( * An observable of the changes of the markerline, as pairs of `(old, new)` */ val markerLine = session.mapSwitchMap { currentSession -> - chat.buffer.switchMap { currentBuffer -> + chat.bufferId.switchMap { currentBuffer -> // Get a stream of the latest marker line currentSession.bufferSyncer.liveMarkerLine(currentBuffer) } } - val bufferData = combineLatest(session, chat.buffer) + val bufferData = combineLatest(session, chat.bufferId) .switchMap { (sessionOptional, id) -> val session = sessionOptional.orNull() val bufferSyncer = session?.bufferSyncer @@ -116,7 +116,7 @@ open class ChatViewModelHelper @Inject constructor( val bufferDataThrottled = bufferData.distinctUntilChanged().throttleLast(100, TimeUnit.MILLISECONDS) - val nickData: Observable<List<IrcUserItem>> = combineLatest(session, chat.buffer) + val nickData: Observable<List<IrcUserItem>> = combineLatest(session, chat.bufferId) .switchMap { (sessionOptional, buffer) -> val session = sessionOptional.orNull() val bufferSyncer = session?.bufferSyncer diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt index 89f1d1011..a70a4eba4 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt @@ -28,7 +28,7 @@ open class EditorViewModelHelper @Inject constructor( ) : ChatViewModelHelper(chat, quassel) { val rawAutoCompleteData: Observable<Triple<Optional<ISession>, BufferId, Pair<String, IntRange>>> = combineLatest(session, - chat.buffer, + chat.bufferId, editor.lastWord).switchMap { (sessionOptional, id, lastWordWrapper) -> lastWordWrapper .distinctUntilChanged() -- GitLab