Skip to content

Commit e44115b

Browse files
authored
use OnBackPressedDispatcher for back press handling (#6483)
1 parent 1424d80 commit e44115b

File tree

15 files changed

+326
-439
lines changed

15 files changed

+326
-439
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Mapbox welcomes participation and contributions from everyone.
88
- Improved precision of congestion visualization and independent leg styling transitions, especially for long routes. [#6476](https://github.com/mapbox/mapbox-navigation-android/pull/6476)
99
- Fixed an issue with congestion visualization on the route line that occurred during route refreshes. [#6476](https://github.com/mapbox/mapbox-navigation-android/pull/6476)
1010
- Made `MapViewBinder#shouldLoadMapStyle` false by default. [#6485](https://github.com/mapbox/mapbox-navigation-android/pull/6485)
11+
- Fixed an issue that prevented any registered `OnBackPressedCallback`s to fire when `NavigationView` is attached. [#6483](https://github.com/mapbox/mapbox-navigation-android/pull/6483)
12+
- Removed `NavigationViewListener.onBackPressed()`. You can register your own [OnBackPressedCallback](https://developer.android.com/reference/androidx/activity/OnBackPressedCallback) to intercept any `NavigationView` BACK press events. [#6483](https://github.com/mapbox/mapbox-navigation-android/pull/6483)
1113

1214
## Mapbox Navigation SDK 2.9.0-beta.2 - 14 October, 2022
1315
### Changelog

libnavui-dropin/api/current.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ package com.mapbox.navigation.dropin.navigationview {
355355
method public void onActiveNavigation();
356356
method public void onArrival();
357357
method public void onAudioGuidanceStateChanged(boolean muted);
358-
method public boolean onBackPressed();
359358
method public void onCameraPaddingChanged(com.mapbox.maps.EdgeInsets padding);
360359
method public void onDestinationChanged(com.mapbox.geojson.Point? destination);
361360
method public void onDestinationPreview();

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/NavigationView.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
2424
import com.mapbox.navigation.core.replay.MapboxReplayer
2525
import com.mapbox.navigation.dropin.actionbutton.ActionButtonsCoordinator
2626
import com.mapbox.navigation.dropin.analytics.AnalyticsComponent
27-
import com.mapbox.navigation.dropin.backpress.OnKeyListenerComponent
27+
import com.mapbox.navigation.dropin.backpress.BackPressedComponent
2828
import com.mapbox.navigation.dropin.databinding.MapboxNavigationViewLayoutBinding
2929
import com.mapbox.navigation.dropin.infopanel.InfoPanelCoordinator
3030
import com.mapbox.navigation.dropin.internal.extensions.navigationViewAccessToken
31-
import com.mapbox.navigation.dropin.internal.extensions.toComponentActivityRef
31+
import com.mapbox.navigation.dropin.internal.extensions.toComponentActivity
3232
import com.mapbox.navigation.dropin.internal.extensions.toViewModelStoreOwner
3333
import com.mapbox.navigation.dropin.maneuver.ManeuverCoordinator
3434
import com.mapbox.navigation.dropin.map.MapLayoutCoordinator
@@ -124,15 +124,16 @@ class NavigationView @JvmOverloads constructor(
124124

125125
MapboxNavigationApp.attach(this)
126126

127+
val componentActivity = context.toComponentActivity()
127128
attachCreated(
128129
AnalyticsComponent(),
129-
LocationPermissionComponent(context.toComponentActivityRef(), navigationContext.store),
130+
LocationPermissionComponent(componentActivity, navigationContext.store),
130131
TripSessionComponent(lifecycle, navigationContext.store),
131132
MapLayoutCoordinator(navigationContext, binding),
132-
OnKeyListenerComponent(
133+
BackPressedComponent(
134+
componentActivity.onBackPressedDispatcher,
133135
navigationContext.store,
134-
this,
135-
navigationContext.listenerRegistry
136+
lifecycleOwner = this,
136137
),
137138
ScalebarPlaceholderCoordinator(navigationContext, binding.scalebarLayout),
138139
ManeuverCoordinator(navigationContext, binding.guidanceLayout),
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.mapbox.navigation.dropin.backpress
2+
3+
import androidx.activity.OnBackPressedCallback
4+
import androidx.activity.OnBackPressedDispatcher
5+
import androidx.lifecycle.Lifecycle
6+
import androidx.lifecycle.LifecycleOwner
7+
import androidx.lifecycle.repeatOnLifecycle
8+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
9+
import com.mapbox.navigation.core.MapboxNavigation
10+
import com.mapbox.navigation.dropin.NavigationView
11+
import com.mapbox.navigation.ui.app.internal.Store
12+
import com.mapbox.navigation.ui.app.internal.destination.DestinationAction
13+
import com.mapbox.navigation.ui.app.internal.endNavigation
14+
import com.mapbox.navigation.ui.app.internal.extension.dispatch
15+
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
16+
import com.mapbox.navigation.ui.app.internal.navigation.NavigationStateAction
17+
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewAction
18+
import com.mapbox.navigation.ui.app.internal.routefetch.RoutesAction
19+
import com.mapbox.navigation.ui.base.lifecycle.UIComponent
20+
import com.mapbox.navigation.utils.internal.logE
21+
import kotlinx.coroutines.flow.collect
22+
import kotlinx.coroutines.flow.onCompletion
23+
import kotlinx.coroutines.launch
24+
25+
/**
26+
* Back pressed callback for the [NavigationView].
27+
*
28+
* On back pressed will update the navigation state.
29+
*
30+
* (FreeDrive) <- (DestinationPreview) <- (RoutePreview) <- (ActiveNavigation)
31+
* <- (Arrival)
32+
*/
33+
@ExperimentalPreviewMapboxNavigationAPI
34+
internal class BackPressedComponent(
35+
onBackPressedDispatcher: OnBackPressedDispatcher,
36+
private val store: Store,
37+
private val lifecycleOwner: LifecycleOwner,
38+
) : UIComponent() {
39+
40+
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
41+
42+
override fun handleOnBackPressed() {
43+
when (store.state.value.navigation) {
44+
NavigationState.FreeDrive -> {
45+
logE(
46+
msg = "The back pressed callback should be disabled in Free Drive state",
47+
category = "BackPressedComponent",
48+
)
49+
}
50+
NavigationState.DestinationPreview -> {
51+
store.dispatch(DestinationAction.SetDestination(null))
52+
store.dispatch(NavigationStateAction.Update(NavigationState.FreeDrive))
53+
}
54+
NavigationState.RoutePreview -> {
55+
store.dispatch(RoutePreviewAction.Ready(emptyList()))
56+
store.dispatch(NavigationStateAction.Update(NavigationState.DestinationPreview))
57+
}
58+
NavigationState.ActiveNavigation -> {
59+
store.dispatch(RoutesAction.SetRoutes(emptyList()))
60+
store.dispatch(NavigationStateAction.Update(NavigationState.RoutePreview))
61+
}
62+
NavigationState.Arrival -> {
63+
store.dispatch(endNavigation())
64+
}
65+
}
66+
}
67+
}
68+
69+
// Add the callback before onAttached so downstream developers can register higher priority
70+
// back press callbacks. This means the component has to be registered via attachCreated.
71+
init {
72+
onBackPressedDispatcher.addCallback(onBackPressedCallback)
73+
}
74+
75+
override fun onAttached(mapboxNavigation: MapboxNavigation) {
76+
super.onAttached(mapboxNavigation)
77+
coroutineScope.launch {
78+
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
79+
store.select { it.navigation != NavigationState.FreeDrive }
80+
.onCompletion { onBackPressedCallback.isEnabled = false }
81+
.collect { onBackPressedCallback.isEnabled = it }
82+
}
83+
}
84+
}
85+
86+
override fun onDetached(mapboxNavigation: MapboxNavigation) {
87+
super.onDetached(mapboxNavigation)
88+
onBackPressedCallback.remove()
89+
}
90+
}

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/backpress/OnKeyListenerComponent.kt

Lines changed: 0 additions & 82 deletions
This file was deleted.

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/internal/extensions/ContextEx.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import android.content.Context
77
import android.content.ContextWrapper
88
import androidx.activity.ComponentActivity
99
import androidx.lifecycle.ViewModelStoreOwner
10-
import com.mapbox.navigation.utils.internal.logW
11-
import java.lang.ref.WeakReference
1210

1311
internal tailrec fun Context.recursiveUnwrap(): Context =
1412
if (this !is Activity && this is ContextWrapper) {
@@ -25,10 +23,10 @@ internal fun Context.toViewModelStoreOwner(): ViewModelStoreOwner {
2523
return viewModelStoreOwner
2624
}
2725

28-
internal fun Context.toComponentActivityRef(): WeakReference<ComponentActivity>? {
26+
internal fun Context.toComponentActivity(): ComponentActivity {
2927
val componentActivity = this.recursiveUnwrap() as? ComponentActivity
30-
if (componentActivity == null) {
31-
logW("Unable to find ComponentActivity to request location permissions")
28+
checkNotNull(componentActivity) {
29+
"Please ensure that the hosting Context is a valid ComponentActivity"
3230
}
33-
return componentActivity?.let { WeakReference(it) }
31+
return componentActivity
3432
}

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/navigationview/NavigationViewListener.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,6 @@ abstract class NavigationViewListener {
141141
*/
142142
open fun onInfoPanelSlide(slideOffset: Float) = Unit
143143

144-
/**
145-
* Called when hardware back button has been pressed.
146-
*
147-
* @return True if the listener has consumed the event, false otherwise.
148-
*/
149-
open fun onBackPressed(): Boolean = false
150-
151144
/**
152145
* Called when maneuver view has been expanded.
153146
*/

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/navigationview/NavigationViewListenerRegistry.kt

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.mapbox.navigation.dropin.navigationview
22

3-
import android.view.KeyEvent
4-
import android.view.View
53
import com.google.android.material.bottomsheet.BottomSheetBehavior
64
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
75
import com.mapbox.navigation.dropin.infopanel.InfoPanelBehavior
@@ -27,7 +25,7 @@ internal class NavigationViewListenerRegistry(
2725
private val infoPanelSubscriber: InfoPanelBehavior,
2826
private val mapClickSubscriber: MapClickBehavior,
2927
private val coroutineScope: CoroutineScope
30-
) : View.OnKeyListener {
28+
) {
3129
private var listeners = mutableMapOf<NavigationViewListener, Job>()
3230

3331
fun registerListener(listener: NavigationViewListener) {
@@ -39,19 +37,6 @@ internal class NavigationViewListenerRegistry(
3937
listeners.remove(listener)?.cancel()
4038
}
4139

42-
//region View.OnKeyListener
43-
44-
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
45-
if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
46-
listeners.forEach { (listener, _) ->
47-
if (listener.onBackPressed()) return true
48-
}
49-
}
50-
return false
51-
}
52-
53-
//endregion
54-
5540
private fun connectListener(listener: NavigationViewListener): Job {
5641
return coroutineScope.launch {
5742
launch {

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/permission/LocationPermissionComponent.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ import java.lang.ref.WeakReference
2020
/**
2121
* Default experience for location permissions.
2222
*
23-
* @param componentActivityRef used for requesting location permissions
23+
* @param componentActivity used for requesting location permissions
2424
* @param tripSessionStarterViewModel used to notify when location permissions are granted
2525
*/
2626
@ExperimentalPreviewMapboxNavigationAPI
2727
internal class LocationPermissionComponent(
28-
private val componentActivityRef: WeakReference<ComponentActivity>?,
28+
componentActivity: ComponentActivity,
2929
private val store: Store
3030
) : UIComponent() {
3131

32+
private val componentActivityRef = WeakReference(componentActivity)
33+
3234
private val callback = ActivityResultCallback { permissions: Map<String, Boolean> ->
3335
val accessFineLocation = permissions[FINE_LOCATION_PERMISSIONS]
3436
?: false
@@ -39,7 +41,7 @@ internal class LocationPermissionComponent(
3941
}
4042

4143
private val launcher = try {
42-
componentActivityRef?.get()?.registerForActivityResult(
44+
componentActivityRef.get()?.registerForActivityResult(
4345
ActivityResultContracts.RequestMultiplePermissions(),
4446
callback
4547
)
@@ -82,7 +84,7 @@ internal class LocationPermissionComponent(
8284
*/
8385
private fun notifyGrantedOnForegrounded(applicationContext: Context) {
8486
coroutineScope.launch {
85-
componentActivityRef?.get()?.repeatOnLifecycle(Lifecycle.State.STARTED) {
87+
componentActivityRef.get()?.repeatOnLifecycle(Lifecycle.State.STARTED) {
8688
if (!store.state.value.tripSession.isLocationPermissionGranted) {
8789
val isGranted = PermissionsManager.areLocationPermissionsGranted(
8890
applicationContext

0 commit comments

Comments
 (0)