feat: make sure app is allowed to scan for bluetooth devices
This commit is contained in:
@@ -3,27 +3,45 @@ package com.example.sensortestingapp
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.example.sensortestingapp.databinding.ActivityMainBinding
|
||||
|
||||
private const val ENABLE_BLUETOOTH_REQUEST_CODE = 1
|
||||
|
||||
private const val RUNTIME_PERMISSION_REQUEST_CODE = 2
|
||||
|
||||
fun Context.hasPermission(permissionType: String): Boolean {
|
||||
return ContextCompat.checkSelfPermission(this, permissionType) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun Context.hasRequiredRuntimePermissions(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
hasPermission(Manifest.permission.BLUETOOTH_SCAN) &&
|
||||
hasPermission(Manifest.permission.BLUETOOTH_CONNECT)
|
||||
} else {
|
||||
hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
@@ -48,9 +66,10 @@ class MainActivity : AppCompatActivity() {
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
|
||||
binding.fab.setOnClickListener { view ->
|
||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
.setAnchorView(R.id.fab)
|
||||
.setAction("Action", null).show()
|
||||
startBleScan()
|
||||
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// .setAnchorView(R.id.fab)
|
||||
// .setAction("Action", null).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +113,44 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
when (requestCode) {
|
||||
RUNTIME_PERMISSION_REQUEST_CODE -> {
|
||||
val containsPermanentDenial = permissions.zip(grantResults.toTypedArray()).any {
|
||||
it.second == PackageManager.PERMISSION_DENIED &&
|
||||
!ActivityCompat.shouldShowRequestPermissionRationale(this, it.first)
|
||||
}
|
||||
val containsDenial = grantResults.any { it == PackageManager.PERMISSION_DENIED }
|
||||
val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
|
||||
when {
|
||||
containsPermanentDenial -> {
|
||||
// TODO: Handle permanent denial (e.g., show AlertDialog with justification)
|
||||
// Note: The user will need to navigate to App Settings and manually grant
|
||||
// permissions that were permanently denied
|
||||
}
|
||||
|
||||
containsDenial -> {
|
||||
requestRelevantRuntimePermissions()
|
||||
}
|
||||
|
||||
allGranted && hasRequiredRuntimePermissions() -> {
|
||||
startBleScan()
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Unexpected scenario encountered when handling permissions
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun promptEnableBluetooth() {
|
||||
if (!bluetoothAdapter.isEnabled) {
|
||||
@@ -102,4 +159,93 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun startBleScan() {
|
||||
if (!hasRequiredRuntimePermissions()) {
|
||||
requestRelevantRuntimePermissions()
|
||||
} else { /* TODO: Actually perform scan */
|
||||
}
|
||||
}
|
||||
|
||||
private fun Activity.requestRelevantRuntimePermissions() {
|
||||
if (hasRequiredRuntimePermissions()) {
|
||||
return
|
||||
}
|
||||
when {
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> {
|
||||
requestLocationPermission()
|
||||
}
|
||||
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
requestBluetoothPermissions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestLocationPermission() {
|
||||
|
||||
|
||||
val onClick = { dialog: DialogInterface, which: Int ->
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||
RUNTIME_PERMISSION_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
runOnUiThread {
|
||||
val builder = AlertDialog.Builder(this)
|
||||
|
||||
with(builder)
|
||||
{
|
||||
setTitle("Location permission required")
|
||||
setMessage(
|
||||
"Starting from Android M (6.0), the system requires apps to be granted " +
|
||||
"location access in order to scan for BLE devices."
|
||||
)
|
||||
setCancelable(false)
|
||||
setPositiveButton(
|
||||
android.R.string.ok,
|
||||
DialogInterface.OnClickListener(function = onClick)
|
||||
)
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestBluetoothPermissions() {
|
||||
|
||||
val onClick = { dialog: DialogInterface, which: Int ->
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(
|
||||
Manifest.permission.BLUETOOTH_SCAN,
|
||||
Manifest.permission.BLUETOOTH_CONNECT
|
||||
),
|
||||
RUNTIME_PERMISSION_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
runOnUiThread {
|
||||
val builder = AlertDialog.Builder(this)
|
||||
|
||||
with(builder)
|
||||
{
|
||||
setTitle("Bluetooth permission required")
|
||||
setMessage(
|
||||
"Starting from Android 12, the system requires apps to be granted " +
|
||||
"Bluetooth access in order to scan for and connect to BLE devices."
|
||||
)
|
||||
setCancelable(false)
|
||||
setPositiveButton(
|
||||
android.R.string.ok,
|
||||
DialogInterface.OnClickListener(function = onClick)
|
||||
)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,6 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="@dimen/fab_margin"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
||||
app:srcCompat="@android:drawable/stat_sys_data_bluetooth" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
Reference in New Issue
Block a user