Added meeting/group call conversation history in call log

This commit is contained in:
Sylvain Berfini 2024-11-14 10:09:24 +01:00
parent f74976f563
commit 4afb10d82a
13 changed files with 168 additions and 15 deletions

View file

@ -34,8 +34,8 @@ class ConversationFragment : ConversationFragment() {
super.onViewCreated(view, savedInstanceState)
Log.i("$TAG Creating an in-call ConversationFragment")
sendMessageViewModel.isInCallConversation.value = true
viewModel.isInCallConversation.value = true
sendMessageViewModel.isCallConversation.value = true
viewModel.isCallConversation.value = true
binding.setBackClickListener {
findNavController().popBackStack()

View file

@ -60,7 +60,7 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo
val showBackButton = MutableLiveData<Boolean>()
val isInCallConversation = MutableLiveData<Boolean>()
val isCallConversation = MutableLiveData<Boolean>()
val avatarModel = MutableLiveData<ContactAvatarModel>()

View file

@ -88,7 +88,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo
val isKeyboardOpen = MutableLiveData<Boolean>()
val isInCallConversation = MutableLiveData<Boolean>()
val isCallConversation = MutableLiveData<Boolean>()
val isVoiceRecording = MutableLiveData<Boolean>()
@ -152,7 +152,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo
isEmojiPickerOpen.value = false
isPlayingVoiceRecord.value = false
isInCallConversation.value = false
isCallConversation.value = false
maxNumberOfAttachmentsReached.value = false
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* 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 org.linphone.ui.main.history.fragment
import android.os.Bundle
import android.view.View
import androidx.navigation.fragment.findNavController
import org.linphone.core.tools.Log
import org.linphone.ui.main.chat.fragment.ConversationFragment
class ConferenceConversationFragment : ConversationFragment() {
companion object {
private const val TAG = "[Conference Conversation Fragment]"
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.i("$TAG Creating a conference history ConversationFragment")
sendMessageViewModel.isCallConversation.value = true
viewModel.isCallConversation.value = true
binding.setBackClickListener {
findNavController().popBackStack()
}
}
}

View file

@ -160,6 +160,22 @@ class HistoryFragment : SlidingPaneChildFragment() {
}
}
viewModel.goToMeetingConversationEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val localAddress = pair.first
val remoteAddress = pair.second
if (findNavController().currentDestination?.id == R.id.historyFragment) {
Log.i("$TAG Going to meeting conversation [$localAddress][$remoteAddress]")
val action =
HistoryFragmentDirections.actionHistoryFragmentToConferenceConversationFragment(
localAddress,
remoteAddress
)
findNavController().navigate(action)
}
}
}
viewModel.conferenceToJoinEvent.observe(viewLifecycleOwner) {
it.consume { conferenceUri ->
Log.i("$TAG Requesting to go to waiting room for conference URI [$conferenceUri]")

View file

@ -56,12 +56,18 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
val isConferenceCallLog = MutableLiveData<Boolean>()
val isChatRoomAvailable = MutableLiveData<Boolean>()
val callLogFoundEvent = MutableLiveData<Event<Boolean>>()
val chatRoomCreationErrorEvent: MutableLiveData<Event<Int>> by lazy {
MutableLiveData<Event<Int>>()
}
val goToMeetingConversationEvent: MutableLiveData<Event<Pair<String, String>>> by lazy {
MutableLiveData<Event<Pair<String, String>>>()
}
val goToConversationEvent: MutableLiveData<Event<Pair<String, String>>> by lazy {
MutableLiveData<Event<Pair<String, String>>>()
}
@ -76,6 +82,8 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
private lateinit var address: Address
private var meetingChatRoom: ChatRoom? = null
private val chatRoomListener = object : ChatRoomListenerStub() {
@WorkerThread
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State?) {
@ -124,7 +132,15 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
address = model.address
callLogModel.postValue(model)
isConferenceCallLog.postValue(callLog.wasConference())
val conference = callLog.wasConference()
isConferenceCallLog.postValue(conference)
meetingChatRoom = callLog.chatRoom
isChatRoomAvailable.postValue(meetingChatRoom != null)
if (conference) {
Log.i(
"$TAG Conference call log, chat room is ${ if (meetingChatRoom != null) "available" else "not available"}"
)
}
val peerAddress = callLog.remoteAddress
val history = arrayListOf<CallLogHistoryModel>()
@ -172,6 +188,25 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
}
}
@UiThread
fun goToMeetingConversation() {
coreContext.postOnCoreThread {
val chatRoom = meetingChatRoom
if (chatRoom != null) {
goToMeetingConversationEvent.postValue(
Event(
Pair(
chatRoom.localAddress.asStringUriOnly(),
chatRoom.peerAddress.asStringUriOnly()
)
)
)
} else {
Log.e("$TAG Failed to find chat room for current call log!")
}
}
}
@UiThread
fun goToConversation() {
coreContext.postOnCoreThread { core ->

View file

@ -89,7 +89,9 @@ class ScheduleMeetingFragment : GenericMainFragment() {
observeToastEvents(viewModel)
val subject = args.subject
viewModel.subject.value = subject
if (subject.isNotEmpty()) {
viewModel.subject.value = subject
}
val participants = args.participants
if (!participants.isNullOrEmpty()) {

View file

@ -73,7 +73,7 @@
android:padding="15dp"
android:adjustViewBounds="true"
android:onClick="@{backClickListener}"
android:visibility="@{viewModel.isInCallConversation || viewModel.showBackButton &amp;&amp; !viewModel.searchBarVisible ? View.VISIBLE : View.GONE}"
android:visibility="@{viewModel.isCallConversation || viewModel.showBackButton &amp;&amp; !viewModel.searchBarVisible ? View.VISIBLE : View.GONE}"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:tint="?attr/color_main1_500"
@ -87,7 +87,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="@{viewModel.isInCallConversation || viewModel.showBackButton ? @dimen/chat_conversation_header_avatar_start_margin_if_back_button : @dimen/chat_conversation_header_avatar_start_margin_if_no_back_button, default=@dimen/chat_conversation_header_avatar_start_margin_if_back_button}"
android:layout_marginStart="@{viewModel.isCallConversation || viewModel.showBackButton ? @dimen/chat_conversation_header_avatar_start_margin_if_back_button : @dimen/chat_conversation_header_avatar_start_margin_if_no_back_button, default=@dimen/chat_conversation_header_avatar_start_margin_if_back_button}"
layout="@layout/contact_avatar"
bind:model="@{viewModel.avatarModel}"
app:layout_constraintStart_toEndOf="@id/back"
@ -171,7 +171,7 @@
android:adjustViewBounds="true"
android:src="@drawable/dots_three_vertical"
android:contentDescription="@string/content_description_show_popup_menu"
android:visibility="@{viewModel.isInCallConversation ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.isCallConversation ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="?attr/color_main2_500"/>
@ -184,7 +184,7 @@
android:padding="15dp"
android:src="@drawable/phone"
android:contentDescription="@string/content_description_call_start"
android:visibility="@{viewModel.isInCallConversation || viewModel.isReadOnly || viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.isCallConversation || viewModel.isReadOnly || viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/show_menu"
app:tint="?attr/color_main2_500" />

View file

@ -33,7 +33,7 @@
android:id="@+id/extra_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isVoiceRecording ? View.INVISIBLE : (viewModel.isKeyboardOpen || viewModel.isInCallConversation || !viewModel.isFileTransferServerAvailable) ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.isVoiceRecording ? View.INVISIBLE : (viewModel.isKeyboardOpen || viewModel.isCallConversation || !viewModel.isFileTransferServerAvailable) ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="emoji_picker_toggle, capture_image" />
<include
@ -173,7 +173,7 @@
android:layout_height="0dp"
android:layout_marginEnd="4dp"
android:enabled="@{viewModel.textToSend.length() > 0 || viewModel.attachments.size() > 0}"
android:visibility="@{viewModel.isInCallConversation || viewModel.textToSend.length() > 0 || viewModel.attachments.size() > 0 ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{viewModel.isCallConversation || viewModel.textToSend.length() > 0 || viewModel.attachments.size() > 0 ? View.VISIBLE : View.GONE, default=gone}"
android:onClick="@{() -> viewModel.sendMessage()}"
android:padding="8dp"
android:src="@drawable/paper_plane_right"
@ -188,7 +188,7 @@
android:layout_width="40dp"
android:layout_height="0dp"
android:layout_marginEnd="4dp"
android:visibility="@{viewModel.isInCallConversation || viewModel.textToSend.length() > 0 || viewModel.attachments.size() > 0 || viewModel.isVoiceRecording || !viewModel.isFileTransferServerAvailable ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.isCallConversation || viewModel.textToSend.length() > 0 || viewModel.attachments.size() > 0 || viewModel.isVoiceRecording || !viewModel.isFileTransferServerAvailable ? View.GONE : View.VISIBLE}"
android:onClick="@{() -> viewModel.startVoiceMessageRecording()}"
android:padding="8dp"
android:src="@drawable/microphone"

View file

@ -249,8 +249,8 @@
android:contentDescription="@string/content_description_join_conference"
android:visibility="@{viewModel.isConferenceCallLog ? View.VISIBLE : View.GONE, default=gone}"
app:tint="?attr/color_main2_500"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/meeting_chat"
app:layout_constraintTop_toBottomOf="@id/status" />
<androidx.appcompat.widget.AppCompatTextView
@ -267,6 +267,36 @@
app:layout_constraintStart_toStartOf="@id/meeting"
app:layout_constraintEnd_toEndOf="@id/meeting"/>
<ImageView
android:id="@+id/meeting_chat"
android:onClick="@{() -> viewModel.goToMeetingConversation()}"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginTop="20dp"
android:background="@drawable/circle_light_blue_button_background"
android:padding="16dp"
android:src="@drawable/chat_teardrop_text"
android:contentDescription="@string/content_description_go_to_conversation"
android:visibility="@{viewModel.isConferenceCallLog &amp;&amp; viewModel.isChatRoomAvailable ? View.VISIBLE : View.GONE, default=gone}"
app:tint="?attr/color_main2_500"
app:layout_constraintStart_toEndOf="@id/meeting"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/status" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/meeting_chat_label"
android:onClick="@{() -> viewModel.goToMeetingConversation()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/history_group_call_go_to_conversation"
android:textSize="14sp"
android:visibility="@{viewModel.isConferenceCallLog &amp;&amp; viewModel.isChatRoomAvailable ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/meeting_chat"
app:layout_constraintStart_toStartOf="@id/meeting_chat"
app:layout_constraintEnd_toEndOf="@id/meeting_chat"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/call_history"
android:layout_width="0dp"

View file

@ -25,11 +25,34 @@
app:destination="@id/emptyFragment"
app:popUpTo="@id/historyFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_historyFragment_to_conferenceConversationFragment"
app:destination="@id/conferenceConversationFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:launchSingleTop="true" />
</fragment>
<action
android:id="@+id/action_global_historyFragment"
app:destination="@id/historyFragment"
app:exitAnim="@anim/slide_out_left"
app:popExitAnim="@anim/slide_out_right"
app:launchSingleTop="true" />
<fragment
android:id="@+id/conferenceConversationFragment"
android:name="org.linphone.ui.main.history.fragment.ConferenceConversationFragment"
android:label="ConferenceConversationFragment"
tools:layout="@layout/chat_conversation_fragment">
<argument
android:name="localSipUri"
app:argType="string" />
<argument
android:name="remoteSipUri"
app:argType="string" />
</fragment>
</navigation>

View file

@ -324,6 +324,7 @@
<string name="history_group_call_start_dialog_set_subject">Nommer l\'appel de groupe</string>
<string name="history_group_call_start_dialog_subject_hint">Nom de l\'appel de groupe</string>
<string name="history_list_empty_history">Aucun appel dans votre historique…</string>
<string name="history_group_call_go_to_conversation">Conversation</string>
<string name="history_dialog_delete_all_call_logs_title">Voulez-vous tout supprimer ?</string>
<string name="history_dialog_delete_all_call_logs_message">Tout votre historique d\'appels sera supprimé.</string>

View file

@ -362,6 +362,7 @@
<string name="history_group_call_start_dialog_set_subject">Set group call subject</string>
<string name="history_group_call_start_dialog_subject_hint">Group call subject</string>
<string name="history_list_empty_history">No call for the moment…</string>
<string name="history_group_call_go_to_conversation">Conversation</string>
<string name="history_dialog_delete_all_call_logs_title">Do you really want to delete all calls history?</string>
<string name="history_dialog_delete_all_call_logs_message">All calls will be removed from the history</string>
@ -871,6 +872,7 @@
<string name="content_description_ldap_delete">Delete this LDAP configuration</string>
<string name="content_description_ldap_save">Save LDAP configuration</string>
<string name="content_description_play_call_recording">Plays the call recording</string>
<string name="content_description_go_to_conversation">Go to conversation</string>
<!-- Copy of private hosts_allowlist_sample in androidx.car.app:app:1.7.0-beta01, as they recommend it -->
<string-array name="hosts_allowlist_sample_copy" translatable="false">