1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-21 01:29:02 +00:00

feat(core/ui): eckhart: BLE event handling

[no changelog]
This commit is contained in:
Martin Milata 2025-04-01 18:10:38 +02:00 committed by M1nd3r
parent 251c7c7c48
commit a533bf898c
6 changed files with 113 additions and 16 deletions

View File

@ -1621,14 +1621,16 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// *,
/// device_name: str,
/// ) -> LayoutObj[UiResult]:
/// """Pairing device: first screen (device name)."""
/// """Pairing device: first screen (device name).
/// Returns if BLEEvent::PairingRequest is received."""
Qstr::MP_QSTR_show_pairing_device_name => obj_fn_kw!(0, new_show_pairing_device_name).as_obj(),
/// def show_pairing_code(
/// *,
/// code: str,
/// ) -> LayoutObj[UiResult]:
/// """Pairing device: second screen (pairing code)."""
/// """Pairing device: second screen (pairing code).
/// Returns on BLEEvent::{PairingCanceled, Disconnected}."""
Qstr::MP_QSTR_show_pairing_code => obj_fn_kw!(0, new_show_pairing_code).as_obj(),
/// def show_info(

View File

@ -0,0 +1,83 @@
use crate::ui::{
component::{Component, Event, EventCtx},
event::BLEEvent,
};
pub struct BLEHandler<T> {
inner: T,
waiting_for_pairing: bool,
}
pub enum BLEHandlerMsg<T> {
Content(T),
PairingCode(u32),
Cancelled,
}
impl<T> BLEHandler<T> {
pub fn new(inner: T, waiting_for_pairing: bool) -> Self {
Self {
inner,
waiting_for_pairing,
}
}
}
impl<T> Component for BLEHandler<T>
where
T: Component,
{
type Msg = BLEHandlerMsg<T::Msg>;
fn place(&mut self, bounds: crate::ui::geometry::Rect) -> crate::ui::geometry::Rect {
self.inner.place(bounds)
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match (event, self.waiting_for_pairing) {
(Event::BLE(BLEEvent::PairingRequest(num)), true) => {
return Some(BLEHandlerMsg::PairingCode(num))
}
(Event::BLE(BLEEvent::PairingCanceled), false)
| (Event::BLE(BLEEvent::Disconnected), false) => return Some(BLEHandlerMsg::Cancelled),
_ => {}
}
self.inner.event(ctx, event).map(BLEHandlerMsg::Content)
}
fn render<'s>(&'s self, target: &mut impl crate::ui::shape::Renderer<'s>) {
self.inner.render(target)
}
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for BLEHandler<T>
where
T: crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
self.inner.trace(t)
}
}
#[cfg(feature = "micropython")]
mod micropython {
use super::*;
use crate::{
error::Error,
micropython::obj::Obj,
ui::layout::{obj::ComponentMsgObj, result::CANCELLED},
};
impl<T> ComponentMsgObj for BLEHandler<T>
where
T: ComponentMsgObj,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
BLEHandlerMsg::Content(msg) => self.inner.msg_try_into_obj(msg),
BLEHandlerMsg::PairingCode(num) => num.try_into(),
BLEHandlerMsg::Cancelled => Ok(CANCELLED.as_obj()),
}
}
}
}

View File

@ -2,6 +2,8 @@
pub mod bar;
pub mod base;
#[cfg(feature = "ble")]
mod ble;
pub mod border;
pub mod button_request;
#[cfg(all(
@ -32,6 +34,8 @@ 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 border::Border;
pub use button_request::{ButtonRequestExt, SendButtonRequest};
#[cfg(all(

View File

