diff --git a/app/src/main/java/org/linphone/ui/main/calls/fragment/CallsListFragment.kt b/app/src/main/java/org/linphone/ui/main/calls/fragment/CallsListFragment.kt index f6a2c7689..6e360c924 100644 --- a/app/src/main/java/org/linphone/ui/main/calls/fragment/CallsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/calls/fragment/CallsListFragment.kt @@ -19,7 +19,6 @@ */ package org.linphone.ui.main.calls.fragment -import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -32,12 +31,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.databinding.CallsListFragmentBinding -import org.linphone.ui.main.MainActivity import org.linphone.ui.main.calls.adapter.CallsListAdapter import org.linphone.ui.main.calls.viewmodel.CallsListViewModel import org.linphone.ui.main.fragment.GenericFragment import org.linphone.utils.Event -import org.linphone.utils.setKeyboardInsetListener class CallsListFragment : GenericFragment() { @@ -73,11 +70,6 @@ class CallsListFragment : GenericFragment() { postponeEnterTransition() - binding.root.setKeyboardInsetListener { keyboardVisible -> - val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE - listViewModel.bottomNavBarVisible.value = !portraitOrientation || !keyboardVisible - } - adapter = CallsListAdapter(viewLifecycleOwner) binding.callsList.setHasFixedSize(true) binding.callsList.adapter = adapter @@ -108,13 +100,15 @@ class CallsListFragment : GenericFragment() { val layoutManager = LinearLayoutManager(requireContext()) binding.callsList.layoutManager = layoutManager - binding.setOnAvatarClickListener { - (requireActivity() as MainActivity).toggleDrawerMenu() - } - listViewModel.callLogs.observe(viewLifecycleOwner) { adapter.submitList(it) startPostponedEnterTransition() } + + sharedViewModel.searchFilter.observe(viewLifecycleOwner) { + it.consume { filter -> + listViewModel.applyFilter(filter) + } + } } } diff --git a/app/src/main/java/org/linphone/ui/main/calls/viewmodel/CallsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/calls/viewmodel/CallsListViewModel.kt index b5d864ea9..48042f418 100644 --- a/app/src/main/java/org/linphone/ui/main/calls/viewmodel/CallsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/calls/viewmodel/CallsListViewModel.kt @@ -20,27 +20,56 @@ package org.linphone.ui.main.calls.viewmodel import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.core.CallLog +import org.linphone.core.Core +import org.linphone.core.CoreListenerStub import org.linphone.ui.main.calls.model.CallLogModel -import org.linphone.ui.main.viewmodel.TopBarViewModel -class CallsListViewModel : TopBarViewModel() { +class CallsListViewModel : ViewModel() { val callLogs = MutableLiveData>() - init { - title.value = "Calls" - bottomNavBarVisible.value = true + private var currentFilter = "" - coreContext.postOnCoreThread { core -> - val list = arrayListOf() - - // TODO : filter depending on currently selected account - for (callLog in core.callLogs) { - val model = CallLogModel(callLog) - list.add(model) - } - - callLogs.postValue(list) + private val coreListener = object : CoreListenerStub() { + override fun onCallLogUpdated(core: Core, callLog: CallLog) { + computeCallLogsList() } } + + init { + coreContext.postOnCoreThread { core -> + core.addListener(coreListener) + + computeCallLogsList() + } + } + + override fun onCleared() { + super.onCleared() + + coreContext.postOnCoreThread { core -> + core.removeListener(coreListener) + } + } + + fun applyFilter(filter: String) { + // UI thread + currentFilter = filter + // TODO + } + + private fun computeCallLogsList() { + // Core thread + val list = arrayListOf() + + // TODO : filter depending on currently selected account + for (callLog in coreContext.core.callLogs) { + val model = CallLogModel(callLog) + list.add(model) + } + + callLogs.postValue(list) + } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt index de2f89190..6d908d167 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt @@ -19,7 +19,6 @@ */ package org.linphone.ui.main.contacts.fragment -import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -32,14 +31,10 @@ import androidx.navigation.navGraphViewModels import androidx.recyclerview.widget.LinearLayoutManager import org.linphone.R import org.linphone.databinding.ContactsListFragmentBinding -import org.linphone.ui.main.MainActivity import org.linphone.ui.main.contacts.adapter.ContactsListAdapter import org.linphone.ui.main.contacts.viewmodel.ContactsListViewModel import org.linphone.ui.main.fragment.GenericFragment import org.linphone.utils.Event -import org.linphone.utils.hideKeyboard -import org.linphone.utils.setKeyboardInsetListener -import org.linphone.utils.showKeyboard class ContactsListFragment : GenericFragment() { private lateinit var binding: ContactsListFragmentBinding @@ -76,11 +71,6 @@ class ContactsListFragment : GenericFragment() { binding.lifecycleOwner = viewLifecycleOwner binding.viewModel = listViewModel - binding.root.setKeyboardInsetListener { keyboardVisible -> - val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE - listViewModel.bottomNavBarVisible.value = !portraitOrientation || !keyboardVisible - } - adapter = ContactsListAdapter(viewLifecycleOwner, false) binding.contactsList.setHasFixedSize(true) binding.contactsList.adapter = adapter @@ -114,28 +104,15 @@ class ContactsListFragment : GenericFragment() { favouritesAdapter.submitList(it) } - listViewModel.focusSearchBarEvent.observe(viewLifecycleOwner) { - it.consume { show -> - if (show) { - // To automatically open keyboard - binding.topBar.search.showKeyboard(requireActivity().window) - } else { - binding.topBar.search.hideKeyboard() - } + sharedViewModel.searchFilter.observe(viewLifecycleOwner) { + it.consume { filter -> + listViewModel.applyFilter(filter) } } - listViewModel.searchFilter.observe(viewLifecycleOwner) { filter -> - listViewModel.applyFilter(filter) - } - binding.setOnNewContactClicked { findNavController().navigate(R.id.action_global_newContactFragment) } - - binding.setOnAvatarClickListener { - (requireActivity() as MainActivity).toggleDrawerMenu() - } } private fun configureAdapter(adapter: ContactsListAdapter) { diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt index 2615bb876..886d0f393 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt @@ -20,6 +20,7 @@ package org.linphone.ui.main.contacts.viewmodel import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import java.util.ArrayList import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.contacts.ContactsListener @@ -29,15 +30,16 @@ import org.linphone.core.MagicSearchListenerStub import org.linphone.core.SearchResult import org.linphone.core.tools.Log import org.linphone.ui.main.contacts.model.ContactAvatarModel -import org.linphone.ui.main.viewmodel.TopBarViewModel -class ContactsListViewModel : TopBarViewModel() { +class ContactsListViewModel : ViewModel() { val contactsList = MutableLiveData>() val favourites = MutableLiveData>() val showFavourites = MutableLiveData() + val isListFiltered = MutableLiveData() + private var currentFilter = "" private var previousFilter = "NotSet" @@ -54,13 +56,16 @@ class ContactsListViewModel : TopBarViewModel() { private val contactsListener = object : ContactsListener { override fun onContactsLoaded() { // Core thread - applyFilter(currentFilter) + applyFilter( + currentFilter, + "", + MagicSearch.Source.Friends.toInt(), + MagicSearch.Aggregation.Friend + ) } } init { - title.value = "Contacts" - bottomNavBarVisible.value = true showFavourites.value = true coreContext.postOnCoreThread { core -> @@ -129,6 +134,7 @@ class ContactsListViewModel : TopBarViewModel() { fun applyFilter(filter: String) { // UI thread + isListFiltered.value = filter.isNotEmpty() coreContext.postOnCoreThread { applyFilter( filter, diff --git a/app/src/main/java/org/linphone/ui/main/conversations/ConversationsFragment.kt b/app/src/main/java/org/linphone/ui/main/conversations/ConversationsFragment.kt index 1a8cbaf67..4587adb3b 100644 --- a/app/src/main/java/org/linphone/ui/main/conversations/ConversationsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/conversations/ConversationsFragment.kt @@ -37,8 +37,6 @@ import org.linphone.R import org.linphone.databinding.ConversationsFragmentBinding import org.linphone.ui.main.conversations.adapter.ConversationsListAdapter import org.linphone.ui.main.conversations.viewmodel.ConversationsListViewModel -import org.linphone.utils.hideKeyboard -import org.linphone.utils.showKeyboard class ConversationsFragment : Fragment() { private lateinit var binding: ConversationsFragmentBinding @@ -145,7 +143,7 @@ class ConversationsFragment : Fragment() { } } - listViewModel.focusSearchBarEvent.observe(viewLifecycleOwner) { + /*listViewModel.focusSearchBarEvent.observe(viewLifecycleOwner) { it.consume { show -> if (show) { // To automatically open keyboard @@ -154,7 +152,7 @@ class ConversationsFragment : Fragment() { binding.topBar.search.hideKeyboard() } } - } + }*/ binding.setOnNewConversationClicked { if (findNavController().currentDestination?.id == R.id.conversationsFragment) { diff --git a/app/src/main/java/org/linphone/ui/main/conversations/viewmodel/ConversationsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/conversations/viewmodel/ConversationsListViewModel.kt index 35a94d914..82330f6c8 100644 --- a/app/src/main/java/org/linphone/ui/main/conversations/viewmodel/ConversationsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/conversations/viewmodel/ConversationsListViewModel.kt @@ -20,6 +20,7 @@ package org.linphone.ui.main.conversations.viewmodel import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import java.util.ArrayList import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.contacts.ContactsListener @@ -29,11 +30,10 @@ import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.core.tools.Log import org.linphone.ui.main.conversations.data.ChatRoomData -import org.linphone.ui.main.viewmodel.TopBarViewModel import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils -class ConversationsListViewModel : TopBarViewModel() { +class ConversationsListViewModel : ViewModel() { val chatRoomsList = MutableLiveData>() val notifyItemChangedEvent = MutableLiveData>() @@ -101,8 +101,6 @@ class ConversationsListViewModel : TopBarViewModel() { coreContext.postOnCoreThread { core -> updateChatRoomsList() } - - title.value = "Conversations" } override fun onCleared() { diff --git a/app/src/main/java/org/linphone/ui/main/fragment/BottomNavBarFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/BottomNavBarFragment.kt index 7fa8016e2..2c8169b4e 100644 --- a/app/src/main/java/org/linphone/ui/main/fragment/BottomNavBarFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/fragment/BottomNavBarFragment.kt @@ -19,6 +19,7 @@ */ package org.linphone.ui.main.fragment +import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -29,6 +30,7 @@ import org.linphone.R import org.linphone.databinding.BottomNavBarBinding import org.linphone.ui.main.viewmodel.SharedMainViewModel import org.linphone.utils.Event +import org.linphone.utils.setKeyboardInsetListener class BottomNavBarFragment : Fragment() { private lateinit var binding: BottomNavBarBinding @@ -53,6 +55,11 @@ class BottomNavBarFragment : Fragment() { binding.lifecycleOwner = viewLifecycleOwner + binding.root.setKeyboardInsetListener { keyboardVisible -> + val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE + binding.bottomNavBar.visibility = if (!portraitOrientation || !keyboardVisible) View.VISIBLE else View.GONE + } + binding.setOnContactsClicked { if (sharedViewModel.currentlyDisplayedFragment.value != R.id.contactsFragment) { sharedViewModel.navigateToContactsEvent.value = Event(true) diff --git a/app/src/main/java/org/linphone/ui/main/fragment/TopBarFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/TopBarFragment.kt new file mode 100644 index 000000000..899a4f395 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/main/fragment/TopBarFragment.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.ui.main.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import org.linphone.R +import org.linphone.databinding.TopSearchBarBinding +import org.linphone.ui.main.MainActivity +import org.linphone.ui.main.viewmodel.SharedMainViewModel +import org.linphone.ui.main.viewmodel.TopBarViewModel +import org.linphone.utils.Event +import org.linphone.utils.hideKeyboard +import org.linphone.utils.showKeyboard + +class TopBarFragment : Fragment() { + private lateinit var binding: TopSearchBarBinding + + private lateinit var viewModel: TopBarViewModel + private lateinit var sharedViewModel: SharedMainViewModel + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = TopSearchBarBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel = requireActivity().run { + ViewModelProvider(this)[TopBarViewModel::class.java] + } + + sharedViewModel = requireActivity().run { + ViewModelProvider(this)[SharedMainViewModel::class.java] + } + + binding.lifecycleOwner = viewLifecycleOwner + binding.viewModel = viewModel + + viewModel.openDrawerMenuEvent.observe(viewLifecycleOwner) { + it.consume { + (requireActivity() as MainActivity).toggleDrawerMenu() + } + } + + viewModel.focusSearchBarEvent.observe(viewLifecycleOwner) { + it.consume { show -> + if (show) { + // To automatically open keyboard + binding.search.showKeyboard(requireActivity().window) + } else { + binding.search.hideKeyboard() + } + } + } + + viewModel.searchFilter.observe(viewLifecycleOwner) { filter -> + sharedViewModel.searchFilter.value = Event(filter) + } + + sharedViewModel.currentlyDisplayedFragment.observe(viewLifecycleOwner) { + binding.title.text = when (it) { + R.id.contactsFragment -> { + "Contacts" + } + R.id.callsFragment -> { + "Calls" + } + R.id.conversationsFragment -> { + "Conversations" + } + else -> { + "" + } + } + } + } +} diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt index 37f6468fc..09d0de0f3 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt @@ -40,6 +40,10 @@ class SharedMainViewModel : ViewModel() { var currentlyDisplayedFragment = MutableLiveData() + /* Top bar related */ + + val searchFilter = MutableLiveData>() + /* Contacts related */ val showContactEvent = MutableLiveData>() diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/TopBarViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/TopBarViewModel.kt index c343a4d11..dfde83e8a 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/TopBarViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/TopBarViewModel.kt @@ -23,7 +23,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.linphone.utils.Event -abstract class TopBarViewModel : ViewModel() { +class TopBarViewModel : ViewModel() { val title = MutableLiveData() val searchBarVisible = MutableLiveData() @@ -34,7 +34,9 @@ abstract class TopBarViewModel : ViewModel() { MutableLiveData>() } - val bottomNavBarVisible = MutableLiveData() + val openDrawerMenuEvent: MutableLiveData> by lazy { + MutableLiveData>() + } init { searchBarVisible.value = false @@ -44,6 +46,11 @@ abstract class TopBarViewModel : ViewModel() { super.onCleared() } + fun openDrawerMenu() { + // UI thread + openDrawerMenuEvent.value = Event(true) + } + fun openSearchBar() { // UI thread searchBarVisible.value = true diff --git a/app/src/main/res/layout-land/calls_list_fragment.xml b/app/src/main/res/layout-land/calls_list_fragment.xml index d78fef779..c066cd384 100644 --- a/app/src/main/res/layout-land/calls_list_fragment.xml +++ b/app/src/main/res/layout-land/calls_list_fragment.xml @@ -5,9 +5,6 @@ - @@ -24,16 +21,15 @@ android:layout_height="match_parent" android:background="@color/primary_color"> - + app:layout_constraintEnd_toEndOf="parent"/> - @@ -38,22 +35,20 @@ android:name="org.linphone.ui.main.fragment.BottomNavBarFragment" android:layout_width="75dp" android:layout_height="0dp" - android:visibility="@{viewModel.bottomNavBarVisible ? View.VISIBLE : View.GONE}" bind:layout="@layout/bottom_nav_bar" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> - + app:layout_constraintEnd_toEndOf="parent"/> - @@ -24,11 +21,15 @@ android:layout_height="match_parent" android:background="@color/primary_color"> - + android:name="org.linphone.ui.main.fragment.TopBarFragment" + android:layout_width="0dp" + android:layout_height="@dimen/top_search_bar_height" + bind:layout="@layout/top_search_bar" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> - @@ -33,11 +30,15 @@ app:constraint_referenced_ids="no_contacts_image, no_contacts_label" android:visibility="@{viewModel.contactsList.empty ? View.VISIBLE : View.GONE}" /> - + android:name="org.linphone.ui.main.fragment.TopBarFragment" + android:layout_width="0dp" + android:layout_height="@dimen/top_search_bar_height" + bind:layout="@layout/top_search_bar" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> - + android:name="org.linphone.ui.main.fragment.TopBarFragment" + android:layout_width="0dp" + android:layout_height="@dimen/top_search_bar_height" + bind:layout="@layout/top_search_bar" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> - @@ -38,7 +35,7 @@ app:barrierDirection="bottom" />