diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 267dbdc7..1041f070 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -189,7 +189,7 @@ class MainActivity : BaseActivity(), Logging, /** Get the minimum permissions our app needs to run correctly */ - private fun getMinimumPermissions(): List { + private fun getMinimumPermissions(): Array { 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 = getMinimumPermissions() + missingPerms: Array = 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 = getMinimumPermissions(), + missingPerms: Array = 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 ) } 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 d16c0758..8a9edf94 100644 --- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt +++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt @@ -47,17 +47,17 @@ fun Context.hasGps(): Boolean = /** * return a list of the permissions we don't have */ -fun Context.getMissingPermissions(perms: List) = perms.filter { +fun Context.getMissingPermissions(perms: List): Array = 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 { +fun Context.getConnectPermissions(): Array { val perms = mutableListOf() /* TODO - wait for targetSdkVersion 31 @@ -76,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 { +fun Context.getScanPermissions(): Array { val perms = mutableListOf() /* TODO - wait for targetSdkVersion 31 @@ -101,7 +101,7 @@ fun Context.hasScanPermission() = getScanPermissions().isEmpty() /** * Camera permission (or empty if we already have what we need) */ -fun Context.getCameraPermissions(): List { +fun Context.getCameraPermissions(): Array { val perms = mutableListOf(Manifest.permission.CAMERA) return getMissingPermissions(perms) @@ -113,7 +113,7 @@ fun Context.hasCameraPermission() = getCameraPermissions().isEmpty() /** * Location permission (or empty if we already have what we need) */ -fun Context.getLocationPermissions(): List { +fun Context.getLocationPermissions(): Array { val perms = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION) return getMissingPermissions(perms) @@ -125,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 { - val perms = mutableListOf() +fun Context.getBackgroundPermissions(): Array { + val perms = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION) if (Build.VERSION.SDK_INT >= 29) // only added later perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION) diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt index 8dc87326..443dbc53 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt @@ -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 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 e73896ee..89090079 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -793,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) @@ -805,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) { @@ -940,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)