refactor permissions

pull/475/head
andrekir 2022-09-03 11:07:10 -03:00
rodzic 40313ddca6
commit 4f4750c339
11 zmienionych plików z 67 dodań i 218 usunięć

Wyświetl plik

@ -1,7 +1,6 @@
package com.geeksville.mesh
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.content.*
@ -34,7 +33,7 @@ import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.android.ServiceClient
import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.android.*
import com.geeksville.mesh.android.getMissingPermissions
import com.geeksville.mesh.databinding.ActivityMainBinding
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.BluetoothViewModel
@ -116,16 +115,7 @@ eventually:
val utf8: Charset = Charset.forName("UTF-8")
@AndroidEntryPoint
class MainActivity : BaseActivity(), Logging,
ActivityCompat.OnRequestPermissionsResultCallback {
companion object {
// const val REQUEST_ENABLE_BT = 10
const val DID_REQUEST_PERM = 11
// const val RC_SIGN_IN = 12 // google signin completed
// const val SELECT_DEVICE_REQUEST_CODE = 13
// const val CREATE_CSV_FILE = 14
}
class MainActivity : BaseActivity(), Logging {
private lateinit var binding: ActivityMainBinding
@ -139,6 +129,15 @@ class MainActivity : BaseActivity(), Logging,
@Inject
internal lateinit var radioInterfaceService: RadioInterfaceService
private val requestPermissionsLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (!permissions.entries.all { it.value }) {
errormsg("User denied permissions")
showSnackbar(getString(R.string.permission_missing_31))
}
bluetoothViewModel.permissionsUpdated()
}
data class TabInfo(val text: String, val icon: Int, val content: Fragment)
// private val tabIndexes = generateSequence(0) { it + 1 } FIXME, instead do withIndex or zip? to get the ids below, also stop duplicating strings
@ -188,74 +187,28 @@ class MainActivity : BaseActivity(), Logging,
/** Get the minimum permissions our app needs to run correctly
*/
private fun getMinimumPermissions(): Array<String> {
val perms = mutableListOf(
Manifest.permission.WAKE_LOCK
val perms = mutableListOf<String>()
// We only need this for logging to capture files for the simulator - turn off for most users
// Manifest.permission.WRITE_EXTERNAL_STORAGE
)
// We only need this for logging to capture files for the simulator - turn off for production
// perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
/* TODO - wait for targetSdkVersion 31
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
perms.add(Manifest.permission.BLUETOOTH_SCAN)
perms.add(Manifest.permission.BLUETOOTH_CONNECT)
} else {
perms.add(Manifest.permission.BLUETOOTH)
}
*/
perms.add(Manifest.permission.BLUETOOTH)
// Some old phones complain about requesting perms they don't understand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
perms.add(Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
perms.add(Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)
}
return getMissingPermissions(perms)
}
/** Ask the user to grant Bluetooth scan/discovery permission */
fun requestScanPermission() = requestPermission(getScanPermissions(), true)
/**
* @return a localized string warning user about missing permissions. Or null if everything is find
*/
@SuppressLint("InlinedApi")
fun getMissingMessage(
missingPerms: Array<String> = getMinimumPermissions()
): 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),
Manifest.permission.BLUETOOTH_CONNECT to "Bluetooth"
)
val deniedPermissions = missingPerms.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
* @param shouldShowDialog usually false in cases where we've already shown a dialog elsewhere we skip it.
* @param shouldShowDialog usually true, but in cases where we've already shown a dialog elsewhere we skip it.
*
* @return true if we already have the needed permissions
*/
private fun requestPermission(
missingPerms: Array<String> = getMinimumPermissions(),
shouldShowDialog: Boolean = false
shouldShowDialog: Boolean = true
): Boolean =
if (missingPerms.isNotEmpty()) {
val shouldShow = missingPerms.filter {
@ -265,11 +218,7 @@ class MainActivity : BaseActivity(), Logging,
fun doRequest() {
info("requesting permissions")
// Ask for all the missing perms
ActivityCompat.requestPermissions(
this,
missingPerms,
DID_REQUEST_PERM
)
requestPermissionsLauncher.launch(missingPerms)
}
if (shouldShow.isNotEmpty() && shouldShowDialog) {
@ -280,7 +229,7 @@ class MainActivity : BaseActivity(), Logging,
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.required_permissions))
.setMessage(getMissingMessage(missingPerms))
.setMessage(getString(R.string.permission_missing_31))
.setNeutralButton(R.string.cancel) { _, _ ->
warn("User bailed due to permissions")
}
@ -300,81 +249,6 @@ class MainActivity : BaseActivity(), Logging,
true
}
/**
* Remind user he's disabled permissions we need
*
* @return true if we did warn
*/
@SuppressLint("InlinedApi") // This function is careful to work with old APIs correctly
fun warnMissingPermissions(): Boolean {
val message = getMissingMessage()
return if (message != null) {
errormsg("Denied permissions: $message")
showSnackbar(message)
true
} else
false
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
DID_REQUEST_PERM -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)
) {
// Permission is granted. Continue the action or workflow
// in your app.
// yay!
} else {
// Explain to the user that the feature is unavailable because
// the features requires a permission that the user has denied.
// At the same time, respect the user's decision. Don't link to
// system settings in an effort to convince the user to change
// their decision.
warnMissingPermissions()
}
}
else -> {
// ignore other requests
}
}
bluetoothViewModel.permissionsUpdated()
}
private fun sendTestPackets() {
exceptionReporter {
val m = model.meshService!!
// Do some test operations
val testPayload = "hello world".toByteArray()
m.send(
DataPacket(
"+16508675310",
testPayload,
Portnums.PortNum.PRIVATE_APP_VALUE
)
)
m.send(
DataPacket(
"+16508675310",
testPayload,
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE
)
)
}
}
/// Ask user to rate in play store
private fun askToRate() {
exceptionReporter { // Got one IllegalArgumentException from inside this lib, but we don't want to crash our app because of bugs in this optional feature
@ -417,23 +291,6 @@ class MainActivity : BaseActivity(), Logging,
/// Set theme
setUITheme(prefs)
/* not yet working
// Configure sign-in to request the user's ID, email address, and basic
// profile. ID and basic profile are included in DEFAULT_SIGN_IN.
val gso =
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
// Build a GoogleSignInClient with the options specified by gso.
UIState.googleSignInClient = GoogleSignIn.getClient(this, gso);
*/
/* setContent {
MeshApp()
} */
setContentView(binding.root)
initToolbar()

Wyświetl plik

@ -55,48 +55,22 @@ fun Context.getMissingPermissions(perms: List<String>): Array<String> = perms.fi
}.toTypedArray()
/**
* Bluetooth connect permissions (or empty if we already have what we need)
* Bluetooth permissions (or empty if we already have what we need)
*/
fun Context.getConnectPermissions(): Array<String> {
fun Context.getBluetoothPermissions(): Array<String> {
val perms = mutableListOf<String>()
/* TODO - wait for targetSdkVersion 31
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
perms.add(Manifest.permission.BLUETOOTH_SCAN)
perms.add(Manifest.permission.BLUETOOTH_CONNECT)
} else {
perms.add(Manifest.permission.BLUETOOTH)
}
*/
return getMissingPermissions(perms)
}
/** @return true if the user already has Bluetooth connect permission */
fun Context.hasConnectPermission() = getConnectPermissions().isEmpty()
/**
* Bluetooth scan/discovery permissions (or empty if we already have what we need)
*/
fun Context.getScanPermissions(): Array<String> {
val perms = mutableListOf<String>()
/* TODO - wait for targetSdkVersion 31
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
perms.add(Manifest.permission.BLUETOOTH_SCAN)
} else if (!BluetoothInterface.hasCompanionDeviceApi(this)) {
perms.add(Manifest.permission.ACCESS_FINE_LOCATION)
perms.add(Manifest.permission.BLUETOOTH_ADMIN)
}
*/
if (!hasCompanionDeviceApi()) {
perms.add(Manifest.permission.ACCESS_FINE_LOCATION)
perms.add(Manifest.permission.BLUETOOTH_ADMIN)
}
return getMissingPermissions(perms)
}
/** @return true if the user already has Bluetooth scan/discovery permission */
fun Context.hasScanPermission() = getScanPermissions().isEmpty()
fun Context.hasBluetoothPermission() = getBluetoothPermissions().isEmpty()
/**
* Camera permission (or empty if we already have what we need)

Wyświetl plik

@ -131,7 +131,7 @@ class BTScanModel @Inject constructor(
private val bluetoothAdapter = context.bluetoothManager?.adapter
private val deviceManager get() = context.deviceManager
val hasCompanionDeviceApi get() = context.hasCompanionDeviceApi()
private val hasConnectPermission get() = application.hasConnectPermission()
private val hasBluetoothPermission get() = application.hasBluetoothPermission()
private val usbManager get() = context.usbManager
var selectedAddress: String? = null

Wyświetl plik

@ -9,7 +9,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import com.geeksville.android.Logging
import com.geeksville.mesh.CoroutineDispatchers
import com.geeksville.mesh.android.hasConnectPermission
import com.geeksville.mesh.android.hasBluetoothPermission
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -66,7 +66,7 @@ class BluetoothRepository @Inject constructor(
@SuppressLint("MissingPermission")
internal suspend fun updateBluetoothState() {
val newState: BluetoothState = bluetoothAdapterLazy.get()?.takeIf {
application.hasConnectPermission().also { hasPerms ->
application.hasBluetoothPermission().also { hasPerms ->
if (!hasPerms) errormsg("Still missing needed bluetooth permissions")
}
}?.let { adapter ->

Wyświetl plik

@ -179,19 +179,16 @@ 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
connected == MeshService.ConnectionState.CONNECTED -> {
when (connected) {
MeshService.ConnectionState.CONNECTED -> {
statusText.text = if (region.number == 0) getString(R.string.must_set_region)
else getString(R.string.connected_to).format(info?.firmwareString ?: "unknown")
}
connected == MeshService.ConnectionState.DISCONNECTED ->
MeshService.ConnectionState.DISCONNECTED ->
statusText.text = getString(R.string.not_connected)
connected == MeshService.ConnectionState.DEVICE_SLEEP ->
MeshService.ConnectionState.DEVICE_SLEEP ->
statusText.text = getString(R.string.connected_sleeping)
else -> {}
}
}
@ -480,14 +477,40 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
initCommonUI()
val requestPermissionAndScanLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.entries.all { it.value }) {
checkLocationEnabled()
scanLeDevice()
} else {
errormsg("User denied scan permissions")
showSnackbar(getString(R.string.permission_missing))
}
}
binding.changeRadioButton.setOnClickListener {
debug("User clicked changeRadioButton")
if (!myActivity.hasScanPermission()) {
myActivity.requestScanPermission()
} else {
checkBTEnabled()
if (!scanModel.hasCompanionDeviceApi) checkLocationEnabled()
checkBTEnabled()
if ((scanModel.hasCompanionDeviceApi)) {
scanLeDevice()
} else {
// Location is the only runtime permission for classic bluetooth scan
if (myActivity.hasLocationPermission()) {
checkLocationEnabled()
scanLeDevice()
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.required_permissions))
.setMessage(getString(R.string.permission_missing))
.setNeutralButton(R.string.cancel) { _, _ ->
warn("User bailed due to permissions")
}
.setPositiveButton(R.string.accept) { _, _ ->
info("requesting scan permissions")
requestPermissionAndScanLauncher.launch(myActivity.getLocationPermissions())
}
.show()
}
}
}
}

Wyświetl plik

@ -113,7 +113,6 @@
<string name="show_system_settings">시스템 설정 보기</string>
<string name="why_background_required">기능을위해서, 옵션에서 위치권한을 \"항상 허용\" 으로 설정해야합니다.앱이 닫혀있거나 사용중이지 않을때에도 메쉬태스틱이 당신의 스마트폰의 위치를 읽어 당신의 메쉬의 다른 사용자에게 위치를 전송할수있게합니다.</string>
<string name="required_permissions">권한부여 필요</string>
<string name="location">장소</string>
<string name="cancel_no_radio">취소 (no radio access)</string>
<string name="allow_will_show">허용 (대화에 보일것입니다)</string>
<string name="provide_location_to_mesh">메쉬에 현재 위치 공유</string>

Wyświetl plik

@ -26,7 +26,7 @@
<string name="are_you_sure_channel">Are you sure you want to change the channel? All communication with other nodes will stop until you share the new channel settings.</string>
<string name="new_channel_rcvd">Odebrano nowy URL kanału</string>
<string name="do_you_want_switch">Chcesz przełączyć na \'%s\' kanał?</string>
<string name="permission_missing">Meshtastic potrzebuje %s zezwolenie i lokalizacja muszą być włączone, aby można było znaleźć nowe urządzenia przez Bluetooth. Możesz go później wyłączyć.</string>
<string name="permission_missing">Meshtastic potrzebuje lokalizacja zezwolenie i lokalizacja muszą być włączone, aby można było znaleźć nowe urządzenia przez Bluetooth. Możesz go później wyłączyć.</string>
<string name="radio_sleeping">Radio było w trybie uśpienia, nie mogło zmienić kanału</string>
<string name="report_bug">Zgłoś bug</string>
<string name="report_a_bug">Zgłoś bug</string>
@ -105,7 +105,6 @@
<string name="background_required">Lokalizacja w tle</string>
<string name="show_system_settings">Pokaż ustawienia systemowe</string>
<string name="required_permissions">Wymagane uprawnienia</string>
<string name="location">Lokalizacja</string>
<string name="provide_location_to_mesh">Podaj lokalizację do sieci</string>
<plurals name="delete_messages">
<item quantity="one">Usunąć wiadomość?</item>

Wyświetl plik

@ -26,7 +26,7 @@
<string name="are_you_sure_channel">Tem certeza que deseja mudar de canal? Toda comunicação com os outros dispositivos será interrompida até serem compartilhadas as novas configurações do canal.</string>
<string name="new_channel_rcvd">Novo link de canal recebido</string>
<string name="do_you_want_switch">Deseja mudar para o canal \'%s\'?</string>
<string name="permission_missing">Meshtastic precisa da permissão de %s e da localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="permission_missing">Meshtastic precisa da permissão de localização e localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="radio_sleeping">Rádio estava em suspensão (sleep), não foi possível mudar de canal</string>
<string name="report_bug">Informar Bug</string>
<string name="report_a_bug">Informar um bug</string>
@ -102,7 +102,6 @@
<string name="show_system_settings">Exibir configurações do sistema</string>
<string name="why_background_required">Para este recurso, você deve conceder permissão para acessar Local com a opção \"Permitir o tempo todo\".\nIsto permite ao Meshtastic ler a localização do seu smartphone e enviar aos membros da sua mesh, mesmo quando o aplicativo está fechado ou não em uso.</string>
<string name="required_permissions">Permissões necessárias</string>
<string name="location">localização</string>
<string name="cancel_no_radio">Cancelar (sem acesso ao rádio)</string>
<string name="allow_will_show">Permitir (exibe diálogo)</string>
<string name="provide_location_to_mesh">Fornecer localização para mesh</string>

Wyświetl plik

@ -25,7 +25,7 @@
<string name="are_you_sure_channel">Tem certeza que deseja mudar de canal? Todas as comunicações com outros nós serão interrompidas até que partilhe as novas configurações do canal.</string>
<string name="new_channel_rcvd">Novo Link Recebido do Canal</string>
<string name="do_you_want_switch">Pretende mudar para o canal \'%s\' ?</string>
<string name="permission_missing">Meshtastic precisa da permissão de %s e da localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="permission_missing">Meshtastic precisa da permissão de localização e localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="radio_sleeping">O rádio estava a dormir, não conseguia mudar de canal</string>
<string name="report_bug">Reportar Bug</string>
<string name="report_a_bug">Reportar a bug</string>
@ -102,7 +102,6 @@
<string name="show_system_settings">Exibir configurações do sistema</string>
<string name="why_background_required">Para este recurso, você deve conceder permissão para acessar Local com a opção \"Permitir o tempo todo\".\nIsto permite ao Meshtastic ler a localização do seu smartphone e enviar aos membros da sua mesh, mesmo quando o aplicativo está fechado ou não em uso.</string>
<string name="required_permissions">Permissões necessárias</string>
<string name="location">localização</string>
<string name="cancel_no_radio">Cancelar (sem acesso ao rádio)</string>
<string name="allow_will_show">Permitir (exibe diálogo)</string>
<string name="provide_location_to_mesh">Fornecer localização para mesh</string>

Wyświetl plik

@ -107,7 +107,6 @@
<string name="show_system_settings">Zobraziť nastavenia systému</string>
<string name="why_background_required">Pre túto možnosť musíte povoliť prístup ku polohe zariadenia v režime \"Vždy povolené\".\nTáto možnosť povolí aplikácii Meshtastic zistiť polohu Vášho zariadenia a odoslať ju členom Vašej siete aj keď je aplikácia Meshtastic vypnutá alebo sa nepoužíva.</string>
<string name="required_permissions">Požadované oprávnenia</string>
<string name="location">poloha</string>
<string name="cancel_no_radio">Zrušiť (žiaden dosah na vysielač)</string>
<string name="allow_will_show">Povoliť (zobrazí potvrdzovací dialóg)</string>
<string name="provide_location_to_mesh">Odoslať polohu do siete</string>

Wyświetl plik

@ -30,7 +30,7 @@
<string name="are_you_sure_channel">Are you sure you want to change the channel? All communication with other nodes will stop until you share the new channel settings.</string>
<string name="new_channel_rcvd">New Channel URL received</string>
<string name="do_you_want_switch">Do you want to switch to the \'%s\' channel?</string>
<string name="permission_missing">Meshtastic needs %s permission and location must be turned on to find new devices via bluetooth. You can turn it off again afterwards.</string>
<string name="permission_missing">Meshtastic needs location permission and location must be turned on to find new devices via Bluetooth. You can turn it off again afterwards.</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,7 +108,6 @@
<string name="show_system_settings">Show system settings</string>
<string name="why_background_required">For this feature, you must grant Location permission option \"Allow all the time\".\nThis allows Meshtastic to read your smartphone location and send it to other members of your mesh, even when the app is closed or not in use.</string>
<string name="required_permissions">Required permissions</string>
<string name="location">location</string>
<string name="cancel_no_radio">Cancel (no radio access)</string>
<string name="allow_will_show">Allow (will show dialog)</string>
<string name="provide_location_to_mesh">Provide location to mesh</string>
@ -155,4 +154,5 @@
<string name="are_you_sure_factory_reset">Are you sure you want to factory reset?</string>
<string name="factory_reset_description">This will clear all device configuration you have done.</string>
<string name="bluetooth_disabled">Bluetooth disabled.</string>
<string name="permission_missing_31">Meshtastic needs Nearby devices permission to find and connect to devices via Bluetooth. You can turn it off when not in use.</string>
</resources>