mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-22 22:28:13 +00:00
Added message forward + a few small improvements
This commit is contained in:
parent
678949aff2
commit
d52c12606f
8 changed files with 115 additions and 51 deletions
|
|
@ -278,6 +278,14 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
}
|
||||
} else {
|
||||
sendMessageViewModel.configureChatRoom(viewModel.chatRoom)
|
||||
|
||||
// Wait for chat room to be ready before trying to forward a message in it
|
||||
sharedViewModel.messageToForwardEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { toForward ->
|
||||
Log.i("$TAG Found message to forward")
|
||||
sendMessageViewModel.forwardMessage(toForward)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,7 +305,7 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
|
||||
viewModel.scrollToBottomEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
binding.eventsList.scrollToPosition(adapter.itemCount - 1)
|
||||
scrollToBottom()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +524,7 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
sendMessageViewModel.isEmojiPickerOpen.value = false
|
||||
|
||||
// Scroll to bottom when keyboard is opened so latest message is visible
|
||||
binding.eventsList.scrollToPosition(adapter.itemCount - 1)
|
||||
scrollToBottom()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -533,7 +541,7 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
if (viewModel.scrollingPosition != SCROLLING_POSITION_NOT_SET) {
|
||||
binding.eventsList.scrollToPosition(viewModel.scrollingPosition)
|
||||
} else {
|
||||
binding.eventsList.scrollToPosition(adapter.itemCount - 1)
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -573,34 +581,40 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
currentChatMessageModelForBottomSheet = null
|
||||
}
|
||||
|
||||
private fun scrollToFirstUnreadMessageOrBottom(smooth: Boolean): Boolean {
|
||||
if (adapter.itemCount > 0) {
|
||||
val recyclerView = binding.eventsList
|
||||
private fun scrollToBottom() {
|
||||
if (adapter.itemCount == 0) return
|
||||
|
||||
// Scroll to first unread message if any, unless we are already on it
|
||||
val firstUnreadMessagePosition = adapter.getFirstUnreadMessagePosition()
|
||||
val currentPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
|
||||
val indexToScrollTo = if (firstUnreadMessagePosition != -1 && firstUnreadMessagePosition != currentPosition) {
|
||||
firstUnreadMessagePosition
|
||||
} else {
|
||||
adapter.itemCount - 1
|
||||
}
|
||||
|
||||
Log.i(
|
||||
"$TAG Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition"
|
||||
)
|
||||
if (smooth) {
|
||||
recyclerView.smoothScrollToPosition(indexToScrollTo)
|
||||
} else {
|
||||
recyclerView.scrollToPosition(indexToScrollTo)
|
||||
}
|
||||
|
||||
if (firstUnreadMessagePosition == 0) {
|
||||
// Return true only if all unread messages don't fit in the recyclerview height
|
||||
return recyclerView.computeVerticalScrollRange() > recyclerView.height
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
delay(100)
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.eventsList.scrollToPosition(adapter.itemCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun scrollToFirstUnreadMessageOrBottom(smooth: Boolean) {
|
||||
if (adapter.itemCount == 0) return
|
||||
|
||||
val recyclerView = binding.eventsList
|
||||
// Scroll to first unread message if any, unless we are already on it
|
||||
val firstUnreadMessagePosition = adapter.getFirstUnreadMessagePosition()
|
||||
val currentPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
|
||||
val indexToScrollTo = if (firstUnreadMessagePosition != -1 && firstUnreadMessagePosition != currentPosition) {
|
||||
firstUnreadMessagePosition
|
||||
} else {
|
||||
adapter.itemCount - 1
|
||||
}
|
||||
|
||||
Log.i(
|
||||
"$TAG Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition"
|
||||
)
|
||||
if (smooth) {
|
||||
recyclerView.smoothScrollToPosition(indexToScrollTo)
|
||||
} else {
|
||||
recyclerView.scrollToPosition(indexToScrollTo)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showChatMessageLongPressMenu(chatMessageModel: MessageModel) {
|
||||
|
|
@ -648,6 +662,16 @@ class ConversationFragment : SlidingPaneChildFragment() {
|
|||
dialog.dismiss()
|
||||
}
|
||||
|
||||
layout.setForwardClickListener {
|
||||
Log.i("$TAG Forwarding message, going back to conversations list")
|
||||
// Remove observer before setting the message to forward
|
||||
// as we don't want to forward it in this chat room
|
||||
sharedViewModel.messageToForwardEvent.removeObservers(viewLifecycleOwner)
|
||||
sharedViewModel.messageToForwardEvent.postValue(Event(chatMessageModel))
|
||||
dialog.dismiss()
|
||||
goBack()
|
||||
}
|
||||
|
||||
layout.setReplyClickListener {
|
||||
Log.i("$TAG Updating sending area to reply to selected message")
|
||||
sendMessageViewModel.replyToMessage(chatMessageModel)
|
||||
|
|
|
|||
|
|
@ -273,6 +273,39 @@ class ConversationsListFragment : AbstractTopBarFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
sharedViewModel.messageToForwardEvent.observe(viewLifecycleOwner) { event ->
|
||||
if (!event.consumed()) {
|
||||
// Do not consume it yet
|
||||
val message = getString(R.string.toast_message_waiting_to_be_forwarded)
|
||||
val icon = R.drawable.forward
|
||||
(requireActivity() as MainActivity).showGreenToast(message, icon)
|
||||
Log.i("$TAG Found a message waiting to be forwarded")
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.filesToShareFromIntent.observe(viewLifecycleOwner) { filesToShare ->
|
||||
val count = filesToShare.size
|
||||
if (count > 0) {
|
||||
val message = AppUtils.getStringWithPlural(
|
||||
R.plurals.toast_files_waiting_to_be_shared,
|
||||
count,
|
||||
filesToShare.size.toString()
|
||||
)
|
||||
val icon = R.drawable.file
|
||||
(requireActivity() as MainActivity).showGreenToast(message, icon)
|
||||
Log.i("$TAG Found [$count] files waiting to be shared")
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.textToShareFromIntent.observe(viewLifecycleOwner) { textToShare ->
|
||||
if (textToShare.isNotEmpty()) {
|
||||
val message = getString(R.string.toast_text_waiting_to_be_shared)
|
||||
val icon = R.drawable.file
|
||||
(requireActivity() as MainActivity).showGreenToast(message, icon)
|
||||
Log.i("$TAG Found text waiting to be shared")
|
||||
}
|
||||
}
|
||||
|
||||
// TopBarFragment related
|
||||
|
||||
setViewModelAndTitle(
|
||||
|
|
@ -311,19 +344,6 @@ class ConversationsListFragment : AbstractTopBarFragment() {
|
|||
Log.e("$TAG Failed to unregister data observer to adapter: $e")
|
||||
}
|
||||
|
||||
val filesToShare = sharedViewModel.filesToShareFromIntent.value.orEmpty()
|
||||
if (filesToShare.isNotEmpty()) {
|
||||
val count = filesToShare.size
|
||||
val message = AppUtils.getStringWithPlural(
|
||||
R.plurals.toast_files_waiting_to_be_shared,
|
||||
count,
|
||||
filesToShare.size.toString()
|
||||
)
|
||||
val icon = R.drawable.file
|
||||
(requireActivity() as MainActivity).showGreenToast(message, icon)
|
||||
Log.i("$TAG Found [$count] files waiting to be shared")
|
||||
}
|
||||
|
||||
// Scroll to top when fragment is resumed
|
||||
binding.conversationsList.scrollToPosition(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -348,6 +348,18 @@ class SendMessageInConversationViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun forwardMessage(toForward: MessageModel) {
|
||||
coreContext.postOnCoreThread {
|
||||
if (::chatRoom.isInitialized) {
|
||||
val messageToForward = toForward.chatMessage
|
||||
val forwardedMessage = chatRoom.createForwardMessage(messageToForward)
|
||||
Log.i("$TAG Sending forwarded message")
|
||||
forwardedMessage.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun startVoiceMessageRecording() {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.annotation.UiThread
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.ui.main.chat.model.MessageModel
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class SharedMainViewModel @UiThread constructor() : ViewModel() {
|
||||
|
|
@ -102,6 +103,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val filesToShareFromIntent = MutableLiveData<ArrayList<String>>()
|
||||
|
||||
val messageToForwardEvent = MutableLiveData<Event<MessageModel>>()
|
||||
|
||||
var displayedChatRoom: ChatRoom? = null // Prevents the need to go look for the chat room
|
||||
val showConversationEvent: MutableLiveData<Event<Pair<String, String>>> by lazy {
|
||||
MutableLiveData<Event<Pair<String, String>>>()
|
||||
|
|
|
|||
|
|
@ -37,4 +37,9 @@ open class Event<out T> @AnyThread constructor(private val content: T) {
|
|||
handleContent(content)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun consumed(): Boolean {
|
||||
return handled.get()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@
|
|||
android:id="@+id/reply_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@{model.replyTo, default=`John Doe`}"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/color_main2_500"
|
||||
|
|
@ -135,7 +135,7 @@
|
|||
android:id="@+id/forward_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/message_forwarded_label"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/color_main2_500"
|
||||
|
|
@ -187,11 +187,10 @@
|
|||
android:onClick="@{() -> model.firstImageClicked()}"
|
||||
android:onLongClick="@{onLongClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="@dimen/chat_bubble_big_image_max_size"
|
||||
android:layout_height="@dimen/chat_bubble_big_image_max_size"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="@{model.filesList.size() == 1 && model.firstImagePath.length() >= 0 ? View.VISIBLE : View.GONE}"
|
||||
android:visibility="@{model.filesList.size() == 1 && model.firstImagePath.length() >= 0 ? View.VISIBLE : View.GONE, default=gone}"
|
||||
coilBubble="@{model.firstImagePath}"/>
|
||||
|
||||
<ViewStub
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
android:id="@+id/reply_icon"
|
||||
android:layout_width="@dimen/small_icon_size"
|
||||
android:layout_height="@dimen/small_icon_size"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@drawable/reply"
|
||||
app:layout_constraintEnd_toStartOf="@id/reply_name"
|
||||
app:layout_constraintTop_toTopOf="@id/reply_name"
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
android:id="@+id/forward_icon"
|
||||
android:layout_width="@dimen/small_icon_size"
|
||||
android:layout_height="@dimen/small_icon_size"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@drawable/forward"
|
||||
android:visibility="@{model.isForward ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintEnd_toStartOf="@id/forward_label"
|
||||
|
|
@ -150,11 +150,10 @@
|
|||
android:onClick="@{() -> model.firstImageClicked()}"
|
||||
android:onLongClick="@{onLongClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="@dimen/chat_bubble_big_image_max_size"
|
||||
android:layout_height="@dimen/chat_bubble_big_image_max_size"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="@{model.filesList.size() == 1 && model.firstImagePath.length() >= 0 ? View.VISIBLE : View.GONE}"
|
||||
android:visibility="@{model.filesList.size() == 1 && model.firstImagePath.length() >= 0 ? View.VISIBLE : View.GONE, default=gone}"
|
||||
coilBubble="@{model.firstImagePath}"/>
|
||||
|
||||
<ViewStub
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@
|
|||
<item quantity="one">%s file waiting to be shared</item>
|
||||
<item quantity="other">%s files waiting to be shared</item>
|
||||
</plurals>
|
||||
<string name="toast_text_waiting_to_be_shared">Text is waiting to be shared</string>
|
||||
<string name="toast_message_waiting_to_be_forwarded">A message is waiting to be forwarded</string>
|
||||
|
||||
<string name="assistant_account_login">Login</string>
|
||||
<string name="assistant_scan_qr_code">Scan QR code</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue