diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
index 42417be67..a9e9074e4 100644
--- a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
+++ b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
@@ -96,7 +96,7 @@ class ActiveCallFragment : GenericCallFragment() {
// Holds fragment in place while new contact fragment slides over it
return AnimationUtils.loadAnimation(activity, R.anim.hold)
}
- return AnimationUtils.loadAnimation(activity, R.anim.hold)
+ return super.onCreateAnimation(transit, enter, nextAnim)
}
override fun onCreateView(
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
new file mode 100644
index 000000000..8a5f6c9a8
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010-2023 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 .
+ */
+package org.linphone.ui.main.chat.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
+import org.linphone.core.tools.Log
+import org.linphone.databinding.ChatConversationFragmentBinding
+import org.linphone.ui.main.chat.viewmodel.ConversationViewModel
+import org.linphone.ui.main.fragment.GenericFragment
+import org.linphone.utils.Event
+
+class ConversationFragment : GenericFragment() {
+ companion object {
+ private const val TAG = "[Conversation Fragment]"
+ }
+
+ private lateinit var binding: ChatConversationFragmentBinding
+
+ private lateinit var viewModel: ConversationViewModel
+
+ private val args: ConversationFragmentArgs by navArgs()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = ChatConversationFragmentBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun goBack(): Boolean {
+ sharedViewModel.closeSlidingPaneEvent.value = Event(true)
+ // If not done, when going back to ConversationsFragment this fragment will be created again
+ return findNavController().popBackStack()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ postponeEnterTransition()
+
+ binding.lifecycleOwner = viewLifecycleOwner
+
+ viewModel = ViewModelProvider(this)[ConversationViewModel::class.java]
+ binding.viewModel = viewModel
+
+ val localSipUri = args.localSipUri
+ val remoteSipUri = args.remoteSipUri
+ Log.i(
+ "$TAG Looking up for conversation with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri]"
+ )
+ viewModel.findChatRoom(localSipUri, remoteSipUri)
+
+ binding.setBackClickListener {
+ goBack()
+ }
+
+ viewModel.chatRoomFoundEvent.observe(viewLifecycleOwner) {
+ it.consume { found ->
+ if (found) {
+ Log.i(
+ "$TAG Found matching chat room for local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri]"
+ )
+ startPostponedEnterTransition()
+ sharedViewModel.openSlidingPaneEvent.value = Event(true)
+ } else {
+ Log.e("$TAG Failed to find chat room, going back")
+ goBack()
+ }
+ }
+ }
+
+ sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { slideable ->
+ viewModel.showBackButton.value = slideable
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt
index 39076038f..adebf3294 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt
@@ -101,6 +101,21 @@ class ConversationsFragment : GenericFragment() {
}
}
+ sharedViewModel.showConversationEvent.observe(viewLifecycleOwner) {
+ it.consume { pair ->
+ val localSipUri = pair.first
+ val remoteSipUri = pair.second
+ Log.i(
+ "$TAG Navigating to conversation fragment with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri]"
+ )
+ val action = ConversationFragmentDirections.actionGlobalConversationFragment(
+ localSipUri,
+ remoteSipUri
+ )
+ binding.chatNavContainer.findNavController().navigate(action)
+ }
+ }
+
sharedViewModel.navigateToContactsEvent.observe(viewLifecycleOwner) {
it.consume {
if (findNavController().currentDestination?.id == R.id.conversationsFragment) {
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
index e71ce3bca..ce707c83d 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
@@ -65,10 +65,6 @@ class ConversationsListFragment : AbstractTopBarFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = listViewModel
- binding.setOnNewConversationClicked {
- // TODO: open start conversation fragment
- }
-
adapter = ConversationsListAdapter(viewLifecycleOwner)
binding.conversationsList.setHasFixedSize(true)
binding.conversationsList.adapter = adapter
@@ -113,7 +109,9 @@ class ConversationsListFragment : AbstractTopBarFragment() {
adapter.conversationClickedEvent.observe(viewLifecycleOwner) {
it.consume { model ->
Log.i("$TAG Show conversation with ID [${model.id}]")
- // TODO
+ sharedViewModel.showConversationEvent.value = Event(
+ Pair(model.localSipUri, model.remoteSipUri)
+ )
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt
index 6bf27cbd3..7d33c1743 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt
@@ -40,6 +40,10 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
val id = LinphoneUtils.getChatRoomId(chatRoom)
+ val localSipUri = chatRoom.localAddress.asStringUriOnly()
+
+ val remoteSipUri = chatRoom.peerAddress.asStringUriOnly()
+
val isGroup = !chatRoom.hasCapability(Capabilities.OneToOne.toInt())
val lastUpdateTime = MutableLiveData()
diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt
new file mode 100644
index 000000000..953539711
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010-2023 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 .
+ */
+package org.linphone.ui.main.chat.viewmodel
+
+import androidx.annotation.UiThread
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.core.ChatRoom
+import org.linphone.core.Factory
+import org.linphone.core.tools.Log
+import org.linphone.ui.main.contacts.model.ContactAvatarModel
+import org.linphone.utils.Event
+
+class ConversationViewModel @UiThread constructor() : ViewModel() {
+ companion object {
+ private const val TAG = "[Conversation ViewModel]"
+ }
+
+ val showBackButton = MutableLiveData()
+
+ val avatarModel = MutableLiveData()
+
+ val chatRoomFoundEvent = MutableLiveData>()
+
+ private lateinit var chatRoom: ChatRoom
+
+ @UiThread
+ fun findChatRoom(localSipUri: String, remoteSipUri: String) {
+ coreContext.postOnCoreThread { core ->
+ Log.i(
+ "$TAG Looking for chat room with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri]"
+ )
+
+ val localAddress = Factory.instance().createAddress(localSipUri)
+ val remoteAddress = Factory.instance().createAddress(remoteSipUri)
+ if (localAddress != null && remoteAddress != null) {
+ val found = core.searchChatRoom(
+ null,
+ localAddress,
+ remoteAddress,
+ arrayOfNulls(
+ 0
+ )
+ )
+ if (found != null) {
+ chatRoom = found
+ configureChatRoom()
+ chatRoomFoundEvent.postValue(Event(true))
+ } else {
+ Log.e("Failed to find chat room given local & remote addresses!")
+ chatRoomFoundEvent.postValue(Event(false))
+ }
+ } else {
+ Log.e("Failed to parse local or remote SIP URI as Address!")
+ chatRoomFoundEvent.postValue(Event(false))
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun configureChatRoom() {
+ val address = if (chatRoom.hasCapability(ChatRoom.Capabilities.Basic.toInt())) {
+ chatRoom.peerAddress
+ } else {
+ val firstParticipant = chatRoom.participants.firstOrNull()
+ firstParticipant?.address ?: chatRoom.peerAddress
+ }
+
+ val friend = coreContext.contactsManager.findContactByAddress(address)
+ if (friend != null) {
+ avatarModel.postValue(ContactAvatarModel(friend))
+ } else {
+ val fakeFriend = coreContext.core.createFriend()
+ fakeFriend.address = address
+ avatarModel.postValue(ContactAvatarModel(fakeFriend))
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt
index 3bcfb5340..c72e5b42b 100644
--- a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt
@@ -58,7 +58,7 @@ class HistoryFragment : GenericFragment() {
// Holds fragment in place while new contact fragment slides over it
return AnimationUtils.loadAnimation(activity, R.anim.hold)
}
- return AnimationUtils.loadAnimation(activity, R.anim.hold)
+ return super.onCreateAnimation(transit, enter, nextAnim)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
index 4eaccb346..f4165e544 100644
--- a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
@@ -100,4 +100,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
val showStartConversationEvent: MutableLiveData> by lazy {
MutableLiveData>()
}
+
+ val showConversationEvent: MutableLiveData>> by lazy {
+ MutableLiveData>>()
+ }
}
diff --git a/app/src/main/res/drawable/paperclip.xml b/app/src/main/res/drawable/paperclip.xml
new file mode 100644
index 000000000..8320d0255
--- /dev/null
+++ b/app/src/main/res/drawable/paperclip.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-land/chat_fragment.xml b/app/src/main/res/layout-land/chat_fragment.xml
index a9a66f7e9..35d35ed0d 100644
--- a/app/src/main/res/layout-land/chat_fragment.xml
+++ b/app/src/main/res/layout-land/chat_fragment.xml
@@ -6,37 +6,27 @@
-
-
+
+
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ app:defaultNavHost="false"
+ app:navGraph="@navigation/chat_nav_graph"/>
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/contacts_fragment.xml b/app/src/main/res/layout-land/contacts_fragment.xml
index 47a7e8bf3..82213983c 100644
--- a/app/src/main/res/layout-land/contacts_fragment.xml
+++ b/app/src/main/res/layout-land/contacts_fragment.xml
@@ -6,37 +6,27 @@
-
-
+
+
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ app:defaultNavHost="false"
+ app:navGraph="@navigation/contacts_nav_graph"/>
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/history_fragment.xml b/app/src/main/res/layout-land/history_fragment.xml
index 558de2058..39f7e1818 100644
--- a/app/src/main/res/layout-land/history_fragment.xml
+++ b/app/src/main/res/layout-land/history_fragment.xml
@@ -6,37 +6,27 @@
-
-
+
+
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ app:defaultNavHost="false"
+ app:navGraph="@navigation/history_nav_graph"/>
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/chat_conversation_fragment.xml b/app/src/main/res/layout/chat_conversation_fragment.xml
new file mode 100644
index 000000000..711a4ae43
--- /dev/null
+++ b/app/src/main/res/layout/chat_conversation_fragment.xml
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/chat_list_cell.xml b/app/src/main/res/layout/chat_list_cell.xml
index 971cd8a1a..827070291 100644
--- a/app/src/main/res/layout/chat_list_cell.xml
+++ b/app/src/main/res/layout/chat_list_cell.xml
@@ -118,7 +118,7 @@
android:id="@+id/notifications_count"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_marginEnd="32dp"
+ android:layout_marginEnd="16dp"
android:gravity="center"
android:background="@drawable/shape_red_round"
android:text="@{String.valueOf(model.unreadMessageCount), default=`1`}"
diff --git a/app/src/main/res/navigation/chat_nav_graph.xml b/app/src/main/res/navigation/chat_nav_graph.xml
index d0fda79b8..851aa32e4 100644
--- a/app/src/main/res/navigation/chat_nav_graph.xml
+++ b/app/src/main/res/navigation/chat_nav_graph.xml
@@ -12,8 +12,21 @@
tools:layout="@layout/empty_fragment"/>
+ android:id="@+id/conversationFragment"
+ android:name="org.linphone.ui.main.chat.fragment.ConversationFragment"
+ android:label="ConversationFragment"
+ tools:layout="@layout/chat_conversation_fragment">
+
+
+
+
+
+
\ 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 b1dfec863..0f89dcf53 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -329,6 +329,7 @@
Search contact
Create a group conversation
No contact for the moment…
+ Say something…
Operation in progress, please wait