mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: remove BACKGROUND_LOCATION permission
This commit is contained in:
parent
7b44fea81c
commit
3cdbb86fa2
6 changed files with 19 additions and 56 deletions
|
|
@ -29,7 +29,6 @@
|
||||||
<!-- Permissions required for providing location (from phone GPS) to mesh -->
|
<!-- 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_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_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 -->
|
<!-- This permission is required for analytics - and soon the MQTT gateway -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<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 -->
|
<!-- 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" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
<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" />
|
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||||
|
|
||||||
<!-- Needed to open our bluetooth connection to our paired device (after reboot) -->
|
<!-- Needed to open our bluetooth connection to our paired device (after reboot) -->
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
<service
|
<service
|
||||||
android:name="com.geeksville.mesh.service.MeshService"
|
android:name="com.geeksville.mesh.service.MeshService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:foregroundServiceType="connectedDevice"
|
android:foregroundServiceType="connectedDevice|location"
|
||||||
android:exported="true" tools:ignore="ExportedActivity">
|
android:exported="true" tools:ignore="ExportedActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.geeksville.mesh.Service" />
|
<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 */
|
/** @return true if the user already has location permission */
|
||||||
fun Context.hasLocationPermission() = getLocationPermissions().isEmpty()
|
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)
|
* Notification permission (or empty if we already have what we need)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.geeksville.mesh.repository.location
|
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.app.Application
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
import androidx.annotation.RequiresPermission
|
||||||
import androidx.core.location.LocationCompat
|
import androidx.core.location.LocationCompat
|
||||||
import androidx.core.location.LocationListenerCompat
|
import androidx.core.location.LocationListenerCompat
|
||||||
import androidx.core.location.LocationManagerCompat
|
import androidx.core.location.LocationManagerCompat
|
||||||
|
|
@ -10,7 +12,6 @@ import androidx.core.location.LocationRequestCompat
|
||||||
import androidx.core.location.altitude.AltitudeConverterCompat
|
import androidx.core.location.altitude.AltitudeConverterCompat
|
||||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||||
import com.geeksville.mesh.android.Logging
|
import com.geeksville.mesh.android.Logging
|
||||||
import com.geeksville.mesh.android.hasBackgroundPermission
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.asExecutor
|
import kotlinx.coroutines.asExecutor
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
|
@ -32,9 +33,8 @@ class LocationRepository @Inject constructor(
|
||||||
private val _receivingLocationUpdates: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
private val _receivingLocationUpdates: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||||
val receivingLocationUpdates: StateFlow<Boolean> get() = _receivingLocationUpdates
|
val receivingLocationUpdates: StateFlow<Boolean> get() = _receivingLocationUpdates
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
|
||||||
private fun LocationManager.requestLocationUpdates() = callbackFlow {
|
private fun LocationManager.requestLocationUpdates() = callbackFlow {
|
||||||
if (!context.hasBackgroundPermission()) close()
|
|
||||||
|
|
||||||
val intervalMs = 30 * 1000L // 30 seconds
|
val intervalMs = 30 * 1000L // 30 seconds
|
||||||
val minDistanceM = 0f
|
val minDistanceM = 0f
|
||||||
|
|
@ -96,5 +96,6 @@ class LocationRepository @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* Observable flow for location updates
|
* Observable flow for location updates
|
||||||
*/
|
*/
|
||||||
|
@RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
|
||||||
fun getLocations() = locationManager.get().requestLocationUpdates()
|
fun getLocations() = locationManager.get().requestLocationUpdates()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.geeksville.mesh.service
|
package com.geeksville.mesh.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
|
@ -17,7 +18,7 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
||||||
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
||||||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||||
import com.geeksville.mesh.MeshProtos.ToRadio
|
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.MeshLogRepository
|
||||||
import com.geeksville.mesh.database.PacketRepository
|
import com.geeksville.mesh.database.PacketRepository
|
||||||
import com.geeksville.mesh.database.entity.MeshLog
|
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 we're already observing updates, don't register again
|
||||||
if (locationFlow?.isActive == true) return
|
if (locationFlow?.isActive == true) return
|
||||||
|
|
||||||
if (hasBackgroundPermission()) {
|
@SuppressLint("MissingPermission")
|
||||||
|
if (hasLocationPermission()) {
|
||||||
locationFlow = locationRepository.getLocations().onEach { location ->
|
locationFlow = locationRepository.getLocations().onEach { location ->
|
||||||
sendPosition(
|
sendPosition(
|
||||||
position {
|
position {
|
||||||
|
|
@ -299,7 +301,7 @@ class MeshService : Service(), Logging {
|
||||||
serviceNotifications.notifyId,
|
serviceNotifications.notifyId,
|
||||||
notification,
|
notification,
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
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 {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -138,24 +138,16 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
|
|
||||||
private fun initCommonUI() {
|
private fun initCommonUI() {
|
||||||
|
|
||||||
val requestBackgroundAndCheckLauncher =
|
val requestLocationPermissionLauncher =
|
||||||
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 =
|
|
||||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||||
if (permissions.entries.all { it.value }) {
|
if (permissions.entries.all { it.value }) {
|
||||||
// Older versions of android only need Location permission
|
model.provideLocation.value = true
|
||||||
if (!requireContext().hasBackgroundPermission())
|
model.meshService?.startProvideLocation()
|
||||||
requestBackgroundAndCheckLauncher.launch(requireContext().getBackgroundPermissions())
|
|
||||||
} else {
|
} else {
|
||||||
debug("User denied location permission")
|
debug("User denied location permission")
|
||||||
model.showSnackbar(getString(R.string.why_background_required))
|
model.showSnackbar(getString(R.string.why_background_required))
|
||||||
}
|
}
|
||||||
|
bluetoothViewModel.permissionsUpdated()
|
||||||
}
|
}
|
||||||
|
|
||||||
// init our region spinner
|
// init our region spinner
|
||||||
|
|
@ -240,7 +232,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
|
|
||||||
binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked ->
|
binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked ->
|
||||||
// Don't check the box until the system setting changes
|
// 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)
|
if (view.isPressed) { // We want to ignore changes caused by code (as opposed to the user)
|
||||||
debug("User changed location tracking to $isChecked")
|
debug("User changed location tracking to $isChecked")
|
||||||
|
|
@ -255,9 +247,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
.setPositiveButton(getString(R.string.accept)) { _, _ ->
|
.setPositiveButton(getString(R.string.accept)) { _, _ ->
|
||||||
// Make sure we have location permission (prerequisite)
|
// Make sure we have location permission (prerequisite)
|
||||||
if (!requireContext().hasLocationPermission()) {
|
if (!requireContext().hasLocationPermission()) {
|
||||||
requestLocationAndBackgroundLauncher.launch(requireContext().getLocationPermissions())
|
requestLocationPermissionLauncher.launch(requireContext().getLocationPermissions())
|
||||||
} else {
|
|
||||||
requestBackgroundAndCheckLauncher.launch(requireContext().getBackgroundPermissions())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
|
|
|
||||||
|
|
@ -182,21 +182,6 @@ fun MapView(
|
||||||
if (permissions.entries.all { it.value }) map.toggleMyLocation()
|
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 nodes by model.nodeList.collectAsStateWithLifecycle()
|
||||||
val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap())
|
val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap())
|
||||||
|
|
||||||
|
|
@ -667,7 +652,7 @@ fun MapView(
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (context.hasLocationPermission()) map.toggleMyLocation()
|
if (context.hasLocationPermission()) map.toggleMyLocation()
|
||||||
else requestPermissionAndToggle()
|
else requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
|
||||||
},
|
},
|
||||||
enabled = hasGps,
|
enabled = hasGps,
|
||||||
drawableRes = if (myLocationOverlay == null) R.drawable.ic_twotone_my_location_24
|
drawableRes = if (myLocationOverlay == null) R.drawable.ic_twotone_my_location_24
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue