Skip to content
Draft
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
9 changes: 8 additions & 1 deletion Maps3DSamples/advanced/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@
<activity
android:name=".scenarios.ScenariosActivity"
android:exported="true"
/>
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="scenario" />
</intent-filter>
</activity>
<activity
android:name=".route.RouteSampleActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,20 @@ import com.example.advancedmaps3dsamples.scenarios.ScenariosActivity
import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme
import dagger.hilt.android.AndroidEntryPoint

data class MapSample(@StringRes val label: Int, val clazz: Class<*>)
data class MapSample(
@StringRes val label: Int,
val clazz: Class<*>,
val scenario: String? = null,
)

private val samples =
listOf(
MapSample(R.string.map_sample_basic, BasicComposeMapActivity::class.java),
MapSample(R.string.scenarios_seattle_cinematic_reveal, ScenariosActivity::class.java, "seattle_cinematic_reveal"),
MapSample(R.string.scenarios_uk_shard_tower_bridge, ScenariosActivity::class.java, "uk_shard_to_tower_bridge"),
MapSample(R.string.scenarios_brazil_copacabana_ipanema, ScenariosActivity::class.java, "brazil_copacabana_to_ipanema"),
MapSample(R.string.scenarios_scenic_world_tour, ScenariosActivity::class.java, "scenic_world_tour"),
MapSample(R.string.map_sample_scenarios, ScenariosActivity::class.java),
MapSample(R.string.map_sample_basic, BasicComposeMapActivity::class.java),
MapSample(R.string.map_sample_route, RouteSampleActivity::class.java),
)

