mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-22 00:44:26 +01:00
Change scanner to CompanionDeviceManager
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="no.nordicsemi.android.scanner">
|
||||
|
||||
<uses-feature android:name="android.software.companion_device_setup"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
|
||||
tools:ignore="CoarseFineLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
|
||||
</manifest>
|
||||
@@ -1,71 +0,0 @@
|
||||
package no.nordicsemi.android.scanner
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import no.nordicsemi.android.events.exhaustive
|
||||
|
||||
@Composable
|
||||
internal fun ListOfDevicesScreen(onDeviceSelected: (BluetoothDevice) -> Unit) {
|
||||
|
||||
val viewModel = hiltViewModel<NordicBleScannerViewModel>()
|
||||
|
||||
val result = viewModel.scannerResult.collectAsState().value
|
||||
|
||||
when (result) {
|
||||
is DeviceListResult -> DeviceListView(result.devices, onDeviceSelected)
|
||||
is ScanningErrorResult -> ScanningErrorView()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Composable
|
||||
private fun DeviceListView(
|
||||
devices: List<BluetoothDevice>,
|
||||
onDeviceSelected: (BluetoothDevice) -> Unit
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(stringResource(id = R.string.scanner__list_of_devices))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
|
||||
itemsIndexed(devices) { _, device ->
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { onDeviceSelected(device) }
|
||||
) {
|
||||
Column {
|
||||
Text(device.name ?: stringResource(id = R.string.scanner__no_name))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = device.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScanningErrorView() {
|
||||
Text(text = stringResource(id = R.string.scanner__error))
|
||||
}
|
||||
@@ -9,9 +9,13 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import no.nordicsemi.android.events.exhaustive
|
||||
import no.nordicsemi.android.scanner.bluetooth.BluetoothNotAvailableScreen
|
||||
import no.nordicsemi.android.scanner.bluetooth.BluetoothNotEnabledScreen
|
||||
import no.nordicsemi.android.scanner.permissions.RequestPermissionScreen
|
||||
import no.nordicsemi.android.scanner.tools.ScannerStatus
|
||||
import no.nordicsemi.android.scanner.ui.BluetoothNotAvailableScreen
|
||||
import no.nordicsemi.android.scanner.ui.BluetoothNotEnabledScreen
|
||||
import no.nordicsemi.android.scanner.ui.NordicBleScannerViewModel
|
||||
import no.nordicsemi.android.scanner.ui.RequestPermissionScreen
|
||||
import no.nordicsemi.android.scanner.ui.ScanDeviceScreen
|
||||
import no.nordicsemi.android.scanner.ui.ScannerViewEvent
|
||||
|
||||
@Composable
|
||||
fun ScannerRoute(navController: NavController) {
|
||||
@@ -35,15 +39,6 @@ private fun ScannerScreen(
|
||||
ScannerStatus.PERMISSION_REQUIRED -> RequestPermissionScreen { onEvent(ScannerViewEvent.PERMISSION_CHECKED) }
|
||||
ScannerStatus.NOT_AVAILABLE -> BluetoothNotAvailableScreen()
|
||||
ScannerStatus.DISABLED -> BluetoothNotEnabledScreen { onEvent(ScannerViewEvent.BLUETOOTH_ENABLED) }
|
||||
ScannerStatus.ENABLED -> {
|
||||
onEvent(ScannerViewEvent.ENABLE_SCANNING)
|
||||
ListOfDevicesScreen {
|
||||
navController.previousBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("result", it)
|
||||
navController.popBackStack()
|
||||
onEvent(ScannerViewEvent.DISABLE_SCANNING)
|
||||
}
|
||||
}
|
||||
ScannerStatus.ENABLED -> ScanDeviceScreen(navController)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package no.nordicsemi.android.scanner
|
||||
package no.nordicsemi.android.scanner.tools
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
@@ -1,4 +1,4 @@
|
||||
package no.nordicsemi.android.scanner
|
||||
package no.nordicsemi.android.scanner.tools
|
||||
|
||||
enum class ScannerStatus {
|
||||
PERMISSION_REQUIRED, ENABLED, DISABLED, NOT_AVAILABLE
|
||||
@@ -1,4 +1,4 @@
|
||||
package no.nordicsemi.android.scanner.bluetooth
|
||||
package no.nordicsemi.android.scanner.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
@@ -1,9 +1,11 @@
|
||||
package no.nordicsemi.android.scanner
|
||||
package no.nordicsemi.android.scanner.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import no.nordicsemi.android.events.exhaustive
|
||||
import no.nordicsemi.android.scanner.tools.NordicBleScanner
|
||||
import no.nordicsemi.android.scanner.tools.ScannerStatus
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -20,8 +22,6 @@ internal class NordicBleScannerViewModel @Inject constructor(
|
||||
when (event) {
|
||||
ScannerViewEvent.PERMISSION_CHECKED -> onPermissionChecked()
|
||||
ScannerViewEvent.BLUETOOTH_ENABLED -> onBluetoothEnabled()
|
||||
ScannerViewEvent.ENABLE_SCANNING -> bleScanner.startScanning()
|
||||
ScannerViewEvent.DISABLE_SCANNING -> bleScanner.stopScanning()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ internal class NordicBleScannerViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
enum class ScannerViewEvent {
|
||||
PERMISSION_CHECKED, BLUETOOTH_ENABLED, ENABLE_SCANNING, DISABLE_SCANNING
|
||||
PERMISSION_CHECKED, BLUETOOTH_ENABLED
|
||||
}
|
||||
|
||||
internal data class NordicBleScannerState(
|
||||
@@ -1,4 +1,4 @@
|
||||
package no.nordicsemi.android.scanner.permissions
|
||||
package no.nordicsemi.android.scanner.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -0,0 +1,62 @@
|
||||
package no.nordicsemi.android.scanner.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.companion.AssociationRequest
|
||||
import android.companion.BluetoothDeviceFilter
|
||||
import android.companion.CompanionDeviceManager
|
||||
import android.content.Context
|
||||
import android.content.IntentSender
|
||||
import android.os.Build
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavController
|
||||
|
||||
@Composable
|
||||
fun ScanDeviceScreen(navController: NavController,) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
|
||||
.build()
|
||||
|
||||
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
|
||||
.build()
|
||||
|
||||
val deviceManager =
|
||||
LocalContext.current.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
|
||||
|
||||
val contract = ActivityResultContracts.StartIntentSenderForResult()
|
||||
val launcher = rememberLauncherForActivityResult(contract = contract, onResult = {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
val deviceToPair: BluetoothDevice? = it.data?.getParcelableExtra(
|
||||
CompanionDeviceManager.EXTRA_DEVICE)
|
||||
navController.previousBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("result", deviceToPair)
|
||||
navController.popBackStack()
|
||||
}
|
||||
})
|
||||
|
||||
val hasBeenInvoked = remember { mutableStateOf(false) }
|
||||
if (hasBeenInvoked.value) {
|
||||
return
|
||||
}
|
||||
hasBeenInvoked.value = true
|
||||
deviceManager.associate(pairingRequest,
|
||||
object : CompanionDeviceManager.Callback() {
|
||||
override fun onDeviceFound(chooserLauncher: IntentSender) {
|
||||
val request = IntentSenderRequest.Builder(chooserLauncher).build()
|
||||
launcher.launch(request)
|
||||
}
|
||||
|
||||
override fun onFailure(error: CharSequence?) {
|
||||
}
|
||||
}, null)
|
||||
} else {
|
||||
TODO("VERSION.SDK_INT < O")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user