From d9b6f0482afcd7e248b1b449b45a4117bb5a718c Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 12 Oct 2023 12:12:38 +0200 Subject: [PATCH] PoC for blurring conversation except for long pressed chat bubble --- .../chat/adapter/ConversationEventAdapter.kt | 45 +++-- .../chat/fragment/ConversationFragment.kt | 74 +++++++ .../ui/main/chat/model/EventLogModel.kt | 2 +- .../java/org/linphone/utils/DialogUtils.kt | 8 +- .../res/drawable/arrow_bend_up_left_bold.xml | 9 + .../res/drawable/arrow_bend_up_right_bold.xml | 9 + .../main/res/layout/chat_bubble_incoming.xml | 5 +- .../chat_conversation_long_press_menu.xml | 191 ++++++++++++++++++ .../main/res/layout/operation_in_progress.xml | 3 +- app/src/main/res/values/colors.xml | 1 - app/src/main/res/values/dimen.xml | 2 + app/src/main/res/values/strings.xml | 9 + 12 files changed, 329 insertions(+), 29 deletions(-) create mode 100644 app/src/main/res/drawable/arrow_bend_up_left_bold.xml create mode 100644 app/src/main/res/drawable/arrow_bend_up_right_bold.xml create mode 100644 app/src/main/res/layout/chat_conversation_long_press_menu.xml diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt index 2a3307c9b..94dbf52f3 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt @@ -23,10 +23,10 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import java.lang.Math.abs import org.linphone.R import org.linphone.core.ChatMessage import org.linphone.databinding.ChatBubbleIncomingBinding @@ -35,6 +35,7 @@ import org.linphone.databinding.ChatEventBinding import org.linphone.ui.main.chat.model.ChatMessageModel import org.linphone.ui.main.chat.model.EventLogModel import org.linphone.ui.main.chat.model.EventModel +import org.linphone.utils.Event class ConversationEventAdapter( private val viewLifecycleOwner: LifecycleOwner @@ -47,7 +48,7 @@ class ConversationEventAdapter( const val MAX_TIME_TO_GROUP_MESSAGES = 60 // 1 minute } - var selectedAdapterPosition = -1 + val chatMessageLongPressEvent = MutableLiveData>>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { @@ -59,13 +60,12 @@ class ConversationEventAdapter( override fun getItemViewType(position: Int): Int { val data = getItem(position) - if (data.data is ChatMessageModel) { - if (data.data.isOutgoing) { - return OUTGOING_CHAT_MESSAGE - } - return INCOMING_CHAT_MESSAGE + if (data.isEvent) return EVENT + + if ((data.model as ChatMessageModel).isOutgoing) { + return OUTGOING_CHAT_MESSAGE } - return EVENT + return INCOMING_CHAT_MESSAGE } private fun createIncomingChatBubble(parent: ViewGroup): IncomingBubbleViewHolder { @@ -101,24 +101,19 @@ class ConversationEventAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val eventLog = getItem(position) when (holder) { - is IncomingBubbleViewHolder -> holder.bind(eventLog.data as ChatMessageModel) - is OutgoingBubbleViewHolder -> holder.bind(eventLog.data as ChatMessageModel) - is EventViewHolder -> holder.bind(eventLog.data as EventModel) + is IncomingBubbleViewHolder -> holder.bind(eventLog.model as ChatMessageModel) + is OutgoingBubbleViewHolder -> holder.bind(eventLog.model as ChatMessageModel) + is EventViewHolder -> holder.bind(eventLog.model as EventModel) } } - fun resetSelection() { - notifyItemChanged(selectedAdapterPosition) - selectedAdapterPosition = -1 - } - fun groupPreviousItem(item: ChatMessageModel, position: Int): Boolean { return if (position == 0) { false } else { val previous = position - 1 if (getItemViewType(position) == getItemViewType(previous)) { - val previousItem = getItem(previous).data as ChatMessageModel + val previousItem = getItem(previous).model as ChatMessageModel if (kotlin.math.abs(item.timestamp - previousItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) { previousItem.fromSipUri == item.fromSipUri } else { @@ -136,7 +131,7 @@ class ConversationEventAdapter( } else { val next = position + 1 if (getItemViewType(next) == getItemViewType(position)) { - val nextItem = getItem(next).data as ChatMessageModel + val nextItem = getItem(next).model as ChatMessageModel if (kotlin.math.abs(item.timestamp - nextItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) { nextItem.fromSipUri != item.fromSipUri } else { @@ -159,6 +154,14 @@ class ConversationEventAdapter( isGroupedWithPreviousOne = groupPreviousItem(message, position) isLastOneOfGroup = isLastItemOfGroup(message, position) + setOnLongClickListener { + val screen = IntArray(2) + root.getLocationOnScreen(screen) + + chatMessageLongPressEvent.value = Event(Pair(message, screen[1])) + true + } + lifecycleOwner = viewLifecycleOwner executePendingBindings() } @@ -199,8 +202,8 @@ class ConversationEventAdapter( return if (oldItem.isEvent && newItem.isEvent) { oldItem.notifyId == newItem.notifyId } else if (!oldItem.isEvent && !newItem.isEvent) { - val oldData = (oldItem.data as ChatMessageModel) - val newData = (newItem.data as ChatMessageModel) + val oldData = (oldItem.model as ChatMessageModel) + val newData = (newItem.model as ChatMessageModel) oldData.id.isNotEmpty() && oldData.id == newData.id } else { false @@ -211,7 +214,7 @@ class ConversationEventAdapter( return if (oldItem.isEvent && newItem.isEvent) { true } else { - val newData = (newItem.data as ChatMessageModel) + val newData = (newItem.model as ChatMessageModel) newData.state.value == ChatMessage.State.Displayed } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt index bd65cf033..59daa1f17 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt @@ -19,22 +19,38 @@ */ package org.linphone.ui.main.chat.fragment +import android.app.Dialog +import android.graphics.Rect +import android.graphics.RenderEffect +import android.graphics.Shader +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager import androidx.annotation.UiThread +import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.doOnPreDraw +import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.bottomsheet.BottomSheetBehavior +import kotlin.math.max +import kotlin.math.min +import org.linphone.R import org.linphone.core.tools.Log import org.linphone.databinding.ChatConversationFragmentBinding +import org.linphone.databinding.ChatConversationLongPressMenuBinding import org.linphone.ui.main.chat.adapter.ConversationEventAdapter +import org.linphone.ui.main.chat.model.ChatMessageModel import org.linphone.ui.main.chat.viewmodel.ConversationViewModel import org.linphone.ui.main.fragment.GenericFragment +import org.linphone.utils.AppUtils import org.linphone.utils.Event @UiThread @@ -115,6 +131,12 @@ class ConversationFragment : GenericFragment() { val layoutManager = LinearLayoutManager(requireContext()) binding.eventsList.layoutManager = layoutManager + adapter.chatMessageLongPressEvent.observe(viewLifecycleOwner) { + it.consume { pair -> + showChatMessageLongPressMenu(pair.first, pair.second) + } + } + viewModel.events.observe(viewLifecycleOwner) { items -> val currentCount = adapter.itemCount adapter.submitList(items) @@ -146,4 +168,56 @@ class ConversationFragment : GenericFragment() { }*/ } } + + private fun showChatMessageLongPressMenu(chatMessageModel: ChatMessageModel, yPosition: Int) { + // TODO: handle backward compat for blurring + val blurEffect = RenderEffect.createBlurEffect(16F, 16F, Shader.TileMode.MIRROR) + binding.root.setRenderEffect(blurEffect) + + val dialog = Dialog(requireContext(), R.style.Theme_LinphoneDialog) + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) + + val layout: ChatConversationLongPressMenuBinding = DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.chat_conversation_long_press_menu, + null, + false + ) + layout.root.setOnClickListener { + dialog.dismiss() + binding.root.setRenderEffect(null) + } + + layout.model = chatMessageModel + val screenY = yPosition - AppUtils.getDimension( + R.dimen.chat_bubble_long_press_menu_bubble_offset + ) + + val rect = Rect() + binding.root.getGlobalVisibleRect(rect) + val height = rect.height() + val percent = ((screenY * 100) / height) + // To prevent bubble from being behind the bottom actions or the emojis to be out of the screen + val guideline = min(max(0.1f, (percent / 100)), 0.4f) // value must be between 0 and 1 + + val constraintLayout = layout.constraintLayout + val set = ConstraintSet() + set.clone(constraintLayout) + set.setGuidelinePercent(R.id.guideline, guideline) + set.applyTo(constraintLayout) + + dialog.setContentView(layout.root) + + dialog.window + ?.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT + ) + val d: Drawable = ColorDrawable( + AppUtils.getColor(R.color.gray_300) + ) + d.alpha = 102 + dialog.window?.setBackgroundDrawable(d) + dialog.show() + } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt index fe3e360d4..9e0e198af 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt @@ -28,7 +28,7 @@ class EventLogModel @WorkerThread constructor(eventLog: EventLog, avatarModel: C val isEvent = type != EventLog.Type.ConferenceChatMessage - val data = if (isEvent) { + val model = if (isEvent) { EventModel(eventLog) } else { ChatMessageModel(eventLog.chatMessage!!, avatarModel) diff --git a/app/src/main/java/org/linphone/utils/DialogUtils.kt b/app/src/main/java/org/linphone/utils/DialogUtils.kt index f8b21a28d..26c0f7529 100644 --- a/app/src/main/java/org/linphone/utils/DialogUtils.kt +++ b/app/src/main/java/org/linphone/utils/DialogUtils.kt @@ -305,15 +305,15 @@ class DialogUtils { dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) dialog.setContentView(binding.root) - val d: Drawable = ColorDrawable( - AppUtils.getColor(R.color.gray_main2_800_alpha_65) - ) - // d.alpha = 166 dialog.window ?.setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT ) + val d: Drawable = ColorDrawable( + AppUtils.getColor(R.color.gray_300) + ) + d.alpha = 102 dialog.window?.setBackgroundDrawable(d) return dialog } diff --git a/app/src/main/res/drawable/arrow_bend_up_left_bold.xml b/app/src/main/res/drawable/arrow_bend_up_left_bold.xml new file mode 100644 index 000000000..4ae3f6839 --- /dev/null +++ b/app/src/main/res/drawable/arrow_bend_up_left_bold.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/arrow_bend_up_right_bold.xml b/app/src/main/res/drawable/arrow_bend_up_right_bold.xml new file mode 100644 index 000000000..26252d3b5 --- /dev/null +++ b/app/src/main/res/drawable/arrow_bend_up_right_bold.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/chat_bubble_incoming.xml b/app/src/main/res/layout/chat_bubble_incoming.xml index 71af95afc..b7b2e16c7 100644 --- a/app/src/main/res/layout/chat_bubble_incoming.xml +++ b/app/src/main/res/layout/chat_bubble_incoming.xml @@ -7,6 +7,9 @@ + @@ -19,7 +22,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/operation_in_progress.xml b/app/src/main/res/layout/operation_in_progress.xml index fefb6452c..ec0d53ab8 100644 --- a/app/src/main/res/layout/operation_in_progress.xml +++ b/app/src/main/res/layout/operation_in_progress.xml @@ -12,7 +12,8 @@ #80FFEACB #22334D - #A622334D #364860 #4E6074 #6C7A87 diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 90c001aec..00d1ac237 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -55,4 +55,6 @@ 4dp 16dp + 60dp + 105dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c84be9c9f..e42e9994a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,6 +15,12 @@ linphone_notification_service_id linphone_notification_chat_id + ❤️ + 👍 + 😂 + 😮 + 😢 + GNU General Public License v3.0 © Belledonne Communications 2010-2023 linphone-android@belledonne-communications.com @@ -272,6 +278,9 @@ Delete history Delete Invite + Reply + Forward + Copy Call history New call