input_pipeline/
focus_listener.rs1use crate::metrics;
6use anyhow::{Context, Error};
7use focus_chain_provider::FocusChainProviderPublisher;
8use fuchsia_component::client::connect_to_protocol;
9use futures::StreamExt;
10use metrics_registry::*;
11use {fidl_fuchsia_ui_focus as focus, fidl_fuchsia_ui_keyboard_focus as kbd_focus};
12
13pub struct FocusListener {
15 text_manager: kbd_focus::ControllerProxy,
17
18 focus_chain_listener: focus::FocusChainListenerRequestStream,
20
21 focus_chain_publisher: FocusChainProviderPublisher,
23
24 metrics_logger: metrics::MetricsLogger,
26}
27
28impl FocusListener {
29 pub fn new(
56 focus_chain_publisher: FocusChainProviderPublisher,
57 metrics_logger: metrics::MetricsLogger,
58 ) -> Result<Self, Error> {
59 let text_manager = connect_to_protocol::<kbd_focus::ControllerMarker>()?;
60
61 let (focus_chain_listener_client_end, focus_chain_listener) =
62 fidl::endpoints::create_request_stream::<focus::FocusChainListenerMarker>();
63
64 let focus_chain_listener_registry: focus::FocusChainListenerRegistryProxy =
65 connect_to_protocol::<focus::FocusChainListenerRegistryMarker>()?;
66 focus_chain_listener_registry
67 .register(focus_chain_listener_client_end)
68 .context("Failed to register focus chain listener.")?;
69
70 Ok(Self::new_listener(
71 text_manager,
72 focus_chain_listener,
73 focus_chain_publisher,
74 metrics_logger,
75 ))
76 }
77
78 fn new_listener(
89 text_manager: kbd_focus::ControllerProxy,
90 focus_chain_listener: focus::FocusChainListenerRequestStream,
91 focus_chain_publisher: FocusChainProviderPublisher,
92 metrics_logger: metrics::MetricsLogger,
93 ) -> Self {
94 Self { text_manager, focus_chain_listener, focus_chain_publisher, metrics_logger }
95 }
96
97 pub async fn dispatch_focus_changes(&mut self) -> Result<(), Error> {
99 while let Some(focus_change) = self.focus_chain_listener.next().await {
100 match focus_change {
101 Ok(focus::FocusChainListenerRequest::OnFocusChange {
102 focus_chain,
103 responder,
104 ..
105 }) => {
106 self.focus_chain_publisher
108 .set_state_and_notify_if_changed(&focus_chain)
109 .context("while notifying FocusChainProviderPublisher")?;
110
111 if let Some(ref focus_chain) = focus_chain.focus_chain {
113 if let Some(ref view_ref) = focus_chain.last() {
114 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
115 self.text_manager
116 .notify(view_ref_dup)
117 .await
118 .context("while notifying text_manager")?;
119 }
120 };
121
122 responder.send().context("while sending focus chain listener response")?;
123 }
124 Err(e) => self.metrics_logger.log_error(
125 InputPipelineErrorMetricDimensionEvent::FocusChainListenerRequestError,
126 std::format!("FocusChainListenerRequest has error: {}.", e),
127 ),
128 }
129 }
130 log::warn!("Stopped dispatching focus changes.");
131 Ok(())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use fidl_fuchsia_ui_focus_ext::FocusChainExt;
139 use fidl_fuchsia_ui_views_ext::ViewRefExt;
140 use futures::join;
141 use pretty_assertions::assert_eq;
142 use {fidl_fuchsia_ui_views as fidl_ui_views, fuchsia_scenic as scenic};
143
144 async fn expect_focus_ctl_focus_change(
152 mut request_stream: kbd_focus::ControllerRequestStream,
153 ) -> fidl_ui_views::ViewRef {
154 match request_stream.next().await {
155 Some(Ok(kbd_focus::ControllerRequest::Notify { view_ref, responder, .. })) => {
156 let _ = responder.send();
157 view_ref
158 }
159 _ => panic!("Error expecting text_manager focus change."),
160 }
161 }
162
163 async fn expect_focus_koid_chain(
164 focus_chain_provider_proxy: &focus::FocusChainProviderProxy,
165 ) -> focus::FocusKoidChain {
166 focus_chain_provider_proxy
167 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
168 .await
169 .expect("watch_focus_koid_chain")
170 }
171
172 #[fuchsia_async::run_until_stalled(test)]
174 async fn dispatch_focus() -> Result<(), Error> {
175 let (focus_proxy, focus_request_stream) =
176 fidl::endpoints::create_proxy_and_stream::<kbd_focus::ControllerMarker>();
177
178 let (focus_chain_listener_client_end, focus_chain_listener) =
179 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainListenerMarker>();
180
181 let (focus_chain_watcher, focus_chain_provider_stream) =
182 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
183 let (focus_chain_provider_publisher, focus_chain_provider_stream_handler) =
184 focus_chain_provider::make_publisher_and_stream_handler();
185 focus_chain_provider_stream_handler
186 .handle_request_stream(focus_chain_provider_stream)
187 .detach();
188
189 let mut listener = FocusListener::new_listener(
190 focus_proxy,
191 focus_chain_listener,
192 focus_chain_provider_publisher,
193 metrics::MetricsLogger::default(),
194 );
195
196 fuchsia_async::Task::local(async move {
197 let _ = listener.dispatch_focus_changes().await;
198 })
199 .detach();
200
201 let got_focus_koid_chain = expect_focus_koid_chain(&focus_chain_watcher).await;
206 assert_eq!(got_focus_koid_chain, focus::FocusKoidChain::default());
207
208 let view_ref = scenic::ViewRefPair::new()?.view_ref;
209 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
210 let focus_chain =
211 focus::FocusChain { focus_chain: Some(vec![view_ref]), ..Default::default() };
212
213 let (_, view_ref, got_focus_koid_chain) = join!(
214 focus_chain_listener_client_end.on_focus_change(focus_chain.duplicate().unwrap()),
215 expect_focus_ctl_focus_change(focus_request_stream),
216 expect_focus_koid_chain(&focus_chain_watcher),
217 );
218
219 assert_eq!(view_ref.get_koid().unwrap(), view_ref_dup.get_koid().unwrap(),);
220 assert!(focus_chain.equivalent(&got_focus_koid_chain).unwrap());
221
222 Ok(())
223 }
224}