Started new conversation fragment

This commit is contained in:
Sylvain Berfini 2023-06-23 11:52:33 +02:00
parent a2d038eb46
commit d5c78f58c0
17 changed files with 567 additions and 24 deletions

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2010-2020 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.contacts
import androidx.lifecycle.MutableLiveData
import org.linphone.core.*
class ContactSelectionData(searchResult: SearchResult) {
val name = MutableLiveData<String>()
init {
name.value = searchResult.friend?.name ?: searchResult.toString()
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2010-2020 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.contacts
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.core.SearchResult
import org.linphone.databinding.ContactSelectionCellBinding
class ContactsSelectionAdapter(
private val viewLifecycleOwner: LifecycleOwner
) : ListAdapter<SearchResult, RecyclerView.ViewHolder>(SearchResultDiffCallback()) {
init {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding: ContactSelectionCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.contact_selection_cell,
parent,
false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(getItem(position))
}
inner class ViewHolder(
private val binding: ContactSelectionCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(searchResult: SearchResult) {
with(binding) {
val searchResultViewModel = ContactSelectionData(searchResult)
data = searchResultViewModel
lifecycleOwner = viewLifecycleOwner
executePendingBindings()
}
}
}
}
private class SearchResultDiffCallback : DiffUtil.ItemCallback<SearchResult>() {
override fun areItemsTheSame(
oldItem: SearchResult,
newItem: SearchResult
): Boolean {
val oldAddress = oldItem.address
val newAddress = newItem.address
return if (oldAddress != null && newAddress != null) oldAddress.weakEqual(newAddress) else false
}
override fun areContentsTheSame(
oldItem: SearchResult,
newItem: SearchResult
): Boolean {
return newItem.friend != null
}
}

View file

@ -24,11 +24,11 @@ import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.loader.app.LoaderManager
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationBarView
@ -45,15 +45,15 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainViewModel
private val onNavDestinationChangedListener =
NavController.OnDestinationChangedListener { _, destination, _ ->
binding.mainNavView?.visibility = View.VISIBLE
}
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, true)
super.onCreate(savedInstanceState)
window.statusBarColor = ContextCompat.getColor(
this,
R.color.primary_color
)
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
val manager = LoaderManager.getInstance(this)
manager.restartLoader(0, null, ContactLoader())
@ -84,9 +84,6 @@ class MainActivity : AppCompatActivity() {
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
binding.mainNavHostFragment.findNavController()
.addOnDestinationChangedListener(onNavDestinationChangedListener)
getNavBar()?.setupWithNavController(binding.mainNavHostFragment.findNavController())
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
@ -113,4 +110,12 @@ class MainActivity : AppCompatActivity() {
private fun getNavBar(): NavigationBarView? {
return binding.mainNavView ?: binding.mainNavRail
}
fun hideNavBar() {
binding.mainNavView?.visibility = View.GONE
}
fun showNavBar() {
binding.mainNavView?.visibility = View.VISIBLE
}
}

View file

@ -23,13 +23,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.databinding.ConversationsFragmentBinding
import org.linphone.ui.MainActivity
class ConversationsFragment : Fragment() {
private lateinit var binding: ConversationsFragmentBinding
@ -42,11 +44,13 @@ class ConversationsFragment : Fragment() {
override fun onChanged() {
scrollToTop()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0 && itemCount == 1) {
scrollToTop()
}
}
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
scrollToTop()
}
@ -64,13 +68,6 @@ class ConversationsFragment : Fragment() {
savedInstanceState: Bundle?
): View {
binding = ConversationsFragmentBinding.inflate(layoutInflater)
val window = requireActivity().window
window.statusBarColor = ContextCompat.getColor(
requireContext(),
R.color.gray_1
)
return binding.root
}
@ -78,6 +75,9 @@ class ConversationsFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = listViewModel
postponeEnterTransition()
adapter = ConversationsListAdapter(viewLifecycleOwner)
adapter.registerAdapterDataObserver(observer)
@ -104,6 +104,11 @@ class ConversationsFragment : Fragment() {
viewLifecycleOwner
) {
adapter.submitList(it)
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
(requireActivity() as MainActivity).showNavBar()
}
}
listViewModel.notifyItemChangedEvent.observe(viewLifecycleOwner) {
@ -111,6 +116,16 @@ class ConversationsFragment : Fragment() {
adapter.notifyItemChanged(index)
}
}
binding.setOnNewConversationClicked {
goToNewConversation()
}
}
private fun goToNewConversation() {
findNavController().navigate(
R.id.action_conversationsFragment_to_newConversationFragment
)
}
private fun scrollToTop() {

View file

@ -0,0 +1,87 @@
/*
* 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 android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment
import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.R
import org.linphone.contacts.ContactsSelectionAdapter
import org.linphone.databinding.NewConversationFragmentBinding
import org.linphone.ui.MainActivity
class NewConversationFragment : Fragment() {
private lateinit var binding: NewConversationFragmentBinding
private lateinit var adapter: ContactsSelectionAdapter
private val viewModel: NewConversationViewModel by navGraphViewModels(
R.id.conversationsFragment
)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = NewConversationFragmentBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
postponeEnterTransition()
adapter = ContactsSelectionAdapter(viewLifecycleOwner)
binding.contactsList.adapter = adapter
binding.contactsList.setHasFixedSize(true)
val layoutManager = LinearLayoutManager(requireContext())
binding.contactsList.layoutManager = layoutManager
viewModel.contactsList.observe(
viewLifecycleOwner
) {
adapter.submitList(it)
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
(requireActivity() as MainActivity).hideNavBar()
}
}
viewModel.filter.observe(
viewLifecycleOwner
) {
viewModel.applyFilter(it.orEmpty().trim())
}
binding.setCancelClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}

View file

@ -0,0 +1,87 @@
/*
* 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.core.MagicSearch
import org.linphone.core.MagicSearchListenerStub
import org.linphone.core.SearchResult
import org.linphone.core.tools.Log
class NewConversationViewModel : ViewModel() {
val contactsList = MutableLiveData<ArrayList<SearchResult>>()
val filter = MutableLiveData<String>()
private var previousFilter = "NotSet"
private val magicSearch: MagicSearch by lazy {
val magicSearch = coreContext.core.createMagicSearch()
magicSearch.limitedSearch = false
magicSearch
}
private val magicSearchListener = object : MagicSearchListenerStub() {
override fun onSearchResultsReceived(magicSearch: MagicSearch) {
processMagicSearchResults(magicSearch.lastSearch)
}
}
init {
magicSearch.addListener(magicSearchListener)
applyFilter("")
}
override fun onCleared() {
magicSearch.removeListener(magicSearchListener)
super.onCleared()
}
fun applyFilter(filterValue: String) {
Log.i("[New Conversation ViewModel] Filtering contacts using [$filterValue]")
if (previousFilter.isNotEmpty() && (
previousFilter.length > filterValue.length ||
(previousFilter.length == filterValue.length && previousFilter != filterValue)
)
) {
coreContext.postOnCoreThread { core ->
magicSearch.resetSearchCache()
}
}
previousFilter = filterValue
coreContext.postOnCoreThread { core ->
magicSearch.getContactsListAsync(
filterValue,
"",
MagicSearch.Source.Friends.toInt(),
MagicSearch.Aggregation.Friend
)
}
}
private fun processMagicSearchResults(results: Array<SearchResult>) {
Log.i("[New Conversation ViewModel] [${results.size}] matching results")
val list = arrayListOf<SearchResult>()
list.addAll(results)
contactsList.postValue(list)
}
}

View file

@ -0,0 +1,20 @@
<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="wrapper">
<clip-path
android:name="clip0_116_4583"
android:pathData="M 0 0 L 24 0 L 24 24 L 0 24 Z"/>
<group android:name="group">
<path
android:name="path"
android:pathData="M 12 12.75 C 13.63 12.75 15.07 13.14 16.24 13.65 C 17.32 14.13 18 15.21 18 16.38 L 18 18 L 6 18 L 6 16.39 C 6 15.21 6.68 14.13 7.76 13.66 C 8.93 13.14 10.37 12.75 12 12.75 Z M 4 13 C 5.1 13 6 12.1 6 11 C 6 9.9 5.1 9 4 9 C 2.9 9 2 9.9 2 11 C 2 12.1 2.9 13 4 13 Z M 5.13 14.1 C 4.76 14.04 4.39 14 4 14 C 3.01 14 2.07 14.21 1.22 14.58 C 0.48 14.9 0 15.62 0 16.43 L 0 18 L 4.5 18 L 4.5 16.39 C 4.5 15.56 4.73 14.78 5.13 14.1 Z M 20 13 C 21.1 13 22 12.1 22 11 C 22 9.9 21.1 9 20 9 C 18.9 9 18 9.9 18 11 C 18 12.1 18.9 13 20 13 Z M 24 16.43 C 24 15.62 23.52 14.9 22.78 14.58 C 21.93 14.21 20.99 14 20 14 C 19.61 14 19.24 14.04 18.87 14.1 C 19.27 14.78 19.5 15.56 19.5 16.39 L 19.5 18 L 24 18 L 24 16.43 Z M 12 6 C 13.66 6 15 7.34 15 9 C 15 10.66 13.66 12 12 12 C 10.34 12 9 10.66 9 9 C 9 7.34 10.34 6 12 6 Z"
android:fillColor="#ffffff"
android:strokeWidth="1"/>
</group>
</group>
</vector>

View file

@ -0,0 +1,16 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:name="path"
android:pathData="M 5.667 3.333 L 10.333 8 L 5.667 12.666"
android:fillColor="#000"
android:strokeColor="#6c7a87"
android:strokeWidth="1.5"
android:strokeLineCap="round"
android:strokeLineJoin="round"/>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topLeftRadius="10dp" android:topRightRadius="10dp" />
<solid android:color="@color/gray_1"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<size android:width="44dp" android:height="44dp" />
<solid android:color="@color/primary_color"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="@color/gray_6"/>
</shape>

View file

@ -14,6 +14,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary_color"
tools:context=".ui.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View"/>
<import type="org.linphone.core.ConsolidatedPresence"/>
<variable
name="data"
type="org.linphone.contacts.ContactSelectionData" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/contact_avatar"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@{data.name, default=`John Doe`}"
android:textColor="@color/black"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@id/avatar"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -9,10 +9,7 @@
name="viewModel"
type="org.linphone.ui.conversations.ConversationsListViewModel" />
<variable
name="backClickListener"
type="View.OnClickListener" />
<variable
name="menuClickListener"
name="onNewConversationClicked"
type="View.OnClickListener" />
</data>
@ -22,8 +19,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_1">
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
@ -70,6 +66,7 @@
android:background="@drawable/shape_search_round_background"
android:hint="Rechercher un contact, une conversation..."
android:textSize="14sp"
android:inputType="textPersonName|textNoSuggestions"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
@ -116,6 +113,7 @@
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/new_conversation"
android:onClick="@{onNewConversationClicked}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"

View file

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="cancelClickListener"
type="View.OnClickListener" />
<variable
name="viewModel"
type="org.linphone.ui.conversations.NewConversationViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:src="@drawable/shape_white_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/cancel"
android:onClick="@{cancelClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Annuler"
android:textSize="15sp"
android:padding="10dp"
android:textColor="@color/gray_1"
android:layout_marginStart="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/subtitle"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nouvelle conversation"
android:textSize="18sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginTop="22dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chiffrée de bout en bout"
android:textSize="14sp"
android:textColor="@color/gray_1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"/>
<EditText
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:padding="10dp"
android:drawableStart="@drawable/search"
android:drawablePadding="10dp"
android:background="@drawable/shape_search_square_background"
android:hint="Rechercher des contacts"
android:text="@={viewModel.filter}"
android:textSize="14sp"
android:inputType="textPersonName|textNoSuggestions"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/subtitle" />
<ImageView
android:id="@+id/new_group_avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/group_chat"
android:padding="10dp"
android:background="@drawable/shape_orange_round"
android:layout_marginTop="30dp"
android:layout_marginStart="20dp"
android:visibility="@{viewModel.filter.length() > 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toBottomOf="@id/search_bar"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Créer un nouveau groupe"
android:textStyle="bold"
android:textSize="16sp"
android:layout_marginStart="12dp"
android:layout_marginEnd="25dp"
android:drawableEnd="@drawable/next"
android:drawablePadding="5dp"
android:visibility="@{viewModel.filter.length() > 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toEndOf="@id/new_group_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/new_group_avatar"
app:layout_constraintBottom_toBottomOf="@id/new_group_avatar"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contacts_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/new_group_avatar"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -9,6 +9,16 @@
android:id="@+id/conversationsFragment"
android:name="org.linphone.ui.conversations.ConversationsFragment"
android:label="ConversationsFragment"
tools:layout="@layout/conversations_fragment"/>
tools:layout="@layout/conversations_fragment">
<action
android:id="@+id/action_conversationsFragment_to_newConversationFragment"
app:destination="@id/newConversationFragment" />
</fragment>
<fragment
android:id="@+id/newConversationFragment"
android:name="org.linphone.ui.conversations.NewConversationFragment"
android:label="NewConversationFragment"
tools:layout="@layout/new_conversation_fragment" />
</navigation>

View file

@ -11,5 +11,6 @@
<color name="gray_3">#EEF6F8</color>
<color name="gray_4">#949494</color>
<color name="gray_5">#4E4E4E</color>
<color name="gray_6">#EDEDED</color>
<color name="separator">#E5E5EA</color>
</resources>