diff --git a/core/embed/rust/src/ui/layout_bolt/component/homescreen.rs b/core/embed/rust/src/ui/layout_bolt/component/homescreen.rs index 9696777457..5c7660a253 100644 --- a/core/embed/rust/src/ui/layout_bolt/component/homescreen.rs +++ b/core/embed/rust/src/ui/layout_bolt/component/homescreen.rs @@ -1,3 +1,7 @@ +use super::{ + super::{constant, fonts, theme::IMAGE_HOMESCREEN}, + theme, Button, Loader, LoaderMsg, +}; use crate::{ io::BinaryData, strutil::TString, @@ -5,7 +9,8 @@ use crate::{ translations::TR, trezorhal::usb::usb_configured, ui::{ - component::{text::TextStyle, Component, Event, EventCtx, Pad, Timer}, + component::{text::TextStyle, Component, Event, EventCtx, Label, Pad, Timer}, + constant::{HEIGHT, WIDTH}, display::{ image::{ImageInfo, ToifFormat}, toif::Icon, @@ -14,16 +19,22 @@ use crate::{ event::TouchEvent, geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect}, layout::util::get_user_custom_image, + layout_bolt::component::bl_confirm::{Confirm, ConfirmMsg, ConfirmTitle}, shape::{self, Renderer}, }, }; -use crate::ui::constant::{HEIGHT, WIDTH}; +#[cfg(feature = "ble")] +use crate::trezorhal::ble::{self, connected, pairing_mode}; -use super::{ - super::{constant, fonts, theme::IMAGE_HOMESCREEN}, - theme, Loader, LoaderMsg, -}; +#[cfg(feature = "ble")] +use crate::ui::event::BLEEvent; + +#[cfg(feature = "rgb_led")] +use crate::trezorhal::rgb_led::set_color; + +#[cfg(feature = "button")] +use crate::ui::event::{ButtonEvent, PhysicalButton}; const AREA: Rect = constant::screen(); const TOP_CENTER: Point = AREA.top_center(); @@ -63,6 +74,8 @@ pub struct Homescreen { pad: Pad, paint_notification_only: bool, delay: Timer, + pairing: bool, + pairing_dialog: Option>, } #[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))] @@ -85,11 +98,14 @@ impl Homescreen { pad: Pad::with_background(theme::BG), paint_notification_only: false, delay: Timer::new(), + pairing: false, + pairing_dialog: None, } } fn level_to_style(level: u8) -> (Color, Icon) { match level { + 4 => (theme::BLUE, theme::ICON_MAGIC), 3 => (theme::YELLOW, theme::ICON_COINJOIN), 2 => (theme::VIOLET, theme::ICON_MAGIC), 1 => (theme::YELLOW, theme::ICON_WARN), @@ -98,6 +114,15 @@ impl Homescreen { } fn get_notification(&self) -> Option { + if connected() { + let (color, icon) = Self::level_to_style(4); + return Some(HomescreenNotification { + text: "BLE connected".into(), + icon, + color, + }); + } + if !usb_configured() { let (color, icon) = Self::level_to_style(0); Some(HomescreenNotification { @@ -137,6 +162,60 @@ impl Homescreen { } } + fn event_ble(&mut self, ctx: &mut EventCtx, event: Event) { + match event { + Event::BLE(BLEEvent::Connected) | Event::BLE(BLEEvent::Disconnected) => { + self.paint_notification_only = true; + ctx.request_paint(); + #[cfg(feature = "rgb_led")] + set_color(0); + } + Event::BLE(BLEEvent::PairingCanceled) => { + self.pairing = false; + self.pairing_dialog = None; + #[cfg(feature = "rgb_led")] + set_color(0); + ctx.request_paint(); + } + Event::BLE(BLEEvent::PairingRequest(data)) => { + self.pairing = true; + + let code = core::str::from_utf8(data).unwrap(); + + let mut pd = Confirm::new( + theme::BG, + Button::with_text("Cancel".into()), + Button::with_text("Confirm".into()), + ConfirmTitle::Text(Label::new( + "Pairing Request".into(), + Alignment::Start, + theme::TEXT_BOLD, + )), + Label::new( + "Pairing request received from another device. Confirm to pair.".into(), + Alignment::Center, + theme::TEXT_NORMAL, + ), + ) + .with_alert(Label::new( + code.into(), + Alignment::Center, + theme::TEXT_NORMAL, + )); + + pd.place(AREA); + + self.pairing_dialog = Some(pd); + ctx.request_paint(); + } + _ => {} + } + } + + fn pairing(&self) -> bool { + self.pairing + } + fn event_hold(&mut self, ctx: &mut EventCtx, event: Event) -> bool { match event { Event::Touch(TouchEvent::TouchStart(_)) => { @@ -187,13 +266,51 @@ impl Component for Homescreen { fn place(&mut self, bounds: Rect) -> Rect { self.pad.place(AREA); self.loader.place(AREA.translate(LOADER_OFFSET)); + self.pairing_dialog.place(AREA); bounds } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { Self::event_usb(self, ctx, event); + Self::event_ble(self, ctx, event); if self.hold_to_lock { - Self::event_hold(self, ctx, event).then_some(HomescreenMsg::Dismissed) + if Self::event_hold(self, ctx, event) { + return Some(HomescreenMsg::Dismissed); + } + } + + if self.pairing() { + if let Some(msg) = self.pairing_dialog.event(ctx, event) { + match msg { + ConfirmMsg::Cancel => { + ble::reject_pairing(); + self.pairing = false; + ctx.request_paint(); + #[cfg(feature = "rgb_led")] + set_color(0); + } + ConfirmMsg::Confirm => { + ble::allow_pairing(); + self.pairing = false; + ctx.request_paint(); + #[cfg(feature = "rgb_led")] + set_color(0); + } + } + } + } + + if let Event::Attach(_) = event { + #[cfg(feature = "rgb_led")] + set_color(0); + } + + if let Event::Button(ButtonEvent::ButtonPressed(PhysicalButton::Power)) = event { + #[cfg(feature = "rgb_led")] + set_color(0xff); + self.label.map(|t| pairing_mode(t)); + None + //Some(HomescreenMsg::Dismissed) } else { None } @@ -203,6 +320,8 @@ impl Component for Homescreen { self.pad.render(target); if self.loader.is_animating() || self.loader.is_completely_grown(Instant::now()) { self.render_loader(target); + } else if self.pairing() { + self.pairing_dialog.render(target); } else { match ImageInfo::parse(self.image) { ImageInfo::Jpeg(_) => {