Started chat room layout

This commit is contained in:
Sylvain Berfini 2023-06-26 10:11:11 +02:00
parent 7b71f32d18
commit 33a33867b5
22 changed files with 527 additions and 23 deletions

View file

@ -22,6 +22,7 @@ package org.linphone.ui
import android.Manifest import android.Manifest
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
@ -105,4 +106,12 @@ class MainActivity : AppCompatActivity() {
private fun getNavBar(): NavigationBarView? { private fun getNavBar(): NavigationBarView? {
return binding.mainNavView ?: binding.mainNavRail return binding.mainNavView ?: binding.mainNavRail
} }
fun hideNavBar() {
getNavBar()?.visibility = View.GONE
}
fun showNavBar() {
getNavBar()?.visibility = View.VISIBLE
}
} }

View file

@ -32,6 +32,8 @@ import org.linphone.utils.TimestampUtils
class ChatRoomData(val chatRoom: ChatRoom) { class ChatRoomData(val chatRoom: ChatRoom) {
val id = LinphoneUtils.getChatRoomId(chatRoom) val id = LinphoneUtils.getChatRoomId(chatRoom)
val localSipUri = chatRoom.localAddress.asString()
val remoteSipUri = chatRoom.peerAddress.asString()
val contactName = MutableLiveData<String>() val contactName = MutableLiveData<String>()

View file

@ -24,10 +24,15 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.navGraphViewModels
import org.linphone.R
import org.linphone.databinding.ConversationFragmentBinding import org.linphone.databinding.ConversationFragmentBinding
class ConversationFragment : Fragment() { class ConversationFragment : Fragment() {
private lateinit var binding: ConversationFragmentBinding private lateinit var binding: ConversationFragmentBinding
private val viewModel: ConversationViewModel by navGraphViewModels(
R.id.conversationFragment
)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -42,5 +47,23 @@ class ConversationFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
val localSipUri = arguments?.getString("localSipUri")
?: savedInstanceState?.getString("localSipUri")
val remoteSipUri = arguments?.getString("remoteSipUri")
?: savedInstanceState?.getString("remoteSipUri")
if (localSipUri != null && remoteSipUri != null) {
viewModel.loadChatRoom(localSipUri, remoteSipUri)
} else {
// Chat room not found, going back
// TODO FIXME : show error
}
arguments?.clear()
binding.setBackClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
} }
} }

View file

