diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0b878ee71..5e08f29db 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,7 +29,6 @@
-
@@ -48,6 +47,7 @@
+
@@ -104,7 +104,7 @@
diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
index 7565da079..9232bfbac 100644
--- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
+++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
@@ -145,21 +145,6 @@ fun Context.getLocationPermissions(): Array {
/** @return true if the user already has location permission */
fun Context.hasLocationPermission() = getLocationPermissions().isEmpty()
-/**
- * A list of missing background location permissions (or empty if we already have what we need)
- */
-fun Context.getBackgroundPermissions(): Array {
- val perms = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION)
-
- if (android.os.Build.VERSION.SDK_INT >= 29) // only added later
- perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
-
- return getMissingPermissions(perms)
-}
-
-/** @return true if the user already has background location permission */
-fun Context.hasBackgroundPermission() = getBackgroundPermissions().isEmpty()
-
/**
* Notification permission (or empty if we already have what we need)
*/
diff --git a/app/src/main/java/com/geeksville/mesh/repository/location/LocationRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/location/LocationRepository.kt
index 23504fba5..9cd064814 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/location/LocationRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/location/LocationRepository.kt
@@ -1,8 +1,10 @@
package com.geeksville.mesh.repository.location
-import android.annotation.SuppressLint
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.app.Application
import android.location.LocationManager
+import androidx.annotation.RequiresPermission
import androidx.core.location.LocationCompat
import androidx.core.location.LocationListenerCompat
import androidx.core.location.LocationManagerCompat
@@ -10,7 +12,6 @@ import androidx.core.location.LocationRequestCompat
import androidx.core.location.altitude.AltitudeConverterCompat
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
-import com.geeksville.mesh.android.hasBackgroundPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
@@ -32,9 +33,8 @@ class LocationRepository @Inject constructor(
private val _receivingLocationUpdates: MutableStateFlow = MutableStateFlow(false)
val receivingLocationUpdates: StateFlow get() = _receivingLocationUpdates
- @SuppressLint("MissingPermission")
+ @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
private fun LocationManager.requestLocationUpdates() = callbackFlow {
- if (!context.hasBackgroundPermission()) close()
val intervalMs = 30 * 1000L // 30 seconds
val minDistanceM = 0f
@@ -96,5 +96,6 @@ class LocationRepository @Inject constructor(
/**
* Observable flow for location updates
*/
+ @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
fun getLocations() = locationManager.get().requestLocationUpdates()
}
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
index 20782fd6b..737e7ac4d 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
@@ -1,5 +1,6 @@
package com.geeksville.mesh.service
+import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
@@ -17,7 +18,7 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
import com.geeksville.mesh.MeshProtos.MeshPacket
import com.geeksville.mesh.MeshProtos.ToRadio
-import com.geeksville.mesh.android.hasBackgroundPermission
+import com.geeksville.mesh.android.hasLocationPermission
import com.geeksville.mesh.database.MeshLogRepository
import com.geeksville.mesh.database.PacketRepository
import com.geeksville.mesh.database.entity.MeshLog
@@ -175,7 +176,8 @@ class MeshService : Service(), Logging {
// If we're already observing updates, don't register again
if (locationFlow?.isActive == true) return
- if (hasBackgroundPermission()) {
+ @SuppressLint("MissingPermission")
+ if (hasLocationPermission()) {
locationFlow = locationRepository.getLocations().onEach { location ->
sendPosition(
position {
@@ -299,7 +301,7 @@ class MeshService : Service(), Logging {
serviceNotifications.notifyId,
notification,
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
- ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
} else {
0
},
diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
index 7b253b7fc..5911c91d8 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
@@ -138,24 +138,16 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
private fun initCommonUI() {
- val requestBackgroundAndCheckLauncher =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
- if (permissions.entries.any { !it.value }) {
- debug("User denied background permission")
- model.showSnackbar(getString(R.string.why_background_required))
- }
- }
-
- val requestLocationAndBackgroundLauncher =
+ val requestLocationPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.entries.all { it.value }) {
- // Older versions of android only need Location permission
- if (!requireContext().hasBackgroundPermission())
- requestBackgroundAndCheckLauncher.launch(requireContext().getBackgroundPermissions())
+ model.provideLocation.value = true
+ model.meshService?.startProvideLocation()
} else {
debug("User denied location permission")
model.showSnackbar(getString(R.string.why_background_required))
}
+ bluetoothViewModel.permissionsUpdated()
}
// init our region spinner
@@ -240,7 +232,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked ->
// Don't check the box until the system setting changes
- view.isChecked = isChecked && requireContext().hasBackgroundPermission()
+ view.isChecked = isChecked && requireContext().hasLocationPermission()
if (view.isPressed) { // We want to ignore changes caused by code (as opposed to the user)
debug("User changed location tracking to $isChecked")
@@ -255,9 +247,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
.setPositiveButton(getString(R.string.accept)) { _, _ ->
// Make sure we have location permission (prerequisite)
if (!requireContext().hasLocationPermission()) {
- requestLocationAndBackgroundLauncher.launch(requireContext().getLocationPermissions())
- } else {
- requestBackgroundAndCheckLauncher.launch(requireContext().getBackgroundPermissions())
+ requestLocationPermissionLauncher.launch(requireContext().getLocationPermissions())
}
}
.show()
diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt
index 801819870..cf0308afa 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt
@@ -182,21 +182,6 @@ fun MapView(
if (permissions.entries.all { it.value }) map.toggleMyLocation()
}
- fun requestPermissionAndToggle() {
- // Google rejects releases claiming this requires BACKGROUND_LOCATION prominent
- // disclosure. Adding to comply even though it does not use background location.
- MaterialAlertDialogBuilder(context)
- .setTitle(R.string.background_required)
- .setMessage(R.string.why_background_required)
- .setNeutralButton(R.string.cancel) { _, _ ->
- debug("User denied location permission")
- }
- .setPositiveButton(R.string.accept) { _, _ ->
- requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
- }
- .show()
- }
-
val nodes by model.nodeList.collectAsStateWithLifecycle()
val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap())
@@ -667,7 +652,7 @@ fun MapView(
IconButton(
onClick = {
if (context.hasLocationPermission()) map.toggleMyLocation()
- else requestPermissionAndToggle()
+ else requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
},
enabled = hasGps,
drawableRes = if (myLocationOverlay == null) R.drawable.ic_twotone_my_location_24