diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..15655ac --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt index d1c5dc3..cdebebe 100644 --- a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt +++ b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt @@ -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, + 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() + } + } + + } + } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 16313e0..406189e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -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" /> \ No newline at end of file