[fp-rs] Added bluetooth pairing button, result displayed in alert dialog.

This commit is contained in:
Lucas Silva Shepard
2023-08-13 16:52:28 -07:00
parent bf05ab5db4
commit 0505a23b3f
7 changed files with 146 additions and 13 deletions
+2 -1
View File
@@ -17,7 +17,8 @@ mod common;
use api::{BleAdapter, BleDevice, ClassicDevice};
pub use common::{
BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress,
BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError,
ClassicAddress, PairingResult,
};
cfg_if::cfg_if! {
@@ -16,4 +16,8 @@ abstract class Rust {
Stream<String> eventStream({dynamic hint});
FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta;
Future<String> pair({dynamic hint});
FlutterRustBridgeTaskConstMeta get kPairConstMeta;
}
@@ -57,6 +57,22 @@ class RustImpl implements Rust {
argNames: [],
);
Future<String> pair({dynamic hint}) {
return _platform.executeNormal(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_pair(port_),
parseSuccessData: _wire2api_String,
constMeta: kPairConstMeta,
argValues: [],
hint: hint,
));
}
FlutterRustBridgeTaskConstMeta get kPairConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "pair",
argNames: [],
);
void dispose() {
_platform.dispose();
}
@@ -214,6 +230,18 @@ class RustWire implements FlutterRustBridgeWireBase {
late final _wire_event_stream =
_wire_event_streamPtr.asFunction<void Function(int)>();
void wire_pair(
int port_,
) {
return _wire_pair(
port_,
);
}
late final _wire_pairPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>('wire_pair');
late final _wire_pair = _wire_pairPtr.asFunction<void Function(int)>();
void free_WireSyncReturn(
WireSyncReturn ptr,
) {
+50 -5
View File
@@ -27,11 +27,56 @@ class HomePage extends StatelessWidget {
appBar: AppBar(title: const Text("Fast Pair")),
body: Center(
child: StreamBuilder(
// All Rust functions are called as Future's
stream: api.eventStream(), // The Rust function we are calling.
builder: (context, data) {
if (data.hasData) {
return Text(data.data!); // The string to display
// Retrieve device stream from Rust side.
stream: api.eventStream(),
builder: (context, deviceName) {
if (deviceName.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(deviceName.data!),
OutlinedButton(
onPressed: () => showDialog<String>(
context: context,
// Rust functions are invoked as futures.
builder: (context) => FutureBuilder(
future: api.pair(),
builder: (context, pairResult) {
return pairResult.hasData
? AlertDialog(
title: const Text('Pairing result'),
content: Text(pairResult.data!),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'OK'),
child: const Text('OK'),
)
],
)
: const AlertDialog(
title: Text('Pairing...'),
// Ensures the progress indicator has sensible dimensions,
// otherwise it follows the height/width of the alert dialog.
content: Column(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
width: 50,
height: 50,
child:
CircularProgressIndicator(),
),
],
),
);
})),
child: const Text('Pair'),
),
]);
}
return const Center(
child: CircularProgressIndicator(),
+47 -7
View File
@@ -1,8 +1,8 @@
use std::sync::RwLock;
use bluetooth::{
api::{BleAdapter, BleDevice},
BleDataTypeId, Platform,
api::{BleAdapter, BleDevice, ClassicDevice},
BleAddress, BleDataTypeId, ClassicAddress, PairingResult, Platform,
};
use flutter_rust_bridge::StreamSink;
use futures::executor;
@@ -11,6 +11,9 @@ use tracing::info;
// Sends a device name to Flutter via `StreamSink` FFI layer.
static NAME_STREAM: RwLock<Option<StreamSink<String>>> = RwLock::new(None);
// Saves the currently displayed device's address, to be used for pairing.
static CURR_ADDRESS: RwLock<Option<BleAddress>> = RwLock::new(None);
/// Sets up initial constructs and infinitely polls for advertisements.
pub fn init() {
let run = async {
@@ -34,12 +37,15 @@ pub fn init() {
let ble_device = Platform::new_ble_device(addr).await.unwrap();
let name = ble_device.name().unwrap();
info!("device: {}", name);
match NAME_STREAM.read().unwrap().as_ref() {
Some(s) => {
s.add(name);
if name.contains("LE_WH-1000XM3") {
match NAME_STREAM.read().unwrap().as_ref() {
Some(stream) => {
stream.add(name);
let mut curr_addr = CURR_ADDRESS.write().unwrap();
*curr_addr = Some(addr);
}
None => info!("Stream is None"),
}
None => info!("Stream is None"),
}
}
}
@@ -55,3 +61,37 @@ pub fn event_stream(s: StreamSink<String>) -> Result<(), anyhow::Error> {
*stream = Some(s);
Ok(())
}
/// Attempt classic pairing with device of address `CURR_ADDRESS`.
pub fn pair() -> String {
let result = match CURR_ADDRESS.read().unwrap().as_ref() {
Some(addr) => {
let run = async {
let classic_addr = ClassicAddress::try_from(*addr).unwrap();
let classic_device = Platform::new_classic_device(classic_addr).await.unwrap();
match classic_device.pair().await {
Ok(result) => match result {
PairingResult::Success => String::from("Pairing success!"),
PairingResult::AlreadyPaired => {
String::from("This device is already paired.")
}
PairingResult::AlreadyInProgress => {
String::from("Pairing already in progress.")
}
_ => String::from("Unknown result."),
},
Err(err) => {
format!("Error {}", err)
}
}
};
executor::block_on(run)
}
None => String::from("No device available to pair."),
};
info!(result);
result
}
@@ -11,6 +11,11 @@ pub extern "C" fn wire_event_stream(port_: i64) {
wire_event_stream_impl(port_)
}
#[no_mangle]
pub extern "C" fn wire_pair(port_: i64) {
wire_pair_impl(port_)
}
// Section: allocate functions
// Section: related functions
@@ -42,6 +42,16 @@ fn wire_event_stream_impl(port_: MessagePort) {
move || move |task_callback| event_stream(task_callback.stream_sink::<_, String>()),
)
}
fn wire_pair_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String>(
WrapInfo {
debug_name: "pair",
port: Some(port_),
mode: FfiCallMode::Normal,
},
move || move |task_callback| Ok(pair()),
)
}
// Section: wrapper structs
// Section: static checks