Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android route overview #345

Merged
merged 27 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7e77c8c
Initial checkpoint commit on Android route overview
ianthetechie Nov 4, 2024
ce94b79
Apply automatic changes
ianthetechie Nov 4, 2024
794d2de
Minor clean-up; version updates
ianthetechie Nov 5, 2024
5063fc7
Merge branch 'main' into route-overview-android
ianthetechie Nov 5, 2024
060e839
Merge branch 'main' into route-overview-android
ianthetechie Nov 6, 2024
6ab8f7e
Cleanup: by delegation
ianthetechie Nov 6, 2024
584bb74
State -> raw value
ianthetechie Nov 7, 2024
8318660
Move isNavigating into state so we don't have to pass a full viewmode…
ianthetechie Nov 7, 2024
bc096e3
Make onMapReadyCallback optional rather than a capturing default closure
ianthetechie Nov 7, 2024
3e99c47
Auto-adjust most insets
ianthetechie Nov 7, 2024
dd73e99
Support horizontal padding
ianthetechie Nov 7, 2024
8fc0ce8
Snapshots; hide mute button and move recenter to bottom leading corner
ianthetechie Nov 7, 2024
cf15dd8
Refactor; the layering makes sense now!
ianthetechie Nov 7, 2024
742a5ca
Undo experimental change
ianthetechie Nov 7, 2024
cee5ffd
Minor tweaks; update macOS CI to Sonoma
ianthetechie Nov 7, 2024
aee4461
Switch to iOS 18 simulator for snapshots; update nanopixel-perfect sn…
ianthetechie Nov 7, 2024
6819739
Ditch the view model swapping!
ianthetechie Nov 8, 2024
c12f3c8
Apply automatic changes
ianthetechie Nov 8, 2024
6545310
Remove old comments
ianthetechie Nov 8, 2024
06843fe
Fix imports
ianthetechie Nov 8, 2024
1d8d8db
Allow stopping demo VM location updates
ianthetechie Nov 10, 2024
df63897
Move view model to AppModule to better reflect a typical DI use case
ianthetechie Nov 11, 2024
5c03f41
Apply automatic changes
ianthetechie Nov 11, 2024
cc83bbd
Hide zoom controls as well when showing the recenter button
ianthetechie Nov 11, 2024
28dd2cc
Update snapshots
ianthetechie Nov 11, 2024
9cfff25
Freshen docs
ianthetechie Nov 11, 2024
98effcf
Release prep: bump version
ianthetechie Nov 11, 2024
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
2 changes: 1 addition & 1 deletion android/composeui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {

android {
namespace 'com.stadiamaps.ferrostar.composeui'
compileSdk 34
compileSdk 35

defaultConfig {
minSdk 25
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stadiamaps.ferrostar.composeui.config

import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp

sealed class CameraControlState {
data object Hidden : CameraControlState()

data class ShowRecenter(val updateCamera: () -> Unit) : CameraControlState()

data class ShowRouteOverview(val updateCamera: () -> Unit) : CameraControlState()
}

data class VisualNavigationViewConfig(
var showMute: Boolean = false,
var showZoom: Boolean = false,
var buttonSize: DpSize = DpSize(56.dp, 56.dp)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was previously an informal constant that was copied everywhere. This at least gives it a place to live, and opens the path for button scaling enhancements if needed.

) {
companion object {
fun Default() = VisualNavigationViewConfig(showMute = true, showZoom = true)
}
}

/** Enables the mute button in the navigation view. */
fun VisualNavigationViewConfig.useMuteButton(): VisualNavigationViewConfig {
showMute = true
return this
}

/** Enables the zoom button in the navigation view. */
fun VisualNavigationViewConfig.useZoomButton(): VisualNavigationViewConfig {
showZoom = true
return this
}

/** Changes the size of navigation buttons. */
fun VisualNavigationViewConfig.buttonSize(size: DpSize): VisualNavigationViewConfig {
buttonSize = size
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import uniffi.ferrostar.VisualInstructionContent
fun InstructionsView(
instructions: VisualInstruction,
distanceToNextManeuver: Double?,
modifier: Modifier = Modifier,
distanceFormatter: DistanceFormatter = LocalizedDistanceFormatter(),
theme: InstructionRowTheme = DefaultInstructionRowTheme,
remainingSteps: List<RouteStep>? = null,
Expand All @@ -69,7 +70,8 @@ fun InstructionsView(

Box(
modifier =
Modifier.fillMaxWidth()
modifier
.fillMaxWidth()
.heightIn(max = screenHeight)
.animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessHigh))
.background(theme.backgroundColor, RoundedCornerShape(10.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package com.stadiamaps.ferrostar.composeui.views.controls

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
Expand All @@ -18,6 +17,7 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.stadiamaps.ferrostar.composeui.R

Expand All @@ -32,13 +32,14 @@ import com.stadiamaps.ferrostar.composeui.R
@Composable
fun NavigationUIButton(
onClick: () -> Unit,
buttonSize: DpSize,
containerColor: Color = FloatingActionButtonDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
content: @Composable () -> Unit
) {
FloatingActionButton(
onClick,
modifier = Modifier.width(56.dp).height(56.dp).shadow(6.dp, shape = CircleShape),
modifier = Modifier.size(buttonSize).shadow(6.dp, shape = CircleShape),
shape = CircleShape,
containerColor,
contentColor) {
Expand All @@ -50,7 +51,7 @@ fun NavigationUIButton(
@Composable
fun NavigationUIButtonPreview() {
Box(Modifier.background(Color.LightGray).padding(16.dp)) {
NavigationUIButton({}) {
NavigationUIButton({}, DpSize(56.dp, 56.dp)) {
Icon(Icons.Filled.Close, contentDescription = stringResource(id = R.string.end_navigation))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
Expand All @@ -22,11 +23,13 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.stadiamaps.ferrostar.composeui.R

@Composable
fun NavigationUIZoomButton(
buttonSize: DpSize,
onClickZoomIn: () -> Unit,
onClickZoomOut: () -> Unit,
containerColor: Color = FloatingActionButtonDefaults.containerColor,
Expand All @@ -38,7 +41,7 @@ fun NavigationUIZoomButton(
Column(modifier = Modifier.shadow(6.dp, shape = RoundedCornerShape(50))) {
FloatingActionButton(
onClick = onClickZoomIn,
modifier = Modifier.height(56.dp).width(56.dp),
modifier = Modifier.size(buttonSize),
shape = RoundedCornerShape(topStartPercent = 50, topEndPercent = 50),
containerColor = containerColor,
contentColor = contentColor,
Expand All @@ -48,13 +51,13 @@ fun NavigationUIZoomButton(
contentDescription = stringResource(id = R.string.zoom_in))
}

Box(modifier = Modifier.height(1.dp).width(56.dp)) {
Box(modifier = Modifier.height(1.dp).width(buttonSize.width)) {
HorizontalDivider(color = MaterialTheme.colorScheme.surfaceVariant)
}

FloatingActionButton(
onClick = onClickZoomOut,
modifier = Modifier.height(56.dp).width(56.dp),
modifier = Modifier.size(buttonSize),
shape = RoundedCornerShape(bottomStartPercent = 50, bottomEndPercent = 50),
containerColor = containerColor,
contentColor = contentColor,
Expand All @@ -69,5 +72,8 @@ fun NavigationUIZoomButton(
@Preview
@Composable
fun NavigationUIZoomButtonPreview() {
Box(Modifier.background(Color.LightGray).padding(16.dp)) { NavigationUIZoomButton({}, {}) }
Box(Modifier.background(Color.LightGray).padding(16.dp)) {
NavigationUIZoomButton(
buttonSize = DpSize(56.dp, 56.dp), onClickZoomIn = {}, onClickZoomOut = {})
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package com.stadiamaps.ferrostar.composeui.views.gridviews

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.VolumeOff
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Navigation
import androidx.compose.material.icons.filled.VolumeOff
import androidx.compose.material.icons.filled.Route
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.stadiamaps.ferrostar.composeui.R
import com.stadiamaps.ferrostar.composeui.config.CameraControlState
import com.stadiamaps.ferrostar.composeui.views.controls.NavigationUIButton
import com.stadiamaps.ferrostar.composeui.views.controls.NavigationUIZoomButton

Expand All @@ -25,11 +30,11 @@ fun NavigatingInnerGridView(
showMute: Boolean = true,
isMuted: Boolean?,
onClickMute: () -> Unit = {},
buttonSize: DpSize,
cameraControlState: CameraControlState = CameraControlState.Hidden,
showZoom: Boolean = true,
onClickZoomIn: () -> Unit = {},
onClickZoomOut: () -> Unit = {},
showCentering: Boolean = true,
onClickCenter: () -> Unit = {},
topCenter: @Composable () -> Unit = { Spacer(Modifier.width(12.dp)) },
centerStart: @Composable () -> Unit = { Spacer(Modifier.width(12.dp)) },
bottomEnd: @Composable () -> Unit = { Spacer(Modifier.width(12.dp)) }
Expand All @@ -41,29 +46,52 @@ fun NavigatingInnerGridView(
},
topCenter = topCenter,
topEnd = {
if (showMute && isMuted != null) {
NavigationUIButton(onClick = onClickMute) {
if (isMuted) {
Icon(
Icons.AutoMirrored.Filled.VolumeOff,
contentDescription = stringResource(id = R.string.unmute_description))
} else {
Icon(
Icons.AutoMirrored.Filled.VolumeUp,
contentDescription = stringResource(id = R.string.mute_description))
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
when (cameraControlState) {
CameraControlState.Hidden -> {
// Nothing to draw here :)
}
is CameraControlState.ShowRecenter -> {
// We decided to put this in the bottom corner for now
}
is CameraControlState.ShowRouteOverview -> {
NavigationUIButton(
onClick = cameraControlState.updateCamera, buttonSize = buttonSize) {
Icon(
Icons.Default.Route,
modifier = Modifier.rotate(90.0f),
contentDescription = stringResource(id = R.string.route_overview))
}
}
}

// NOTE: Some controls hidden when the camera is not following the user
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if my memory of this is incorrect, but I believe your request on the call was to hide the mute button but leave zoom (if present), progress, and instructions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct (as per matching inspired by Apple Maps):

if (showMute &&
isMuted != null &&
cameraControlState !is CameraControlState.ShowRecenter) {
NavigationUIButton(onClick = onClickMute, buttonSize = buttonSize) {
if (isMuted) {
Icon(
Icons.AutoMirrored.Filled.VolumeOff,
contentDescription = stringResource(id = R.string.unmute_description))
} else {
Icon(
Icons.AutoMirrored.Filled.VolumeUp,
contentDescription = stringResource(id = R.string.mute_description))
}
}
}
}
},
centerStart = centerStart,
centerEnd = {
if (showZoom) {
NavigationUIZoomButton(onClickZoomIn, onClickZoomOut)
NavigationUIZoomButton(buttonSize, onClickZoomIn, onClickZoomOut)
}
},
bottomStart = {
if (showCentering) {
NavigationUIButton(onClick = onClickCenter) {
if (cameraControlState is CameraControlState.ShowRecenter) {
NavigationUIButton(onClick = cameraControlState.updateCamera, buttonSize = buttonSize) {
Icon(
Icons.Filled.Navigation,
contentDescription = stringResource(id = R.string.recenter))
Expand All @@ -75,14 +103,56 @@ fun NavigatingInnerGridView(

@Preview(device = Devices.PIXEL_5)
@Composable
fun NavigatingInnerGridViewPreview() {
NavigatingInnerGridView(modifier = Modifier.fillMaxSize(), isMuted = false)
fun NavigatingInnerGridViewNonTrackingPreview() {
NavigatingInnerGridView(
modifier = Modifier.fillMaxSize(),
isMuted = false,
buttonSize = DpSize(56.dp, 56.dp),
cameraControlState =
CameraControlState.ShowRecenter {
// Do nothing
})
}

@Preview(device = Devices.PIXEL_5)
@Composable
fun NavigatingInnerGridViewTrackingPreview() {
NavigatingInnerGridView(
modifier = Modifier.fillMaxSize(),
isMuted = false,
buttonSize = DpSize(56.dp, 56.dp),
cameraControlState =
CameraControlState.ShowRouteOverview {
// Do nothing
})
}

@Preview(
device =
"spec:height=411dp,width=891dp,dpi=420,isRound=false,chinSize=0dp,orientation=landscape")
@Composable
fun NavigatingInnerGridViewLandscapeNonTrackingPreview() {
NavigatingInnerGridView(
modifier = Modifier.fillMaxSize(),
isMuted = true,
buttonSize = DpSize(56.dp, 56.dp),
cameraControlState =
CameraControlState.ShowRecenter {
// Do nothing
})
}

@Preview(
device =
"spec:width=411dp,height=891dp,dpi=420,isRound=false,chinSize=0dp,orientation=landscape")
"spec:height=411dp,width=891dp,dpi=420,isRound=false,chinSize=0dp,orientation=landscape")
@Composable
fun NavigatingInnerGridViewLandscapePreview() {
NavigatingInnerGridView(modifier = Modifier.fillMaxSize(), isMuted = true)
fun NavigatingInnerGridViewLandscapeTrackingPreview() {
NavigatingInnerGridView(
modifier = Modifier.fillMaxSize(),
isMuted = true,
buttonSize = DpSize(56.dp, 56.dp),
cameraControlState =
CameraControlState.ShowRouteOverview {
// Do nothing
})
}
1 change: 1 addition & 0 deletions android/composeui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
<string name="preparing">Preparing...</string>
<string name="arrived_title">Arrived</string>
<string name="arrived_description">You have arrived at your destination.</string>
<string name="route_overview">Route Overview</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.stadiamaps.ferrostar.views

import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewLandscapePreview
import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewPreview
import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewLandscapeNonTrackingPreview
import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewLandscapeTrackingPreview
import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewNonTrackingPreview
import com.stadiamaps.ferrostar.composeui.views.gridviews.NavigatingInnerGridViewTrackingPreview
import com.stadiamaps.ferrostar.support.paparazziDefault
import com.stadiamaps.ferrostar.support.withSnapshotBackground
import org.junit.Rule
Expand All @@ -12,12 +14,26 @@ class NavigatingInnerGridViewTest {
@get:Rule val paparazzi = paparazziDefault()

@Test
fun testNavigatingInnerGridView() {
paparazzi.snapshot { withSnapshotBackground { NavigatingInnerGridViewPreview() } }
fun testNavigatingInnerGridViewTracking() {
paparazzi.snapshot { withSnapshotBackground { NavigatingInnerGridViewTrackingPreview() } }
}

@Test
fun testNavigatingInnerGridViewLandscape() {
paparazzi.snapshot { withSnapshotBackground { NavigatingInnerGridViewLandscapePreview() } }
fun testNavigatingInnerGridViewNonTracking() {
paparazzi.snapshot { withSnapshotBackground { NavigatingInnerGridViewNonTrackingPreview() } }
}

@Test
fun testNavigatingInnerGridViewTrackingLandscape() {
paparazzi.snapshot {
withSnapshotBackground { NavigatingInnerGridViewLandscapeTrackingPreview() }
}
}

@Test
fun testNavigatingInnerGridViewNonTrackingLandscape() {
paparazzi.snapshot {
withSnapshotBackground { NavigatingInnerGridViewLandscapeNonTrackingPreview() }
}
}
}
Loading
Loading