mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-27 08:55:54 +03:00
add openGraph link previews
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
fd1f2c77ad
commit
9bc42334d4
19 changed files with 1119 additions and 1 deletions
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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 reactionsInterface: ReactionsInterface
|
||||
|
||||
@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
|
||||
)
|
||||
|
||||
Reaction().showReactions(
|
||||
message,
|
||||
binding.reactions,
|
||||
binding.messageTime.context,
|
||||
false,
|
||||
viewThemeUtils
|
||||
)
|
||||
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
|
||||
reactionsInterface.onClickReactions(message)
|
||||
}
|
||||
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
|
||||
reactionsInterface.onLongClickReactions(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 assignReactionInterface(reactionsInterface: ReactionsInterface) {
|
||||
this.reactionsInterface = reactionsInterface
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = IncomingLinkPreviewMessageViewHolder::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 reactionsInterface: ReactionsInterface
|
||||
|
||||
@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
|
||||
)
|
||||
|
||||
Reaction().showReactions(
|
||||
message,
|
||||
binding.reactions,
|
||||
binding.messageTime.context,
|
||||
true,
|
||||
viewThemeUtils
|
||||
)
|
||||
binding.reactions.reactionsEmojiWrapper.setOnClickListener {
|
||||
reactionsInterface.onClickReactions(message)
|
||||
}
|
||||
binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? ->
|
||||
reactionsInterface.onLongClickReactions(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 assignReactionInterface(reactionsInterface: ReactionsInterface) {
|
||||
this.reactionsInterface = reactionsInterface
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = OutcomingLinkPreviewMessageViewHolder::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ 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.IncomingLinkPreviewMessageViewHolder
|
||||
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
|
||||
import com.nextcloud.talk.adapters.messages.IncomingPollMessageViewHolder
|
||||
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
|
||||
|
@ -115,6 +116,7 @@ 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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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|$)"""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
67
app/src/main/res/layout/reference_inside_message.xml
Normal file
67
app/src/main/res/layout/reference_inside_message.xml
Normal file
|
@ -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>
|
Loading…
Reference in a new issue