focus_chain_provider/
lib.rsmod instance_counter;
use crate::instance_counter::InstanceCounter;
use async_utils::hanging_get::server as hanging_get;
use fidl_fuchsia_ui_focus::{
self as focus, FocusChainProviderWatchFocusKoidChainResponder, FocusKoidChain,
};
use fidl_fuchsia_ui_focus_ext::FocusChainExt;
use fuchsia_async as fasync;
use futures::lock::Mutex;
use futures::stream::TryStreamExt;
use futures::TryFutureExt;
use std::sync::Arc;
use tracing::error;
type HangingGetNotifyFn =
Box<dyn Fn(&FocusKoidChain, FocusChainProviderWatchFocusKoidChainResponder) -> bool + Send>;
type HangingGetBroker = hanging_get::HangingGet<
FocusKoidChain,
FocusChainProviderWatchFocusKoidChainResponder,
HangingGetNotifyFn,
>;
type HangingGetPublisher = hanging_get::Publisher<
FocusKoidChain,
FocusChainProviderWatchFocusKoidChainResponder,
HangingGetNotifyFn,
>;
pub fn make_publisher_and_stream_handler(
) -> (FocusChainProviderPublisher, FocusChainProviderRequestStreamHandler) {
let notify_fn: HangingGetNotifyFn =
Box::new(|focus_koid_chain, responder| match responder.send(&focus_koid_chain) {
Ok(()) => true,
Err(e) => {
error!("Failed to send focus chain to client: {e:?}");
false
}
});
let broker = hanging_get::HangingGet::new(FocusKoidChain::default(), notify_fn);
let publisher = broker.new_publisher();
let subscriber_counter = InstanceCounter::new();
(
FocusChainProviderPublisher { publisher },
FocusChainProviderRequestStreamHandler {
broker: Arc::new(Mutex::new(broker)),
subscriber_counter,
},
)
}
#[derive(Clone)]
pub struct FocusChainProviderPublisher {
publisher: HangingGetPublisher,
}
impl FocusChainProviderPublisher {
pub fn set_state_and_notify_if_changed<C: FocusChainExt>(
&self,
new_state: &C,
) -> Result<(), zx::Status> {
let new_state = new_state.to_focus_koid_chain()?;
let publisher = self.publisher.clone();
publisher.update(|old_state| match old_state.as_ref().unwrap().equivalent(&new_state) {
Ok(true) => false,
Ok(false) => {
*old_state = Some(new_state);
true
}
Err(e) => unreachable!("Unexpected state {e:?}"),
});
Ok(())
}
pub fn set_state_and_notify_always<C: FocusChainExt>(
&self,
new_state: &C,
) -> Result<(), zx::Status> {
let publisher = self.publisher.clone();
publisher.set(new_state.to_focus_koid_chain()?);
Ok(())
}
}
#[derive(Clone)]
pub struct FocusChainProviderRequestStreamHandler {
broker: Arc<Mutex<HangingGetBroker>>,
subscriber_counter: InstanceCounter,
}
impl FocusChainProviderRequestStreamHandler {
#[must_use = "The Task must be retained or `.detach()`ed."]
pub fn handle_request_stream(
&self,
mut stream: focus::FocusChainProviderRequestStream,
) -> fasync::Task<()> {
let broker = self.broker.clone();
let counter = self.subscriber_counter.clone();
fasync::Task::local(
async move {
let subscriber = broker.lock().await.new_subscriber();
let _count_token = counter.make_token();
while let Some(req) = stream.try_next().await? {
match req {
focus::FocusChainProviderRequest::WatchFocusKoidChain {
payload: _payload,
responder,
} => {
subscriber.register(responder)?;
}
}
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| error!("{e:#?}")),
)
}
pub fn subscriber_count(&self) -> usize {
self.subscriber_counter.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_ui_focus_test_helpers::make_focus_chain;
#[fuchsia::test]
async fn smoke_test() {
let (publisher, stream_handler) = super::make_publisher_and_stream_handler();
let (client, stream) =
fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
stream_handler.handle_request_stream(stream).detach();
assert_eq!(stream_handler.subscriber_count(), 0);
let received_focus_koid_chain = client
.watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
.await
.expect("watch_focus_koid_chain");
assert!(received_focus_koid_chain.equivalent(&FocusKoidChain::default()).unwrap());
assert_eq!(stream_handler.subscriber_count(), 1);
let (served_focus_chain, _view_ref_controls) = make_focus_chain(2);
publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
let received_focus_koid_chain = client
.watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
.await
.expect("watch_focus_chain");
assert!(received_focus_koid_chain.equivalent(&served_focus_chain).unwrap());
assert_eq!(stream_handler.subscriber_count(), 1);
}
#[fuchsia::test]
async fn only_newest_value_is_sent() {
let (publisher, stream_handler) = super::make_publisher_and_stream_handler();
let (client, stream) =
fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
stream_handler.handle_request_stream(stream).detach();
let received_focus_koid_chain = client
.watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
.await
.expect("watch_focus_koid_chain");
assert!(received_focus_koid_chain.equivalent(&FocusKoidChain::default()).unwrap());
let (served_focus_chain, _view_ref_controls) = make_focus_chain(2);
publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
let (served_focus_chain, _view_ref_controls) = make_focus_chain(3);
publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
let received_focus_koid_chain = client
.watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
.await
.expect("watch_focus_chain");
assert_eq!(received_focus_koid_chain.len(), 3);
assert!(received_focus_koid_chain.equivalent(&served_focus_chain).unwrap());
}
}