mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-01 12:42:58 -04:00
refactor: remove BACKGROUND_LOCATION permission
This commit is contained in:
@@ -29,7 +29,6 @@
|
||||
<!-- Permissions required for providing location (from phone GPS) to mesh -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
<!-- This permission is required for analytics - and soon the MQTT gateway -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
@@ -48,6 +47,7 @@
|
||||
<!-- We run our mesh code as a foreground service - FIXME, find a way to stop doing this -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
|
||||
<!-- Needed to open our bluetooth connection to our paired device (after reboot) -->
|
||||
@@ -104,7 +104,7 @@
|
||||
<service
|
||||
android:name="com.geeksville.mesh.service.MeshService"
|
||||
android:enabled="true"
|
||||
android:foregroundServiceType="connectedDevice"
|
||||
android:foregroundServiceType="connectedDevice|location"
|
||||
android:exported="true" tools:ignore="ExportedActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.geeksville.mesh.Service" />
|
||||
|
||||
@@ -145,21 +145,6 @@ fun Context.getLocationPermissions(): Array<String> {
|
||||
/** @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<String> {
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -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<Boolean> = MutableStateFlow(false)
|
||||
val receivingLocationUpdates: StateFlow<Boolean> 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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user