Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/skip menu #152

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
18 changes: 18 additions & 0 deletions app/ui/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ void view_review_init(viewfunc_getItem_t viewfuncGetItem,
viewdata.viewfuncGetItem = viewfuncGetItem;
viewdata.viewfuncGetNumItems = viewfuncGetNumItems;
viewdata.viewfuncAccept = viewfuncAccept;

#if defined(TARGET_NANOS) || defined(TARGET_NANOS2) || defined(TARGET_NANOX)
viewdata.with_confirmation = false;
#endif

}

void view_review_init_progressive(
viewfunc_getItem_t viewfuncGetItem,
viewfunc_getNumItems_t viewfuncGetNumItems,
viewfunc_accept_t viewfuncAccept) {

view_review_init(viewfuncGetItem, viewfuncGetNumItems, viewfuncAccept);

#if defined(TARGET_NANOS) || defined(TARGET_NANOS2) || defined(TARGET_NANOX)
viewdata.with_confirmation = true;
#endif

}

void view_initialize_init(viewfunc_initialize_t viewFuncInit) {
Expand Down
8 changes: 8 additions & 0 deletions app/ui/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ typedef void (*viewfunc_accept_t)();

typedef zxerr_t (*viewfunc_initialize_t)();

// Callback type for continuation confirmation
typedef void (*viewfunc_confirm_continue_t)(char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen);

typedef enum {
REVIEW_UI = 0,
REVIEW_ADDRESS,
Expand Down Expand Up @@ -117,6 +120,11 @@ void view_review_init(viewfunc_getItem_t viewfuncGetItem,
viewfunc_getNumItems_t viewfuncGetNumItems,
viewfunc_accept_t viewfuncAccept);

void view_review_init_progressive(
viewfunc_getItem_t viewfuncGetItem,
viewfunc_getNumItems_t viewfuncGetNumItems,
viewfunc_accept_t viewfuncAccept);

void view_inspect_init(viewfunc_getInnerItem_t view_funcGetInnerItem, viewfunc_getNumItems_t view_funcGetInnerNumItems,
viewfunc_canInspectItem_t view_funcCanInspectItem);

Expand Down
22 changes: 22 additions & 0 deletions app/ui/view_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,28 @@ typedef struct {
uint8_t pageCount;

inner_state_t innerField;
#if defined(TARGET_NANOS) || defined(TARGET_NANOS2) || defined(TARGET_NANOX)
/**
* @brief Determines whether to prompt the user for confirmation during the review process.
*
* When `with_confirmation` is set to `true`, the engine will display a confirmation prompt
* to the user after each item review. This allows the user to either continue reviewing items
* or proceed to the final approval step.
*
* If `with_confirmation` is set to `false`, the engine will skip the confirmation prompts
* forcing users to review all items until they get to the approval menu.
*
* **Applicable Targets:**
* - Ledger Nano S (TARGET_NANOS)
* - Ledger Nano S2 (TARGET_NANOS2)
* - Ledger Nano X (TARGET_NANOX)
*
* **Excluded Targets:**
* - Stax
* - Flex
*/
bool with_confirmation;
#endif
} view_t;

typedef enum {
Expand Down
73 changes: 68 additions & 5 deletions app/ui/view_s.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ void os_exit(uint32_t id) {
(void)id;
os_sched_exit(0);
}
static unsigned int view_skip_button(unsigned int button_mask, unsigned int button_mask_counter);
const bagl_element_t *view_prepro(const bagl_element_t *element);

// Add new view state for skip screen
static const bagl_element_t view_skip[] = {
UI_BACKGROUND_LEFT_RIGHT_ICONS,
UI_LabelLine(UIID_LABEL + 0, 0, 8, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK,
"Press right to read"),
UI_LabelLine(UIID_LABEL + 1, 0, 19, UI_SCREEN_WIDTH, UI_11PX, UI_WHITE, UI_BLACK,
"Double-press to skip"),
};

const ux_menu_entry_t menu_main[] = {
{NULL, NULL, 0, &C_icon_app, MENU_MAIN_APP_LINE1, viewdata.key, 33, 12},
Expand Down Expand Up @@ -187,19 +198,39 @@ static unsigned int view_message_button(unsigned int button_mask, __Z_UNUSED uns
return 0;
}

static unsigned int view_review_button(unsigned int button_mask, __Z_UNUSED unsigned int button_mask_counter) {
static unsigned int view_review_button(unsigned int button_mask, unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT:
h_review_button_both();
break;
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
// Press left to progress to the previous element
h_review_button_left();
break;

// Are we at the point to display the skip/continue menu?
// to do so, with_cofirmation must be set
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
// Press right to progress to the next element
h_review_button_right();
if (viewdata.with_confirmation &&
(review_type == REVIEW_TXN || review_type == REVIEW_MSG) &&
// To enable left arrow rendering
viewdata.pageIdx > 0 &&
// Not in reject screen
viewdata.pageIdx == viewdata.pageCount - 1 &&
// Not in approve screen
viewdata.itemIdx != viewdata.itemCount - 2 &&
!is_reject_item() &&
viewdata.itemIdx < viewdata.itemCount - 2) { // Not in final screens
UX_DISPLAY(view_skip, view_prepro);
} else if (is_reject_item() && h_paging_can_increase()) {
// If we're at reject and can move forward, do normal navigation
h_review_button_right();
} else if (is_reject_item()) {
// If we're at reject and can't move forward, wrap to beginning
viewdata.itemIdx = 0;
viewdata.pageIdx = 0;
h_review_update();
} else {
h_review_button_right();
}
break;
}
return 0;
Expand Down Expand Up @@ -297,6 +328,19 @@ void h_review_button_right() {
}

static void h_review_action(unsigned int requireReply) {
if (viewdata.with_confirmation &&
(review_type == REVIEW_TXN || review_type == REVIEW_MSG) &&
viewdata.pageIdx == viewdata.pageCount - 1 &&
viewdata.itemIdx < viewdata.itemCount - 2) {
// Force jump to approval screen
viewdata.itemIdx = viewdata.itemCount - 1;
viewdata.pageIdx = 0;

h_review_update();

return;
}

if( is_accept_item() ){
zemu_log_stack("action_accept");
h_approve(1);
Expand Down Expand Up @@ -508,4 +552,23 @@ bool exceed_pixel_in_display(const uint8_t length) {
const unsigned short strWidth = zx_compute_line_width_light(viewdata.value, length);
return (strWidth >= (BAGL_WIDTH - BAGL_WIDTH_MARGIN));
}

static unsigned int view_skip_button(unsigned int button_mask, unsigned int button_mask_counter) {
switch (button_mask) {
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT:
// Skip to approve
h_review_action(review_type);
break;
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
// Continue review
h_paging_increase();
h_review_update();
break;
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
// Go back
h_review_update();
break;
}
return 0;
}
#endif
112 changes: 103 additions & 9 deletions app/ui/view_x.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@
#include <string.h>
#include <stdio.h>

#include "ux.h"

ux_state_t G_ux;
bolos_ux_params_t G_ux_params;
uint8_t flow_inside_loop;
extern unsigned int review_type;

bool custom_callback_active = false;
// Add global variable to store original callback at the top with other globals
unsigned int (*original_button_callback)(unsigned int button_mask, unsigned int button_mask_counter) = NULL;



void account_enabled();
void shortcut_enabled();

Expand All @@ -48,6 +61,9 @@ static void h_expert_update();
static void h_review_loop_start();
static void h_review_loop_inside();
static void h_review_loop_end();
static unsigned int handle_button_push(unsigned int button_mask, unsigned int button_mask_counter);
static void set_button_callback(unsigned int slot);
static void reset_button_callback(unsigned int slot) ;

#ifdef APP_SECRET_MODE_ENABLED
static void h_secret_click();
Expand All @@ -73,17 +89,23 @@ static void h_shortcut(unsigned int);
static void run_ux_review_flow(review_type_e reviewType, const ux_flow_step_t* const start_step);
const ux_flow_step_t *ux_review_flow[MAX_REVIEW_UX_SCREENS];

#include "ux.h"
ux_state_t G_ux;
bolos_ux_params_t G_ux_params;
uint8_t flow_inside_loop;
extern unsigned int review_type;


UX_STEP_NOCB(ux_idle_flow_1_step, pbb, { &C_icon_app, MENU_MAIN_APP_LINE1, viewdata.key,});
UX_STEP_CB_INIT(ux_idle_flow_2_step, bn, h_expert_update(), h_expert_toggle(), { "Expert mode:", viewdata.value, });
UX_STEP_NOCB(ux_idle_flow_3_step, bn, { APPVERSION_LINE1, APPVERSION_LINE2, });

UX_STEP_NOCB_INIT(
ux_review_skip_step,
nn,
{
// This will execute during initialization without requiring validation
custom_callback_active = true;
set_button_callback(stack_slot);
},
{
"Press right to read",
"Double-press to skip"
});

#ifdef APP_SECRET_MODE_ENABLED
UX_STEP_CB(ux_idle_flow_4_step, bn, h_secret_click(), { "Developed by:", "Zondax.ch", });
#else
Expand Down Expand Up @@ -270,7 +292,29 @@ void h_review_loop_end() {
switch(err) {
case zxerr_ok:
ux_layout_bnnn_paging_reset();
// If we're at the end of current item and there's more to show
if (viewdata.with_confirmation &&
(review_type == REVIEW_TXN || review_type == REVIEW_MSG) &&
viewdata.pageIdx == viewdata.pageCount - 1 &&
viewdata.itemIdx > getIntroPages() &&
viewdata.itemIdx < viewdata.itemCount - 1) {

// Show skip screen and enable button handler
uint8_t index = 0;

ux_review_flow[index++] = &ux_review_skip_step;
ux_review_flow[index++] = FLOW_END_STEP;


unsigned int current_slot = G_ux.stack_count - 1;
ux_flow_init(current_slot, ux_review_flow, NULL);
// set the callback after flow initialization, otherwise
// it would be overwritten
set_button_callback(current_slot);
return;
}
break;

case zxerr_no_data: {
flow_inside_loop = 0;
ux_flow_next();
Expand All @@ -281,7 +325,7 @@ void h_review_loop_end() {
break;
}
} else {
// coming from right
// coming from right
h_paging_decrease();
h_review_update();
}
Expand Down Expand Up @@ -486,6 +530,7 @@ void run_ux_review_flow(review_type_e reviewType, const ux_flow_step_t* const st
ux_review_flow[index++] = &ux_review_flow_2_start_step;
ux_review_flow[index++] = &ux_review_flow_2_step;
ux_review_flow[index++] = &ux_review_flow_2_end_step;

if (reviewType == REVIEW_MSG) {
ux_review_flow[index++] = &ux_review_flow_6_step;
} else {
Expand Down Expand Up @@ -523,7 +568,56 @@ void view_custom_error_show_impl() {
ux_flow_init(0, ux_custom_error_flow, NULL);
}

void view_blindsign_error_show_impl() {
void view_blindsign_error_show_impl() {
ux_flow_init(0, ux_warning_blind_sign_flow, NULL);
}

static unsigned int handle_button_push(unsigned int button_mask, unsigned int button_mask_counter) {
UNUSED(button_mask_counter);

if (!custom_callback_active) {
if (original_button_callback != NULL) {
// Just pass through to original callback
return original_button_callback(button_mask, button_mask_counter);
}
return 0;
}

// This is meant to handle the button interactions
// over the skip_step screen
switch (button_mask) {
// Handle skip to approve
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT:
if (review_type == REVIEW_MSG) {
run_ux_review_flow((review_type_e)review_type, &ux_review_flow_6_step);
} else {
run_ux_review_flow((review_type_e)review_type, &ux_review_flow_3_step);
}
return 1;

// Handle continue review
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
viewdata.itemIdx++;
run_ux_review_flow((review_type_e)review_type, &ux_review_flow_2_start_step);
return 1;

case BUTTON_EVT_RELEASED | BUTTON_LEFT:
// h_paging_init();
run_ux_review_flow((review_type_e)review_type, &ux_review_flow_2_start_step);
return 1;
}
return 0;
}

static void set_button_callback(unsigned int slot) {
// Store default callback to restablish later
original_button_callback = G_ux.stack[slot].button_push_callback;
G_ux.stack[slot].button_push_callback = handle_button_push;
}

static void reset_button_callback(unsigned int slot) {
G_ux.stack[slot].button_push_callback = original_button_callback;
if (custom_callback_active)
custom_callback_active = false;
}
#endif
2 changes: 1 addition & 1 deletion include/zxversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@

#define ZXLIB_MAJOR 29
#define ZXLIB_MINOR 4
#define ZXLIB_PATCH 1
#define ZXLIB_PATCH 2
Loading