mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-22 08:54:21 +01:00
Change scanner to CompanionDeviceManager
This commit is contained in:
@@ -1,15 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="no.nordicsemi.android.scanner">
|
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-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
<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" />
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
|
||||||
</manifest>
|
</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.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import no.nordicsemi.android.events.exhaustive
|
import no.nordicsemi.android.events.exhaustive
|
||||||
import no.nordicsemi.android.scanner.bluetooth.BluetoothNotAvailableScreen
|
import no.nordicsemi.android.scanner.tools.ScannerStatus
|
||||||
import no.nordicsemi.android.scanner.bluetooth.BluetoothNotEnabledScreen
|
import no.nordicsemi.android.scanner.ui.BluetoothNotAvailableScreen
|
||||||
import no.nordicsemi.android.scanner.permissions.RequestPermissionScreen
|
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
|
@Composable
|
||||||
fun ScannerRoute(navController: NavController) {
|
fun ScannerRoute(navController: NavController) {
|
||||||
@@ -35,15 +39,6 @@ private fun ScannerScreen(
|
|||||||
ScannerStatus.PERMISSION_REQUIRED -> RequestPermissionScreen { onEvent(ScannerViewEvent.PERMISSION_CHECKED) }
|
ScannerStatus.PERMISSION_REQUIRED -> RequestPermissionScreen { onEvent(ScannerViewEvent.PERMISSION_CHECKED) }
|
||||||
ScannerStatus.NOT_AVAILABLE -> BluetoothNotAvailableScreen()
|
ScannerStatus.NOT_AVAILABLE -> BluetoothNotAvailableScreen()
|
||||||
ScannerStatus.DISABLED -> BluetoothNotEnabledScreen { onEvent(ScannerViewEvent.BLUETOOTH_ENABLED) }
|
ScannerStatus.DISABLED -> BluetoothNotEnabledScreen { onEvent(ScannerViewEvent.BLUETOOTH_ENABLED) }
|
||||||
ScannerStatus.ENABLED -> {
|
ScannerStatus.ENABLED -> ScanDeviceScreen(navController)
|
||||||
onEvent(ScannerViewEvent.ENABLE_SCANNING)
|
|
||||||
ListOfDevicesScreen {
|
|
||||||
navController.previousBackStackEntry
|
|
||||||
?.savedStateHandle
|
|
||||||
?.set("result", it)
|
|
||||||
navController.popBackStack()
|
|
||||||
onEvent(ScannerViewEvent.DISABLE_SCANNING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package no.nordicsemi.android.scanner
|
package no.nordicsemi.android.scanner.tools
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package no.nordicsemi.android.scanner
|
package no.nordicsemi.android.scanner.tools
|
||||||
|
|
||||||
enum class ScannerStatus {
|
enum class ScannerStatus {
|
||||||
PERMISSION_REQUIRED, ENABLED, DISABLED, NOT_AVAILABLE
|
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.app.Activity
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package no.nordicsemi.android.scanner
|
package no.nordicsemi.android.scanner.ui
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import no.nordicsemi.android.events.exhaustive
|
import no.nordicsemi.android.events.exhaustive
|
||||||
|
import no.nordicsemi.android.scanner.tools.NordicBleScanner
|
||||||
|
import no.nordicsemi.android.scanner.tools.ScannerStatus
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -20,8 +22,6 @@ internal class NordicBleScannerViewModel @Inject constructor(
|
|||||||
when (event) {
|
when (event) {
|
||||||
ScannerViewEvent.PERMISSION_CHECKED -> onPermissionChecked()
|
ScannerViewEvent.PERMISSION_CHECKED -> onPermissionChecked()
|
||||||
ScannerViewEvent.BLUETOOTH_ENABLED -> onBluetoothEnabled()
|
ScannerViewEvent.BLUETOOTH_ENABLED -> onBluetoothEnabled()
|
||||||
ScannerViewEvent.ENABLE_SCANNING -> bleScanner.startScanning()
|
|
||||||
ScannerViewEvent.DISABLE_SCANNING -> bleScanner.stopScanning()
|
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ internal class NordicBleScannerViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class ScannerViewEvent {
|
enum class ScannerViewEvent {
|
||||||
PERMISSION_CHECKED, BLUETOOTH_ENABLED, ENABLE_SCANNING, DISABLE_SCANNING
|
PERMISSION_CHECKED, BLUETOOTH_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class NordicBleScannerState(
|
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.Context
|
||||||
import android.content.Intent
|
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