Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-20.0] Queuing offline messages to be sent online #4205

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import com.nextcloud.talk.utils.text.Spans
import com.otaliastudios.autocomplete.Autocomplete
import com.stfalcon.chatkit.commons.models.IMessage
import com.vanniktech.emoji.EmojiPopup
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -141,6 +142,11 @@ class MessageInputFragment : Fragment() {
saveState()
}

override fun onResume() {
super.onResume()
chatActivity.messageInputViewModel.restoreMessageQueue(chatActivity.roomToken)
}

override fun onDestroyView() {
super.onDestroyView()
if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) {
Expand Down Expand Up @@ -178,12 +184,19 @@ class MessageInputFragment : Fragment() {
val connectionGained = (!wasOnline && isOnline)
wasOnline = !binding.fragmentMessageInputView.isShown
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")

// FIXME timeout exception - maybe something to do with the room?
// handleMessageQueue(isOnline)
delay(500)
handleMessageQueue(isOnline)
handleUI(isOnline, connectionGained)
}.collect()
}

chatActivity.messageInputViewModel.messageQueueSizeFlow.observe(viewLifecycleOwner) { size ->
if (size > 0) {
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_queued, size)
} else {
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
}
}
}

private fun handleUI(isOnline: Boolean, connectionGained: Boolean) {
Expand Down Expand Up @@ -220,12 +233,9 @@ class MessageInputFragment : Fragment() {
binding.fragmentConnectionLost.clearAnimation()
binding.fragmentConnectionLost.visibility = View.GONE
binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed))
binding.fragmentConnectionLost.text =
getString(R.string.connection_lost_sent_messages_are_queued)
binding.fragmentConnectionLost.visibility = View.VISIBLE
binding.fragmentMessageInputView.attachmentButton.isEnabled = false
binding.fragmentMessageInputView.recordAudioButton.isEnabled = false
binding.fragmentMessageInputView.messageInput.isEnabled = false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
import com.nextcloud.talk.chat.data.io.AudioRecorderManager
import com.nextcloud.talk.chat.data.io.MediaPlayerManager
Expand All @@ -26,6 +27,9 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import java.lang.Thread.sleep
import javax.inject.Inject

class MessageInputViewModel @Inject constructor(
Expand All @@ -51,7 +55,7 @@ class MessageInputViewModel @Inject constructor(
)

private var isQueueing: Boolean = false
private val messageQueue: MutableList<QueuedMessage> = mutableListOf()
private var messageQueue: MutableList<QueuedMessage> = mutableListOf()

override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
Expand All @@ -76,9 +80,6 @@ class MessageInputViewModel @Inject constructor(
mediaPlayerManager.handleOnStop()
}

companion object {
private val TAG = MessageInputViewModel::class.java.simpleName
}
val getAudioFocusChange: LiveData<AudioFocusRequestManager.ManagerState>
get() = audioFocusRequestManager.getManagerState

Expand Down Expand Up @@ -119,6 +120,10 @@ class MessageInputViewModel @Inject constructor(
val isVoicePreviewPlaying: LiveData<Boolean>
get() = _isVoicePreviewPlaying

private val _messageQueueSizeFlow = MutableStateFlow(messageQueue.size)
val messageQueueSizeFlow: LiveData<Int>
get() = _messageQueueSizeFlow.asLiveData()

@Suppress("LongParameterList")
fun sendChatMessage(
roomToken: String,
Expand All @@ -132,6 +137,7 @@ class MessageInputViewModel @Inject constructor(
if (isQueueing) {
messageQueue.add(QueuedMessage(message, displayName, replyTo, sendWithoutNotification))
dataStore.saveMessageQueue(roomToken, messageQueue)
_messageQueueSizeFlow.update { messageQueue.size }
return
}

Expand Down Expand Up @@ -244,6 +250,7 @@ class MessageInputViewModel @Inject constructor(
dataStore.saveMessageQueue(roomToken, null) // empties the queue
while (queue.size > 0) {
val msg = queue.removeFirst()
sleep(DELAY_BETWEEN_QUEUED_MESSAGES)
sendChatMessage(
roomToken,
credentials,
Expand All @@ -259,4 +266,14 @@ class MessageInputViewModel @Inject constructor(
fun switchToMessageQueue(shouldQueue: Boolean) {
isQueueing = shouldQueue
}

fun restoreMessageQueue(roomToken: String) {
messageQueue = dataStore.getMessageQueue(roomToken)
_messageQueueSizeFlow.tryEmit(messageQueue.size)
}

companion object {
private val TAG = MessageInputViewModel::class.java.simpleName
private const val DELAY_BETWEEN_QUEUED_MESSAGES: Long = 100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
var queueStr = ""
queue?.let {
for (msg in queue) {
val msgStr = "[${msg.message},${msg.replyTo},${msg.displayName},${msg.sendWithoutNotification}]"
val msgStr = "${msg.message},${msg.replyTo},${msg.displayName},${msg.sendWithoutNotification}^"
queueStr += msgStr
}
}
Expand All @@ -500,18 +500,20 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
val queue: MutableList<MessageInputViewModel.QueuedMessage> = mutableListOf()
if (queueStr.isEmpty()) return queue

for (msgStr in queueStr.split("]")) {
for (msgStr in queueStr.split("^")) {
try {
val msgArray = msgStr.replace("[", "").split(",")
val message = msgArray[MESSAGE_INDEX]
val replyTo = msgArray[REPLY_TO_INDEX].toInt()
val displayName = msgArray[DISPLY_NAME_INDEX]
val silent = msgArray[SILENT_INDEX].toBoolean()

val qMsg = MessageInputViewModel.QueuedMessage(message, displayName, replyTo, silent)
queue.add(qMsg)
if (msgStr.isNotEmpty()) {
val msgArray = msgStr.split(",")
val message = msgArray[MESSAGE_INDEX]
val replyTo = msgArray[REPLY_TO_INDEX].toInt()
val displayName = msgArray[DISPLY_NAME_INDEX]
val silent = msgArray[SILENT_INDEX].toBoolean()

val qMsg = MessageInputViewModel.QueuedMessage(message, displayName, replyTo, silent)
queue.add(qMsg)
}
} catch (e: IndexOutOfBoundsException) {
Log.e(TAG, "Message string: $msgStr\n $e")
Log.e(TAG, "Message string: $msgStr\n Queue String: $queueStr \n$e")
}
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ How to translate with transifex:
<string name="show_banned_participants">Show banned participants</string>
<string name="bans_list">Bans list</string>
<string name="connection_lost_sent_messages_are_queued">Connection lost - Sent messages are queued</string>
<string name="connection_lost_queued">Connection lost - %1$d are queued</string>
<string name="connection_established">Connection established</string>
<string name="message_deleted_by_you">Message deleted by you</string>
<string name="unban">Unban</string>
Expand Down
Loading