diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt new file mode 100644 index 0000000000..9d39d987c5 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt @@ -0,0 +1,25 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * 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 . + */ +package com.nextcloud.talk.adapters.messages + +interface CallStartedMessageInterface { + fun joinAudioCall() + fun joinVideoCall() +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt new file mode 100644 index 0000000000..2658fdcaec --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt @@ -0,0 +1,95 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * 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 . + */ + +package com.nextcloud.talk.adapters.messages + +import android.content.Context +import android.text.Spannable +import android.text.SpannableString +import android.view.View +import autodagger.AutoInjector +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.CallStartedMessageBinding +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.ui.theme.ViewThemeUtils +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.DisplayUtils +import com.stfalcon.chatkit.messages.MessageHolders +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class CallStartedViewHolder(incomingView: View, payload: Any) : + MessageHolders.BaseIncomingMessageViewHolder(incomingView, payload) { + private val binding: CallStartedMessageBinding = CallStartedMessageBinding.bind(incomingView) + + @Inject + lateinit var context: Context + + @Inject + lateinit var userManager: UserManager + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private lateinit var messageInterface: CallStartedMessageInterface + + override fun onBind(message: ChatMessage?) { + super.onBind(message) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + themeBackground() + val name = "@" + message!!.actorDisplayName + val text = name + " " + context.resources.getString(R.string.started_a_call) + var spannable: Spannable = SpannableString(text) + // TODO figure out a way to replace with mentioned chip + val mentionColor: Int = context.resources.getColor(R.color.textColorMaxContrast) + spannable = DisplayUtils.searchAndColor(spannable, name, mentionColor) + + binding.callAuthorTextview.text = spannable + binding.joinVideoCall.setOnClickListener { messageInterface.joinVideoCall() } + binding.joinAudioCall.setOnClickListener { messageInterface.joinAudioCall() } + } + + private fun themeBackground() { + binding.callStartedBackground.apply { + viewThemeUtils.talk.themeOutgoingMessageBubble(this, grouped = true, false) + } + } + + fun assignCallStartedMessageInterface(inf: CallStartedMessageInterface) { + messageInterface = inf + } + + companion object { + var TAG: String? = CallStartedViewHolder::class.simpleName + } + + override fun viewDetached() { + // unused atm + } + + override fun viewAttached() { + // unused atm + } + + override fun viewRecycled() { + // unused atm + } +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java index a245c60458..16c8d9884e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java @@ -77,6 +77,8 @@ public void onBindViewHolder(ViewHolder holder, int position) { } else if (holder instanceof SystemMessageViewHolder) { ((SystemMessageViewHolder) holder).assignSystemMessageInterface(chatActivity); + } else if (holder instanceof CallStartedViewHolder) { + ((CallStartedViewHolder) holder).assignCallStartedMessageInterface(chatActivity); } } } diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 159a99b416..2d82297e09 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -121,6 +121,8 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.TakePhotoActivity +import com.nextcloud.talk.adapters.messages.CallStartedMessageInterface +import com.nextcloud.talk.adapters.messages.CallStartedViewHolder import com.nextcloud.talk.adapters.messages.CommonMessageInterface import com.nextcloud.talk.adapters.messages.IncomingLinkPreviewMessageViewHolder import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder @@ -267,7 +269,8 @@ class ChatActivity : VoiceMessageInterface, CommonMessageInterface, PreviewMessageInterface, - SystemMessageInterface { + SystemMessageInterface, + CallStartedMessageInterface { var active = false @@ -376,6 +379,8 @@ class ChatActivity : var typedWhileTypingTimerIsRunning: Boolean = false val typingParticipants = HashMap() + var callStarted = false + private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { override fun onSwitchTo(token: String?) { if (token != null) { @@ -945,6 +950,17 @@ class ChatActivity : R.layout.item_custom_outcoming_preview_message ) + messageHolders.registerContentType( + CONTENT_TYPE_CALL_STARTED, + CallStartedViewHolder::class.java, + payload, + R.layout.call_started_message, + CallStartedViewHolder::class.java, + payload, + R.layout.call_started_message, + this + ) + messageHolders.registerContentType( CONTENT_TYPE_SYSTEM_MESSAGE, SystemMessageViewHolder::class.java, @@ -3164,6 +3180,16 @@ class ChatActivity : Integer.parseInt(it) } + val last = adapter?.items!!.first { + it.item is ChatMessage && + (it.item as ChatMessage).systemMessageType != null && + (it.item as ChatMessage).systemMessageType!!.ordinal <= 11 && + (it.item as ChatMessage).systemMessageType!!.ordinal >= 5 + }.item + + if (last != null) + processMostRecentMessage(last as ChatMessage, chatMessageList) + updateReadStatusOfAllMessages(newXChatLastCommonRead) adapter?.notifyDataSetChanged() @@ -4207,10 +4233,36 @@ class ChatActivity : CONTENT_TYPE_LINK_PREVIEW -> message.isLinkPreview() CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage) CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1" + CONTENT_TYPE_CALL_STARTED -> message.id == "-2" + else -> false } } + private fun processMostRecentMessage(recent: ChatMessage, chatMessageList: List) { + when (recent.systemMessageType) { + ChatMessage.SystemMessageType.CALL_STARTED -> { + if (!callStarted) { + val unreadChatMessage = ChatMessage() + unreadChatMessage.jsonMessageId = -2 + unreadChatMessage.actorId = "-2" + unreadChatMessage.actorDisplayName = recent.actorDisplayName + unreadChatMessage.timestamp = chatMessageList[0].timestamp + unreadChatMessage.message = null + adapter?.addToStart(unreadChatMessage, false) + callStarted = true + } + } // add CallStartedMessage with id -2 + ChatMessage.SystemMessageType.CALL_ENDED, + ChatMessage.SystemMessageType.CALL_MISSED, + ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE -> { + adapter?.deleteById("-2") + callStarted = false + } // remove message of id -2 + else -> {} + } + } + @Subscribe(threadMode = ThreadMode.BACKGROUND) fun onMessageEvent(webSocketCommunicationEvent: WebSocketCommunicationEvent) { /* @@ -4344,6 +4396,14 @@ class ChatActivity : } } + override fun joinAudioCall() { + startACall(true, false) + } + + override fun joinVideoCall() { + startACall(false, false) + } + private fun logConversationInfos(methodName: String) { Log.d(TAG, " |-----------------------------------------------") Log.d(TAG, " | method: $methodName") @@ -4356,12 +4416,13 @@ class ChatActivity : companion object { private val TAG = ChatActivity::class.simpleName - private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1 - private const val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2 - private const val CONTENT_TYPE_LOCATION: Byte = 3 - private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 4 - private const val CONTENT_TYPE_POLL: Byte = 5 - private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 6 + private const val CONTENT_TYPE_CALL_STARTED: Byte = 1 + private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 2 + private const val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 3 + private const val CONTENT_TYPE_LOCATION: Byte = 4 + private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 5 + private const val CONTENT_TYPE_POLL: Byte = 6 + private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 7 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_LOBBY: Long = 5000 diff --git a/app/src/main/res/layout/call_started_message.xml b/app/src/main/res/layout/call_started_message.xml new file mode 100644 index 0000000000..88b350f839 --- /dev/null +++ b/app/src/main/res/layout/call_started_message.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dca3644d00..08fdf54f17 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -706,5 +706,8 @@ How to translate with transifex: Custom Set Calendar + Video Call + Audio Call + started a call