mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-12-18 06:32:08 +03:00
Merge pull request #4425 from nextcloud/backport/4251/stable-20.0
[stable-20.0] Showing temporary messages when queued
This commit is contained in:
commit
d556b9fc89
14 changed files with 490 additions and 45 deletions
|
@ -3,15 +3,19 @@
|
||||||
<option name="myName" value="ktlint" />
|
<option name="myName" value="ktlint" />
|
||||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
|
|
@ -95,11 +95,14 @@ import com.nextcloud.talk.ui.dialog.AudioOutputDialog
|
||||||
import com.nextcloud.talk.ui.dialog.MoreCallActionsDialog
|
import com.nextcloud.talk.ui.dialog.MoreCallActionsDialog
|
||||||
import com.nextcloud.talk.users.UserManager
|
import com.nextcloud.talk.users.UserManager
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import com.nextcloud.talk.utils.SpreedFeatures
|
import com.nextcloud.talk.utils.CapabilitiesUtil
|
||||||
|
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
|
||||||
|
import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
|
||||||
import com.nextcloud.talk.utils.DisplayUtils
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom
|
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom
|
||||||
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
|
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
|
||||||
import com.nextcloud.talk.utils.ReceiverFlag
|
import com.nextcloud.talk.utils.ReceiverFlag
|
||||||
|
import com.nextcloud.talk.utils.SpreedFeatures
|
||||||
import com.nextcloud.talk.utils.VibrationUtils.vibrateShort
|
import com.nextcloud.talk.utils.VibrationUtils.vibrateShort
|
||||||
import com.nextcloud.talk.utils.animations.PulseAnimation
|
import com.nextcloud.talk.utils.animations.PulseAnimation
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
|
||||||
|
@ -117,9 +120,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM
|
||||||
import com.nextcloud.talk.utils.CapabilitiesUtil
|
|
||||||
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
|
|
||||||
import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
|
|
||||||
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
|
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
|
||||||
import com.nextcloud.talk.utils.power.PowerManagerUtils
|
import com.nextcloud.talk.utils.power.PowerManagerUtils
|
||||||
import com.nextcloud.talk.utils.registerPermissionHandlerBroadcastReceiver
|
import com.nextcloud.talk.utils.registerPermissionHandlerBroadcastReceiver
|
||||||
|
@ -129,9 +129,9 @@ import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingConfirmStop
|
||||||
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingErrorState
|
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingErrorState
|
||||||
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingStartedState
|
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingStartedState
|
||||||
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingStartingState
|
import com.nextcloud.talk.viewmodels.CallRecordingViewModel.RecordingStartingState
|
||||||
import com.nextcloud.talk.webrtc.WebRTCUtils
|
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper.PeerConnectionObserver
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper.PeerConnectionObserver
|
||||||
|
import com.nextcloud.talk.webrtc.WebRTCUtils
|
||||||
import com.nextcloud.talk.webrtc.WebRtcAudioManager
|
import com.nextcloud.talk.webrtc.WebRtcAudioManager
|
||||||
import com.nextcloud.talk.webrtc.WebRtcAudioManager.AudioDevice
|
import com.nextcloud.talk.webrtc.WebRtcAudioManager.AudioDevice
|
||||||
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper
|
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper
|
||||||
|
|
|
@ -65,6 +65,8 @@ public class TalkMessagesListAdapter<M extends IMessage> extends MessagesListAda
|
||||||
((SystemMessageViewHolder) holder).assignSystemMessageInterface(chatActivity);
|
((SystemMessageViewHolder) holder).assignSystemMessageInterface(chatActivity);
|
||||||
} else if (holder instanceof CallStartedViewHolder) {
|
} else if (holder instanceof CallStartedViewHolder) {
|
||||||
((CallStartedViewHolder) holder).assignCallStartedMessageInterface(chatActivity);
|
((CallStartedViewHolder) holder).assignCallStartedMessageInterface(chatActivity);
|
||||||
|
} else if (holder instanceof TemporaryMessageViewHolder) {
|
||||||
|
((TemporaryMessageViewHolder) holder).assignTemporaryMessageInterface(chatActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onBindViewHolder(holder, position);
|
super.onBindViewHolder(holder, position);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
interface TemporaryMessageInterface {
|
||||||
|
fun editTemporaryMessage(id: Int, newMessage: String)
|
||||||
|
fun deleteTemporaryMessage(id: Int)
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import autodagger.AutoInjector
|
||||||
|
import coil.load
|
||||||
|
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.chat.ChatActivity
|
||||||
|
import com.nextcloud.talk.chat.data.model.ChatMessage
|
||||||
|
import com.nextcloud.talk.databinding.ItemTemporaryMessageBinding
|
||||||
|
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
|
import com.nextcloud.talk.utils.message.MessageUtils
|
||||||
|
import com.stfalcon.chatkit.messages.MessagesListAdapter
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
|
class TemporaryMessageViewHolder(outgoingView: View, payload: Any) :
|
||||||
|
MessagesListAdapter.OutcomingMessageViewHolder<ChatMessage>(outgoingView) {
|
||||||
|
|
||||||
|
private val binding: ItemTemporaryMessageBinding = ItemTemporaryMessageBinding.bind(outgoingView)
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var viewThemeUtils: ViewThemeUtils
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var context: Context
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var messageUtils: MessageUtils
|
||||||
|
|
||||||
|
lateinit var temporaryMessageInterface: TemporaryMessageInterface
|
||||||
|
var isEditing = false
|
||||||
|
|
||||||
|
override fun onBind(message: ChatMessage) {
|
||||||
|
super.onBind(message)
|
||||||
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
|
|
||||||
|
viewThemeUtils.platform.colorImageView(binding.tempMsgEdit, ColorRole.PRIMARY)
|
||||||
|
viewThemeUtils.platform.colorImageView(binding.tempMsgDelete, ColorRole.PRIMARY)
|
||||||
|
|
||||||
|
binding.tempMsgEdit.setOnClickListener {
|
||||||
|
isEditing = !isEditing
|
||||||
|
if (isEditing) {
|
||||||
|
binding.tempMsgEdit.setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(
|
||||||
|
context.resources,
|
||||||
|
R.drawable.ic_check,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
binding.messageEdit.visibility = View.VISIBLE
|
||||||
|
binding.messageEdit.requestFocus()
|
||||||
|
ViewCompat.getWindowInsetsController(binding.root)?.show(WindowInsetsCompat.Type.ime())
|
||||||
|
binding.messageEdit.setText(binding.messageText.text)
|
||||||
|
binding.messageText.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.tempMsgEdit.setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(
|
||||||
|
context.resources,
|
||||||
|
R.drawable.ic_edit,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
binding.messageEdit.visibility = View.GONE
|
||||||
|
binding.messageText.visibility = View.VISIBLE
|
||||||
|
val newMessage = binding.messageEdit.text.toString()
|
||||||
|
message.message = newMessage
|
||||||
|
temporaryMessageInterface.editTemporaryMessage(message.tempMessageId, newMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.tempMsgDelete.setOnClickListener {
|
||||||
|
temporaryMessageInterface.deleteTemporaryMessage(message.tempMessageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parent message handling
|
||||||
|
if (message.parentMessageId != null && message.parentMessageId!! > 0) {
|
||||||
|
processParentMessage(message)
|
||||||
|
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val bgBubbleColor = bubble.resources.getColor(R.color.bg_message_list_incoming_bubble, null)
|
||||||
|
val layout = R.drawable.shape_outcoming_message
|
||||||
|
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||||
|
bgBubbleColor,
|
||||||
|
ResourcesCompat.getColor(bubble.resources, R.color.transparent, null),
|
||||||
|
bgBubbleColor,
|
||||||
|
layout
|
||||||
|
)
|
||||||
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processParentMessage(message: ChatMessage) {
|
||||||
|
if (message.parentMessageId != null && !message.isDeleted) {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
try {
|
||||||
|
val chatActivity = temporaryMessageInterface as ChatActivity
|
||||||
|
val urlForChatting = ApiUtils.getUrlForChat(
|
||||||
|
chatActivity.chatApiVersion,
|
||||||
|
chatActivity.conversationUser?.baseUrl,
|
||||||
|
chatActivity.roomToken
|
||||||
|
)
|
||||||
|
|
||||||
|
val parentChatMessage = withContext(Dispatchers.IO) {
|
||||||
|
chatActivity.chatViewModel.getMessageById(
|
||||||
|
urlForChatting,
|
||||||
|
chatActivity.currentConversation!!,
|
||||||
|
message.parentMessageId!!
|
||||||
|
).first()
|
||||||
|
}
|
||||||
|
|
||||||
|
parentChatMessage!!.activeUser = message.activeUser
|
||||||
|
parentChatMessage.imageUrl?.let {
|
||||||
|
binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
|
||||||
|
val placeholder = context.resources.getDrawable(R.drawable.ic_mimetype_image)
|
||||||
|
binding.messageQuote.quotedMessageImage.setImageDrawable(placeholder)
|
||||||
|
binding.messageQuote.quotedMessageImage.load(it) {
|
||||||
|
addHeader(
|
||||||
|
"Authorization",
|
||||||
|
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
binding.messageQuote.quotedMessageImage.visibility = View.GONE
|
||||||
|
}
|
||||||
|
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
|
||||||
|
?: context.getText(R.string.nc_nick_guest)
|
||||||
|
binding.messageQuote.quotedMessage.text = messageUtils
|
||||||
|
.enrichChatReplyMessageText(
|
||||||
|
binding.messageQuote.quotedMessage.context,
|
||||||
|
parentChatMessage,
|
||||||
|
false,
|
||||||
|
viewThemeUtils
|
||||||
|
)
|
||||||
|
|
||||||
|
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
|
||||||
|
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
|
||||||
|
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
|
||||||
|
|
||||||
|
binding.messageQuote.quotedChatMessageView.setOnClickListener {
|
||||||
|
val chatActivity = temporaryMessageInterface as ChatActivity
|
||||||
|
chatActivity.jumpToQuotedMessage(parentChatMessage)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(TAG, "Error when processing parent message in view holder", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assignTemporaryMessageInterface(temporaryMessageInterface: TemporaryMessageInterface) {
|
||||||
|
this.temporaryMessageInterface = temporaryMessageInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun viewDetached() {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun viewAttached() {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun viewRecycled() {
|
||||||
|
// unused atm
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = TemporaryMessageViewHolder::class.java.simpleName
|
||||||
|
}
|
||||||
|
}
|
|
@ -101,6 +101,8 @@ import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.SystemMessageInterface
|
import com.nextcloud.talk.adapters.messages.SystemMessageInterface
|
||||||
import com.nextcloud.talk.adapters.messages.SystemMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.SystemMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
|
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
|
||||||
|
import com.nextcloud.talk.adapters.messages.TemporaryMessageInterface
|
||||||
|
import com.nextcloud.talk.adapters.messages.TemporaryMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.UnreadNoticeMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.UnreadNoticeMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
|
import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
|
||||||
import com.nextcloud.talk.api.NcApi
|
import com.nextcloud.talk.api.NcApi
|
||||||
|
@ -213,7 +215,8 @@ class ChatActivity :
|
||||||
CommonMessageInterface,
|
CommonMessageInterface,
|
||||||
PreviewMessageInterface,
|
PreviewMessageInterface,
|
||||||
SystemMessageInterface,
|
SystemMessageInterface,
|
||||||
CallStartedMessageInterface {
|
CallStartedMessageInterface,
|
||||||
|
TemporaryMessageInterface {
|
||||||
|
|
||||||
var active = false
|
var active = false
|
||||||
|
|
||||||
|
@ -534,6 +537,37 @@ class ChatActivity :
|
||||||
private fun initObservers() {
|
private fun initObservers() {
|
||||||
Log.d(TAG, "initObservers Called")
|
Log.d(TAG, "initObservers Called")
|
||||||
|
|
||||||
|
messageInputViewModel.messageQueueFlow.observe(this) { list ->
|
||||||
|
list.forEachIndexed { _, qMsg ->
|
||||||
|
Log.d("Julius", "Message recieved: ${qMsg.message}")
|
||||||
|
val temporaryChatMessage = ChatMessage()
|
||||||
|
temporaryChatMessage.jsonMessageId = -3
|
||||||
|
temporaryChatMessage.actorId = "-3"
|
||||||
|
temporaryChatMessage.timestamp = System.currentTimeMillis() / 1000
|
||||||
|
temporaryChatMessage.message = qMsg.message.toString()
|
||||||
|
temporaryChatMessage.tempMessageId = qMsg.id
|
||||||
|
temporaryChatMessage.isTempMessage = true
|
||||||
|
temporaryChatMessage.parentMessageId = qMsg.replyTo!!.toLong()
|
||||||
|
val pos = adapter?.getMessagePositionById(qMsg.replyTo.toString())
|
||||||
|
adapter?.addToStart(temporaryChatMessage, true)
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messageInputViewModel.messageQueueSizeFlow.observe(this) { size ->
|
||||||
|
if (size == 0) {
|
||||||
|
var i = 0
|
||||||
|
var pos = adapter?.getMessagePositionById("-3")
|
||||||
|
while (pos != null && pos > -1) {
|
||||||
|
adapter?.items?.removeAt(pos)
|
||||||
|
i++
|
||||||
|
pos = adapter?.getMessagePositionById("-3")
|
||||||
|
}
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
Log.d("Julius", "End i: $i")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.lifecycleScope.launch {
|
this.lifecycleScope.launch {
|
||||||
chatViewModel.getConversationFlow
|
chatViewModel.getConversationFlow
|
||||||
.onEach { conversationModel ->
|
.onEach { conversationModel ->
|
||||||
|
@ -620,6 +654,7 @@ class ChatActivity :
|
||||||
withCredentials = credentials!!,
|
withCredentials = credentials!!,
|
||||||
withUrl = urlForChatting
|
withUrl = urlForChatting
|
||||||
)
|
)
|
||||||
|
messageInputViewModel.getTempMessagesFromMessageQueue(currentConversation!!.internalId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,6 +1205,17 @@ class ChatActivity :
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
||||||
|
messageHolders.registerContentType(
|
||||||
|
CONTENT_TYPE_TEMP,
|
||||||
|
TemporaryMessageViewHolder::class.java,
|
||||||
|
payload,
|
||||||
|
R.layout.item_temporary_message,
|
||||||
|
TemporaryMessageViewHolder::class.java,
|
||||||
|
payload,
|
||||||
|
R.layout.item_temporary_message,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
messageHolders.registerContentType(
|
messageHolders.registerContentType(
|
||||||
CONTENT_TYPE_SYSTEM_MESSAGE,
|
CONTENT_TYPE_SYSTEM_MESSAGE,
|
||||||
SystemMessageViewHolder::class.java,
|
SystemMessageViewHolder::class.java,
|
||||||
|
@ -2330,8 +2376,8 @@ class ChatActivity :
|
||||||
try {
|
try {
|
||||||
EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString()
|
EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString()
|
||||||
} catch (e: java.lang.IllegalStateException) {
|
} catch (e: java.lang.IllegalStateException) {
|
||||||
|
Log.e(TAG, "setActionBarTitle failed $e")
|
||||||
currentConversation?.displayName
|
currentConversation?.displayName
|
||||||
error(e)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
@ -2445,9 +2491,9 @@ class ChatActivity :
|
||||||
|
|
||||||
if (currentConversation!!.remoteServer != null) {
|
if (currentConversation!!.remoteServer != null) {
|
||||||
val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V3, 2, 1))
|
val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V3, 2, 1))
|
||||||
ncApi!!.getSignalingSettings(
|
ncApi.getSignalingSettings(
|
||||||
credentials,
|
credentials,
|
||||||
ApiUtils.getUrlForSignalingSettings(apiVersion, conversationUser!!.baseUrl, roomToken!!)
|
ApiUtils.getUrlForSignalingSettings(apiVersion, conversationUser!!.baseUrl, roomToken)
|
||||||
).blockingSubscribe(object : Observer<SignalingSettingsOverall> {
|
).blockingSubscribe(object : Observer<SignalingSettingsOverall> {
|
||||||
override fun onSubscribe(d: Disposable) {
|
override fun onSubscribe(d: Disposable) {
|
||||||
// unused atm
|
// unused atm
|
||||||
|
@ -3072,7 +3118,10 @@ class ChatActivity :
|
||||||
|
|
||||||
private fun openMessageActionsDialog(iMessage: IMessage?) {
|
private fun openMessageActionsDialog(iMessage: IMessage?) {
|
||||||
val message = iMessage as ChatMessage
|
val message = iMessage as ChatMessage
|
||||||
if (hasVisibleItems(message) && !isSystemMessage(message)) {
|
if (hasVisibleItems(message) &&
|
||||||
|
!isSystemMessage(message) &&
|
||||||
|
message.id != "-3"
|
||||||
|
) {
|
||||||
MessageActionsDialog(
|
MessageActionsDialog(
|
||||||
this,
|
this,
|
||||||
message,
|
message,
|
||||||
|
@ -3475,6 +3524,7 @@ class ChatActivity :
|
||||||
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
|
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
|
||||||
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1"
|
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1"
|
||||||
CONTENT_TYPE_CALL_STARTED -> message.id == "-2"
|
CONTENT_TYPE_CALL_STARTED -> message.id == "-2"
|
||||||
|
CONTENT_TYPE_TEMP -> message.id == "-3"
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -3626,6 +3676,30 @@ class ChatActivity :
|
||||||
startACall(false, false)
|
startACall(false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun editTemporaryMessage(id: Int, newMessage: String) {
|
||||||
|
messageInputViewModel.editQueuedMessage(currentConversation!!.internalId, id, newMessage)
|
||||||
|
adapter?.notifyDataSetChanged() // TODO optimize this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteTemporaryMessage(id: Int) {
|
||||||
|
messageInputViewModel.removeFromQueue(currentConversation!!.internalId, id)
|
||||||
|
var i = 0
|
||||||
|
val max = messageInputViewModel.messageQueueSizeFlow.value?.plus(1)
|
||||||
|
for (item in adapter?.items!!) {
|
||||||
|
if (i > max!! && max < 1) break
|
||||||
|
if (item.item is ChatMessage &&
|
||||||
|
(item.item as ChatMessage).isTempMessage &&
|
||||||
|
(item.item as ChatMessage).tempMessageId == id
|
||||||
|
) {
|
||||||
|
val index = adapter?.items!!.indexOf(item)
|
||||||
|
adapter?.items!!.removeAt(index)
|
||||||
|
adapter?.notifyItemRemoved(index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun logConversationInfos(methodName: String) {
|
private fun logConversationInfos(methodName: String) {
|
||||||
Log.d(TAG, " |-----------------------------------------------")
|
Log.d(TAG, " |-----------------------------------------------")
|
||||||
Log.d(TAG, " | method: $methodName")
|
Log.d(TAG, " | method: $methodName")
|
||||||
|
@ -3655,6 +3729,7 @@ class ChatActivity :
|
||||||
private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 5
|
private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 5
|
||||||
private const val CONTENT_TYPE_POLL: Byte = 6
|
private const val CONTENT_TYPE_POLL: Byte = 6
|
||||||
private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 7
|
private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 7
|
||||||
|
private const val CONTENT_TYPE_TEMP: Byte = 8
|
||||||
private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200
|
private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200
|
||||||
private const val GET_ROOM_INFO_DELAY_NORMAL: Long = 30000
|
private const val GET_ROOM_INFO_DELAY_NORMAL: Long = 30000
|
||||||
private const val GET_ROOM_INFO_DELAY_LOBBY: Long = 5000
|
private const val GET_ROOM_INFO_DELAY_LOBBY: Long = 5000
|
||||||
|
|
|
@ -73,7 +73,6 @@ import com.nextcloud.talk.utils.text.Spans
|
||||||
import com.otaliastudios.autocomplete.Autocomplete
|
import com.otaliastudios.autocomplete.Autocomplete
|
||||||
import com.stfalcon.chatkit.commons.models.IMessage
|
import com.stfalcon.chatkit.commons.models.IMessage
|
||||||
import com.vanniktech.emoji.EmojiPopup
|
import com.vanniktech.emoji.EmojiPopup
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -144,7 +143,7 @@ class MessageInputFragment : Fragment() {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
chatActivity.messageInputViewModel.restoreMessageQueue(chatActivity.roomToken)
|
chatActivity.messageInputViewModel.restoreMessageQueue(chatActivity.currentConversation!!.internalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -179,18 +178,20 @@ class MessageInputFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
var wasOnline = true
|
var wasOnline: Boolean
|
||||||
networkMonitor.isOnline.onEach { isOnline ->
|
networkMonitor.isOnline
|
||||||
val connectionGained = (!wasOnline && isOnline)
|
.onEach { isOnline ->
|
||||||
wasOnline = !binding.fragmentMessageInputView.isShown
|
wasOnline = !binding.fragmentConnectionLost.isShown
|
||||||
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
|
val connectionGained = (!wasOnline && isOnline)
|
||||||
delay(500)
|
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
|
||||||
handleMessageQueue(isOnline)
|
handleMessageQueue(isOnline)
|
||||||
handleUI(isOnline, connectionGained)
|
handleUI(isOnline, connectionGained)
|
||||||
}.collect()
|
}.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
chatActivity.messageInputViewModel.messageQueueSizeFlow.observe(viewLifecycleOwner) { size ->
|
chatActivity.messageInputViewModel.messageQueueSizeFlow.observe(viewLifecycleOwner) { size ->
|
||||||
|
Log.d("Julius", "MessageQueueSizeFlow recieved: $size")
|
||||||
|
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_queued, size)
|
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_queued, size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -233,7 +234,7 @@ class MessageInputFragment : Fragment() {
|
||||||
binding.fragmentConnectionLost.clearAnimation()
|
binding.fragmentConnectionLost.clearAnimation()
|
||||||
binding.fragmentConnectionLost.visibility = View.GONE
|
binding.fragmentConnectionLost.visibility = View.GONE
|
||||||
binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed))
|
binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed))
|
||||||
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
|
// binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
|
||||||
binding.fragmentConnectionLost.visibility = View.VISIBLE
|
binding.fragmentConnectionLost.visibility = View.VISIBLE
|
||||||
binding.fragmentMessageInputView.attachmentButton.isEnabled = false
|
binding.fragmentMessageInputView.attachmentButton.isEnabled = false
|
||||||
binding.fragmentMessageInputView.recordAudioButton.isEnabled = false
|
binding.fragmentMessageInputView.recordAudioButton.isEnabled = false
|
||||||
|
@ -244,7 +245,7 @@ class MessageInputFragment : Fragment() {
|
||||||
if (isOnline) {
|
if (isOnline) {
|
||||||
chatActivity.messageInputViewModel.switchToMessageQueue(false)
|
chatActivity.messageInputViewModel.switchToMessageQueue(false)
|
||||||
chatActivity.messageInputViewModel.sendAndEmptyMessageQueue(
|
chatActivity.messageInputViewModel.sendAndEmptyMessageQueue(
|
||||||
chatActivity.roomToken,
|
chatActivity.currentConversation!!.internalId,
|
||||||
chatActivity.conversationUser!!.getCredentials(),
|
chatActivity.conversationUser!!.getCredentials(),
|
||||||
ApiUtils.getUrlForChat(
|
ApiUtils.getUrlForChat(
|
||||||
chatActivity.chatApiVersion,
|
chatActivity.chatApiVersion,
|
||||||
|
@ -793,7 +794,7 @@ class MessageInputFragment : Fragment() {
|
||||||
|
|
||||||
private fun sendMessage(message: CharSequence, replyTo: Int?, sendWithoutNotification: Boolean) {
|
private fun sendMessage(message: CharSequence, replyTo: Int?, sendWithoutNotification: Boolean) {
|
||||||
chatActivity.messageInputViewModel.sendChatMessage(
|
chatActivity.messageInputViewModel.sendChatMessage(
|
||||||
chatActivity.roomToken,
|
chatActivity.currentConversation!!.internalId,
|
||||||
chatActivity.conversationUser!!.getCredentials(),
|
chatActivity.conversationUser!!.getCredentials(),
|
||||||
ApiUtils.getUrlForChat(
|
ApiUtils.getUrlForChat(
|
||||||
chatActivity.chatApiVersion,
|
chatActivity.chatApiVersion,
|
||||||
|
|
|
@ -111,7 +111,11 @@ data class ChatMessage(
|
||||||
|
|
||||||
var hiddenByCollapse: Boolean = false,
|
var hiddenByCollapse: Boolean = false,
|
||||||
|
|
||||||
var openWhenDownloaded: Boolean = true
|
var openWhenDownloaded: Boolean = true,
|
||||||
|
|
||||||
|
var isTempMessage: Boolean = false,
|
||||||
|
|
||||||
|
var tempMessageId: Int = -1
|
||||||
|
|
||||||
) : MessageContentType, MessageContentType.Image {
|
) : MessageContentType, MessageContentType.Image {
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,8 @@ class MessageInputViewModel @Inject constructor(
|
||||||
val disposableSet = mutableSetOf<Disposable>()
|
val disposableSet = mutableSetOf<Disposable>()
|
||||||
|
|
||||||
data class QueuedMessage(
|
data class QueuedMessage(
|
||||||
val message: CharSequence? = null,
|
val id: Int,
|
||||||
|
var message: CharSequence? = null,
|
||||||
val displayName: String? = null,
|
val displayName: String? = null,
|
||||||
val replyTo: Int? = null,
|
val replyTo: Int? = null,
|
||||||
val sendWithoutNotification: Boolean? = null
|
val sendWithoutNotification: Boolean? = null
|
||||||
|
@ -124,9 +125,13 @@ class MessageInputViewModel @Inject constructor(
|
||||||
val messageQueueSizeFlow: LiveData<Int>
|
val messageQueueSizeFlow: LiveData<Int>
|
||||||
get() = _messageQueueSizeFlow.asLiveData()
|
get() = _messageQueueSizeFlow.asLiveData()
|
||||||
|
|
||||||
|
private val _messageQueueFlow: MutableLiveData<List<QueuedMessage>> = MutableLiveData()
|
||||||
|
val messageQueueFlow: LiveData<List<QueuedMessage>>
|
||||||
|
get() = _messageQueueFlow
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
fun sendChatMessage(
|
fun sendChatMessage(
|
||||||
roomToken: String,
|
internalId: String,
|
||||||
credentials: String,
|
credentials: String,
|
||||||
url: String,
|
url: String,
|
||||||
message: CharSequence,
|
message: CharSequence,
|
||||||
|
@ -135,9 +140,13 @@ class MessageInputViewModel @Inject constructor(
|
||||||
sendWithoutNotification: Boolean
|
sendWithoutNotification: Boolean
|
||||||
) {
|
) {
|
||||||
if (isQueueing) {
|
if (isQueueing) {
|
||||||
messageQueue.add(QueuedMessage(message, displayName, replyTo, sendWithoutNotification))
|
val tempID = System.currentTimeMillis().toInt()
|
||||||
dataStore.saveMessageQueue(roomToken, messageQueue)
|
val qMsg = QueuedMessage(tempID, message, displayName, replyTo, sendWithoutNotification)
|
||||||
|
messageQueue = dataStore.getMessageQueue(internalId)
|
||||||
|
messageQueue.add(qMsg)
|
||||||
|
dataStore.saveMessageQueue(internalId, messageQueue)
|
||||||
_messageQueueSizeFlow.update { messageQueue.size }
|
_messageQueueSizeFlow.update { messageQueue.size }
|
||||||
|
_messageQueueFlow.postValue(listOf(qMsg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,17 +251,16 @@ class MessageInputViewModel @Inject constructor(
|
||||||
_getRecordingTime.postValue(time)
|
_getRecordingTime.postValue(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendAndEmptyMessageQueue(roomToken: String, credentials: String, url: String) {
|
fun sendAndEmptyMessageQueue(internalId: String, credentials: String, url: String) {
|
||||||
if (isQueueing) return
|
if (isQueueing) return
|
||||||
messageQueue.clear()
|
messageQueue.clear()
|
||||||
|
|
||||||
val queue = dataStore.getMessageQueue(roomToken)
|
val queue = dataStore.getMessageQueue(internalId)
|
||||||
dataStore.saveMessageQueue(roomToken, null) // empties the queue
|
dataStore.saveMessageQueue(internalId, null) // empties the queue
|
||||||
while (queue.size > 0) {
|
while (queue.size > 0) {
|
||||||
val msg = queue.removeFirst()
|
val msg = queue.removeAt(0)
|
||||||
sleep(DELAY_BETWEEN_QUEUED_MESSAGES)
|
|
||||||
sendChatMessage(
|
sendChatMessage(
|
||||||
roomToken,
|
internalId,
|
||||||
credentials,
|
credentials,
|
||||||
url,
|
url,
|
||||||
msg.message!!,
|
msg.message!!,
|
||||||
|
@ -260,20 +268,55 @@ class MessageInputViewModel @Inject constructor(
|
||||||
msg.replyTo!!,
|
msg.replyTo!!,
|
||||||
msg.sendWithoutNotification!!
|
msg.sendWithoutNotification!!
|
||||||
)
|
)
|
||||||
|
sleep(DELAY_BETWEEN_QUEUED_MESSAGES)
|
||||||
}
|
}
|
||||||
|
_messageQueueSizeFlow.tryEmit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTempMessagesFromMessageQueue(internalId: String) {
|
||||||
|
val queue = dataStore.getMessageQueue(internalId)
|
||||||
|
val list = mutableListOf<QueuedMessage>()
|
||||||
|
for (msg in queue) {
|
||||||
|
Log.d("Julius", "Msg: ${msg.message}")
|
||||||
|
list.add(msg)
|
||||||
|
}
|
||||||
|
_messageQueueFlow.postValue(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun switchToMessageQueue(shouldQueue: Boolean) {
|
fun switchToMessageQueue(shouldQueue: Boolean) {
|
||||||
isQueueing = shouldQueue
|
isQueueing = shouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restoreMessageQueue(roomToken: String) {
|
fun restoreMessageQueue(internalId: String) {
|
||||||
messageQueue = dataStore.getMessageQueue(roomToken)
|
messageQueue = dataStore.getMessageQueue(internalId)
|
||||||
_messageQueueSizeFlow.tryEmit(messageQueue.size)
|
_messageQueueSizeFlow.tryEmit(messageQueue.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeFromQueue(internalId: String, id: Int) {
|
||||||
|
val queue = dataStore.getMessageQueue(internalId)
|
||||||
|
for (qMsg in queue) {
|
||||||
|
if (qMsg.id == id) {
|
||||||
|
queue.remove(qMsg)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataStore.saveMessageQueue(internalId, queue)
|
||||||
|
_messageQueueSizeFlow.tryEmit(queue.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun editQueuedMessage(internalId: String, id: Int, newMessage: String) {
|
||||||
|
val queue = dataStore.getMessageQueue(internalId)
|
||||||
|
for (qMsg in queue) {
|
||||||
|
if (qMsg.id == id) {
|
||||||
|
qMsg.message = newMessage
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataStore.saveMessageQueue(internalId, queue)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = MessageInputViewModel::class.java.simpleName
|
private val TAG = MessageInputViewModel::class.java.simpleName
|
||||||
private const val DELAY_BETWEEN_QUEUED_MESSAGES: Long = 100
|
private const val DELAY_BETWEEN_QUEUED_MESSAGES: Long = 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -73,6 +74,7 @@ class NetworkMonitorImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.conflate()
|
.conflate()
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||||
import com.nextcloud.talk.models.json.push.PushConfigurationState;
|
import com.nextcloud.talk.models.json.push.PushConfigurationState;
|
||||||
import com.nextcloud.talk.users.UserManager;
|
import com.nextcloud.talk.users.UserManager;
|
||||||
import com.nextcloud.talk.utils.ApiUtils;
|
import com.nextcloud.talk.utils.ApiUtils;
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||||
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
|
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
|
||||||
|
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
|
@ -53,6 +54,8 @@ public class AccountRemovalWorker extends Worker {
|
||||||
|
|
||||||
@Inject ArbitraryStorageManager arbitraryStorageManager;
|
@Inject ArbitraryStorageManager arbitraryStorageManager;
|
||||||
|
|
||||||
|
@Inject AppPreferences appPreferences;
|
||||||
|
|
||||||
@Inject Retrofit retrofit;
|
@Inject Retrofit retrofit;
|
||||||
|
|
||||||
@Inject OkHttpClient okHttpClient;
|
@Inject OkHttpClient okHttpClient;
|
||||||
|
@ -193,6 +196,7 @@ public class AccountRemovalWorker extends Worker {
|
||||||
if (user.getId() != null) {
|
if (user.getId() != null) {
|
||||||
String username = user.getUsername();
|
String username = user.getUsername();
|
||||||
try {
|
try {
|
||||||
|
appPreferences.deleteAllMessageQueuesFor(user.getUserId());
|
||||||
userManager.deleteUser(user.getId());
|
userManager.deleteUser(user.getId());
|
||||||
Log.d(TAG, "deleted user: " + username);
|
Log.d(TAG, "deleted user: " + username);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
|
@ -176,6 +176,8 @@ public interface AppPreferences {
|
||||||
|
|
||||||
List<MessageInputViewModel.QueuedMessage> getMessageQueue(String internalConversationId);
|
List<MessageInputViewModel.QueuedMessage> getMessageQueue(String internalConversationId);
|
||||||
|
|
||||||
|
void deleteAllMessageQueuesFor(String userId);
|
||||||
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,7 +484,10 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
|
||||||
var queueStr = ""
|
var queueStr = ""
|
||||||
queue?.let {
|
queue?.let {
|
||||||
for (msg in queue) {
|
for (msg in queue) {
|
||||||
val msgStr = "${msg.message},${msg.replyTo},${msg.displayName},${msg.sendWithoutNotification}^"
|
val msgStr = "${msg.id},${msg.message},${msg.replyTo},${msg.displayName},${
|
||||||
|
msg
|
||||||
|
.sendWithoutNotification
|
||||||
|
}^"
|
||||||
queueStr += msgStr
|
queueStr += msgStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,12 +507,13 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
|
||||||
try {
|
try {
|
||||||
if (msgStr.isNotEmpty()) {
|
if (msgStr.isNotEmpty()) {
|
||||||
val msgArray = msgStr.split(",")
|
val msgArray = msgStr.split(",")
|
||||||
|
val id = msgArray[ID].toInt()
|
||||||
val message = msgArray[MESSAGE_INDEX]
|
val message = msgArray[MESSAGE_INDEX]
|
||||||
val replyTo = msgArray[REPLY_TO_INDEX].toInt()
|
val replyTo = msgArray[REPLY_TO_INDEX].toInt()
|
||||||
val displayName = msgArray[DISPLY_NAME_INDEX]
|
val displayName = msgArray[DISPLAY_NAME_INDEX]
|
||||||
val silent = msgArray[SILENT_INDEX].toBoolean()
|
val silent = msgArray[SILENT_INDEX].toBoolean()
|
||||||
|
|
||||||
val qMsg = MessageInputViewModel.QueuedMessage(message, displayName, replyTo, silent)
|
val qMsg = MessageInputViewModel.QueuedMessage(id, message, displayName, replyTo, silent)
|
||||||
queue.add(qMsg)
|
queue.add(qMsg)
|
||||||
}
|
}
|
||||||
} catch (e: IndexOutOfBoundsException) {
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
@ -520,6 +524,26 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deleteAllMessageQueuesFor(userId: String) {
|
||||||
|
runBlocking {
|
||||||
|
async {
|
||||||
|
val keyList = mutableListOf<Preferences.Key<*>>()
|
||||||
|
val preferencesMap = context.dataStore.data.first().asMap()
|
||||||
|
for (preference in preferencesMap) {
|
||||||
|
if (preference.key.name.contains("$userId@")) {
|
||||||
|
keyList.add(preference.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in keyList) {
|
||||||
|
context.dataStore.edit {
|
||||||
|
it.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun clear() {}
|
override fun clear() {}
|
||||||
|
|
||||||
private suspend fun writeString(key: String, value: String) =
|
private suspend fun writeString(key: String, value: String) =
|
||||||
|
@ -572,10 +596,11 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
|
||||||
@Suppress("UnusedPrivateProperty")
|
@Suppress("UnusedPrivateProperty")
|
||||||
private val TAG = AppPreferencesImpl::class.simpleName
|
private val TAG = AppPreferencesImpl::class.simpleName
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
private const val MESSAGE_INDEX: Int = 0
|
private const val ID: Int = 0
|
||||||
private const val REPLY_TO_INDEX: Int = 1
|
private const val MESSAGE_INDEX: Int = 1
|
||||||
private const val DISPLY_NAME_INDEX: Int = 2
|
private const val REPLY_TO_INDEX: Int = 2
|
||||||
private const val SILENT_INDEX: Int = 3
|
private const val DISPLAY_NAME_INDEX: Int = 3
|
||||||
|
private const val SILENT_INDEX: Int = 4
|
||||||
const val PROXY_TYPE = "proxy_type"
|
const val PROXY_TYPE = "proxy_type"
|
||||||
const val PROXY_SERVER = "proxy_server"
|
const val PROXY_SERVER = "proxy_server"
|
||||||
const val PROXY_HOST = "proxy_host"
|
const val PROXY_HOST = "proxy_host"
|
||||||
|
|
78
app/src/main/res/layout/item_temporary_message.xml
Normal file
78
app/src/main/res/layout/item_temporary_message.xml
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Nextcloud Talk - Android Client
|
||||||
|
~
|
||||||
|
~ SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||||
|
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
<RelativeLayout 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:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_centerVertical="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/temp_msg_edit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_edit"
|
||||||
|
android:paddingHorizontal="@dimen/standard_half_padding"
|
||||||
|
android:layout_marginEnd="@dimen/standard_quarter_margin" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/temp_msg_delete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_delete"
|
||||||
|
android:paddingHorizontal="@dimen/standard_half_padding"
|
||||||
|
android:layout_marginStart="@dimen/standard_quarter_margin" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.flexbox.FlexboxLayout
|
||||||
|
android:id="@id/bubble"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginStart="@dimen/message_outcoming_bubble_margin_left"
|
||||||
|
app:alignContent="stretch"
|
||||||
|
app:alignItems="stretch"
|
||||||
|
app:flexWrap="wrap"
|
||||||
|
app:justifyContent="flex_end">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/message_quote"
|
||||||
|
layout="@layout/item_message_quote"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
<androidx.emoji2.widget.EmojiTextView
|
||||||
|
android:id="@id/messageText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColorHighlight="@color/nc_grey"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
tools:text="Talk to you later!" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/message_edit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.flexbox.FlexboxLayout>
|
||||||
|
</RelativeLayout>
|
Loading…
Reference in a new issue