Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/unreleased/bugfixes/6845.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Improved `NavigationView` camera behavior to ignore keyboard insets.
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class NavigationView @JvmOverloads constructor(
backPressedComponent(activity),
scalebarPlaceholderCoordinator(binding.scalebarLayout),
maneuverCoordinator(binding.guidanceLayout),
infoPanelCoordinator(binding.infoPanelLayout, binding.guidelineBottom),
infoPanelCoordinator(binding),
actionButtonsCoordinator(binding.actionListLayout),
speedLimitCoordinator(binding.speedLimitLayout),
roadNameCoordinator(binding.roadNameLayout),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package com.mapbox.navigation.dropin.camera

import android.content.res.Configuration
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mapbox.maps.EdgeInsets
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.dropin.R
import com.mapbox.navigation.dropin.databinding.MapboxNavigationViewLayoutBinding
import com.mapbox.navigation.ui.app.internal.Store
import com.mapbox.navigation.dropin.navigationview.NavigationViewContext
import com.mapbox.navigation.ui.app.internal.camera.CameraAction
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
import com.mapbox.navigation.ui.base.lifecycle.UIComponent

internal class CameraLayoutObserver(
private val store: Store,
private val context: NavigationViewContext,
private val mapView: View,
private val binding: MapboxNavigationViewLayoutBinding,
) : UIComponent() {
Expand All @@ -21,63 +21,43 @@ internal class CameraLayoutObserver(
.getDimensionPixelSize(R.dimen.mapbox_camera_overview_padding_v).toDouble()
private val hPadding = binding.root.resources
.getDimensionPixelSize(R.dimen.mapbox_camera_overview_padding_h).toDouble()
private val vPaddingLandscape = binding.root.resources
.getDimensionPixelSize(R.dimen.mapbox_camera_overview_padding_landscape_v).toDouble()
private val hPaddingLandscape = binding.root.resources
.getDimensionPixelSize(R.dimen.mapbox_camera_overview_padding_landscape_h).toDouble()

private val layoutListener = View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
val edgeInsets = when (deviceOrientation()) {
Configuration.ORIENTATION_LANDSCAPE -> getLandscapePadding()
else -> getPortraitPadding()
}
store.dispatch(CameraAction.UpdatePadding(edgeInsets))
updateCameraPadding()
}

override fun onAttached(mapboxNavigation: MapboxNavigation) {
super.onAttached(mapboxNavigation)
binding.coordinatorLayout.addOnLayoutChangeListener(layoutListener)
context.behavior.infoPanelBehavior.bottomSheetState.observe { updateCameraPadding() }
}

override fun onDetached(mapboxNavigation: MapboxNavigation) {
super.onDetached(mapboxNavigation)
binding.coordinatorLayout.removeOnLayoutChangeListener(layoutListener)
}

private fun getPortraitPadding(): EdgeInsets {
val top = binding.guidanceLayout.height.toDouble()
val bottom = mapView.height.toDouble() - binding.roadNameLayout.top.toDouble()
return when (store.state.value.navigation) {
is NavigationState.DestinationPreview,
is NavigationState.FreeDrive,
is NavigationState.RoutePreview -> {
EdgeInsets(vPadding, hPadding, bottom, hPadding)
}
is NavigationState.ActiveNavigation,
is NavigationState.Arrival -> {
EdgeInsets(vPadding + top, hPadding, bottom, hPadding)
}
private fun updateCameraPadding() {
val edgeInsets = when (deviceOrientation()) {
Configuration.ORIENTATION_LANDSCAPE -> getLandscapePadding()
else -> getPortraitPadding()
}
context.store.dispatch(CameraAction.UpdatePadding(edgeInsets))
}

private fun getPortraitPadding(): EdgeInsets {
val top = binding.guidanceLayout.bottom
val bottom = mapView.height - binding.roadNameLayout.top
return EdgeInsets(vPadding + top, hPadding, vPadding + bottom, hPadding)
}

private fun getLandscapePadding(): EdgeInsets {
val bottom = mapView.height.toDouble() - binding.roadNameLayout.top.toDouble()
return when (store.state.value.navigation) {
is NavigationState.FreeDrive -> {
EdgeInsets(vPaddingLandscape, hPaddingLandscape, bottom, hPaddingLandscape)
}
is NavigationState.DestinationPreview,
is NavigationState.RoutePreview,
is NavigationState.ActiveNavigation,
is NavigationState.Arrival -> {
EdgeInsets(
vPaddingLandscape,
hPaddingLandscape + binding.infoPanelLayout.right.toDouble(),
bottom,
hPaddingLandscape
)
}
}
val bottomSheetState = context.behavior.infoPanelBehavior.bottomSheetState.value
val isBottomSheetVisible =
bottomSheetState != null && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
val bottomSheetWidth = if (isBottomSheetVisible) binding.infoPanelLayout.right else 0
val bottom = mapView.height - binding.roadNameLayout.top
return EdgeInsets(vPadding, hPadding + bottomSheetWidth, vPadding + bottom, hPadding)
}

private fun deviceOrientation() = binding.root.resources.configuration.orientation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package com.mapbox.navigation.dropin.infopanel
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.constraintlayout.widget.Guideline
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.dropin.databinding.MapboxNavigationViewLayoutBinding
import com.mapbox.navigation.dropin.navigationview.NavigationViewContext
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
import com.mapbox.navigation.ui.base.lifecycle.UIBinder
Expand All @@ -27,11 +27,10 @@ import kotlinx.coroutines.launch
*/
internal class InfoPanelCoordinator(
private val context: NavigationViewContext,
private val infoPanel: ViewGroup,
private val guidelineBottom: Guideline
) : UICoordinator<ViewGroup>(infoPanel) {
private val binding: MapboxNavigationViewLayoutBinding,
) : UICoordinator<ViewGroup>(binding.infoPanelLayout) {
private val store = context.store
private val behavior = BottomSheetBehavior.from(infoPanel)
private val behavior = BottomSheetBehavior.from(binding.infoPanelLayout)

private val updateGuideline = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
Expand All @@ -48,22 +47,23 @@ internal class InfoPanelCoordinator(
@OptIn(ExperimentalCoroutinesApi::class)
private val infoPanelTop = callbackFlow {
val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
trySend(infoPanel.top)
trySend(binding.infoPanelLayout.top)
}
val viewTreeObserver = infoPanel.viewTreeObserver
val viewTreeObserver = binding.infoPanelLayout.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
awaitClose { viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener) }
}.distinctUntilChanged()

init {
infoPanel.addOnLayoutChangeListener(FixBottomSheetLayoutWhenHidden(infoPanel, behavior))
binding.infoPanelLayout.addOnLayoutChangeListener(FixBottomSheetLayoutWhenHidden())
behavior.peekHeight = context.styles.infoPanelPeekHeight.value
behavior.hide()
}

override fun onAttached(mapboxNavigation: MapboxNavigation) {
super.onAttached(mapboxNavigation)

context.behavior.infoPanelBehavior.updateBottomSheetState(behavior.state)
behavior.addBottomSheetCallback(updateGuideline)
coroutineScope.launch {
bottomSheetState().collect { state ->
Expand All @@ -79,7 +79,10 @@ internal class InfoPanelCoordinator(
}
}
coroutineScope.launch {
context.systemBarsInsets.collect { updateGuidelinePosition(systemBarsInsets = it) }
context.systemBarsInsets.collect { insets ->
binding.container.setPadding(insets.left, insets.top, insets.right, insets.bottom)
updateGuidelinePosition(systemBarsInsets = insets)
}
}
coroutineScope.launch {
context.styles.infoPanelGuidelineMaxPosPercent.collect {
Expand Down Expand Up @@ -162,14 +165,14 @@ internal class InfoPanelCoordinator(

private fun updateGuidelinePosition(
systemBarsInsets: Insets = context.systemBarsInsets.value,
infoPanelTop: Int = infoPanel.top,
infoPanelTop: Int = binding.infoPanelLayout.top,
maxPosPercent: Float = context.styles.infoPanelGuidelineMaxPosPercent.value
) {
val parentHeight = (infoPanel.parent as ViewGroup).height
val parentHeight = binding.coordinatorLayout.height
val maxPos = (parentHeight * maxPosPercent).toInt() - systemBarsInsets.bottom
if (0 < maxPos) {
val pos = parentHeight - infoPanelTop - systemBarsInsets.bottom
guidelineBottom.setGuidelineEnd(pos.coerceIn(0, maxPos))
binding.guidelineBottom.setGuidelineEnd(pos.coerceIn(0, maxPos))
}
}

Expand All @@ -193,10 +196,7 @@ internal class InfoPanelCoordinator(
* An OnLayoutChangeListener that ensures the bottom sheet is always laid out at the bottom of
* the parent view when in STATE_HIDDEN.
*/
private class FixBottomSheetLayoutWhenHidden(
private val layout: ViewGroup,
private val behavior: BottomSheetBehavior<ViewGroup>
) : View.OnLayoutChangeListener {
private inner class FixBottomSheetLayoutWhenHidden : View.OnLayoutChangeListener {

override fun onLayoutChange(
v: View?,
Expand All @@ -210,7 +210,10 @@ internal class InfoPanelCoordinator(
oldBottom: Int
) {
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
ViewCompat.offsetTopAndBottom(layout, (layout.parent as? View)?.height ?: 0)
ViewCompat.offsetTopAndBottom(
binding.infoPanelLayout,
binding.coordinatorLayout.height,
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package com.mapbox.navigation.dropin.internal.extensions
import android.view.ViewGroup
import androidx.activity.ComponentActivity
import androidx.annotation.Px
import androidx.constraintlayout.widget.Guideline
import com.mapbox.maps.MapView
import com.mapbox.maps.plugin.locationcomponent.location
import com.mapbox.navigation.core.MapboxNavigation
Expand Down Expand Up @@ -269,9 +268,8 @@ internal fun NavigationViewContext.maneuverCoordinator(guidanceLayout: ViewGroup
ManeuverCoordinator(this, guidanceLayout)

internal fun NavigationViewContext.infoPanelCoordinator(
infoPanelLayout: ViewGroup,
guidelineBottom: Guideline
) = InfoPanelCoordinator(this, infoPanelLayout, guidelineBottom)
binding: MapboxNavigationViewLayoutBinding,
) = InfoPanelCoordinator(this, binding)

internal fun NavigationViewContext.actionButtonsCoordinator(actionListLayout: ViewGroup) =
ActionButtonsCoordinator(this, actionListLayout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class MapViewBinder : UIBinder {
val store = context.store
val navigationState = store.select { it.navigation }
return navigationListOf(
CameraLayoutObserver(store, mapView, navigationViewBinding),
CameraLayoutObserver(context, mapView, navigationViewBinding),
LocationComponent(context.locationProvider),
context.locationPuckComponent(mapView),
LogoAttributionComponent(mapView, context.systemBarsInsets),
Expand Down
Loading