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 fuchsia_trace::duration!(c"input", c"dispatch_focus_changes");
101 match focus_change {
102 Ok(focus::FocusChainListenerRequest::OnFocusChange {
103 focus_chain,
104 responder,
105 ..
106 }) => {
107 fuchsia_trace::duration!(c"input", c"dispatch_focus_changes[processing]");
108 self.focus_chain_publisher
110 .set_state_and_notify_if_changed(&focus_chain)
111 .context("while notifying FocusChainProviderPublisher")?;
112
113 if let Some(ref focus_chain) = focus_chain.focus_chain {
115 if let Some(ref view_ref) = focus_chain.last() {
116 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
117 self.text_manager
118 .notify(view_ref_dup)
119 .await
120 .context("while notifying text_manager")?;
121 }
122 };
123
124 responder.send().context("while sending focus chain listener response")?;
125 }
126 Err(e) => self.metrics_logger.log_error(
127 InputPipelineErrorMetricDimensionEvent::FocusChainListenerRequestError,
128 std::format!("FocusChainListenerRequest has error: {}.", e),
129 ),
130 }
131 }
132 log::warn!("Stopped dispatching focus changes.");
133 Ok(())
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use fidl_fuchsia_ui_focus_ext::FocusChainExt;
141 use fidl_fuchsia_ui_views_ext::ViewRefExt;
142 use futures::join;
143 use pretty_assertions::assert_eq;
144 use {fidl_fuchsia_ui_views as fidl_ui_views, fuchsia_scenic as scenic};
145
146 async fn expect_focus_ctl_focus_change(
154 mut request_stream: kbd_focus::ControllerRequestStream,
155 ) -> fidl_ui_views::ViewRef {
156 match request_stream.next().await {
157 Some(Ok(kbd_focus::ControllerRequest::Notify { view_ref, responder, .. })) => {
158 let _ = responder.send();
159 view_ref
160 }
161 _ => panic!("Error expecting text_manager focus change."),
162 }
163 }
164
165 async fn expect_focus_koid_chain(
166 focus_chain_provider_proxy: &focus::FocusChainProviderProxy,
167 ) -> focus::FocusKoidChain {
168 focus_chain_provider_proxy
169 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
170 .await
171 .expect("watch_focus_koid_chain")
172 }
173
174 #[fuchsia_async::run_until_stalled(test)]
176 async fn dispatch_focus() -> Result<(), Error> {
177 let (focus_proxy, focus_request_stream) =
178 fidl::endpoints::create_proxy_and_stream::<kbd_focus::ControllerMarker>();
179
180 let (focus_chain_listener_client_end, focus_chain_listener) =
181 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainListenerMarker>();
182
183 let (focus_chain_watcher, focus_chain_provider_stream) =
184 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
185 let (focus_chain_provider_publisher, focus_chain_provider_stream_handler) =
186 focus_chain_provider::make_publisher_and_stream_handler();
187 focus_chain_provider_stream_handler
188 .handle_request_stream(focus_chain_provider_stream)
189 .detach();
190
191 let mut listener = FocusListener::new_listener(
192 focus_proxy,
193 focus_chain_listener,
194 focus_chain_provider_publisher,
195 metrics::MetricsLogger::default(),
196 );
197
198 fuchsia_async::Task::local(async move {
199 let _ = listener.dispatch_focus_changes().await;
200 })
201 .detach();
202
203 let got_focus_koid_chain = expect_focus_koid_chain(&focus_chain_watcher).await;
208 assert_eq!(got_focus_koid_chain, focus::FocusKoidChain::default());
209
210 let view_ref = scenic::ViewRefPair::new()?.view_ref;
211 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
212 let focus_chain =
213 focus::FocusChain { focus_chain: Some(vec![view_ref]), ..Default::default() };
214
215 let (_, view_ref, got_focus_koid_chain) = join!(
216 focus_chain_listener_client_end.on_focus_change(focus_chain.duplicate().unwrap()),
217 expect_focus_ctl_focus_change(focus_request_stream),
218 expect_focus_koid_chain(&focus_chain_watcher),
219 );
220
221 assert_eq!(view_ref.get_koid().unwrap(), view_ref_dup.get_koid().unwrap(),);
222 assert!(focus_chain.equivalent(&got_focus_koid_chain).unwrap());
223
224 Ok(())
225 }
226}