Changes to use shared main view model for sliding pane state

This commit is contained in:
Sylvain Berfini 2023-08-04 13:54:20 +02:00
parent f3233528d6
commit bb22845b54
22 changed files with 330 additions and 225 deletions

View file

@ -7,11 +7,11 @@
<deviceKey> <deviceKey>
<Key> <Key>
<type value="SERIAL_NUMBER" /> <type value="SERIAL_NUMBER" />
<value value="1A051FDEE005XG" /> <value value="R5CNA03EXMM" />
</Key> </Key>
</deviceKey> </deviceKey>
</Target> </Target>
</runningDeviceTargetSelectedWithDropDown> </runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-08-04T10:20:13.247754228Z" /> <timeTargetWasSelectedWithDropDown value="2023-08-04T11:43:29.149882171Z" />
</component> </component>
</project> </project>

View file

@ -21,6 +21,7 @@ package org.linphone.contacts
import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Friend
import org.linphone.ui.MainActivity import org.linphone.ui.MainActivity
class ContactsManager { class ContactsManager {
@ -55,6 +56,10 @@ class ContactsManager {
} }
} }
fun findContactById(id: String): Friend? {
return coreContext.core.defaultFriendList?.findFriendByRefKey(id)
}
fun onCoreStarted() { fun onCoreStarted() {
} }

View file

@ -25,13 +25,24 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.navigation.navGraphViewModels
import androidx.transition.ChangeBounds import androidx.transition.ChangeBounds
import org.linphone.R
import org.linphone.databinding.ContactFragmentBinding import org.linphone.databinding.ContactFragmentBinding
import org.linphone.ui.contacts.viewmodel.ContactViewModel
import org.linphone.ui.viewmodel.SharedMainViewModel
class ContactFragment : Fragment() { class ContactFragment : Fragment() {
private lateinit var binding: ContactFragmentBinding private lateinit var binding: ContactFragmentBinding
private lateinit var sharedViewModel: SharedMainViewModel
private val viewModel: ContactViewModel by navGraphViewModels(
R.id.contactFragment
)
private val args: ContactFragmentArgs by navArgs() private val args: ContactFragmentArgs by navArgs()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -45,6 +56,11 @@ class ContactFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
binding = ContactFragmentBinding.inflate(layoutInflater) binding = ContactFragmentBinding.inflate(layoutInflater)
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedMainViewModel::class.java]
}
return binding.root return binding.root
} }
@ -54,16 +70,23 @@ class ContactFragment : Fragment() {
postponeEnterTransition() postponeEnterTransition()
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
val model = args.contact val refKey = args.contactRefKey
binding.model = model viewModel.findContactByRefKey(refKey)
binding.setBackClickListener { binding.setBackClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed() requireActivity().onBackPressedDispatcher.onBackPressed()
} }
(view.parent as? ViewGroup)?.doOnPreDraw { sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { slideable ->
startPostponedEnterTransition() viewModel.showBackButton.value = slideable
}
viewModel.contact.observe(viewLifecycleOwner) {
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
}
} }
} }
} }

View file

