Skip to main content

recovery_ui/
proxy_view_assistant.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use carnelian::render::Context;
7use carnelian::{input, Message, Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr};
8use recovery_util::ota::controller::SendEvent;
9use recovery_util::ota::state_machine::Event as StateMachineEvent;
10use std::collections::VecDeque;
11use zx::Event;
12
13#[cfg(feature = "debug_console")]
14use crate::console::ConsoleMessages;
15#[cfg(test)]
16use mockall::{predicate::*, *};
17
18pub enum ProxyMessages {
19    NewViewAssistant(Option<ViewAssistantPtr>),
20    ReplaceViewAssistant(Option<ViewAssistantPtr>),
21    PopViewAssistant,
22}
23
24pub struct ProxyViewAssistant {
25    event_sender: Option<Box<dyn SendEvent>>,
26    console_view_assistant: Option<ViewAssistantPtr>,
27    view_assistant_stack: VecDeque<ViewAssistantPtr>,
28    first_call_after_switch: bool,
29    #[cfg(feature = "debug_console")]
30    console_active: bool,
31}
32
33impl ProxyViewAssistant {
34    /// Creates a new ProxyViewAssistant with the provided view assistants.
35    /// Console is disabled if `console_view_assistant` is `None`.
36    pub fn new(
37        event_sender: Option<Box<dyn SendEvent>>,
38        console_view_assistant: Option<ViewAssistantPtr>,
39        view_assistant_ptr: ViewAssistantPtr,
40    ) -> Result<ProxyViewAssistant, Error> {
41        let mut view_assistant_stack = VecDeque::new();
42        view_assistant_stack.push_front(view_assistant_ptr);
43
44        Ok(ProxyViewAssistant {
45            event_sender,
46            console_view_assistant,
47            view_assistant_stack,
48            first_call_after_switch: true,
49            #[cfg(feature = "debug_console")]
50            console_active: false,
51        })
52    }
53
54    /// Returns true if event should toggle display of Console (and consider the event consumed).
55    #[cfg(feature = "debug_console")]
56    fn should_console_toggle(&mut self, event: &input::Event) -> bool {
57        if self.console_view_assistant.is_none() {
58            return false;
59        }
60        let mut toggle_requested = false;
61
62        match &event.event_type {
63            input::EventType::Touch(touch_event) => {
64                for contact in &touch_event.contacts {
65                    let pointer_event = &input::pointer::Event::new_from_contact(contact);
66                    match pointer_event.phase {
67                        input::pointer::Phase::Down(location) => {
68                            if location.x < 100 && location.y < 100 {
69                                toggle_requested = true;
70                                break;
71                            }
72                        }
73                        _ => {}
74                    };
75                }
76            }
77            _ => {}
78        }
79
80        toggle_requested
81    }
82}
83
84#[cfg_attr(test, automock)]
85impl ViewAssistant for ProxyViewAssistant {
86    fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
87        if let Some(console) = self.console_view_assistant.as_mut() {
88            console.setup(context)?;
89        }
90        self.view_assistant_stack.front_mut().unwrap().setup(context)
91    }
92
93    fn resize(&mut self, new_size: &Size) -> Result<(), Error> {
94        self.view_assistant_stack.front_mut().unwrap().resize(new_size)
95    }
96
97    fn render(
98        &mut self,
99        render_context: &mut Context,
100        buffer_ready_event: Event,
101        view_context: &ViewAssistantContext,
102    ) -> Result<(), Error> {
103        #[cfg(feature = "debug_console")]
104        if self.console_active {
105            if let Some(console) = self.console_view_assistant.as_mut() {
106                return console.render(render_context, buffer_ready_event, view_context);
107            } else {
108                eprintln!("Error: Console could not be found to render");
109            }
110        }
111
112        self.view_assistant_stack.front_mut().unwrap().render(
113            render_context,
114            buffer_ready_event,
115            view_context,
116        )
117    }
118
119    fn handle_input_event(
120        &mut self,
121        context: &mut ViewAssistantContext,
122        event: &input::Event,
123    ) -> Result<(), Error> {
124        #[cfg(feature = "debug_console")]
125        if self.should_console_toggle(event) {
126            self.console_active = !self.console_active;
127            return Ok(()); // Consume the console-toggling event.
128        }
129
130        #[cfg(feature = "debug_console")]
131        if self.console_active {
132            return Ok(()); // Consume the event when the console is active.
133        }
134
135        if self.first_call_after_switch {
136            self.first_call_after_switch = false;
137            self.handle_focus_event(context, true)?;
138        }
139        self.view_assistant_stack.front_mut().unwrap().handle_input_event(context, event)
140    }
141
142    fn handle_mouse_event(
143        &mut self,
144        context: &mut ViewAssistantContext,
145        event: &input::Event,
146        mouse_event: &input::mouse::Event,
147    ) -> Result<(), Error> {
148        self.view_assistant_stack.front_mut().unwrap().handle_mouse_event(
149            context,
150            event,
151            mouse_event,
152        )
153    }
154
155    fn handle_touch_event(
156        &mut self,
157        context: &mut ViewAssistantContext,
158        event: &input::Event,
159        touch_event: &input::touch::Event,
160    ) -> Result<(), Error> {
161        self.view_assistant_stack.front_mut().unwrap().handle_touch_event(
162            context,
163            event,
164            touch_event,
165        )
166    }
167
168    fn handle_pointer_event(
169        &mut self,
170        context: &mut ViewAssistantContext,
171        event: &input::Event,
172        pointer_event: &input::pointer::Event,
173    ) -> Result<(), Error> {
174        if self.first_call_after_switch {
175            self.first_call_after_switch = false;
176            self.handle_focus_event(context, true)?;
177        }
178        self.view_assistant_stack.front_mut().unwrap().handle_pointer_event(
179            context,
180            event,
181            pointer_event,
182        )
183    }
184
185    fn handle_keyboard_event(
186        &mut self,
187        context: &mut ViewAssistantContext,
188        event: &input::Event,
189        keyboard_event: &input::keyboard::Event,
190    ) -> Result<(), Error> {
191        self.view_assistant_stack.front_mut().unwrap().handle_keyboard_event(
192            context,
193            event,
194            keyboard_event,
195        )
196    }
197
198    fn handle_consumer_control_event(
199        &mut self,
200        context: &mut ViewAssistantContext,
201        event: &input::Event,
202        consumer_control_event: &input::consumer_control::Event,
203    ) -> Result<(), Error> {
204        self.view_assistant_stack.front_mut().unwrap().handle_consumer_control_event(
205            context,
206            event,
207            consumer_control_event,
208        )
209    }
210
211    fn handle_focus_event(
212        &mut self,
213        context: &mut ViewAssistantContext,
214        focused: bool,
215    ) -> Result<(), Error> {
216        self.view_assistant_stack.front_mut().unwrap().handle_focus_event(context, focused)
217    }
218
219    fn handle_message(&mut self, message: Message) {
220        if message.is::<ProxyMessages>() {
221            let proxy_message = message.downcast::<ProxyMessages>().unwrap();
222            match *proxy_message {
223                ProxyMessages::NewViewAssistant(mut view_assistant_ptr) => {
224                    let view_assistant_ptr =
225                        std::mem::replace(&mut view_assistant_ptr, None).unwrap();
226                    self.first_call_after_switch = true;
227                    self.view_assistant_stack.push_front(view_assistant_ptr);
228                }
229                ProxyMessages::ReplaceViewAssistant(mut view_assistant_ptr) => {
230                    let view_assistant_ptr =
231                        std::mem::replace(&mut view_assistant_ptr, None).unwrap();
232                    self.first_call_after_switch = true;
233                    self.view_assistant_stack.pop_front();
234                    self.view_assistant_stack.push_front(view_assistant_ptr);
235                }
236                ProxyMessages::PopViewAssistant => {
237                    if self.view_assistant_stack.len() > 1 {
238                        self.first_call_after_switch = true;
239                        self.view_assistant_stack.pop_front();
240                    }
241                }
242            }
243            return;
244        } else if message.is::<StateMachineEvent>() {
245            if let Some(event) = message.downcast_ref::<StateMachineEvent>() {
246                if let Some(event_sender) = &mut self.event_sender {
247                    event_sender.send(event.clone());
248                }
249            }
250        }
251
252        #[cfg(feature = "debug_console")]
253        if message.is::<ConsoleMessages>() {
254            if let Some(console) = self.console_view_assistant.as_mut() {
255                console.handle_message(message);
256            } else {
257                eprintln!("Error: Unable to find console to pass ConsoleMessages");
258            }
259            return;
260        }
261
262        self.view_assistant_stack.front_mut().unwrap().handle_message(message);
263    }
264
265    fn uses_pointer_events(&self) -> bool {
266        self.view_assistant_stack.front().unwrap().uses_pointer_events()
267    }
268
269    fn ownership_changed(&mut self, _owned: bool) -> Result<(), Error> {
270        self.view_assistant_stack.front_mut().unwrap().ownership_changed(_owned)
271    }
272
273    fn get_render_offset(&mut self) -> Option<i64> {
274        self.view_assistant_stack.front_mut().unwrap().get_render_offset()
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use carnelian::make_message;
282
283    enum MockMessageType {
284        MockMessage,
285    }
286
287    fn get_input_event() -> input::Event {
288        let mouse_event = input::mouse::Event {
289            buttons: Default::default(),
290            phase: input::mouse::Phase::Moved,
291            location: Default::default(),
292        };
293        input::Event {
294            event_time: 0,
295            device_id: Default::default(),
296            event_type: input::EventType::Mouse(mouse_event),
297        }
298    }
299
300    #[test]
301    fn test_proxy_view_assistant_switching() -> std::result::Result<(), anyhow::Error> {
302        #[cfg(feature = "debug_console")]
303        let mock0 = MockProxyViewAssistant::new();
304        let mut mock1 = MockProxyViewAssistant::new();
305        mock1.expect_handle_message().times(2).return_const(());
306        let mut mock2 = MockProxyViewAssistant::new();
307        mock2.expect_handle_message().times(1).return_const(());
308        let mut proxy = ProxyViewAssistant::new(
309            None,
310            #[cfg(feature = "debug_console")]
311            Some(Box::new(mock0)),
312            Box::new(mock1),
313        )
314        .unwrap();
315        assert_eq!(proxy.view_assistant_stack.len(), 1);
316        proxy.handle_message(make_message(MockMessageType::MockMessage));
317        proxy.handle_message(make_message(ProxyMessages::NewViewAssistant(Some(Box::new(mock2)))));
318        assert_eq!(proxy.view_assistant_stack.len(), 2);
319        proxy.handle_message(make_message(MockMessageType::MockMessage));
320        proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
321        assert_eq!(proxy.view_assistant_stack.len(), 1);
322        proxy.handle_message(make_message(MockMessageType::MockMessage));
323        Ok(())
324    }
325
326    #[test]
327    fn test_proxy_view_assistant_pop_first_entry() -> std::result::Result<(), anyhow::Error> {
328        #[cfg(feature = "debug_console")]
329        let mock0 = MockProxyViewAssistant::new();
330        let mut mock1 = MockProxyViewAssistant::new();
331        mock1.expect_handle_message().times(0).return_const(());
332        let mut proxy = ProxyViewAssistant::new(
333            None,
334            #[cfg(feature = "debug_console")]
335            Some(Box::new(mock0)),
336            Box::new(mock1),
337        )
338        .unwrap();
339        assert_eq!(proxy.view_assistant_stack.len(), 1);
340        proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
341        assert_eq!(proxy.view_assistant_stack.len(), 1);
342        Ok(())
343    }
344
345    #[test]
346    fn test_proxy_view_assistant_replace_view() -> std::result::Result<(), anyhow::Error> {
347        #[cfg(feature = "debug_console")]
348        let mock0 = MockProxyViewAssistant::new();
349        let mut mock1 = MockProxyViewAssistant::new();
350        mock1.expect_handle_message().times(1).return_const(());
351        let mut proxy = ProxyViewAssistant::new(
352            None,
353            #[cfg(feature = "debug_console")]
354            None,
355            Box::new(mock0),
356        )
357        .unwrap();
358        assert_eq!(proxy.view_assistant_stack.len(), 1);
359        proxy.handle_message(make_message(ProxyMessages::ReplaceViewAssistant(Some(Box::new(
360            mock1,
361        )))));
362        assert_eq!(proxy.view_assistant_stack.len(), 1);
363        proxy.handle_message(make_message(MockMessageType::MockMessage));
364        Ok(())
365    }
366
367    #[test]
368    fn test_proxy_view_assistant_first_call_pointer_event() -> std::result::Result<(), anyhow::Error>
369    {
370        let context = &mut ViewAssistantContext::new_for_testing();
371        #[cfg(feature = "debug_console")]
372        let mock0 = MockProxyViewAssistant::new();
373        let mut mock1 = MockProxyViewAssistant::new();
374        mock1.expect_handle_pointer_event().times(2).returning(|_, _, _| Ok(()));
375        mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
376        let mut proxy = ProxyViewAssistant::new(
377            None,
378            #[cfg(feature = "debug_console")]
379            Some(Box::new(mock0)),
380            Box::new(mock1),
381        )
382        .unwrap();
383        let event = get_input_event();
384        let pointer_id =
385            input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
386        let pointer_event =
387            input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
388        assert_eq!(proxy.first_call_after_switch, true);
389        proxy.handle_pointer_event(context, &event, &pointer_event)?;
390        assert_eq!(proxy.first_call_after_switch, false);
391        proxy.handle_pointer_event(context, &event, &pointer_event)?;
392        assert_eq!(proxy.first_call_after_switch, false);
393        Ok(())
394    }
395
396    #[test]
397    fn test_proxy_view_assistant_first_call_input_event() -> std::result::Result<(), anyhow::Error>
398    {
399        let context = &mut ViewAssistantContext::new_for_testing();
400        #[cfg(feature = "debug_console")]
401        let mock0 = MockProxyViewAssistant::new();
402        let mut mock1 = MockProxyViewAssistant::new();
403        mock1.expect_handle_input_event().times(2).returning(|_, _| Ok(()));
404        mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
405        let mut proxy = ProxyViewAssistant::new(
406            None,
407            #[cfg(feature = "debug_console")]
408            Some(Box::new(mock0)),
409            Box::new(mock1),
410        )
411        .unwrap();
412        let event = get_input_event();
413        assert_eq!(proxy.first_call_after_switch, true);
414        proxy.handle_input_event(context, &event)?;
415        assert_eq!(proxy.first_call_after_switch, false);
416        proxy.handle_input_event(context, &event)?;
417        assert_eq!(proxy.first_call_after_switch, false);
418        Ok(())
419    }
420    #[test]
421
422    fn test_proxy_view_assistant_first_call_input_pointer_event(
423    ) -> std::result::Result<(), anyhow::Error> {
424        let context = &mut ViewAssistantContext::new_for_testing();
425        #[cfg(feature = "debug_console")]
426        let mock0 = MockProxyViewAssistant::new();
427        let mut mock1 = MockProxyViewAssistant::new();
428        mock1.expect_handle_pointer_event().times(1).returning(|_, _, _| Ok(()));
429        mock1.expect_handle_input_event().times(1).returning(|_, _| Ok(()));
430        mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
431        let mut proxy = ProxyViewAssistant::new(
432            None,
433            #[cfg(feature = "debug_console")]
434            Some(Box::new(mock0)),
435            Box::new(mock1),
436        )
437        .unwrap();
438        let event = get_input_event();
439        let pointer_id =
440            input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
441        let pointer_event =
442            input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
443        assert_eq!(proxy.first_call_after_switch, true);
444        proxy.handle_pointer_event(context, &event, &pointer_event)?;
445        assert_eq!(proxy.first_call_after_switch, false);
446        proxy.handle_input_event(context, &event)?;
447        assert_eq!(proxy.first_call_after_switch, false);
448        Ok(())
449    }
450}