Fixed & improved back press callbacks set by app

This commit is contained in:
Sylvain Berfini 2023-12-04 09:50:55 +01:00
parent ddc8ba7105
commit d178ce40b5
12 changed files with 148 additions and 125 deletions

View file

@ -11,11 +11,11 @@ import androidx.navigation.fragment.navArgs
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatEphemeralLifetimeFragmentBinding
import org.linphone.ui.main.chat.viewmodel.ConversationEphemeralLifetimeViewModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.utils.Event
@UiThread
class ConversationEphemeralLifetimeFragment : GenericFragment() {
class ConversationEphemeralLifetimeFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Conversation Ephemeral Lifetime Fragment]"
}
@ -40,9 +40,6 @@ class ConversationEphemeralLifetimeFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner

View file

@ -72,7 +72,7 @@ import org.linphone.ui.main.chat.view.RichEditText
import org.linphone.ui.main.chat.viewmodel.ConversationViewModel
import org.linphone.ui.main.chat.viewmodel.ConversationViewModel.Companion.SCROLLING_POSITION_NOT_SET
import org.linphone.ui.main.chat.viewmodel.SendMessageInConversationViewModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.LinphoneUtils
@ -84,7 +84,7 @@ import org.linphone.utils.setKeyboardInsetListener
import org.linphone.utils.showKeyboard
@UiThread
class ConversationFragment : GenericFragment() {
class ConversationFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Conversation Fragment]"
}
@ -212,9 +212,6 @@ class ConversationFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner

View file

@ -41,12 +41,12 @@ import org.linphone.ui.main.chat.adapter.ConversationParticipantsAdapter
import org.linphone.ui.main.chat.model.ConversationEditSubjectDialogModel
import org.linphone.ui.main.chat.model.ParticipantModel
import org.linphone.ui.main.chat.viewmodel.ConversationInfoViewModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
@UiThread
class ConversationInfoFragment : GenericFragment() {
class ConversationInfoFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Conversation Info Fragment]"
}
@ -79,9 +79,6 @@ class ConversationInfoFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
postponeEnterTransition()
super.onViewCreated(view, savedInstanceState)

View file

@ -45,13 +45,13 @@ import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel
import org.linphone.ui.main.contacts.model.TrustCallDialogModel
import org.linphone.ui.main.contacts.viewmodel.ContactViewModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.ui.main.history.model.ConfirmationDialogModel
import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
@UiThread
class ContactFragment : GenericFragment() {
class ContactFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Contact Fragment]"
}
@ -82,9 +82,6 @@ class ContactFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
postponeEnterTransition()
super.onViewCreated(view, savedInstanceState)

View file

@ -43,13 +43,13 @@ import org.linphone.databinding.ContactNewOrEditFragmentBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.contacts.model.NewOrEditNumberOrAddressModel
import org.linphone.ui.main.contacts.viewmodel.ContactNewOrEditViewModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.ui.main.history.model.ConfirmationDialogModel
import org.linphone.utils.DialogUtils
import org.linphone.utils.FileUtils
@UiThread
class EditContactFragment : GenericFragment() {
class EditContactFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Edit Contact Fragment]"
}
@ -97,9 +97,6 @@ class EditContactFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
postponeEnterTransition()
super.onViewCreated(view, savedInstanceState)

View file

