@@ -20,9 +20,12 @@ import android.os.Bundle
20
20
import android.view.LayoutInflater
21
21
import android.view.View
22
22
import android.view.ViewGroup
23
+ import androidx.activity.OnBackPressedCallback
23
24
import androidx.fragment.app.activityViewModels
24
25
import androidx.navigation.NavController
26
+ import androidx.navigation.NavDestination
25
27
import androidx.navigation.fragment.NavHostFragment
28
+ import androidx.slidingpanelayout.widget.SlidingPaneLayout
26
29
import com.google.samples.apps.iosched.R
27
30
import com.google.samples.apps.iosched.ScheduleDetailNavGraphDirections
28
31
import com.google.samples.apps.iosched.databinding.FragmentScheduleTwoPaneBinding
@@ -47,6 +50,8 @@ class ScheduleTwoPaneFragment : MainNavigationFragment() {
47
50
private lateinit var listPaneNavController: NavController
48
51
private lateinit var detailPaneNavController: NavController
49
52
53
+ private val backPressHandler = BackPressHandler ()
54
+
50
55
override fun onCreateView (
51
56
inflater : LayoutInflater ,
52
57
container : ViewGroup ? ,
@@ -61,11 +66,20 @@ class ScheduleTwoPaneFragment : MainNavigationFragment() {
61
66
62
67
setupSnackbarManager(snackbarMessageManager, binding.snackbar)
63
68
69
+ binding.slidingPaneLayout.apply {
70
+ // Disable dragging the detail pane.
71
+ lockMode = SlidingPaneLayout .LOCK_MODE_LOCKED
72
+ // Listen for movement of the detail pane.
73
+ addPanelSlideListener(backPressHandler)
74
+ }
75
+
64
76
childFragmentManager.run {
65
77
listPaneNavController =
66
78
(findFragmentById(R .id.list_pane) as NavHostFragment ).navController
67
79
detailPaneNavController =
68
80
(findFragmentById(R .id.detail_pane) as NavHostFragment ).navController
81
+ listPaneNavController.addOnDestinationChangedListener(backPressHandler)
82
+ detailPaneNavController.addOnDestinationChangedListener(backPressHandler)
69
83
}
70
84
71
85
scheduleTwoPaneViewModel.navigateToSessionAction.observe(
@@ -74,6 +88,9 @@ class ScheduleTwoPaneFragment : MainNavigationFragment() {
74
88
detailPaneNavController.navigate(
75
89
ScheduleDetailNavGraphDirections .toSessionDetail(sessionId)
76
90
)
91
+ // On narrow screens, slide the detail pane over the list pane if it isn't already
92
+ // on top. If both panes are visible, this will have no effect.
93
+ binding.slidingPaneLayout.open()
77
94
}
78
95
)
79
96
@@ -83,11 +100,69 @@ class ScheduleTwoPaneFragment : MainNavigationFragment() {
83
100
openSignInDialog()
84
101
}
85
102
)
103
+
104
+ requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressHandler)
86
105
}
87
106
88
107
// TODO convert this to a dialog destination in the nav graph
89
108
private fun openSignInDialog () {
90
109
val dialog = SignInDialogFragment ()
91
110
dialog.show(requireActivity().supportFragmentManager, SignInDialogFragment .DIALOG_SIGN_IN )
92
111
}
112
+
113
+ /* * Handles back button press while this fragment is on screen. */
114
+ inner class BackPressHandler :
115
+ OnBackPressedCallback (false ),
116
+ SlidingPaneLayout .PanelSlideListener ,
117
+ NavController .OnDestinationChangedListener {
118
+
119
+ override fun handleOnBackPressed () {
120
+ // Back press can have three possible effects that we check for in order.
121
+ // 1. In the detail pane, go back from Speaker Detail to Session Detail.
122
+ val listDestination = listPaneNavController.currentDestination?.id
123
+ val detailDestination = detailPaneNavController.currentDestination?.id
124
+ var done = false
125
+ if (detailDestination == R .id.navigation_speaker_detail) {
126
+ done = detailPaneNavController.popBackStack()
127
+ }
128
+ // 2. On narrow screens, if the detail pane is in front, "go back" by sliding it away.
129
+ if (! done) {
130
+ done = binding.slidingPaneLayout.closePane()
131
+ }
132
+ // 3. Try to pop the list pane, e.g. back from Search to Schedule.
133
+ if (! done && listDestination == R .id.navigation_schedule_search) {
134
+ listPaneNavController.popBackStack()
135
+ }
136
+
137
+ syncEnabledState()
138
+ }
139
+
140
+ override fun onPanelSlide (panel : View , slideOffset : Float ) {
141
+ // noop
142
+ }
143
+
144
+ override fun onPanelOpened (panel : View ) {
145
+ syncEnabledState()
146
+ }
147
+
148
+ override fun onPanelClosed (panel : View ) {
149
+ syncEnabledState()
150
+ }
151
+
152
+ override fun onDestinationChanged (
153
+ controller : NavController ,
154
+ destination : NavDestination ,
155
+ arguments : Bundle ?
156
+ ) {
157
+ syncEnabledState()
158
+ }
159
+
160
+ private fun syncEnabledState () {
161
+ val listDestination = listPaneNavController.currentDestination?.id
162
+ val detailDestination = detailPaneNavController.currentDestination?.id
163
+ isEnabled = listDestination == R .id.navigation_schedule_search ||
164
+ detailDestination == R .id.navigation_speaker_detail ||
165
+ (binding.slidingPaneLayout.isSlideable && binding.slidingPaneLayout.isOpen)
166
+ }
167
+ }
93
168
}
0 commit comments