Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_use_case.c
Go to the documentation of this file.
1
6#ifdef NBGL_USE_CASE
7#ifdef HAVE_SE_TOUCH
8/*********************
9 * INCLUDES
10 *********************/
11#include <string.h>
12#include <stdio.h>
13#include "nbgl_debug.h"
14#include "nbgl_use_case.h"
15#include "glyphs.h"
16#include "os_pic.h"
17#include "os_print.h"
18#include "os_helpers.h"
19
20/*********************
21 * DEFINES
22 *********************/
23
24/* Defines for definition and usage of genericContextPagesInfo */
25#define PAGE_NB_ELEMENTS_BITS 3
26#define GET_PAGE_NB_ELEMENTS(pageData) ((pageData) &0x07)
27#define SET_PAGE_NB_ELEMENTS(nbElements) ((nbElements) &0x07)
28
29#define PAGE_FLAG_BITS 1
30#define GET_PAGE_FLAG(pageData) (((pageData) &0x08) >> 3)
31#define SET_PAGE_FLAG(flag) (((flag) &0x01) << 3)
32
33#define PAGE_DATA_BITS (PAGE_NB_ELEMENTS_BITS + PAGE_FLAG_BITS)
34#define PAGES_PER_UINT8 (8 / PAGE_DATA_BITS)
35#define SET_PAGE_DATA(pageIdx, pageData) \
36 (pagesData[pageIdx / PAGES_PER_UINT8] = pageData \
37 << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS))
38
39#define MAX_PAGE_NB 256
40
41/* Alias to clarify usage of genericContext hasStartingContent and hasFinishingContent feature */
42#define STARTING_CONTENT localContentsList[0]
43#define FINISHING_CONTENT localContentsList[1]
44
45/* max number of lines for address under QR Code */
46#define QRCODE_NB_MAX_LINES 3
47/* max number of char for reduced QR Code address */
48#define QRCODE_REDUCED_ADDR_LEN 128
49
50// macros to ease access to shared contexts
51#define keypadContext sharedContext.keypad
52#define reviewWithWarnCtx sharedContext.reviewWithWarning
53
54/* max length of the string displaying the description of the Web3 Checks report */
55#define W3C_DESCRIPTION_MAX_LEN 128
56
62#define RISKY_OPERATION (1 << 6)
63
64/**********************
65 * TYPEDEFS
66 **********************/
67enum {
68 BACK_TOKEN = 0,
69 NEXT_TOKEN,
70 QUIT_TOKEN,
71 NAV_TOKEN,
72 SKIP_TOKEN,
73 CONTINUE_TOKEN,
74 ADDRESS_QRCODE_BUTTON_TOKEN,
75 ACTION_BUTTON_TOKEN,
76 CHOICE_TOKEN,
77 DETAILS_BUTTON_TOKEN,
78 CONFIRM_TOKEN,
79 REJECT_TOKEN,
80 VALUE_ALIAS_TOKEN,
81 INFO_ALIAS_TOKEN,
82 BLIND_WARNING_TOKEN,
83 WARNING_BUTTON_TOKEN,
84 TIP_BOX_TOKEN,
85 WARNING_CHOICE_TOKEN,
86 DISMISS_QR_TOKEN,
87 DISMISS_WARNING_TOKEN,
88 FIRST_WARN_BAR_TOKEN,
89 LAST_WARN_BAR_TOKEN = (FIRST_WARN_BAR_TOKEN + NB_WARNING_TYPES - 1),
90};
91
92typedef enum {
93 REVIEW_NAV = 0,
94 SETTINGS_NAV,
95 GENERIC_NAV,
96 STREAMING_NAV
97} NavType_t;
98
99typedef struct DetailsContext_s {
100 uint8_t nbPages;
101 uint8_t currentPage;
102 bool wrapping;
103 const char *tag;
104 const char *value;
105 const char *nextPageStart;
106} DetailsContext_t;
107
108typedef struct AddressConfirmationContext_s {
109 nbgl_layoutTagValue_t tagValuePair;
110 nbgl_layout_t *modalLayout;
111} AddressConfirmationContext_t;
112
113#ifdef NBGL_KEYPAD
114typedef struct KeypadContext_s {
115 uint8_t pinEntry[KEYPAD_MAX_DIGITS];
116 uint8_t pinLen;
117 uint8_t pinMinDigits;
118 uint8_t pinMaxDigits;
119 nbgl_layout_t *layoutCtx;
120 bool hidden;
121} KeypadContext_t;
122#endif
123
124typedef struct BlindSigningContext_s {
125 bool isStreaming;
126 nbgl_operationType_t operationType;
127 const nbgl_contentTagValueList_t *tagValueList;
128 const nbgl_icon_details_t *icon;
129 const char *reviewTitle;
130 const char *reviewSubTitle;
131 const char *finishTitle;
132 const nbgl_tipBox_t *tipBox;
133 const nbgl_warning_t *warning;
134 nbgl_choiceCallback_t choiceCallback;
135 nbgl_layout_t *layoutCtx;
136 nbgl_layout_t *modalLayout;
137 uint8_t securityReportLevel; // level 1 is the first level of menus
138 bool isIntro; // set to true during intro (before actual review)
139} ReviewWithWarningContext_t;
140
141// this union is intended to save RAM for context storage
142// indeed, these 2 contexts cannot happen simultaneously
143typedef union {
144#ifdef NBGL_KEYPAD
145 KeypadContext_t keypad;
146#endif
147 ReviewWithWarningContext_t reviewWithWarning;
148} SharedContext_t;
149
150typedef enum {
151 USE_CASE_GENERIC = 0,
152 USE_CASE_SPINNER
153} GenericContextType_t;
154
155typedef struct {
156 GenericContextType_t type; // type of Generic context usage
157 uint8_t spinnerPosition;
158 nbgl_genericContents_t genericContents;
159 int8_t currentContentIdx;
160 uint8_t currentContentElementNb;
161 uint8_t currentElementIdx;
162 bool hasStartingContent;
163 bool hasFinishingContent;
164 const char *detailsItem;
165 const char *detailsvalue;
166 bool detailsWrapping;
168 *currentPairs; // to be used to retrieve the pairs with value alias
170 currentCallback; // to be used to retrieve the pairs with value alias
171 nbgl_layout_t modalLayout;
172 nbgl_layout_t backgroundLayout;
173} GenericContext_t;
174
175typedef struct {
176 const char *appName;
177 const nbgl_icon_details_t *appIcon;
178 const char *tagline;
179 const nbgl_genericContents_t *settingContents;
180 const nbgl_contentInfoList_t *infosList;
181 nbgl_homeAction_t homeAction;
182 nbgl_callback_t quitCallback;
183} nbgl_homeAndSettingsContext_t;
184
185typedef struct {
186 nbgl_operationType_t operationType;
187 nbgl_choiceCallback_t choiceCallback;
188} nbgl_reviewContext_t;
189
190typedef struct {
191 nbgl_operationType_t operationType;
192 nbgl_choiceCallback_t choiceCallback;
193 nbgl_callback_t skipCallback;
194 const nbgl_icon_details_t *icon;
195 uint8_t stepPageNb;
196} nbgl_reviewStreamingContext_t;
197
198typedef union {
199 nbgl_homeAndSettingsContext_t homeAndSettings;
200 nbgl_reviewContext_t review;
201 nbgl_reviewStreamingContext_t reviewStreaming;
202} nbgl_BundleNavContext_t;
203
204typedef struct {
205 const nbgl_icon_details_t *icon;
206 const char *text;
207 const char *subText;
208} SecurityReportItem_t;
209
210/**********************
211 * STATIC VARIABLES
212 **********************/
213
214// char buffers to build some strings
215static char tmpString[W3C_DESCRIPTION_MAX_LEN];
216
217// multi-purposes callbacks
218static nbgl_callback_t onQuit;
219static nbgl_callback_t onContinue;
220static nbgl_callback_t onAction;
221static nbgl_navCallback_t onNav;
222static nbgl_layoutTouchCallback_t onControls;
223static nbgl_contentActionCallback_t onContentAction;
224static nbgl_choiceCallback_t onChoice;
225static nbgl_callback_t onModalConfirm;
226#ifdef NBGL_KEYPAD
227static nbgl_pinValidCallback_t onValidatePin;
228#endif
229
230// contexts for background and modal pages
231static nbgl_page_t *pageContext;
232static nbgl_page_t *modalPageContext;
233
234// context for pages
235static const char *pageTitle;
236
237// context for tip-box
238static const char *tipBoxModalTitle;
239static nbgl_contentInfoList_t tipBoxInfoList;
240
241// context for navigation use case
242static nbgl_pageNavigationInfo_t navInfo;
243static bool forwardNavOnly;
244static NavType_t navType;
245
246static DetailsContext_t detailsContext;
247
248// multi-purpose context shared for non-concurrent usages
249static SharedContext_t sharedContext;
250
251// context for address review
252static AddressConfirmationContext_t addressConfirmationContext;
253
254// contexts for generic navigation
255static GenericContext_t genericContext;
256static nbgl_content_t
257 localContentsList[3]; // 3 needed for nbgl_useCaseReview (starting page / tags / final page)
258static uint8_t genericContextPagesInfo[MAX_PAGE_NB / PAGES_PER_UINT8];
259
260// contexts for bundle navigation
261static nbgl_BundleNavContext_t bundleNavContext;
262
263// indexed by nbgl_contentType_t
264static const uint8_t nbMaxElementsPerContentType[] = {
265 1, // CENTERED_INFO
266 1, // EXTENDED_CENTER
267 1, // INFO_LONG_PRESS
268 1, // INFO_BUTTON
269 1, // TAG_VALUE_LIST (computed dynamically)
270 1, // TAG_VALUE_DETAILS
271 1, // TAG_VALUE_CONFIRM
272 3, // SWITCHES_LIST (computed dynamically)
273 3, // INFOS_LIST (computed dynamically)
274 5, // CHOICES_LIST (computed dynamically)
275 5, // BARS_LIST (computed dynamically)
276};
277
278static const SecurityReportItem_t securityReportItems[NB_WARNING_TYPES] = {
279 [BLIND_SIGNING_WARN] = {.icon = &WARNING_ICON,
280 .text = "Blind signing",
281 .subText = "This transaction cannot be fully decoded." },
282 [W3C_ISSUE_WARN] = {.icon = &ROUND_WARN_ICON,
283 .text = "Web3 Checks issue",
284 .subText = "Web3 Checks could not verify this transaction." },
285 [W3C_LOSING_SWAP_WARN] = {.icon = &ROUND_WARN_ICON,
286 .text = "Risk detected",
287 .subText = "This transaction was scanned as risky by Web3 Checks." },
289 = {.icon = &ROUND_WARN_ICON,
290 .text = "Threat detected",
291 .subText = "This transaction was scanned as malicious by Web3 Checks."}
292};
293
294// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
295static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
296
297#ifdef NBGL_QRCODE
298/* buffer to store reduced address under QR Code */
299static char reducedAddress[QRCODE_REDUCED_ADDR_LEN];
300#endif // NBGL_QRCODE
301
302/**********************
303 * STATIC FUNCTIONS
304 **********************/
305static void displayReviewPage(uint8_t page, bool forceFullRefresh);
306static void displayDetailsPage(uint8_t page, bool forceFullRefresh);
307static void displayFullValuePage(const char *backText,
308 const char *aliasText,
309 const nbgl_contentValueExt_t *extension);
310static void displayTipBoxModal(void);
311static void displaySettingsPage(uint8_t page, bool forceFullRefresh);
312static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh);
313static void pageCallback(int token, uint8_t index);
314#ifdef NBGL_QRCODE
315static void displayAddressQRCode(void);
316#endif // NBGL_QRCODE
317static void modalLayoutTouchCallback(int token, uint8_t index);
318static void displaySkipWarning(void);
319
320static void bundleNavStartHome(void);
321static void bundleNavStartSettingsAtPage(uint8_t initSettingPage);
322static void bundleNavStartSettings(void);
323
324static void bundleNavReviewStreamingChoice(bool confirm);
325static void displaySecurityReport(uint32_t set);
326static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details);
327static void displayInitialWarning(void);
328static void useCaseReview(nbgl_operationType_t operationType,
329 const nbgl_contentTagValueList_t *tagValueList,
330 const nbgl_icon_details_t *icon,
331 const char *reviewTitle,
332 const char *reviewSubTitle,
333 const char *finishTitle,
334 const nbgl_tipBox_t *tipBox,
335 nbgl_choiceCallback_t choiceCallback,
336 bool isLight,
337 bool playNotifSound);
338static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
339 const nbgl_icon_details_t *icon,
340 const char *reviewTitle,
341 const char *reviewSubTitle,
342 nbgl_choiceCallback_t choiceCallback,
343 bool playNotifSound);
344static void useCaseHomeExt(const char *appName,
345 const nbgl_icon_details_t *appIcon,
346 const char *tagline,
347 bool withSettings,
348 nbgl_homeAction_t *homeAction,
349 nbgl_callback_t topRightCallback,
350 nbgl_callback_t quitCallback);
351static void displayDetails(const char *tag, const char *value, bool wrapping);
352
353static void reset_callbacks(void)
354{
355 onQuit = NULL;
356 onContinue = NULL;
357 onAction = NULL;
358 onNav = NULL;
359 onControls = NULL;
360 onContentAction = NULL;
361 onChoice = NULL;
362 onModalConfirm = NULL;
363#ifdef NBGL_KEYPAD
364 onValidatePin = NULL;
365#endif
366}
367
368// Helper to set genericContext page info
369static void genericContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements, bool flag)
370{
371 uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements) + SET_PAGE_FLAG(flag);
372
373 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
374 &= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
375 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
376 |= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
377}
378
379// Helper to get genericContext page info
380static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool *flag)
381{
382 uint8_t pageData = genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
383 >> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
384 if (nbElements != NULL) {
385 *nbElements = GET_PAGE_NB_ELEMENTS(pageData);
386 }
387 if (flag != NULL) {
388 *flag = GET_PAGE_FLAG(pageData);
389 }
390}
391
392// Simple helper to get the number of elements inside a nbgl_content_t
393static uint8_t getContentNbElement(const nbgl_content_t *content)
394{
395 switch (content->type) {
396 case TAG_VALUE_LIST:
397 return content->content.tagValueList.nbPairs;
398 case SWITCHES_LIST:
399 return content->content.switchesList.nbSwitches;
400 case INFOS_LIST:
401 return content->content.infosList.nbInfos;
402 case CHOICES_LIST:
403 return content->content.choicesList.nbChoices;
404 case BARS_LIST:
405 return content->content.barsList.nbBars;
406 default:
407 return 1;
408 }
409}
410
411// Helper to retrieve the content inside a nbgl_genericContents_t using
412// either the contentsList or using the contentGetterCallback
413static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
414 int8_t contentIdx,
415 nbgl_content_t *content)
416{
417 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
418 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
419 return NULL;
420 }
421
422 if (genericContents->callbackCallNeeded) {
423 // Retrieve content through callback, but first memset the content.
424 memset(content, 0, sizeof(nbgl_content_t));
425 genericContents->contentGetterCallback(contentIdx, content);
426 return content;
427 }
428 else {
429 // Retrieve content through list
430 return PIC(&genericContents->contentsList[contentIdx]);
431 }
432}
433
434static void prepareNavInfo(bool isReview, uint8_t nbPages, const char *rejectText)
435{
436 memset(&navInfo, 0, sizeof(navInfo));
437
438 navInfo.nbPages = nbPages;
439 navInfo.tuneId = TUNE_TAP_CASUAL;
440 navInfo.progressIndicator = false;
441 navInfo.navType = NAV_WITH_BUTTONS;
442
443 if (isReview == false) {
444 navInfo.navWithButtons.navToken = NAV_TOKEN;
445 navInfo.navWithButtons.backButton = true;
446 }
447 else {
448 navInfo.quitToken = REJECT_TOKEN;
449 navInfo.navWithButtons.quitText = rejectText;
450 navInfo.navWithButtons.navToken = NAV_TOKEN;
452 = ((navType == STREAMING_NAV) && (nbPages < 2)) ? false : true;
453 navInfo.navWithButtons.visiblePageIndicator = (navType != STREAMING_NAV);
454 }
455}
456
457static void prepareReviewFirstPage(nbgl_contentCenter_t *contentCenter,
458 const nbgl_icon_details_t *icon,
459 const char *reviewTitle,
460 const char *reviewSubTitle)
461{
462 contentCenter->icon = icon;
463 contentCenter->title = reviewTitle;
464 contentCenter->description = reviewSubTitle;
465 contentCenter->subText = "Swipe to review";
466 contentCenter->smallTitle = NULL;
467 contentCenter->iconHug = 0;
468 contentCenter->padding = false;
469 contentCenter->illustrType = ICON_ILLUSTRATION;
470}
471
472static void prepareReviewLastPage(nbgl_contentInfoLongPress_t *infoLongPress,
473 const nbgl_icon_details_t *icon,
474 const char *finishTitle)
475{
476 infoLongPress->text = finishTitle;
477 infoLongPress->icon = icon;
478 infoLongPress->longPressText = "Hold to sign";
479 infoLongPress->longPressToken = CONFIRM_TOKEN;
480}
481
482static void prepareReviewLightLastPage(nbgl_contentInfoButton_t *infoButton,
483 const nbgl_icon_details_t *icon,
484 const char *finishTitle)
485{
486 infoButton->text = finishTitle;
487 infoButton->icon = icon;
488 infoButton->buttonText = "Approve";
489 infoButton->buttonToken = CONFIRM_TOKEN;
490}
491
492static const char *getRejectReviewText(nbgl_operationType_t operationType)
493{
494 UNUSED(operationType);
495 return "Reject";
496}
497
498// function called when navigating (or exiting) modal details pages
499// or when skip choice is displayed
500static void pageModalCallback(int token, uint8_t index)
501{
502 LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index);
503
504 if (token == INFO_ALIAS_TOKEN) {
505 // the icon next to info type alias has been touched
506 displayFullValuePage(tipBoxInfoList.infoTypes[index],
507 tipBoxInfoList.infoContents[index],
508 &tipBoxInfoList.infoExtensions[index]);
509 return;
510 }
511 nbgl_pageRelease(modalPageContext);
512 modalPageContext = NULL;
513 if (token == NAV_TOKEN) {
514 if (index == EXIT_PAGE) {
515 // redraw the background layer
517 nbgl_refresh();
518 }
519 else {
520 displayDetailsPage(index, false);
521 }
522 }
523 if (token == QUIT_TOKEN) {
524 // redraw the background layer
526 nbgl_refresh();
527 }
528 else if (token == SKIP_TOKEN) {
529 if (index == 0) {
530 // display the last forward only review page, whatever it is
531 displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true);
532 }
533 else {
534 // display background, which should be the page where skip has been touched
536 nbgl_refresh();
537 }
538 }
539 else if (token == CHOICE_TOKEN) {
540 if (index == 0) {
541 if (onModalConfirm != NULL) {
542 onModalConfirm();
543 }
544 }
545 else {
546 // display background, which should be the page where skip has been touched
548 nbgl_refresh();
549 }
550 }
551}
552
553// generic callback for all pages except modal
554static void pageCallback(int token, uint8_t index)
555{
556 LOG_DEBUG(USE_CASE_LOGGER, "pageCallback, token = %d, index = %d\n", token, index);
557 if (token == QUIT_TOKEN) {
558 if (onQuit != NULL) {
559 onQuit();
560 }
561 }
562 else if (token == CONTINUE_TOKEN) {
563 if (onContinue != NULL) {
564 onContinue();
565 }
566 }
567 else if (token == CHOICE_TOKEN) {
568 if (onChoice != NULL) {
569 onChoice((index == 0) ? true : false);
570 }
571 }
572 else if (token == ACTION_BUTTON_TOKEN) {
573 if ((index == 0) && (onAction != NULL)) {
574 onAction();
575 }
576 else if ((index == 1) && (onQuit != NULL)) {
577 onQuit();
578 }
579 }
580#ifdef NBGL_QRCODE
581 else if (token == ADDRESS_QRCODE_BUTTON_TOKEN) {
582 displayAddressQRCode();
583 }
584#endif // NBGL_QRCODE
585 else if (token == CONFIRM_TOKEN) {
586 if (onChoice != NULL) {
587 onChoice(true);
588 }
589 }
590 else if (token == REJECT_TOKEN) {
591 if (onChoice != NULL) {
592 onChoice(false);
593 }
594 }
595 else if (token == DETAILS_BUTTON_TOKEN) {
596 displayDetails(genericContext.detailsItem,
597 genericContext.detailsvalue,
598 genericContext.detailsWrapping);
599 }
600 else if (token == NAV_TOKEN) {
601 if (index == EXIT_PAGE) {
602 if (onQuit != NULL) {
603 onQuit();
604 }
605 }
606 else {
607 if (navType == GENERIC_NAV || navType == STREAMING_NAV) {
608 displayGenericContextPage(index, false);
609 }
610 else if (navType == REVIEW_NAV) {
611 displayReviewPage(index, false);
612 }
613 else {
614 displaySettingsPage(index, false);
615 }
616 }
617 }
618 else if (token == NEXT_TOKEN) {
619 if (onNav != NULL) {
620 displayReviewPage(navInfo.activePage + 1, false);
621 }
622 else {
623 displayGenericContextPage(navInfo.activePage + 1, false);
624 }
625 }
626 else if (token == BACK_TOKEN) {
627 if (onNav != NULL) {
628 displayReviewPage(navInfo.activePage - 1, true);
629 }
630 else {
631 displayGenericContextPage(navInfo.activePage - 1, false);
632 }
633 }
634 else if (token == SKIP_TOKEN) {
635 // display a modal warning to confirm skip
636 displaySkipWarning();
637 }
638 else if (token == VALUE_ALIAS_TOKEN) {
639 // the icon next to value alias has been touched
640 const nbgl_contentTagValue_t *pair;
641 if (genericContext.currentPairs != NULL) {
642 pair = &genericContext.currentPairs[genericContext.currentElementIdx + index];
643 }
644 else {
645 pair = genericContext.currentCallback(genericContext.currentElementIdx + index);
646 }
647 displayFullValuePage(pair->item, pair->value, pair->extension);
648 }
649 else if (token == BLIND_WARNING_TOKEN) {
650 reviewWithWarnCtx.isIntro = false;
651 reviewWithWarnCtx.warning = NULL;
652 displaySecurityReport(1 << BLIND_SIGNING_WARN);
653 }
654 else if (token == WARNING_BUTTON_TOKEN) {
655 // top-right button, display the security modal
656 reviewWithWarnCtx.securityReportLevel = 1;
657 // if preset is used
658 if (reviewWithWarnCtx.warning->predefinedSet) {
659 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
660 }
661 else {
662 // use customized warning
663 if (reviewWithWarnCtx.isIntro) {
664 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->introDetails);
665 }
666 else {
667 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->reviewDetails);
668 }
669 }
670 }
671 else if (token == TIP_BOX_TOKEN) {
672 displayTipBoxModal();
673 }
674 else { // probably a control provided by caller
675 if (onContentAction != NULL) {
676 onContentAction(token, index, navInfo.activePage);
677 }
678 if (onControls != NULL) {
679 onControls(token, index);
680 }
681 }
682}
683
684// callback used for confirmation
685static void tickerCallback(void)
686{
687 nbgl_pageRelease(pageContext);
688 if (onQuit != NULL) {
689 onQuit();
690 }
691}
692
693// function used to display the current page in review
694static void displaySettingsPage(uint8_t page, bool forceFullRefresh)
695{
696 nbgl_pageContent_t content = {0};
697
698 if ((onNav == NULL) || (onNav(page, &content) == false)) {
699 return;
700 }
701
702 // override some fields
703 content.title = pageTitle;
704 content.isTouchableTitle = true;
705 content.titleToken = QUIT_TOKEN;
706 content.tuneId = TUNE_TAP_CASUAL;
707
708 navInfo.activePage = page;
709 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
710
711 if (forceFullRefresh) {
713 }
714 else {
716 }
717}
718
719// function used to display the current page in review
720static void displayReviewPage(uint8_t page, bool forceFullRefresh)
721{
722 nbgl_pageContent_t content = {0};
723
724 // ensure the page is valid
725 if ((navInfo.nbPages != 0) && (page >= (navInfo.nbPages))) {
726 return;
727 }
728 navInfo.activePage = page;
729 if ((onNav == NULL) || (onNav(navInfo.activePage, &content) == false)) {
730 return;
731 }
732
733 // override some fields
734 content.title = NULL;
735 content.isTouchableTitle = false;
736 content.tuneId = TUNE_TAP_CASUAL;
737
738 if (content.type == INFO_LONG_PRESS) { // last page
739 // for forward only review without known length...
740 // if we don't do that we cannot remove the '>' in the navigation bar at the last page
741 navInfo.nbPages = navInfo.activePage + 1;
742 content.infoLongPress.longPressToken = CONFIRM_TOKEN;
743 if (forwardNavOnly) {
744 // remove the "Skip" button
745 navInfo.skipText = NULL;
746 }
747 }
748
749 // override smallCaseForValue for tag/value types to false
750 if (content.type == TAG_VALUE_DETAILS) {
752 // the maximum displayable number of lines for value is NB_MAX_LINES_IN_REVIEW (without More
753 // button)
754 content.tagValueDetails.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
755 }
756 else if (content.type == TAG_VALUE_LIST) {
757 content.tagValueList.smallCaseForValue = false;
758 }
759 else if (content.type == TAG_VALUE_CONFIRM) {
761 // use confirm token for black button
762 content.tagValueConfirm.confirmationToken = CONFIRM_TOKEN;
763 }
764
765 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
766
767 if (forceFullRefresh) {
769 }
770 else {
772 }
773}
774
775// Helper that does the computing of which nbgl_content_t and which element in the content should be
776// displayed for the next generic context navigation page
777static const nbgl_content_t *genericContextComputeNextPageParams(uint8_t pageIdx,
778 nbgl_content_t *content,
779 uint8_t *p_nbElementsInNextPage,
780 bool *p_flag)
781{
782 int8_t nextContentIdx = genericContext.currentContentIdx;
783 int16_t nextElementIdx = genericContext.currentElementIdx;
784 uint8_t nbElementsInNextPage;
785
786 // Retrieve info on the next page
787 genericContextGetPageInfo(pageIdx, &nbElementsInNextPage, p_flag);
788 *p_nbElementsInNextPage = nbElementsInNextPage;
789
790 // Handle forward navigation:
791 // add to current index the number of pairs of the currently active page
792 if (pageIdx > navInfo.activePage) {
793 uint8_t nbElementsInCurrentPage;
794
795 genericContextGetPageInfo(navInfo.activePage, &nbElementsInCurrentPage, NULL);
796 nextElementIdx += nbElementsInCurrentPage;
797
798 // Handle case where the content to be displayed is in the next content
799 // In such case start at element index 0.
800 // If currentContentElementNb == 0, means not initialized, so skip
801 if ((nextElementIdx >= genericContext.currentContentElementNb)
802 && (genericContext.currentContentElementNb > 0)) {
803 nextContentIdx += 1;
804 nextElementIdx = 0;
805 }
806 }
807
808 // Handle backward navigation:
809 // add to current index the number of pairs of the currently active page
810 if (pageIdx < navInfo.activePage) {
811 // Backward navigation: remove to current index the number of pairs of the current page
812 nextElementIdx -= nbElementsInNextPage;
813
814 // Handle case where the content to be displayed is in the previous content
815 // In such case set a negative number as element index so that it is handled
816 // later once the previous content is accessible so that its elements number
817 // can be retrieved.
818 if (nextElementIdx < 0) {
819 nextContentIdx -= 1;
820 nextElementIdx = -nbElementsInNextPage;
821 }
822 }
823
824 const nbgl_content_t *p_content;
825 // Retrieve next content
826 if ((nextContentIdx == -1) && (genericContext.hasStartingContent)) {
827 p_content = &STARTING_CONTENT;
828 }
829 else if ((nextContentIdx == genericContext.genericContents.nbContents)
830 && (genericContext.hasFinishingContent)) {
831 p_content = &FINISHING_CONTENT;
832 }
833 else {
834 p_content = getContentAtIdx(&genericContext.genericContents, nextContentIdx, content);
835
836 if (p_content == NULL) {
837 LOG_DEBUG(USE_CASE_LOGGER, "Fail to retrieve content\n");
838 return NULL;
839 }
840 }
841
842 // Handle cases where we are going to display data from a new content:
843 // - navigation to a previous or to the next content
844 // - First page display (genericContext.currentContentElementNb == 0)
845 //
846 // In such case we need to:
847 // - Update genericContext.currentContentIdx
848 // - Update genericContext.currentContentElementNb
849 // - Update onContentAction callback
850 // - Update nextElementIdx in case it was previously not calculable
851 if ((nextContentIdx != genericContext.currentContentIdx)
852 || (genericContext.currentContentElementNb == 0)) {
853 genericContext.currentContentIdx = nextContentIdx;
854 genericContext.currentContentElementNb = getContentNbElement(p_content);
855 onContentAction = PIC(p_content->contentActionCallback);
856 if (nextElementIdx < 0) {
857 nextElementIdx = genericContext.currentContentElementNb + nextElementIdx;
858 }
859 }
860
861 // Sanity check
862 if ((nextElementIdx < 0) || (nextElementIdx >= genericContext.currentContentElementNb)) {
864 "Invalid element index %d / %d\n",
865 nextElementIdx,
866 genericContext.currentContentElementNb);
867 return NULL;
868 }
869
870 // Update genericContext elements
871 genericContext.currentElementIdx = nextElementIdx;
872 navInfo.activePage = pageIdx;
873
874 return p_content;
875}
876
877// Helper that generates a nbgl_pageContent_t to be displayed for the next generic context
878// navigation page
879static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
880 uint8_t nbElementsInPage,
881 bool flag,
882 nbgl_pageContent_t *pageContent)
883{
884 uint8_t nextElementIdx = genericContext.currentElementIdx;
885
886 pageContent->title = pageTitle;
887 pageContent->isTouchableTitle = false;
888 pageContent->titleToken = QUIT_TOKEN;
889 pageContent->tuneId = TUNE_TAP_CASUAL;
890
891 pageContent->type = p_content->type;
892 switch (pageContent->type) {
893 case CENTERED_INFO:
894 memcpy(&pageContent->centeredInfo,
895 &p_content->content.centeredInfo,
896 sizeof(pageContent->centeredInfo));
897 break;
898 case EXTENDED_CENTER:
899 memcpy(&pageContent->extendedCenter,
900 &p_content->content.extendedCenter,
901 sizeof(pageContent->extendedCenter));
902 break;
903 case INFO_LONG_PRESS:
904 memcpy(&pageContent->infoLongPress,
905 &p_content->content.infoLongPress,
906 sizeof(pageContent->infoLongPress));
907 break;
908
909 case INFO_BUTTON:
910 memcpy(&pageContent->infoButton,
911 &p_content->content.infoButton,
912 sizeof(pageContent->infoButton));
913 break;
914
915 case TAG_VALUE_LIST: {
916 nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
917
918 // memorize pairs (or callback) for usage when alias is used
919 genericContext.currentPairs = p_content->content.tagValueList.pairs;
920 genericContext.currentCallback = p_content->content.tagValueList.callback;
921
922 if (flag) {
923 // Flag can be set if the pair is too long to fit or because it needs
924 // to be displayed as centered info.
925
926 // First retrieve the pair
927 const nbgl_layoutTagValue_t *pair;
928
929 if (p_content->content.tagValueList.pairs != NULL) {
930 pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
931 }
932 else {
933 pair = PIC(p_content->content.tagValueList.callback(nextElementIdx));
934 }
935
936 if (pair->centeredInfo) {
937 pageContent->type = EXTENDED_CENTER;
938 prepareReviewFirstPage(&pageContent->extendedCenter.contentCenter,
939 pair->valueIcon,
940 pair->item,
941 pair->value);
942
943 // Skip population of nbgl_contentTagValueList_t structure
944 p_tagValueList = NULL;
945 }
946 else {
947 // if the pair is too long to fit, we use a TAG_VALUE_DETAILS content
948 pageContent->type = TAG_VALUE_DETAILS;
949 pageContent->tagValueDetails.detailsButtonText = "More";
950 pageContent->tagValueDetails.detailsButtonIcon = NULL;
951 pageContent->tagValueDetails.detailsButtonToken = DETAILS_BUTTON_TOKEN;
952
953 p_tagValueList = &pageContent->tagValueDetails.tagValueList;
954
955 // Backup pair info for easy data access upon click on button
956 genericContext.detailsItem = pair->item;
957 genericContext.detailsvalue = pair->value;
958 genericContext.detailsWrapping = p_content->content.tagValueList.wrapping;
959 }
960 }
961
962 if (p_tagValueList != NULL) {
963 p_tagValueList->nbPairs = nbElementsInPage;
964 p_tagValueList->token = p_content->content.tagValueList.token;
965 if (p_content->content.tagValueList.pairs != NULL) {
966 p_tagValueList->pairs
967 = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
968 // parse pairs to check if any contains an alias for value
969 for (uint8_t i = 0; i < nbElementsInPage; i++) {
970 if (p_tagValueList->pairs[i].aliasValue) {
971 p_tagValueList->token = VALUE_ALIAS_TOKEN;
972 break;
973 }
974 }
975 }
976 else {
977 p_tagValueList->pairs = NULL;
978 p_tagValueList->callback = p_content->content.tagValueList.callback;
979 p_tagValueList->startIndex = nextElementIdx;
980 // parse pairs to check if any contains an alias for value
981 for (uint8_t i = 0; i < nbElementsInPage; i++) {
982 const nbgl_layoutTagValue_t *pair
983 = PIC(p_content->content.tagValueList.callback(nextElementIdx + i));
984 if (pair->aliasValue) {
985 p_tagValueList->token = VALUE_ALIAS_TOKEN;
986 break;
987 }
988 }
989 }
990 p_tagValueList->smallCaseForValue = false;
991 p_tagValueList->nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
992 p_tagValueList->wrapping = p_content->content.tagValueList.wrapping;
993 }
994
995 break;
996 }
998 memcpy(&pageContent->tagValueConfirm,
999 &p_content->content.tagValueConfirm,
1000 sizeof(pageContent->tagValueConfirm));
1001 break;
1002 case SWITCHES_LIST:
1003 pageContent->switchesList.nbSwitches = nbElementsInPage;
1004 pageContent->switchesList.switches
1005 = PIC(&p_content->content.switchesList.switches[nextElementIdx]);
1006 break;
1007 case INFOS_LIST:
1008 pageContent->infosList.nbInfos = nbElementsInPage;
1009 pageContent->infosList.infoTypes
1010 = PIC(&p_content->content.infosList.infoTypes[nextElementIdx]);
1011 pageContent->infosList.infoContents
1012 = PIC(&p_content->content.infosList.infoContents[nextElementIdx]);
1013 break;
1014 case CHOICES_LIST:
1015 memcpy(&pageContent->choicesList,
1016 &p_content->content.choicesList,
1017 sizeof(pageContent->choicesList));
1018 pageContent->choicesList.nbChoices = nbElementsInPage;
1019 pageContent->choicesList.names
1020 = PIC(&p_content->content.choicesList.names[nextElementIdx]);
1021 if ((p_content->content.choicesList.initChoice >= nextElementIdx)
1022 && (p_content->content.choicesList.initChoice
1023 < nextElementIdx + nbElementsInPage)) {
1024 pageContent->choicesList.initChoice
1025 = p_content->content.choicesList.initChoice - nextElementIdx;
1026 }
1027 else {
1028 pageContent->choicesList.initChoice = nbElementsInPage;
1029 }
1030 break;
1031 case BARS_LIST:
1032 pageContent->barsList.nbBars = nbElementsInPage;
1033 pageContent->barsList.barTexts
1034 = PIC(&p_content->content.barsList.barTexts[nextElementIdx]);
1035 pageContent->barsList.tokens = PIC(&p_content->content.barsList.tokens[nextElementIdx]);
1036 pageContent->barsList.tuneId = p_content->content.barsList.tuneId;
1037 break;
1038 default:
1039 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported type %d\n", pageContent->type);
1040 return false;
1041 }
1042
1043 bool isFirstOrLastPage
1044 = ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
1045 || (p_content->type == INFO_LONG_PRESS);
1046 nbgl_operationType_t operationType
1047 = (navType == STREAMING_NAV)
1048 ? bundleNavContext.reviewStreaming.operationType
1049 : ((navType == GENERIC_NAV) ? bundleNavContext.review.operationType : 0);
1050
1051 // if first or last page of review and blind/risky operation, add the warning top-right button
1052 if (isFirstOrLastPage && (operationType & (BLIND_OPERATION | RISKY_OPERATION))) {
1053 // if issue is only Web3Checks issue, use '!' icon
1054 if ((operationType & RISKY_OPERATION)
1055 && (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_ISSUE_WARN))) {
1056 pageContent->topRightIcon = &EXCLAMATION_ICON;
1057 }
1058 else {
1059 pageContent->topRightIcon = &WARNING_ICON;
1060 }
1061
1062 pageContent->topRightToken
1063 = (operationType & BLIND_OPERATION) ? BLIND_WARNING_TOKEN : WARNING_BUTTON_TOKEN;
1064 }
1065
1066 return true;
1067}
1068
1069// function used to display the current page in generic context navigation mode
1070static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh)
1071{
1072 // Retrieve next page parameters
1073 nbgl_content_t content;
1074 uint8_t nbElementsInPage;
1075 bool flag;
1076 const nbgl_content_t *p_content = NULL;
1077
1078 if (navType == STREAMING_NAV) {
1079 if (pageIdx == LAST_PAGE_FOR_REVIEW) {
1080 if (bundleNavContext.reviewStreaming.skipCallback != NULL) {
1081 bundleNavContext.reviewStreaming.skipCallback();
1082 }
1083 return;
1084 }
1085 else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) {
1086 bundleNavReviewStreamingChoice(true);
1087 return;
1088 }
1089 }
1090
1091 if (navInfo.activePage == pageIdx) {
1092 p_content
1093 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1094 }
1095 else if (navInfo.activePage < pageIdx) {
1096 // Support going more than one step forward.
1097 // It occurs when initializing a navigation on an arbitrary page
1098 for (int i = navInfo.activePage + 1; i <= pageIdx; i++) {
1099 p_content = genericContextComputeNextPageParams(i, &content, &nbElementsInPage, &flag);
1100 }
1101 }
1102 else {
1103 if (pageIdx - navInfo.activePage > 1) {
1104 // We don't support going more than one step backward as it doesn't occurs for now?
1105 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported navigation\n");
1106 return;
1107 }
1108 p_content
1109 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1110 }
1111
1112 if (p_content == NULL) {
1113 return;
1114 }
1115
1116 // Create next page content
1117 nbgl_pageContent_t pageContent = {0};
1118 if (!genericContextPreparePageContent(p_content, nbElementsInPage, flag, &pageContent)) {
1119 return;
1120 }
1121
1122 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &pageContent);
1123
1124 if (forceFullRefresh) {
1126 }
1127 else {
1129 }
1130}
1131
1132// from the current details context, return a pointer on the details at the given page
1133static const char *getDetailsPageAt(uint8_t detailsPage)
1134{
1135 uint8_t page = 0;
1136 const char *currentChar = detailsContext.value;
1137 while (page < detailsPage) {
1138 uint16_t nbLines
1139 = nbgl_getTextNbLinesInWidth(SMALL_BOLD_FONT, currentChar, AVAILABLE_WIDTH, false);
1140 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1141 uint16_t len;
1142 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1143 currentChar,
1145 NB_MAX_LINES_IN_DETAILS,
1146 &len,
1147 detailsContext.wrapping);
1148 len -= 3;
1149 currentChar = currentChar + len;
1150 }
1151 page++;
1152 }
1153 return currentChar;
1154}
1155
1156// function used to display the current page in details review mode
1157static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh)
1158{
1159 static nbgl_layoutTagValue_t currentPair;
1160 nbgl_pageNavigationInfo_t info = {.activePage = detailsPage,
1161 .nbPages = detailsContext.nbPages,
1162 .navType = NAV_WITH_BUTTONS,
1163 .quitToken = QUIT_TOKEN,
1164 .navWithButtons.navToken = NAV_TOKEN,
1165 .navWithButtons.quitButton = true,
1166 .navWithButtons.backButton = true,
1167 .navWithButtons.quitText = NULL,
1168 .progressIndicator = false,
1169 .tuneId = TUNE_TAP_CASUAL};
1171 .topRightIcon = NULL,
1172 .tagValueList.nbPairs = 1,
1173 .tagValueList.pairs = &currentPair,
1174 .tagValueList.smallCaseForValue = true,
1175 .tagValueList.wrapping = detailsContext.wrapping};
1176
1177 if (modalPageContext != NULL) {
1178 nbgl_pageRelease(modalPageContext);
1179 }
1180 currentPair.item = detailsContext.tag;
1181 // if move backward or first page
1182 if (detailsPage <= detailsContext.currentPage) {
1183 // recompute current start from beginning
1184 currentPair.value = getDetailsPageAt(detailsPage);
1185 forceFullRefresh = true;
1186 }
1187 // else move forward
1188 else {
1189 currentPair.value = detailsContext.nextPageStart;
1190 }
1191 detailsContext.currentPage = detailsPage;
1193 SMALL_BOLD_FONT, currentPair.value, AVAILABLE_WIDTH, detailsContext.wrapping);
1194
1195 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1196 uint16_t len;
1197 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1198 currentPair.value,
1200 NB_MAX_LINES_IN_DETAILS,
1201 &len,
1202 detailsContext.wrapping);
1203 len -= 3;
1204 // memorize next position to save processing
1205 detailsContext.nextPageStart = currentPair.value + len;
1206 // use special feature to keep only NB_MAX_LINES_IN_DETAILS lines and replace the last 3
1207 // chars by "..."
1208 content.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_DETAILS;
1209 }
1210 else {
1211 detailsContext.nextPageStart = NULL;
1212 content.tagValueList.nbMaxLinesForValue = 0;
1213 }
1214 if (info.nbPages == 1) {
1215 // if only one page, no navigation bar, and use a footer instead
1216 info.navWithButtons.quitText = "Close";
1217 }
1218 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1219
1220 if (forceFullRefresh) {
1222 }
1223 else {
1225 }
1226}
1227
1228// function used to display the content of a full value, when touching an alias of a tag/value pair
1229static void displayFullValuePage(const char *backText,
1230 const char *aliasText,
1231 const nbgl_contentValueExt_t *extension)
1232{
1233 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1234 .withLeftBorder = true,
1235 .onActionCallback = &modalLayoutTouchCallback,
1236 .tapActionText = NULL};
1238 .separationLine = false,
1239 .backAndText.token = 0,
1240 .backAndText.tuneId = TUNE_TAP_CASUAL,
1241 .backAndText.text = PIC(backText)};
1242 genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1243 // add header with the tag part of the pair, to go back
1244 nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1245 // add either QR Code or full value text
1246 if (extension->aliasType == QR_CODE_ALIAS) {
1247#ifdef NBGL_QRCODE
1248 nbgl_layoutQRCode_t qrCode
1249 = {.url = extension->fullValue,
1250 .text1 = (extension->title != NULL) ? extension->title : extension->fullValue,
1251 .text2 = extension->explanation,
1252 .centered = true,
1253 .offsetY = 0};
1254
1255 nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode);
1256#endif // NBGL_QRCODE
1257 }
1258 else {
1259 const char *info;
1260 // add full value text
1261 if (extension->aliasType == ENS_ALIAS) {
1262 info = "ENS names are resolved by Ledger backend.";
1263 }
1264 else if (extension->aliasType == ADDRESS_BOOK_ALIAS) {
1265 info = "This account label comes from your Address Book in Ledger Live.";
1266 }
1267 else {
1268 info = extension->explanation;
1269 }
1271 genericContext.modalLayout, aliasText, extension->fullValue, info);
1272 }
1273 // draw & refresh
1274 nbgl_layoutDraw(genericContext.modalLayout);
1275 nbgl_refresh();
1276}
1277
1278// function used to display the modal containing tip-box infos
1279static void displayTipBoxModal(void)
1280{
1282 .nbPages = 1,
1283 .navType = NAV_WITH_BUTTONS,
1284 .quitToken = QUIT_TOKEN,
1285 .navWithButtons.navToken = NAV_TOKEN,
1286 .navWithButtons.quitButton = false,
1287 .navWithButtons.backButton = true,
1288 .navWithButtons.quitText = NULL,
1289 .progressIndicator = false,
1290 .tuneId = TUNE_TAP_CASUAL};
1291 nbgl_pageContent_t content = {.type = INFOS_LIST,
1292 .topRightIcon = NULL,
1293 .infosList.nbInfos = tipBoxInfoList.nbInfos,
1294 .infosList.withExtensions = tipBoxInfoList.withExtensions,
1295 .infosList.infoTypes = tipBoxInfoList.infoTypes,
1296 .infosList.infoContents = tipBoxInfoList.infoContents,
1297 .infosList.infoExtensions = tipBoxInfoList.infoExtensions,
1298 .infosList.token = INFO_ALIAS_TOKEN,
1299 .title = tipBoxModalTitle,
1300 .titleToken = QUIT_TOKEN,
1301 .tuneId = TUNE_TAP_CASUAL};
1302
1303 if (modalPageContext != NULL) {
1304 nbgl_pageRelease(modalPageContext);
1305 }
1306 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1307
1309}
1310
1311#ifdef NBGL_QRCODE
1312static void displayAddressQRCode(void)
1313{
1314 // display the address as QR Code
1315 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1316 .withLeftBorder = true,
1317 .onActionCallback = &modalLayoutTouchCallback,
1318 .tapActionText = NULL};
1319 nbgl_layoutHeader_t headerDesc = {
1320 .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1321 nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1322 .text1 = NULL,
1323 .centered = true,
1324 .offsetY = 0};
1325
1326 addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1327 // add empty header for better look
1328 nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1329 // compute nb lines to check whether it shall be shorten (max is 3 lines)
1331 SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1332
1333 if (nbLines <= QRCODE_NB_MAX_LINES) {
1334 qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1335 }
1336 else {
1337 // only keep beginning and end of text, and add ... in the middle
1338 nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1339 addressConfirmationContext.tagValuePair.value,
1341 QRCODE_NB_MAX_LINES,
1342 reducedAddress,
1343 QRCODE_REDUCED_ADDR_LEN);
1344 qrCode.text2 = reducedAddress; // in gray
1345 }
1346
1347 nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1348
1350 addressConfirmationContext.modalLayout, "Close", DISMISS_QR_TOKEN, TUNE_TAP_CASUAL);
1351 nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1352 nbgl_refresh();
1353}
1354
1355#endif // NBGL_QRCODE
1356
1357// called when header is touched on modal page, to dismiss it
1358static void modalLayoutTouchCallback(int token, uint8_t index)
1359{
1360 UNUSED(index);
1361 if (token == DISMISS_QR_TOKEN) {
1362 // dismiss modal
1363 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1364 addressConfirmationContext.modalLayout = NULL;
1366 }
1367 else if (token == DISMISS_WARNING_TOKEN) {
1368 // dismiss modal
1369 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1370 // if already at first level, simply redraw the background
1371 if (reviewWithWarnCtx.securityReportLevel <= 1) {
1372 reviewWithWarnCtx.modalLayout = NULL;
1374 }
1375 else {
1376 // We are at level 2 of warning modal, so re-display the level 1
1377 reviewWithWarnCtx.securityReportLevel = 1;
1378 // if preset is used
1379 if (reviewWithWarnCtx.warning->predefinedSet) {
1380 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1381 }
1382 else {
1383 // use customized warning
1384 const nbgl_warningDetails_t *details
1385 = (reviewWithWarnCtx.isIntro) ? reviewWithWarnCtx.warning->introDetails
1386 : reviewWithWarnCtx.warning->reviewDetails;
1387 displayCustomizedSecurityReport(details);
1388 }
1389 return;
1390 }
1391 }
1392 else if ((token >= FIRST_WARN_BAR_TOKEN) && (token <= LAST_WARN_BAR_TOKEN)) {
1393 // dismiss modal before creating a new one
1394 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1395 reviewWithWarnCtx.securityReportLevel = 2;
1396 // if preset is used
1397 if (reviewWithWarnCtx.warning->predefinedSet) {
1398 displaySecurityReport(1 << (token - FIRST_WARN_BAR_TOKEN));
1399 }
1400 else {
1401 // use customized warning
1402 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1403 ? reviewWithWarnCtx.warning->introDetails
1404 : reviewWithWarnCtx.warning->reviewDetails;
1405 if (details->type == BAR_LIST_WARNING) {
1406 displayCustomizedSecurityReport(
1407 &details->barList.details[token - FIRST_WARN_BAR_TOKEN]);
1408 }
1409 }
1410 return;
1411 }
1412 else {
1413 // dismiss modal
1414 nbgl_layoutRelease(genericContext.modalLayout);
1415 genericContext.modalLayout = NULL;
1417 }
1418 nbgl_refresh();
1419}
1420
1421// called when item are touched in layout
1422static void layoutTouchCallback(int token, uint8_t index)
1423{
1424 // choice buttons in initial warning page
1425 if (token == WARNING_CHOICE_TOKEN) {
1426 if (index == 0) { // top button to exit
1427 reviewWithWarnCtx.choiceCallback(false);
1428 }
1429 else { // bottom button to continue to review
1430 reviewWithWarnCtx.isIntro = false;
1431 if (reviewWithWarnCtx.isStreaming) {
1432 useCaseReviewStreamingStart(reviewWithWarnCtx.operationType,
1433 reviewWithWarnCtx.icon,
1434 reviewWithWarnCtx.reviewTitle,
1435 reviewWithWarnCtx.reviewSubTitle,
1436 reviewWithWarnCtx.choiceCallback,
1437 false);
1438 }
1439 else {
1440 useCaseReview(reviewWithWarnCtx.operationType,
1441 reviewWithWarnCtx.tagValueList,
1442 reviewWithWarnCtx.icon,
1443 reviewWithWarnCtx.reviewTitle,
1444 reviewWithWarnCtx.reviewSubTitle,
1445 reviewWithWarnCtx.finishTitle,
1446 reviewWithWarnCtx.tipBox,
1447 reviewWithWarnCtx.choiceCallback,
1448 false,
1449 false);
1450 }
1451 }
1452 }
1453 // top-right button in initial warning page
1454 else if (token == WARNING_BUTTON_TOKEN) {
1455 // start at first level of security report
1456 reviewWithWarnCtx.securityReportLevel = 1;
1457 // if preset is used
1458 if (reviewWithWarnCtx.warning->predefinedSet) {
1459 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1460 }
1461 else {
1462 // use customized warning
1463 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1464 ? reviewWithWarnCtx.warning->introDetails
1465 : reviewWithWarnCtx.warning->reviewDetails;
1466 displayCustomizedSecurityReport(details);
1467 }
1468 }
1469}
1470
1471// called when skip button is touched in footer, during forward only review
1472static void displaySkipWarning(void)
1473{
1475 .cancelText = "Go back to review",
1476 .centeredInfo.text1 = "Skip review?",
1477 .centeredInfo.text2
1478 = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1479 .centeredInfo.text3 = NULL,
1480 .centeredInfo.style = LARGE_CASE_INFO,
1481 .centeredInfo.icon = &C_Important_Circle_64px,
1482 .centeredInfo.offsetY = 0,
1483 .confirmationText = "Yes, skip",
1484 .confirmationToken = SKIP_TOKEN,
1485 .tuneId = TUNE_TAP_CASUAL,
1486 .modal = true};
1487 if (modalPageContext != NULL) {
1488 nbgl_pageRelease(modalPageContext);
1489 }
1490 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1492}
1493
1494#ifdef NBGL_KEYPAD
1495// called to update the keypad and automatically show / hide:
1496// - backspace key if no digits are present
1497// - validation key if the min digit is reached
1498// - keypad if the max number of digit is reached
1499static void updateKeyPad(bool add)
1500{
1501 bool enableValidate, enableBackspace, enableDigits;
1502 bool redrawKeypad = false;
1504
1505 enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1506 enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1507 enableBackspace = (keypadContext.pinLen > 0);
1508 if (add) {
1509 if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1510 || // activate "validate" button on keypad
1511 (keypadContext.pinLen == keypadContext.pinMaxDigits)
1512 || // deactivate "digits" on keypad
1513 (keypadContext.pinLen == 1)) { // activate "backspace"
1514 redrawKeypad = true;
1515 }
1516 }
1517 else { // remove
1518 if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1519 (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1520 || // deactivate "validate" button on keypad
1521 (keypadContext.pinLen
1522 == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1523 redrawKeypad = true;
1524 }
1525 }
1526 if (keypadContext.hidden == true) {
1527 nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1528 }
1529 else {
1531 keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1532 }
1533 if (redrawKeypad) {
1535 keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1536 }
1537
1538 if ((!add) && (keypadContext.pinLen == 0)) {
1539 // Full refresh to fully clean the bullets when reaching 0 digits
1540 mode = FULL_COLOR_REFRESH;
1541 }
1543}
1544
1545// called when a key is touched on the keypad
1546static void keypadCallback(char touchedKey)
1547{
1548 switch (touchedKey) {
1549 case BACKSPACE_KEY:
1550 if (keypadContext.pinLen > 0) {
1551 keypadContext.pinLen--;
1552 keypadContext.pinEntry[keypadContext.pinLen] = 0;
1553 }
1554 updateKeyPad(false);
1555 break;
1556
1557 case VALIDATE_KEY:
1558 // Gray out keyboard / buttons as a first user feedback
1559 nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1562
1563 onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1564 break;
1565
1566 default:
1567 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1568 if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1569 keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1570 keypadContext.pinLen++;
1571 }
1572 updateKeyPad(true);
1573 }
1574 break;
1575 }
1576}
1577
1578// called to create a keypad, with either hidden or visible digits
1579static void keypadGenericUseCase(const char *title,
1580 uint8_t minDigits,
1581 uint8_t maxDigits,
1582 uint8_t backToken,
1583 bool shuffled,
1584 bool hidden,
1585 tune_index_e tuneId,
1586 nbgl_pinValidCallback_t validatePinCallback,
1587 nbgl_layoutTouchCallback_t actionCallback)
1588{
1589 nbgl_layoutDescription_t layoutDescription = {0};
1591 .separationLine = true,
1592 .backAndText.token = backToken,
1593 .backAndText.tuneId = tuneId,
1594 .backAndText.text = NULL};
1595 int status = -1;
1596
1597 if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1598 return;
1599 }
1600
1601 reset_callbacks();
1602 // reset the keypad context
1603 memset(&keypadContext, 0, sizeof(KeypadContext_t));
1604
1605 // get a layout
1606 layoutDescription.onActionCallback = actionCallback;
1607 layoutDescription.modal = false;
1608 layoutDescription.withLeftBorder = false;
1609 keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1610 keypadContext.hidden = hidden;
1611
1612 // set back key in header
1613 nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1614
1615 // add keypad
1616 status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1617 if (status < 0) {
1618 return;
1619 }
1620 // add keypad content
1622 keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1623
1624 if (status < 0) {
1625 return;
1626 }
1627
1628 // validation pin callback
1629 onValidatePin = validatePinCallback;
1630 // pin code acceptable lengths
1631 keypadContext.pinMinDigits = minDigits;
1632 keypadContext.pinMaxDigits = maxDigits;
1633
1634 nbgl_layoutDraw(keypadContext.layoutCtx);
1636}
1637#endif
1638
1639static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1640 uint8_t pageIdxStart,
1641 bool isLast,
1642 bool isSkippable)
1643{
1644 uint8_t nbElements = 0;
1645 uint8_t nbPages = 0;
1646 uint8_t nbElementsInPage;
1647 uint8_t elemIdx = 0;
1648 bool flag;
1649
1650 nbElements = getContentNbElement(content);
1651
1652 while (nbElements > 0) {
1653 flag = 0;
1654 // if the current page is not the first one (or last), a navigation bar exists
1655 bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1656 if (content->type == TAG_VALUE_LIST) {
1657 nbElementsInPage = nbgl_useCaseGetNbTagValuesInPageExt(
1658 nbElements, &content->content.tagValueList, elemIdx, isSkippable, &flag);
1659 }
1660 else if (content->type == INFOS_LIST) {
1661 nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1662 nbElements, &content->content.infosList, elemIdx, hasNav);
1663 }
1664 else if (content->type == SWITCHES_LIST) {
1665 nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1666 nbElements, &content->content.switchesList, elemIdx, hasNav);
1667 }
1668 else if (content->type == BARS_LIST) {
1669 nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1670 nbElements, &content->content.barsList, elemIdx, hasNav);
1671 }
1672 else if (content->type == CHOICES_LIST) {
1673 nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1674 nbElements, &content->content.choicesList, elemIdx, hasNav);
1675 }
1676 else {
1677 nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1678 }
1679
1680 elemIdx += nbElementsInPage;
1681 genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1682 nbElements -= nbElementsInPage;
1683 nbPages++;
1684 }
1685
1686 return nbPages;
1687}
1688
1689static uint8_t getNbPagesForGenericContents(const nbgl_genericContents_t *genericContents,
1690 uint8_t pageIdxStart,
1691 bool isSkippable)
1692{
1693 uint8_t nbPages = 0;
1694 nbgl_content_t content;
1695 const nbgl_content_t *p_content;
1696
1697 for (int i = 0; i < genericContents->nbContents; i++) {
1698 p_content = getContentAtIdx(genericContents, i, &content);
1699 if (p_content == NULL) {
1700 return 0;
1701 }
1702 nbPages += nbgl_useCaseGetNbPagesForContent(p_content,
1703 pageIdxStart + nbPages,
1704 (i == (genericContents->nbContents - 1)),
1705 isSkippable);
1706 }
1707
1708 return nbPages;
1709}
1710
1711static void prepareAddressConfirmationPages(const char *address,
1712 const nbgl_contentTagValueList_t *tagValueList,
1713 nbgl_content_t *firstPageContent,
1714 nbgl_content_t *secondPageContent)
1715{
1716 nbgl_contentTagValueConfirm_t *tagValueConfirm;
1717
1718 addressConfirmationContext.tagValuePair.item = "Address";
1719 addressConfirmationContext.tagValuePair.value = address;
1720
1721 // First page
1722 firstPageContent->type = TAG_VALUE_CONFIRM;
1723 tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1724
1725#ifdef NBGL_QRCODE
1726 tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1727 // only use "Show as QR" when it's not the last page
1728 if (tagValueList != NULL) {
1729 tagValueConfirm->detailsButtonText = "Show as QR";
1730 }
1731 else {
1732 tagValueConfirm->detailsButtonText = NULL;
1733 }
1734 tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1735#else // NBGL_QRCODE
1736 tagValueConfirm->detailsButtonText = NULL;
1737 tagValueConfirm->detailsButtonIcon = NULL;
1738#endif // NBGL_QRCODE
1739 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1740 tagValueConfirm->tagValueList.nbPairs = 1;
1741 tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1742 tagValueConfirm->tagValueList.smallCaseForValue = false;
1743 tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1744 tagValueConfirm->tagValueList.wrapping = false;
1745 // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1746 // no confirmation button
1747 if (tagValueList != NULL) {
1748 tagValueConfirm->confirmationText = NULL;
1749 }
1750 else {
1751 // otherwise no tap to continue but a confirmation button
1752 tagValueConfirm->confirmationText = "Confirm";
1753 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1754 }
1755
1756 // Second page if any:
1757 if (tagValueList != NULL) {
1758 // the second page is dedicated to the extended tag/value pairs
1759 secondPageContent->type = TAG_VALUE_CONFIRM;
1760 tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1761 tagValueConfirm->confirmationText = "Confirm";
1762 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1763 tagValueConfirm->detailsButtonText = NULL;
1764 tagValueConfirm->detailsButtonIcon = NULL;
1765 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1766 memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1767 }
1768}
1769
1770static void bundleNavStartHome(void)
1771{
1772 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1773
1774 useCaseHomeExt(context->appName,
1775 context->appIcon,
1776 context->tagline,
1777 context->settingContents != NULL ? true : false,
1778 &context->homeAction,
1779 bundleNavStartSettings,
1780 context->quitCallback);
1781}
1782
1783static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1784{
1785 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1786
1787 nbgl_useCaseGenericSettings(context->appName,
1788 initSettingPage,
1789 context->settingContents,
1790 context->infosList,
1791 bundleNavStartHome);
1792}
1793
1794static void bundleNavStartSettings(void)
1795{
1796 bundleNavStartSettingsAtPage(0);
1797}
1798
1799static void bundleNavReviewConfirmRejection(void)
1800{
1801 bundleNavContext.review.choiceCallback(false);
1802}
1803
1804static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1805 nbgl_callback_t callback)
1806{
1807 const char *title;
1808 const char *confirmText;
1809 // clear skip and blind bits
1810 operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION | RISKY_OPERATION);
1811 if (operationType == TYPE_TRANSACTION) {
1812 title = "Reject transaction?";
1813 confirmText = "Go back to transaction";
1814 }
1815 else if (operationType == TYPE_MESSAGE) {
1816 title = "Reject message?";
1817 confirmText = "Go back to message";
1818 }
1819 else {
1820 title = "Reject operation?";
1821 confirmText = "Go back to operation";
1822 }
1823
1824 // display a choice to confirm/cancel rejection
1825 nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1826}
1827
1828static void bundleNavReviewChoice(bool confirm)
1829{
1830 if (confirm) {
1831 bundleNavContext.review.choiceCallback(true);
1832 }
1833 else {
1834 bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1835 bundleNavReviewConfirmRejection);
1836 }
1837}
1838
1839static void bundleNavReviewStreamingConfirmRejection(void)
1840{
1841 bundleNavContext.reviewStreaming.choiceCallback(false);
1842}
1843
1844static void bundleNavReviewStreamingChoice(bool confirm)
1845{
1846 if (confirm) {
1847 // Display a spinner if it wasn't the finish step
1848 if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1849 nbgl_useCaseSpinner("Processing");
1850 }
1851 bundleNavContext.reviewStreaming.choiceCallback(true);
1852 }
1853 else {
1854 bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1855 bundleNavReviewStreamingConfirmRejection);
1856 }
1857}
1858
1859// function used to display the security level page in modal
1860// it can be either a single page with text or QR code, or a list of touchable bars leading
1861// to single pages
1862static void displaySecurityReport(uint32_t set)
1863{
1864 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1865 .withLeftBorder = true,
1866 .onActionCallback = modalLayoutTouchCallback,
1867 .tapActionText = NULL};
1869 .separationLine = true,
1870 .backAndText.icon = NULL,
1871 .backAndText.tuneId = TUNE_TAP_CASUAL,
1872 .backAndText.token = DISMISS_WARNING_TOKEN};
1873 nbgl_layoutFooter_t footerDesc
1874 = {.type = FOOTER_EMPTY, .separationLine = false, .emptySpace.height = 0};
1875 uint8_t i;
1876 uint8_t nbWarnings = 0;
1877 const char *provider;
1878
1879 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
1880
1881 // count the number of warnings
1882 for (i = 0; i < NB_WARNING_TYPES; i++) {
1883 if (set & (1 << i)) {
1884 nbWarnings++;
1885 }
1886 }
1887
1888 if ((nbWarnings > 1) && (reviewWithWarnCtx.securityReportLevel == 1)) {
1889 // if more than one warning warning, so use a list of touchable bars
1890 for (i = 0; i < NB_WARNING_TYPES; i++) {
1891 if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) {
1892 nbgl_layoutBar_t bar;
1893 if ((i == BLIND_SIGNING_WARN)
1894 || (reviewWithWarnCtx.warning->providerMessage == NULL)) {
1895 bar.subText = securityReportItems[i].subText;
1896 }
1897 else {
1898 snprintf(tmpString,
1899 W3C_DESCRIPTION_MAX_LEN,
1900 "Web3 Checks found a risk:\n%s.",
1901 reviewWithWarnCtx.warning->providerMessage);
1902 bar.subText = tmpString;
1903 }
1904 bar.text = securityReportItems[i].text;
1905 bar.iconRight = &PUSH_ICON;
1906 bar.iconLeft = securityReportItems[i].icon;
1907 bar.token = FIRST_WARN_BAR_TOKEN + i;
1908 bar.tuneId = TUNE_TAP_CASUAL;
1909 bar.large = false;
1910 bar.inactive = false;
1911 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
1912 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
1913 }
1914 }
1915 headerDesc.backAndText.text = "Security report";
1916 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
1917 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
1918 nbgl_refresh();
1919 return;
1920 }
1921 if (reviewWithWarnCtx.warning && reviewWithWarnCtx.warning->reportProvider) {
1922 provider = reviewWithWarnCtx.warning->reportProvider;
1923 }
1924 else {
1925 provider = "[unknown]";
1926 }
1927 if (set & (1 << BLIND_SIGNING_WARN)) {
1928 if (reviewWithWarnCtx.isIntro) {
1929#ifdef NBGL_QRCODE
1930 // display a QR Code if in intro
1931 nbgl_layoutQRCode_t qrCode
1932 = {.url = "ledger.com/e8",
1933 .text1 = "ledger.com/e8",
1934 .text2 = "Scan to learn about the risks of blind signing.",
1935 .centered = true,
1936 .offsetY = 0};
1937 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode);
1938 footerDesc.emptySpace.height = 24;
1939#endif // NBGL_QRCODE
1940 headerDesc.backAndText.text = "Blind signing report";
1941 }
1942 else {
1943 // display a centered if in review
1944 nbgl_contentCenter_t info = {0};
1945 info.icon = &C_Warning_64px;
1946 info.title = "Blind Signing";
1947 info.description
1948 = "This transaction's details are not fully verifiable. If you sign it, you could "
1949 "lose all your assets.\n\n"
1950 "Learn about blind signing:\nledger.com/e8";
1951 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
1952 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
1953 headerDesc.separationLine = false;
1954 }
1955 }
1956 else if (set & (1 << W3C_ISSUE_WARN)) {
1957 // if W3 Checks issue, display a centered info
1958 nbgl_contentCenter_t info = {0};
1959 info.icon = &C_Important_Circle_64px;
1960 info.title = "Web3 Checks could not verify this message";
1961 info.description = "An issue prevented Web3 Checks from running.\nGet help: ledger.com/e11";
1962 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
1963 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
1964 headerDesc.separationLine = false;
1965 }
1966 else if (set & (1 << W3C_THREAT_DETECTED_WARN)) {
1967 if (reviewWithWarnCtx.isIntro) {
1968#ifdef NBGL_QRCODE
1969 // display a QR Code if in intro
1970 nbgl_layoutQRCode_t qrCode = {.url = reviewWithWarnCtx.warning->reportUrl,
1971 .text1 = reviewWithWarnCtx.warning->reportUrl,
1972 .text2 = tmpString,
1973 .centered = true,
1974 .offsetY = 0};
1975 snprintf(tmpString,
1976 W3C_DESCRIPTION_MAX_LEN,
1977 "Scan to view the threat report from %s.",
1978 provider);
1979 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode);
1980 footerDesc.emptySpace.height = 24;
1981#endif // NBGL_QRCODE
1982 headerDesc.backAndText.text = "Web3 Checks threat report";
1983 }
1984 else {
1985 // display a centered if in review
1986 nbgl_contentCenter_t info = {0};
1987 info.icon = &C_Warning_64px;
1988 info.title = "Threat detected";
1989 info.description = tmpString;
1990 if (reviewWithWarnCtx.warning->providerMessage != NULL) {
1991 info.smallTitle = reviewWithWarnCtx.warning->providerMessage;
1992 }
1993 else {
1994 info.smallTitle = "Known drainer contract";
1995 }
1996 snprintf(tmpString,
1997 W3C_DESCRIPTION_MAX_LEN,
1998 "This transaction was scanned as malicious by Web3 Checks.\n\n"
1999 "View full %s report:\n%s",
2000 provider,
2001 reviewWithWarnCtx.warning->reportUrl);
2002 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2003 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2004 headerDesc.separationLine = false;
2005 }
2006 }
2007 else if (set & (1 << W3C_LOSING_SWAP_WARN)) {
2008 if (reviewWithWarnCtx.isIntro) {
2009#ifdef NBGL_QRCODE
2010 // display a QR Code if in intro
2011 nbgl_layoutQRCode_t qrCode = {.url = reviewWithWarnCtx.warning->reportUrl,
2012 .text1 = reviewWithWarnCtx.warning->reportUrl,
2013 .text2 = tmpString,
2014 .centered = true,
2015 .offsetY = 0};
2016 snprintf(tmpString,
2017 W3C_DESCRIPTION_MAX_LEN,
2018 "Scan to view the risk report from %s.",
2019 provider);
2020 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode);
2021 footerDesc.emptySpace.height = 24;
2022#endif // NBGL_QRCODE
2023 headerDesc.backAndText.text = "Web3 Checks risk report";
2024 }
2025 else {
2026 // display a centered if in review
2027 nbgl_contentCenter_t info = {0};
2028 info.icon = &C_Warning_64px;
2029 info.title = "Risk detected";
2030 info.description = tmpString;
2031 if (reviewWithWarnCtx.warning->providerMessage != NULL) {
2032 info.smallTitle = reviewWithWarnCtx.warning->providerMessage;
2033 }
2034 else {
2035 info.smallTitle = "Losing swap";
2036 }
2037 snprintf(tmpString,
2038 W3C_DESCRIPTION_MAX_LEN,
2039 "This transaction was scanned as risky by Web3 Checks.\n\n"
2040 "View full %s report:\n%s",
2041 provider,
2042 reviewWithWarnCtx.warning->reportUrl);
2043 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2044 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2045 headerDesc.separationLine = false;
2046 }
2047 }
2048 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2049 if (footerDesc.emptySpace.height > 0) {
2050 nbgl_layoutAddExtendedFooter(reviewWithWarnCtx.modalLayout, &footerDesc);
2051 }
2052 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2053 nbgl_refresh();
2054}
2055
2056// function used to display the security level page in modal
2057// it can be either a single page with text or QR code, or a list of touchable bars leading
2058// to single pages
2059static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details)
2060{
2061 nbgl_layoutDescription_t layoutDescription = {.modal = true,
2062 .withLeftBorder = true,
2063 .onActionCallback = modalLayoutTouchCallback,
2064 .tapActionText = NULL};
2066 .separationLine = true,
2067 .backAndText.icon = NULL,
2068 .backAndText.tuneId = TUNE_TAP_CASUAL,
2069 .backAndText.token = DISMISS_WARNING_TOKEN};
2070 uint8_t i;
2071
2072 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
2073 headerDesc.backAndText.text = details->title;
2074 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2075 if (details->type == BAR_LIST_WARNING) {
2076 // if more than one warning warning, so use a list of touchable bars
2077 for (i = 0; i < details->barList.nbBars; i++) {
2078 nbgl_layoutBar_t bar;
2079 bar.text = details->barList.texts[i];
2080 bar.subText = details->barList.subTexts[i];
2081 bar.iconRight = &PUSH_ICON;
2082 bar.iconLeft = details->barList.icons[i];
2083 bar.token = FIRST_WARN_BAR_TOKEN + i;
2084 bar.tuneId = TUNE_TAP_CASUAL;
2085 bar.large = false;
2086 bar.inactive = false;
2087 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
2088 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
2089 }
2090 }
2091 else if (details->type == QRCODE_WARNING) {
2092#ifdef NBGL_QRCODE
2093 // display a QR Code
2094 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &details->qrCode);
2095#endif // NBGL_QRCODE
2096 headerDesc.backAndText.text = details->title;
2097 }
2098 else if (details->type == CENTERED_INFO_WARNING) {
2099 // display a centered info
2100 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &details->centeredInfo);
2101 headerDesc.separationLine = false;
2102 }
2103 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2104 nbgl_refresh();
2105}
2106
2107// function used to display the initial warning page when starting a "review with warning"
2108static void displayInitialWarning(void)
2109{
2110 // Play notification sound
2111#ifdef HAVE_PIEZO_SOUND
2112 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2113#endif // HAVE_PIEZO_SOUND
2114 nbgl_layoutDescription_t layoutDescription;
2115 nbgl_layoutChoiceButtons_t buttonsInfo = {.bottomText = "Continue anyway",
2116 .token = WARNING_CHOICE_TOKEN,
2117 .topText = "Back to safety",
2118 .style = ROUNDED_AND_FOOTER_STYLE,
2119 .tuneId = TUNE_TAP_CASUAL};
2120 nbgl_layoutHeader_t headerDesc = {.type = HEADER_EMPTY,
2121 .separationLine = false,
2122 .emptySpace.height = MEDIUM_CENTERING_HEADER};
2123
2124 reviewWithWarnCtx.isIntro = true;
2125
2126 layoutDescription.modal = false;
2127 layoutDescription.withLeftBorder = true;
2128
2129 layoutDescription.onActionCallback = layoutTouchCallback;
2130 layoutDescription.tapActionText = NULL;
2131
2132 layoutDescription.ticker.tickerCallback = NULL;
2133 reviewWithWarnCtx.layoutCtx = nbgl_layoutGet(&layoutDescription);
2134
2135 nbgl_layoutAddHeader(reviewWithWarnCtx.layoutCtx, &headerDesc);
2136 // do not display top-right icon if only Web3 Checks issue
2137 if (reviewWithWarnCtx.warning->predefinedSet != (1 << W3C_ISSUE_WARN)) {
2138 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2140 reviewWithWarnCtx.layoutCtx, &PRIVACY_ICON, WARNING_BUTTON_TOKEN, TUNE_TAP_CASUAL);
2141 }
2142 else if (reviewWithWarnCtx.warning->introTopRightIcon != NULL) {
2143 nbgl_layoutAddTopRightButton(reviewWithWarnCtx.layoutCtx,
2144 reviewWithWarnCtx.warning->introTopRightIcon,
2145 WARNING_BUTTON_TOKEN,
2146 TUNE_TAP_CASUAL);
2147 }
2148 }
2149 // add button and footer on bottom
2150 nbgl_layoutAddChoiceButtons(reviewWithWarnCtx.layoutCtx, &buttonsInfo);
2151 // add main content
2152 // if predefined content is configured, use it preferably
2153 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2154 nbgl_contentCenter_t info = {0};
2155 info.icon = &C_Warning_64px;
2156 if (reviewWithWarnCtx.warning->predefinedSet == (1 << BLIND_SIGNING_WARN)) {
2157 info.title = "Blind signing ahead";
2158 info.description
2159 = "The details of this transaction or message are not fully verifiable. If "
2160 "you sign it, you could lose all "
2161 "your assets.";
2162 }
2163 else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_ISSUE_WARN)) {
2164 info.icon = &C_Important_Circle_64px;
2165 info.title = "Web3 Checks could not verify this message";
2166 info.description
2167 = "An issue prevented Web3 Checks from running.\nGet help: ledger.com/e11";
2168 }
2169 else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_LOSING_SWAP_WARN)) {
2170 info.title = "Risk detected";
2171 info.description = "This transaction was scanned as ricky by Web3 Checks.";
2172 if (reviewWithWarnCtx.warning->providerMessage != NULL) {
2173 info.smallTitle = reviewWithWarnCtx.warning->providerMessage;
2174 }
2175 else {
2176 info.smallTitle = "Losing swap";
2177 }
2178 }
2179 else if (reviewWithWarnCtx.warning->predefinedSet == (1 << W3C_THREAT_DETECTED_WARN)) {
2180 info.title = "Threat detected";
2181 info.description = "This transaction was scanned as malicious by Web3 Checks.";
2182 if (reviewWithWarnCtx.warning->providerMessage != NULL) {
2183 info.smallTitle = reviewWithWarnCtx.warning->providerMessage;
2184 }
2185 else {
2186 info.smallTitle = "Known drainer contract";
2187 }
2188 }
2189 else {
2190 // Case with Several warnings (e.g. Blind + W3C)
2191 info.title = "Dangerous transaction";
2192 if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_ISSUE_WARN)) {
2193 info.description
2194 = "This transaction cannot be fully decoded, and was not verified by Web3 "
2195 "Checks.";
2196 }
2197 else {
2198 info.description
2199 = "This transaction cannot be fully decoded, and was marked as risky by Web3 "
2200 "Checks.";
2201 }
2202 }
2203 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, &info);
2204 }
2205 else if (reviewWithWarnCtx.warning->info != NULL) {
2206 // if no predefined content, use custom one
2207 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, reviewWithWarnCtx.warning->info);
2208 }
2209
2210 nbgl_layoutDraw(reviewWithWarnCtx.layoutCtx);
2211 nbgl_refresh();
2212}
2213
2214// function to factorize code for all simple reviews
2215static void useCaseReview(nbgl_operationType_t operationType,
2216 const nbgl_contentTagValueList_t *tagValueList,
2217 const nbgl_icon_details_t *icon,
2218 const char *reviewTitle,
2219 const char *reviewSubTitle,
2220 const char *finishTitle,
2221 const nbgl_tipBox_t *tipBox,
2222 nbgl_choiceCallback_t choiceCallback,
2223 bool isLight,
2224 bool playNotifSound)
2225{
2226 reset_callbacks();
2227 memset(&genericContext, 0, sizeof(genericContext));
2228
2229 bundleNavContext.review.operationType = operationType;
2230 bundleNavContext.review.choiceCallback = choiceCallback;
2231
2232 // memorize context
2233 onChoice = bundleNavReviewChoice;
2234 navType = GENERIC_NAV;
2235 pageTitle = NULL;
2236
2237 genericContext.genericContents.contentsList = localContentsList;
2238 genericContext.genericContents.nbContents = 3;
2239 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
2240
2241 // First a centered info
2242 STARTING_CONTENT.type = EXTENDED_CENTER;
2243 prepareReviewFirstPage(
2244 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2245 if (tipBox != NULL) {
2246 // do not display "Swipe to review" if a tip-box is displayed
2247 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL;
2248
2249 STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
2250 STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
2251 STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
2252 STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
2253 tipBoxModalTitle = tipBox->modalTitle;
2254 // the only supported type yet is @ref INFOS_LIST
2255 if (tipBox->type == INFOS_LIST) {
2256 tipBoxInfoList.nbInfos = tipBox->infos.nbInfos;
2257 tipBoxInfoList.withExtensions = tipBox->infos.withExtensions;
2258 tipBoxInfoList.infoTypes = PIC(tipBox->infos.infoTypes);
2259 tipBoxInfoList.infoContents = PIC(tipBox->infos.infoContents);
2260 tipBoxInfoList.infoExtensions = PIC(tipBox->infos.infoExtensions);
2261 }
2262 }
2263
2264 // Then the tag/value pairs
2265 localContentsList[1].type = TAG_VALUE_LIST;
2266 memcpy(&localContentsList[1].content.tagValueList,
2267 tagValueList,
2269 localContentsList[1].contentActionCallback = tagValueList->actionCallback;
2270
2271 // The last page
2272 if (isLight) {
2273 localContentsList[2].type = INFO_BUTTON;
2274 prepareReviewLightLastPage(&localContentsList[2].content.infoButton, icon, finishTitle);
2275 }
2276 else {
2277 localContentsList[2].type = INFO_LONG_PRESS;
2278 prepareReviewLastPage(&localContentsList[2].content.infoLongPress, icon, finishTitle);
2279 }
2280
2281 // compute number of pages & fill navigation structure
2282 uint8_t nbPages = getNbPagesForGenericContents(
2283 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2284 prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
2285
2286 // Play notification sound if required
2287 if (playNotifSound) {
2288#ifdef HAVE_PIEZO_SOUND
2289 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2290#endif // HAVE_PIEZO_SOUND
2291 }
2292
2293 displayGenericContextPage(0, true);
2294}
2295
2296// function to factorize code for all streaming reviews
2297static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
2298 const nbgl_icon_details_t *icon,
2299 const char *reviewTitle,
2300 const char *reviewSubTitle,
2301 nbgl_choiceCallback_t choiceCallback,
2302 bool playNotifSound)
2303{
2304 reset_callbacks();
2305 memset(&genericContext, 0, sizeof(genericContext));
2306
2307 bundleNavContext.reviewStreaming.operationType = operationType;
2308 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
2309 bundleNavContext.reviewStreaming.icon = icon;
2310
2311 // memorize context
2312 onChoice = bundleNavReviewStreamingChoice;
2313 navType = STREAMING_NAV;
2314 pageTitle = NULL;
2315
2316 genericContext.genericContents.contentsList = localContentsList;
2317 genericContext.genericContents.nbContents = 1;
2318 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
2319
2320 // First a centered info
2321 STARTING_CONTENT.type = EXTENDED_CENTER;
2322 prepareReviewFirstPage(
2323 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2324
2325 // compute number of pages & fill navigation structure
2326 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
2327 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2328 prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
2329 // no back button on first page
2330 navInfo.navWithButtons.backButton = false;
2331
2332 // Play notification sound if required
2333 if (playNotifSound) {
2334#ifdef HAVE_PIEZO_SOUND
2335 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2336#endif // HAVE_PIEZO_SOUND
2337 }
2338
2339 displayGenericContextPage(0, true);
2340}
2341
2358static void useCaseHomeExt(const char *appName,
2359 const nbgl_icon_details_t *appIcon,
2360 const char *tagline,
2361 bool withSettings,
2362 nbgl_homeAction_t *homeAction,
2363 nbgl_callback_t topRightCallback,
2364 nbgl_callback_t quitCallback)
2365{
2366 reset_callbacks();
2367
2368 nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
2369 .centeredInfo.text1 = appName,
2370 .centeredInfo.text3 = NULL,
2371 .centeredInfo.style = LARGE_CASE_INFO,
2372 .centeredInfo.offsetY = 0,
2373 .footerText = NULL,
2374 .bottomButtonStyle = QUIT_APP_TEXT,
2375 .tapActionText = NULL,
2376 .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
2377 .topRightToken = CONTINUE_TOKEN,
2378 .tuneId = TUNE_TAP_CASUAL};
2379 if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
2380 // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
2381 info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
2382 onAction = homeAction->callback;
2383 info.actionButtonText = homeAction->text;
2384 info.actionButtonIcon = homeAction->icon;
2387 }
2388 else {
2389 info.bottomButtonsToken = QUIT_TOKEN;
2390 onAction = NULL;
2391 info.actionButtonText = NULL;
2392 info.actionButtonIcon = NULL;
2393 }
2394 if (tagline == NULL) {
2395 if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
2396 snprintf(tmpString,
2398 "This app enables signing\ntransactions on its network.");
2399 }
2400 else {
2401 snprintf(tmpString,
2403 "%s %s\n%s",
2405 appName,
2407 }
2408
2409 // If there is more than 3 lines, it means the appName was split, so we put it on the next
2410 // line
2411 if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, tmpString, AVAILABLE_WIDTH, false) > 3) {
2412 snprintf(tmpString,
2414 "%s\n%s %s",
2416 appName,
2418 }
2419 info.centeredInfo.text2 = tmpString;
2420 }
2421 else {
2422 info.centeredInfo.text2 = tagline;
2423 }
2424
2425 onContinue = topRightCallback;
2426 onQuit = quitCallback;
2427
2428 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2430}
2431
2440static void displayDetails(const char *tag, const char *value, bool wrapping)
2441{
2442 memset(&detailsContext, 0, sizeof(detailsContext));
2443
2444 uint16_t nbLines
2445 = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
2446
2447 // initialize context
2448 detailsContext.tag = tag;
2449 detailsContext.value = value;
2450 detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
2451 detailsContext.currentPage = 0;
2452 detailsContext.wrapping = wrapping;
2453 // add some spare for room lost with "..." substitution
2454 if (detailsContext.nbPages > 1) {
2455 uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
2456 uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
2457 / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
2458 uint8_t nbLinesInLastPage
2459 = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
2460
2461 detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
2462 if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
2463 > NB_MAX_LINES_IN_DETAILS) {
2464 detailsContext.nbPages++;
2465 }
2466 }
2467
2468 displayDetailsPage(0, true);
2469}
2470
2471/**********************
2472 * GLOBAL FUNCTIONS
2473 **********************/
2474
2488 const nbgl_contentTagValueList_t *tagValueList,
2489 uint8_t startIndex,
2490 bool *requireSpecificDisplay)
2491{
2493 nbPairs, tagValueList, startIndex, false, requireSpecificDisplay);
2494}
2495
2510 const nbgl_contentTagValueList_t *tagValueList,
2511 uint8_t startIndex,
2512 bool isSkippable,
2513 bool *requireSpecificDisplay)
2514{
2515 uint8_t nbPairsInPage = 0;
2516 uint16_t currentHeight = PRE_TAG_VALUE_MARGIN; // upper margin
2517 uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
2518
2519 // if the review is skippable, it means that there is less height for tag/value pairs
2520 // the small centering header becomes a touchable header
2521 if (isSkippable) {
2522 maxUsableHeight -= TOUCHABLE_HEADER_BAR_HEIGHT - SMALL_CENTERING_HEADER;
2523 }
2524
2525 *requireSpecificDisplay = false;
2526 while (nbPairsInPage < nbPairs) {
2527 const nbgl_layoutTagValue_t *pair;
2528 nbgl_font_id_e value_font;
2529 uint16_t nbLines;
2530
2531 // margin between pairs
2532 // 12 or 24 px between each tag/value pair
2533 if (nbPairsInPage > 0) {
2534 currentHeight += INTER_TAG_VALUE_MARGIN;
2535 }
2536 // fetch tag/value pair strings.
2537 if (tagValueList->pairs != NULL) {
2538 pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2539 }
2540 else {
2541 pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2542 }
2543
2544 if (pair->forcePageStart && nbPairsInPage > 0) {
2545 // This pair must be at the top of a page
2546 break;
2547 }
2548
2549 if (pair->centeredInfo) {
2550 if (nbPairsInPage > 0) {
2551 // This pair must be at the top of a page
2552 break;
2553 }
2554 else {
2555 // This pair is the only one of the page and has a specific display behavior
2556 nbPairsInPage = 1;
2557 *requireSpecificDisplay = true;
2558 break;
2559 }
2560 }
2561
2562 // tag height
2563 currentHeight += nbgl_getTextHeightInWidth(
2564 SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2565 // space between tag and value
2566 currentHeight += 4;
2567 // set value font
2568 if (tagValueList->smallCaseForValue) {
2569 value_font = SMALL_REGULAR_FONT;
2570 }
2571 else {
2572 value_font = LARGE_MEDIUM_FONT;
2573 }
2574 // value height
2575 currentHeight += nbgl_getTextHeightInWidth(
2576 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2577 // nb lines for value
2579 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2580 if ((currentHeight >= maxUsableHeight) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2581 if (nbPairsInPage == 0) {
2582 // Pair too long to fit in a single screen
2583 // It will be the only one of the page and has a specific display behavior
2584 nbPairsInPage = 1;
2585 *requireSpecificDisplay = true;
2586 }
2587 break;
2588 }
2589 nbPairsInPage++;
2590 }
2591 return nbPairsInPage;
2592}
2593
2604 const nbgl_contentInfoList_t *infosList,
2605 uint8_t startIndex,
2606 bool withNav)
2607{
2608 uint8_t nbInfosInPage = 0;
2609 uint16_t currentHeight = 0;
2610 uint16_t previousHeight;
2611 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2612 const char *const *infoTypes = PIC(infosList->infoTypes);
2613 const char *const *infoContents = PIC(infosList->infoContents);
2614
2615 while (nbInfosInPage < nbInfos) {
2616 // margin between infos
2617 currentHeight += PRE_TEXT_MARGIN;
2618
2619 // type height
2620 currentHeight += nbgl_getTextHeightInWidth(
2621 SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2622 // space between type and content
2623 currentHeight += TEXT_SUBTEXT_MARGIN;
2624
2625 // content height
2626 currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2627 PIC(infoContents[startIndex + nbInfosInPage]),
2629 true);
2630 currentHeight += POST_SUBTEXT_MARGIN; // under the content
2631 // if height is over the limit
2632 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2633 // if there was no nav, now there will be, so it can be necessary to remove the last
2634 // item
2635 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2636 nbInfosInPage--;
2637 }
2638 break;
2639 }
2640 previousHeight = currentHeight;
2641 nbInfosInPage++;
2642 }
2643 return nbInfosInPage;
2644}
2645
2656 const nbgl_contentSwitchesList_t *switchesList,
2657 uint8_t startIndex,
2658 bool withNav)
2659{
2660 uint8_t nbSwitchesInPage = 0;
2661 uint16_t currentHeight = 0;
2662 uint16_t previousHeight;
2663 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2664 nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2665
2666 while (nbSwitchesInPage < nbSwitches) {
2667 // margin between switches
2668 currentHeight += PRE_TEXT_MARGIN;
2669
2670 // text height
2671 currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2672 switchArray[startIndex + nbSwitchesInPage].text,
2674 true);
2675 // space between text and sub-text
2676 currentHeight += TEXT_SUBTEXT_MARGIN;
2677
2678 // sub-text height
2679 currentHeight
2680 += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2681 switchArray[startIndex + nbSwitchesInPage].subText,
2683 true);
2684 currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2685 // if height is over the limit
2686 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2687 // if there was no nav, now there will be, so it can be necessary to remove the last
2688 // item
2689 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2690 nbSwitchesInPage--;
2691 }
2692 break;
2693 }
2694 previousHeight = currentHeight;
2695 nbSwitchesInPage++;
2696 }
2697 return nbSwitchesInPage;
2698}
2699
2710 const nbgl_contentBarsList_t *barsList,
2711 uint8_t startIndex,
2712 bool withNav)
2713{
2714 uint8_t nbBarsInPage = 0;
2715 uint16_t currentHeight = 0;
2716 uint16_t previousHeight;
2717 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2718
2719 UNUSED(barsList);
2720 UNUSED(startIndex);
2721
2722 while (nbBarsInPage < nbBars) {
2723 currentHeight += TOUCHABLE_BAR_HEIGHT;
2724 // if height is over the limit
2725 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2726 break;
2727 }
2728 previousHeight = currentHeight;
2729 nbBarsInPage++;
2730 }
2731 // if there was no nav, now there may will be, so it can be necessary to remove the last
2732 // item
2733 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2734 nbBarsInPage--;
2735 }
2736 return nbBarsInPage;
2737}
2738
2749 const nbgl_contentRadioChoice_t *choicesList,
2750 uint8_t startIndex,
2751 bool withNav)
2752{
2753 uint8_t nbChoicesInPage = 0;
2754 uint16_t currentHeight = 0;
2755 uint16_t previousHeight;
2756 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2757
2758 UNUSED(choicesList);
2759 UNUSED(startIndex);
2760
2761 while (nbChoicesInPage < nbChoices) {
2762 currentHeight += TOUCHABLE_BAR_HEIGHT;
2763 // if height is over the limit
2764 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2765 // if there was no nav, now there will be, so it can be necessary to remove the last
2766 // item
2767 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2768 nbChoicesInPage--;
2769 }
2770 break;
2771 }
2772 previousHeight = currentHeight;
2773 nbChoicesInPage++;
2774 }
2775 return nbChoicesInPage;
2776}
2777
2785{
2786 uint8_t nbPages = 0;
2787 uint8_t nbPairs = tagValueList->nbPairs;
2788 uint8_t nbPairsInPage;
2789 uint8_t i = 0;
2790 bool flag;
2791
2792 while (i < tagValueList->nbPairs) {
2793 // upper margin
2794 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2795 i += nbPairsInPage;
2796 nbPairs -= nbPairsInPage;
2797 nbPages++;
2798 }
2799 return nbPages;
2800}
2801
2806void nbgl_useCaseHome(const char *appName,
2807 const nbgl_icon_details_t *appIcon,
2808 const char *tagline,
2809 bool withSettings,
2810 nbgl_callback_t topRightCallback,
2811 nbgl_callback_t quitCallback)
2812{
2813 nbgl_homeAction_t homeAction = {0};
2814 useCaseHomeExt(
2815 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2816}
2817
2822void nbgl_useCaseHomeExt(const char *appName,
2823 const nbgl_icon_details_t *appIcon,
2824 const char *tagline,
2825 bool withSettings,
2826 const char *actionButtonText,
2827 nbgl_callback_t actionCallback,
2828 nbgl_callback_t topRightCallback,
2829 nbgl_callback_t quitCallback)
2830{
2831 nbgl_homeAction_t homeAction = {.callback = actionCallback,
2832 .icon = NULL,
2833 .style = STRONG_HOME_ACTION,
2834 .text = actionButtonText};
2835
2836 useCaseHomeExt(
2837 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2838}
2839
2853void nbgl_useCaseNavigableContent(const char *title,
2854 uint8_t initPage,
2855 uint8_t nbPages,
2856 nbgl_callback_t quitCallback,
2857 nbgl_navCallback_t navCallback,
2858 nbgl_layoutTouchCallback_t controlsCallback)
2859{
2860 reset_callbacks();
2861
2862 // memorize context
2863 onQuit = quitCallback;
2864 onNav = navCallback;
2865 onControls = controlsCallback;
2866 pageTitle = title;
2867 navType = SETTINGS_NAV;
2868
2869 // fill navigation structure
2870 prepareNavInfo(false, nbPages, NULL);
2871
2872 displaySettingsPage(initPage, true);
2873}
2874
2880void nbgl_useCaseSettings(const char *title,
2881 uint8_t initPage,
2882 uint8_t nbPages,
2883 bool touchable,
2884 nbgl_callback_t quitCallback,
2885 nbgl_navCallback_t navCallback,
2886 nbgl_layoutTouchCallback_t controlsCallback)
2887{
2888 UNUSED(touchable);
2890 title, initPage, nbPages, quitCallback, navCallback, controlsCallback);
2891}
2892
2905void nbgl_useCaseGenericSettings(const char *appName,
2906 uint8_t initPage,
2907 const nbgl_genericContents_t *settingContents,
2908 const nbgl_contentInfoList_t *infosList,
2909 nbgl_callback_t quitCallback)
2910{
2911 reset_callbacks();
2912 memset(&genericContext, 0, sizeof(genericContext));
2913
2914 // memorize context
2915 onQuit = quitCallback;
2916 pageTitle = appName;
2917 navType = GENERIC_NAV;
2918
2919 if (settingContents != NULL) {
2920 memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
2921 }
2922 if (infosList != NULL) {
2923 genericContext.hasFinishingContent = true;
2924 memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
2925 FINISHING_CONTENT.type = INFOS_LIST;
2926 memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
2927 }
2928
2929 // fill navigation structure
2930 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2931 if (infosList != NULL) {
2932 nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true, false);
2933 }
2934
2935 prepareNavInfo(false, nbPages, NULL);
2936
2937 displayGenericContextPage(initPage, true);
2938}
2939
2951void nbgl_useCaseGenericConfiguration(const char *title,
2952 uint8_t initPage,
2953 const nbgl_genericContents_t *contents,
2954 nbgl_callback_t quitCallback)
2955{
2956 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2957}
2958
2976 const char *appName,
2977 const nbgl_icon_details_t *appIcon,
2978 const char *tagline,
2979 const uint8_t
2980 initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
2981 const nbgl_genericContents_t *settingContents,
2982 const nbgl_contentInfoList_t *infosList,
2983 const nbgl_homeAction_t *action, // Set to NULL if no additional action
2984 nbgl_callback_t quitCallback)
2985{
2986 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
2987
2988 context->appName = appName;
2989 context->appIcon = appIcon;
2990 context->tagline = tagline;
2991 context->settingContents = settingContents;
2992 context->infosList = infosList;
2993 if (action != NULL) {
2994 memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
2995 }
2996 else {
2997 memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
2998 }
2999 context->quitCallback = quitCallback;
3000
3001 if (initSettingPage != INIT_HOME_PAGE) {
3002 bundleNavStartSettingsAtPage(initSettingPage);
3003 }
3004 else {
3005 bundleNavStartHome();
3006 }
3007}
3008
3016void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
3017{
3018 reset_callbacks();
3019
3021 .tickerCallback = &tickerCallback,
3022 .tickerIntervale = 0, // not periodic
3023 .tickerValue = 3000 // 3 seconds
3024 };
3025 onQuit = quitCallback;
3026 if (isSuccess) {
3027#ifdef HAVE_PIEZO_SOUND
3028 io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
3029#endif // HAVE_PIEZO_SOUND
3030
3031 pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
3032 }
3033 else {
3035 .footerText = NULL,
3036 .centeredInfo.icon = &C_Denied_Circle_64px,
3037 .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
3038 .centeredInfo.onTop = false,
3039 .centeredInfo.style = LARGE_CASE_INFO,
3040 .centeredInfo.text1 = message,
3041 .centeredInfo.text2 = NULL,
3042 .centeredInfo.text3 = NULL,
3043 .tapActionText = "",
3044 .isSwipeable = false,
3045 .tapActionToken = QUIT_TOKEN,
3046 .topRightStyle = NO_BUTTON_STYLE,
3047 .actionButtonText = NULL,
3048 .tuneId = TUNE_TAP_CASUAL};
3049 pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
3050 }
3052}
3053
3061 nbgl_callback_t quitCallback)
3062{
3063 const char *msg;
3064 bool isSuccess;
3065 switch (reviewStatusType) {
3067 msg = "Operation signed";
3068 isSuccess = true;
3069 break;
3071 msg = "Operation rejected";
3072 isSuccess = false;
3073 break;
3075 msg = "Transaction signed";
3076 isSuccess = true;
3077 break;
3079 msg = "Transaction rejected";
3080 isSuccess = false;
3081 break;
3083 msg = "Message signed";
3084 isSuccess = true;
3085 break;
3087 msg = "Message rejected";
3088 isSuccess = false;
3089 break;
3091 msg = "Address verified";
3092 isSuccess = true;
3093 break;
3095 msg = "Address verification\ncancelled";
3096 isSuccess = false;
3097 break;
3098 default:
3099 return;
3100 }
3101 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
3102}
3103
3117 const char *message,
3118 const char *subMessage,
3119 const char *confirmText,
3120 const char *cancelText,
3121 nbgl_choiceCallback_t callback)
3122{
3123 reset_callbacks();
3124
3125 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3126 .centeredInfo.text1 = message,
3127 .centeredInfo.text2 = subMessage,
3128 .centeredInfo.text3 = NULL,
3129 .centeredInfo.style = LARGE_CASE_INFO,
3130 .centeredInfo.icon = icon,
3131 .centeredInfo.offsetY = 0,
3132 .confirmationText = confirmText,
3133 .confirmationToken = CHOICE_TOKEN,
3134 .tuneId = TUNE_TAP_CASUAL,
3135 .modal = false};
3136 // check params
3137 if ((confirmText == NULL) || (cancelText == NULL)) {
3138 return;
3139 }
3140 onChoice = callback;
3141 pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
3143}
3144
3158void nbgl_useCaseConfirm(const char *message,
3159 const char *subMessage,
3160 const char *confirmText,
3161 const char *cancelText,
3162 nbgl_callback_t callback)
3163{
3164 // Don't reset callback or nav context as this is just a modal.
3165
3166 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3167 .centeredInfo.text1 = message,
3168 .centeredInfo.text2 = subMessage,
3169 .centeredInfo.text3 = NULL,
3170 .centeredInfo.style = LARGE_CASE_INFO,
3171 .centeredInfo.icon = &C_Important_Circle_64px,
3172 .centeredInfo.offsetY = 0,
3173 .confirmationText = confirmText,
3174 .confirmationToken = CHOICE_TOKEN,
3175 .tuneId = TUNE_TAP_CASUAL,
3176 .modal = true};
3177 onModalConfirm = callback;
3178 if (modalPageContext != NULL) {
3179 nbgl_pageRelease(modalPageContext);
3180 }
3181 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
3183}
3184
3197 const char *reviewTitle,
3198 const char *reviewSubTitle,
3199 const char *rejectText,
3200 nbgl_callback_t continueCallback,
3201 nbgl_callback_t rejectCallback)
3202{
3203 reset_callbacks();
3204
3205 nbgl_pageInfoDescription_t info = {.footerText = rejectText,
3206 .footerToken = QUIT_TOKEN,
3207 .tapActionText = NULL,
3208 .isSwipeable = true,
3209 .tapActionToken = CONTINUE_TOKEN,
3210 .topRightStyle = NO_BUTTON_STYLE,
3211 .actionButtonText = NULL,
3212 .tuneId = TUNE_TAP_CASUAL};
3213 info.centeredInfo.icon = icon;
3214 info.centeredInfo.text1 = reviewTitle;
3215 info.centeredInfo.text2 = reviewSubTitle;
3216 info.centeredInfo.text3 = "Swipe to review";
3218 info.centeredInfo.offsetY = 0;
3219 onQuit = rejectCallback;
3220 onContinue = continueCallback;
3221
3222#ifdef HAVE_PIEZO_SOUND
3223 // Play notification sound
3224 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3225#endif // HAVE_PIEZO_SOUND
3226
3227 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
3228 nbgl_refresh();
3229}
3230
3236 uint8_t nbPages,
3237 const char *rejectText,
3238 nbgl_layoutTouchCallback_t buttonCallback,
3239 nbgl_navCallback_t navCallback,
3240 nbgl_choiceCallback_t choiceCallback)
3241{
3242 reset_callbacks();
3243
3244 // memorize context
3245 onChoice = choiceCallback;
3246 onNav = navCallback;
3247 onControls = buttonCallback;
3248 forwardNavOnly = false;
3249 navType = REVIEW_NAV;
3250
3251 // fill navigation structure
3252 UNUSED(rejectText);
3253 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3254
3255 displayReviewPage(initPage, true);
3256}
3257
3271 const nbgl_pageInfoLongPress_t *infoLongPress,
3272 const char *rejectText,
3273 nbgl_choiceCallback_t callback)
3274{
3275 uint8_t offset = 0;
3276
3277 reset_callbacks();
3278 memset(&genericContext, 0, sizeof(genericContext));
3279
3280 // memorize context
3281 onChoice = callback;
3282 navType = GENERIC_NAV;
3283 pageTitle = NULL;
3284 bundleNavContext.review.operationType = TYPE_OPERATION;
3285
3286 genericContext.genericContents.contentsList = localContentsList;
3287 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3288
3289 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3290 localContentsList[offset].type = TAG_VALUE_LIST;
3291 memcpy(&localContentsList[offset].content.tagValueList,
3292 tagValueList,
3294 offset++;
3295 }
3296
3297 localContentsList[offset].type = INFO_LONG_PRESS;
3298 memcpy(&localContentsList[offset].content.infoLongPress,
3299 infoLongPress,
3300 sizeof(nbgl_pageInfoLongPress_t));
3301 localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
3302 offset++;
3303
3304 genericContext.genericContents.nbContents = offset;
3305
3306 // compute number of pages & fill navigation structure
3307 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3308 UNUSED(rejectText);
3309 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3310
3311 displayGenericContextPage(0, true);
3312}
3313
3328 const nbgl_pageInfoLongPress_t *infoLongPress,
3329 const char *rejectText,
3330 nbgl_choiceCallback_t callback)
3331{
3332 uint8_t offset = 0;
3333
3334 reset_callbacks();
3335 memset(&genericContext, 0, sizeof(genericContext));
3336
3337 // memorize context
3338 onChoice = callback;
3339 navType = GENERIC_NAV;
3340 pageTitle = NULL;
3341
3342 genericContext.genericContents.contentsList = localContentsList;
3343 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3344
3345 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3346 localContentsList[offset].type = TAG_VALUE_LIST;
3347 memcpy(&localContentsList[offset].content.tagValueList,
3348 tagValueList,
3350 offset++;
3351 }
3352
3353 localContentsList[offset].type = INFO_BUTTON;
3354 localContentsList[offset].content.infoButton.text = infoLongPress->text;
3355 localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
3356 localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
3357 localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
3358 localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
3359 offset++;
3360
3361 genericContext.genericContents.nbContents = offset;
3362
3363 // compute number of pages & fill navigation structure
3364 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3365 UNUSED(rejectText);
3366 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3367
3368 displayGenericContextPage(0, true);
3369}
3370
3387void nbgl_useCaseReview(nbgl_operationType_t operationType,
3388 const nbgl_contentTagValueList_t *tagValueList,
3389 const nbgl_icon_details_t *icon,
3390 const char *reviewTitle,
3391 const char *reviewSubTitle,
3392 const char *finishTitle,
3393 nbgl_choiceCallback_t choiceCallback)
3394{
3395 useCaseReview(operationType,
3396 tagValueList,
3397 icon,
3398 reviewTitle,
3399 reviewSubTitle,
3400 finishTitle,
3401 NULL,
3402 choiceCallback,
3403 false,
3404 true);
3405}
3406
3425 const nbgl_contentTagValueList_t *tagValueList,
3426 const nbgl_icon_details_t *icon,
3427 const char *reviewTitle,
3428 const char *reviewSubTitle,
3429 const char *finishTitle,
3430 const nbgl_tipBox_t *tipBox,
3431 nbgl_choiceCallback_t choiceCallback)
3432{
3433 useCaseReview(operationType,
3434 tagValueList,
3435 icon,
3436 reviewTitle,
3437 reviewSubTitle,
3438 finishTitle,
3439 tipBox,
3440 choiceCallback,
3441 false,
3442 true);
3443}
3444
3465 const nbgl_contentTagValueList_t *tagValueList,
3466 const nbgl_icon_details_t *icon,
3467 const char *reviewTitle,
3468 const char *reviewSubTitle,
3469 const char *finishTitle,
3470 const nbgl_tipBox_t *tipBox,
3471 nbgl_choiceCallback_t choiceCallback)
3472{
3473 nbgl_useCaseReviewWithWarning(operationType,
3474 tagValueList,
3475 icon,
3476 reviewTitle,
3477 reviewSubTitle,
3478 finishTitle,
3479 tipBox,
3480 &blindSigningWarning,
3481 choiceCallback);
3482}
3483
3507 const nbgl_contentTagValueList_t *tagValueList,
3508 const nbgl_icon_details_t *icon,
3509 const char *reviewTitle,
3510 const char *reviewSubTitle,
3511 const char *finishTitle,
3512 const nbgl_tipBox_t *tipBox,
3513 const nbgl_warning_t *warning,
3514 nbgl_choiceCallback_t choiceCallback)
3515{
3516 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3517 reviewWithWarnCtx.isStreaming = false;
3518 reviewWithWarnCtx.operationType = operationType | RISKY_OPERATION;
3519 reviewWithWarnCtx.tagValueList = tagValueList;
3520 reviewWithWarnCtx.icon = icon;
3521 reviewWithWarnCtx.reviewTitle = reviewTitle;
3522 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3523 reviewWithWarnCtx.finishTitle = finishTitle;
3524 reviewWithWarnCtx.tipBox = tipBox;
3525 reviewWithWarnCtx.warning = warning;
3526 reviewWithWarnCtx.choiceCallback = choiceCallback;
3527
3528 displayInitialWarning();
3529}
3530
3548 const nbgl_contentTagValueList_t *tagValueList,
3549 const nbgl_icon_details_t *icon,
3550 const char *reviewTitle,
3551 const char *reviewSubTitle,
3552 const char *finishTitle,
3553 nbgl_choiceCallback_t choiceCallback)
3554{
3555 useCaseReview(operationType,
3556 tagValueList,
3557 icon,
3558 reviewTitle,
3559 reviewSubTitle,
3560 finishTitle,
3561 NULL,
3562 choiceCallback,
3563 true,
3564 true);
3565}
3566
3576 const char *rejectText,
3577 nbgl_callback_t rejectCallback)
3578{
3579 reset_callbacks();
3580 memset(&genericContext, 0, sizeof(genericContext));
3581
3582 // memorize context
3583 onQuit = rejectCallback;
3584 navType = GENERIC_NAV;
3585 pageTitle = NULL;
3586 bundleNavContext.review.operationType = TYPE_OPERATION;
3587
3588 memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3589
3590 // compute number of pages & fill navigation structure
3591 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3592 prepareNavInfo(true, nbPages, rejectText);
3593 navInfo.quitToken = QUIT_TOKEN;
3594
3595#ifdef HAVE_PIEZO_SOUND
3596 // Play notification sound
3597 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3598#endif // HAVE_PIEZO_SOUND
3599
3600 displayGenericContextPage(0, true);
3601}
3602
3616 const nbgl_icon_details_t *icon,
3617 const char *reviewTitle,
3618 const char *reviewSubTitle,
3619 nbgl_choiceCallback_t choiceCallback)
3620{
3621 useCaseReviewStreamingStart(
3622 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3623}
3624
3639 const nbgl_icon_details_t *icon,
3640 const char *reviewTitle,
3641 const char *reviewSubTitle,
3642 nbgl_choiceCallback_t choiceCallback)
3643{
3645 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
3646}
3647
3664 const nbgl_icon_details_t *icon,
3665 const char *reviewTitle,
3666 const char *reviewSubTitle,
3667 const nbgl_warning_t *warning,
3668 nbgl_choiceCallback_t choiceCallback)
3669{
3670 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3671
3672 reviewWithWarnCtx.isStreaming = true;
3673 reviewWithWarnCtx.operationType = operationType | RISKY_OPERATION;
3674 reviewWithWarnCtx.icon = icon;
3675 reviewWithWarnCtx.reviewTitle = reviewTitle;
3676 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3677 reviewWithWarnCtx.choiceCallback = choiceCallback;
3678 reviewWithWarnCtx.warning = warning;
3679
3680 displayInitialWarning();
3681}
3682
3697 nbgl_choiceCallback_t choiceCallback,
3698 nbgl_callback_t skipCallback)
3699{
3700 // Should follow a call to nbgl_useCaseReviewStreamingStart
3701 memset(&genericContext, 0, sizeof(genericContext));
3702
3703 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3704 bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3705
3706 // memorize context
3707 onChoice = bundleNavReviewStreamingChoice;
3708 navType = STREAMING_NAV;
3709 pageTitle = NULL;
3710
3711 genericContext.genericContents.contentsList = localContentsList;
3712 genericContext.genericContents.nbContents = 1;
3713 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3714
3715 // Then the tag/value pairs
3716 STARTING_CONTENT.type = TAG_VALUE_LIST;
3717 memcpy(
3718 &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3719
3720 // compute number of pages & fill navigation structure
3721 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3722 &genericContext.genericContents,
3723 0,
3724 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3725 prepareNavInfo(true,
3727 getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3728 // if the operation is skippable
3729 if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3730 navInfo.progressIndicator = false;
3731 navInfo.skipText = "Skip";
3732 navInfo.skipToken = SKIP_TOKEN;
3733 }
3734
3735 displayGenericContextPage(0, true);
3736}
3737
3749 nbgl_choiceCallback_t choiceCallback)
3750{
3751 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3752}
3753
3762void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3763 nbgl_choiceCallback_t choiceCallback)
3764{
3765 // Should follow a call to nbgl_useCaseReviewStreamingContinue
3766 memset(&genericContext, 0, sizeof(genericContext));
3767
3768 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3769
3770 // memorize context
3771 onChoice = bundleNavReviewStreamingChoice;
3772 navType = STREAMING_NAV;
3773 pageTitle = NULL;
3774
3775 genericContext.genericContents.contentsList = localContentsList;
3776 genericContext.genericContents.nbContents = 1;
3777 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3778
3779 // Eventually the long press page
3780 STARTING_CONTENT.type = INFO_LONG_PRESS;
3781 prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress,
3782 bundleNavContext.reviewStreaming.icon,
3783 finishTitle);
3784
3785 // compute number of pages & fill navigation structure
3786 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3787 &genericContext.genericContents,
3788 0,
3789 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3790 prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3791
3792 displayGenericContextPage(0, true);
3793}
3794
3799void nbgl_useCaseAddressConfirmationExt(const char *address,
3800 nbgl_choiceCallback_t callback,
3801 const nbgl_contentTagValueList_t *tagValueList)
3802{
3803 reset_callbacks();
3804 memset(&genericContext, 0, sizeof(genericContext));
3805 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3806
3807 // save context
3808 onChoice = callback;
3809 navType = GENERIC_NAV;
3810 pageTitle = NULL;
3811
3812 genericContext.genericContents.contentsList = localContentsList;
3813 genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3814 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3815 prepareAddressConfirmationPages(
3816 address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3817
3818 // fill navigation structure, common to all pages
3819 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3820
3821 prepareNavInfo(true, nbPages, "Cancel");
3822
3823#ifdef HAVE_PIEZO_SOUND
3824 // Play notification sound
3825 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3826#endif // HAVE_PIEZO_SOUND
3827
3828 displayGenericContextPage(0, true);
3829}
3830
3847void nbgl_useCaseAddressReview(const char *address,
3848 const nbgl_contentTagValueList_t *additionalTagValueList,
3849 const nbgl_icon_details_t *icon,
3850 const char *reviewTitle,
3851 const char *reviewSubTitle,
3852 nbgl_choiceCallback_t choiceCallback)
3853{
3854 reset_callbacks();
3855 memset(&genericContext, 0, sizeof(genericContext));
3856 // release a potential modal
3857 if (addressConfirmationContext.modalLayout) {
3858 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
3859 }
3860 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3861
3862 // save context
3863 onChoice = choiceCallback;
3864 navType = GENERIC_NAV;
3865 pageTitle = NULL;
3866 bundleNavContext.review.operationType = TYPE_OPERATION;
3867
3868 genericContext.genericContents.contentsList = localContentsList;
3869 genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
3870 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
3871
3872 // First a centered info
3873 STARTING_CONTENT.type = EXTENDED_CENTER;
3874 prepareReviewFirstPage(
3875 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
3876 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
3877
3878 // Then the address confirmation pages
3879 prepareAddressConfirmationPages(
3880 address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
3881
3882 // fill navigation structure, common to all pages
3883 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3884
3885 prepareNavInfo(true, nbPages, "Cancel");
3886
3887#ifdef HAVE_PIEZO_SOUND
3888 // Play notification sound
3889 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3890#endif // HAVE_PIEZO_SOUND
3891
3892 displayGenericContextPage(0, true);
3893}
3894
3903void nbgl_useCaseSpinner(const char *text)
3904{
3905 // if the previous Use Case was not Spinner, fresh start
3906 if (genericContext.type != USE_CASE_SPINNER) {
3907 memset(&genericContext, 0, sizeof(genericContext));
3908 genericContext.type = USE_CASE_SPINNER;
3909 nbgl_layoutDescription_t layoutDescription = {0};
3910
3911 layoutDescription.withLeftBorder = true;
3912
3913 genericContext.backgroundLayout = nbgl_layoutGet(&layoutDescription);
3914
3916 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
3917
3918 nbgl_layoutDraw(genericContext.backgroundLayout);
3920 }
3921 else {
3922 // otherwise increment spinner
3923 genericContext.spinnerPosition++;
3924 // there are only NB_SPINNER_POSITIONSpositions
3925 if (genericContext.spinnerPosition == NB_SPINNER_POSITIONS) {
3926 genericContext.spinnerPosition = 0;
3927 }
3928 int ret = nbgl_layoutUpdateSpinner(
3929 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
3930 if (ret == 1) {
3932 }
3933 else if (ret == 2) {
3935 }
3936 }
3937}
3938
3939#ifdef NBGL_KEYPAD
3959void nbgl_useCaseKeypadDigits(const char *title,
3960 uint8_t minDigits,
3961 uint8_t maxDigits,
3962 uint8_t backToken,
3963 bool shuffled,
3964 tune_index_e tuneId,
3965 nbgl_pinValidCallback_t validatePinCallback,
3966 nbgl_layoutTouchCallback_t actionCallback)
3967{
3968 keypadGenericUseCase(title,
3969 minDigits,
3970 maxDigits,
3971 backToken,
3972 shuffled,
3973 false,
3974 tuneId,
3975 validatePinCallback,
3976 actionCallback);
3977}
3997void nbgl_useCaseKeypadPIN(const char *title,
3998 uint8_t minDigits,
3999 uint8_t maxDigits,
4000 uint8_t backToken,
4001 bool shuffled,
4002 tune_index_e tuneId,
4003 nbgl_pinValidCallback_t validatePinCallback,
4004 nbgl_layoutTouchCallback_t actionCallback)
4005{
4006 keypadGenericUseCase(title,
4007 minDigits,
4008 maxDigits,
4009 backToken,
4010 shuffled,
4011 true,
4012 tuneId,
4013 validatePinCallback,
4014 actionCallback);
4015}
4016#endif // NBGL_KEYPAD
4017
4018#endif // HAVE_SE_TOUCH
4019#endif // NBGL_USE_CASE
nbgl_contentTagValue_t *(* nbgl_contentTagValueCallback_t)(uint8_t pairIndex)
prototype of tag/value pair retrieval callback
@ ICON_ILLUSTRATION
simple icon
@ LARGE_CASE_GRAY_INFO
@ LARGE_CASE_INFO
text in BLACK and large case (INTER 32px), subText in black in Inter24px
@ INFO_LONG_PRESS
a centered info and a long press button
@ EXTENDED_CENTER
a centered content and a possible tip-box
@ CHOICES_LIST
list of choices through radio buttons
@ CENTERED_INFO
a centered info
@ SWITCHES_LIST
list of switches with descriptions
@ TAG_VALUE_DETAILS
a tag/value pair and a small button to get details.
@ INFOS_LIST
list of infos with titles
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
@ TAG_VALUE_LIST
list of tag/value pairs
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
@ INFO_BUTTON
a centered info and a simple black button
@ ENS_ALIAS
alias comes from ENS
@ ADDRESS_BOOK_ALIAS
alias comes from Address Book
@ QR_CODE_ALIAS
alias is an address to be displayed as a QR Code
void(* nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
prototype of function to be called when an action on a content object occurs
debug traces management
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition nbgl_debug.h:35
bool nbgl_getTextMaxLenInNbLines(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, uint16_t maxNbLines, uint16_t *len, bool wrapping)
compute the len of the given text (in bytes) fitting in the given maximum nb lines,...
Definition nbgl_fonts.c:562
nbgl_font_id_e
Definition nbgl_fonts.h:136
void nbgl_textReduceOnNbLines(nbgl_font_id_e fontId, const char *origText, uint16_t maxWidth, uint8_t nbLines, char *reducedText, uint16_t reducedTextLen)
Create a reduced version of given ASCII text to wrap it on the given max width (in pixels),...
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
return the height of the given multiline text, with the given font.
uint16_t nbgl_getTextNbLinesInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
compute the number of lines of the given text fitting in the given maxWidth
Definition nbgl_fonts.c:721
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
int nbgl_layoutAddContentCenter(nbgl_layout_t *layout, const nbgl_contentCenter_t *info)
Creates an area on the center of the main panel, with a possible icon, and possible texts under it.
int nbgl_layoutAddTextContent(nbgl_layout_t *layout, const char *title, const char *description, const char *info)
Creates in the main container three text areas:
int nbgl_layoutUpdateKeypad(nbgl_layout_t *layout, uint8_t index, bool enableValidate, bool enableBackspace, bool enableDigits)
Updates an existing keypad on bottom of the screen, with the given configuration.
int nbgl_layoutAddSeparationLine(nbgl_layout_t *layout)
adds a separation line on bottom of the last added item
int nbgl_layoutAddQRCode(nbgl_layout_t *layout, const nbgl_layoutQRCode_t *info)
Creates an area on the center of the main panel, with a QRCode, a possible text in black (bold) under...
int nbgl_layoutDraw(nbgl_layout_t *layout)
Applies given layout. The screen will be redrawn.
int nbgl_layoutAddTouchableBar(nbgl_layout_t *layout, const nbgl_layoutBar_t *barLayout)
Creates a touchable bar in main panel.
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
int nbgl_layoutAddTopRightButton(nbgl_layout_t *layout, const nbgl_icon_details_t *icon, uint8_t token, tune_index_e tuneId)
Creates a Top-right button in the top right corner of the top panel.
#define AVAILABLE_WIDTH
Definition nbgl_layout.h:84
void * nbgl_layout_t
type shared externally
@ HEADER_EMPTY
empty space, to have a better vertical centering of centered info
@ HEADER_BACK_AND_TEXT
back key and optional text
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_t *footerDesc)
Creates a touchable area at the footer of the screen, containing various controls,...
@ ROUNDED_AND_FOOTER_STYLE
A black background button on top of a footer.
int nbgl_layoutAddChoiceButtons(nbgl_layout_t *layout, const nbgl_layoutChoiceButtons_t *info)
Creates two buttons to make a choice. Both buttons are mandatory. Both buttons are full width,...
int nbgl_layoutAddSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t initPosition)
Creates a centered (vertically & horizontally) spinner with a text under it.
int nbgl_layoutUpdateSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t position)
Update an existing spinner (must be the only object of the layout)
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition nbgl_layout.h:30
int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *headerDesc)
Creates a touchable (or not) area at the header of the screen, containing various controls,...
@ FOOTER_EMPTY
empty space, to have a better vertical centering of centered info
int nbgl_layoutRelease(nbgl_layout_t *layout)
Release the layout obtained with nbgl_layoutGet()
#define EXIT_PAGE
Definition nbgl_layout.h:38
int nbgl_layoutAddFooter(nbgl_layout_t *layout, const char *text, uint8_t token, tune_index_e tuneId)
Creates a touchable text at the footer of the screen, separated with a thin line from the rest of the...
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
#define NB_SPINNER_POSITIONS
Definition nbgl_obj.h:181
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition nbgl_obj.c:1559
#define KEYPAD_MAX_DIGITS
Definition nbgl_obj.h:63
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
This functions refreshes the actual screen on display with what has changed since the last refresh,...
Definition nbgl_obj.c:1569
#define BACKSPACE_KEY
Definition nbgl_obj.h:26
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition nbgl_obj.c:1585
#define VALIDATE_KEY
Definition nbgl_obj.h:27
nbgl_page_t * nbgl_pageDrawGenericContent(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_pageNavigationInfo_t *nav, nbgl_pageContent_t *content)
draw a generic content page, with the given content, and if nav parameter is not NULL,...
Definition nbgl_page.c:612
nbgl_page_t * nbgl_pageDrawInfo(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_screenTickerConfiguration_t *ticker, const nbgl_pageInfoDescription_t *info)
draw a page with a centered info (icon and/or texts) with a touchable footer, in a potential "tapable...
Definition nbgl_page.c:325
void * nbgl_page_t
type shared externally
Definition nbgl_page.h:81
@ NAV_WITH_BUTTONS
move forward and backward with buttons in bottom nav bar
Definition nbgl_page.h:89
nbgl_page_t * nbgl_pageDrawLedgerInfo(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_screenTickerConfiguration_t *ticker, const char *text, int tapActionToken)
draw a page with a centered text in large case, with a round check icon
Definition nbgl_page.c:256
nbgl_page_t * nbgl_pageDrawConfirmation(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_pageConfirmationDescription_t *info)
draw a confirmation page, with a centered info (icon and/or text), a button to confirm and a footer t...
Definition nbgl_page.c:442
int nbgl_pageRelease(nbgl_page_t *)
Release the page obtained with any of the nbgl_pageDrawXXX() functions.
Definition nbgl_page.c:625
nbgl_page_t * nbgl_pageDrawGenericContentExt(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_pageNavigationInfo_t *nav, nbgl_pageContent_t *content, bool modal)
draw a generic content page, with the given content, and if nav parameter is not NULL,...
Definition nbgl_page.c:483
@ QUIT_APP_TEXT
A full width button with "Quit app" text (only for bottom button)
Definition nbgl_page.h:40
@ INFO_ICON
info (i) icon in the button.
Definition nbgl_page.h:39
@ NO_BUTTON_STYLE
no button.
Definition nbgl_page.h:36
@ SETTINGS_ICON
settings (wheel) icon in the button.
Definition nbgl_page.h:37
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition nbgl_screen.c:66
@ POST_REFRESH_FORCE_POWER_ON
Force screen power on after refresh.
Definition nbgl_types.h:334
#define MIN(x, y)
Definition nbgl_types.h:98
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
nbgl_refresh_mode_t
different modes of refresh for nbgl_refreshSpecial()
Definition nbgl_types.h:306
@ FULL_COLOR_CLEAN_REFRESH
to be used for lock screen display (cleaner but longer refresh)
Definition nbgl_types.h:309
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition nbgl_types.h:311
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition nbgl_types.h:308
@ FULL_COLOR_REFRESH
to be used for normal refresh
Definition nbgl_types.h:307
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
DEPRECATED void nbgl_useCaseHome(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, bool withSettings, nbgl_callback_t topRightCallback, nbgl_callback_t quitCallback)
DEPRECATED void nbgl_useCaseSettings(const char *settingsTitle, uint8_t initPage, uint8_t nbPages, bool touchableTitle, nbgl_callback_t quitCallback, nbgl_navCallback_t navCallback, nbgl_layoutTouchCallback_t controlsCallback)
uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool isSkippable, bool *requireSpecificDisplay)
void(* nbgl_callback_t)(void)
prototype of generic callback function
#define SKIPPABLE_OPERATION
This is to use in nbgl_operationType_t when the operation is skippable. This is used.
void nbgl_useCaseGenericSettings(const char *appName, uint8_t initPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, nbgl_callback_t quitCallback)
void nbgl_useCaseKeypadPIN(const char *title, uint8_t minDigits, uint8_t maxDigits, uint8_t backToken, bool shuffled, tune_index_e tuneId, nbgl_pinValidCallback_t validatePinCallback, nbgl_layoutTouchCallback_t actionCallback)
uint32_t nbgl_operationType_t
This mask is used to describe the type of operation to review with additional options It is a mask of...
void nbgl_useCaseReviewStreamingWithWarningStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool *requireSpecificDisplay)
uint8_t nbgl_useCaseGetNbPagesForTagValueList(const nbgl_contentTagValueList_t *tagValueList)
DEPRECATED void nbgl_useCaseRegularReview(uint8_t initPage, uint8_t nbPages, const char *rejectText, nbgl_layoutTouchCallback_t buttonCallback, nbgl_navCallback_t navCallback, nbgl_choiceCallback_t choiceCallback)
void(* nbgl_pinValidCallback_t)(const uint8_t *pin, uint8_t pinLen)
prototype of pin validation callback function
void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseHomeAndSettings(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, const uint8_t initSettingPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, const nbgl_homeAction_t *action, nbgl_callback_t quitCallback)
void nbgl_useCaseStaticReviewLight(const nbgl_contentTagValueList_t *tagValueList, const nbgl_pageInfoLongPress_t *infoLongPress, const char *rejectText, nbgl_choiceCallback_t callback)
uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos, const nbgl_contentInfoList_t *infosList, uint8_t startIndex, bool withNav)
void(* nbgl_choiceCallback_t)(bool confirm)
prototype of choice callback function
uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars, const nbgl_contentBarsList_t *barsList, uint8_t startIndex, bool withNav)
void nbgl_useCaseNavigableContent(const char *title, uint8_t initPage, uint8_t nbPages, nbgl_callback_t quitCallback, nbgl_navCallback_t navCallback, nbgl_layoutTouchCallback_t controlsCallback)
#define INFOS_AREA_HEIGHT
height available for infos pairs display
@ STRONG_HOME_ACTION
Black button, implicating the main action of the App.
void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewWithWarning(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
void nbgl_useCaseReviewStart(const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *rejectText, nbgl_callback_t continueCallback, nbgl_callback_t rejectCallback)
DEPRECATED void nbgl_useCaseHomeExt(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, bool withSettings, const char *actionButtonText, nbgl_callback_t actionCallback, nbgl_callback_t topRightCallback, nbgl_callback_t quitCallback)
void nbgl_useCaseKeypadDigits(const char *title, uint8_t minDigits, uint8_t maxDigits, uint8_t backToken, bool shuffled, tune_index_e tuneId, nbgl_pinValidCallback_t validatePinCallback, nbgl_layoutTouchCallback_t actionCallback)
#define APP_DESCRIPTION_MAX_LEN
Length of buffer used for the default Home tagline.
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
@ W3C_THREAT_DETECTED_WARN
Web3 Checks: Thread detexted, malicious (know drainer)
@ W3C_ISSUE_WARN
Web3 Checks issue.
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_LOSING_SWAP_WARN
Web3 Checks: Losing Swap risk.
@ NB_WARNING_TYPES
DEPRECATED void nbgl_useCaseAddressConfirmationExt(const char *address, nbgl_choiceCallback_t callback, const nbgl_contentTagValueList_t *tagValueList)
void nbgl_useCaseGenericConfiguration(const char *title, uint8_t initPage, const nbgl_genericContents_t *contents, nbgl_callback_t quitCallback)
#define INIT_HOME_PAGE
Value to pass to nbgl_useCaseHomeAndSettings() initSettingPage parameter to initialize the use case o...
void nbgl_useCaseReviewStreamingContinueExt(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback, nbgl_callback_t skipCallback)
void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseChoice(const nbgl_icon_details_t *icon, const char *message, const char *subMessage, const char *confirmText, const char *rejectString, nbgl_choiceCallback_t callback)
#define TAG_VALUE_AREA_HEIGHT
height available for tag/value pairs display
#define BLIND_OPERATION
This is to use in nbgl_operationType_t when the operation is "blind" This is used to indicate a warni...
#define TAGLINE_PART1
Default strings used in the Home tagline.
void nbgl_useCaseGenericReview(const nbgl_genericContents_t *contents, const char *rejectText, nbgl_callback_t rejectCallback)
#define TAGLINE_PART2
void nbgl_useCaseStaticReview(const nbgl_contentTagValueList_t *tagValueList, const nbgl_pageInfoLongPress_t *infoLongPress, const char *rejectText, nbgl_choiceCallback_t callback)
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices, const nbgl_contentRadioChoice_t *choicesList, uint8_t startIndex, bool withNav)
void nbgl_useCaseReviewStatus(nbgl_reviewStatusType_t reviewStatusType, nbgl_callback_t quitCallback)
#define LAST_PAGE_FOR_REVIEW
value of page parameter used with navigation callback when "skip" button is touched,...
@ CENTERED_INFO_WARNING
Centered info.
@ QRCODE_WARNING
QR Code.
@ BAR_LIST_WARNING
list of touchable bars, to display sub-pages
nbgl_reviewStatusType_t
The different types of review status.
@ STATUS_TYPE_TRANSACTION_REJECTED
@ STATUS_TYPE_ADDRESS_REJECTED
@ STATUS_TYPE_TRANSACTION_SIGNED
@ STATUS_TYPE_OPERATION_REJECTED
@ STATUS_TYPE_OPERATION_SIGNED
@ STATUS_TYPE_ADDRESS_VERIFIED
@ STATUS_TYPE_MESSAGE_SIGNED
@ STATUS_TYPE_MESSAGE_REJECTED
bool(* nbgl_navCallback_t)(uint8_t page, nbgl_pageContent_t *content)
prototype of navigation callback function
#define MAX_APP_NAME_FOR_SDK_TAGLINE
Max supported length of appName used for the default Home tagline.
void nbgl_useCaseAddressReview(const char *address, const nbgl_contentTagValueList_t *additionalTagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
@ TYPE_MESSAGE
@ TYPE_TRANSACTION
For operations transferring a coin or taken from an account to another.
@ TYPE_OPERATION
For other types of operation (generic type)
uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches, const nbgl_contentSwitchesList_t *switchesList, uint8_t startIndex, bool withNav)
This structure contains data to build a BARS_LIST content.
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
uint8_t nbBars
number of elements in barTexts and tokens array
This structure contains info to build a centered (vertically and horizontally) area,...
uint16_t iconHug
vertical margin to apply on top and bottom of the icon
const nbgl_icon_details_t * icon
the icon (can be null)
const char * title
title in black large (can be null)
const char * description
description in black small regular case (can be null)
const char * subText
sub-text in dark gray regular small case
bool padding
if true, apply a padding of 40px at the bottom
const char * smallTitle
sub-title in black small bold case (can be null)
nbgl_contentIllustrationType_t illustrType
const char * text2
second text (can be null)
const char * text1
first text (can be null)
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
int16_t offsetY
vertical shift to apply to this info (if >0, shift to bottom)
const char * text3
third text (can be null)
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
nbgl_contentCenter_t contentCenter
centered content (icon + text(s))
This structure contains data to build a centered info + simple black button content.
const char * buttonText
text of the long press button
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * text
centered text in large case
This structure contains data to build a INFOS_LIST content.
uint8_t nbInfos
number of elements in infoTypes and infoContents array
const char *const * infoContents
array of contents of infos (in black)
const char *const * infoTypes
array of types of infos (in black/bold)
const nbgl_contentValueExt_t * infoExtensions
This structure contains data to build a centered info + long press button content.
const char * longPressText
text of the long press button
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * text
centered text in large case
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
This structure contains [item,value] pair(s) and info about a potential "details" button,...
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
uint8_t confirmationToken
the token used as argument of the onActionCallback
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
const char * detailsButtonText
this text is used for "details" button (if NULL, no button)
const nbgl_icon_details_t * detailsButtonIcon
icon to use in details button
const nbgl_icon_details_t * detailsButtonIcon
icon to use in details button
const char * detailsButtonText
this text is used for "details" button
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
This structure contains a list of [tag,value] pairs.
nbgl_contentActionCallback_t actionCallback
called when a valueIcon is touched on a given pair
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
bool wrapping
if set to true, value text will be wrapped on ' ' to avoid cutting words
uint8_t startIndex
index of the first pair to get with callback
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair.
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
const nbgl_icon_details_t * valueIcon
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
const char * value
string giving the value name
const char * item
string giving the tag name
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
const char * fullValue
full string of the value when used as an alias
nbgl_contentValueAliasType_t aliasType
type of alias
This structure contains data to build a content.
nbgl_content_u content
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
nbgl_contentType_t type
type of page content in the content union
uint8_t nbContents
number of contents
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
nbgl_contentCallback_t contentGetterCallback
function to call to retrieve a given content
Structure describing the action button in Home Screen.
const nbgl_icon_details_t * icon
icon to use in action button in Home page
nbgl_callback_t callback
function to call when action button is touched in Home page
const char * text
text to use in action button in Home page
nbgl_homeActionStyle_t style
style of action button
This structure contains info to build a clickable "bar" with a text and an icon.
bool inactive
if set to true, the bar is grayed-out and cannot be touched
const char * text
text (can be NULL)
uint8_t token
the token that will be used as argument of the callback
bool large
set to true only for the main level of OS settings
const char * subText
sub text (can be NULL)
const nbgl_icon_details_t * iconLeft
a buffer containing the 1BPP icon for icon on left (can be NULL)
const nbgl_icon_details_t * iconRight
This structure contains info to build a pair of buttons, one on top of the other.
const char * bottomText
bottom-button text (index 1)
Structure containing all information when creating a layout. This structure must be passed as argumen...
nbgl_screenTickerConfiguration_t ticker
const char * tapActionText
Light gray text used when main container is "tapable".
nbgl_layoutTouchCallback_t onActionCallback
the callback to be called on any action on the layout
This structure contains info to build an extended footer.
struct nbgl_layoutFooter_t::@17::@19 emptySpace
if type is FOOTER_EMPTY
nbgl_layoutFooterType_t type
type of footer
This structure contains info to build a header.
nbgl_layoutHeaderType_t type
type of header
struct nbgl_layoutHeader_t::@9::@12 backAndText
if type is HEADER_BACK_ICON_AND_TEXT or HEADER_BACK_AND_TEXT
bool separationLine
if true, a separation line is added at the bottom of this control
const char * text
can be NULL if no text
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * url
URL for QR code.
Structure containing all specific information when creating a confirmation page.
Definition nbgl_page.h:149
const char * cancelText
the text used for cancel action, if NULL a simple X button is used
Definition nbgl_page.h:152
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played when button is pressed
Definition nbgl_page.h:157
This structure contains data to build a page in multi-pages mode (nbgl_pageDrawGenericContent)
Definition nbgl_flow.h:58
nbgl_contentTagValueDetails_t tagValueDetails
TAG_VALUE_DETAILS type
Definition nbgl_page.h:68
const char * title
text for the title of the page (if NULL, no title)
Definition nbgl_page.h:53
uint8_t topRightToken
token used when top-right button (if not NULL) is touched
Definition nbgl_page.h:58
nbgl_contentInfoLongPress_t infoLongPress
INFO_LONG_PRESS type
Definition nbgl_page.h:65
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition nbgl_flow.h:66
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:64
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played when title is touched
Definition nbgl_page.h:57
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:67
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:65
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition nbgl_flow.h:63
const nbgl_icon_details_t * topRightIcon
Definition nbgl_page.h:59
nbgl_contentType_t type
type of page content in the following union
Definition nbgl_flow.h:59
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition nbgl_flow.h:61
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition nbgl_page.h:69
bool isTouchableTitle
if set to true, the title is preceded by <- arrow to go back
Definition nbgl_page.h:54
nbgl_contentExtendedCenter_t extendedCenter
EXTENDED_CENTER type
Definition nbgl_page.h:64
Structure containing all specific information when creating an information page.
Definition nbgl_page.h:185
nbgl_layoutButtonStyle_t actionButtonStyle
style of "action" button
Definition nbgl_page.h:202
const char * actionButtonText
if not NULL an "action" button is set under the centered info
Definition nbgl_page.h:200
const nbgl_icon_details_t * actionButtonIcon
potential icon of "action" button
Definition nbgl_page.h:201
nbgl_pageButtonStyle_t bottomButtonStyle
style to apply to the Bottom button
Definition nbgl_page.h:188
nbgl_layoutCenteredInfo_t centeredInfo
description of the centered info to be used
Definition nbgl_page.h:186
Structure containing all specific information when creating a multi-screens page.
Definition nbgl_page.h:127
uint8_t nbPages
the number of pages to display (if <2, no navigation bar)
Definition nbgl_page.h:129
uint8_t quitToken
the token used as argument of the actionCallback when the footer is touched
Definition nbgl_page.h:131
uint8_t skipToken
if skipText is NULL the token used when right part of footer is touched
Definition nbgl_page.h:139
nbgl_pageNavigationType_t navType
Definition nbgl_page.h:132
uint8_t activePage
the index of the page to display at start-up
Definition nbgl_page.h:128
bool progressIndicator
if set to true, display a progress indicator on top of the page
Definition nbgl_page.h:134
nbgl_pageNavWithButtons_t navWithButtons
structure used when navigation with buttons
Definition nbgl_page.h:142
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played when next or back is pressed
Definition nbgl_page.h:136
bool visiblePageIndicator
if set to true, the page indicator will be visible in navigation
Definition nbgl_page.h:116
const char * quitText
the text displayed in footer (on the left), used to quit (only on Flex)
Definition nbgl_page.h:120
bool backButton
if set to true, a back button (<-) is displayed in the nav bar
Definition nbgl_page.h:114
This structure contains data to build a SWITCHES_LIST content.
uint8_t nbSwitches
number of elements in switches and tokens array
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
const char * modalTitle
title given to modal window displayed when tip-box is touched
nbgl_contentInfoList_t infos
infos pairs displayed in modal.
const char * text
text of the tip-box
nbgl_contentType_t type
type of page content in the following union
const nbgl_icon_details_t * icon
icon of the tip-box
const char *const * texts
array of texts for each bar (nbBars items, in black/bold)
const struct nbgl_warningDetails_s * details
array of nbBars structures giving what to display when each bar is touched.
const char *const * subTexts
array of texts for each bar (nbBars items, in black)
uint8_t nbBars
number of touchable bars
const nbgl_icon_details_t ** icons
array of icons for each bar (nbBars items)
The necessary parameters to build the page(s) displayed when the top-right button is touched in intro...
nbgl_warningDetailsType_t type
type of content in the page, determining what to use in the following union
nbgl_contentCenter_t centeredInfo
centered info, if type == CENTERED_INFO_WARNING
const char * title
text of the page (used to go back)
nbgl_layoutQRCode_t qrCode
QR code, if type == QRCODE_WARNING.
nbgl_warningBarList_t barList
touchable bars list, if type == BAR_LIST_WARNING
The necessary parameters to build a warning page preceding a review. One can either use predefinedSet...
uint32_t predefinedSet
Union of the different type of contents.
nbgl_contentInfoList_t infosList
INFOS_LIST type
nbgl_contentInfoLongPress_t infoLongPress
INFO_LONG_PRESS type
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
nbgl_contentBarsList_t barsList
BARS_LIST type
nbgl_contentExtendedCenter_t extendedCenter
EXTENDED_CENTER type
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
signed short int16_t
Definition usbd_conf.h:50
unsigned short uint16_t
Definition usbd_conf.h:54
unsigned char uint8_t
Definition usbd_conf.h:53
signed char int8_t
Definition usbd_conf.h:49