Expand Down Expand Up @@ -83,7 +91,7 @@ class MainActivity : ComponentActivity() {
for (sample in samples) {
Sample(
sample = sample,
onClick = { launchActivity(sample.clazz) },
onClick = { launchActivity(sample) },
modifier = Modifier.fillMaxWidth(),
)
}
Expand All @@ -94,12 +102,15 @@ class MainActivity : ComponentActivity() {

}

private fun launchActivity(clazz: Class<*>) {
private fun launchActivity(sample: MapSample) {
try {
startActivity(Intent(this, clazz))
val intent = Intent(this, sample.clazz).apply {
sample.scenario?.let { putExtra("scenario", it) }
}
startActivity(intent)
} catch (e: ClassNotFoundException) {
Log.e("SamplesMenu", "Activity not found: $clazz", e)
Toast.makeText(this, "Activity not found: $clazz", Toast.LENGTH_SHORT).show()
Log.e("SamplesMenu", "Activity not found: ${sample.clazz}", e)
Toast.makeText(this, "Activity not found: ${sample.clazz}", Toast.LENGTH_SHORT).show()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ abstract class Map3dViewModel : ViewModel() {
* Consumers should use the `mapReady` flow to react to the availability of the map.
*/
private var _googleMap3D = MutableStateFlow<GoogleMap3D?>(null)
val googleMap3D = _googleMap3D.asStateFlow()

private val _cameraRestriction = MutableStateFlow<CameraRestriction?>(null)
val cameraRestriction = _cameraRestriction.asStateFlow()
Expand Down Expand Up @@ -402,8 +403,7 @@ abstract class Map3dViewModel : ViewModel() {
}

suspend fun awaitCameraUpdate(cameraUpdate: CameraUpdate) {
_googleMap3D.value?.let { controller ->
com.example.advancedmaps3dsamples.utils.awaitCameraUpdate(controller, cameraUpdate)
}
val controller = _googleMap3D.filterNotNull().first()
com.example.advancedmaps3dsamples.utils.awaitCameraUpdate(controller, cameraUpdate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ package com.example.advancedmaps3dsamples.scenarios

import com.google.android.gms.maps3d.model.FlyAroundOptions
import com.google.android.gms.maps3d.model.FlyToOptions
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

sealed interface AnimationStep {
suspend operator fun invoke(viewModel: ScenariosViewModel)
Expand Down Expand Up @@ -46,8 +48,33 @@ data class FlyToStep(val flyToOptions: FlyToOptions) : AnimationStep {
}
}

data class FlyAroundStep(val flyAroundOptions: FlyAroundOptions) : AnimationStep {
data class FlyAroundStep(
val flyAroundOptions: FlyAroundOptions,
val switchMapMode: Int? = null,
val switchDelayMillis: Long = 0L,
) : AnimationStep {
override suspend operator fun invoke(viewModel: ScenariosViewModel) {
viewModel.awaitFlyAround(flyAroundOptions)
if (switchMapMode != null && switchDelayMillis > 0) {
coroutineScope {
val switchJob = launch {
delay(switchDelayMillis)
android.util.Log.i("FlyAroundStep", "Switching map mode to $switchMapMode after ${switchDelayMillis}ms")
viewModel.setMapMode(switchMapMode)
}
try {
viewModel.awaitFlyAround(flyAroundOptions)
} finally {
switchJob.cancel()
}
}
} else {
viewModel.awaitFlyAround(flyAroundOptions)
}
}
}

data class SetMapModeStep(val mapMode: Int) : AnimationStep {
override suspend operator fun invoke(viewModel: ScenariosViewModel) {
viewModel.setMapMode(mapMode)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ data class Scenario(
val models: List<ModelOptions> = emptyList(), // Add models property
val polylines: List<PolylineOptions> = emptyList(),
val polygons: List<PolygonOptions> = emptyList(),
val loop: Boolean = false,
) {
@Composable fun getTitle() = stringResource(titleId)

Expand Down Expand Up @@ -70,6 +71,7 @@ fun createScenario(
models: String = "",
polylines: String = "",
polygon: String? = null,
loop: Boolean = false,
): Scenario {
return Scenario(
name = name,
Expand All @@ -80,6 +82,7 @@ fun createScenario(
models = models.toModels(),
polylines = polylines.toPolylines(),
polygons = polygon?.toPolygons() ?: emptyList(),
loop = loop,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,128 @@ val denverPolygon = """

val scenarios =
listOf(
createScenario(
name = "seattle_cinematic_reveal",
titleId = R.string.scenarios_seattle_cinematic_reveal,
initialState =
"mode=roadmap;camera=lat=47.6100,lng=-122.3950,alt=1200.0,hdg=55.0,tilt=60.0,range=7500.0",
animationString =
"waitUntilTheMapIsSteady=timeout=10000;" +
// Stop 1: Space Needle, Seattle (Fly-in in ROADMAP, orbit, and reveal HYBRID mid-orbit)
"setMapMode=roadmap;" +
"flyTo=lat=47.620506,lng=-122.349277,alt=120.0,hdg=135.0,tilt=67.0,range=650.0,dur=4500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=47.620506,lng=-122.349277,alt=120.0,hdg=135.0,tilt=67.0,range=650.0,dur=20000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1500;" +
// Stop 2: Downtown Seattle & Columbia Center (Glide to downtown high-rises in HYBRID)
"flyTo=lat=47.604500,lng=-122.330700,alt=180.0,hdg=315.0,tilt=68.0,range=750.0,dur=4000;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=47.604500,lng=-122.330700,alt=180.0,hdg=315.0,tilt=68.0,range=750.0,dur=18000,count=1.0;" +
"delay=dur=1500",
loop = true,
),
createScenario(
name = "uk_shard_to_tower_bridge",
titleId = R.string.scenarios_uk_shard_tower_bridge,
initialState =
"mode=satellite;camera=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=55.0,range=1200.0",
animationString =
"waitUntilTheMapIsSteady=timeout=10000;" +
// Stop 1: The Shard, London
"setMapMode=satellite;" +
"flyTo=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=68.0,range=650.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=68.0,range=650.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// Stop 2: Tower Bridge, London
"setMapMode=satellite;" +
"flyTo=lat=51.505500,lng=-0.075400,alt=20.0,hdg=-16.4,tilt=65.0,range=564.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=51.505500,lng=-0.075400,alt=20.0,hdg=-16.4,tilt=65.0,range=564.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
"setMapMode=satellite",
loop = true,
),
createScenario(
name = "brazil_copacabana_to_ipanema",
titleId = R.string.scenarios_brazil_copacabana_ipanema,
initialState =
"mode=satellite;camera=lat=-22.971100,lng=-43.182500,alt=25.0,hdg=38.0,tilt=55.0,range=2200.0",
animationString =
"waitUntilTheMapIsSteady=timeout=10000;" +
// Stop 1: Copacabana Beach, Rio de Janeiro
"setMapMode=satellite;" +
"flyTo=lat=-22.971100,lng=-43.182500,alt=25.0,hdg=38.0,tilt=68.0,range=1350.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=-22.971100,lng=-43.182500,alt=25.0,hdg=38.0,tilt=68.0,range=1350.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// Stop 2: Ipanema Beach & Arpoador, Rio de Janeiro
"setMapMode=satellite;" +
"flyTo=lat=-22.988800,lng=-43.192600,alt=25.0,hdg=282.0,tilt=67.0,range=1150.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=-22.988800,lng=-43.192600,alt=25.0,hdg=282.0,tilt=67.0,range=1150.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
"setMapMode=satellite",
loop = true,
),
createScenario(
name = "scenic_world_tour",
titleId = R.string.scenarios_scenic_world_tour,
initialState =
"mode=satellite;camera=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=55.0,range=1200.0",
animationString =
"waitUntilTheMapIsSteady=timeout=10000;" +
// 1. UK: The Shard, London
"setMapMode=satellite;" +
"flyTo=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=68.0,range=650.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=51.504500,lng=-0.086500,alt=140.0,hdg=35.0,tilt=68.0,range=650.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 2. UK: Tower Bridge, London
"setMapMode=satellite;" +
"flyTo=lat=51.505500,lng=-0.075400,alt=20.0,hdg=-16.4,tilt=65.0,range=564.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=51.505500,lng=-0.075400,alt=20.0,hdg=-16.4,tilt=65.0,range=564.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 3. Brazil: Copacabana Beach, Rio de Janeiro
"setMapMode=satellite;" +
"flyTo=lat=-22.971100,lng=-43.182500,alt=25.0,hdg=38.0,tilt=68.0,range=1350.0,dur=4500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=-22.971100,lng=-43.182500,alt=25.0,hdg=38.0,tilt=68.0,range=1350.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 4. Brazil: Ipanema Beach & Arpoador, Rio de Janeiro
"setMapMode=satellite;" +
"flyTo=lat=-22.988800,lng=-43.192600,alt=25.0,hdg=282.0,tilt=67.0,range=1150.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=-22.988800,lng=-43.192600,alt=25.0,hdg=282.0,tilt=67.0,range=1150.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 5. USA: Golden Gate Bridge & Marin Headlands (wide framing to capture full span and bay)
"setMapMode=satellite;" +
"flyTo=lat=37.819900,lng=-122.478300,alt=180.0,hdg=106.0,tilt=62.0,range=4000.0,dur=4500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=37.819900,lng=-122.478300,alt=180.0,hdg=106.0,tilt=62.0,range=4000.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 6. USA: Grand Canyon South Rim (panoramic vista of canyon gorges and red strata)
"setMapMode=satellite;" +
"flyTo=lat=36.057000,lng=-112.143000,alt=2200.0,hdg=35.0,tilt=68.0,range=14000.0,dur=4000;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=36.057000,lng=-112.143000,alt=2200.0,hdg=35.0,tilt=68.0,range=14000.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 7. USA: Delicate Arch, Arches National Park
"setMapMode=satellite;" +
"flyTo=lat=38.743502,lng=-109.499374,alt=1467.0,hdg=-10.4,tilt=58.1,range=180.0,dur=3500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=38.743502,lng=-109.499374,alt=1467.0,hdg=-10.4,tilt=58.1,range=180.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
// 8. Japan: Mount Fuji Summit Crater
"setMapMode=satellite;" +
"flyTo=lat=35.360600,lng=138.727400,alt=3650.0,hdg=145.0,tilt=64.0,range=4200.0,dur=4500;" +
"waitUntilTheMapIsSteady=timeout=10000;" +
"flyAround=lat=35.360600,lng=138.727400,alt=3650.0,hdg=145.0,tilt=64.0,range=4200.0,dur=12000,count=1.0,switchmode=hybrid,switchdelay=6000;" +
"delay=dur=1000;" +
"setMapMode=satellite",
loop = true,
),
createScenario(
name = "tower_bridge",
titleId = R.string.scenarios_tower_bridge,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ fun String.toMap3DMode(): Int {
return when (this.lowercase()) {
"hybrid" -> Map3DMode.HYBRID
"satellite" -> Map3DMode.SATELLITE
"roadmap" -> Map3DMode.ROADMAP
else -> {
Log.w(TAG, "Unsupported map mode '$this', defaulting to SATELLITE.")
Map3DMode.SATELLITE
Expand All @@ -172,7 +173,18 @@ fun String.toAnimation(): List<AnimationStep> {
when (key.trim().lowercase()) {
"flyto" -> add(FlyToStep(value.toFlyTo()))
"delay" -> add(DelayStep(value.toDelay()))
"flyaround" -> add(FlyAroundStep(value.toFlyAround()))
"flyaround" -> {
val options = value.toFlyAround()
val attributes = value.toAttributesMap()
val switchModeStr = attributes["switchmode"] ?: attributes["switchMode"]
val switchMode = switchModeStr?.toMap3DMode()
val switchDelay = (attributes["switchdelay"] ?: attributes["switchDelay"])?.toLongOrNull() ?: 0L
Log.d(TAG, "Parsed flyAround: switchMode=$switchMode ($switchModeStr), switchDelay=$switchDelay")
add(FlyAroundStep(options, switchMapMode = switchMode, switchDelayMillis = switchDelay))
}
"setmapmode", "mapmode" -> {
add(SetMapModeStep(value.trim().toMap3DMode()))
}
"waituntilthemapissteady" -> {
add(WaitUntilTheMapIsSteadyStep(value.toTimeout()))
}
Expand Down
Loading
Loading