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