cranpose_services/wearable.rs
1//! Messages and byte streams between an application and the same application
2//! on the paired phone or watch: a watch app streams its microphone to the
3//! phone, and the phone sends its results back.
4//!
5//! On Android the link is the Wear OS Data Layer. An application turns it on
6//! with `cranpose { services.add("wearable") }`, which brings the framework's
7//! Java and Google Play services' wearable library into its build. On other
8//! platforms, and in a build without the service, every call returns
9//! [`WearableError::Unavailable`].
10//!
11//! **Sending.** [`wearable_peers`] lists the connected devices,
12//! [`send_wearable_message`] delivers a small message, and
13//! [`open_wearable_stream`] opens a byte stream for continuous data such as
14//! audio. Each call blocks until the platform answers, so call them off the
15//! UI thread.
16//!
17//! **Receiving.** The application installs one [`WearableReceiver`] with
18//! [`set_wearable_receiver`]. Events arrive on a platform thread. A
19//! [`WearableStream`] is read until it ends, on whatever thread the receiver
20//! hands it to. An event that arrives while no receiver is installed is
21//! dropped.
22
23use std::{
24 io::{Read, Write},
25 sync::Arc,
26};
27
28use crate::registry::ServiceRegistry;
29
30/// A connected phone or watch.
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct WearablePeer {
33 /// The platform's identifier of the device, the `peer` the other calls take.
34 pub id: String,
35 /// The name the person gave the device.
36 pub name: String,
37}
38
39/// Why a call to the paired device did not happen.
40#[derive(thiserror::Error, Debug)]
41pub enum WearableError {
42 /// This platform or this build has no link to a paired device.
43 #[error("there is no link to a paired device in this build")]
44 Unavailable,
45 /// The platform reported a failure, for example a device out of range.
46 #[error("the paired device link failed: {0}")]
47 Failed(String),
48}
49
50/// A byte stream the paired device opened to this application.
51pub struct WearableStream {
52 /// The device that opened it.
53 pub peer: String,
54 /// The path it was opened under.
55 pub path: String,
56 reader: Box<dyn Read + Send>,
57}
58
59impl WearableStream {
60 /// A stream from `peer` under `path`, read through `reader`. Platform
61 /// backends create it.
62 pub fn new(peer: String, path: String, reader: Box<dyn Read + Send>) -> Self {
63 Self { peer, path, reader }
64 }
65}
66
67impl Read for WearableStream {
68 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
69 self.reader.read(buffer)
70 }
71}
72
73/// Something the paired device sent.
74pub enum WearableEvent {
75 /// A message sent with [`send_wearable_message`] on the other device.
76 Message {
77 /// The device that sent it.
78 peer: String,
79 /// The path it was sent under.
80 path: String,
81 /// Its bytes.
82 data: Vec<u8>,
83 },
84 /// A stream opened with [`open_wearable_stream`] on the other device.
85 Stream(WearableStream),
86}
87
88/// Receives what the paired device sends.
89pub trait WearableReceiver: Send + Sync {
90 /// Handles one event, on a platform thread.
91 fn receive(&self, event: WearableEvent);
92}
93
94impl<F: Fn(WearableEvent) + Send + Sync> WearableReceiver for F {
95 fn receive(&self, event: WearableEvent) {
96 self(event);
97 }
98}
99
100/// The platform's link to the paired device, installed by the platform
101/// backend with [`set_platform_wearable_link`].
102pub trait WearableLink: Send + Sync {
103 /// The connected devices.
104 fn peers(&self) -> Result<Vec<WearablePeer>, WearableError>;
105 /// Delivers `data` to `peer` under `path`.
106 fn send_message(&self, peer: &str, path: &str, data: &[u8]) -> Result<(), WearableError>;
107 /// Opens a byte stream to `peer` under `path`.
108 fn open_stream(&self, peer: &str, path: &str) -> Result<Box<dyn Write + Send>, WearableError>;
109}
110
111static PLATFORM: ServiceRegistry<dyn WearableLink> = ServiceRegistry::new();
112static RECEIVER: ServiceRegistry<dyn WearableReceiver> = ServiceRegistry::new();
113
114/// Installs the platform's link, or removes it with `None`; without one,
115/// every call returns [`WearableError::Unavailable`]. Platform backends call it.
116pub fn set_platform_wearable_link(link: Option<Arc<dyn WearableLink>>) {
117 match link {
118 Some(link) => PLATFORM.set(link),
119 None => PLATFORM.clear(),
120 }
121}
122
123fn link() -> Result<Arc<dyn WearableLink>, WearableError> {
124 PLATFORM.get().ok_or(WearableError::Unavailable)
125}
126
127/// The connected phones or watches.
128pub fn wearable_peers() -> Result<Vec<WearablePeer>, WearableError> {
129 link()?.peers()
130}
131
132/// Delivers `data` to the application on `peer` under `path`.
133pub fn send_wearable_message(peer: &str, path: &str, data: &[u8]) -> Result<(), WearableError> {
134 link()?.send_message(peer, path, data)
135}
136
137/// Opens a byte stream to the application on `peer` under `path`; the other
138/// device receives it as [`WearableEvent::Stream`]. Dropping the writer
139/// closes the stream.
140pub fn open_wearable_stream(
141 peer: &str,
142 path: &str,
143) -> Result<Box<dyn Write + Send>, WearableError> {
144 link()?.open_stream(peer, path)
145}
146
147/// Installs the application's receiver of what the paired device sends,
148/// replacing any earlier one, or removes it with `None`; later events are then
149/// dropped.
150pub fn set_wearable_receiver(receiver: Option<Arc<dyn WearableReceiver>>) {
151 match receiver {
152 Some(receiver) => RECEIVER.set(receiver),
153 None => RECEIVER.clear(),
154 }
155}
156
157/// Hands an event from the platform to the application's receiver, or drops
158/// it when there is none. Platform backends call it.
159pub fn publish_wearable_event(event: WearableEvent) {
160 match RECEIVER.get() {
161 Some(receiver) => receiver.receive(event),
162 None => log::warn!(
163 "a paired device sent something, and the application has no wearable receiver"
164 ),
165 }
166}
167
168#[cfg(test)]
169#[path = "tests/wearable_tests.rs"]
170mod tests;