mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Merge pull request #3293 from nextcloud/feature/3073/groupSystemMessagesFromCalls
Feature/3073/group system messages from calls
This commit is contained in:
commit
3e44e5a63f
@ -0,0 +1,8 @@
|
|||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
|
|
||||||
|
interface SystemMessageInterface {
|
||||||
|
fun expandSystemMessage(chatMessage: ChatMessage)
|
||||||
|
fun collapseSystemMessages()
|
||||||
|
}
|
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk application
|
|
||||||
*
|
|
||||||
* @author Mario Danic
|
|
||||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.nextcloud.talk.adapters.messages;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.text.SpannableString;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import com.nextcloud.talk.R;
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage;
|
|
||||||
import com.nextcloud.talk.utils.DateUtils;
|
|
||||||
import com.nextcloud.talk.utils.DisplayUtils;
|
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
|
||||||
import com.stfalcon.chatkit.messages.MessageHolders;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import autodagger.AutoInjector;
|
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication.class)
|
|
||||||
public class SystemMessageViewHolder extends MessageHolders.IncomingTextMessageViewHolder<ChatMessage> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AppPreferences appPreferences;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
Context context;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
DateUtils dateUtils;
|
|
||||||
|
|
||||||
protected ViewGroup background;
|
|
||||||
|
|
||||||
public SystemMessageViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
|
||||||
background = itemView.findViewById(R.id.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(ChatMessage message) {
|
|
||||||
super.onBind(message);
|
|
||||||
|
|
||||||
Resources resources = itemView.getResources();
|
|
||||||
int pressedColor;
|
|
||||||
int mentionColor;
|
|
||||||
|
|
||||||
pressedColor = resources.getColor(R.color.bg_message_list_incoming_bubble);
|
|
||||||
mentionColor = resources.getColor(R.color.textColorMaxContrast);
|
|
||||||
|
|
||||||
Drawable bubbleDrawable = DisplayUtils.getMessageSelector(resources.getColor(R.color.transparent),
|
|
||||||
resources.getColor(R.color.transparent),
|
|
||||||
pressedColor,
|
|
||||||
R.drawable.shape_grouped_incoming_message);
|
|
||||||
ViewCompat.setBackground(background, bubbleDrawable);
|
|
||||||
|
|
||||||
Spannable messageString = new SpannableString(message.getText());
|
|
||||||
|
|
||||||
if (message.getMessageParameters() != null && message.getMessageParameters().size() > 0) {
|
|
||||||
for (String key : message.getMessageParameters().keySet()) {
|
|
||||||
Map<String, String> individualMap = message.getMessageParameters().get(key);
|
|
||||||
|
|
||||||
if (individualMap != null && individualMap.containsKey("name")) {
|
|
||||||
String searchText;
|
|
||||||
if ("user".equals(individualMap.get("type")) ||
|
|
||||||
"guest".equals(individualMap.get("type")) ||
|
|
||||||
"call".equals(individualMap.get("type"))
|
|
||||||
) {
|
|
||||||
searchText = "@" + individualMap.get("name");
|
|
||||||
} else {
|
|
||||||
searchText = individualMap.get("name");
|
|
||||||
}
|
|
||||||
messageString = DisplayUtils.searchAndColor(messageString, searchText, mentionColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text.setText(messageString);
|
|
||||||
|
|
||||||
if (time != null) {
|
|
||||||
time.setText(dateUtils.getLocalTimeStringFromTimestamp(message.getTimestamp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
itemView.setTag(R.string.replyable_message_view_tag, message.getReplyable());
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import autodagger.AutoInjector
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||||
|
import com.nextcloud.talk.databinding.ItemSystemMessageBinding
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
|
import com.nextcloud.talk.utils.DateUtils
|
||||||
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import com.stfalcon.chatkit.messages.MessageHolders
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
|
class SystemMessageViewHolder(itemView: View) : MessageHolders.IncomingTextMessageViewHolder<ChatMessage>(itemView) {
|
||||||
|
|
||||||
|
private val binding: ItemSystemMessageBinding = ItemSystemMessageBinding.bind(itemView)
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var appPreferences: AppPreferences? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var context: Context? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var dateUtils: DateUtils? = null
|
||||||
|
protected var background: ViewGroup
|
||||||
|
|
||||||
|
lateinit var systemMessageInterface: SystemMessageInterface
|
||||||
|
|
||||||
|
init {
|
||||||
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
|
background = itemView.findViewById(R.id.container)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onBind(message: ChatMessage) {
|
||||||
|
super.onBind(message)
|
||||||
|
val resources = itemView.resources
|
||||||
|
val pressedColor: Int = resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||||
|
val mentionColor: Int = resources.getColor(R.color.textColorMaxContrast)
|
||||||
|
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||||
|
resources.getColor(R.color.transparent),
|
||||||
|
resources.getColor(R.color.transparent),
|
||||||
|
pressedColor,
|
||||||
|
R.drawable.shape_grouped_incoming_message
|
||||||
|
)
|
||||||
|
ViewCompat.setBackground(background, bubbleDrawable)
|
||||||
|
var messageString: Spannable = SpannableString(message.text)
|
||||||
|
if (message.messageParameters != null && message.messageParameters!!.size > 0) {
|
||||||
|
for (key in message.messageParameters!!.keys) {
|
||||||
|
val individualMap: Map<String?, String?>? = message.messageParameters!![key]
|
||||||
|
if (individualMap != null && individualMap.containsKey("name")) {
|
||||||
|
var searchText: String? = if ("user" == individualMap["type"] ||
|
||||||
|
"guest" == individualMap["type"] ||
|
||||||
|
"call" == individualMap["type"]
|
||||||
|
) {
|
||||||
|
"@" + individualMap["name"]
|
||||||
|
} else {
|
||||||
|
individualMap["name"]
|
||||||
|
}
|
||||||
|
messageString = DisplayUtils.searchAndColor(messageString, searchText, mentionColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.systemMessageLayout.visibility = View.VISIBLE
|
||||||
|
binding.similarMessagesHint.visibility = View.GONE
|
||||||
|
if (message.expandableParent) {
|
||||||
|
binding.expandCollapseIcon.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
if (!message.isExpanded) {
|
||||||
|
val similarMessages = String.format(
|
||||||
|
sharedApplication!!.resources.getString(R.string.see_similar_system_messages),
|
||||||
|
message.expandableChildrenAmount
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.messageText.text = messageString
|
||||||
|
binding.similarMessagesHint.visibility = View.VISIBLE
|
||||||
|
binding.similarMessagesHint.text = similarMessages
|
||||||
|
|
||||||
|
binding.expandCollapseIcon.setImageDrawable(
|
||||||
|
ContextCompat.getDrawable(context!!, R.drawable.baseline_unfold_more_24)
|
||||||
|
)
|
||||||
|
binding.systemMessageLayout.setOnClickListener { systemMessageInterface.expandSystemMessage(message) }
|
||||||
|
binding.messageText.setOnClickListener { systemMessageInterface.expandSystemMessage(message) }
|
||||||
|
} else {
|
||||||
|
binding.messageText.text = messageString
|
||||||
|
binding.similarMessagesHint.visibility = View.GONE
|
||||||
|
binding.similarMessagesHint.text = ""
|
||||||
|
|
||||||
|
binding.expandCollapseIcon.setImageDrawable(
|
||||||
|
ContextCompat.getDrawable(context!!, R.drawable.baseline_unfold_less_24)
|
||||||
|
)
|
||||||
|
binding.systemMessageLayout.setOnClickListener { systemMessageInterface.collapseSystemMessages() }
|
||||||
|
binding.messageText.setOnClickListener { systemMessageInterface.collapseSystemMessages() }
|
||||||
|
}
|
||||||
|
} else if (message.hiddenByCollapse) {
|
||||||
|
binding.systemMessageLayout.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.expandCollapseIcon.visibility = View.GONE
|
||||||
|
binding.messageText.text = messageString
|
||||||
|
binding.expandCollapseIcon.setImageDrawable(null)
|
||||||
|
binding.systemMessageLayout.setOnClickListener(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.expandableParent && message.lastItemOfExpandableGroup != 0) {
|
||||||
|
binding.systemMessageLayout.setOnClickListener { systemMessageInterface.collapseSystemMessages() }
|
||||||
|
binding.messageText.setOnClickListener { systemMessageInterface.collapseSystemMessages() }
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.messageTime.text = dateUtils!!.getLocalTimeStringFromTimestamp(message.timestamp)
|
||||||
|
itemView.setTag(R.string.replyable_message_view_tag, message.replyable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assignSystemMessageInterface(systemMessageInterface: SystemMessageInterface) {
|
||||||
|
this.systemMessageInterface = systemMessageInterface
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,9 @@ public class TalkMessagesListAdapter<M extends IMessage> extends MessagesListAda
|
|||||||
} else if (holder instanceof PreviewMessageViewHolder) {
|
} else if (holder instanceof PreviewMessageViewHolder) {
|
||||||
((PreviewMessageViewHolder) holder).assignPreviewMessageInterface(chatActivity);
|
((PreviewMessageViewHolder) holder).assignPreviewMessageInterface(chatActivity);
|
||||||
((PreviewMessageViewHolder) holder).assignCommonMessageInterface(chatActivity);
|
((PreviewMessageViewHolder) holder).assignCommonMessageInterface(chatActivity);
|
||||||
|
|
||||||
|
} else if (holder instanceof SystemMessageViewHolder) {
|
||||||
|
((SystemMessageViewHolder) holder).assignSystemMessageInterface(chatActivity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ import com.nextcloud.talk.adapters.messages.OutcomingTextMessageViewHolder
|
|||||||
import com.nextcloud.talk.adapters.messages.OutcomingVoiceMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.OutcomingVoiceMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.PreviewMessageInterface
|
import com.nextcloud.talk.adapters.messages.PreviewMessageInterface
|
||||||
import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
|
||||||
|
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.UnreadNoticeMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.UnreadNoticeMessageViewHolder
|
||||||
@ -264,7 +265,8 @@ class ChatActivity :
|
|||||||
ContentChecker<ChatMessage>,
|
ContentChecker<ChatMessage>,
|
||||||
VoiceMessageInterface,
|
VoiceMessageInterface,
|
||||||
CommonMessageInterface,
|
CommonMessageInterface,
|
||||||
PreviewMessageInterface {
|
PreviewMessageInterface,
|
||||||
|
SystemMessageInterface {
|
||||||
|
|
||||||
var active = false
|
var active = false
|
||||||
|
|
||||||
@ -1891,6 +1893,45 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun collapseSystemMessages() {
|
||||||
|
adapter?.items?.forEach {
|
||||||
|
if (it.item is ChatMessage) {
|
||||||
|
val chatMessage = it.item as ChatMessage
|
||||||
|
if (isChildOfExpandableSystemMessage(chatMessage)) {
|
||||||
|
chatMessage.hiddenByCollapse = true
|
||||||
|
}
|
||||||
|
chatMessage.isExpanded = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isChildOfExpandableSystemMessage(chatMessage: ChatMessage): Boolean {
|
||||||
|
return isSystemMessage(chatMessage) &&
|
||||||
|
!chatMessage.expandableParent &&
|
||||||
|
chatMessage.lastItemOfExpandableGroup != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun expandSystemMessage(chatMessageToExpand: ChatMessage) {
|
||||||
|
adapter?.items?.forEach {
|
||||||
|
if (it.item is ChatMessage) {
|
||||||
|
val belongsToGroupToExpand =
|
||||||
|
(it.item as ChatMessage).lastItemOfExpandableGroup == chatMessageToExpand.lastItemOfExpandableGroup
|
||||||
|
|
||||||
|
if (belongsToGroupToExpand) {
|
||||||
|
(it.item as ChatMessage).hiddenByCollapse = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chatMessageToExpand.isExpanded = true
|
||||||
|
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("LongLogTag")
|
@SuppressLint("LongLogTag")
|
||||||
private fun downloadFileToCache(message: ChatMessage) {
|
private fun downloadFileToCache(message: ChatMessage) {
|
||||||
message.isDownloadingVoiceMessage = true
|
message.isDownloadingVoiceMessage = true
|
||||||
@ -3085,7 +3126,14 @@ class ChatActivity :
|
|||||||
Log.d(TAG, "pullChatMessages - HTTP_CODE_OK.")
|
Log.d(TAG, "pullChatMessages - HTTP_CODE_OK.")
|
||||||
|
|
||||||
val chatOverall = response.body() as ChatOverall?
|
val chatOverall = response.body() as ChatOverall?
|
||||||
val chatMessageList = handleSystemMessages(chatOverall?.ocs!!.data!!)
|
|
||||||
|
var chatMessageList = chatOverall?.ocs!!.data!!
|
||||||
|
|
||||||
|
chatMessageList = handleSystemMessages(chatMessageList)
|
||||||
|
|
||||||
|
determinePreviousMessageIds(chatMessageList)
|
||||||
|
|
||||||
|
handleExpandableSystemMessages(chatMessageList)
|
||||||
|
|
||||||
processHeaderChatLastGiven(response, lookIntoFuture)
|
processHeaderChatLastGiven(response, lookIntoFuture)
|
||||||
|
|
||||||
@ -3100,6 +3148,8 @@ class ChatActivity :
|
|||||||
processMessagesFromTheFuture(chatMessageList)
|
processMessagesFromTheFuture(chatMessageList)
|
||||||
} else {
|
} else {
|
||||||
processMessagesNotFromTheFuture(chatMessageList)
|
processMessagesNotFromTheFuture(chatMessageList)
|
||||||
|
|
||||||
|
collapseSystemMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
val newXChatLastCommonRead = response.headers()["X-Chat-Last-Common-Read"]?.let {
|
val newXChatLastCommonRead = response.headers()["X-Chat-Last-Common-Read"]?.let {
|
||||||
@ -3123,6 +3173,8 @@ class ChatActivity :
|
|||||||
isFirstMessagesProcessing = false
|
isFirstMessagesProcessing = false
|
||||||
binding.progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
binding.messagesListView.visibility = View.VISIBLE
|
binding.messagesListView.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
collapseSystemMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3188,6 +3240,7 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processExpiredMessages() {
|
private fun processExpiredMessages() {
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun deleteExpiredMessages() {
|
fun deleteExpiredMessages() {
|
||||||
val messagesToDelete: ArrayList<ChatMessage> = ArrayList()
|
val messagesToDelete: ArrayList<ChatMessage> = ArrayList()
|
||||||
val systemTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
|
val systemTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
|
||||||
@ -3248,8 +3301,6 @@ class ChatActivity :
|
|||||||
adapter?.addToStart(unreadChatMessage, false)
|
adapter?.addToStart(unreadChatMessage, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
determinePreviousMessageIds(chatMessageList)
|
|
||||||
|
|
||||||
addMessagesToAdapter(shouldAddNewMessagesNotice, chatMessageList)
|
addMessagesToAdapter(shouldAddNewMessagesNotice, chatMessageList)
|
||||||
|
|
||||||
if (shouldAddNewMessagesNotice && adapter != null) {
|
if (shouldAddNewMessagesNotice && adapter != null) {
|
||||||
@ -3257,6 +3308,36 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processMessagesNotFromTheFuture(chatMessageList: List<ChatMessage>) {
|
||||||
|
var countGroupedMessages = 0
|
||||||
|
|
||||||
|
for (i in chatMessageList.indices) {
|
||||||
|
if (chatMessageList.size > i + 1) {
|
||||||
|
if (isSameDayNonSystemMessages(chatMessageList[i], chatMessageList[i + 1]) &&
|
||||||
|
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
||||||
|
countGroupedMessages < GROUPED_MESSAGES_THRESHOLD
|
||||||
|
) {
|
||||||
|
chatMessageList[i].isGrouped = true
|
||||||
|
countGroupedMessages++
|
||||||
|
} else {
|
||||||
|
countGroupedMessages = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val chatMessage = chatMessageList[i]
|
||||||
|
chatMessage.isOneToOneConversation =
|
||||||
|
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||||
|
chatMessage.isFormerOneToOneConversation =
|
||||||
|
(currentConversation?.type == ConversationType.FORMER_ONE_TO_ONE)
|
||||||
|
chatMessage.activeUser = conversationUser
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter?.addToEnd(chatMessageList, false)
|
||||||
|
}
|
||||||
|
scrollToRequestedMessageIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
private fun scrollToFirstUnreadMessage() {
|
private fun scrollToFirstUnreadMessage() {
|
||||||
adapter?.let {
|
adapter?.let {
|
||||||
layoutManager?.scrollToPositionWithOffset(
|
layoutManager?.scrollToPositionWithOffset(
|
||||||
@ -3286,10 +3367,8 @@ class ChatActivity :
|
|||||||
|
|
||||||
adapter?.let {
|
adapter?.let {
|
||||||
chatMessage.isGrouped = (
|
chatMessage.isGrouped = (
|
||||||
it.isPreviousSameAuthor(
|
it.isPreviousSameAuthor(chatMessage.actorId, -1) &&
|
||||||
chatMessage.actorId,
|
it.getSameAuthorLastMessagesCount(chatMessage.actorId) %
|
||||||
-1
|
|
||||||
) && it.getSameAuthorLastMessagesCount(chatMessage.actorId) %
|
|
||||||
GROUPED_MESSAGES_SAME_AUTHOR_THRESHOLD > 0
|
GROUPED_MESSAGES_SAME_AUTHOR_THRESHOLD > 0
|
||||||
)
|
)
|
||||||
chatMessage.isOneToOneConversation =
|
chatMessage.isOneToOneConversation =
|
||||||
@ -3318,37 +3397,6 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processMessagesNotFromTheFuture(chatMessageList: List<ChatMessage>) {
|
|
||||||
var countGroupedMessages = 0
|
|
||||||
determinePreviousMessageIds(chatMessageList)
|
|
||||||
|
|
||||||
for (i in chatMessageList.indices) {
|
|
||||||
if (chatMessageList.size > i + 1) {
|
|
||||||
if (isSameDayNonSystemMessages(chatMessageList[i], chatMessageList[i + 1]) &&
|
|
||||||
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
|
||||||
countGroupedMessages < GROUPED_MESSAGES_THRESHOLD
|
|
||||||
) {
|
|
||||||
chatMessageList[i].isGrouped = true
|
|
||||||
countGroupedMessages++
|
|
||||||
} else {
|
|
||||||
countGroupedMessages = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val chatMessage = chatMessageList[i]
|
|
||||||
chatMessage.isOneToOneConversation =
|
|
||||||
currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
|
||||||
chatMessage.isFormerOneToOneConversation =
|
|
||||||
(currentConversation?.type == ConversationType.FORMER_ONE_TO_ONE)
|
|
||||||
chatMessage.activeUser = conversationUser
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adapter != null) {
|
|
||||||
adapter?.addToEnd(chatMessageList, false)
|
|
||||||
}
|
|
||||||
scrollToRequestedMessageIfNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun determinePreviousMessageIds(chatMessageList: List<ChatMessage>) {
|
private fun determinePreviousMessageIds(chatMessageList: List<ChatMessage>) {
|
||||||
var previousMessageId = NO_PREVIOUS_MESSAGE_ID
|
var previousMessageId = NO_PREVIOUS_MESSAGE_ID
|
||||||
for (i in chatMessageList.indices.reversed()) {
|
for (i in chatMessageList.indices.reversed()) {
|
||||||
@ -3574,6 +3622,30 @@ class ChatActivity :
|
|||||||
return chatMessageMap.values.toList()
|
return chatMessageMap.values.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleExpandableSystemMessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
|
||||||
|
val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap()
|
||||||
|
val chatMessageIterator = chatMessageMap.iterator()
|
||||||
|
while (chatMessageIterator.hasNext()) {
|
||||||
|
val currentMessage = chatMessageIterator.next()
|
||||||
|
|
||||||
|
val previousMessage = chatMessageMap[currentMessage.value.previousMessageId.toString()]
|
||||||
|
if (isSystemMessage(currentMessage.value) &&
|
||||||
|
previousMessage?.systemMessageType == currentMessage.value.systemMessageType
|
||||||
|
) {
|
||||||
|
previousMessage?.expandableParent = true
|
||||||
|
currentMessage.value.expandableParent = false
|
||||||
|
|
||||||
|
if (currentMessage.value.lastItemOfExpandableGroup == 0) {
|
||||||
|
currentMessage.value.lastItemOfExpandableGroup = currentMessage.value.jsonMessageId
|
||||||
|
}
|
||||||
|
|
||||||
|
previousMessage?.lastItemOfExpandableGroup = currentMessage.value.lastItemOfExpandableGroup
|
||||||
|
previousMessage?.expandableChildrenAmount = currentMessage.value.expandableChildrenAmount + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chatMessageMap.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry<String, ChatMessage>): Boolean {
|
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry<String, ChatMessage>): Boolean {
|
||||||
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
|
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
|
||||||
.SystemMessageType.MESSAGE_DELETED
|
.SystemMessageType.MESSAGE_DELETED
|
||||||
|
@ -135,7 +135,17 @@ data class ChatMessage(
|
|||||||
|
|
||||||
var voiceMessageSeekbarProgress: Int = 0,
|
var voiceMessageSeekbarProgress: Int = 0,
|
||||||
|
|
||||||
var voiceMessageFloatArray: FloatArray? = null
|
var voiceMessageFloatArray: FloatArray? = null,
|
||||||
|
|
||||||
|
var expandableParent: Boolean = false,
|
||||||
|
|
||||||
|
var isExpanded: Boolean = false,
|
||||||
|
|
||||||
|
var lastItemOfExpandableGroup: Int = 0,
|
||||||
|
|
||||||
|
var expandableChildrenAmount: Int = 0,
|
||||||
|
|
||||||
|
var hiddenByCollapse: Boolean = false
|
||||||
|
|
||||||
) : Parcelable, MessageContentType, MessageContentType.Image {
|
) : Parcelable, MessageContentType, MessageContentType.Image {
|
||||||
|
|
||||||
|
9
app/src/main/res/drawable/baseline_unfold_less_24.xml
Normal file
9
app/src/main/res/drawable/baseline_unfold_less_24.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:width="24dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zM16.59,5.41L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/baseline_unfold_more_24.xml
Normal file
9
app/src/main/res/drawable/baseline_unfold_more_24.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:width="24dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
|
||||||
|
</vector>
|
@ -24,12 +24,27 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/systemMessageLayout"
|
||||||
android:layout_marginStart="@dimen/standard_margin"
|
android:layout_marginStart="@dimen/standard_margin"
|
||||||
android:layout_marginTop="@dimen/standard_eighth_margin"
|
android:layout_marginTop="@dimen/standard_eighth_margin"
|
||||||
android:layout_marginEnd="@dimen/standard_margin"
|
android:layout_marginEnd="@dimen/standard_margin"
|
||||||
android:layout_marginBottom="@dimen/standard_eighth_margin">
|
android:layout_marginBottom="@dimen/standard_eighth_margin">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/expandCollapseIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
<com.google.android.flexbox.FlexboxLayout
|
<com.google.android.flexbox.FlexboxLayout
|
||||||
android:id="@id/container"
|
android:id="@id/container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -71,5 +86,26 @@
|
|||||||
app:layout_flexGrow="1"
|
app:layout_flexGrow="1"
|
||||||
app:layout_wrapBefore="false"
|
app:layout_wrapBefore="false"
|
||||||
tools:text="17:30" />
|
tools:text="17:30" />
|
||||||
|
|
||||||
|
<androidx.emoji2.widget.EmojiTextView
|
||||||
|
android:id="@+id/similarMessagesHint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginStart="@dimen/standard_margin"
|
||||||
|
android:layout_marginEnd="@dimen/standard_margin"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/grey_600"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_alignSelf="center"
|
||||||
|
app:layout_flexGrow="1"
|
||||||
|
app:layout_wrapBefore="true"
|
||||||
|
tools:text="See 5 similar messages" />
|
||||||
</com.google.android.flexbox.FlexboxLayout>
|
</com.google.android.flexbox.FlexboxLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -376,6 +376,7 @@ How to translate with transifex:
|
|||||||
<string name="nc_add_attachment">Add attachment</string>
|
<string name="nc_add_attachment">Add attachment</string>
|
||||||
<string name="emoji_category_recent">Recent</string>
|
<string name="emoji_category_recent">Recent</string>
|
||||||
<string name="emoji_backspace">Backspace</string>
|
<string name="emoji_backspace">Backspace</string>
|
||||||
|
<string name="see_similar_system_messages">See %1$s similar messages</string>
|
||||||
|
|
||||||
<!-- Conversation info guest access -->
|
<!-- Conversation info guest access -->
|
||||||
<string name="nc_guest_access">Guest access</string>
|
<string name="nc_guest_access">Guest access</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user