diff --git a/cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsScreen.kt b/cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsScreen.kt index 7e41430..78c3cb1 100644 --- a/cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsScreen.kt +++ b/cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsScreen.kt @@ -122,45 +122,45 @@ fun DirectionsScreen( val coroutineScope = rememberCoroutineScope() // Auto-retry location request when permissions are granted - LaunchedEffect(hasLocationPermission, pendingLocationRequest) { - if (hasLocationPermission && pendingLocationRequest != null) { - val targetField = pendingLocationRequest!! - pendingLocationRequest = null // Clear the pending request - - // Automatically fetch location for the target field - coroutineScope.launch { - val myLocationPlace = viewModel.getCurrentLocationAsPlace() - myLocationPlace?.let { place -> - // Update the appropriate place based on which field was focused - if (targetField == FieldFocusState.FROM) { - viewModel.updateFromPlace(place) - } else { - viewModel.updateToPlace(place) - } - // Clear focus state after selection - fieldFocusState = FieldFocusState.NONE - } - } - } else if (hasLocationPermission && appPreferences.continuousLocationTracking.value) { - coroutineScope.launch { - viewModel.initializeDeparture() - } + AutoRetryMyLocation( + hasLocationPermission = hasLocationPermission, + pendingLocationRequest = pendingLocationRequest, + coroutineScope = coroutineScope, + viewModel = viewModel, + appPreferences = appPreferences, + onCompletion = { + fieldFocusState = FieldFocusState.NONE } - } - - stringResource(string.change_start_location_to_my_location) + ) Column( modifier = Modifier .fillMaxSize() .padding(horizontal = dimensionResource(dimen.padding)) ) { - if (!isAnyFieldFocused) { + if (isAnyFieldFocused) { + DirectionsScreenFocusedField( + viewModel = viewModel, + fieldFocusState = fieldFocusState, + onFieldFocusStateChange = { + fieldFocusState = it + if (fieldFocusState != FieldFocusState.NONE) { + onFullExpansionRequired() + } + }, + savedPlaces = savedPlaces, + hasLocationPermission = hasLocationPermission, + onRequestLocationPermission = { + pendingLocationRequest = fieldFocusState + onRequestLocationPermission() + }, + coroutineScope = coroutineScope + ) + } else { DirectionsScreenFullUI( viewModel = viewModel, onPeekHeightChange = onPeekHeightChange, onBack = onBack, - onFullExpansionRequired = onFullExpansionRequired, navController = navController, onFieldFocusStateChange = { fieldFocusState = it }, fieldFocusState = fieldFocusState, @@ -170,16 +170,40 @@ fun DirectionsScreen( hasNotificationPermission = hasNotificationPermission, onRequestNotificationPermission = onRequestNotificationPermission ) - } else { - DirectionsScreenFocusedField( - viewModel = viewModel, - fieldFocusState = fieldFocusState, - savedPlaces = savedPlaces, - hasLocationPermission = hasLocationPermission, - onRequestLocationPermission = onRequestLocationPermission, - pendingLocationRequest = pendingLocationRequest, - coroutineScope = coroutineScope - ) + } + } +} + +@Composable +private fun AutoRetryMyLocation( + hasLocationPermission: Boolean, + pendingLocationRequest: FieldFocusState?, + coroutineScope: CoroutineScope, + viewModel: DirectionsViewModel, + appPreferences: AppPreferenceRepository, + onCompletion: () -> Unit, +) { + LaunchedEffect(hasLocationPermission, pendingLocationRequest) { + if (hasLocationPermission && pendingLocationRequest != null) { + val targetField = pendingLocationRequest + + // Automatically fetch location for the target field + coroutineScope.launch { + val myLocationPlace = viewModel.getCurrentLocationAsPlace() + myLocationPlace?.let { place -> + // Update the appropriate place based on which field was focused + if (targetField == FieldFocusState.FROM) { + viewModel.updateFromPlace(place) + } else { + viewModel.updateToPlace(place) + } + onCompletion() + } + } + } else if (hasLocationPermission && appPreferences.continuousLocationTracking.value) { + coroutineScope.launch { + viewModel.initializeDeparture() + } } } } @@ -189,7 +213,6 @@ private fun DirectionsScreenFullUI( viewModel: DirectionsViewModel, onPeekHeightChange: (Dp) -> Unit, onBack: () -> Unit, - onFullExpansionRequired: () -> Job, navController: NavController, onFieldFocusStateChange: (FieldFocusState) -> Unit, fieldFocusState: FieldFocusState, @@ -324,7 +347,12 @@ private fun DirectionsRouteResults( ) { val planState = viewModel.planState if (viewModel.selectedRoutingMode == RoutingMode.PUBLIC_TRANSPORT) { - TransitRouteResults(planState = planState, navController = navController, viewModel = viewModel, appPreferences = appPreferences) + TransitRouteResults( + planState = planState, + navController = navController, + viewModel = viewModel, + appPreferences = appPreferences + ) } else { NonTransitRouteResults( routeState = routeState, @@ -445,10 +473,10 @@ private fun NonTransitRouteResults( private fun DirectionsScreenFocusedField( viewModel: DirectionsViewModel, fieldFocusState: FieldFocusState, + onFieldFocusStateChange: (FieldFocusState) -> Unit, savedPlaces: List, hasLocationPermission: Boolean, onRequestLocationPermission: () -> Unit, - pendingLocationRequest: FieldFocusState?, coroutineScope: CoroutineScope ) { // Show only the focused field and search results when a field is focused @@ -464,7 +492,11 @@ private fun DirectionsScreenFocusedField( }, onTextChange = { viewModel.updateSearchQuery(it) }, onTextFieldFocusChange = { isFocused -> - + if (isFocused) { + onFieldFocusStateChange(fieldFocusState) + } else { + onFieldFocusStateChange(FieldFocusState.NONE) + } }, isFocused = true, modifier = Modifier @@ -479,8 +511,8 @@ private fun DirectionsScreenFocusedField( savedPlaces = savedPlaces, hasLocationPermission = hasLocationPermission, onRequestLocationPermission = onRequestLocationPermission, - pendingLocationRequest = pendingLocationRequest, - coroutineScope = coroutineScope + coroutineScope = coroutineScope, + onFieldFocusStateChange = onFieldFocusStateChange, ) } @@ -488,10 +520,10 @@ private fun DirectionsScreenFocusedField( private fun FocusedFieldContent( viewModel: DirectionsViewModel, fieldFocusState: FieldFocusState, + onFieldFocusStateChange: (FieldFocusState) -> Unit, savedPlaces: List, hasLocationPermission: Boolean, onRequestLocationPermission: () -> Unit, - pendingLocationRequest: FieldFocusState?, coroutineScope: CoroutineScope ) { when { @@ -506,15 +538,16 @@ private fun FocusedFieldContent( savedPlaces = savedPlaces, hasLocationPermission = hasLocationPermission, onRequestLocationPermission = onRequestLocationPermission, - pendingLocationRequest = pendingLocationRequest, - coroutineScope = coroutineScope + coroutineScope = coroutineScope, + onFieldFocusStateChange = onFieldFocusStateChange, ) } else -> { SearchResultsContent( viewModel = viewModel, - fieldFocusState = fieldFocusState + fieldFocusState = fieldFocusState, + onFieldFocusStateChange = onFieldFocusStateChange, ) } } @@ -523,7 +556,7 @@ private fun FocusedFieldContent( @Composable private fun SearchingIndicator() { Text( - text = "Searching...", + text = stringResource(string.searching), modifier = Modifier .fillMaxWidth() .padding(dimensionResource(dimen.padding)) @@ -534,10 +567,10 @@ private fun SearchingIndicator() { private fun QuickSuggestionsContent( viewModel: DirectionsViewModel, fieldFocusState: FieldFocusState, + onFieldFocusStateChange: (FieldFocusState) -> Unit, savedPlaces: List, hasLocationPermission: Boolean, onRequestLocationPermission: () -> Unit, - pendingLocationRequest: FieldFocusState?, coroutineScope: CoroutineScope ) { QuickSuggestions( @@ -546,11 +579,13 @@ private fun QuickSuggestionsContent( fieldFocusState = fieldFocusState, hasLocationPermission = hasLocationPermission, onRequestLocationPermission = onRequestLocationPermission, - coroutineScope = coroutineScope + coroutineScope = coroutineScope, + onFieldFocusStateChange = onFieldFocusStateChange, ), savedPlaces = savedPlaces, onSavedPlaceSelected = { place -> updatePlaceForField(viewModel, fieldFocusState, place) + onFieldFocusStateChange(FieldFocusState.NONE) }, isGettingLocation = viewModel.isGettingLocation, modifier = Modifier.fillMaxWidth() @@ -560,6 +595,7 @@ private fun QuickSuggestionsContent( @Composable private fun SearchResultsContent( viewModel: DirectionsViewModel, + onFieldFocusStateChange: (FieldFocusState) -> Unit, fieldFocusState: FieldFocusState ) { SearchResults( @@ -567,6 +603,7 @@ private fun SearchResultsContent( geocodeResults = deduplicateSearchResults(viewModel.geocodeResults.value), onPlaceSelected = { place -> updatePlaceForField(viewModel, fieldFocusState, place) + onFieldFocusStateChange(FieldFocusState.NONE) }, modifier = Modifier.fillMaxWidth() ) @@ -589,6 +626,7 @@ private fun handleMyLocationSelected( fieldFocusState: FieldFocusState, hasLocationPermission: Boolean, onRequestLocationPermission: () -> Unit, + onFieldFocusStateChange: (FieldFocusState) -> Unit, coroutineScope: CoroutineScope ): () -> Unit = { if (hasLocationPermission) { @@ -596,6 +634,7 @@ private fun handleMyLocationSelected( val myLocationPlace = viewModel.getCurrentLocationAsPlace() myLocationPlace?.let { place -> updatePlaceForField(viewModel, fieldFocusState, place) + onFieldFocusStateChange(FieldFocusState.NONE) } } } else { diff --git a/cardinal-android/app/src/main/res/values/strings.xml b/cardinal-android/app/src/main/res/values/strings.xml index 4bb6165..c68e443 100644 --- a/cardinal-android/app/src/main/res/values/strings.xml +++ b/cardinal-android/app/src/main/res/values/strings.xml @@ -247,4 +247,5 @@ Imperial Zoom in Zoom out + Searching…