From bd29afecd251a1a8d06ccbc399bd58babe77871e Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Mon, 28 Sep 2026 14:26:50 -0500 Subject: [PATCH] fix(location): open map with approximate or one-time location permission Share location only opened the map when precise location was granted. The chat never requested the runtime permission, so "Ask every time" could never be granted, and both the chat check and the location picker required ACCESS_FINE_LOCATION. Now the permission is requested from the chat (fine and coarse, so the user can choose on API 31+) and either grant is accepted. The location services check uses LocationManagerCompat.isLocationEnabled, and getLastKnownLocation is guarded against SecurityException. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: rapterjet2004 --- .../com/nextcloud/talk/chat/ChatActivity.kt | 48 +++++++++++++++---- .../components/LocationPickerScreen.kt | 22 +++++---- .../permissions/PlatformPermissionUtilImpl.kt | 6 ++- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 164916cc032..5a5c7f28f5a 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -74,6 +74,7 @@ import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker.PERMISSION_GRANTED +import androidx.core.location.LocationManagerCompat import androidx.core.net.toUri import androidx.core.os.bundleOf import androidx.core.view.ViewCompat @@ -2837,6 +2838,20 @@ class ChatActivity : .make(binding.root, context.getString(R.string.take_photo_permission), Snackbar.LENGTH_LONG) .show() } + } else if (requestCode == REQUEST_LOCATION_PERMISSION) { + if (grantResults.any { it == PackageManager.PERMISSION_GRANTED }) { + openLocationPicker() + } else if (!shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { + showLocationPermissionDeniedDialog() + } else { + Snackbar + .make( + binding.root, + context.getString(R.string.nc_location_permission_required), + Snackbar.LENGTH_LONG + ) + .show() + } } } @@ -2904,20 +2919,32 @@ class ChatActivity : Log.d(TAG, "showShareLocationScreen") val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager - val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) - if (!isGpsEnabled) { + if (!LocationManagerCompat.isLocationEnabled(locationManager)) { showLocationServicesDisabledDialog() - } else if (!permissionUtil.isLocationPermissionGranted()) { - showLocationPermissionDeniedDialog() + } else if (permissionUtil.isLocationPermissionGranted()) { + openLocationPicker() + } else { + requestLocationPermissions() } + } - if (permissionUtil.isLocationPermissionGranted() && isGpsEnabled) { - val intent = Intent(this, LocationPickerActivity::class.java) - intent.putExtra(KEY_ROOM_TOKEN, roomToken) - intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) - startActivity(intent) - } + private fun openLocationPicker() { + val intent = Intent(this, LocationPickerActivity::class.java) + intent.putExtra(KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) + startActivity(intent) + } + + private fun requestLocationPermissions() { + // request both, so that the user can choose between precise and approximate location (API 31+) + requestPermissions( + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ), + REQUEST_LOCATION_PERMISSION + ) } private fun showLocationServicesDisabledDialog() { @@ -4323,6 +4350,7 @@ class ChatActivity : private const val REQUEST_RECORD_AUDIO_PERMISSION = 222 private const val REQUEST_READ_CONTACT_PERMISSION = 234 private const val REQUEST_CAMERA_PERMISSION = 223 + private const val REQUEST_LOCATION_PERMISSION = 224 private const val FILE_DATE_PATTERN = "yyyy-MM-dd HH-mm-ss" private const val VIDEO_SUFFIX = ".mp4" private const val PICTURE_SUFFIX = ".jpg" diff --git a/app/src/main/java/com/nextcloud/talk/location/components/LocationPickerScreen.kt b/app/src/main/java/com/nextcloud/talk/location/components/LocationPickerScreen.kt index 4f743d720e8..6a8c398c5b4 100644 --- a/app/src/main/java/com/nextcloud/talk/location/components/LocationPickerScreen.kt +++ b/app/src/main/java/com/nextcloud/talk/location/components/LocationPickerScreen.kt @@ -209,7 +209,7 @@ fun LocationPickerScreen( @SuppressLint("LocalContextGetResourceValueCall") val permissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - if (permissions.values.all { it }) { + if (permissions.values.any { it }) { requestLocationUpdates(locationManager, locationListener) { msgRes -> coroutineScope.launch { snackbarHostState.showSnackbar(context.getString(msgRes)) } } @@ -680,21 +680,27 @@ private fun LocationPickerSharePanel( @SuppressLint("MissingPermission") private fun getLastKnownLocation(locationManager: LocationManager): android.location.Location? = - when { - locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> - locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) + try { + when { + locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> + locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) - locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) -> - locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) + locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) -> + locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) - else -> null + else -> null + } + } catch (e: SecurityException) { + // before API 31 the GPS provider requires fine location, which may not be granted + Log.w(TAG, "Missing permission to get last known location", e) + null } private fun isLocationPermissionsGranted(context: Context): Boolean = PermissionChecker.checkSelfPermission( context, Manifest.permission.ACCESS_COARSE_LOCATION - ) == PermissionChecker.PERMISSION_GRANTED && + ) == PermissionChecker.PERMISSION_GRANTED || PermissionChecker.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION diff --git a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt index a939561ab58..f29ef0e4bc7 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt @@ -95,7 +95,11 @@ class PlatformPermissionUtilImpl(private val context: Context) : PlatformPermiss PermissionChecker.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION - ) == PermissionChecker.PERMISSION_GRANTED + ) == PermissionChecker.PERMISSION_GRANTED || + PermissionChecker.checkSelfPermission( + context, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PermissionChecker.PERMISSION_GRANTED companion object { private val TAG = PlatformPermissionUtilImpl::class.simpleName