kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
commit
801b132d4e
app/src/main/java/com/geeksville/mesh
|
@ -189,7 +189,7 @@ class MainActivity : BaseActivity(), Logging,
|
|||
|
||||
/** Get the minimum permissions our app needs to run correctly
|
||||
*/
|
||||
private fun getMinimumPermissions(): List<String> {
|
||||
private fun getMinimumPermissions(): Array<String> {
|
||||
val perms = mutableListOf(
|
||||
Manifest.permission.WAKE_LOCK
|
||||
|
||||
|
@ -218,18 +218,12 @@ class MainActivity : BaseActivity(), Logging,
|
|||
/** Ask the user to grant Bluetooth scan/discovery permission */
|
||||
fun requestScanPermission() = requestPermission(getScanPermissions(), true)
|
||||
|
||||
/** Ask the user to grant foreground location permission */
|
||||
fun requestLocationPermission() = requestPermission(getLocationPermissions())
|
||||
|
||||
/** Ask the user to grant background location permission */
|
||||
fun requestBackgroundPermission() = requestPermission(getBackgroundPermissions())
|
||||
|
||||
/**
|
||||
* @return a localized string warning user about missing permissions. Or null if everything is find
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
fun getMissingMessage(
|
||||
missingPerms: List<String> = getMinimumPermissions()
|
||||
missingPerms: Array<String> = getMinimumPermissions()
|
||||
): String? {
|
||||
val renamedPermissions = mapOf(
|
||||
// Older versions of android don't know about these permissions - ignore failure to grant
|
||||
|
@ -262,7 +256,7 @@ class MainActivity : BaseActivity(), Logging,
|
|||
* @return true if we already have the needed permissions
|
||||
*/
|
||||
private fun requestPermission(
|
||||
missingPerms: List<String> = getMinimumPermissions(),
|
||||
missingPerms: Array<String> = getMinimumPermissions(),
|
||||
shouldShowDialog: Boolean = false
|
||||
): Boolean =
|
||||
if (missingPerms.isNotEmpty()) {
|
||||
|
@ -275,7 +269,7 @@ class MainActivity : BaseActivity(), Logging,
|
|||
// Ask for all the missing perms
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
missingPerms.toTypedArray(),
|
||||
missingPerms,
|
||||
DID_REQUEST_PERM
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,20 +38,26 @@ fun Context.hasCompanionDeviceApi(): Boolean =
|
|||
packageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)
|
||||
else false
|
||||
|
||||
/**
|
||||
* @return true if the device has a GPS receiver
|
||||
*/
|
||||
fun Context.hasGps(): Boolean =
|
||||
packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)
|
||||
|
||||
/**
|
||||
* return a list of the permissions we don't have
|
||||
*/
|
||||
fun Context.getMissingPermissions(perms: List<String>) = perms.filter {
|
||||
fun Context.getMissingPermissions(perms: List<String>): Array<String> = perms.filter {
|
||||
ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
it
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
/**
|
||||
* Bluetooth connect permissions (or empty if we already have what we need)
|
||||
*/
|
||||
fun Context.getConnectPermissions(): List<String> {
|
||||
fun Context.getConnectPermissions(): Array<String> {
|
||||
val perms = mutableListOf<String>()
|
||||
|
||||
/* TODO - wait for targetSdkVersion 31
|
||||
|
@ -70,7 +76,7 @@ fun Context.hasConnectPermission() = getConnectPermissions().isEmpty()
|
|||
/**
|
||||
* Bluetooth scan/discovery permissions (or empty if we already have what we need)
|
||||
*/
|
||||
fun Context.getScanPermissions(): List<String> {
|
||||
fun Context.getScanPermissions(): Array<String> {
|
||||
val perms = mutableListOf<String>()
|
||||
|
||||
/* TODO - wait for targetSdkVersion 31
|
||||
|
@ -95,7 +101,7 @@ fun Context.hasScanPermission() = getScanPermissions().isEmpty()
|
|||
/**
|
||||
* Camera permission (or empty if we already have what we need)
|
||||
*/
|
||||
fun Context.getCameraPermissions(): List<String> {
|
||||
fun Context.getCameraPermissions(): Array<String> {
|
||||
val perms = mutableListOf(Manifest.permission.CAMERA)
|
||||
|
||||
return getMissingPermissions(perms)
|
||||
|
@ -107,7 +113,7 @@ fun Context.hasCameraPermission() = getCameraPermissions().isEmpty()
|
|||
/**
|
||||
* Location permission (or empty if we already have what we need)
|
||||
*/
|
||||
fun Context.getLocationPermissions(): List<String> {
|
||||
fun Context.getLocationPermissions(): Array<String> {
|
||||
val perms = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
return getMissingPermissions(perms)
|
||||
|
@ -119,8 +125,8 @@ fun Context.hasLocationPermission() = getLocationPermissions().isEmpty()
|
|||
/**
|
||||
* A list of missing background location permissions (or empty if we already have what we need)
|
||||
*/
|
||||
fun Context.getBackgroundPermissions(): List<String> {
|
||||
val perms = mutableListOf<String>()
|
||||
fun Context.getBackgroundPermissions(): Array<String> {
|
||||
val perms = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 29) // only added later
|
||||
perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.ColorMatrix
|
||||
|
@ -25,6 +24,7 @@ import com.geeksville.mesh.AppOnlyProtos
|
|||
import com.geeksville.mesh.ChannelProtos
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.getCameraPermissions
|
||||
import com.geeksville.mesh.android.hasCameraPermission
|
||||
import com.geeksville.mesh.databinding.ChannelFragmentBinding
|
||||
import com.geeksville.mesh.model.Channel
|
||||
|
@ -219,12 +219,11 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
debug("Camera permission denied")
|
||||
}
|
||||
.setPositiveButton(getString(R.string.accept)) { _, _ ->
|
||||
requestPermissionAndScanLauncher.launch(Manifest.permission.CAMERA)
|
||||
requestPermissionAndScanLauncher.launch(requireContext().getCameraPermissions())
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
|
||||
private fun mlkitScan() {
|
||||
debug("Starting ML Kit QR code scanner")
|
||||
val options = GmsBarcodeScannerOptions.Builder()
|
||||
|
@ -368,8 +367,8 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
}
|
||||
|
||||
private val requestPermissionAndScanLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { allowed ->
|
||||
if (allowed) zxingScan()
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||
if (permissions.entries.all { it.value == true }) zxingScan()
|
||||
}
|
||||
|
||||
// Register zxing launcher and result handler
|
||||
|
|
|
@ -9,9 +9,9 @@ import android.companion.AssociationRequest
|
|||
import android.companion.BluetoothDeviceFilter
|
||||
import android.companion.CompanionDeviceManager
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.hardware.usb.UsbManager
|
||||
import android.location.LocationManager
|
||||
import android.net.nsd.NsdServiceInfo
|
||||
import android.os.*
|
||||
import android.view.LayoutInflater
|
||||
|
@ -48,9 +48,6 @@ import com.geeksville.mesh.service.SoftwareUpdateService
|
|||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.geeksville.util.exceptionToSnackbar
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.google.android.gms.location.LocationSettingsRequest
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver
|
||||
|
@ -553,19 +550,11 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
private val bluetoothViewModel: BluetoothViewModel by activityViewModels()
|
||||
private val model: UIViewModel by activityViewModels()
|
||||
|
||||
// FIXME - move this into a standard GUI helper class
|
||||
private val guiJob = Job()
|
||||
|
||||
@Inject
|
||||
internal lateinit var usbRepository: UsbRepository
|
||||
|
||||
private val myActivity get() = requireActivity() as MainActivity
|
||||
|
||||
override fun onDestroy() {
|
||||
guiJob.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun doFirmwareUpdate() {
|
||||
model.meshService?.let { service ->
|
||||
|
||||
|
@ -804,11 +793,9 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked ->
|
||||
if (view.isPressed && isChecked) { // We want to ignore changes caused by code (as opposed to the user)
|
||||
// Don't check the box until the system setting changes
|
||||
view.isChecked = myActivity.hasLocationPermission() && myActivity.hasBackgroundPermission()
|
||||
view.isChecked = myActivity.hasBackgroundPermission()
|
||||
|
||||
if (!myActivity.hasLocationPermission()) // Make sure we have location permission (prerequisite)
|
||||
myActivity.requestLocationPermission()
|
||||
else if (!myActivity.hasBackgroundPermission())
|
||||
if (!myActivity.hasBackgroundPermission())
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.background_required)
|
||||
.setMessage(R.string.why_background_required)
|
||||
|
@ -816,7 +803,12 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
debug("User denied background permission")
|
||||
}
|
||||
.setPositiveButton(getString(R.string.accept)) { _, _ ->
|
||||
myActivity.requestBackgroundPermission()
|
||||
// Make sure we have location permission (prerequisite)
|
||||
if (!myActivity.hasLocationPermission()) {
|
||||
requestLocationAndBackgroundLauncher.launch(myActivity.getLocationPermissions())
|
||||
} else {
|
||||
requestBackgroundAndCheckLauncher.launch(myActivity.getBackgroundPermissions())
|
||||
}
|
||||
}
|
||||
.show()
|
||||
if (view.isChecked) {
|
||||
|
@ -951,6 +943,23 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private val requestLocationAndBackgroundLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||
if (permissions.entries.all { it.value == true }) {
|
||||
// Older versions of android only need Location permission
|
||||
if (myActivity.hasBackgroundPermission()) {
|
||||
binding.provideLocationCheckbox.isChecked = true
|
||||
} else requestBackgroundAndCheckLauncher.launch(myActivity.getBackgroundPermissions())
|
||||
}
|
||||
}
|
||||
|
||||
private val requestBackgroundAndCheckLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||
if (permissions.entries.all { it.value == true }) {
|
||||
binding.provideLocationCheckbox.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
@ -967,62 +976,23 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
// If the user has not turned on location access throw up a toast warning
|
||||
// If the user has not turned on location access throw up a warning
|
||||
private fun checkLocationEnabled(
|
||||
warningReason: String = getString(R.string.location_disabled_warning)
|
||||
) {
|
||||
val locationManager =
|
||||
myActivity.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
var gpsEnabled = false
|
||||
|
||||
val hasGps: Boolean =
|
||||
myActivity.packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)
|
||||
try {
|
||||
gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
} catch (ex: Throwable) {
|
||||
debug("LocationManager GPS_PROVIDER error: ${ex.message}")
|
||||
}
|
||||
|
||||
// FIXME If they don't have google play for now we don't check for location enabled
|
||||
if (hasGps && isGooglePlayAvailable(requireContext())) {
|
||||
// We do this painful process because LocationManager.isEnabled is only SDK28 or latest
|
||||
val builder = LocationSettingsRequest.Builder()
|
||||
builder.setNeedBle(true)
|
||||
|
||||
val request = LocationRequest.create().apply {
|
||||
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
|
||||
}
|
||||
builder.addLocationRequest(request) // Make sure we are granted high accuracy permission
|
||||
|
||||
val locationSettingsResponse = LocationServices.getSettingsClient(requireActivity())
|
||||
.checkLocationSettings(builder.build())
|
||||
|
||||
fun weNeedAccess(warningReason: String) {
|
||||
warn("Telling user we need need location access")
|
||||
showSnackbar(warningReason)
|
||||
}
|
||||
|
||||
locationSettingsResponse.addOnSuccessListener {
|
||||
if (!it.locationSettingsStates?.isBleUsable!! || !it.locationSettingsStates?.isLocationUsable!!)
|
||||
weNeedAccess(warningReason)
|
||||
else
|
||||
debug("We have location access")
|
||||
}
|
||||
|
||||
locationSettingsResponse.addOnFailureListener {
|
||||
errormsg("Failed to get location access")
|
||||
// We always show the toast regardless of what type of exception we receive. Because even non
|
||||
// resolvable api exceptions mean user still needs to fix something.
|
||||
|
||||
///if (exception is ResolvableApiException) {
|
||||
|
||||
// Location settings are not satisfied, but this can be fixed
|
||||
// by showing the user a dialog.
|
||||
|
||||
// Show the dialog by calling startResolutionForResult(),
|
||||
// and check the result in onActivityResult().
|
||||
// exception.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTINGS)
|
||||
|
||||
// For now just punt and show a dialog
|
||||
|
||||
// The context might be gone (if activity is going away) by the time this handler is called
|
||||
weNeedAccess(warningReason)
|
||||
|
||||
//} else
|
||||
// Exceptions.report(exception)
|
||||
}
|
||||
if (myActivity.hasGps() && !gpsEnabled) {
|
||||
warn("Telling user we need need location access")
|
||||
showSnackbar(warningReason)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1063,7 +1033,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
scanModel.setupScan()
|
||||
|
||||
// system permissions might have changed while we were away
|
||||
binding.provideLocationCheckbox.isChecked = myActivity.hasLocationPermission() && myActivity.hasBackgroundPermission() && (model.provideLocation.value ?: false) && isGooglePlayAvailable(requireContext())
|
||||
binding.provideLocationCheckbox.isChecked = myActivity.hasBackgroundPermission() && (model.provideLocation.value ?: false) && isGooglePlayAvailable(requireContext())
|
||||
|
||||
myActivity.registerReceiver(updateProgressReceiver, updateProgressFilter)
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue