input_pipeline/
focus_listener.rsuse crate::metrics;
use anyhow::{Context, Error};
use focus_chain_provider::FocusChainProviderPublisher;
use fuchsia_component::client::connect_to_protocol;
use futures::StreamExt;
use metrics_registry::*;
use {fidl_fuchsia_ui_focus as focus, fidl_fuchsia_ui_keyboard_focus as kbd_focus};
pub struct FocusListener {
text_manager: kbd_focus::ControllerProxy,
focus_chain_listener: focus::FocusChainListenerRequestStream,
focus_chain_publisher: FocusChainProviderPublisher,
metrics_logger: metrics::MetricsLogger,
}
impl FocusListener {
pub fn new(
focus_chain_publisher: FocusChainProviderPublisher,
metrics_logger: metrics::MetricsLogger,
) -> Result<Self, Error> {
let text_manager = connect_to_protocol::<kbd_focus::ControllerMarker>()?;
let (focus_chain_listener_client_end, focus_chain_listener) =
fidl::endpoints::create_request_stream::<focus::FocusChainListenerMarker>();
let focus_chain_listener_registry: focus::FocusChainListenerRegistryProxy =
connect_to_protocol::<focus::FocusChainListenerRegistryMarker>()?;
focus_chain_listener_registry
.register(focus_chain_listener_client_end)
.context("Failed to register focus chain listener.")?;
Ok(Self::new_listener(
text_manager,
focus_chain_listener,
focus_chain_publisher,
metrics_logger,
))
}
fn new_listener(
text_manager: kbd_focus::ControllerProxy,
focus_chain_listener: focus::FocusChainListenerRequestStream,
focus_chain_publisher: FocusChainProviderPublisher,
metrics_logger: metrics::MetricsLogger,
) -> Self {
Self { text_manager, focus_chain_listener, focus_chain_publisher, metrics_logger }
}
pub async fn dispatch_focus_changes(&mut self) -> Result<(), Error> {
while let Some(focus_change) = self.focus_chain_listener.next().await {
match focus_change {
Ok(focus::FocusChainListenerRequest::OnFocusChange {
focus_chain,
responder,
..
}) => {
self.focus_chain_publisher
.set_state_and_notify_if_changed(&focus_chain)
.context("while notifying FocusChainProviderPublisher")?;
if let Some(ref focus_chain) = focus_chain.focus_chain {
if let Some(ref view_ref) = focus_chain.last() {
let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
self.text_manager
.notify(view_ref_dup)
.await
.context("while notifying text_manager")?;
}
};
responder.send().context("while sending focus chain listener response")?;
}
Err(e) => self.metrics_logger.log_error(
InputPipelineErrorMetricDimensionEvent::FocusChainListenerRequestError,
std::format!("FocusChainListenerRequest has error: {}.", e),
),
}
}
tracing::warn!("Stopped dispatching focus changes.");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_ui_focus_ext::FocusChainExt;
use fidl_fuchsia_ui_views_ext::ViewRefExt;
use futures::join;
use pretty_assertions::assert_eq;
use {fidl_fuchsia_ui_views as fidl_ui_views, fuchsia_scenic as scenic};
async fn expect_focus_ctl_focus_change(
mut request_stream: kbd_focus::ControllerRequestStream,
) -> fidl_ui_views::ViewRef {
match request_stream.next().await {
Some(Ok(kbd_focus::ControllerRequest::Notify { view_ref, responder, .. })) => {
let _ = responder.send();
view_ref
}
_ => panic!("Error expecting text_manager focus change."),
}
}
async fn expect_focus_koid_chain(
focus_chain_provider_proxy: &focus::FocusChainProviderProxy,
) -> focus::FocusKoidChain {
focus_chain_provider_proxy
.watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
.await
.expect("watch_focus_koid_chain")
}
#[fuchsia_async::run_until_stalled(test)]
async fn dispatch_focus() -> Result<(), Error> {
let (focus_proxy, focus_request_stream) =
fidl::endpoints::create_proxy_and_stream::<kbd_focus::ControllerMarker>();
let (focus_chain_listener_client_end, focus_chain_listener) =
fidl::endpoints::create_proxy_and_stream::<focus::FocusChainListenerMarker>();
let (focus_chain_watcher, focus_chain_provider_stream) =
fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
let (focus_chain_provider_publisher, focus_chain_provider_stream_handler) =
focus_chain_provider::make_publisher_and_stream_handler();
focus_chain_provider_stream_handler
.handle_request_stream(focus_chain_provider_stream)
.detach();
let mut listener = FocusListener::new_listener(
focus_proxy,
focus_chain_listener,
focus_chain_provider_publisher,
metrics::MetricsLogger::default(),
);
fuchsia_async::Task::local(async move {
let _ = listener.dispatch_focus_changes().await;
})
.detach();
let got_focus_koid_chain = expect_focus_koid_chain(&focus_chain_watcher).await;
assert_eq!(got_focus_koid_chain, focus::FocusKoidChain::default());
let view_ref = scenic::ViewRefPair::new()?.view_ref;
let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
let focus_chain =
focus::FocusChain { focus_chain: Some(vec![view_ref]), ..Default::default() };
let (_, view_ref, got_focus_koid_chain) = join!(
focus_chain_listener_client_end.on_focus_change(focus_chain.duplicate().unwrap()),
expect_focus_ctl_focus_change(focus_request_stream),
expect_focus_koid_chain(&focus_chain_watcher),
);
assert_eq!(view_ref.get_koid().unwrap(), view_ref_dup.get_koid().unwrap(),);
assert!(focus_chain.equivalent(&got_focus_koid_chain).unwrap());
Ok(())
}
}