diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt index eaad0376f..2ad646194 100644 --- a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt @@ -24,22 +24,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.doOnPreDraw -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.navArgs import androidx.navigation.navGraphViewModels import androidx.transition.ChangeBounds import org.linphone.R import org.linphone.databinding.ContactFragmentBinding import org.linphone.ui.contacts.viewmodel.ContactViewModel -import org.linphone.ui.viewmodel.SharedMainViewModel +import org.linphone.ui.fragment.GenericFragment import org.linphone.utils.Event -class ContactFragment : Fragment() { +class ContactFragment : GenericFragment() { private lateinit var binding: ContactFragmentBinding - private lateinit var sharedViewModel: SharedMainViewModel - private val viewModel: ContactViewModel by navGraphViewModels( R.id.contactFragment ) @@ -57,11 +53,6 @@ class ContactFragment : Fragment() { savedInstanceState: Bundle? ): View { binding = ContactFragmentBinding.inflate(layoutInflater) - - sharedViewModel = requireActivity().run { - ViewModelProvider(this)[SharedMainViewModel::class.java] - } - return binding.root } diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsFragment.kt index 024ec2303..cab1df8e6 100644 --- a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsFragment.kt +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsFragment.kt @@ -24,34 +24,23 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.doOnPreDraw -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import androidx.slidingpanelayout.widget.SlidingPaneLayout -import androidx.transition.AutoTransition import org.linphone.R import org.linphone.databinding.ContactsFragmentBinding -import org.linphone.ui.viewmodel.SharedMainViewModel +import org.linphone.ui.fragment.GenericFragment import org.linphone.utils.SlidingPaneBackPressedCallback -class ContactsFragment : Fragment() { +class ContactsFragment : GenericFragment() { private lateinit var binding: ContactsFragmentBinding - private lateinit var sharedViewModel: SharedMainViewModel - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = ContactsFragmentBinding.inflate(layoutInflater) - sharedElementEnterTransition = AutoTransition() - - sharedViewModel = requireActivity().run { - ViewModelProvider(this)[SharedMainViewModel::class.java] - } - return binding.root } @@ -62,6 +51,7 @@ class ContactsFragment : Fragment() { binding.root.doOnPreDraw { val slidingPane = binding.slidingPaneLayout + slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED sharedViewModel.isSlidingPaneSlideable.value = slidingPane.isSlideable @@ -69,8 +59,6 @@ class ContactsFragment : Fragment() { viewLifecycleOwner, SlidingPaneBackPressedCallback(slidingPane) ) - - slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED } sharedViewModel.closeSlidingPaneEvent.observe( diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsListFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsListFragment.kt index 533d1d4cc..06d61f2fe 100644 --- a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactsListFragment.kt @@ -27,8 +27,6 @@ import android.view.ViewGroup import android.view.animation.Animation import android.view.animation.AnimationUtils import androidx.core.view.doOnPreDraw -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import androidx.navigation.navGraphViewModels import androidx.recyclerview.widget.LinearLayoutManager @@ -37,17 +35,15 @@ import org.linphone.databinding.ContactsListFragmentBinding import org.linphone.ui.MainActivity import org.linphone.ui.contacts.adapter.ContactsListAdapter import org.linphone.ui.contacts.viewmodel.ContactsListViewModel -import org.linphone.ui.viewmodel.SharedMainViewModel +import org.linphone.ui.fragment.GenericFragment import org.linphone.utils.Event import org.linphone.utils.hideKeyboard import org.linphone.utils.setKeyboardInsetListener import org.linphone.utils.showKeyboard -class ContactsListFragment : Fragment() { +class ContactsListFragment : GenericFragment() { private lateinit var binding: ContactsListFragmentBinding - private lateinit var sharedViewModel: SharedMainViewModel - private val listViewModel: ContactsListViewModel by navGraphViewModels( R.id.contactsListFragment ) @@ -69,11 +65,6 @@ class ContactsListFragment : Fragment() { savedInstanceState: Bundle? ): View { binding = ContactsListFragmentBinding.inflate(layoutInflater) - - sharedViewModel = requireActivity().run { - ViewModelProvider(this)[SharedMainViewModel::class.java] - } - return binding.root } diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/NewContactFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/NewContactFragment.kt index 84a39ae4c..e7af5af12 100644 --- a/app/src/main/java/org/linphone/ui/contacts/fragment/NewContactFragment.kt +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/NewContactFragment.kt @@ -24,11 +24,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.Animation -import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import org.linphone.databinding.NewContactFragmentBinding +import org.linphone.ui.fragment.GenericFragment -class NewContactFragment : Fragment() { +class NewContactFragment : GenericFragment() { private lateinit var binding: NewContactFragmentBinding override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { diff --git a/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt b/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt new file mode 100644 index 000000000..fadf9ccf9 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt @@ -0,0 +1,134 @@ +/* + * 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.fragment + +import android.os.Bundle +import android.view.View +import android.widget.ImageView +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.findNavController +import org.linphone.R +import org.linphone.core.tools.Log +import org.linphone.ui.viewmodel.SharedMainViewModel + +abstract class GenericFragment : Fragment() { + protected lateinit var sharedViewModel: SharedMainViewModel + + private val onBackPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + try { + val navController = findNavController() + Log.i("[Generic Fragment] ${getFragmentRealClassName()} handleOnBackPressed") + if (!navController.popBackStack()) { + Log.i("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop") + if (!navController.navigateUp()) { + Log.i( + "[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up" + ) + // Disable this callback & start a new back press event + isEnabled = false + goBack() + } + } + } catch (ise: IllegalStateException) { + Log.e( + "[Generic Fragment] ${getFragmentRealClassName()}.handleOnBackPressed() Can't go back: $ise" + ) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + sharedViewModel = requireActivity().run { + ViewModelProvider(this)[SharedMainViewModel::class.java] + } + + sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { + Log.i( + "[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed" + ) + onBackPressedCallback.isEnabled = backPressedCallBackEnabled() + } + + setupBackPressCallback() + } + + override fun onDestroyView() { + super.onDestroyView() + + onBackPressedCallback.remove() + } + + private fun getFragmentRealClassName(): String { + return this.javaClass.name + } + + protected fun goBack() { + try { + requireActivity().onBackPressedDispatcher.onBackPressed() + } catch (ise: IllegalStateException) { + Log.w("[Generic Fragment] ${getFragmentRealClassName()}.goBack() can't go back: $ise") + onBackPressedCallback.handleOnBackPressed() + } + } + + private fun setupBackPressCallback() { + Log.i("[Generic Fragment] ${getFragmentRealClassName()} setupBackPressCallback") + + val backButton = view?.findViewById(R.id.back) + if (backButton != null && backButton.visibility == View.VISIBLE) { + Log.i("[Generic Fragment] ${getFragmentRealClassName()} found back button") + // If popping navigation back stack entry would bring us to an "empty" fragment + // then don't do it if sliding pane layout isn't "flat" + onBackPressedCallback.isEnabled = backPressedCallBackEnabled() + backButton.setOnClickListener { goBack() } + } else { + onBackPressedCallback.isEnabled = false + } + + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + onBackPressedCallback + ) + } + + private fun backPressedCallBackEnabled(): Boolean { + // This allow to navigate a SlidingPane child nav graph. + // This only concerns fragments for which the nav graph is inside a SlidingPane layout. + // In our case it's all graphs except the main one. + if (findNavController().graph.id == R.id.main_nav_graph) return false + + val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false + Log.i( + "[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat" + ) + val isPreviousFragmentEmpty = findNavController().previousBackStackEntry?.destination?.id == R.id.emptyFragment + Log.i( + "[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty" + ) + val popBackStack = isSlidingPaneFlat || !isPreviousFragmentEmpty + Log.i("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack") + return popBackStack + } +} diff --git a/app/src/main/res/navigation/contact_right_nav_graph.xml b/app/src/main/res/navigation/contact_right_nav_graph.xml index 1e1593fdb..a4297ad7c 100644 --- a/app/src/main/res/navigation/contact_right_nav_graph.xml +++ b/app/src/main/res/navigation/contact_right_nav_graph.xml @@ -21,7 +21,9 @@ app:argType="string" /> - + \ No newline at end of file