@ -180,6 +180,10 @@ impl<'a> DeviceMenuScreen<'a> {
let device = screen.add_device_menu(device_name, about);
let settings = screen.add_settings_menu(security, device);
let is_connected = !paired_devices.is_empty(); // TODO this is mostly bad
let connected_subtext: Option<TString<'static>> =
is_connected.then_some("1 device connected".into());
let mut paired_device_indices: Vec<usize, 1> = Vec::new();
for (i, device) in paired_devices.iter().enumerate() {
unwrap!(paired_device_indices
@ -187,9 +191,10 @@ impl<'a> DeviceMenuScreen<'a> {
}
let devices = screen.add_paired_devices_menu(paired_devices, paired_device_indices);
let pair_and_connect = screen.add_pair_and_connect_menu(devices);
let pair_and_connect = screen.add_pair_and_connect_menu(devices, connected_subtext);
let root = screen.add_root_menu(failed_backup, pair_and_connect, settings);
let root =
screen.add_root_menu(failed_backup, pair_and_connect, settings, connected_subtext);
screen.set_active_subscreen(root);
@ -219,17 +224,18 @@ impl<'a> DeviceMenuScreen<'a> {
self.add_subscreen(Subscreen::Submenu(submenu_index))
}
fn add_pair_and_connect_menu(&mut self, manage_devices_index: usize) -> usize {
fn add_pair_and_connect_menu(
&mut self,
manage_devices_index: usize,
connected_subtext: Option<TString<'static>>,
) -> usize {
let mut items: Vec<MenuItem, MENU_MAX_ITEMS> = Vec::new();
unwrap!(items.push(
MenuItem::new(
"Manage paired devices".into(),
Some(Action::GoTo(manage_devices_index)),
)
.with_subtext(Some((
"1 device connected".into(),
Some(Button::SUBTEXT_STYLE_GREEN)
)))
.with_subtext(connected_subtext.map(|t| (t, Some(Button::SUBTEXT_STYLE_GREEN))))
));
unwrap!(items.push(MenuItem::new(
"Pair new device".into(),
@ -293,6 +299,7 @@ impl<'a> DeviceMenuScreen<'a> {
failed_backup: bool,
pair_and_connect_index: usize,
settings_index: usize,
connected_subtext: Option<TString<'static>>,
) -> usize {
let mut items: Vec<MenuItem, MENU_MAX_ITEMS> = Vec::new();
if failed_backup {
@ -310,10 +317,7 @@ impl<'a> DeviceMenuScreen<'a> {
"Pair & connect".into(),
Some(Action::GoTo(pair_and_connect_index)),
)
.with_subtext(Some((
"1 device connected".into(),
Some(Button::SUBTEXT_STYLE_GREEN)
)))
.with_subtext(connected_subtext.map(|t| (t, Some(Button::SUBTEXT_STYLE_GREEN))))
));
unwrap!(items.push(MenuItem::new(
"Settings".into(),

View File

@ -15,7 +15,7 @@ use crate::{
Paragraphs, VecExt,
},
},
Empty, FormattedText,
BLEHandler, Empty, FormattedText,
},
geometry::{Alignment, LinearPlacement, Offset},
layout::{
@ -814,6 +814,7 @@ impl FirmwareUI for UIEckhart {
.with_action_bar(ActionBar::new_single(Button::with_text(
"Continue on host".into(),
)));
let screen = BLEHandler::new(screen, true);
let layout = RootComponent::new(screen);
Ok(layout)
}
@ -828,6 +829,7 @@ impl FirmwareUI for UIEckhart {
let screen = TextScreen::new(FormattedText::new(ops))
.with_header(Header::new("Bluetooth pairing".into()))
.with_action_bar(ActionBar::new_cancel_confirm());
let screen = BLEHandler::new(screen, false);
let layout = RootComponent::new(screen);
Ok(layout)
}

View File

@ -549,7 +549,8 @@ def show_pairing_device_name(
*,
device_name: str,
) -> LayoutObj[UiResult]:
"""Pairing device: first screen (device name)."""
"""Pairing device: first screen (device name).
Returns if BLEEvent::PairingRequest is received."""
# rust/src/ui/api/firmware_micropython.rs
@ -557,7 +558,8 @@ def show_pairing_code(
*,
code: str,
) -> LayoutObj[UiResult]:
"""Pairing device: second screen (pairing code)."""
"""Pairing device: second screen (pairing code).
Returns on BLEEvent::{PairingCanceled, Disconnected}."""
# rust/src/ui/api/firmware_micropython.rs