location warnings are now much cleaner

1.2-legacy
Kevin Hester 2021-06-23 11:40:15 -07:00
rodzic d32f8ad99e
commit 7efaf56f4f
4 zmienionych plików z 104 dodań i 45 usunięć

Wyświetl plik

@ -118,7 +118,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.fragment:fragment-ktx:1.3.4'
implementation 'androidx.fragment:fragment-ktx:1.3.5'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

Wyświetl plik

@ -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

Wyświetl plik

@ -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(

Wyświetl plik

@ -35,7 +35,7 @@
<string name="map_not_allowed">You have analytics disabled. Unfortunately our map provider (mapbox) requires analytics to be allowed for their \'free\' plan. So we have turned off the map view.\n\n
If you would like to see the map, you\'ll need to turn on analytics in the Settings pane (also, for the time being you might need to force restart the application).\n\n
If you are interested in us paying for mapbox (or switching to a different map provider), please post in meshtastic.discourse.group</string>
<string name="permission_missing">A required permission is missing, Meshtastic won\'t be able to work properly. Please enable in Android application settings.</string>
<string name="permission_missing">Meshtastic needs %s permission granted. Without this Android will not allow connecting to the LoRa bluetooth radios.</string>
<string name="radio_sleeping">Radio was sleeping, could not change channel</string>
<string name="report_bug">Report Bug</string>
<string name="report_a_bug">Report a bug</string>
@ -108,4 +108,9 @@
<string name="theme_dark">Dark</string>
<string name="theme_system">System default</string>
<string name="choose_theme_title">Choose theme</string>
<string name="background_required">Background location access required</string>
<string name="show_system_settings">Show system settings</string>
<string name="why_background_required">In order to enable this feature, you must grant this application "allow location access all the time" permission. This allows meshtastic to read your location while the application is in the background, so that it can send your location to other members of your mesh.</string>
<string name="required_permissions">Required permissions</string>
<string name="location">location</string>
</resources>