diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerComponent.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerComponent.kt
index 46c7ae976d9..37f5badfc80 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerComponent.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerComponent.kt
@@ -17,6 +17,7 @@
*/
package com.wire.android.ui.home.messagecomposer.location
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -30,9 +31,14 @@ import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SheetValue
+import androidx.compose.material3.SnackbarDuration
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -40,6 +46,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import com.wire.android.R
import com.wire.android.ui.common.Icon
@@ -58,6 +65,7 @@ import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.orDefault
import com.wire.android.util.permission.PermissionsDeniedRequestDialog
import com.wire.android.util.permission.rememberCurrentLocationFlow
+import kotlinx.coroutines.launch
/**
* Component to pick the current location to send.
@@ -104,42 +112,91 @@ fun LocationPickerComponent(
}
}
add {
- Column(
+ Box(
modifier = Modifier
- .align(alignment = Alignment.Start)
- .padding(horizontal = dimensions().spacing16x)
.wrapContentHeight()
.fillMaxWidth()
) {
- WirePrimaryButton(
- onClick = {
- onLocationPicked(geoLocatedAddress!!)
- onLocationClosed()
- },
- leadingIcon = Icons.Filled.Send.Icon(Modifier.padding(end = dimensions().spacing8x)),
- text = stringResource(id = R.string.content_description_send_button),
- state = if (isLocationLoading || geoLocatedAddress == null) {
- WireButtonState.Disabled
- } else {
- WireButtonState.Default
+ if (showLocationSharingError) {
+ LocationErrorMessage {
+ coroutineScope.launch {
+ sheetState.hide()
+ viewModel.onLocationSharingErrorDialogDiscarded()
+ onLocationClosed()
+ }
}
+ }
+ SendLocationButton(
+ isLocationLoading = isLocationLoading,
+ geoLocatedAddress = geoLocatedAddress,
+ onLocationPicked = onLocationPicked,
+ onLocationClosed = onLocationClosed
)
- VerticalSpace.x16()
}
}
}
)
+
+ if (showPermissionDeniedDialog) {
+ PermissionsDeniedRequestDialog(
+ body = R.string.location_app_permission_dialog_body,
+ onDismiss = {
+ viewModel.onPermissionsDialogDiscarded()
+ onLocationClosed()
+ }
+ )
+ }
}
}
+}
- if (viewModel.state.showPermissionDeniedDialog) {
- PermissionsDeniedRequestDialog(
- body = R.string.location_app_permission_dialog_body,
- onDismiss = {
- viewModel.onPermissionsDialogDiscarded()
+@Composable
+private fun SendLocationButton(
+ isLocationLoading: Boolean,
+ geoLocatedAddress: GeoLocatedAddress?,
+ onLocationPicked: (GeoLocatedAddress) -> Unit,
+ onLocationClosed: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = dimensions().spacing16x)
+ .wrapContentHeight()
+ .fillMaxWidth()
+ ) {
+ WirePrimaryButton(
+ onClick = {
+ onLocationPicked(geoLocatedAddress!!)
onLocationClosed()
+ },
+ leadingIcon = Icons.Filled.Send.Icon(Modifier.padding(end = dimensions().spacing8x)),
+ text = stringResource(id = R.string.content_description_send_button),
+ state = if (isLocationLoading || geoLocatedAddress == null) {
+ WireButtonState.Disabled
+ } else {
+ WireButtonState.Default
}
)
+ VerticalSpace.x16()
+ }
+}
+
+@Composable
+private fun LocationErrorMessage(
+ message: String = stringResource(id = R.string.location_could_not_be_shared),
+ onLocationClosed: () -> Unit
+) {
+ Box(Modifier.zIndex(Float.MAX_VALUE), contentAlignment = Alignment.BottomCenter) {
+ val snackbarHostState = remember { SnackbarHostState() }
+ LaunchedEffect(snackbarHostState) {
+ val result = snackbarHostState.showSnackbar(message = message, duration = SnackbarDuration.Short)
+ when (result) {
+ SnackbarResult.Dismissed -> onLocationClosed()
+ SnackbarResult.ActionPerformed -> {
+ /* do nothing */
+ }
+ }
+ }
+ SnackbarHost(hostState = snackbarHostState)
}
}
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerState.kt
index e8d10c30848..d0f2d55d703 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerState.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerState.kt
@@ -27,5 +27,6 @@ data class LocationPickerState(
val isLocationLoading: Boolean = false,
val isPermissionDiscarded: Boolean = false,
val showPermissionDeniedDialog: Boolean = false,
+ val showLocationSharingError: Boolean = false,
val wireModalSheetState: WireModalSheetState = WireModalSheetState(SheetValue.Hidden)
)
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerViewModel.kt
index fbde6c867d7..23c685054e1 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerViewModel.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/location/LocationPickerViewModel.kt
@@ -26,6 +26,7 @@ import android.location.LocationManager
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.core.location.LocationManagerCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.gms.location.LocationServices
@@ -40,6 +41,7 @@ import javax.inject.Inject
@HiltViewModel
class LocationPickerViewModel @Inject constructor() : ViewModel() {
+
var state: LocationPickerState by mutableStateOf(LocationPickerState())
private set
@@ -47,16 +49,36 @@ class LocationPickerViewModel @Inject constructor() : ViewModel() {
state = state.copy(showPermissionDeniedDialog = false)
}
+ fun onLocationSharingErrorDialogDiscarded() {
+ state = state.copy(showLocationSharingError = false)
+ }
+
fun onPermissionsDenied() {
state = state.copy(showPermissionDeniedDialog = true)
}
private fun toStartLoadingLocationState() {
- state = state.copy(isLocationLoading = true, geoLocatedAddress = null)
+ state = state.copy(
+ showLocationSharingError = false,
+ isLocationLoading = true,
+ geoLocatedAddress = null
+ )
}
private fun toLocationLoadedState(geoLocatedAddress: GeoLocatedAddress) {
- state = state.copy(isLocationLoading = false, geoLocatedAddress = geoLocatedAddress)
+ state = state.copy(
+ showLocationSharingError = false,
+ isLocationLoading = false,
+ geoLocatedAddress = geoLocatedAddress
+ )
+ }
+
+ private fun toLocationError() {
+ state = state.copy(
+ showLocationSharingError = true,
+ isLocationLoading = false,
+ geoLocatedAddress = null,
+ )
}
fun getCurrentLocation(context: Context) {
@@ -74,23 +96,36 @@ class LocationPickerViewModel @Inject constructor() : ViewModel() {
@SuppressLint("MissingPermission")
private fun getLocationWithGms(context: Context) = viewModelScope.launch {
appLogger.d("Getting location with GMS")
- val locationProvider = LocationServices.getFusedLocationProviderClient(context)
- val currentLocation = locationProvider.getCurrentLocation(PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token).await()
- val address = Geocoder(context).getFromLocation(currentLocation.latitude, currentLocation.longitude, 1).orEmpty()
- toLocationLoadedState(GeoLocatedAddress(address.firstOrNull(), currentLocation))
+ if (isLocationServicesEnabled(context)) {
+ val locationProvider = LocationServices.getFusedLocationProviderClient(context)
+ val currentLocation = locationProvider.getCurrentLocation(PRIORITY_HIGH_ACCURACY, CancellationTokenSource().token).await()
+ val address = Geocoder(context).getFromLocation(currentLocation.latitude, currentLocation.longitude, 1).orEmpty()
+ toLocationLoadedState(GeoLocatedAddress(address.firstOrNull(), currentLocation))
+ } else {
+ toLocationError()
+ }
}
@SuppressLint("MissingPermission")
private fun getLocationWithoutGms(context: Context) = viewModelScope.launch {
appLogger.d("Getting location without GMS")
- val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
- val networkLocationListener: LocationListener = object : LocationListener {
- override fun onLocationChanged(location: Location) {
- val address = Geocoder(context).getFromLocation(location.latitude, location.longitude, 1).orEmpty()
- toLocationLoadedState(GeoLocatedAddress(address.firstOrNull(), location))
- locationManager.removeUpdates(this) // important step, otherwise it will keep listening for location changes
+ if (isLocationServicesEnabled(context)) {
+ val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ val networkLocationListener: LocationListener = object : LocationListener {
+ override fun onLocationChanged(location: Location) {
+ val address = Geocoder(context).getFromLocation(location.latitude, location.longitude, 1).orEmpty()
+ toLocationLoadedState(GeoLocatedAddress(address.firstOrNull(), location))
+ locationManager.removeUpdates(this) // important step, otherwise it will keep listening for location changes
+ }
}
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, networkLocationListener)
+ } else {
+ toLocationError()
}
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, networkLocationListener)
+ }
+
+ private fun isLocationServicesEnabled(context: Context): Boolean {
+ val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ return LocationManagerCompat.isLocationEnabled(locationManager)
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 55464d682d5..5d0860c87be 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1360,4 +1360,5 @@
Share Location
Allow Wire to access your device location to send your location.
Please wait...
+ Location could not be shared