@@ -16,11 +16,16 @@ package com.example.advancedmaps3dsamples.scenarios
1616
1717import android.content.Intent
1818import android.os.Bundle
19+ import android.view.WindowManager
1920import androidx.activity.ComponentActivity
21+ import androidx.activity.SystemBarStyle
2022import androidx.activity.compose.BackHandler
2123import androidx.activity.compose.setContent
2224import androidx.activity.enableEdgeToEdge
2325import androidx.activity.viewModels
26+ import androidx.compose.animation.core.Animatable
27+ import androidx.compose.animation.core.LinearEasing
28+ import androidx.compose.animation.core.tween
2429import androidx.compose.foundation.background
2530import androidx.compose.foundation.layout.Arrangement
2631import androidx.compose.foundation.layout.Box
@@ -32,6 +37,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
3237import androidx.compose.foundation.layout.height
3338import androidx.compose.foundation.layout.offset
3439import androidx.compose.foundation.layout.padding
40+ import androidx.compose.foundation.layout.safeDrawingPadding
3541import androidx.compose.foundation.layout.size
3642import androidx.compose.foundation.shape.CircleShape
3743import androidx.compose.material.icons.Icons
@@ -46,21 +52,30 @@ import androidx.compose.material3.ExperimentalMaterial3Api
4652import androidx.compose.material3.Icon
4753import androidx.compose.material3.MaterialTheme
4854import androidx.compose.material3.Scaffold
55+ import androidx.compose.material3.SnackbarHostState
4956import androidx.compose.material3.Text
5057import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
5158import androidx.compose.runtime.Composable
59+ import androidx.compose.runtime.LaunchedEffect
5260import androidx.compose.runtime.getValue
5361import androidx.compose.runtime.remember
62+ import androidx.compose.runtime.rememberCoroutineScope
5463import androidx.compose.ui.Alignment
5564import androidx.compose.ui.Modifier
65+ import androidx.compose.ui.draw.alpha
5666import androidx.compose.ui.graphics.Color
67+ import androidx.compose.ui.graphics.rememberGraphicsLayer
5768import androidx.compose.ui.graphics.vector.ImageVector
5869import androidx.compose.ui.res.stringResource
5970import androidx.compose.ui.text.style.TextAlign
6071import androidx.compose.ui.tooling.preview.Preview
6172import androidx.compose.ui.unit.dp
73+ import androidx.core.view.WindowCompat
74+ import androidx.core.view.WindowInsetsCompat
75+ import androidx.core.view.WindowInsetsControllerCompat
6276import androidx.lifecycle.compose.collectAsStateWithLifecycle
6377import com.example.advancedmaps3dsamples.R
78+ import com.example.advancedmaps3dsamples.ainavigator.WhiskeyCompass
6479import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme
6580import com.example.advancedmaps3dsamples.utils.DEFAULT_ROLL
6681import com.example.advancedmaps3dsamples.utils.toHeading
@@ -69,7 +84,10 @@ import com.example.advancedmaps3dsamples.utils.toRoll
6984import com.example.advancedmaps3dsamples.utils.toTilt
7085import com.google.android.gms.maps3d.model.Camera
7186import dagger.hilt.android.AndroidEntryPoint
87+ import kotlinx.coroutines.delay
7288import kotlinx.coroutines.flow.map
89+ import kotlinx.coroutines.launch
90+ import kotlin.time.Duration.Companion.seconds
7391
7492@AndroidEntryPoint
7593@OptIn(ExperimentalMaterial3Api ::class )
@@ -85,7 +103,14 @@ class ScenariosActivity : ComponentActivity() {
85103
86104 override fun onCreate (savedInstanceState : Bundle ? ) {
87105 super .onCreate(savedInstanceState)
88- enableEdgeToEdge()
106+ enableEdgeToEdge(
107+ statusBarStyle = SystemBarStyle .light(android.graphics.Color .TRANSPARENT , android.graphics.Color .TRANSPARENT ),
108+ navigationBarStyle = SystemBarStyle .light(android.graphics.Color .TRANSPARENT , android.graphics.Color .TRANSPARENT )
109+ )
110+
111+ hideSystemUI()
112+ window.addFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
113+
89114 setContent {
90115 val viewState by viewModel.viewState.collectAsStateWithLifecycle()
91116 val currentCamera by viewModel.currentCamera.collectAsStateWithLifecycle(Camera .DEFAULT_CAMERA )
@@ -95,21 +120,47 @@ class ScenariosActivity : ComponentActivity() {
95120
96121 val cameraAttribute by viewModel.trackedAttribute.collectAsStateWithLifecycle()
97122
123+ val camera by viewModel.currentCamera.collectAsStateWithLifecycle()
124+ val compassAlpha = remember { Animatable (0.55f ) }
125+
126+ // This LaunchedEffect controls the compass alpha based on camera heading changes.
127+ LaunchedEffect (camera.heading) {
128+ // When camera.heading changes, this LaunchedEffect is cancelled and restarted.
129+ // Any coroutine launched within its scope (like the one below) is also cancelled.
130+
131+ // Reset alpha to initial state and stop any ongoing animation on compassAlpha.
132+ compassAlpha.snapTo(0.55f )
133+
134+ // Launch a new coroutine within this LaunchedEffect's scope.
135+ // This coroutine will handle the delay and subsequent fade-out animation.
136+ // If camera.heading changes again before this completes, this coroutine will be cancelled.
137+ launch {
138+ delay(2 .seconds) // Wait for 2 seconds of stable heading
139+ // If this point is reached, it means camera.heading was stable for 2 seconds.
140+ compassAlpha.animateTo(
141+ targetValue = 0.3f ,
142+ animationSpec = tween(durationMillis = 500 , easing = LinearEasing )
143+ )
144+ }
145+ }
146+
98147 AdvancedMaps3DSamplesTheme (
99148 dynamicColor = false
100149 ) {
101150 Scaffold (
102151 modifier = Modifier .fillMaxSize(),
103152 topBar = {
104- CenterAlignedTopAppBar (
105- colors = topAppBarColors(
106- containerColor = MaterialTheme .colorScheme.primaryContainer,
107- titleContentColor = MaterialTheme .colorScheme.primary,
108- ),
109- title = {
110- Text (stringResource(viewState.scenario?.titleId ? : R .string.scenarios_none))
111- }
112- )
153+ if (viewState.scenario == null ) {
154+ CenterAlignedTopAppBar (
155+ colors = topAppBarColors(
156+ containerColor = MaterialTheme .colorScheme.primaryContainer,
157+ titleContentColor = MaterialTheme .colorScheme.primary,
158+ ),
159+ title = {
160+ Text (stringResource(viewState.scenario?.titleId ? : R .string.scenarios_none))
161+ }
162+ )
163+ }
113164 }
114165 ) { innerPadding ->
115166 val modifier = Modifier .padding(innerPadding)
@@ -135,6 +186,17 @@ class ScenariosActivity : ComponentActivity() {
135186 onMap3dViewReady = { viewModel.setGoogleMap3D(it) },
136187 onReleaseMap = { viewModel.releaseGoogleMap3D() },
137188 )
189+
190+ WhiskeyCompass (
191+ heading = camera.heading?.toFloat() ? : 0f ,
192+ modifier = Modifier
193+ .fillMaxWidth()
194+ .alpha(compassAlpha.value)
195+ .safeDrawingPadding(),
196+ stripHeight = 90 .dp,
197+ pixelsPerDegree = 7f ,
198+ degreeLabelInterval = 30 ,
199+ )
138200 }
139201
140202 if (viewState.countDownVisible) {
@@ -165,6 +227,14 @@ class ScenariosActivity : ComponentActivity() {
165227 }
166228 }
167229 }
230+
231+ private fun hideSystemUI () {
232+ WindowCompat .setDecorFitsSystemWindows(window, false )
233+ WindowInsetsControllerCompat (window, window.decorView).let { controller ->
234+ controller.hide(WindowInsetsCompat .Type .systemBars())
235+ controller.systemBarsBehavior = WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
236+ }
237+ }
168238}
169239
170240/* *
@@ -256,7 +326,10 @@ fun FinishedOverlay(
256326 ) {
257327 // Close Button at the top end corner
258328 OverlayButton (
259- modifier = modifier.align(Alignment .TopEnd ).offset(x = (- 16 ).dp, y = 16 .dp).size(size),
329+ modifier = modifier
330+ .align(Alignment .TopEnd )
331+ .offset(x = (- 16 ).dp, y = 16 .dp)
332+ .size(size),
260333 onExitClick = onCloseClick,
261334 imageVector = Icons .Default .Close ,
262335 contentDescription = stringResource(R .string.close)
0 commit comments