feat: make sure app is allowed to scan for bluetooth devices

This commit is contained in:
Fabian Christoffel
2023-06-14 16:14:13 +02:00
parent e704139a07
commit 6752e79515
3 changed files with 171 additions and 8 deletions

17
.idea/deploymentTargetDropDown.xml generated Normal file
View 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>

View File

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

View File

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