mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-19 10:45:13 +01:00
parent
1acf508024
commit
7d2fffd165
@ -305,6 +305,7 @@ dependencies {
|
|||||||
implementation 'com.github.mario:PopupBubble:a365177d96'
|
implementation 'com.github.mario:PopupBubble:a365177d96'
|
||||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||||
implementation 'eu.medsea.mimeutil:mime-util:2.1.3'
|
implementation 'eu.medsea.mimeutil:mime-util:2.1.3'
|
||||||
|
implementation 'com.github.izjumovfs:SwipeToReply:1.0.1'
|
||||||
|
|
||||||
implementation 'com.afollestad.material-dialogs:core:3.1.0'
|
implementation 'com.afollestad.material-dialogs:core:3.1.0'
|
||||||
implementation 'com.afollestad.material-dialogs:datetime:3.1.0'
|
implementation 'com.afollestad.material-dialogs:datetime:3.1.0'
|
||||||
|
@ -46,7 +46,6 @@ open class ChatPresenter<T : Any>(context: Context, private val onElementClickPa
|
|||||||
override fun onBind(page: Page, holder: Holder, element: Element<T>, payloads: List<Any>) {
|
override fun onBind(page: Page, holder: Holder, element: Element<T>, payloads: List<Any>) {
|
||||||
super.onBind(page, holder, element, payloads)
|
super.onBind(page, holder, element, payloads)
|
||||||
|
|
||||||
|
|
||||||
holder.itemView.setOnLongClickListener {
|
holder.itemView.setOnLongClickListener {
|
||||||
onElementLongClick?.invoke(page, holder, element, mapOf())
|
onElementLongClick?.invoke(page, holder, element, mapOf())
|
||||||
true
|
true
|
||||||
|
@ -36,12 +36,12 @@ import android.text.Editable
|
|||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
@ -54,6 +54,7 @@ import com.bluelinelabs.conductor.archlifecycle.ControllerLifecycleOwner
|
|||||||
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
||||||
|
import com.capybaralabs.swipetoreply.ISwipeControllerActions
|
||||||
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.callbacks.MentionAutocompleteCallback
|
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
|
||||||
@ -68,6 +69,7 @@ import com.nextcloud.talk.newarch.local.models.getMaxMessageLength
|
|||||||
import com.nextcloud.talk.newarch.local.models.toUserEntity
|
import com.nextcloud.talk.newarch.local.models.toUserEntity
|
||||||
import com.nextcloud.talk.newarch.mvvm.BaseView
|
import com.nextcloud.talk.newarch.mvvm.BaseView
|
||||||
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
|
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
|
||||||
|
import com.nextcloud.talk.newarch.utils.ChatSwipeCallback
|
||||||
import com.nextcloud.talk.newarch.utils.Images
|
import com.nextcloud.talk.newarch.utils.Images
|
||||||
import com.nextcloud.talk.newarch.utils.NetworkComponents
|
import com.nextcloud.talk.newarch.utils.NetworkComponents
|
||||||
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
||||||
@ -93,7 +95,6 @@ import com.vanniktech.emoji.EmojiPopup
|
|||||||
import kotlinx.android.synthetic.main.controller_chat.view.*
|
import kotlinx.android.synthetic.main.controller_chat.view.*
|
||||||
import kotlinx.android.synthetic.main.item_message_quote.view.*
|
import kotlinx.android.synthetic.main.item_message_quote.view.*
|
||||||
import kotlinx.android.synthetic.main.lobby_view.view.*
|
import kotlinx.android.synthetic.main.lobby_view.view.*
|
||||||
import kotlinx.android.synthetic.main.rv_chat_item.view.*
|
|
||||||
import kotlinx.android.synthetic.main.view_message_input.view.*
|
import kotlinx.android.synthetic.main.view_message_input.view.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.parceler.Parcels
|
import org.parceler.Parcels
|
||||||
@ -247,6 +248,21 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
view.cancelReplyButton.setOnClickListener { hideReplyView() }
|
view.cancelReplyButton.setOnClickListener { hideReplyView() }
|
||||||
|
|
||||||
|
val controller = ChatSwipeCallback(messagesAdapter, context, ISwipeControllerActions {
|
||||||
|
val element = messagesAdapter.elementAt(it)
|
||||||
|
if (element != null) {
|
||||||
|
val adapterChatElement = element.element as Element<ChatElement>
|
||||||
|
if (adapterChatElement.data is ChatElement) {
|
||||||
|
val chatElement = adapterChatElement.data as ChatElement
|
||||||
|
view.messagesRecyclerView.postDelayed({ showReplyView(chatElement.data as ChatMessage)}, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val itemTouchHelper = ItemTouchHelper(controller)
|
||||||
|
itemTouchHelper.attachToRecyclerView(view.messagesRecyclerView)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,6 +375,49 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showReplyView(chatMessage: ChatMessage) {
|
||||||
|
view?.let {
|
||||||
|
with(it.messageInputView) {
|
||||||
|
attachmentButton.isVisible = false
|
||||||
|
attachmentButtonSpace.isVisible = false
|
||||||
|
cancelReplyButton.isVisible = true
|
||||||
|
quotedChatText.maxLines = 2
|
||||||
|
quotedChatText.ellipsize = TextUtils.TruncateAt.END
|
||||||
|
quotedChatText.text = chatMessage.text
|
||||||
|
quotedAuthor.text = chatMessage.user.name
|
||||||
|
quotedMessageTime.text = DateFormatter.format(chatMessage.createdAt, DateFormatter.Template.TIME)
|
||||||
|
|
||||||
|
loadImage(quotedUserAvatar, chatMessage.user.avatar)
|
||||||
|
|
||||||
|
chatMessage.imageUrl?.let { previewImageUrl ->
|
||||||
|
if (previewImageUrl == "no-preview") {
|
||||||
|
if (chatMessage.selectedIndividualHashMap?.containsKey("mimetype") == true) {
|
||||||
|
quotedPreviewImage.isVisible = true
|
||||||
|
networkComponents.getImageLoader(viewModel.user).loadAny(context, DrawableUtils.getDrawableResourceIdForMimeType(chatMessage.selectedIndividualHashMap!!["mimetype"])) {
|
||||||
|
target(quotedPreviewImage) }
|
||||||
|
} else {
|
||||||
|
quotedPreviewImage.isVisible = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quotedPreviewImage.isVisible = true
|
||||||
|
val mutableMap = mutableMapOf<String, String>()
|
||||||
|
if (chatMessage.selectedIndividualHashMap?.containsKey("mimetype") == true) {
|
||||||
|
mutableMap["mimetype"] = chatMessage.selectedIndividualHashMap!!["mimetype"]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
loadImage(quotedPreviewImage, previewImageUrl, mutableMap)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
quotedPreviewImage.isVisible = false
|
||||||
|
}
|
||||||
|
quotedMessageLayout.tag = chatMessage.jsonMessageId
|
||||||
|
quotedMessageLayout.isVisible = true
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onElementLongClick(page: Page, holder: Presenter.Holder, element: Element<ChatElement>, payload: Map<String, String>) {
|
private fun onElementLongClick(page: Page, holder: Presenter.Holder, element: Element<ChatElement>, payload: Map<String, String>) {
|
||||||
if (element.type == ChatElementTypes.CHAT_MESSAGE.ordinal) {
|
if (element.type == ChatElementTypes.CHAT_MESSAGE.ordinal) {
|
||||||
element.data?.let { chatElement ->
|
element.data?.let { chatElement ->
|
||||||
@ -367,6 +426,7 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface {
|
|||||||
chatMessage = chatMessage.parentMessage!!
|
chatMessage = chatMessage.parentMessage!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PopupMenu(this.context, holder.itemView, Gravity.START).apply {
|
PopupMenu(this.context, holder.itemView, Gravity.START).apply {
|
||||||
setOnMenuItemClickListener { item ->
|
setOnMenuItemClickListener { item ->
|
||||||
when (item?.itemId) {
|
when (item?.itemId) {
|
||||||
@ -379,45 +439,7 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_reply_to_message -> {
|
R.id.action_reply_to_message -> {
|
||||||
view?.let {
|
showReplyView(chatMessage)
|
||||||
with(it.messageInputView) {
|
|
||||||
attachmentButton.isVisible = false
|
|
||||||
attachmentButtonSpace.isVisible = false
|
|
||||||
cancelReplyButton.isVisible = true
|
|
||||||
quotedChatText.maxLines = 2
|
|
||||||
quotedChatText.ellipsize = TextUtils.TruncateAt.END
|
|
||||||
quotedChatText.text = chatMessage.text
|
|
||||||
quotedAuthor.text = chatMessage.user.name
|
|
||||||
quotedMessageTime.text = DateFormatter.format(chatMessage.createdAt, DateFormatter.Template.TIME)
|
|
||||||
|
|
||||||
loadImage(quotedUserAvatar, chatMessage.user.avatar)
|
|
||||||
|
|
||||||
chatMessage.imageUrl?.let { previewImageUrl ->
|
|
||||||
if (previewImageUrl == "no-preview") {
|
|
||||||
if (chatMessage.selectedIndividualHashMap?.containsKey("mimetype") == true) {
|
|
||||||
quotedPreviewImage.isVisible = true
|
|
||||||
networkComponents.getImageLoader(viewModel.user).loadAny(context, DrawableUtils.getDrawableResourceIdForMimeType(chatMessage.selectedIndividualHashMap!!["mimetype"])) {
|
|
||||||
target(quotedPreviewImage) }
|
|
||||||
} else {
|
|
||||||
quotedPreviewImage.isVisible = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quotedPreviewImage.isVisible = true
|
|
||||||
val mutableMap = mutableMapOf<String, String>()
|
|
||||||
if (chatMessage.selectedIndividualHashMap?.containsKey("mimetype") == true) {
|
|
||||||
mutableMap["mimetype"] = chatMessage.selectedIndividualHashMap!!["mimetype"]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
loadImage(quotedPreviewImage, previewImageUrl, mutableMap)
|
|
||||||
}
|
|
||||||
} ?: run {
|
|
||||||
quotedPreviewImage.isVisible = false
|
|
||||||
}
|
|
||||||
quotedMessageLayout.tag = chatMessage.jsonMessageId
|
|
||||||
quotedMessageLayout.isVisible = true
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.nextcloud.talk.newarch.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.capybaralabs.swipetoreply.ISwipeControllerActions
|
||||||
|
import com.capybaralabs.swipetoreply.SwipeController
|
||||||
|
import com.nextcloud.talk.newarch.features.chat.ChatElement
|
||||||
|
import com.nextcloud.talk.newarch.features.chat.ChatElementTypes
|
||||||
|
import com.otaliastudios.elements.Adapter
|
||||||
|
import com.otaliastudios.elements.Element
|
||||||
|
|
||||||
|
class ChatSwipeCallback(private val adapter: Adapter, context: Context?, swipeControllerActions: ISwipeControllerActions?) : SwipeController(context, swipeControllerActions) {
|
||||||
|
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||||
|
val position: Int = viewHolder.adapterPosition
|
||||||
|
val element = adapter.elementAt(position)
|
||||||
|
if (element != null) {
|
||||||
|
val adapterChatElement = element.element as Element<ChatElement>
|
||||||
|
if (adapterChatElement.data is ChatElement) {
|
||||||
|
val chatElement = adapterChatElement.data as ChatElement
|
||||||
|
if (chatElement.elementType == ChatElementTypes.CHAT_MESSAGE) {
|
||||||
|
return super.getMovementFlags(recyclerView, viewHolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
12
app/src/main/res/drawable/ic_round_shape.xml
Normal file
12
app/src/main/res/drawable/ic_round_shape.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
|
||||||
|
<solid
|
||||||
|
android:color="@color/bg_message_list_incoming_bubble"/>
|
||||||
|
|
||||||
|
<size
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"/>
|
||||||
|
</shape>
|
@ -3,7 +3,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
Loading…
Reference in New Issue
Block a user