@ -54,6 +54,7 @@ class ConversationMenuDialogFragment(
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
// TODO FIXME: use a viewmodel and use core thread
val view = ChatRoomMenuBinding.inflate(layoutInflater) val view = ChatRoomMenuBinding.inflate(layoutInflater)
val id = LinphoneUtils.getChatRoomId(chatRoom) val id = LinphoneUtils.getChatRoomId(chatRoom)

View file

@ -0,0 +1,106 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.conversations
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.contacts.ContactData
import org.linphone.contacts.ContactsListener
import org.linphone.core.ChatRoom
import org.linphone.core.Factory
import org.linphone.core.tools.Log
import org.linphone.utils.LinphoneUtils
class ConversationViewModel : ViewModel() {
private lateinit var chatRoom: ChatRoom
val contactName = MutableLiveData<String>()
val contactData = MutableLiveData<ContactData>()
val subject = MutableLiveData<String>()
val isOneToOne = MutableLiveData<Boolean>()
private val contactsListener = object : ContactsListener {
override fun onContactsLoaded() {
contactLookup()
}
}
init {
coreContext.contactsManager.addListener(contactsListener)
}
override fun onCleared() {
coreContext.contactsManager.removeListener(contactsListener)
}
fun loadChatRoom(localSipUri: String, remoteSipUri: String) {
coreContext.postOnCoreThread { core ->
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
val found = core.searchChatRoom(
null,
localAddress,
remoteSipAddress,
arrayOfNulls(
0
)
)
if (found != null) {
chatRoom = found
isOneToOne.postValue(chatRoom.hasCapability(ChatRoom.Capabilities.OneToOne.toInt()))
subject.postValue(chatRoom.subject)
contactLookup()
}
}
}
private fun contactLookup() {
if (chatRoom.hasCapability(ChatRoom.Capabilities.Basic.toInt())) {
val remoteAddress = chatRoom.peerAddress
val friend = chatRoom.core.findFriend(remoteAddress)
if (friend != null) {
contactData.postValue(ContactData(friend))
}
contactName.postValue(friend?.name ?: LinphoneUtils.getDisplayName(remoteAddress))
} else {
if (chatRoom.hasCapability(ChatRoom.Capabilities.OneToOne.toInt())) {
val first = chatRoom.participants.firstOrNull()
if (first != null) {
val remoteAddress = first.address
val friend = chatRoom.core.findFriend(remoteAddress)
if (friend != null) {
contactData.postValue(ContactData(friend))
}
contactName.postValue(
friend?.name ?: LinphoneUtils.getDisplayName(remoteAddress)
)
} else {
Log.e("[Conversation View Model] No participant in the chat room!")
}
}
}
}
}

View file

@ -25,6 +25,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import androidx.core.os.bundleOf
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -33,6 +34,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.R import org.linphone.R
import org.linphone.databinding.ConversationsFragmentBinding import org.linphone.databinding.ConversationsFragmentBinding
import org.linphone.ui.MainActivity
class ConversationsFragment : Fragment() { class ConversationsFragment : Fragment() {
private lateinit var binding: ConversationsFragmentBinding private lateinit var binding: ConversationsFragmentBinding
@ -58,6 +60,7 @@ class ConversationsFragment : Fragment() {
} }
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
// Holds fragment in place while new fragment slides over it
return AnimationUtils.loadAnimation(activity, R.anim.hold) return AnimationUtils.loadAnimation(activity, R.anim.hold)
} }
@ -91,9 +94,17 @@ class ConversationsFragment : Fragment() {
adapter.chatRoomClickedEvent.observe(viewLifecycleOwner) { adapter.chatRoomClickedEvent.observe(viewLifecycleOwner) {
it.consume { data -> it.consume { data ->
findNavController().navigate( val bundle = bundleOf()
R.id.action_conversationsFragment_to_conversationFragment bundle.putString("localSipUri", data.localSipUri)
) bundle.putString("remoteSipUri", data.remoteSipUri)
if (findNavController().currentDestination?.id == R.id.conversationsFragment) {
(requireActivity() as MainActivity).hideNavBar()
findNavController().navigate(
R.id.action_conversationsFragment_to_conversationFragment,
bundle
)
}
} }
} }
@ -126,12 +137,20 @@ class ConversationsFragment : Fragment() {
} }
binding.setOnNewConversationClicked { binding.setOnNewConversationClicked {
findNavController().navigate( if (findNavController().currentDestination?.id == R.id.conversationsFragment) {
R.id.action_conversationsFragment_to_newConversationFragment (requireActivity() as MainActivity).hideNavBar()
) findNavController().navigate(
R.id.action_conversationsFragment_to_newConversationFragment
)
}
} }
} }
override fun onResume() {
super.onResume()
(requireActivity() as MainActivity).showNavBar()
}
private fun scrollToTop() { private fun scrollToTop() {
binding.conversationsList.scrollToPosition(0) binding.conversationsList.scrollToPosition(0)
} }

View file

@ -23,8 +23,11 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.navGraphViewModels import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
@ -39,6 +42,14 @@ class NewConversationFragment : Fragment() {
R.id.conversationsFragment R.id.conversationsFragment
) )
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
if (findNavController().currentDestination?.id == R.id.conversationFragment) {
// Holds fragment in place while created conversation fragment slides over it
return AnimationUtils.loadAnimation(activity, R.anim.hold)
}
return super.onCreateAnimation(transit, enter, nextAnim)
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -85,5 +96,15 @@ class NewConversationFragment : Fragment() {
binding.setCancelClickListener { binding.setCancelClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed() requireActivity().onBackPressedDispatcher.onBackPressed()
} }
viewModel.goToChatRoom.observe(viewLifecycleOwner) {
it.consume {
if (findNavController().currentDestination?.id == R.id.newConversationFragment) {
findNavController().navigate(
R.id.action_newConversationFragment_to_conversationFragment
)
}
}
}
} }
} }

View file

