1+ @file:OptIn(ExperimentalPermissionsApi ::class )
2+
13package com.espressodev.gptmap.feature.map
24
35import StreetView
46import android.annotation.SuppressLint
7+ import android.util.Log
58import androidx.activity.compose.BackHandler
69import androidx.compose.animation.AnimatedVisibility
710import androidx.compose.animation.core.LinearEasing
@@ -24,6 +27,8 @@ import androidx.compose.foundation.layout.width
2427import androidx.compose.foundation.pager.HorizontalPager
2528import androidx.compose.foundation.pager.rememberPagerState
2629import androidx.compose.foundation.shape.RoundedCornerShape
30+ import androidx.compose.material3.FilledTonalIconButton
31+ import androidx.compose.material3.Icon
2732import androidx.compose.material3.MaterialTheme
2833import androidx.compose.material3.OutlinedButton
2934import androidx.compose.material3.Scaffold
@@ -34,7 +39,6 @@ import androidx.compose.runtime.LaunchedEffect
3439import androidx.compose.runtime.getValue
3540import androidx.compose.runtime.mutableStateOf
3641import androidx.compose.runtime.remember
37- import androidx.compose.runtime.setValue
3842import androidx.compose.ui.Alignment
3943import androidx.compose.ui.Modifier
4044import androidx.compose.ui.graphics.graphicsLayer
@@ -73,6 +77,8 @@ import com.espressodev.gptmap.feature.map.ComponentLoadingState.MAP
7377import com.espressodev.gptmap.feature.map.MapBottomSheetState.BOTTOM_SHEET_HIDDEN
7478import com.espressodev.gptmap.feature.map.MapBottomSheetState.DETAIL_CARD
7579import com.espressodev.gptmap.feature.map.MapBottomSheetState.SMALL_INFORMATION_CARD
80+ import com.google.accompanist.permissions.ExperimentalPermissionsApi
81+ import com.google.accompanist.permissions.rememberMultiplePermissionsState
7682import com.google.android.gms.maps.CameraUpdateFactory
7783import com.google.android.gms.maps.model.CameraPosition
7884import com.google.android.gms.maps.model.MapStyleOptions
@@ -88,7 +94,7 @@ import com.espressodev.gptmap.core.designsystem.R.string as AppText
8894@SuppressLint(" UnusedMaterial3ScaffoldPaddingParameter" )
8995@Composable
9096fun MapRoute (
91- navigateToStreetView : (Float , Float ) -> Unit ,
97+ navigateToStreetView : (Pair < Float , Float > ) -> Unit ,
9298 navigateToScreenshot : () -> Unit ,
9399 navigateToProfile : () -> Unit ,
94100 favouriteId : String ,
@@ -102,9 +108,7 @@ fun MapRoute(
102108 onEvent = { event ->
103109 viewModel.onEvent(
104110 event = event,
105- navigateToStreetView = { latLng ->
106- navigateToStreetView(latLng.first.toFloat(), latLng.second.toFloat())
107- }
111+ navigateToStreetView = navigateToStreetView
108112 )
109113 },
110114 onAvatarClick = navigateToProfile,
@@ -162,7 +166,7 @@ private fun MapScreen(
162166 DisplayBottomSheet (
163167 bottomSheetState = uiState.bottomSheetState,
164168 location = uiState.location,
165- onEvent = onEvent
169+ onEvent = onEvent,
166170 )
167171 }
168172}
@@ -186,25 +190,63 @@ private fun DisplayImageGallery(
186190private fun BoxScope.DisplayBottomSheet (
187191 bottomSheetState : MapBottomSheetState ,
188192 location : Location ,
189- onEvent : (MapUiEvent ) -> Unit
193+ onEvent : (MapUiEvent ) -> Unit ,
194+ modifier : Modifier = Modifier
190195) {
191196 when (bottomSheetState) {
192- SMALL_INFORMATION_CARD -> {
193- SmallInformationCard (
194- content = location.content,
195- onExploreWithAiClick = { onEvent(MapUiEvent .OnExploreWithAiClick ) },
196- onBackClick = { onEvent(MapUiEvent .OnBackClick ) },
197- )
198- }
197+ SMALL_INFORMATION_CARD -> SmallInformationCard (
198+ content = location.content,
199+ onExploreWithAiClick = { onEvent(MapUiEvent .OnExploreWithAiClick ) },
200+ onBackClick = { onEvent(MapUiEvent .OnBackClick ) },
201+ modifier = modifier
202+ )
199203
200- DETAIL_CARD -> {
201- DetailSheet (location = location, onEvent = onEvent)
202- }
204+ DETAIL_CARD -> DetailSheet (location = location, onEvent = onEvent, modifier = modifier)
203205
204206 BOTTOM_SHEET_HIDDEN -> {}
205207 }
206208}
207209
210+ @Composable
211+ fun BoxScope.MyCurrentLocationButton (onClick : () -> Unit , modifier : Modifier = Modifier ) {
212+ val locationPermissionState = rememberMultiplePermissionsState(
213+ permissions = listOf (
214+ android.Manifest .permission.ACCESS_FINE_LOCATION ,
215+ android.Manifest .permission.ACCESS_COARSE_LOCATION
216+ )
217+ )
218+
219+ val (shouldShowDialog, setShouldShowDialog) = remember { mutableStateOf(value = false ) }
220+
221+ LaunchedEffect (shouldShowDialog) {
222+ if (shouldShowDialog) {
223+ if (! locationPermissionState.allPermissionsGranted) {
224+ locationPermissionState.launchMultiplePermissionRequest()
225+ } else {
226+ onClick()
227+ }
228+ }
229+ setShouldShowDialog(false )
230+ }
231+
232+ FilledTonalIconButton (
233+ onClick = {
234+ if (locationPermissionState.allPermissionsGranted) {
235+ onClick()
236+ } else {
237+ setShouldShowDialog(true )
238+ }
239+ },
240+ modifier = modifier
241+ .align(Alignment .BottomEnd )
242+ .padding(8 .dp)
243+ ) {
244+ Icon (
245+ imageVector = GmIcons .MyLocationOutlined ,
246+ contentDescription = stringResource(id = AppText .my_location)
247+ )
248+ }
249+ }
208250
209251@OptIn(ExperimentalFoundationApi ::class )
210252@Composable
@@ -266,7 +308,7 @@ private fun MapSearchBar(
266308@Composable
267309private fun MapSection (uiState : MapUiState , isPinVisible : Boolean , onEvent : (MapUiEvent ) -> Unit ) {
268310 val context = LocalContext .current
269- var isMapLoaded by remember { mutableStateOf(value = false ) }
311+ val ( isMapLoaded, setMapLoaded) = remember { mutableStateOf(value = false ) }
270312 val mapProperties = remember {
271313 MapProperties (
272314 mapStyleOptions = MapStyleOptions .loadRawResourceStyle(
@@ -280,7 +322,24 @@ private fun MapSection(uiState: MapUiState, isPinVisible: Boolean, onEvent: (Map
280322 }
281323
282324 LaunchedEffect (uiState.coordinatesLatLng) {
283- cameraPositionState.animate(CameraUpdateFactory .newLatLngZoom(uiState.coordinatesLatLng, 14f ))
325+ cameraPositionState.animate(
326+ CameraUpdateFactory .newLatLngZoom(
327+ uiState.coordinatesLatLng,
328+ 14f
329+ )
330+ )
331+ }
332+ Log .d(" MapScreen" , " MapSection: ${uiState.myCurrentLocationState} " )
333+ LaunchedEffect (uiState.myCurrentLocationState) {
334+ if (uiState.myCurrentLocationState.first) {
335+ cameraPositionState.animate(
336+ CameraUpdateFactory .newLatLngZoom(
337+ uiState.myCoordinatesLatLng,
338+ 14f
339+ )
340+ )
341+ onEvent(MapUiEvent .OnUnsetMyCurrentLocationState )
342+ }
284343 }
285344
286345 Box (modifier = Modifier .fillMaxSize()) {
@@ -305,8 +364,13 @@ private fun MapSection(uiState: MapUiState, isPinVisible: Boolean, onEvent: (Map
305364 cameraPositionState = cameraPositionState,
306365 uiSettings = MapUiSettings (zoomControlsEnabled = false ),
307366 properties = mapProperties,
308- onMapLoaded = { isMapLoaded = true }
367+ onMapLoaded = { setMapLoaded( true ) },
309368 )
369+ if (uiState.isMyLocationButtonVisible)
370+ MyCurrentLocationButton (
371+ onClick = { onEvent(MapUiEvent .OnMyCurrentLocationClick ) },
372+ modifier = Modifier .zIndex(1f )
373+ )
310374 }
311375}
312376
@@ -316,7 +380,7 @@ private fun BoxScope.LoadingDialog(
316380 modifier : Modifier = Modifier
317381) {
318382 AnimatedVisibility (
319- visible = loadingState == MAP ,
383+ visible = loadingState != ComponentLoadingState . NOTHING ,
320384 modifier = modifier
321385 .zIndex(1f )
322386 .align(Alignment .TopCenter )
@@ -325,6 +389,11 @@ private fun BoxScope.LoadingDialog(
325389 .padding(top = 72 .dp)
326390 .padding(horizontal = 32 .dp)
327391 ) {
392+ val title = when (loadingState) {
393+ MAP -> AppText .discovering_your_dream_place
394+ ComponentLoadingState .MY_LOCATION -> AppText .loading_your_location
395+ ComponentLoadingState .NOTHING -> AppText .not_valid_name
396+ }
328397 Surface (
329398 shape = RoundedCornerShape (16 .dp),
330399 color = MaterialTheme .colorScheme.surface.copy(alpha = 0.8f ),
@@ -335,7 +404,7 @@ private fun BoxScope.LoadingDialog(
335404 ) {
336405 DefaultLoadingAnimation ()
337406 Text (
338- text = stringResource(AppText .discovering_your_dream_place ),
407+ text = stringResource(title ),
339408 textAlign = TextAlign .Center ,
340409 style = MaterialTheme .typography.titleSmall,
341410 fontWeight = FontWeight .Medium ,
@@ -380,7 +449,8 @@ private fun BoxScope.LocationPin(
380449 animationSpec = tween(
381450 durationMillis = if (isCameraMoving) 800 else 2000 ,
382451 easing = LinearEasing
383- ), label = " lottie animation progress"
452+ ),
453+ label = " lottie animation progress"
384454 )
385455
386456 LottieAnimation (
0 commit comments