Issue
I am trying to write a program to communicate with ESP32 modules via bluetooth. For the program to work, Bt must be turned on and the FINE_LOCATION permission granted. I am using API 29.
The code below works, but it can be done much better.
I am a beginner, this is the only way I can do it.
I have a few questions :
Can I use shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
together with ActivityResultContracts.RequestPermission()
, if yes how?
To achieve my goal if the user refuses the first time to grant permissions, I run an almost identical contract with a different dialog.How can this code be reduced?
How to simplify this constant checking:
if (conditions.isReady()) {
buildInterfaceOk()
} else buildInterfaceError()
Half my code seems redundant, I don't know how to get rid of it.
All these problems actually concern the first run, then everything is fine.
Code:
const val TAG = "DEBUG"
data class Conditions (var isBtEnabled : Boolean , var permissionsOk :Boolean){
fun isReady():Boolean{
if (isBtEnabled && permissionsOk) return true
else return false
}
fun log(){
Log.d("DEBUG","Conditions-> $isBtEnabled , $permissionsOk")
}}
class MainActivity : AppCompatActivity() {
private lateinit var bind: ActivityMainBinding
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var bluetoothAdapter: BluetoothAdapter
private var conditions = Conditions(false, false)
private var requestBluetoothEnable =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
Log.d(TAG, "IT RESULT CODE: ${it.resultCode.toString()}")
//kiedy bt jest wlaczone , result -1 , kiedy wylaczone i wlaczamy i akceptujemy tez -1
//a jak odrzucamy to 0
if (it.resultCode == -1) {
conditions.log()
conditions.isBtEnabled = true
}
if (conditions.isReady()) {
buildInterfaceOk()
} else buildInterfaceError()
}
//use it when user denied first time
private val requestPermissionLocationSecond =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
Log.d(TAG, "Permission granted by contract 2")
conditions.permissionsOk = checkPermissions()
if (conditions.isReady()) {
buildInterfaceOk()
} else buildInterfaceError()
} else {
val builder = AlertDialog.Builder(this@MainActivity)
builder.setTitle("V2 - Hi!")
builder.setMessage(
" Please go to the app settings and manually turn on " +
"\"location permission\". Without this permission, I do not work. "
)
builder.setPositiveButton("Ok") { dialog, which -> }
val dialog: AlertDialog = builder.create()
dialog.show()
Log.d(TAG, " V2-> Permission denied, - contract 2")
}
}
// first try to get permission
private var requestPermissionLocation =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
Log.d(TAG, "Permission granted by contract 1")
conditions.permissionsOk = checkPermissions()
if (conditions.isReady()) {
buildInterfaceOk()
} else buildInterfaceError()
//shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
} else {
Log.d(TAG, "Permission denied by contract 1")
val builder = AlertDialog.Builder(this@MainActivity)
builder.setTitle("V2 - Uprawnienie do lokalizacji")
builder.setMessage("I need these permissions to work with Bt devices ")
builder.setPositiveButton("YES") { dialog, which ->
requestPermissionLocationSecond.launch(android.Manifest.permission.ACCESS_FINE_LOCATION)
}
builder.setNegativeButton("No") { dialog, which -> }
val dialog: AlertDialog = builder.create()
dialog.show()
conditions.permissionsOk = checkPermissions()
if (conditions.isReady()) {
buildInterfaceOk()
} else buildInterfaceError()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
bind = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(bind.root)
Log.d(TAG, "BUild version : ${Build.VERSION.SDK_INT} -> ${Build.VERSION.CODENAME}")
val currentDebug = getString(R.string.app_name)
Log.d(TAG, "CURRENT DEBUG : $currentDebug")
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
requestBluetoothEnable.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))
requestPermissionLocation.launch(Manifest.permission.ACCESS_FINE_LOCATION)
conditions.isBtEnabled = bluetoothAdapter.isEnabled
conditions.permissionsOk = checkPermissions()
Log.d(TAG, "FIRST conditions check :")
if (conditions.isReady()) {
conditions.log()
buildInterfaceOk()
}
}
private fun buildInterfaceOk() {
Log.d(TAG, "BUILDING INTERFACE : all is fine")
bind.tvInfo.text = "All is fine i can build interface"
}
private fun buildInterfaceError() {
Log.d(TAG, "BUILDING INTERFACE : errors")
bind.tvInfo.text = "Some errors..."
}
private fun checkPermissions(): Boolean {
val permissionsRequired =
arrayOf(
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
//Manifest.permission.BLUETOOTH_CONNECT, //to znow wymagane od S(API 31) ??
//Manifest.permission.BLUETOOTH_SCAN, //to znow wymagane od S(API 31) ??
Manifest.permission.ACCESS_FINE_LOCATION
//Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
var permissionsOk = true
permissionsRequired.forEach { requiredPermission ->
if (ContextCompat.checkSelfPermission(
this.applicationContext,
requiredPermission
) == PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "PERMISSION : $requiredPermission -> GRANTED")
} else {
Log.d(TAG, "PERMISSION : $requiredPermission -> NOT GRANTED")
permissionsOk = false
}
}
return permissionsOk
}
}
Solution
What I would do is display an AlertDialog
first saying, you MUST ACCEPT all permissions in order to precede then Request Permissions
until the user agrees to them all.
Check Permission -> Pass -> Start App
Check Permission -> Fail -> Alert Dialog "You must accept all permissions for the app to start."
Request Permission -> Check Permission -> Pass -> Start App
Request Permission -> Check Permission -> Fail -> Request Permission
Request Permission -> Check Permission -> Fail & Never ask again ->
Alert Dialog "Go to setting to turn on permissions" -> onPositive "OK" -> Request Permission
The only problem with this is the user can choose "Never ask again", meaning you can no longer Request Permissions.
Luckily you can tell if the user has chosen "Never ask again", this should get you started
Android M - check runtime permission - how to determine if the user checked "Never ask again"?
This way will encourage code reuse because you are essentially doing the same things in a loop until the user accepts permissions. The only way out of the loop is permission granted across the board.
Answered By - avalerio
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.