diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs index 08a6641fee..a7a0a4e0e1 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -879,8 +879,7 @@ extern "C" fn new_show_pairing_code(n_args: usize, args: *const Obj, kwargs: *mu let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let code: TString = kwargs.get(Qstr::MP_QSTR_code)?.try_into()?; - let button: bool = kwargs.get_or(Qstr::MP_QSTR_button, true)?; - let layout = ModelUI::show_pairing_code(title, description, code, button)?; + let layout = ModelUI::show_pairing_code(title, description, code)?; let layout_obj = LayoutObj::new_root(layout)?; Ok(layout_obj.into()) }; @@ -1695,7 +1694,6 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// title: str, /// description: str, /// code: str, - /// button: bool = True, /// ) -> LayoutObj[UiResult]: /// """Pairing device: second screen (pairing code). /// Returns on BLEEvent::{PairingCanceled, Disconnected}.""" diff --git a/core/embed/rust/src/ui/component/base.rs b/core/embed/rust/src/ui/component/base.rs index ce8393d09a..c3848f27b9 100644 --- a/core/embed/rust/src/ui/component/base.rs +++ b/core/embed/rust/src/ui/component/base.rs @@ -625,6 +625,7 @@ pub enum FlowMsg { Next, Choice(usize), Text(ShortString), + Number(u32), } #[cfg(feature = "micropython")] @@ -640,6 +641,7 @@ impl TryFrom for crate::micropython::obj::Obj { FlowMsg::Info => Ok(result::INFO.as_obj()), FlowMsg::Choice(i) => i.try_into(), FlowMsg::Text(s) => s.as_str().try_into(), + FlowMsg::Number(i) => i.try_into(), } } } diff --git a/core/embed/rust/src/ui/component/ble.rs b/core/embed/rust/src/ui/component/ble.rs index 8e4cfac16e..4e2fa66080 100644 --- a/core/embed/rust/src/ui/component/ble.rs +++ b/core/embed/rust/src/ui/component/ble.rs @@ -3,15 +3,19 @@ use crate::ui::{ event::BLEEvent, geometry::Rect, shape::Renderer, + util::Pager, }; +#[cfg(all(feature = "micropython", feature = "touch"))] +use crate::ui::{component::swipe_detect::SwipeConfig, flow::Swipable}; + pub struct BLEHandler { inner: T, waiting_for_pairing: bool, } -pub enum BLEHandlerMsg { - Content(T), +pub enum BLEHandlerMsg { + Content(TMsg), PairingCode(u32), Cancelled, } @@ -25,6 +29,19 @@ impl BLEHandler { } } +#[cfg(all(feature = "micropython", feature = "touch"))] +impl Swipable for BLEHandler +where + T: Component, +{ + fn get_pager(&self) -> Pager { + Pager::single_page() + } + fn get_swipe_config(&self) -> SwipeConfig { + SwipeConfig::default() + } +} + impl Component for BLEHandler where T: Component, diff --git a/core/embed/rust/src/ui/component/mod.rs b/core/embed/rust/src/ui/component/mod.rs index 418618be52..7d54ac985f 100644 --- a/core/embed/rust/src/ui/component/mod.rs +++ b/core/embed/rust/src/ui/component/mod.rs @@ -35,7 +35,7 @@ pub mod timeout; pub use bar::Bar; pub use base::{Child, Component, ComponentExt, Event, EventCtx, FlowMsg, Never, Timer}; #[cfg(feature = "ble")] -pub use ble::BLEHandler; +pub use ble::{BLEHandler, BLEHandlerMsg}; pub use border::Border; pub use button_request::{ButtonRequestExt, SendButtonRequest}; #[cfg(all( diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs index b4f95bf44d..3e280e31d3 100644 --- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs @@ -896,14 +896,13 @@ impl FirmwareUI for UIBolt { title: TString<'static>, description: TString<'static>, code: TString<'static>, - button: bool, ) -> Result { Self::confirm_action( title, Some(code), Some(description), None, - button.then_some(TR::buttons__confirm.into()), + None, None, false, false, diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs index 4d5ed3c1e4..4da678b9ea 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -1093,7 +1093,6 @@ impl FirmwareUI for UICaesar { _title: TString<'static>, _description: TString<'static>, _code: TString<'static>, - _button: bool, ) -> Result { Err::, Error>(Error::ValueError( c"show_pairing_code not supported", diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs index 696abf840f..e816b62e2f 100644 --- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs @@ -920,7 +920,6 @@ impl FirmwareUI for UIDelizia { _title: TString<'static>, _description: TString<'static>, _code: TString<'static>, - _button: bool, ) -> Result { Err::, Error>(Error::ValueError( c"show_pairing_code not supported", diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs index 0706415285..ae197ba775 100644 --- a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs +++ b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs @@ -13,6 +13,7 @@ pub mod prompt_backup; pub mod request_number; pub mod request_passphrase; pub mod show_danger; +pub mod show_pairing_code; pub mod show_share_words; #[cfg(feature = "universal_fw")] @@ -30,4 +31,5 @@ pub use prompt_backup::PromptBackup; pub use request_number::new_request_number; pub use request_passphrase::RequestPassphrase; pub use show_danger::ShowDanger; +pub use show_pairing_code::new_show_pairing_code; pub use show_share_words::new_show_share_words_flow; diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/show_pairing_code.rs b/core/embed/rust/src/ui/layout_eckhart/flow/show_pairing_code.rs new file mode 100644 index 0000000000..3753dc5fed --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/flow/show_pairing_code.rs @@ -0,0 +1,107 @@ +use crate::{ + error, + strutil::TString, + translations::TR, + ui::{ + component::{text::op::OpTextLayout, ComponentExt, FormattedText}, + flow::{ + base::{Decision, DecisionBuilder as _}, + FlowController, FlowMsg, SwipeFlow, + }, + geometry::{Alignment, Direction}, + }, +}; + +#[cfg(feature = "ble")] +use crate::ui::component::BLEHandlerMsg; + +use super::super::{ + component::Button, + firmware::{ + Header, ShortMenuVec, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen, + VerticalMenuScreenMsg, + }, + fonts, theme, +}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ShowPairingCode { + Main, + Menu, +} + +impl FlowController for ShowPairingCode { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, _direction: Direction) -> Decision { + self.do_nothing() + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Main, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled), + (Self::Main, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Main, FlowMsg::Info) => Self::Menu.goto(), + (Self::Main, FlowMsg::Number(pairing_code)) => { + self.return_msg(FlowMsg::Number(pairing_code)) + } + (Self::Menu, FlowMsg::Choice(..)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Cancelled) => Self::Main.goto(), + _ => self.do_nothing(), + } + } +} + +pub fn new_show_pairing_code( + title: TString<'static>, + description: TString<'static>, + code: TString<'static>, +) -> Result { + let mut ops = OpTextLayout::new(theme::firmware::TEXT_REGULAR); + ops.add_text(description, fonts::FONT_SATOSHI_REGULAR_38) + .add_newline() + .add_newline() + .add_newline() + .add_alignment(Alignment::Center) + .add_text(code, fonts::FONT_SATOSHI_EXTRALIGHT_72); + let screen = + TextScreen::new(FormattedText::new(ops)).with_header(Header::new(title).with_menu_button()); + + #[cfg(feature = "ble")] + let main_content = crate::ui::component::BLEHandler::new(screen, false).map(|msg| match msg { + BLEHandlerMsg::Cancelled => Some(FlowMsg::Cancelled), + BLEHandlerMsg::Content(TextScreenMsg::Cancelled) => Some(FlowMsg::Cancelled), + BLEHandlerMsg::Content(TextScreenMsg::Confirmed) => Some(FlowMsg::Confirmed), + BLEHandlerMsg::Content(TextScreenMsg::Menu) => Some(FlowMsg::Info), + BLEHandlerMsg::PairingCode(code) => Some(FlowMsg::Number(code)), + }); + #[cfg(not(feature = "ble"))] + let main_content = screen.map(|msg| match msg { + TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled), + TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), + TextScreenMsg::Menu => Some(FlowMsg::Info), + }); + + let mut menu = VerticalMenu::::empty(); + menu.item(Button::new_menu_item( + TR::buttons__cancel.into(), + theme::menu_item_title_orange(), + )); + + let menu_content = VerticalMenuScreen::new(menu) + .with_header(Header::new(TString::empty()).with_close_button()) + .map(move |msg| match msg { + VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)), + VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled), + _ => None, + }); + + let mut flow = SwipeFlow::new(&ShowPairingCode::Main)?; + flow.add_page(&ShowPairingCode::Main, main_content)? + .add_page(&ShowPairingCode::Menu, menu_content)?; + + Ok(flow) +} diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs index bbb47de160..121969061d 100644 --- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs @@ -18,7 +18,7 @@ use crate::{ }, ComponentExt as _, Empty, FormattedText, Timeout, }, - geometry::{Alignment, LinearPlacement, Offset}, + geometry::{LinearPlacement, Offset}, layout::{ obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, util::{ConfirmValueParams, PropsList, RecoveryType, StrOrBytes}, @@ -1083,27 +1083,9 @@ impl FirmwareUI for UIEckhart { title: TString<'static>, description: TString<'static>, code: TString<'static>, - button: bool, ) -> Result { - let mut ops = OpTextLayout::new(theme::firmware::TEXT_REGULAR); - ops.add_text(description, fonts::FONT_SATOSHI_REGULAR_38) - .add_newline() - .add_newline() - .add_newline() - .add_alignment(Alignment::Center) - .add_text(code, fonts::FONT_SATOSHI_EXTRALIGHT_72); - let mut screen = TextScreen::new(FormattedText::new(ops)); - if button { - screen = screen - .with_header(Header::new(title)) - .with_action_bar(ActionBar::new_cancel_confirm()); - } else { - screen = screen.with_header(Header::new(title).with_close_button()); - } - #[cfg(feature = "ble")] - let screen = crate::ui::component::BLEHandler::new(screen, false); - let layout = RootComponent::new(screen); - Ok(layout) + let flow = flow::show_pairing_code::new_show_pairing_code(title, description, code)?; + Ok(flow) } fn show_info( diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs index 4f18b22df5..0d76ec3c44 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -329,7 +329,6 @@ pub trait FirmwareUI { title: TString<'static>, description: TString<'static>, code: TString<'static>, - button: bool, ) -> Result; fn show_info( diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index 387b0f5d46..212aab7769 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -576,7 +576,6 @@ def show_pairing_code( title: str, description: str, code: str, - button: bool = True, ) -> LayoutObj[UiResult]: """Pairing device: second screen (pairing code). Returns on BLEEvent::{PairingCanceled, Disconnected}.""" diff --git a/core/src/apps/management/ble/pair_new_device.py b/core/src/apps/management/ble/pair_new_device.py index d5b48ba75f..b3b92101e0 100644 --- a/core/src/apps/management/ble/pair_new_device.py +++ b/core/src/apps/management/ble/pair_new_device.py @@ -32,7 +32,6 @@ async def pair_new_device() -> None: title="Bluetooth pairing", description="Pairing code match?", code=f"{code:0>6}", - button=True, ), None, )