location warnings are now much cleaner

This commit is contained in:
Kevin Hester
2021-06-23 11:40:15 -07:00
parent d32f8ad99e
commit 7efaf56f4f
4 changed files with 104 additions and 45 deletions

View File

@@ -25,7 +25,6 @@ import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
@@ -249,38 +248,78 @@ class MainActivity : AppCompatActivity(), Logging,
}
/** 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
*/
fun getMissingMessage(): String? {
val renamedPermissions = mapOf(
// Older versions of android don't know about these permissions - ignore failure to grant
Manifest.permission.ACCESS_COARSE_LOCATION to null,
Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND to null,
Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND to null,
Manifest.permission.ACCESS_FINE_LOCATION to getString(R.string.location)
)
val deniedPermissions = getMinimumPermissions().mapNotNull {
if(renamedPermissions.containsKey(it))
renamedPermissions[it]
else // No localization found - just show the nasty android string
it
}
return if(deniedPermissions.isEmpty())
null
else {
val asEnglish = deniedPermissions.joinToString(" & ")
getString(R.string.permission_missing).format(asEnglish)
}
}
/** Possibly prompt user to grant permissions
*
* @return true if we already have the needed permissions
*/
private fun requestPermission(missingPerms: List<String> = getMinimumPermissions()): Boolean =
if (missingPerms.isNotEmpty()) {
missingPerms.forEach {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, it)) {
// FIXME
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
}
val shouldShow = missingPerms.filter {
ActivityCompat.shouldShowRequestPermissionRationale(this, it)
}
// Ask for all the missing perms
ActivityCompat.requestPermissions(
this,
missingPerms.toTypedArray(),
DID_REQUEST_PERM
)
fun doRequest() {
info("requesting permissions")
// Ask for all the missing perms
ActivityCompat.requestPermissions(
this,
missingPerms.toTypedArray(),
DID_REQUEST_PERM
)
}
if (shouldShow.isNotEmpty()) {
// DID_REQUEST_PERM is an
// app-defined int constant. The callback method gets the
// result of the request.
warn("Permissions $shouldShow missing, we should show dialog")
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.required_permissions))
.setMessage(getMissingMessage())
.setNeutralButton("Cancel (no radio access)") { _, _ ->
error("User bailed due to permissions")
}
.setPositiveButton("Allow (will show dialog)") { _, _ ->
doRequest()
}
.show()
} else {
info("Permissions $missingPerms missing, no need to show dialog, just asking OS")
doRequest()
}
// DID_REQUEST_PERM is an
// app-defined int constant. The callback method gets the
// result of the request.
error("Permissions missing, asked user to grant")
false
} else {
// Permission has already been granted
@@ -295,20 +334,11 @@ class MainActivity : AppCompatActivity(), Logging,
*/
@SuppressLint("InlinedApi") // This function is careful to work with old APIs correctly
fun warnMissingPermissions(): Boolean {
// Older versions of android don't know about these permissions - ignore failure to grant
val ignoredPermissions = setOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND,
Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
)
val message = getMissingMessage()
val deniedPermissions = getMinimumPermissions().filter { name ->
!ignoredPermissions.contains(name)
}
return if (deniedPermissions.isNotEmpty()) {
errormsg("Denied permissions: ${deniedPermissions.joinToString(",")}")
showToast(R.string.permission_missing)
return if (message != null) {
errormsg("Denied permissions: $message")
showToast(message)
true
} else
false

View File

@@ -35,6 +35,7 @@ import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.RadioConfigProtos
import com.geeksville.mesh.android.bluetoothManager
import com.geeksville.mesh.android.hasBackgroundPermission
import com.geeksville.mesh.android.usbManager
import com.geeksville.mesh.databinding.SettingsFragmentBinding
import com.geeksville.mesh.model.UIViewModel
@@ -460,6 +461,8 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
requireContext().getSystemService(CompanionDeviceManager::class.java)
}
private val myActivity get() = requireActivity() as MainActivity
override fun onDestroy() {
guiJob.cancel()
super.onDestroy()
@@ -564,7 +567,11 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
// Update the status string (highest priority messages first)
val info = model.myNodeInfo.value
val statusText = binding.scanStatusText
val permissionsWarning = myActivity.getMissingMessage()
when {
permissionsWarning != null ->
statusText.text = permissionsWarning
region == RadioConfigProtos.RegionCode.Unset ->
statusText.text = getString(R.string.must_set_region)
@@ -643,6 +650,24 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
requireActivity().hideKeyboard()
}
binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked ->
if (view.isPressed && isChecked) { // We want to ignore changes caused by code (as opposed to the user)
debug("User changed location tracking to $isChecked")
view.isChecked =
myActivity.hasBackgroundPermission() // Don't check the box until the system setting changes
if (!view.isChecked)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.background_required)
.setMessage(R.string.why_background_required)
.setNeutralButton(R.string.cancel) { _, _ ->
debug("Decided not to report a bug")
}
.setPositiveButton(getString(R.string.show_system_settings)) { _, _ ->
myActivity.requestBackgroundPermission()
}
.show()
}
}
val app = (requireContext().applicationContext as GeeksvilleApplication)
@@ -689,7 +714,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
binding.scanStatusText.setText(R.string.starting_pairing)
b.isChecked =
scanModel.onSelected(requireActivity() as MainActivity, device)
scanModel.onSelected(myActivity, device)
if (!b.isSelected)
binding.scanStatusText.text = getString(R.string.please_pair)
@@ -812,8 +837,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
.setSingleDevice(false)
.build()
//val mainActivity = requireActivity() as MainActivity
// When the app tries to pair with the Bluetooth device, show the
// appropriate pairing request dialog to the user.
deviceManager.associate(
@@ -901,7 +924,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
}
locationSettingsResponse.addOnSuccessListener {
if(!it.locationSettingsStates.isBleUsable || !it.locationSettingsStates.isLocationUsable)
if (!it.locationSettingsStates.isBleUsable || !it.locationSettingsStates.isLocationUsable)
weNeedAccess()
else
debug("We have location access")
@@ -953,15 +976,16 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
if (!hasCompanionDeviceApi)
scanModel.startScan()
val activity = requireActivity() as MainActivity
// system permissions might have changed while we were away
binding.provideLocationCheckbox.isChecked = myActivity.hasBackgroundPermission()
activity.registerReceiver(updateProgressReceiver, updateProgressFilter)
myActivity.registerReceiver(updateProgressReceiver, updateProgressFilter)
// Keep reminding user BLE is still off
val hasUSB = activity?.let { SerialInterface.findDrivers(it).isNotEmpty() } ?: true
val hasUSB = SerialInterface.findDrivers(myActivity).isNotEmpty()
if (!hasUSB) {
// First warn about permissions, and then if needed warn abotu settings
if(!activity.warnMissingPermissions()) {
// First warn about permissions, and then if needed warn about settings
if (!myActivity.warnMissingPermissions()) {
// Warn user if BLE is disabled
if (scanModel.bluetoothAdapter?.isEnabled != true) {
Toast.makeText(