Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/talk-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorTim Krüger <t@timkrueger.me>2022-10-06 14:19:14 +0300
committerGitHub <noreply@github.com>2022-10-06 14:19:14 +0300
commit009cd5a42525b0383cf19fcc35c2221ba3f9dcf3 (patch)
tree7951dcf65a9dd89666f93d307d4ac539d7446c9d /app
parentb4cf0b722a4ab88164b0286ef6d4b9150e4fd98d (diff)
parent66eea709b48b65358b28e7b68d0a2f257a56b786 (diff)
Merge pull request #2460 from nextcloud/feature/2419/linkPreviews
add openGraph link previews
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt (renamed from app/src/main/java/com/nextcloud/talk/adapters/messages/ReactionsInterface.kt)4
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt211
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt122
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt172
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt10
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/messages/TalkMessagesListAdapter.java19
-rw-r--r--app/src/main/java/com/nextcloud/talk/api/NcApi.java6
-rw-r--r--app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt23
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt4
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/capabilities/CoreCapability.kt43
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt34
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOCS.kt38
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphObject.kt44
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOverall.kt35
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphResponse.kt35
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/Reference.kt42
-rw-r--r--app/src/main/java/com/nextcloud/talk/models/json/opengraph/RichObject.kt44
-rw-r--r--app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java3
-rw-r--r--app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt10
-rw-r--r--app/src/main/res/layout/item_custom_incoming_link_preview_message.xml107
-rw-r--r--app/src/main/res/layout/item_custom_outcoming_link_preview_message.xml96
-rw-r--r--app/src/main/res/layout/reference_inside_message.xml67
30 files changed, 1190 insertions, 59 deletions
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/ReactionsInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt
index 589c6e363..a40509b30 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/ReactionsInterface.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt
@@ -2,7 +2,7 @@ package com.nextcloud.talk.adapters.messages
import com.nextcloud.talk.models.json.chat.ChatMessage
-interface ReactionsInterface {
+interface CommonMessageInterface {
fun onClickReactions(chatMessage: ChatMessage)
- fun onLongClickReactions(chatMessage: ChatMessage)
+ fun onOpenMessageActionsDialog(chatMessage: ChatMessage)
}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt
new file mode 100644
index 000000000..14f96025c
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt
@@ -0,0 +1,211 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ * Copyright (C) 2017-2019 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.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.os.Build
+import android.text.TextUtils
+import android.view.View
+import androidx.core.content.ContextCompat
+import androidx.core.content.res.ResourcesCompat
+import autodagger.AutoInjector
+import coil.load
+import com.amulyakhare.textdrawable.TextDrawable
+import com.nextcloud.talk.R
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
+import com.nextcloud.talk.databinding.ItemCustomIncomingLinkPreviewMessageBinding
+import com.nextcloud.talk.models.json.chat.ChatMessage
+import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.talk.utils.ApiUtils
+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 IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : MessageHolders
+.IncomingTextMessageViewHolder<ChatMessage>(incomingView, payload) {
+
+ private val binding: ItemCustomIncomingLinkPreviewMessageBinding =
+ ItemCustomIncomingLinkPreviewMessageBinding.bind(itemView)
+
+ @Inject
+ lateinit var context: Context
+
+ @Inject
+ lateinit var appPreferences: AppPreferences
+
+ @Inject
+ lateinit var viewThemeUtils: ViewThemeUtils
+
+ @Inject
+ lateinit var ncApi: NcApi
+
+ lateinit var message: ChatMessage
+
+ lateinit var commonMessageInterface: CommonMessageInterface
+
+ @SuppressLint("SetTextI18n")
+ override fun onBind(message: ChatMessage) {
+ super.onBind(message)
+ this.message = message
+ sharedApplication!!.componentApplication.inject(this)
+
+ setAvatarAndAuthorOnMessageItem(message)
+
+ colorizeMessageBubble(message)
+
+ itemView.isSelected = false
+
+ // parent message handling
+ setParentMessageDataOnMessageItem(message)
+
+ LinkPreview().showLink(
+ message,
+ ncApi,
+ binding.referenceInclude,
+ context
+ )
+ binding.referenceInclude.referenceWrapper.setOnLongClickListener { l: View? ->
+ commonMessageInterface.onOpenMessageActionsDialog(message)
+ true
+ }
+
+ Reaction().showReactions(
+ message,
+ binding.reactions,
+ binding.messageTime.context,
+ false,
+ viewThemeUtils
+ )
+ binding.reactions.reactionsEmojiWrapper.setOnClickListener {
+ commonMessageInterface.onClickReactions(message)
+ }
+ binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
+ commonMessageInterface.onOpenMessageActionsDialog(message)
+ true
+ }
+ }
+
+ private fun setAvatarAndAuthorOnMessageItem(message: ChatMessage) {
+ val author: String = message.actorDisplayName!!
+ if (!TextUtils.isEmpty(author)) {
+ binding.messageAuthor.text = author
+ binding.messageUserAvatar.setOnClickListener {
+ (payload as? MessagePayload)?.profileBottomSheet?.showFor(message.actorId!!, itemView.context)
+ }
+ } else {
+ binding.messageAuthor.setText(R.string.nc_nick_guest)
+ }
+
+ if (!message.isGrouped && !message.isOneToOneConversation) {
+ setAvatarOnMessage(message)
+ } else {
+ if (message.isOneToOneConversation) {
+ binding.messageUserAvatar.visibility = View.GONE
+ } else {
+ binding.messageUserAvatar.visibility = View.INVISIBLE
+ }
+ binding.messageAuthor.visibility = View.GONE
+ }
+ }
+
+ private fun setAvatarOnMessage(message: ChatMessage) {
+ binding.messageUserAvatar.visibility = View.VISIBLE
+ if (message.actorType == "guests") {
+ // do nothing, avatar is set
+ } else if (message.actorType == "bots" && message.actorId == "changelog") {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val layers = arrayOfNulls<Drawable>(2)
+ layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
+ layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
+ val layerDrawable = LayerDrawable(layers)
+ binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
+ } else {
+ binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher)
+ }
+ } else if (message.actorType == "bots") {
+ val drawable = TextDrawable.builder()
+ .beginConfig()
+ .bold()
+ .endConfig()
+ .buildRound(
+ ">",
+ ResourcesCompat.getColor(context.resources, R.color.black, null)
+ )
+ binding.messageUserAvatar.visibility = View.VISIBLE
+ binding.messageUserAvatar.setImageDrawable(drawable)
+ }
+ }
+
+ private fun colorizeMessageBubble(message: ChatMessage) {
+ viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
+ }
+
+ private fun setParentMessageDataOnMessageItem(message: ChatMessage) {
+ if (!message.isDeleted && message.parentMessage != null) {
+ val parentChatMessage = message.parentMessage
+ parentChatMessage!!.activeUser = message.activeUser
+ parentChatMessage.imageUrl?.let {
+ binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
+ 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 = parentChatMessage.text
+
+ binding.messageQuote.quotedMessageAuthor
+ .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast))
+
+ if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) {
+ viewThemeUtils.platform.colorPrimaryView(binding.messageQuote.quoteColoredView)
+ } else {
+ binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.textColorMaxContrast)
+ }
+
+ binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
+ } else {
+ binding.messageQuote.quotedChatMessageView.visibility = View.GONE
+ }
+ }
+
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
+ }
+
+ companion object {
+ private val TAG = IncomingLinkPreviewMessageViewHolder::class.java.simpleName
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt
index d579503c7..64b489216 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt
@@ -78,7 +78,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -109,10 +109,10 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -267,8 +267,8 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess
return locationGeoLink.replace("geo:", "geo:0,0?q=")
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt
index 937c95ed5..d3e7dcd78 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt
@@ -68,7 +68,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH
lateinit var message: ChatMessage
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -95,10 +95,10 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -228,8 +228,8 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH
}
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt
index a11e737a4..10f6284a9 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt
@@ -76,7 +76,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
lateinit var message: ChatMessage
lateinit var voiceMessageInterface: VoiceMessageInterface
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -153,10 +153,10 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -307,8 +307,8 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
this.voiceMessageInterface = voiceMessageInterface
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt
new file mode 100644
index 000000000..fd78f6200
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt
@@ -0,0 +1,122 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
+ *
+ * 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/>.
+ *
+ * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at:
+ * https://github.com/nextcloud/ownCloud-Account-Importer
+ */
+
+package com.nextcloud.talk.adapters.messages
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import android.view.View
+import com.facebook.drawee.backends.pipeline.Fresco
+import com.facebook.drawee.interfaces.DraweeController
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.databinding.ReferenceInsideMessageBinding
+import com.nextcloud.talk.models.json.chat.ChatMessage
+import com.nextcloud.talk.models.json.opengraph.OpenGraphOverall
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.DisplayUtils
+import io.reactivex.Observer
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+
+class LinkPreview {
+
+ fun showLink(
+ message: ChatMessage,
+ ncApi: NcApi,
+ binding: ReferenceInsideMessageBinding,
+ context: Context
+ ) {
+ if (!message.extractedUrlToPreview.isNullOrEmpty()) {
+ val credentials: String = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token)
+ val openGraphLink = ApiUtils.getUrlForOpenGraph(message.activeUser?.baseUrl)
+ ncApi.getOpenGraph(
+ credentials,
+ openGraphLink,
+ message.extractedUrlToPreview
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer<OpenGraphOverall> {
+ override fun onSubscribe(d: Disposable) {
+ // unused atm
+ }
+
+ override fun onNext(openGraphOverall: OpenGraphOverall) {
+ val reference = openGraphOverall.ocs?.data?.references?.entries?.iterator()?.next()?.value
+
+ if (reference != null) {
+ val referenceName = reference.openGraphObject?.name
+ if (!referenceName.isNullOrEmpty()) {
+ binding.referenceName.visibility = View.VISIBLE
+ binding.referenceName.text = referenceName
+ } else {
+ binding.referenceName.visibility = View.GONE
+ }
+
+ val referenceLink = reference.openGraphObject?.link
+ if (!referenceLink.isNullOrEmpty()) {
+ binding.referenceLink.visibility = View.VISIBLE
+ binding.referenceLink.text = referenceLink
+ } else {
+ binding.referenceLink.visibility = View.GONE
+ }
+
+ val referenceThumbUrl = reference.openGraphObject?.thumb
+ if (!referenceThumbUrl.isNullOrEmpty()) {
+ binding.referenceThumbImage.visibility = View.VISIBLE
+ val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
+ .setAutoPlayAnimations(true)
+ .setImageRequest(DisplayUtils.getImageRequestForUrl(referenceThumbUrl))
+ .build()
+ binding.referenceThumbImage.controller =
+ draweeController
+ } else {
+ binding.referenceThumbImage.visibility = View.GONE
+ }
+
+ binding.referenceWrapper.setOnClickListener {
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(referenceLink))
+ browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(browserIntent)
+ }
+ }
+ }
+
+ override fun onError(e: Throwable) {
+ Log.e(TAG, "failed to get openGraph data", e)
+ }
+
+ override fun onComplete() {
+ // unused atm
+ }
+ })
+ }
+ }
+
+ companion object {
+ private val TAG = LinkPreview::class.java.simpleName
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
index db93d9e90..c8f2a7a2e 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
@@ -69,7 +69,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message
@Inject
lateinit var appPreferences: AppPreferences
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
override fun onBind(message: ChatMessage) {
super.onBind(message)
@@ -125,10 +125,10 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -249,8 +249,8 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message
return messageStringInternal
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
index 4ae870b0f..66a0d4f3f 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
@@ -60,7 +60,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
override fun onBind(message: ChatMessage) {
super.onBind(message)
@@ -123,10 +123,10 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
Reaction().showReactions(message, binding.reactions, context, true, viewThemeUtils)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -193,8 +193,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
return messageString1
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java
index d61e5df5f..ca7394341 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java
@@ -108,7 +108,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
View clickView;
- ReactionsInterface reactionsInterface;
+ CommonMessageInterface commonMessageInterface;
PreviewMessageInterface previewMessageInterface;
public MagicPreviewMessageViewHolder(View itemView, Object payload) {
@@ -251,10 +251,10 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
true,
viewThemeUtils);
reactionsBinding.reactionsEmojiWrapper.setOnClickListener(l -> {
- reactionsInterface.onClickReactions(message);
+ commonMessageInterface.onClickReactions(message);
});
reactionsBinding.reactionsEmojiWrapper.setOnLongClickListener(l -> {
- reactionsInterface.onLongClickReactions(message);
+ commonMessageInterface.onOpenMessageActionsDialog(message);
return true;
});
}
@@ -347,8 +347,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
});
}
- public void assignReactionInterface(ReactionsInterface reactionsInterface) {
- this.reactionsInterface = reactionsInterface;
+ public void assignCommonMessageInterface(CommonMessageInterface commonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface;
}
public void assignPreviewMessageInterface(PreviewMessageInterface previewMessageInterface) {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt
new file mode 100644
index 000000000..7d501e646
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt
@@ -0,0 +1,172 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ * Copyright (C) 2017-2019 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.graphics.PorterDuff
+import android.view.View
+import androidx.appcompat.content.res.AppCompatResources
+import autodagger.AutoInjector
+import coil.load
+import com.nextcloud.talk.R
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
+import com.nextcloud.talk.databinding.ItemCustomOutcomingLinkPreviewMessageBinding
+import com.nextcloud.talk.models.json.chat.ChatMessage
+import com.nextcloud.talk.models.json.chat.ReadStatus
+import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.preferences.AppPreferences
+import com.stfalcon.chatkit.messages.MessageHolders
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders
+.OutcomingTextMessageViewHolder<ChatMessage>(outcomingView, payload) {
+
+ private val binding: ItemCustomOutcomingLinkPreviewMessageBinding =
+ ItemCustomOutcomingLinkPreviewMessageBinding.bind(itemView)
+
+ @Inject
+ lateinit var context: Context
+
+ @Inject
+ lateinit var viewThemeUtils: ViewThemeUtils
+
+ @Inject
+ lateinit var appPreferences: AppPreferences
+
+ @Inject
+ lateinit var ncApi: NcApi
+
+ lateinit var message: ChatMessage
+
+ lateinit var commonMessageInterface: CommonMessageInterface
+
+ @SuppressLint("SetTextI18n")
+ override fun onBind(message: ChatMessage) {
+ super.onBind(message)
+ this.message = message
+ sharedApplication!!.componentApplication.inject(this)
+ val textColor = viewThemeUtils.getScheme(binding.messageTime.context).onSurfaceVariant
+ binding.messageTime.setTextColor(textColor)
+
+ colorizeMessageBubble(message)
+
+ itemView.isSelected = false
+
+ // parent message handling
+ setParentMessageDataOnMessageItem(message)
+
+ val readStatusDrawableInt = when (message.readStatus) {
+ ReadStatus.READ -> R.drawable.ic_check_all
+ ReadStatus.SENT -> R.drawable.ic_check
+ else -> null
+ }
+
+ val readStatusContentDescriptionString = when (message.readStatus) {
+ ReadStatus.READ -> context?.resources?.getString(R.string.nc_message_read)
+ ReadStatus.SENT -> context?.resources?.getString(R.string.nc_message_sent)
+ else -> null
+ }
+
+ readStatusDrawableInt?.let { drawableInt ->
+ AppCompatResources.getDrawable(context, drawableInt)?.let {
+ binding.checkMark.setImageDrawable(it)
+ binding.checkMark.setColorFilter(
+ viewThemeUtils.getScheme(binding.checkMark.context).onSurfaceVariant, PorterDuff.Mode.SRC_ATOP
+ )
+ }
+ }
+
+ binding.checkMark.contentDescription = readStatusContentDescriptionString
+
+ LinkPreview().showLink(
+ message,
+ ncApi,
+ binding.referenceInclude,
+ context
+ )
+ binding.referenceInclude.referenceWrapper.setOnLongClickListener { l: View? ->
+ commonMessageInterface.onOpenMessageActionsDialog(message)
+ true
+ }
+
+ Reaction().showReactions(
+ message,
+ binding.reactions,
+ binding.messageTime.context,
+ true,
+ viewThemeUtils
+ )
+ binding.reactions.reactionsEmojiWrapper.setOnClickListener {
+ commonMessageInterface.onClickReactions(message)
+ }
+ binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
+ commonMessageInterface.onOpenMessageActionsDialog(message)
+ true
+ }
+ }
+
+ private fun setParentMessageDataOnMessageItem(message: ChatMessage) {
+ if (!message.isDeleted && message.parentMessage != null) {
+ val parentChatMessage = message.parentMessage
+ parentChatMessage!!.activeUser = message.activeUser
+ parentChatMessage.imageUrl?.let {
+ binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
+ 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 = parentChatMessage.text
+ viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
+ viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
+ viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
+
+ binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
+ } else {
+ binding.messageQuote.quotedChatMessageView.visibility = View.GONE
+ }
+ }
+
+ private fun colorizeMessageBubble(message: ChatMessage) {
+ viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
+ }
+
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
+ }
+
+ companion object {
+ private val TAG = OutcomingLinkPreviewMessageViewHolder::class.java.simpleName
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt
index 209b24496..148c45a4d 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt
@@ -70,7 +70,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -128,10 +128,10 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -242,8 +242,8 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
return locationGeoLink.replace("geo:", "geo:0,0?q=")
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt
index 47de01e13..0fb552edd 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt
@@ -64,7 +64,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
lateinit var message: ChatMessage
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -114,10 +114,10 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -190,8 +190,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt
index 96bed4b6c..845aa20c5 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt
@@ -71,7 +71,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
lateinit var handler: Handler
lateinit var voiceMessageInterface: VoiceMessageInterface
- lateinit var reactionsInterface: ReactionsInterface
+ lateinit var commonMessageInterface: CommonMessageInterface
@SuppressLint("SetTextI18n")
override fun onBind(message: ChatMessage) {
@@ -146,10 +146,10 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
viewThemeUtils
)
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
- reactionsInterface.onClickReactions(message)
+ commonMessageInterface.onClickReactions(message)
}
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
- reactionsInterface.onLongClickReactions(message)
+ commonMessageInterface.onOpenMessageActionsDialog(message)
true
}
}
@@ -281,8 +281,8 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
this.voiceMessageInterface = voiceMessageInterface
}
- fun assignReactionInterface(reactionsInterface: ReactionsInterface) {
- this.reactionsInterface = reactionsInterface
+ fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
+ this.commonMessageInterface = commonMessageInterface
}
companion object {
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 7f5e19f09..2947a2faa 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
@@ -50,25 +50,30 @@ public class TalkMessagesListAdapter<M extends IMessage> extends MessagesListAda
super.onBindViewHolder(holder, position);
if (holder instanceof MagicIncomingTextMessageViewHolder) {
- ((MagicIncomingTextMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((MagicIncomingTextMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof MagicOutcomingTextMessageViewHolder) {
- ((MagicOutcomingTextMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((MagicOutcomingTextMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof IncomingLocationMessageViewHolder) {
- ((IncomingLocationMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((IncomingLocationMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof OutcomingLocationMessageViewHolder) {
- ((OutcomingLocationMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((OutcomingLocationMessageViewHolder) holder).assignCommonMessageInterface(chatController);
+
+ } else if (holder instanceof IncomingLinkPreviewMessageViewHolder) {
+ ((IncomingLinkPreviewMessageViewHolder) holder).assignCommonMessageInterface(chatController);
+ } else if (holder instanceof OutcomingLinkPreviewMessageViewHolder) {
+ ((OutcomingLinkPreviewMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof IncomingVoiceMessageViewHolder) {
((IncomingVoiceMessageViewHolder) holder).assignVoiceMessageInterface(chatController);
- ((IncomingVoiceMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((IncomingVoiceMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof OutcomingVoiceMessageViewHolder) {
((OutcomingVoiceMessageViewHolder) holder).assignVoiceMessageInterface(chatController);
- ((OutcomingVoiceMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((OutcomingVoiceMessageViewHolder) holder).assignCommonMessageInterface(chatController);
} else if (holder instanceof MagicPreviewMessageViewHolder) {
((MagicPreviewMessageViewHolder) holder).assignPreviewMessageInterface(chatController);
- ((MagicPreviewMessageViewHolder) holder).assignReactionInterface(chatController);
+ ((MagicPreviewMessageViewHolder) holder).assignCommonMessageInterface(chatController);
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java
index f1c25b638..5b75bff8b 100644
--- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java
+++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java
@@ -35,6 +35,7 @@ import com.nextcloud.talk.models.json.generic.Status;
import com.nextcloud.talk.models.json.hovercard.HoverCardOverall;
import com.nextcloud.talk.models.json.mention.MentionOverall;
import com.nextcloud.talk.models.json.notifications.NotificationOverall;
+import com.nextcloud.talk.models.json.opengraph.OpenGraphOverall;
import com.nextcloud.talk.models.json.participants.AddParticipantOverall;
import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
import com.nextcloud.talk.models.json.push.PushRegistrationOverall;
@@ -570,4 +571,9 @@ public interface NcApi {
Observable<GenericOverall> setMessageExpiration(@Header("Authorization") String authorization,
@Url String url,
@Field("seconds") Integer seconds);
+
+ @GET
+ Observable<OpenGraphOverall> getOpenGraph(@Header("Authorization") String authorization,
+ @Url String url,
+ @Query("reference") String urlToFindPreviewFor);
}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
index f498a52ba..0a227e5f7 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
@@ -106,6 +106,8 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.activities.TakePhotoActivity
+import com.nextcloud.talk.adapters.messages.CommonMessageInterface
+import com.nextcloud.talk.adapters.messages.IncomingLinkPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPollMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
@@ -115,12 +117,12 @@ import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
import com.nextcloud.talk.adapters.messages.MessagePayload
+import com.nextcloud.talk.adapters.messages.OutcomingLinkPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingPollMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingVoiceMessageViewHolder
import com.nextcloud.talk.adapters.messages.PreviewMessageInterface
-import com.nextcloud.talk.adapters.messages.ReactionsInterface
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
import com.nextcloud.talk.api.NcApi
@@ -222,7 +224,7 @@ class ChatController(args: Bundle) :
MessagesListAdapter.OnMessageViewLongClickListener<IMessage>,
ContentChecker<ChatMessage>,
VoiceMessageInterface,
- ReactionsInterface,
+ CommonMessageInterface,
PreviewMessageInterface {
private val binding: ControllerChatBinding by viewBinding(ControllerChatBinding::bind)
@@ -587,6 +589,17 @@ class ChatController(args: Bundle) :
this
)
+ messageHolders.registerContentType(
+ CONTENT_TYPE_LINK_PREVIEW,
+ IncomingLinkPreviewMessageViewHolder::class.java,
+ payload,
+ R.layout.item_custom_incoming_link_preview_message,
+ OutcomingLinkPreviewMessageViewHolder::class.java,
+ payload,
+ R.layout.item_custom_outcoming_link_preview_message,
+ this
+ )
+
val senderId = if (!conversationUser.userId.equals("?")) {
"users/" + conversationUser.userId
} else {
@@ -2788,7 +2801,7 @@ class ChatController(args: Bundle) :
}
}
- override fun onLongClickReactions(chatMessage: ChatMessage) {
+ override fun onOpenMessageActionsDialog(chatMessage: ChatMessage) {
openMessageActionsDialog(chatMessage)
}
@@ -2797,7 +2810,7 @@ class ChatController(args: Bundle) :
}
override fun onPreviewMessageLongClick(chatMessage: ChatMessage) {
- openMessageActionsDialog(chatMessage)
+ onOpenMessageActionsDialog(chatMessage)
}
private fun openMessageActionsDialog(iMessage: IMessage?) {
@@ -3162,6 +3175,7 @@ class ChatController(args: Bundle) :
CONTENT_TYPE_LOCATION -> message.hasGeoLocation()
CONTENT_TYPE_VOICE_MESSAGE -> message.isVoiceMessage
CONTENT_TYPE_POLL -> message.isPoll()
+ CONTENT_TYPE_LINK_PREVIEW -> message.isLinkPreview()
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1"
else -> false
@@ -3322,6 +3336,7 @@ class ChatController(args: Bundle) :
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 NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200
private const val POP_CURRENT_CONTROLLER_DELAY: Long = 100
private const val LOBBY_TIMER_DELAY: Long = 5000
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt
index 6682469b5..bc94d731a 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.kt
@@ -29,6 +29,8 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
@JsonObject
data class Capabilities(
+ @JsonField(name = ["core"])
+ var coreCapability: CoreCapability?,
@JsonField(name = ["spreed"])
var spreedCapability: SpreedCapability?,
@JsonField(name = ["notifications"])
@@ -43,5 +45,5 @@ data class Capabilities(
var userStatusCapability: UserStatusCapability?
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
- constructor() : this(null, null, null, null, null, null)
+ constructor() : this(null, null, null, null, null, null, null)
}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CoreCapability.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CoreCapability.kt
new file mode 100644
index 000000000..d5bf24ff9
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CoreCapability.kt
@@ -0,0 +1,43 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.capabilities
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+import kotlinx.serialization.Serializable
+
+@Parcelize
+@JsonObject
+@Serializable
+data class CoreCapability(
+ @JsonField(name = ["pollinterval"])
+ var pollInterval: Int?,
+ @JsonField(name = ["webdav-root"])
+ var webdavRoot: String?,
+ @JsonField(name = ["reference-api"])
+ var referenceApi: String?,
+ @JsonField(name = ["reference-regex"])
+ var referenceRegex: String?
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this(null, null, null, null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt
index 173c32c14..98937fddf 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt
@@ -37,6 +37,7 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage
import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter
import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
import com.stfalcon.chatkit.commons.models.IUser
import com.stfalcon.chatkit.commons.models.MessageContentType
import kotlinx.android.parcel.Parcelize
@@ -126,8 +127,11 @@ data class ChatMessage(
var voiceMessagePlayedSeconds: Int = 0,
var voiceMessageDownloadProgress: Int = 0,
+
) : Parcelable, MessageContentType, MessageContentType.Image {
+ var extractedUrlToPreview: String? = null
+
// messageTypesToIgnore is weird. must be deleted by refactoring!!!
@JsonIgnore
var messageTypesToIgnore = Arrays.asList(
@@ -174,6 +178,33 @@ data class ChatMessage(
return false
}
+ @Suppress("ReturnCount")
+ fun isLinkPreview(): Boolean {
+ if (CapabilitiesUtilNew.isLinkPreviewAvailable(activeUser!!)) {
+ val regexStringFromServer = activeUser?.capabilities?.coreCapability?.referenceRegex
+
+ val regexFromServer = regexStringFromServer?.toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
+ val regexDefault = REGEX_STRING_DEFAULT.toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
+
+ val messageCharSequence: CharSequence = StringBuffer(message!!)
+
+ if (regexFromServer != null) {
+ val foundLinkInServerRegex = regexFromServer.containsMatchIn(messageCharSequence)
+ if (foundLinkInServerRegex) {
+ extractedUrlToPreview = regexFromServer.find(messageCharSequence)?.groups?.get(0)?.value?.trim()
+ return true
+ }
+ }
+
+ val foundLinkInDefaultRegex = regexDefault.containsMatchIn(messageCharSequence)
+ if (foundLinkInDefaultRegex) {
+ extractedUrlToPreview = regexDefault.find(messageCharSequence)?.groups?.get(0)?.value?.trim()
+ return true
+ }
+ }
+ return false
+ }
+
@Suppress("Detekt.NestedBlockDepth")
override fun getImageUrl(): String? {
if (messageParameters != null && messageParameters!!.size > 0) {
@@ -492,5 +523,8 @@ data class ChatMessage(
companion object {
private const val TAG = "ChatMessage"
private const val MILLIES: Long = 1000L
+
+ private const val REGEX_STRING_DEFAULT =
+ """(\s|\n|^)(https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)"""
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOCS.kt
new file mode 100644
index 000000000..eb834c5f8
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOCS.kt
@@ -0,0 +1,38 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import com.nextcloud.talk.models.json.generic.GenericMeta
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class OpenGraphOCS(
+ @JsonField(name = ["meta"])
+ var meta: GenericMeta?,
+ @JsonField(name = ["data"])
+ var data: OpenGraphResponse?
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this(null, null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphObject.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphObject.kt
new file mode 100644
index 000000000..d92d80ffa
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphObject.kt
@@ -0,0 +1,44 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class OpenGraphObject(
+ @JsonField(name = ["id"])
+ var id: String,
+ @JsonField(name = ["name"])
+ var name: String,
+ @JsonField(name = ["description"])
+ var description: String? = null,
+ @JsonField(name = ["thumb"])
+ var thumb: String? = null,
+ @JsonField(name = ["link"])
+ var link: String? = null,
+
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this("", "", null, null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOverall.kt
new file mode 100644
index 000000000..8cedd0d0a
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphOverall.kt
@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class OpenGraphOverall(
+ @JsonField(name = ["ocs"])
+ var ocs: OpenGraphOCS? = null
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this(null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphResponse.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphResponse.kt
new file mode 100644
index 000000000..f4ac43008
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/OpenGraphResponse.kt
@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class OpenGraphResponse(
+ @JsonField(name = ["references"])
+ var references: HashMap<String, Reference>?
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this(null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/Reference.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/Reference.kt
new file mode 100644
index 000000000..581c20dd8
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/Reference.kt
@@ -0,0 +1,42 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class Reference(
+ @JsonField(name = ["richObjectType"])
+ var richObjectType: String? = null,
+ @JsonField(name = ["richObject"])
+ var richObject: RichObject? = null,
+ @JsonField(name = ["openGraphObject"])
+ var openGraphObject: OpenGraphObject? = null,
+ @JsonField(name = ["accessible"])
+ var accessible: Boolean,
+
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this(null, null, null, false)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/opengraph/RichObject.kt b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/RichObject.kt
new file mode 100644
index 000000000..129d1d4fb
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/models/json/opengraph/RichObject.kt
@@ -0,0 +1,44 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * 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.models.json.opengraph
+
+import android.os.Parcelable
+import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonObject
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+@JsonObject
+data class RichObject(
+ @JsonField(name = ["id"])
+ var id: String,
+ @JsonField(name = ["name"])
+ var name: String,
+ @JsonField(name = ["description"])
+ var description: String? = null,
+ @JsonField(name = ["thumb"])
+ var thumb: String? = null,
+ @JsonField(name = ["link"])
+ var link: String? = null,
+
+) : Parcelable {
+ // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
+ constructor() : this("", "", null, null)
+}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java
index 368b4ea4c..cac2501cd 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java
+++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java
@@ -491,4 +491,7 @@ public class ApiUtils {
return getUrlForRoom(version, baseUrl, token) + "/message-expiration";
}
+ public static String getUrlForOpenGraph(String baseUrl) {
+ return baseUrl + ocsApiVersion + "/references/resolve";
+ }
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt
index f6bcda59d..7f86a9dcf 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt
@@ -154,5 +154,15 @@ object CapabilitiesUtilNew {
return hasSpreedFeatureCapability(user, "unified-search")
}
+ @JvmStatic
+ fun isLinkPreviewAvailable(user: User): Boolean {
+ if (user.capabilities?.coreCapability?.referenceApi != null &&
+ user.capabilities?.coreCapability?.referenceApi == "true"
+ ) {
+ return true
+ }
+ return false
+ }
+
const val DEFAULT_CHAT_SIZE = 1000
}
diff --git a/app/src/main/res/layout/item_custom_incoming_link_preview_message.xml b/app/src/main/res/layout/item_custom_incoming_link_preview_message.xml
new file mode 100644
index 000000000..b94e1cb2c
--- /dev/null
+++ b/app/src/main/res/layout/item_custom_incoming_link_preview_message.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Nextcloud Talk application
+ ~
+ ~ @author Mario Danic
+ ~ @author Andy Scherzinger
+ ~ @author Marcel Hibbe
+ ~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ ~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
+ ~ 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/>.
+ -->
+
+<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">
+
+ <com.facebook.drawee.view.SimpleDraweeView
+ android:id="@id/messageUserAvatar"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentTop="true"
+ android:layout_marginEnd="8dp"
+ app:roundAsCircle="true" />
+
+ <com.google.android.flexbox.FlexboxLayout
+ android:id="@id/bubble"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/message_incoming_bubble_margin_right"
+ android:layout_toEndOf="@id/messageUserAvatar"
+ android:orientation="vertical"
+ app:alignContent="stretch"
+ app:alignItems="stretch"
+ app:flexWrap="wrap">
+
+ <include
+ android:id="@+id/message_quote"
+ layout="@layout/item_message_quote"
+ android:visibility="gone" />
+
+ <androidx.emoji.widget.EmojiTextView
+ android:id="@+id/messageAuthor"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:alpha="0.6"
+ android:textAlignment="viewStart"
+ android:textColor="@color/no_emphasis_text"
+ android:textIsSelectable="false"
+ android:textSize="12sp"
+ tools:text="Jane Doe" />
+
+ <androidx.emoji.widget.EmojiTextView
+ android:id="@id/messageText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="1.2"
+ android:textAlignment="viewStart"
+ android:textIsSelectable="false"
+ app:layout_alignSelf="flex_start"
+ app:layout_flexGrow="1"
+ app:layout_wrapBefore="true"
+ tools:text="Talk to you later!" />
+
+ <include
+ android:id="@+id/referenceInclude"
+ layout="@layout/reference_inside_message" />
+
+ <TextView
+ android:id="@id/messageTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/messageText"
+ android:layout_marginStart="8dp"
+ android:alpha="0.6"
+ android:gravity="end"
+ android:textColor="@color/no_emphasis_text"
+ android:textIsSelectable="false"
+ app:layout_alignSelf="center"
+ app:layout_flexGrow="1"
+ app:layout_wrapBefore="false"
+ tools:text="12:38" />
+
+ <include
+ android:id="@+id/reactions"
+ layout="@layout/reactions_inside_message" />
+
+ </com.google.android.flexbox.FlexboxLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/item_custom_outcoming_link_preview_message.xml b/app/src/main/res/layout/item_custom_outcoming_link_preview_message.xml
new file mode 100644
index 000000000..924ad8553
--- /dev/null
+++ b/app/src/main/res/layout/item_custom_outcoming_link_preview_message.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Nextcloud Talk application
+ ~
+ ~ @author Mario Danic
+ ~ @author Andy Scherzinger
+ ~ @author Marcel Hibbe
+ ~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ ~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
+ ~ 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/>.
+ -->
+
+<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">
+
+ <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" />
+
+ <androidx.emoji.widget.EmojiTextView
+ android:id="@id/messageText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignWithParentIfMissing="true"
+ android:lineSpacingMultiplier="1.2"
+ android:textAlignment="viewStart"
+ android:textColorHighlight="@color/nc_grey"
+ android:textIsSelectable="false"
+ tools:text="Talk to you later!" />
+
+ <include
+ android:id="@+id/referenceInclude"
+ layout="@layout/reference_inside_message" />
+
+ <TextView
+ android:id="@id/messageTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/messageText"
+ android:layout_marginStart="8dp"
+ android:alpha="0.6"
+ android:gravity="end"
+ android:textColor="@color/no_emphasis_text"
+ android:textIsSelectable="false"
+ app:layout_alignSelf="center"
+ app:layout_flexGrow="1"
+ app:layout_wrapBefore="false"
+ tools:text="10:35" />
+
+ <ImageView
+ android:id="@+id/checkMark"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/message_bubble_checkmark_height"
+ android:layout_below="@id/messageTime"
+ android:layout_marginStart="8dp"
+ android:contentDescription="@null"
+ app:layout_alignSelf="center"
+ app:tint="@color/high_emphasis_text" />
+
+ <include
+ android:id="@+id/reactions"
+ layout="@layout/reactions_inside_message" />
+
+ </com.google.android.flexbox.FlexboxLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/reference_inside_message.xml b/app/src/main/res/layout/reference_inside_message.xml
new file mode 100644
index 000000000..4e87579a1
--- /dev/null
+++ b/app/src/main/res/layout/reference_inside_message.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+
+ Nextcloud Talk application
+
+ Copyright (C) 2022 Marcel Hibbe
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+-->
+
+<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:id="@+id/referenceWrapper"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp">
+
+ <View
+ android:id="@+id/referenceColoredView"
+ android:layout_width="2dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:background="@color/high_emphasis_text"
+ tools:layout_height="100dp"/>
+
+ <androidx.emoji.widget.EmojiTextView
+ android:id="@+id/referenceName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="1.2"
+ android:textAlignment="viewStart"
+ android:textIsSelectable="false"
+ android:layout_marginStart="10dp"
+ tools:text="Name of Website" />
+
+ <androidx.emoji.widget.EmojiTextView
+ android:id="@+id/referenceLink"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/referenceName"
+ android:lineSpacingMultiplier="1.2"
+ android:textAlignment="viewStart"
+ android:textIsSelectable="false"
+ android:layout_marginStart="10dp"
+ tools:text="http://nextcloud.com" />
+
+ <com.facebook.drawee.view.SimpleDraweeView
+ android:id="@+id/referenceThumbImage"
+ android:layout_width="match_parent"
+ android:layout_height="120dp"
+ android:scaleType="fitEnd"
+ android:layout_below="@id/referenceLink"
+ android:layout_marginTop="5dp"
+ android:layout_marginStart="10dp"
+ app:roundedCornerRadius="6dp" />
+</RelativeLayout> \ No newline at end of file