@ -60,8 +60,10 @@ abstract class AbstractTopBarFragment : GenericFragment() {
view?.doOnPreDraw {
slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
sharedViewModel.isSlidingPaneSlideable.value = slidingPane.isSlideable
slidingPaneBackPressedCallback.isEnabled = slidingPane.isSlideable
val slideable = slidingPane.isSlideable
sharedViewModel.isSlidingPaneSlideable.value = slideable
slidingPaneBackPressedCallback.isEnabled = slideable && slidingPane.isOpen
Log.d("$TAG Sliding Pane is ${if (slideable) "slideable" else "flat"}")
}
sharedViewModel.closeSlidingPaneEvent.observe(
@ -69,7 +71,7 @@ abstract class AbstractTopBarFragment : GenericFragment() {
) {
it.consume {
if (slidingPane.isOpen && slidingPane.isSlideable) {
Log.i("$TAG Closing sliding pane")
Log.d("$TAG Closing sliding pane")
slidingPane.closePane()
}
}
@ -80,7 +82,7 @@ abstract class AbstractTopBarFragment : GenericFragment() {
) {
it.consume {
if (!slidingPane.isOpen) {
Log.i("$TAG Opening sliding pane")
Log.d("$TAG Opening sliding pane")
slidingPane.openPane()
}
}

View file

@ -62,9 +62,6 @@ class AddParticipantsFragment : GenericAddressPickerFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
viewModel = ViewModelProvider(this)[AddParticipantsViewModel::class.java]
postponeEnterTransition()

View file

@ -21,11 +21,9 @@ package org.linphone.ui.main.fragment
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import org.linphone.core.tools.Log
import org.linphone.ui.main.viewmodel.SharedMainViewModel
@ -37,71 +35,15 @@ abstract class GenericFragment : Fragment() {
protected lateinit var sharedViewModel: SharedMainViewModel
protected var isSlidingPaneChild: Boolean = false
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
Log.d("$TAG ${getFragmentRealClassName()} handleOnBackPressed")
try {
if (!goBack()) {
Log.d(
"$TAG ${getFragmentRealClassName()}'s goBack() method returned false, trying other things"
)
val navController = findNavController()
if (!navController.popBackStack()) {
Log.d("$TAG ${getFragmentRealClassName()} couldn't pop")
if (!navController.navigateUp()) {
Log.d(
"$TAG ${getFragmentRealClassName()} couldn't navigate up"
)
// Disable this callback & start a new back press event
isEnabled = false
try {
requireActivity().onBackPressedDispatcher.onBackPressed()
} catch (ise: IllegalStateException) {
Log.w(
"$TAG ${getFragmentRealClassName()}.goBack() can't go back: $ise"
)
}
}
}
}
} catch (ise: IllegalStateException) {
Log.e(
"$TAG ${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) {
val enabled = backPressedCallBackEnabled()
onBackPressedCallback.isEnabled = enabled
Log.d(
"$TAG ${getFragmentRealClassName()} Our own back press callback is ${if (enabled) "enabled" else "disabled"}"
)
}
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
onBackPressedCallback
)
}
override fun onDestroyView() {
super.onDestroyView()
onBackPressedCallback.remove()
}
private fun getFragmentRealClassName(): String {
protected fun getFragmentRealClassName(): String {
return this.javaClass.name
}
@ -111,24 +53,8 @@ abstract class GenericFragment : Fragment() {
requireActivity().onBackPressedDispatcher.onBackPressed()
} catch (ise: IllegalStateException) {
Log.w("$TAG ${getFragmentRealClassName()}.goBack() can't go back: $ise")
onBackPressedCallback.handleOnBackPressed()
return false
}
return true
}
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 (!isSlidingPaneChild) {
Log.d("$TAG ${getFragmentRealClassName()} isn't a sliding pane child, disable callback")
return false
}
val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false
Log.d(
"$TAG ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat"
)
return !isSlidingPaneFlat
}
}

View file

@ -0,0 +1,113 @@
/*
* 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.main.fragment
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.annotation.UiThread
import androidx.navigation.fragment.findNavController
import org.linphone.core.tools.Log
@UiThread
abstract class SlidingPaneChildFragment : GenericFragment() {
companion object {
private const val TAG = "[Sliding Pane Child Fragment]"
}
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
Log.d("$TAG ${getFragmentRealClassName()} handleOnBackPressed")
try {
if (!goBack()) {
Log.d(
"$TAG ${getFragmentRealClassName()}'s goBack() method returned false, trying other things"
)
val navController = findNavController()
if (!navController.popBackStack()) {
Log.d("$TAG ${getFragmentRealClassName()} couldn't pop")
if (!navController.navigateUp()) {
Log.d(
"$TAG ${getFragmentRealClassName()} couldn't navigate up"
)
// Disable this callback & start a new back press event
isEnabled = false
try {
requireActivity().onBackPressedDispatcher.onBackPressed()
} catch (ise: IllegalStateException) {
Log.w(
"$TAG ${getFragmentRealClassName()}.goBack() can't go back: $ise"
)
}
}
}
}
} catch (ise: IllegalStateException) {
Log.e(
"$TAG ${getFragmentRealClassName()}.handleOnBackPressed() Can't go back: $ise"
)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
onBackPressedCallback
)
sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { slideable ->
val enabled = backPressedCallBackEnabled(slideable)
onBackPressedCallback.isEnabled = enabled
Log.d(
"$TAG ${getFragmentRealClassName()} Our own back press callback is ${if (enabled) "enabled" else "disabled"}"
)
}
}
override fun onDestroyView() {
super.onDestroyView()
onBackPressedCallback.remove()
}
override fun goBack(): Boolean {
if (!super.goBack()) {
if (onBackPressedCallback.isEnabled) {
onBackPressedCallback.handleOnBackPressed()
return true
}
return false
}
return true
}
private fun backPressedCallBackEnabled(slideable: Boolean): 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.
Log.d(
"$TAG ${getFragmentRealClassName()} Sliding pane is ${if (slideable) "slideable" else "flat"}"
)
return slideable
}
}

View file

@ -40,7 +40,7 @@ import org.linphone.core.tools.Log
import org.linphone.databinding.HistoryContactFragmentBinding
import org.linphone.databinding.HistoryContactPopupMenuBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.ui.main.history.adapter.ContactHistoryListAdapter
import org.linphone.ui.main.history.model.ConfirmationDialogModel
import org.linphone.ui.main.history.viewmodel.ContactHistoryViewModel
@ -48,7 +48,7 @@ import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
@UiThread
class HistoryContactFragment : GenericFragment() {
class HistoryContactFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Call Fragment]"
}
@ -84,9 +84,6 @@ class HistoryContactFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
postponeEnterTransition()
super.onViewCreated(view, savedInstanceState)

View file

@ -42,12 +42,12 @@ import org.linphone.core.tools.Log
import org.linphone.databinding.MeetingFragmentBinding
import org.linphone.databinding.MeetingPopupMenuBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.ui.main.meetings.viewmodel.MeetingViewModel
import org.linphone.utils.Event
@UiThread
class MeetingFragment : GenericFragment() {
class MeetingFragment : SlidingPaneChildFragment() {
companion object {
private const val TAG = "[Meeting Fragment]"
}
@ -74,9 +74,6 @@ class MeetingFragment : GenericFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// This fragment is displayed in a SlidingPane "child" area
isSlidingPaneChild = true
postponeEnterTransition()
super.onViewCreated(view, savedInstanceState)

View file

@ -27,9 +27,7 @@ import org.linphone.core.tools.Log
@UiThread
class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneLayout) :
OnBackPressedCallback(
slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen
),
OnBackPressedCallback(false),
SlidingPaneLayout.PanelSlideListener {
companion object {
private const val TAG = "[Sliding Pane Back Pressed Callback]"
@ -37,12 +35,21 @@ class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneL
init {
slidingPaneLayout.addPanelSlideListener(this)
val enableCallback = slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen
Log.d(
"$TAG Sliding pane layout created, back press callback is ${if (enableCallback) "enabled" else "disabled"}"
)
isEnabled = enableCallback
}
override fun handleOnBackPressed() {
Log.d("$TAG handleOnBackPressed: hiding keyboard & closing pane")
slidingPaneLayout.hideKeyboard()
slidingPaneLayout.closePane()
if (slidingPaneLayout.isOpen) {
Log.d("$TAG handleOnBackPressed: hiding keyboard & closing pane")
slidingPaneLayout.hideKeyboard()
slidingPaneLayout.closePane()
} else {
Log.w("$TAG handleOnBackPressed: sliding pane is not open!")
}
}
override fun onPanelOpened(panel: View) {
@ -56,7 +63,6 @@ class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneL
}
override fun onPanelSlide(panel: View, slideOffset: Float) {
Log.d("$TAG Panel is sliding, enabling back press callback")
isEnabled = true
}
}