@ -28,10 +28,17 @@ import org.linphone.core.MagicSearch
import org.linphone.core.MagicSearchListenerStub import org.linphone.core.MagicSearchListenerStub
import org.linphone.core.SearchResult import org.linphone.core.SearchResult
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.Event
class NewConversationViewModel : ViewModel() { class NewConversationViewModel : ViewModel() {
val contactsList = MutableLiveData<ArrayList<ContactData>>() val contactsList = MutableLiveData<ArrayList<ContactData>>()
val groupEnabled = MutableLiveData<Boolean>()
val goToChatRoom: MutableLiveData<Event<Pair<String, String>>> by lazy {
MutableLiveData<Event<Pair<String, String>>>()
}
val filter = MutableLiveData<String>() val filter = MutableLiveData<String>()
private var previousFilter = "NotSet" private var previousFilter = "NotSet"
@ -56,16 +63,16 @@ class NewConversationViewModel : ViewModel() {
init { init {
coreContext.postOnCoreThread { coreContext.postOnCoreThread {
magicSearch.addListener(magicSearchListener) magicSearch.addListener(magicSearchListener)
coreContext.contactsManager.addListener(contactsListener)
applyFilter("") applyFilter("")
} }
coreContext.contactsManager.addListener(contactsListener)
} }
override fun onCleared() { override fun onCleared() {
coreContext.postOnCoreThread { coreContext.postOnCoreThread {
coreContext.contactsManager.removeListener(contactsListener)
magicSearch.removeListener(magicSearchListener) magicSearch.removeListener(magicSearchListener)
} }
coreContext.contactsManager.removeListener(contactsListener)
super.onCleared() super.onCleared()
} }
@ -88,6 +95,14 @@ class NewConversationViewModel : ViewModel() {
) )
} }
fun createGroup() {
goToChatRoom.value = Event(Pair("", ""))
}
fun enableGroupSelection() {
groupEnabled.value = true
}
private fun processMagicSearchResults(results: Array<SearchResult>) { private fun processMagicSearchResults(results: Array<SearchResult>) {
Log.i("[New Conversation ViewModel] [${results.size}] matching results") Log.i("[New Conversation ViewModel] [${results.size}] matching results")
contactsList.value.orEmpty().forEach(ContactData::onDestroy) contactsList.value.orEmpty().forEach(ContactData::onDestroy)

View file

@ -0,0 +1,26 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:name="group">
<path
android:name="path"
android:pathData="M 12 8.327 L 12 15.654 M 15.667 11.991 L 8.333 11.991"
android:fillColor="#6c7a87"
android:fillAlpha="0"
android:strokeColor="#6c7a87"
android:strokeWidth="1"
android:fillType="evenOdd"/>
<path
android:name="path_1"
android:pathData="M 16.686 2 L 7.314 2 C 4.048 2 2 4.312 2 7.585 L 2 16.415 C 2 19.688 4.038 22 7.314 22 L 16.686 22 C 19.962 22 22 19.688 22 16.415 L 22 7.585 C 22 4.312 19.962 2 16.686 2 Z"
android:fillColor="#6c7a87"
android:fillAlpha="0"
android:strokeColor="#6c7a87"
android:strokeWidth="1"
android:fillType="evenOdd"/>
</group>
</vector>

View file

@ -0,0 +1,15 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="21dp"
android:height="20dp"
android:viewportWidth="21"
android:viewportHeight="20">
<path
android:name="path"
android:pathData="M 13.912 15.833 L 8.079 10 L 13.912 4.167"
android:strokeColor="#fe5e00"
android:strokeWidth="1.5"
android:strokeLineCap="round"
android:strokeLineJoin="round"/>
</vector>

View file

@ -7,7 +7,7 @@
android:viewportHeight="12"> android:viewportHeight="12">
<path <path
android:name="path" android:name="path"
android:pathData="M 5.801 6.88 L 6.507 7.586 L 10.74 3.353 L 11.447 4.06 L 6.507 9 L 3.325 5.818 L 4.032 5.111 L 5.095 6.173 L 5.801 6.879 L 5.801 6.88 Z M 5.802 5.466 L 8.278 2.989 L 8.983 3.694 L 6.507 6.171 L 5.802 5.466 Z M 4.388 8.293 L 3.682 9 L 0.5 5.818 L 1.207 5.111 L 1.913 5.817 L 1.913 5.818 L 4.388 8.293 Z" android:pathData="M 5 7.586 L 9.596 2.99 L 10.303 3.696 L 5 9 L 1.818 5.818 L 2.525 5.111 L 5 7.586 Z"
android:fillColor="#6c7a87" android:fillColor="#fe5e00"
android:strokeWidth="1"/> android:strokeWidth="1"/>
</vector> </vector>

View file

@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="19dp"
android:height="18dp"
android:viewportWidth="19"
android:viewportHeight="18">
<path
android:name="path"
android:pathData="M 9.549 18 C 4.578 18 0.549 13.971 0.549 9 C 0.549 4.029 4.578 0 9.549 0 C 14.52 0 18.549 4.029 18.549 9 C 18.549 13.971 14.52 18 9.549 18 Z M 9.549 16.2 C 11.459 16.2 13.29 15.441 14.64 14.091 C 15.991 12.741 16.749 10.91 16.749 9 C 16.749 7.09 15.991 5.259 14.64 3.909 C 13.29 2.559 11.459 1.8 9.549 1.8 C 7.64 1.8 5.808 2.559 4.458 3.909 C 3.108 5.259 2.349 7.09 2.349 9 C 2.349 10.91 3.108 12.741 4.458 14.091 C 5.808 15.441 7.64 16.2 9.549 16.2 Z M 8.649 4.5 L 10.449 4.5 L 10.449 6.3 L 8.649 6.3 L 8.649 4.5 Z M 8.649 8.1 L 10.449 8.1 L 10.449 13.5 L 8.649 13.5 L 8.649 8.1 Z"
android:fillColor="#6c7a87"
android:strokeWidth="1"/>
</vector>

View file

@ -0,0 +1,20 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="19dp"
android:height="18dp"
android:viewportWidth="19"
android:viewportHeight="18">
<group android:name="wrapper">
<clip-path
android:name="clip0_96_25861"
android:pathData="M 0.549 0 L 18.549 0 L 18.549 18 L 0.549 18 Z"/>
<group android:name="group">
<path
android:name="path"
android:pathData="M 5.454 3.75 C 5.499 4.418 5.612 5.07 5.792 5.692 L 4.892 6.593 C 4.584 5.692 4.389 4.74 4.322 3.75 L 5.454 3.75 Z M 12.849 12.765 C 13.487 12.945 14.139 13.057 14.799 13.102 L 14.799 14.22 C 13.809 14.152 12.857 13.957 11.949 13.658 L 12.849 12.765 Z M 6.174 2.25 L 3.549 2.25 C 3.137 2.25 2.799 2.587 2.799 3 C 2.799 10.043 8.507 15.75 15.549 15.75 C 15.962 15.75 16.299 15.412 16.299 15 L 16.299 12.383 C 16.299 11.97 15.962 11.633 15.549 11.633 C 14.619 11.633 13.712 11.482 12.872 11.205 C 12.797 11.175 12.714 11.168 12.639 11.168 C 12.444 11.168 12.257 11.242 12.107 11.385 L 10.457 13.035 C 8.334 11.947 6.594 10.215 5.514 8.092 L 7.164 6.442 C 7.374 6.232 7.434 5.94 7.352 5.678 C 7.074 4.838 6.924 3.938 6.924 3 C 6.924 2.587 6.587 2.25 6.174 2.25 Z"
android:fillColor="#fe5e00"
android:strokeWidth="1"/>
</group>
</group>
</vector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="24dp" />
<solid android:color="@color/white"/>
<size android:height="48dp" android:width="234dp" />
</shape>

View file

@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="19dp"
android:height="14dp"
android:viewportWidth="19"
android:viewportHeight="14">
<path
android:name="path"
android:pathData="M 3.089 13.23 C 2.416 13.23 1.77 12.962 1.294 12.487 C 0.818 12.011 0.55 11.366 0.55 10.693 L 0.55 3.308 C 0.55 2.635 0.818 1.99 1.294 1.514 C 1.77 1.038 2.415 0.77 3.089 0.77 L 11.396 0.77 C 12.052 0.77 12.682 1.025 13.155 1.479 C 13.627 1.934 13.906 2.554 13.933 3.209 L 16.333 2.248 C 16.73 2.09 17.175 2.095 17.569 2.264 C 17.963 2.433 18.273 2.751 18.433 3.148 C 18.509 3.339 18.549 3.543 18.549 3.748 L 18.549 10.248 C 18.549 10.513 18.484 10.773 18.36 11.006 C 18.237 11.239 18.058 11.439 17.839 11.587 C 17.62 11.735 17.368 11.827 17.106 11.855 C 16.843 11.883 16.578 11.847 16.333 11.748 L 13.933 10.789 C 13.906 11.444 13.627 12.064 13.155 12.519 C 12.682 12.973 12.052 13.228 11.396 13.229 L 3.089 13.23 Z M 1.934 3.308 L 1.934 10.693 C 1.934 10.999 2.056 11.292 2.272 11.508 C 2.489 11.725 2.782 11.846 3.089 11.847 L 11.396 11.847 C 11.701 11.846 11.995 11.725 12.211 11.508 C 12.428 11.292 12.549 10.998 12.55 10.693 L 12.55 3.308 C 12.549 3.002 12.428 2.709 12.211 2.492 C 11.995 2.276 11.702 2.154 11.396 2.154 L 3.089 2.154 C 2.782 2.154 2.489 2.276 2.272 2.492 C 2.056 2.709 1.934 3.002 1.934 3.308 Z M 16.85 10.47 C 16.884 10.483 16.922 10.488 16.96 10.484 C 16.997 10.48 17.033 10.467 17.064 10.446 C 17.095 10.425 17.121 10.396 17.138 10.363 C 17.156 10.33 17.165 10.293 17.166 10.255 L 17.166 3.756 C 17.165 3.718 17.156 3.681 17.138 3.648 C 17.121 3.614 17.095 3.586 17.064 3.565 C 17.033 3.544 16.997 3.531 16.96 3.527 C 16.922 3.523 16.884 3.528 16.85 3.542 L 13.935 4.699 L 13.935 9.3 L 16.85 10.47 Z"
android:fillColor="#fe5e00"
android:strokeWidth="1"/>
</vector>

View file

@ -0,0 +1,31 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:name="group_1">
<path
android:name="path_2"
android:pathData="M 12 22 L 12 18.839"
android:fillColor="#000"
android:fillAlpha="0"
android:strokeColor="#6c7a87"
android:strokeWidth="1"/>
<path
android:name="path_3"
android:pathData="M 12 14.848 C 9.757 14.848 7.938 13.022 7.938 10.768 L 7.938 6.081 C 7.938 3.827 9.757 2 12 2 C 14.244 2 16.062 3.827 16.062 6.081 L 16.062 10.768 C 16.062 13.022 14.244 14.848 12 14.848 Z"
android:fillColor="#000"
android:fillAlpha="0"
android:strokeColor="#6c7a87"
android:strokeWidth="1"/>
<path
android:name="path_4"
android:pathData="M 20 10.801 C 20 15.239 16.419 18.838 11.999 18.838 C 7.581 18.838 4 15.239 4 10.801"
android:fillColor="#000"
android:fillAlpha="0"
android:strokeColor="#6c7a87"
android:strokeWidth="1"/>
</group>
</vector>

View file

@ -56,7 +56,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:text="@{data.isComposing ? `... est en train d'écrire` : data.lastMessage, default=`Lorem Ipsum`}" android:text="@{data.isComposing ? `... est en train d'écrire` : data.lastMessage, default=`Lorem Ipsum`}"
android:textColor="@{data.unreadChatCount > 0 ? @color/black : @color/gray_4, default=@color/gray_4}" android:textColor="@{data.isComposing ? @color/primary_color : data.unreadChatCount > 0 ? @color/black : @color/gray_4, default=@color/gray_4}"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="@{data.unreadChatCount > 0 ? Typeface.BOLD : Typeface.NORMAL, default=normal}" android:textStyle="@{data.unreadChatCount > 0 ? Typeface.BOLD : Typeface.NORMAL, default=normal}"
android:maxLines="1" android:maxLines="1"

View file

@ -8,23 +8,176 @@
<variable <variable
name="backClickListener" name="backClickListener"
type="View.OnClickListener" /> type="View.OnClickListener" />
<variable
name="viewModel"
type="org.linphone.ui.conversations.ConversationViewModel" />
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/transparent"> android:background="@color/white">
<ImageView <ImageView
android:id="@+id/back"
android:onClick="@{backClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/back"
android:padding="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:adjustViewBounds="true"
android:src="@{viewModel.isOneToOne ? @drawable/contact_avatar : @drawable/group_avatar, default=@drawable/contact_avatar}"
coilContact="@{viewModel.contactData}"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintBottom_toBottomOf="@id/back"/>
<TextView
android:id="@+id/title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@{viewModel.isOneToOne ? viewModel.contactName : viewModel.subject, default=`John Doe`}"
android:textColor="@color/black"
android:textSize="14sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintEnd_toStartOf="@id/phone_call"
app:layout_constraintTop_toTopOf="@id/avatar" />
<TextView
android:id="@+id/presence"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="Online"
android:textColor="@color/green_online"
android:textSize="14sp"
android:textStyle=""
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintEnd_toStartOf="@id/phone_call"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/avatar"/>
<ImageView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="10dp" android:src="@drawable/info"
android:src="@drawable/shape_white_background" android:padding="10dp"
android:layout_marginEnd="5dp"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/video_call"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:src="@drawable/video_call"
android:padding="10dp"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintEnd_toStartOf="@id/info" />
<ImageView
android:id="@+id/phone_call"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:src="@drawable/phone_call"
android:padding="10dp"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintEnd_toStartOf="@id/video_call" />
<TextView
android:id="@+id/composing_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="... est en train décrire..."
android:textSize="12sp"
android:textColor="#73000000"
android:layout_marginStart="10dp"
android:layout_marginBottom="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_background" />
<View
android:id="@+id/bottom_background"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/gray_7"
android:layout_marginTop="-15dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="@id/message"
app:layout_constraintBottom_toBottomOf="parent" /> app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/emoji_picker"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="🙂"
android:textSize="18sp"
android:padding="5dp"
android:gravity="center"
android:layout_marginStart="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/message"
app:layout_constraintBottom_toBottomOf="@id/message" />
<ImageView
android:id="@+id/attach_file"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:src="@drawable/add_file"
android:padding="5dp"
android:layout_marginStart="10dp"
app:layout_constraintStart_toEndOf="@id/emoji_picker"
app:layout_constraintTop_toTopOf="@id/message"
app:layout_constraintBottom_toBottomOf="@id/message" />
<EditText
android:id="@+id/message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Dites quelque chose..."
android:layout_marginBottom="15dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:paddingStart="24dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:maxLines="5"
android:textSize="14sp"
android:background="@drawable/shape_edittext_white_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/attach_file"
app:layout_constraintEnd_toStartOf="@id/voice_message"/>
<ImageView
android:id="@+id/voice_message"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:src="@drawable/voice_message"
android:padding="5dp"
android:layout_marginEnd="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/message"
app:layout_constraintBottom_toBottomOf="@id/message" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -91,7 +91,7 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:text="Récents" android:text="Récents"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/gray_1" android:textColor="@color/blue_filter"
android:drawablePadding="5dp" android:drawablePadding="5dp"
app:layout_constraintStart_toEndOf="@id/sort_by_label" app:layout_constraintStart_toEndOf="@id/sort_by_label"
app:layout_constraintTop_toBottomOf="@id/search_bar" app:layout_constraintTop_toBottomOf="@id/search_bar"

View file

@ -42,6 +42,21 @@
app:layout_constraintTop_toTopOf="@id/title" app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/subtitle"/> app:layout_constraintBottom_toBottomOf="@id/subtitle"/>
<TextView
android:id="@+id/create_group"
android:onClick="@{() -> viewModel.createGroup()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Créer"
android:textSize="15sp"
android:padding="10dp"
android:textColor="@color/primary_color"
android:layout_marginEnd="15dp"
android:visibility="@{viewModel.groupEnabled ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/subtitle"/>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -86,6 +101,7 @@
app:layout_constraintTop_toBottomOf="@id/subtitle" /> app:layout_constraintTop_toBottomOf="@id/subtitle" />
<ImageView <ImageView
android:onClick="@{() -> viewModel.enableGroupSelection()}"
android:id="@+id/new_group_avatar" android:id="@+id/new_group_avatar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -94,21 +110,23 @@
android:background="@drawable/shape_orange_round" android:background="@drawable/shape_orange_round"
android:layout_marginTop="30dp" android:layout_marginTop="30dp"
android:layout_marginStart="20dp" android:layout_marginStart="20dp"
android:visibility="@{viewModel.filter.length() > 0 ? View.GONE : View.VISIBLE}" android:visibility="@{viewModel.filter.length() > 0 || viewModel.groupEnabled ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toBottomOf="@id/search_bar" app:layout_constraintTop_toBottomOf="@id/search_bar"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"/>
<TextView <TextView
android:onClick="@{() -> viewModel.enableGroupSelection()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Créer un nouveau groupe" android:text="Créer un nouveau groupe"
android:textStyle="bold" android:textStyle="bold"
android:textSize="16sp" android:textSize="16sp"
android:layout_marginStart="12dp" android:padding="5dp"
android:layout_marginEnd="25dp" android:layout_marginStart="7dp"
android:layout_marginEnd="20dp"
android:drawableEnd="@drawable/next" android:drawableEnd="@drawable/next"
android:drawablePadding="5dp" android:drawablePadding="5dp"
android:visibility="@{viewModel.filter.length() > 0 ? View.GONE : View.VISIBLE}" android:visibility="@{viewModel.filter.length() > 0 || viewModel.groupEnabled ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toEndOf="@id/new_group_avatar" app:layout_constraintStart_toEndOf="@id/new_group_avatar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/new_group_avatar" app:layout_constraintTop_toTopOf="@id/new_group_avatar"

View file

@ -14,7 +14,7 @@
android:id="@+id/action_conversationsFragment_to_newConversationFragment" android:id="@+id/action_conversationsFragment_to_newConversationFragment"
app:destination="@id/newConversationFragment" app:destination="@id/newConversationFragment"
app:enterAnim="@anim/slide_in" app:enterAnim="@anim/slide_in"
app:popExitAnim="@anim/slide_out" /> app:popExitAnim="@anim/slide_out"/>
<action <action
android:id="@+id/action_conversationsFragment_to_conversationFragment" android:id="@+id/action_conversationsFragment_to_conversationFragment"
app:destination="@id/conversationFragment" app:destination="@id/conversationFragment"
@ -29,13 +29,23 @@
tools:layout="@layout/new_conversation_fragment" > tools:layout="@layout/new_conversation_fragment" >
<action <action
android:id="@+id/action_newConversationFragment_to_conversationFragment" android:id="@+id/action_newConversationFragment_to_conversationFragment"
app:destination="@id/conversationFragment" /> app:destination="@id/conversationFragment"
app:enterAnim="@anim/slide_in_right"
app:popExitAnim="@anim/slide_out_right"
app:popUpTo="@id/conversationsFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/conversationFragment" android:id="@+id/conversationFragment"
android:name="org.linphone.ui.conversations.ConversationFragment" android:name="org.linphone.ui.conversations.ConversationFragment"
android:label="ConversationFragment" android:label="ConversationFragment"
tools:layout="@layout/conversation_fragment" /> tools:layout="@layout/conversation_fragment" >
<argument
android:name="localSipUri"
app:argType="string" />
<argument
android:name="remoteSipUri"
app:argType="string" />
</fragment>
</navigation> </navigation>

View file

@ -5,6 +5,8 @@
<color name="black">#000000</color> <color name="black">#000000</color>
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="red_danger">#DD5F5F</color> <color name="red_danger">#DD5F5F</color>
<color name="green_online">#4FAE80</color>
<color name="blue_filter">#09C5F4</color>
<color name="gray_1">#6C7A87</color> <color name="gray_1">#6C7A87</color>
<color name="gray_2">#F9F9F9</color> <color name="gray_2">#F9F9F9</color>
@ -12,5 +14,6 @@
<color name="gray_4">#949494</color> <color name="gray_4">#949494</color>
<color name="gray_5">#4E4E4E</color> <color name="gray_5">#4E4E4E</color>
<color name="gray_6">#EDEDED</color> <color name="gray_6">#EDEDED</color>
<color name="gray_7">#F9F9F9</color>
<color name="separator">#E5E5EA</color> <color name="separator">#E5E5EA</color>
</resources> </resources>