mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-04 14:13:47 -04:00
location warnings are now much cleaner
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user