Skip to main content

recovery_ui/
check_network.rs

1// Copyright 2022 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 crate::button::{Button, ButtonMessages, ButtonOptions, ButtonShape, SceneBuilderButtonExt};
6use crate::constants::constants::{
7    BACKGROUND_WHITE, BUTTON_SPACE, LEFT_MARGIN_SPACE, TEXT_COLOR, TEXT_FONT_SIZE, TITLE_COLOR,
8    TITLE_FONT_SIZE,
9};
10use crate::generic_view::ButtonInfo;
11use crate::text_field::{SceneBuilderTextFieldExt, TextField, TextFieldOptions, TextVisibility};
12use anyhow::Error;
13use carnelian::drawing::load_font;
14use carnelian::render::Context as RenderContext;
15use carnelian::scene::facets::{TextFacetOptions, TextHorizontalAlignment, TextVerticalAlignment};
16use carnelian::scene::layout::{
17    CrossAxisAlignment, Flex, FlexOptions, MainAxisAlignment, MainAxisSize,
18};
19use carnelian::scene::scene::{Scene, SceneBuilder};
20use carnelian::{
21    input, make_message, AppSender, Message, MessageTarget, Point, Size, ViewAssistant,
22    ViewAssistantContext, ViewKey,
23};
24use euclid::size2;
25use std::path::PathBuf;
26
27const TEXT_FIELD_WIDTH: f32 = 895.0;
28
29pub struct SceneDetails {
30    pub(crate) scene: Scene,
31    password_field: TextField,
32    buttons: Vec<Button>,
33}
34
35/// Asks the user whether they want to try again, enter a new network or cancel
36pub struct CheckNetworkViewAssistant {
37    app_sender: AppSender,
38    view_key: ViewKey,
39    title_text: String,
40    body_text: String,
41    network: String,
42    password: String,
43    privacy: bool,
44    button_infos: Vec<ButtonInfo>,
45    scene_details: Option<SceneDetails>,
46}
47
48impl CheckNetworkViewAssistant {
49    pub fn new(
50        app_sender: AppSender,
51        view_key: ViewKey,
52        title_text: String,
53        body_text: String,
54        network: String,
55        password: String,
56        button_infos: Vec<ButtonInfo>,
57    ) -> Result<CheckNetworkViewAssistant, Error> {
58        Ok(CheckNetworkViewAssistant {
59            app_sender: app_sender,
60            view_key,
61            scene_details: None,
62            title_text,
63            body_text,
64            network,
65            password,
66            privacy: true,
67            button_infos,
68        })
69    }
70
71    fn button_press(&mut self, text: &String) {
72        for button_info in &self.button_infos {
73            // Generate the event that this button press has caused
74            if button_info.text == text {
75                #[cfg(feature = "debug_logging")]
76                println!("====== Generating event: {:?}", button_info.event);
77                self.app_sender.queue_message(
78                    MessageTarget::View(self.view_key),
79                    make_message(button_info.event.clone()),
80                )
81            }
82        }
83        if let Some(scene_details) = &mut self.scene_details {
84            if scene_details.password_field.get_title() == text {
85                self.privacy = !self.privacy;
86                self.scene_details = None;
87            }
88        }
89    }
90
91    pub fn check_network_scene(&mut self, context: &ViewAssistantContext) -> SceneDetails {
92        let target_size = context.size;
93        let mut builder = SceneBuilder::new().background_color(BACKGROUND_WHITE);
94        let mut buttons: Vec<Button> = Vec::new();
95        let face = load_font(PathBuf::from("/pkg/data/fonts/Roboto-Regular.ttf")).expect("Font");
96        let mut password_field: Option<TextField> = None;
97        builder.group().column().max_size().main_align(MainAxisAlignment::SpaceEvenly).contents(
98            |builder| {
99                builder.start_group(
100                    "left row for margin space",
101                    Flex::with_options_ptr(FlexOptions::row(
102                        MainAxisSize::Min,
103                        MainAxisAlignment::Start,
104                        CrossAxisAlignment::Start,
105                    )),
106                );
107                builder.space(size2(LEFT_MARGIN_SPACE, 10.0));
108                builder.start_group(
109                    "main column",
110                    Flex::with_options_ptr(FlexOptions::column(
111                        MainAxisSize::Min,
112                        MainAxisAlignment::Start,
113                        CrossAxisAlignment::Start,
114                    )),
115                );
116                builder.space(size2(10.0, 30.0));
117                builder.text(
118                    face.clone(),
119                    &self.title_text,
120                    TITLE_FONT_SIZE,
121                    Point::zero(),
122                    TextFacetOptions {
123                        horizontal_alignment: TextHorizontalAlignment::Left,
124                        vertical_alignment: TextVerticalAlignment::Bottom,
125                        color: TITLE_COLOR,
126                        ..TextFacetOptions::default()
127                    },
128                );
129                builder.space(size2(10.0, 30.0));
130                builder.text(
131                    face.clone(),
132                    &self.body_text,
133                    TEXT_FONT_SIZE,
134                    Point::zero(),
135                    TextFacetOptions {
136                        horizontal_alignment: TextHorizontalAlignment::Left,
137                        vertical_alignment: TextVerticalAlignment::Bottom,
138                        color: TEXT_COLOR,
139                        ..TextFacetOptions::default()
140                    },
141                );
142                builder.space(size2(1.0, 50.0));
143                builder.text_field(
144                    "Network".to_string(),
145                    self.network.clone(),
146                    TextVisibility::Always,
147                    size2(TEXT_FIELD_WIDTH, TEXT_FONT_SIZE * 3.0),
148                    TextFieldOptions { text_size: TEXT_FONT_SIZE, ..TextFieldOptions::default() },
149                );
150                builder.space(size2(10.0, 40.0));
151                password_field = Some(builder.text_field(
152                    "Password".to_string(),
153                    self.password.clone(),
154                    TextVisibility::Toggleable(!self.privacy),
155                    size2(TEXT_FIELD_WIDTH, TEXT_FONT_SIZE * 3.0),
156                    TextFieldOptions { text_size: TEXT_FONT_SIZE, ..TextFieldOptions::default() },
157                ));
158                builder.space(size2(10.0, 50.0));
159                buttons.append(&mut self.add_buttons(builder, &self.button_infos));
160
161                builder.end_group(); // main column
162                builder.end_group(); //gui left row for margin space
163            },
164        );
165        let mut scene = builder.build();
166        for button in &mut buttons {
167            button.set_focused(&mut scene, true);
168        }
169        password_field.as_mut().unwrap().set_focused(&mut scene, true);
170        scene.layout(target_size);
171        SceneDetails { scene, password_field: password_field.unwrap(), buttons }
172    }
173
174    fn add_buttons(
175        &self,
176        builder: &mut SceneBuilder,
177        button_infos: &Vec<ButtonInfo>,
178    ) -> Vec<Button> {
179        let mut buttons: Vec<Button> = Vec::new();
180        if !button_infos.is_empty() {
181            builder.start_group(
182                "Button Row",
183                Flex::with_options_ptr(FlexOptions::row(
184                    MainAxisSize::Max,
185                    MainAxisAlignment::End,
186                    CrossAxisAlignment::End,
187                )),
188            );
189            for button_info in button_infos {
190                buttons.push(builder.button(
191                    button_info.text,
192                    None,
193                    ButtonOptions {
194                        shape: ButtonShape::Oval,
195                        bg_fg_swapped: button_info.reversed,
196                        ..ButtonOptions::default()
197                    },
198                ));
199                builder.space(size2(BUTTON_SPACE, 10.0));
200            }
201            // Move the buttons in from the right
202            builder.space(size2(117.0, 10.0));
203            builder.end_group(); // Button Row
204        }
205        buttons
206    }
207}
208
209impl ViewAssistant for CheckNetworkViewAssistant {
210    fn resize(&mut self, _new_size: &Size) -> Result<(), Error> {
211        self.scene_details = None;
212        Ok(())
213    }
214
215    fn render(
216        &mut self,
217        render_context: &mut RenderContext,
218        ready_event: zx::Event,
219        context: &ViewAssistantContext,
220    ) -> Result<(), Error> {
221        let mut scene_details =
222            self.scene_details.take().unwrap_or_else(|| self.check_network_scene(context));
223        scene_details.scene.render(render_context, ready_event, context)?;
224        self.scene_details = Some(scene_details);
225        context.request_render();
226        Ok(())
227    }
228
229    fn handle_message(&mut self, message: Message) {
230        if let Some(button_message) = message.downcast_ref::<ButtonMessages>() {
231            match button_message {
232                ButtonMessages::Pressed(_time, button_text) => {
233                    #[cfg(feature = "debug_logging")]
234                    println!("====== Received button press: {}", button_text);
235                    self.button_press(button_text);
236                }
237            }
238        }
239    }
240
241    fn handle_pointer_event(
242        &mut self,
243        context: &mut ViewAssistantContext,
244        _event: &input::Event,
245        pointer_event: &input::pointer::Event,
246    ) -> Result<(), Error> {
247        if let Some(scene_details) = self.scene_details.as_mut() {
248            for button in &mut scene_details.buttons {
249                button.handle_pointer_event(&mut scene_details.scene, context, &pointer_event);
250            }
251            // We need to pass this to the password field because it is a sub-view
252            scene_details.password_field.handle_pointer_event(
253                &mut scene_details.scene,
254                context,
255                &pointer_event,
256            );
257        }
258        Ok(())
259    }
260}