@ -28,6 +28,7 @@ import android.view.animation.Animation
import android.view.animation.AnimationUtils 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.lifecycle.ViewModelProvider
import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -40,6 +41,7 @@ import org.linphone.databinding.ContactsFragmentBinding
import org.linphone.ui.MainActivity import org.linphone.ui.MainActivity
import org.linphone.ui.contacts.adapter.ContactsListAdapter import org.linphone.ui.contacts.adapter.ContactsListAdapter
import org.linphone.ui.contacts.viewmodel.ContactsListViewModel import org.linphone.ui.contacts.viewmodel.ContactsListViewModel
import org.linphone.ui.viewmodel.SharedMainViewModel
import org.linphone.utils.SlidingPaneBackPressedCallback import org.linphone.utils.SlidingPaneBackPressedCallback
import org.linphone.utils.hideKeyboard import org.linphone.utils.hideKeyboard
import org.linphone.utils.setKeyboardInsetListener import org.linphone.utils.setKeyboardInsetListener
@ -47,9 +49,13 @@ import org.linphone.utils.showKeyboard
class ContactsFragment : Fragment() { class ContactsFragment : Fragment() {
private lateinit var binding: ContactsFragmentBinding private lateinit var binding: ContactsFragmentBinding
private lateinit var sharedViewModel: SharedMainViewModel
private val listViewModel: ContactsListViewModel by navGraphViewModels( private val listViewModel: ContactsListViewModel by navGraphViewModels(
R.id.contactsFragment R.id.contactsFragment
) )
private lateinit var adapter: ContactsListAdapter private lateinit var adapter: ContactsListAdapter
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
@ -67,6 +73,11 @@ class ContactsFragment : Fragment() {
): View { ): View {
binding = ContactsFragmentBinding.inflate(layoutInflater) binding = ContactsFragmentBinding.inflate(layoutInflater)
sharedElementEnterTransition = AutoTransition() sharedElementEnterTransition = AutoTransition()
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedMainViewModel::class.java]
}
return binding.root return binding.root
} }
@ -85,16 +96,20 @@ class ContactsFragment : Fragment() {
binding.root.doOnPreDraw { binding.root.doOnPreDraw {
val slidingPane = binding.slidingPaneLayout val slidingPane = binding.slidingPaneLayout
sharedViewModel.isSlidingPaneSlideable.value = slidingPane.isSlideable
requireActivity().onBackPressedDispatcher.addCallback( requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner, viewLifecycleOwner,
SlidingPaneBackPressedCallback(slidingPane) SlidingPaneBackPressedCallback(slidingPane)
) )
slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
} }
adapter = ContactsListAdapter(viewLifecycleOwner) adapter = ContactsListAdapter(viewLifecycleOwner)
binding.contactsList.setHasFixedSize(true) binding.contactsView.contactsList.setHasFixedSize(true)
binding.contactsList.adapter = adapter binding.contactsView.contactsList.adapter = adapter
adapter.contactLongClickedEvent.observe(viewLifecycleOwner) { adapter.contactLongClickedEvent.observe(viewLifecycleOwner) {
it.consume { model -> it.consume { model ->
@ -106,15 +121,14 @@ class ContactsFragment : Fragment() {
} }
adapter.contactClickedEvent.observe(viewLifecycleOwner) { adapter.contactClickedEvent.observe(viewLifecycleOwner) {
it.consume { pair -> it.consume { model ->
val b = pair.first
val model = pair.second
if (findNavController().currentDestination?.id == R.id.contactsFragment) { if (findNavController().currentDestination?.id == R.id.contactsFragment) {
val navHostFragment = childFragmentManager.findFragmentById( val navHostFragment = childFragmentManager.findFragmentById(
R.id.contacts_nav_container R.id.contacts_nav_container
) as NavHostFragment ) as NavHostFragment
val action = ContactFragmentDirections.actionGlobalContactFragment(model) val action = ContactFragmentDirections.actionGlobalContactFragment(
model.id ?: ""
)
navHostFragment.navController.navigate(action) navHostFragment.navController.navigate(action)
if (!binding.slidingPaneLayout.isOpen) { if (!binding.slidingPaneLayout.isOpen) {
@ -125,7 +139,7 @@ class ContactsFragment : Fragment() {
} }
val layoutManager = LinearLayoutManager(requireContext()) val layoutManager = LinearLayoutManager(requireContext())
binding.contactsList.layoutManager = layoutManager binding.contactsView.contactsList.layoutManager = layoutManager
listViewModel.contactsList.observe( listViewModel.contactsList.observe(
viewLifecycleOwner viewLifecycleOwner
@ -156,13 +170,7 @@ class ContactsFragment : Fragment() {
binding.setOnNewContactClicked { binding.setOnNewContactClicked {
if (findNavController().currentDestination?.id == R.id.contactsFragment) { if (findNavController().currentDestination?.id == R.id.contactsFragment) {
val navHostFragment = findNavController().navigate(R.id.action_contactsFragment_to_newContactFragment)
childFragmentManager.findFragmentById(R.id.contacts_nav_container) as NavHostFragment?
if (navHostFragment != null) {
navHostFragment.navController.navigate(R.id.action_global_newContactFragment)
} else {
findNavController().navigate(R.id.action_contactsFragment_to_newContactFragment)
}
} }
} }

View file

@ -18,8 +18,8 @@ class ContactsListAdapter(
) : ListAdapter<ContactModel, RecyclerView.ViewHolder>(ContactDiffCallback()) { ) : ListAdapter<ContactModel, RecyclerView.ViewHolder>(ContactDiffCallback()) {
var selectedAdapterPosition = -1 var selectedAdapterPosition = -1
val contactClickedEvent: MutableLiveData<Event<Pair<ContactListCellBinding, ContactModel>>> by lazy { val contactClickedEvent: MutableLiveData<Event<ContactModel>> by lazy {
MutableLiveData<Event<Pair<ContactListCellBinding, ContactModel>>>() MutableLiveData<Event<ContactModel>>()
} }
val contactLongClickedEvent: MutableLiveData<Event<ContactModel>> by lazy { val contactLongClickedEvent: MutableLiveData<Event<ContactModel>> by lazy {
@ -74,7 +74,7 @@ class ContactsListAdapter(
binding.root.isSelected = bindingAdapterPosition == selectedAdapterPosition binding.root.isSelected = bindingAdapterPosition == selectedAdapterPosition
binding.setOnClickListener { binding.setOnClickListener {
contactClickedEvent.value = Event(Pair(binding, contactModel)) contactClickedEvent.value = Event(contactModel)
} }
binding.setOnLongClickListener { binding.setOnLongClickListener {

View file

@ -23,13 +23,12 @@ import android.content.ContentUris
import android.net.Uri import android.net.Uri
import android.provider.ContactsContract import android.provider.ContactsContract
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import java.io.Serializable
import org.linphone.core.ConsolidatedPresence import org.linphone.core.ConsolidatedPresence
import org.linphone.core.Friend import org.linphone.core.Friend
import org.linphone.core.FriendListenerStub import org.linphone.core.FriendListenerStub
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
class ContactModel(val friend: Friend) : Serializable { class ContactModel(val friend: Friend) {
val id = friend.refKey val id = friend.refKey
val initials = LinphoneUtils.getInitials(friend.name.orEmpty()) val initials = LinphoneUtils.getInitials(friend.name.orEmpty())

View file

@ -0,0 +1,44 @@
/*
* 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.contacts.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.ui.contacts.model.ContactModel
class ContactViewModel : ViewModel() {
val contact = MutableLiveData<ContactModel>()
val showBackButton = MutableLiveData<Boolean>()
init {
}
fun findContactByRefKey(refKey: String) {
// UI thread
coreContext.postOnCoreThread {
val friend = coreContext.contactsManager.findContactById(refKey)
if (friend != null) {
contact.postValue(ContactModel(friend))
}
}
}
}

View file

@ -27,8 +27,8 @@ import org.linphone.core.Friend
import org.linphone.core.MagicSearch import org.linphone.core.MagicSearch
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.ui.TopBarViewModel
import org.linphone.ui.contacts.model.ContactModel import org.linphone.ui.contacts.model.ContactModel
import org.linphone.ui.viewmodel.TopBarViewModel
class ContactsListViewModel : TopBarViewModel() { class ContactsListViewModel : TopBarViewModel() {
val bottomNavBarVisible = MutableLiveData<Boolean>() val bottomNavBarVisible = MutableLiveData<Boolean>()

View file

@ -29,8 +29,8 @@ import org.linphone.core.Core
import org.linphone.core.CoreListenerStub import org.linphone.core.CoreListenerStub
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.ui.TopBarViewModel
import org.linphone.ui.conversations.data.ChatRoomData import org.linphone.ui.conversations.data.ChatRoomData
import org.linphone.ui.viewmodel.TopBarViewModel
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils

View file

@ -0,0 +1,27 @@
/*
* 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.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SharedMainViewModel : ViewModel() {
val isSlidingPaneSlideable = MutableLiveData<Boolean>()
}

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.ui package org.linphone.ui.viewmodel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel

View file

@ -22,7 +22,6 @@ package org.linphone.utils
import android.view.View import android.view.View
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.slidingpanelayout.widget.SlidingPaneLayout import androidx.slidingpanelayout.widget.SlidingPaneLayout
import org.linphone.core.tools.Log
class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneLayout) : class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneLayout) :
OnBackPressedCallback( OnBackPressedCallback(
@ -31,25 +30,19 @@ class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneL
SlidingPaneLayout.PanelSlideListener { SlidingPaneLayout.PanelSlideListener {
init { init {
Log.d(
"[Master Fragment] SlidingPane isSlideable = ${slidingPaneLayout.isSlideable}, isOpen = ${slidingPaneLayout.isOpen}"
)
slidingPaneLayout.addPanelSlideListener(this) slidingPaneLayout.addPanelSlideListener(this)
} }
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
Log.d("[Master Fragment] handleOnBackPressed, closing sliding pane")
slidingPaneLayout.hideKeyboard() slidingPaneLayout.hideKeyboard()
slidingPaneLayout.closePane() slidingPaneLayout.closePane()
} }
override fun onPanelOpened(panel: View) { override fun onPanelOpened(panel: View) {
Log.d("[Master Fragment] onPanelOpened")
isEnabled = true isEnabled = true
} }
override fun onPanelClosed(panel: View) { override fun onPanelClosed(panel: View) {
Log.d("[Master Fragment] onPanelClosed")
isEnabled = false isEnabled = false
} }

View file

@ -43,6 +43,8 @@
android:gravity="center" android:gravity="center"
android:onClick="@{onContactsClicked}" android:onClick="@{onContactsClicked}"
android:text="Contacts" android:text="Contacts"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
android:textSize="11sp" android:textSize="11sp"
android:textStyle="@{contactsSelected ? Typeface.BOLD : Typeface.NORMAL}" android:textStyle="@{contactsSelected ? Typeface.BOLD : Typeface.NORMAL}"
@ -60,6 +62,8 @@
android:drawableTint="@{callsSelected ? @color/primary_color : @color/gray_9, default=@color/gray_9}" android:drawableTint="@{callsSelected ? @color/primary_color : @color/gray_9, default=@color/gray_9}"
android:gravity="center" android:gravity="center"
android:text="Calls" android:text="Calls"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
android:textSize="11sp" android:textSize="11sp"
android:textStyle="@{callsSelected ? Typeface.BOLD : Typeface.NORMAL}" android:textStyle="@{callsSelected ? Typeface.BOLD : Typeface.NORMAL}"
@ -78,6 +82,8 @@
android:gravity="center" android:gravity="center"
android:onClick="@{onConversationsClicked}" android:onClick="@{onConversationsClicked}"
android:text="Conversations" android:text="Conversations"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
android:textSize="11sp" android:textSize="11sp"
android:textStyle="@{conversationsSelected ? Typeface.BOLD : Typeface.NORMAL}" android:textStyle="@{conversationsSelected ? Typeface.BOLD : Typeface.NORMAL}"
@ -95,6 +101,8 @@
android:drawableTint="@{meetingsSelected ? @color/primary_color : @color/gray_9, default=@color/gray_9}" android:drawableTint="@{meetingsSelected ? @color/primary_color : @color/gray_9, default=@color/gray_9}"
android:gravity="center" android:gravity="center"
android:text="Meetings" android:text="Meetings"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
android:textSize="11sp" android:textSize="11sp"
android:textStyle="@{meetingsSelected ? Typeface.BOLD : Typeface.NORMAL}" android:textStyle="@{meetingsSelected ? Typeface.BOLD : Typeface.NORMAL}"

View file

@ -19,122 +19,88 @@
type="org.linphone.ui.contacts.viewmodel.ContactsListViewModel" /> type="org.linphone.ui.contacts.viewmodel.ContactsListViewModel" />
</data> </data>
<androidx.slidingpanelayout.widget.SlidingPaneLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sliding_pane_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <include
android:layout_width="355dp" bind:onConversationsClicked="@{onConversationsClicked}"
android:layout_height="match_parent" bind:contactsSelected="@{true}"
android:background="@color/primary_color"> android:visibility="@{viewModel.bottomNavBarVisible ? View.VISIBLE : View.GONE}"
android:id="@+id/bottom_nav_bar"
<androidx.constraintlayout.widget.Group layout="@layout/bottom_nav_bar"
android:layout_width="wrap_content" android:layout_width="75dp"
android:layout_height="wrap_content" android:layout_height="0dp"
app:constraint_referenced_ids="no_contacts_image, no_contacts_label" app:layout_constraintStart_toStartOf="parent"
android:visibility="@{viewModel.contactsList.empty ? View.VISIBLE : View.GONE}" />
<include
android:id="@+id/top_bar"
bind:viewModel="@{viewModel}"
bind:onAvatarClickListener="@{onAvatarClickListener}"
layout="@layout/top_search_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="7dp"
android:src="@drawable/shape_white_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintTop_toBottomOf="@id/top_bar" />
<ImageView
android:id="@+id/no_contacts_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="@drawable/illu"
android:layout_margin="10dp"
app:layout_constraintHeight_max="200dp"
app:layout_constraintBottom_toTopOf="@id/no_contacts_label"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintTop_toBottomOf="@id/background" />
<TextView
android:id="@+id/no_contacts_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No contacts for the moment..."
android:textColor="@color/gray_9"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintTop_toBottomOf="@id/no_contacts_image" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contactsList"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintBottom_toBottomOf="parent" />
<include
bind:onConversationsClicked="@{onConversationsClicked}"
bind:contactsSelected="@{true}"
android:visibility="@{viewModel.bottomNavBarVisible ? View.VISIBLE : View.GONE}"
android:id="@+id/bottom_nav_bar"
layout="@layout/bottom_nav_bar"
android:layout_width="75dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/new_contact"
android:onClick="@{onNewContactClicked}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/add"
app:tint="@color/gray_8"
app:backgroundTint="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/contacts_nav_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:defaultNavHost="false"
app:navGraph="@navigation/chat_nav_graph"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintStart_toEndOf="@id/contactsList"
<include
android:id="@+id/top_bar"
bind:viewModel="@{viewModel}"
bind:onAvatarClickListener="@{onAvatarClickListener}"
layout="@layout/top_search_bar"
android:layout_height="wrap_content"
android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>
</androidx.slidingpanelayout.widget.SlidingPaneLayout> <androidx.slidingpanelayout.widget.SlidingPaneLayout
android:id="@+id/sliding_pane_layout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="280dp"
android:layout_height="match_parent"
android:background="@color/primary_color">
<include
android:id="@+id/contacts_view"
layout="@layout/contacts_list_fragment"
bind:viewModel="@{viewModel}"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/new_contact"
android:onClick="@{onNewContactClicked}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/add"
app:tint="@color/gray_8"
app:backgroundTint="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/contacts_nav_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:defaultNavHost="false"
app:navGraph="@navigation/contact_nav_graph"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/contactsList"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -43,6 +43,8 @@
android:drawableTop="@drawable/contacts" android:drawableTop="@drawable/contacts"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:text="Contacts" android:text="Contacts"
android:maxLines="1"
android:ellipsize="end"
android:textSize="11sp" android:textSize="11sp"
android:gravity="center" android:gravity="center"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
@ -63,6 +65,8 @@
android:drawableTop="@drawable/calls" android:drawableTop="@drawable/calls"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:text="Calls" android:text="Calls"
android:maxLines="1"
android:ellipsize="end"
android:textSize="11sp" android:textSize="11sp"
android:gravity="center" android:gravity="center"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
@ -84,6 +88,8 @@
android:drawableTop="@drawable/conversations" android:drawableTop="@drawable/conversations"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:text="Conversations" android:text="Conversations"
android:maxLines="1"
android:ellipsize="end"
android:textSize="11sp" android:textSize="11sp"
android:gravity="center" android:gravity="center"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"
@ -104,6 +110,8 @@
android:drawableTop="@drawable/meetings" android:drawableTop="@drawable/meetings"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:text="Meetings" android:text="Meetings"
android:maxLines="1"
android:ellipsize="end"
android:textSize="11sp" android:textSize="11sp"
android:gravity="center" android:gravity="center"
android:textColor="@color/gray_9" android:textColor="@color/gray_9"

View file

@ -8,8 +8,8 @@
name="backClickListener" name="backClickListener"
type="View.OnClickListener" /> type="View.OnClickListener" />
<variable <variable
name="model" name="viewModel"
type="org.linphone.ui.contacts.model.ContactModel" /> type="org.linphone.ui.contacts.viewmodel.ContactViewModel" />
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -24,6 +24,7 @@
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginTop="17dp" android:layout_marginTop="17dp"
android:onClick="@{backClickListener}" android:onClick="@{backClickListener}"
android:visibility="@{viewModel.showBackButton ? View.VISIBLE : View.GONE}"
android:padding="1dp" android:padding="1dp"
android:src="@drawable/back" android:src="@drawable/back"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -57,7 +58,7 @@
android:layout_height="72dp" android:layout_height="72dp"
android:layout_marginTop="38dp" android:layout_marginTop="38dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
contactAvatar="@{model}" contactAvatar="@{viewModel.contact}"
app:avatarViewBorderWidth="0dp" app:avatarViewBorderWidth="0dp"
app:avatarViewIndicatorBorderColor="@color/white" app:avatarViewIndicatorBorderColor="@color/white"
app:avatarViewIndicatorBorderSizeCriteria="8" app:avatarViewIndicatorBorderSizeCriteria="8"
@ -79,7 +80,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@{model.name, default=`John Doe`}" android:text="@{viewModel.contact.name, default=`John Doe`}"
android:textColor="@color/gray_8" android:textColor="@color/gray_8"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View file

@ -29,66 +29,22 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/primary_color"> android:background="@color/primary_color">
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="no_contacts_image, no_contacts_label"
android:visibility="@{viewModel.contactsList.empty ? View.VISIBLE : View.GONE}" />
<include <include
android:id="@+id/top_bar" android:id="@+id/top_bar"
bind:viewModel="@{viewModel}" bind:viewModel="@{viewModel}"
bind:onAvatarClickListener="@{onAvatarClickListener}" bind:onAvatarClickListener="@{onAvatarClickListener}"
layout="@layout/top_search_bar" /> layout="@layout/top_search_bar" />
<ImageView <include
android:id="@+id/background" android:id="@+id/contacts_view"
layout="@layout/contacts_list_fragment"
bind:viewModel="@{viewModel}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="7dp"
android:src="@drawable/shape_white_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_bar" /> app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar"
<ImageView app:layout_constraintEnd_toEndOf="parent"/>
android:id="@+id/no_contacts_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="@drawable/illu"
android:layout_margin="10dp"
app:layout_constraintHeight_max="200dp"
app:layout_constraintBottom_toTopOf="@id/no_contacts_label"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/background" />
<TextView
android:id="@+id/no_contacts_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No contacts for the moment..."
android:textColor="@color/gray_9"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_contacts_image" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contactsList"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar" />
<include <include
bind:onConversationsClicked="@{onConversationsClicked}" bind:onConversationsClicked="@{onConversationsClicked}"
@ -124,7 +80,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
app:defaultNavHost="false" app:defaultNavHost="false"
app:navGraph="@navigation/chat_nav_graph"/> app:navGraph="@navigation/contact_nav_graph"/>
</androidx.slidingpanelayout.widget.SlidingPaneLayout> </androidx.slidingpanelayout.widget.SlidingPaneLayout>

View file

@ -0,0 +1,75 @@
<?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"
xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="org.linphone.ui.contacts.viewmodel.ContactsListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary_color">
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="no_contacts_image, no_contacts_label"
android:visibility="@{viewModel.contactsList.empty ? View.VISIBLE : View.GONE}" />
<ImageView
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="7dp"
android:src="@drawable/shape_white_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/no_contacts_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="@drawable/illu"
android:layout_margin="10dp"
app:layout_constraintHeight_max="200dp"
app:layout_constraintBottom_toTopOf="@id/no_contacts_label"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/background" />
<TextView
android:id="@+id/no_contacts_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No contacts for the moment..."
android:textColor="@color/gray_9"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_contacts_image" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contactsList"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -11,7 +11,7 @@
type="View.OnClickListener" /> type="View.OnClickListener" />
<variable <variable
name="viewModel" name="viewModel"
type="org.linphone.ui.TopBarViewModel" /> type="org.linphone.ui.viewmodel.TopBarViewModel" />
</data> </data>
<!-- Background color here is mandatory for transparency effect on child views --> <!-- Background color here is mandatory for transparency effect on child views -->

View file

@ -10,7 +10,7 @@
type="View.OnClickListener" /> type="View.OnClickListener" />
<variable <variable
name="viewModel" name="viewModel"
type="org.linphone.ui.TopBarViewModel" /> type="org.linphone.ui.viewmodel.TopBarViewModel" />
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -53,7 +53,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginTop="20dp" android:layout_marginTop="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@{viewModel.title, default=`Title`}" android:text="@{viewModel.title, default=`Title`}"
@ -95,7 +95,7 @@
android:id="@+id/search" android:id="@+id/search"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginTop="20dp" android:layout_marginTop="10dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:textColorHint="@color/gray_6" android:textColorHint="@color/gray_6"
app:hintEnabled="false" app:hintEnabled="false"

View file

@ -2,7 +2,7 @@
<navigation xmlns:android="http://schemas.android.com/apk/res/android" <navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/chat_nav_graph" android:id="@+id/contact_nav_graph"
app:startDestination="@id/emptyFragment"> app:startDestination="@id/emptyFragment">
<fragment <fragment
@ -17,22 +17,11 @@
android:label="ContactFragment" android:label="ContactFragment"
tools:layout="@layout/contact_fragment" > tools:layout="@layout/contact_fragment" >
<argument <argument
android:name="contact" android:name="contactRefKey"
app:argType="org.linphone.ui.contacts.model.ContactModel" /> app:argType="string" />
</fragment> </fragment>
<action android:id="@+id/action_global_contactFragment" <action android:id="@+id/action_global_contactFragment"
app:destination="@id/contactFragment"/> app:destination="@id/contactFragment"/>
<fragment
android:id="@+id/newContactFragment"
android:name="org.linphone.ui.contacts.NewContactFragment"
android:label="NewContactFragment"
tools:layout="@layout/new_contact_fragment" />
<action android:id="@+id/action_global_newContactFragment"
app:destination="@id/newContactFragment"
app:enterAnim="@anim/slide_in"
app:popExitAnim="@anim/slide_out"/>
</navigation> </navigation>

View file

@ -79,11 +79,14 @@
android:label="ContactFragment" android:label="ContactFragment"
tools:layout="@layout/contact_fragment" > tools:layout="@layout/contact_fragment" >
<argument <argument
android:name="contact" android:name="contactRefKey"
app:argType="org.linphone.ui.contacts.model.ContactModel" /> app:argType="string" />
</fragment> </fragment>
<action android:id="@+id/action_global_contactFragment" <fragment
app:destination="@id/contactFragment"/> android:id="@+id/newContactFragment"
android:name="org.linphone.ui.contacts.NewContactFragment"
android:label="NewContactFragment"
tools:layout="@layout/new_contact_fragment" />
</navigation> </navigation>