feat: make sure app is allowed to scan for bluetooth devices
This commit is contained in:
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetDropDown">
|
||||||
|
<runningDeviceTargetSelectedWithDropDown>
|
||||||
|
<Target>
|
||||||
|
<type value="RUNNING_DEVICE_TARGET" />
|
||||||
|
<deviceKey>
|
||||||
|
<Key>
|
||||||
|
<type value="SERIAL_NUMBER" />
|
||||||
|
<value value="a4566948" />
|
||||||
|
</Key>
|
||||||
|
</deviceKey>
|
||||||
|
</Target>
|
||||||
|
</runningDeviceTargetSelectedWithDropDown>
|
||||||
|
<timeTargetWasSelectedWithDropDown value="2023-06-14T13:19:30.001338Z" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -3,27 +3,45 @@ package com.example.sensortestingapp
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.bluetooth.BluetoothManager
|
import android.bluetooth.BluetoothManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
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.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.navigateUp
|
import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.example.sensortestingapp.databinding.ActivityMainBinding
|
import com.example.sensortestingapp.databinding.ActivityMainBinding
|
||||||
|
|
||||||
private const val ENABLE_BLUETOOTH_REQUEST_CODE = 1
|
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() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
@@ -48,9 +66,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
|
||||||
binding.fab.setOnClickListener { view ->
|
binding.fab.setOnClickListener { view ->
|
||||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
startBleScan()
|
||||||
.setAnchorView(R.id.fab)
|
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null).show()
|
// .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")
|
@SuppressLint("MissingPermission")
|
||||||
private fun promptEnableBluetooth() {
|
private fun promptEnableBluetooth() {
|
||||||
if (!bluetoothAdapter.isEnabled) {
|
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_gravity="bottom|end"
|
||||||
android:layout_marginEnd="@dimen/fab_margin"
|
android:layout_marginEnd="@dimen/fab_margin"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
app:srcCompat="@android:drawable/stat_sys_data_bluetooth" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
Reference in New Issue
Block a user