mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 03:59:35 +01:00
differ between incoming and outcoming messages for location
logic was copied from: - MagicIncomingTextMessageViewHolder.kt - MagicOutcomingTextMessageViewHolder.kt xml design was copied from: - item_custom_incoming_text_message.xml - item_custom_outcoming_text_message.xml ... and extended for location related things. because of copying there is now quite a lot of redundant code! But ViewHolders should generally be refactored in the future.. (better inheritance(?) and analyze which of Marios changes to the Chatkit lib were really necessary or if this can be done in an other way..) Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
7540e57dd8
commit
1aa15ebd95
@ -0,0 +1,322 @@
|
|||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.emoji.widget.EmojiTextView
|
||||||
|
import autodagger.AutoInjector
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import coil.load
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.facebook.drawee.view.SimpleDraweeView
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
|
import com.nextcloud.talk.utils.TextMatchers
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import com.stfalcon.chatkit.messages.MessageHolders
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
|
class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
|
||||||
|
.IncomingTextMessageViewHolder<ChatMessage>(incomingView) {
|
||||||
|
|
||||||
|
private val TAG = "LocationMessageViewHolder"
|
||||||
|
|
||||||
|
var mapProviderUrl: String = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
var mapProviderAttribution: String = "OpenStreetMap contributors"
|
||||||
|
|
||||||
|
var locationLon: String? = ""
|
||||||
|
var locationLat: String? = ""
|
||||||
|
var locationName: String? = ""
|
||||||
|
var locationGeoLink: String? = ""
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageAuthor)
|
||||||
|
var messageAuthor: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageText)
|
||||||
|
var messageText: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageUserAvatar)
|
||||||
|
var messageUserAvatarView: SimpleDraweeView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageTime)
|
||||||
|
var messageTimeView: TextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedChatMessageView)
|
||||||
|
var quotedChatMessageView: RelativeLayout? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessageAuthor)
|
||||||
|
var quotedUserName: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessageImage)
|
||||||
|
var quotedMessagePreview: ImageView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessage)
|
||||||
|
var quotedMessage: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quoteColoredView)
|
||||||
|
var quoteColoredView: View? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var context: Context? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var appPreferences: AppPreferences? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.webview)
|
||||||
|
var webview: WebView? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
ButterKnife.bind(
|
||||||
|
this,
|
||||||
|
itemView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled", "ClickableViewAccessibility")
|
||||||
|
override fun onBind(message: ChatMessage) {
|
||||||
|
super.onBind(message)
|
||||||
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
|
val author: String = message.actorDisplayName
|
||||||
|
if (!TextUtils.isEmpty(author)) {
|
||||||
|
messageAuthor!!.text = author
|
||||||
|
} else {
|
||||||
|
messageAuthor!!.setText(R.string.nc_nick_guest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.isGrouped && !message.isOneToOneConversation) {
|
||||||
|
messageUserAvatarView!!.visibility = View.VISIBLE
|
||||||
|
if (message.actorType == "guests") {
|
||||||
|
// do nothing, avatar is set
|
||||||
|
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||||
|
val layers = arrayOfNulls<Drawable>(2)
|
||||||
|
layers[0] = context?.getDrawable(R.drawable.ic_launcher_background)
|
||||||
|
layers[1] = context?.getDrawable(R.drawable.ic_launcher_foreground)
|
||||||
|
val layerDrawable = LayerDrawable(layers)
|
||||||
|
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||||
|
} else if (message.actorType == "bots") {
|
||||||
|
val drawable = TextDrawable.builder()
|
||||||
|
.beginConfig()
|
||||||
|
.bold()
|
||||||
|
.endConfig()
|
||||||
|
.buildRound(
|
||||||
|
">",
|
||||||
|
context!!.resources.getColor(R.color.black)
|
||||||
|
)
|
||||||
|
messageUserAvatarView!!.visibility = View.VISIBLE
|
||||||
|
messageUserAvatarView?.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (message.isOneToOneConversation) {
|
||||||
|
messageUserAvatarView!!.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
messageUserAvatarView!!.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
messageAuthor!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val resources = itemView.resources
|
||||||
|
|
||||||
|
val bgBubbleColor = if (message.isDeleted) {
|
||||||
|
resources.getColor(R.color.bg_message_list_incoming_bubble_deleted)
|
||||||
|
} else {
|
||||||
|
resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bubbleResource = R.drawable.shape_incoming_message
|
||||||
|
|
||||||
|
if (message.isGrouped) {
|
||||||
|
bubbleResource = R.drawable.shape_grouped_incoming_message
|
||||||
|
}
|
||||||
|
|
||||||
|
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||||
|
bgBubbleColor,
|
||||||
|
resources.getColor(R.color.transparent),
|
||||||
|
bgBubbleColor, bubbleResource
|
||||||
|
)
|
||||||
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
|
|
||||||
|
val messageParameters = message.messageParameters
|
||||||
|
|
||||||
|
itemView.isSelected = false
|
||||||
|
messageTimeView!!.setTextColor(context?.resources!!.getColor(R.color.warm_grey_four))
|
||||||
|
|
||||||
|
var messageString: Spannable = SpannableString(message.text)
|
||||||
|
|
||||||
|
var textSize = context?.resources!!.getDimension(R.dimen.chat_text_size)
|
||||||
|
|
||||||
|
if (messageParameters != null && messageParameters.size > 0) {
|
||||||
|
for (key in messageParameters.keys) {
|
||||||
|
val individualHashMap = message.messageParameters[key]
|
||||||
|
if (individualHashMap != null) {
|
||||||
|
if (individualHashMap["type"] == "user" || individualHashMap["type"] == "guest" || individualHashMap["type"] == "call") {
|
||||||
|
if (individualHashMap["id"] == message.activeUser!!.userId) {
|
||||||
|
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
|
||||||
|
messageText!!.context,
|
||||||
|
messageString,
|
||||||
|
individualHashMap["id"]!!,
|
||||||
|
individualHashMap["name"]!!,
|
||||||
|
individualHashMap["type"]!!,
|
||||||
|
message.activeUser!!,
|
||||||
|
R.xml.chip_you
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
|
||||||
|
messageText!!.context,
|
||||||
|
messageString,
|
||||||
|
individualHashMap["id"]!!,
|
||||||
|
individualHashMap["name"]!!,
|
||||||
|
individualHashMap["type"]!!,
|
||||||
|
message.activeUser!!,
|
||||||
|
R.xml.chip_others
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (individualHashMap["type"] == "file") {
|
||||||
|
itemView.setOnClickListener { v ->
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
|
||||||
|
context!!.startActivity(browserIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
|
||||||
|
textSize = (textSize * 2.5).toFloat()
|
||||||
|
itemView.isSelected = true
|
||||||
|
messageAuthor!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
|
messageText!!.text = messageString
|
||||||
|
|
||||||
|
// parent message handling
|
||||||
|
|
||||||
|
if (!message.isDeleted && message.parentMessage != null) {
|
||||||
|
var parentChatMessage = message.parentMessage
|
||||||
|
parentChatMessage.activeUser = message.activeUser
|
||||||
|
parentChatMessage.imageUrl?.let {
|
||||||
|
quotedMessagePreview?.visibility = View.VISIBLE
|
||||||
|
quotedMessagePreview?.load(it) {
|
||||||
|
addHeader(
|
||||||
|
"Authorization",
|
||||||
|
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
quotedMessagePreview?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
quotedUserName?.text = parentChatMessage.actorDisplayName
|
||||||
|
?: context!!.getText(R.string.nc_nick_guest)
|
||||||
|
quotedMessage?.text = parentChatMessage.text
|
||||||
|
|
||||||
|
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
|
||||||
|
|
||||||
|
if (parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
|
||||||
|
quoteColoredView?.setBackgroundResource(R.color.colorPrimary)
|
||||||
|
} else {
|
||||||
|
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
|
||||||
|
}
|
||||||
|
|
||||||
|
quotedChatMessageView?.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
quotedChatMessageView?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// geo-location
|
||||||
|
|
||||||
|
if (message.messageParameters != null && message.messageParameters.size > 0) {
|
||||||
|
for (key in message.messageParameters.keys) {
|
||||||
|
val individualHashMap: Map<String, String> = message.messageParameters[key]!!
|
||||||
|
if (individualHashMap["type"] == "geo-location") {
|
||||||
|
locationLon = individualHashMap["longitude"]
|
||||||
|
locationLat = individualHashMap["latitude"]
|
||||||
|
locationName = individualHashMap["name"]
|
||||||
|
locationGeoLink = individualHashMap["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webview?.settings?.javaScriptEnabled = true
|
||||||
|
|
||||||
|
webview?.webViewClient = object : WebViewClient() {
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
||||||
|
return if (url != null && (url.startsWith("http://") || url.startsWith("https://"))
|
||||||
|
) {
|
||||||
|
view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val urlStringBuffer = StringBuffer("file:///android_asset/leafletMapMessagePreview.html")
|
||||||
|
urlStringBuffer.append("?mapProviderUrl=" + URLEncoder.encode(mapProviderUrl))
|
||||||
|
urlStringBuffer.append("&mapProviderAttribution=" + URLEncoder.encode(mapProviderAttribution))
|
||||||
|
urlStringBuffer.append("&locationLat=" + URLEncoder.encode(locationLat))
|
||||||
|
urlStringBuffer.append("&locationLon=" + URLEncoder.encode(locationLon))
|
||||||
|
urlStringBuffer.append("&locationName=" + URLEncoder.encode(locationName))
|
||||||
|
urlStringBuffer.append("&locationGeoLink=" + URLEncoder.encode(locationGeoLink))
|
||||||
|
|
||||||
|
webview?.loadUrl(urlStringBuffer.toString())
|
||||||
|
|
||||||
|
webview?.setOnTouchListener(object : View.OnTouchListener {
|
||||||
|
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
||||||
|
when (event?.action) {
|
||||||
|
MotionEvent.ACTION_UP -> openGeoLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
return v?.onTouchEvent(event) ?: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openGeoLink() {
|
||||||
|
if (!locationGeoLink.isNullOrEmpty()) {
|
||||||
|
val geoLinkWithMarker = addMarkerToGeoLink(locationGeoLink!!)
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(geoLinkWithMarker))
|
||||||
|
context!!.startActivity(browserIntent)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||||
|
Log.e(TAG, "locationGeoLink was null or empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addMarkerToGeoLink(locationGeoLink: String): String {
|
||||||
|
return locationGeoLink.replace("geo:", "geo:0,0?q=")
|
||||||
|
}
|
||||||
|
}
|
@ -1,128 +0,0 @@
|
|||||||
package com.nextcloud.talk.adapters.messages
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.webkit.WebView
|
|
||||||
import android.webkit.WebViewClient
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
|
||||||
import autodagger.AutoInjector
|
|
||||||
import butterknife.BindView
|
|
||||||
import butterknife.ButterKnife
|
|
||||||
import com.nextcloud.talk.R
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
|
||||||
import com.stfalcon.chatkit.messages.MessageHolders
|
|
||||||
import java.net.URLEncoder
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
|
||||||
class LocationMessageViewHolder(incomingView: View) : MessageHolders
|
|
||||||
.IncomingTextMessageViewHolder<ChatMessage>(incomingView) {
|
|
||||||
|
|
||||||
private val TAG = "LocationMessageViewHolder"
|
|
||||||
|
|
||||||
var mapProviderUrl: String = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
||||||
var mapProviderAttribution: String = "OpenStreetMap contributors"
|
|
||||||
|
|
||||||
var locationLon: String? = ""
|
|
||||||
var locationLat: String? = ""
|
|
||||||
var locationName: String? = ""
|
|
||||||
var locationGeoLink: String? = ""
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
@BindView(R.id.locationText)
|
|
||||||
var messageText: TextView? = null
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
@BindView(R.id.webview)
|
|
||||||
var webview: WebView? = null
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
@Inject
|
|
||||||
var context: Context? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
ButterKnife.bind(
|
|
||||||
this,
|
|
||||||
itemView
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled", "ClickableViewAccessibility")
|
|
||||||
override fun onBind(message: ChatMessage) {
|
|
||||||
super.onBind(message)
|
|
||||||
sharedApplication!!.componentApplication.inject(this)
|
|
||||||
// if (message.messageType == ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE) {
|
|
||||||
// Log.d(TAG, "handle geolocation here")
|
|
||||||
// messageText!!.text = "geolocation..."
|
|
||||||
// }
|
|
||||||
if (message.messageParameters != null && message.messageParameters.size > 0) {
|
|
||||||
for (key in message.messageParameters.keys) {
|
|
||||||
val individualHashMap: Map<String, String> = message.messageParameters[key]!!
|
|
||||||
if (individualHashMap["type"] == "geo-location") {
|
|
||||||
locationLon = individualHashMap["longitude"]
|
|
||||||
locationLat = individualHashMap["latitude"]
|
|
||||||
locationName = individualHashMap["name"]
|
|
||||||
locationGeoLink = individualHashMap["id"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
webview?.settings?.javaScriptEnabled = true
|
|
||||||
|
|
||||||
webview?.webViewClient = object : WebViewClient() {
|
|
||||||
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
|
||||||
return if (url != null && (url.startsWith("http://") || url.startsWith("https://"))
|
|
||||||
) {
|
|
||||||
view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val urlStringBuffer = StringBuffer("file:///android_asset/leafletMapMessagePreview.html")
|
|
||||||
urlStringBuffer.append("?mapProviderUrl=" + URLEncoder.encode(mapProviderUrl))
|
|
||||||
urlStringBuffer.append("&mapProviderAttribution=" + URLEncoder.encode(mapProviderAttribution))
|
|
||||||
urlStringBuffer.append("&locationLat=" + URLEncoder.encode(locationLat))
|
|
||||||
urlStringBuffer.append("&locationLon=" + URLEncoder.encode(locationLon))
|
|
||||||
urlStringBuffer.append("&locationName=" + URLEncoder.encode(locationName))
|
|
||||||
urlStringBuffer.append("&locationGeoLink=" + URLEncoder.encode(locationGeoLink))
|
|
||||||
|
|
||||||
webview?.loadUrl(urlStringBuffer.toString())
|
|
||||||
|
|
||||||
webview?.setOnTouchListener(object : View.OnTouchListener {
|
|
||||||
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
|
||||||
when (event?.action) {
|
|
||||||
MotionEvent.ACTION_UP -> openGeoLink()
|
|
||||||
}
|
|
||||||
|
|
||||||
return v?.onTouchEvent(event) ?: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openGeoLink() {
|
|
||||||
if (!locationGeoLink.isNullOrEmpty()) {
|
|
||||||
val geoLinkWithMarker = addMarkerToGeoLink(locationGeoLink!!)
|
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(geoLinkWithMarker))
|
|
||||||
context!!.startActivity(browserIntent)
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
|
||||||
Log.e(TAG, "locationGeoLink was null or empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addMarkerToGeoLink(locationGeoLink: String): String {
|
|
||||||
return locationGeoLink.replace("geo:", "geo:0,0?q=")
|
|
||||||
}
|
|
||||||
}
|
|
@ -101,10 +101,6 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
|
|||||||
@Inject
|
@Inject
|
||||||
var appPreferences: AppPreferences? = null
|
var appPreferences: AppPreferences? = null
|
||||||
|
|
||||||
init {
|
|
||||||
ButterKnife.bind(this, itemView)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(message: ChatMessage) {
|
override fun onBind(message: ChatMessage) {
|
||||||
super.onBind(message)
|
super.onBind(message)
|
||||||
sharedApplication!!.componentApplication.inject(this)
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
@ -258,4 +254,8 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
|
|||||||
|
|
||||||
itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.isReplyable)
|
itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.isReplyable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
ButterKnife.bindthis, itemView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,8 +177,6 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
|
|||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
Log.e(TAG, "Error when checking if worker already exists", e);
|
Log.e(TAG, "Error when checking if worker already exists", e);
|
||||||
}
|
}
|
||||||
} else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE) {
|
|
||||||
Log.d(TAG, "handle geolocation here");
|
|
||||||
} else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) {
|
} else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) {
|
||||||
messageText.setText("GIPHY");
|
messageText.setText("GIPHY");
|
||||||
DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText);
|
DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText);
|
||||||
|
@ -0,0 +1,285 @@
|
|||||||
|
package com.nextcloud.talk.adapters.messages
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.net.Uri
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.emoji.widget.EmojiTextView
|
||||||
|
import autodagger.AutoInjector
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import coil.load
|
||||||
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
|
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
|
import com.nextcloud.talk.utils.TextMatchers
|
||||||
|
import com.stfalcon.chatkit.messages.MessageHolders
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.util.HashMap
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
|
class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
|
||||||
|
.OutcomingTextMessageViewHolder<ChatMessage>(incomingView) {
|
||||||
|
|
||||||
|
private val TAG = "LocationMessageViewHolder"
|
||||||
|
|
||||||
|
var mapProviderUrl: String = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
var mapProviderAttribution: String = "OpenStreetMap contributors"
|
||||||
|
|
||||||
|
var locationLon: String? = ""
|
||||||
|
var locationLat: String? = ""
|
||||||
|
var locationName: String? = ""
|
||||||
|
var locationGeoLink: String? = ""
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageText)
|
||||||
|
var messageText: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.messageTime)
|
||||||
|
var messageTimeView: TextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedChatMessageView)
|
||||||
|
var quotedChatMessageView: RelativeLayout? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessageAuthor)
|
||||||
|
var quotedUserName: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessageImage)
|
||||||
|
var quotedMessagePreview: ImageView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quotedMessage)
|
||||||
|
var quotedMessage: EmojiTextView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.quoteColoredView)
|
||||||
|
var quoteColoredView: View? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.checkMark)
|
||||||
|
var checkMark: ImageView? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var context: Context? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@BindView(R.id.webview)
|
||||||
|
var webview: WebView? = null
|
||||||
|
|
||||||
|
private val realView: View
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled", "ClickableViewAccessibility")
|
||||||
|
override fun onBind(message: ChatMessage) {
|
||||||
|
super.onBind(message)
|
||||||
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
|
val messageParameters: HashMap<String, HashMap<String, String>>? = message.messageParameters
|
||||||
|
var messageString: Spannable = SpannableString(message.text)
|
||||||
|
realView.isSelected = false
|
||||||
|
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.white60))
|
||||||
|
val layoutParams = messageTimeView!!.layoutParams as FlexboxLayout.LayoutParams
|
||||||
|
layoutParams.isWrapBefore = false
|
||||||
|
var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
|
||||||
|
if (messageParameters != null && messageParameters.size > 0) {
|
||||||
|
for (key in messageParameters.keys) {
|
||||||
|
val individualHashMap: HashMap<String, String>? = message.messageParameters[key]
|
||||||
|
if (individualHashMap != null) {
|
||||||
|
if (individualHashMap["type"] == "user" || (
|
||||||
|
individualHashMap["type"] == "guest"
|
||||||
|
) || individualHashMap["type"] == "call"
|
||||||
|
) {
|
||||||
|
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
|
||||||
|
messageText!!.context,
|
||||||
|
messageString,
|
||||||
|
individualHashMap["id"]!!,
|
||||||
|
individualHashMap["name"]!!,
|
||||||
|
individualHashMap["type"]!!,
|
||||||
|
message.activeUser,
|
||||||
|
R.xml.chip_others
|
||||||
|
)
|
||||||
|
} else if (individualHashMap["type"] == "file") {
|
||||||
|
realView.setOnClickListener(
|
||||||
|
View.OnClickListener { v: View? ->
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
|
||||||
|
context!!.startActivity(browserIntent)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
|
||||||
|
textSize = (textSize * 2.5).toFloat()
|
||||||
|
layoutParams.isWrapBefore = true
|
||||||
|
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
|
||||||
|
realView.isSelected = true
|
||||||
|
}
|
||||||
|
val resources = sharedApplication!!.resources
|
||||||
|
val bgBubbleColor = if (message.isDeleted) {
|
||||||
|
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted)
|
||||||
|
} else {
|
||||||
|
resources.getColor(R.color.bg_message_list_outcoming_bubble)
|
||||||
|
}
|
||||||
|
if (message.isGrouped) {
|
||||||
|
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||||
|
bgBubbleColor,
|
||||||
|
resources.getColor(R.color.transparent),
|
||||||
|
bgBubbleColor,
|
||||||
|
R.drawable.shape_grouped_outcoming_message
|
||||||
|
)
|
||||||
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
|
} else {
|
||||||
|
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||||
|
bgBubbleColor,
|
||||||
|
resources.getColor(R.color.transparent),
|
||||||
|
bgBubbleColor,
|
||||||
|
R.drawable.shape_outcoming_message
|
||||||
|
)
|
||||||
|
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||||
|
}
|
||||||
|
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
|
messageTimeView!!.layoutParams = layoutParams
|
||||||
|
messageText!!.text = messageString
|
||||||
|
|
||||||
|
// parent message handling
|
||||||
|
|
||||||
|
if (!message.isDeleted && message.parentMessage != null) {
|
||||||
|
var parentChatMessage = message.parentMessage
|
||||||
|
parentChatMessage.activeUser = message.activeUser
|
||||||
|
parentChatMessage.imageUrl?.let {
|
||||||
|
quotedMessagePreview?.visibility = View.VISIBLE
|
||||||
|
quotedMessagePreview?.load(it) {
|
||||||
|
addHeader(
|
||||||
|
"Authorization",
|
||||||
|
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
quotedMessagePreview?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
quotedUserName?.text = parentChatMessage.actorDisplayName
|
||||||
|
?: context!!.getText(R.string.nc_nick_guest)
|
||||||
|
quotedMessage?.text = parentChatMessage.text
|
||||||
|
quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
|
||||||
|
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey))
|
||||||
|
|
||||||
|
quoteColoredView?.setBackgroundResource(R.color.white)
|
||||||
|
|
||||||
|
quotedChatMessageView?.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
quotedChatMessageView?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ->
|
||||||
|
context?.resources?.getDrawable(drawableInt, null)?.let {
|
||||||
|
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
|
||||||
|
checkMark?.setImageDrawable(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkMark?.setContentDescription(readStatusContentDescriptionString)
|
||||||
|
|
||||||
|
// geo-location
|
||||||
|
|
||||||
|
if (message.messageParameters != null && message.messageParameters.size > 0) {
|
||||||
|
for (key in message.messageParameters.keys) {
|
||||||
|
val individualHashMap: Map<String, String> = message.messageParameters[key]!!
|
||||||
|
if (individualHashMap["type"] == "geo-location") {
|
||||||
|
locationLon = individualHashMap["longitude"]
|
||||||
|
locationLat = individualHashMap["latitude"]
|
||||||
|
locationName = individualHashMap["name"]
|
||||||
|
locationGeoLink = individualHashMap["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webview?.settings?.javaScriptEnabled = true
|
||||||
|
|
||||||
|
webview?.webViewClient = object : WebViewClient() {
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
||||||
|
return if (url != null && (url.startsWith("http://") || url.startsWith("https://"))
|
||||||
|
) {
|
||||||
|
view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val urlStringBuffer = StringBuffer("file:///android_asset/leafletMapMessagePreview.html")
|
||||||
|
urlStringBuffer.append("?mapProviderUrl=" + URLEncoder.encode(mapProviderUrl))
|
||||||
|
urlStringBuffer.append("&mapProviderAttribution=" + URLEncoder.encode(mapProviderAttribution))
|
||||||
|
urlStringBuffer.append("&locationLat=" + URLEncoder.encode(locationLat))
|
||||||
|
urlStringBuffer.append("&locationLon=" + URLEncoder.encode(locationLon))
|
||||||
|
urlStringBuffer.append("&locationName=" + URLEncoder.encode(locationName))
|
||||||
|
urlStringBuffer.append("&locationGeoLink=" + URLEncoder.encode(locationGeoLink))
|
||||||
|
|
||||||
|
webview?.loadUrl(urlStringBuffer.toString())
|
||||||
|
|
||||||
|
webview?.setOnTouchListener(object : View.OnTouchListener {
|
||||||
|
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
||||||
|
when (event?.action) {
|
||||||
|
MotionEvent.ACTION_UP -> openGeoLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
return v?.onTouchEvent(event) ?: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openGeoLink() {
|
||||||
|
if (!locationGeoLink.isNullOrEmpty()) {
|
||||||
|
val geoLinkWithMarker = addMarkerToGeoLink(locationGeoLink!!)
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(geoLinkWithMarker))
|
||||||
|
context!!.startActivity(browserIntent)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||||
|
Log.e(TAG, "locationGeoLink was null or empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addMarkerToGeoLink(locationGeoLink: String): String {
|
||||||
|
return locationGeoLink.replace("geo:", "geo:0,0?q=")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
ButterKnife.bind(this, itemView)
|
||||||
|
this.realView = itemView
|
||||||
|
}
|
||||||
|
}
|
@ -78,12 +78,13 @@ import com.facebook.imagepipeline.image.CloseableImage
|
|||||||
import com.google.android.flexbox.FlexboxLayout
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.activities.MagicCallActivity
|
import com.nextcloud.talk.activities.MagicCallActivity
|
||||||
import com.nextcloud.talk.adapters.messages.LocationMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
|
||||||
|
import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder
|
||||||
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
|
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
|
||||||
import com.nextcloud.talk.api.NcApi
|
import com.nextcloud.talk.api.NcApi
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
@ -437,10 +438,10 @@ class ChatController(args: Bundle) :
|
|||||||
|
|
||||||
messageHolders.registerContentType(
|
messageHolders.registerContentType(
|
||||||
CONTENT_TYPE_LOCATION,
|
CONTENT_TYPE_LOCATION,
|
||||||
LocationMessageViewHolder::class.java,
|
IncomingLocationMessageViewHolder::class.java,
|
||||||
R.layout.item_custom_location_message,
|
R.layout.item_custom_incoming_location_message,
|
||||||
LocationMessageViewHolder::class.java,
|
OutcomingLocationMessageViewHolder::class.java,
|
||||||
R.layout.item_custom_location_message,
|
R.layout.item_custom_outcoming_location_message,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Nextcloud Talk application
|
||||||
|
~
|
||||||
|
~ @author Mario Danic
|
||||||
|
~ @author Marcel Hibbe
|
||||||
|
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||||
|
~ Copyright (C) 2021 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
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"
|
||||||
|
app:justifyContent="flex_end">
|
||||||
|
|
||||||
|
<include layout="@layout/item_message_quote" android:visibility="gone"/>
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/webview"
|
||||||
|
android:layout_width="400dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
android:id="@+id/messageAuthor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:textColor="@color/textColorMaxContrast"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
android:id="@id/messageText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_alignSelf="flex_start"
|
||||||
|
app:layout_flexGrow="1"
|
||||||
|
app:layout_wrapBefore="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@id/messageTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/messageText"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_alignSelf="center" />
|
||||||
|
|
||||||
|
</com.google.android.flexbox.FlexboxLayout>
|
||||||
|
</RelativeLayout>
|
@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Nextcloud Talk application
|
|
||||||
~
|
|
||||||
~ @author Mario Danic
|
|
||||||
~ @author Marcel Hibbe
|
|
||||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
|
||||||
~ Copyright (C) 2021 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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="200dp"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="8dp">
|
|
||||||
|
|
||||||
<WebView
|
|
||||||
android:id="@+id/webview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/locationText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textColor="@color/warm_grey_four"
|
|
||||||
android:textSize="12sp"
|
|
||||||
tools:text="17:30"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
app:layout_alignSelf="center"
|
|
||||||
app:layout_flexGrow="1"
|
|
||||||
app:layout_wrapBefore="false"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Nextcloud Talk application
|
||||||
|
~
|
||||||
|
~ @author Mario Danic
|
||||||
|
~ @author Andy Scherzinger
|
||||||
|
~ 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="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
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 layout="@layout/item_message_quote" android:visibility="gone"/>
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/webview"
|
||||||
|
android:layout_width="400dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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:textColorHighlight="@color/nc_grey"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
tools:text="Talk to ayou later!" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@id/messageTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/messageText"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_alignSelf="center"
|
||||||
|
tools:text="10:35" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/checkMark"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/messageTime"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_alignSelf="center"
|
||||||
|
android:contentDescription="@null" />
|
||||||
|
|
||||||
|
</com.google.android.flexbox.FlexboxLayout>
|
||||||
|
</RelativeLayout>
|
Loading…
Reference in New Issue
Block a user