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
69#define NO_THREAT_OPERATION (1 << 7)
70
71/**********************
72 * TYPEDEFS
73 **********************/
74enum {
75 BACK_TOKEN = 0,
76 NEXT_TOKEN,
77 QUIT_TOKEN,
78 NAV_TOKEN,
79 SKIP_TOKEN,
80 CONTINUE_TOKEN,
81 ADDRESS_QRCODE_BUTTON_TOKEN,
82 ACTION_BUTTON_TOKEN,
83 CHOICE_TOKEN,
84 DETAILS_BUTTON_TOKEN,
85 CONFIRM_TOKEN,
86 REJECT_TOKEN,
87 VALUE_ALIAS_TOKEN,
88 INFO_ALIAS_TOKEN,
89 INFOS_TIP_BOX_TOKEN,
90 BLIND_WARNING_TOKEN,
91 WARNING_BUTTON_TOKEN,
92 TIP_BOX_TOKEN,
93 QUIT_TIPBOX_MODAL_TOKEN,
94 WARNING_CHOICE_TOKEN,
95 DISMISS_QR_TOKEN,
96 DISMISS_WARNING_TOKEN,
97 FIRST_WARN_BAR_TOKEN,
98 LAST_WARN_BAR_TOKEN = (FIRST_WARN_BAR_TOKEN + NB_WARNING_TYPES - 1),
99};
100
101typedef enum {
102 REVIEW_NAV = 0,
103 SETTINGS_NAV,
104 GENERIC_NAV,
105 STREAMING_NAV
106} NavType_t;
107
108typedef struct DetailsContext_s {
109 uint8_t nbPages;
110 uint8_t currentPage;
111 bool wrapping;
112 const char *tag;
113 const char *value;
114 const char *nextPageStart;
115} DetailsContext_t;
116
117typedef struct AddressConfirmationContext_s {
118 nbgl_layoutTagValue_t tagValuePair;
119 nbgl_layout_t *modalLayout;
120} AddressConfirmationContext_t;
121
122#ifdef NBGL_KEYPAD
123typedef struct KeypadContext_s {
124 uint8_t pinEntry[KEYPAD_MAX_DIGITS];
125 uint8_t pinLen;
126 uint8_t pinMinDigits;
127 uint8_t pinMaxDigits;
128 nbgl_layout_t *layoutCtx;
129 bool hidden;
130} KeypadContext_t;
131#endif
132
133typedef struct ReviewWithWarningContext_s {
134 bool isStreaming;
135 nbgl_operationType_t operationType;
136 const nbgl_contentTagValueList_t *tagValueList;
137 const nbgl_icon_details_t *icon;
138 const char *reviewTitle;
139 const char *reviewSubTitle;
140 const char *finishTitle;
141 const nbgl_warning_t *warning;
142 nbgl_choiceCallback_t choiceCallback;
143 nbgl_layout_t *layoutCtx;
144 nbgl_layout_t *modalLayout;
145 uint8_t securityReportLevel; // level 1 is the first level of menus
146 bool isIntro; // set to true during intro (before actual review)
147} ReviewWithWarningContext_t;
148
149// this union is intended to save RAM for context storage
150// indeed, these 2 contexts cannot happen simultaneously
151typedef union {
152#ifdef NBGL_KEYPAD
153 KeypadContext_t keypad;
154#endif
155 ReviewWithWarningContext_t reviewWithWarning;
156} SharedContext_t;
157
158typedef enum {
159 USE_CASE_GENERIC = 0,
160 USE_CASE_SPINNER
161} GenericContextType_t;
162
163typedef struct {
164 GenericContextType_t type; // type of Generic context usage
165 uint8_t spinnerPosition;
166 nbgl_genericContents_t genericContents;
167 int8_t currentContentIdx;
168 uint8_t currentContentElementNb;
169 uint8_t currentElementIdx;
170 bool hasStartingContent;
171 bool hasFinishingContent;
172 const char *detailsItem;
173 const char *detailsvalue;
174 bool detailsWrapping;
175 bool validWarningCtx; // set to true if the WarningContext is valid
177 *currentPairs; // to be used to retrieve the pairs with value alias
179 currentCallback; // to be used to retrieve the pairs with value alias
180 nbgl_layout_t modalLayout;
181 nbgl_layout_t backgroundLayout;
182 const nbgl_contentInfoList_t *currentInfos;
183} GenericContext_t;
184
185typedef struct {
186 const char *appName;
187 const nbgl_icon_details_t *appIcon;
188 const char *tagline;
189 const nbgl_genericContents_t *settingContents;
190 const nbgl_contentInfoList_t *infosList;
191 nbgl_homeAction_t homeAction;
192 nbgl_callback_t quitCallback;
193} nbgl_homeAndSettingsContext_t;
194
195typedef struct {
196 nbgl_operationType_t operationType;
197 nbgl_choiceCallback_t choiceCallback;
198} nbgl_reviewContext_t;
199
200typedef struct {
201 nbgl_operationType_t operationType;
202 nbgl_choiceCallback_t choiceCallback;
203 nbgl_callback_t skipCallback;
204 const nbgl_icon_details_t *icon;
205 uint8_t stepPageNb;
206} nbgl_reviewStreamingContext_t;
207
208typedef union {
209 nbgl_homeAndSettingsContext_t homeAndSettings;
210 nbgl_reviewContext_t review;
211 nbgl_reviewStreamingContext_t reviewStreaming;
212} nbgl_BundleNavContext_t;
213
214typedef struct {
215 const nbgl_icon_details_t *icon;
216 const char *text;
217 const char *subText;
218} SecurityReportItem_t;
219
220/**********************
221 * STATIC VARIABLES
222 **********************/
223
224// char buffers to build some strings
225static char tmpString[W3C_DESCRIPTION_MAX_LEN];
226
227// multi-purposes callbacks
228static nbgl_callback_t onQuit;
229static nbgl_callback_t onContinue;
230static nbgl_callback_t onAction;
231static nbgl_navCallback_t onNav;
232static nbgl_layoutTouchCallback_t onControls;
233static nbgl_contentActionCallback_t onContentAction;
234static nbgl_choiceCallback_t onChoice;
235static nbgl_callback_t onModalConfirm;
236#ifdef NBGL_KEYPAD
237static nbgl_pinValidCallback_t onValidatePin;
238#endif
239
240// contexts for background and modal pages
241static nbgl_page_t *pageContext;
242static nbgl_page_t *modalPageContext;
243
244// context for pages
245static const char *pageTitle;
246
247// context for tip-box
248static nbgl_tipBox_t activeTipBox;
249
250// context for navigation use case
251static nbgl_pageNavigationInfo_t navInfo;
252static bool forwardNavOnly;
253static NavType_t navType;
254
255static DetailsContext_t detailsContext;
256
257// multi-purpose context shared for non-concurrent usages
258static SharedContext_t sharedContext;
259
260// context for address review
261static AddressConfirmationContext_t addressConfirmationContext;
262
263// contexts for generic navigation
264static GenericContext_t genericContext;
265static nbgl_content_t
266 localContentsList[3]; // 3 needed for nbgl_useCaseReview (starting page / tags / final page)
267static uint8_t genericContextPagesInfo[MAX_PAGE_NB / PAGES_PER_UINT8];
268
269// contexts for bundle navigation
270static nbgl_BundleNavContext_t bundleNavContext;
271
272// indexed by nbgl_contentType_t
273static const uint8_t nbMaxElementsPerContentType[] = {
274 1, // CENTERED_INFO
275 1, // EXTENDED_CENTER
276 1, // INFO_LONG_PRESS
277 1, // INFO_BUTTON
278 1, // TAG_VALUE_LIST (computed dynamically)
279 1, // TAG_VALUE_DETAILS
280 1, // TAG_VALUE_CONFIRM
281 3, // SWITCHES_LIST (computed dynamically)
282 3, // INFOS_LIST (computed dynamically)
283 5, // CHOICES_LIST (computed dynamically)
284 5, // BARS_LIST (computed dynamically)
285};
286
287// clang-format off
288static const SecurityReportItem_t securityReportItems[NB_WARNING_TYPES] = {
290 .icon = &WARNING_ICON,
291 .text = "Blind signing required",
292 .subText = "This transaction's details are not fully verifiable. If "
293 "you sign, you could lose all your assets."
294 },
295 [W3C_ISSUE_WARN] = {
296 .icon = &WARNING_ICON,
297 .text = "Transaction Check unavailable",
298 .subText = NULL
299 },
301 .icon = &WARNING_ICON,
302 .text = "Risk detected",
303 .subText = "This transaction was scanned as risky by Web3 Checks."
304 },
306 .icon = &WARNING_ICON,
307 .text = "Critical threat",
308 .subText = "This transaction was scanned as malicious by Web3 Checks."
309 },
311 .icon = NULL,
312 .text = "No threat detected",
313 .subText = "Transaction Check didn't find any threat, but always "
314 "review transaction details carefully."
315 }
316};
317// clang-format on
318
319// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
320static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
321
322#ifdef NBGL_QRCODE
323/* buffer to store reduced address under QR Code */
324static char reducedAddress[QRCODE_REDUCED_ADDR_LEN];
325#endif // NBGL_QRCODE
326
327/**********************
328 * STATIC FUNCTIONS
329 **********************/
330static void displayReviewPage(uint8_t page, bool forceFullRefresh);
331static void displayDetailsPage(uint8_t page, bool forceFullRefresh);
332static void displayFullValuePage(const char *backText,
333 const char *aliasText,
334 const nbgl_contentValueExt_t *extension);
335static void displayInfosListModal(const char *modalTitle,
336 const nbgl_contentInfoList_t *infos,
337 bool fromReview);
338static void displaySettingsPage(uint8_t page, bool forceFullRefresh);
339static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh);
340static void pageCallback(int token, uint8_t index);
341#ifdef NBGL_QRCODE
342static void displayAddressQRCode(void);
343#endif // NBGL_QRCODE
344static void modalLayoutTouchCallback(int token, uint8_t index);
345static void displaySkipWarning(void);
346
347static void bundleNavStartHome(void);
348static void bundleNavStartSettingsAtPage(uint8_t initSettingPage);
349static void bundleNavStartSettings(void);
350
351static void bundleNavReviewStreamingChoice(bool confirm);
352static void displaySecurityReport(uint32_t set);
353static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details);
354static void displayInitialWarning(void);
355static void useCaseReview(nbgl_operationType_t operationType,
356 const nbgl_contentTagValueList_t *tagValueList,
357 const nbgl_icon_details_t *icon,
358 const char *reviewTitle,
359 const char *reviewSubTitle,
360 const char *finishTitle,
361 const nbgl_tipBox_t *tipBox,
362 nbgl_choiceCallback_t choiceCallback,
363 bool isLight,
364 bool playNotifSound);
365static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
366 const nbgl_icon_details_t *icon,
367 const char *reviewTitle,
368 const char *reviewSubTitle,
369 nbgl_choiceCallback_t choiceCallback,
370 bool playNotifSound);
371static void useCaseHomeExt(const char *appName,
372 const nbgl_icon_details_t *appIcon,
373 const char *tagline,
374 bool withSettings,
375 nbgl_homeAction_t *homeAction,
376 nbgl_callback_t topRightCallback,
377 nbgl_callback_t quitCallback);
378static void displayDetails(const char *tag, const char *value, bool wrapping);
379
380static void reset_callbacks(void)
381{
382 onQuit = NULL;
383 onContinue = NULL;
384 onAction = NULL;
385 onNav = NULL;
386 onControls = NULL;
387 onContentAction = NULL;
388 onChoice = NULL;
389 onModalConfirm = NULL;
390#ifdef NBGL_KEYPAD
391 onValidatePin = NULL;
392#endif
393}
394
395// Helper to set genericContext page info
396static void genericContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements, bool flag)
397{
398 uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements) + SET_PAGE_FLAG(flag);
399
400 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
401 &= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
402 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
403 |= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
404}
405
406// Helper to get genericContext page info
407static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool *flag)
408{
409 uint8_t pageData = genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
410 >> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
411 if (nbElements != NULL) {
412 *nbElements = GET_PAGE_NB_ELEMENTS(pageData);
413 }
414 if (flag != NULL) {
415 *flag = GET_PAGE_FLAG(pageData);
416 }
417}
418
419// Simple helper to get the number of elements inside a nbgl_content_t
420static uint8_t getContentNbElement(const nbgl_content_t *content)
421{
422 switch (content->type) {
423 case TAG_VALUE_LIST:
424 return content->content.tagValueList.nbPairs;
425 case SWITCHES_LIST:
426 return content->content.switchesList.nbSwitches;
427 case INFOS_LIST:
428 return content->content.infosList.nbInfos;
429 case CHOICES_LIST:
430 return content->content.choicesList.nbChoices;
431 case BARS_LIST:
432 return content->content.barsList.nbBars;
433 default:
434 return 1;
435 }
436}
437
438// Helper to retrieve the content inside a nbgl_genericContents_t using
439// either the contentsList or using the contentGetterCallback
440static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
441 int8_t contentIdx,
442 nbgl_content_t *content)
443{
444 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
445 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
446 return NULL;
447 }
448
449 if (genericContents->callbackCallNeeded) {
450 // Retrieve content through callback, but first memset the content.
451 memset(content, 0, sizeof(nbgl_content_t));
452 genericContents->contentGetterCallback(contentIdx, content);
453 return content;
454 }
455 else {
456 // Retrieve content through list
457 return PIC(&genericContents->contentsList[contentIdx]);
458 }
459}
460
461static void prepareNavInfo(bool isReview, uint8_t nbPages, const char *rejectText)
462{
463 memset(&navInfo, 0, sizeof(navInfo));
464
465 navInfo.nbPages = nbPages;
466 navInfo.tuneId = TUNE_TAP_CASUAL;
467 navInfo.progressIndicator = false;
468 navInfo.navType = NAV_WITH_BUTTONS;
469
470 if (isReview == false) {
471 navInfo.navWithButtons.navToken = NAV_TOKEN;
472 navInfo.navWithButtons.backButton = true;
473 }
474 else {
475 navInfo.quitToken = REJECT_TOKEN;
476 navInfo.navWithButtons.quitText = rejectText;
477 navInfo.navWithButtons.navToken = NAV_TOKEN;
479 = ((navType == STREAMING_NAV) && (nbPages < 2)) ? false : true;
480 navInfo.navWithButtons.visiblePageIndicator = (navType != STREAMING_NAV);
481 }
482}
483
484static void prepareReviewFirstPage(nbgl_contentCenter_t *contentCenter,
485 const nbgl_icon_details_t *icon,
486 const char *reviewTitle,
487 const char *reviewSubTitle)
488{
489 contentCenter->icon = icon;
490 contentCenter->title = reviewTitle;
491 contentCenter->description = reviewSubTitle;
492 contentCenter->subText = "Swipe to review";
493 contentCenter->smallTitle = NULL;
494 contentCenter->iconHug = 0;
495 contentCenter->padding = false;
496 contentCenter->illustrType = ICON_ILLUSTRATION;
497}
498
499static const char *getFinishTitle(nbgl_operationType_t operationType, const char *finishTitle)
500{
501 if (finishTitle != NULL) {
502 return finishTitle;
503 }
504 switch (operationType & REAL_TYPE_MASK) {
505 case TYPE_TRANSACTION:
506 return "Sign transaction";
507 case TYPE_MESSAGE:
508 return "Sign message";
509 default:
510 return "Sign operation";
511 }
512}
513
514static void prepareReviewLastPage(nbgl_operationType_t operationType,
515 nbgl_contentInfoLongPress_t *infoLongPress,
516 const nbgl_icon_details_t *icon,
517 const char *finishTitle)
518{
519 infoLongPress->text = getFinishTitle(operationType, finishTitle);
520 infoLongPress->icon = icon;
521 infoLongPress->longPressText = "Hold to sign";
522 infoLongPress->longPressToken = CONFIRM_TOKEN;
523}
524
525static void prepareReviewLightLastPage(nbgl_operationType_t operationType,
526 nbgl_contentInfoButton_t *infoButton,
527 const nbgl_icon_details_t *icon,
528 const char *finishTitle)
529{
530 infoButton->text = getFinishTitle(operationType, finishTitle);
531 infoButton->icon = icon;
532 infoButton->buttonText = "Approve";
533 infoButton->buttonToken = CONFIRM_TOKEN;
534}
535
536static const char *getRejectReviewText(nbgl_operationType_t operationType)
537{
538 UNUSED(operationType);
539 return "Reject";
540}
541
542// function called when navigating (or exiting) modal details pages
543// or when skip choice is displayed
544static void pageModalCallback(int token, uint8_t index)
545{
546 LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index);
547
548 if (token == INFOS_TIP_BOX_TOKEN) {
549 // the icon next to info type alias has been touched
550 displayFullValuePage(activeTipBox.infos.infoTypes[index],
551 activeTipBox.infos.infoContents[index],
552 &activeTipBox.infos.infoExtensions[index]);
553 return;
554 }
555 else if (token == INFO_ALIAS_TOKEN) {
556 // the icon next to info type alias has been touched, but coming from review
557 displayFullValuePage(genericContext.currentInfos->infoTypes[index],
558 genericContext.currentInfos->infoContents[index],
559 &genericContext.currentInfos->infoExtensions[index]);
560 return;
561 }
562 nbgl_pageRelease(modalPageContext);
563 modalPageContext = NULL;
564 if (token == NAV_TOKEN) {
565 if (index == EXIT_PAGE) {
566 // redraw the background layer
568 nbgl_refresh();
569 }
570 else {
571 displayDetailsPage(index, false);
572 }
573 }
574 else if (token == QUIT_TOKEN) {
575 // redraw the background layer
577 nbgl_refresh();
578 }
579 else if (token == QUIT_TIPBOX_MODAL_TOKEN) {
580 // if in "warning context", go back to security report
581 if (reviewWithWarnCtx.securityReportLevel == 2) {
582 reviewWithWarnCtx.securityReportLevel = 1;
583 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
584 }
585 else {
586 // redraw the background layer
588 nbgl_refresh();
589 }
590 }
591 else if (token == SKIP_TOKEN) {
592 if (index == 0) {
593 // display the last forward only review page, whatever it is
594 displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true);
595 }
596 else {
597 // display background, which should be the page where skip has been touched
599 nbgl_refresh();
600 }
601 }
602 else if (token == CHOICE_TOKEN) {
603 if (index == 0) {
604 if (onModalConfirm != NULL) {
605 onModalConfirm();
606 }
607 }
608 else {
609 // display background, which should be the page where skip has been touched
611 nbgl_refresh();
612 }
613 }
614}
615
616// generic callback for all pages except modal
617static void pageCallback(int token, uint8_t index)
618{
619 LOG_DEBUG(USE_CASE_LOGGER, "pageCallback, token = %d, index = %d\n", token, index);
620 if (token == QUIT_TOKEN) {
621 if (onQuit != NULL) {
622 onQuit();
623 }
624 }
625 else if (token == CONTINUE_TOKEN) {
626 if (onContinue != NULL) {
627 onContinue();
628 }
629 }
630 else if (token == CHOICE_TOKEN) {
631 if (onChoice != NULL) {
632 onChoice((index == 0) ? true : false);
633 }
634 }
635 else if (token == ACTION_BUTTON_TOKEN) {
636 if ((index == 0) && (onAction != NULL)) {
637 onAction();
638 }
639 else if ((index == 1) && (onQuit != NULL)) {
640 onQuit();
641 }
642 }
643#ifdef NBGL_QRCODE
644 else if (token == ADDRESS_QRCODE_BUTTON_TOKEN) {
645 displayAddressQRCode();
646 }
647#endif // NBGL_QRCODE
648 else if (token == CONFIRM_TOKEN) {
649 if (onChoice != NULL) {
650 onChoice(true);
651 }
652 }
653 else if (token == REJECT_TOKEN) {
654 if (onChoice != NULL) {
655 onChoice(false);
656 }
657 }
658 else if (token == DETAILS_BUTTON_TOKEN) {
659 displayDetails(genericContext.detailsItem,
660 genericContext.detailsvalue,
661 genericContext.detailsWrapping);
662 }
663 else if (token == NAV_TOKEN) {
664 if (index == EXIT_PAGE) {
665 if (onQuit != NULL) {
666 onQuit();
667 }
668 }
669 else {
670 if (navType == GENERIC_NAV || navType == STREAMING_NAV) {
671 displayGenericContextPage(index, false);
672 }
673 else if (navType == REVIEW_NAV) {
674 displayReviewPage(index, false);
675 }
676 else {
677 displaySettingsPage(index, false);
678 }
679 }
680 }
681 else if (token == NEXT_TOKEN) {
682 if (onNav != NULL) {
683 displayReviewPage(navInfo.activePage + 1, false);
684 }
685 else {
686 displayGenericContextPage(navInfo.activePage + 1, false);
687 }
688 }
689 else if (token == BACK_TOKEN) {
690 if (onNav != NULL) {
691 displayReviewPage(navInfo.activePage - 1, true);
692 }
693 else {
694 displayGenericContextPage(navInfo.activePage - 1, false);
695 }
696 }
697 else if (token == SKIP_TOKEN) {
698 // display a modal warning to confirm skip
699 displaySkipWarning();
700 }
701 else if (token == VALUE_ALIAS_TOKEN) {
702 // the icon next to value alias has been touched
703 const nbgl_contentTagValue_t *pair;
704 if (genericContext.currentPairs != NULL) {
705 pair = &genericContext.currentPairs[genericContext.currentElementIdx + index];
706 }
707 else {
708 pair = genericContext.currentCallback(genericContext.currentElementIdx + index);
709 }
710 displayFullValuePage(pair->item, pair->value, pair->extension);
711 }
712 else if (token == BLIND_WARNING_TOKEN) {
713 reviewWithWarnCtx.isIntro = false;
714 reviewWithWarnCtx.warning = NULL;
715 displaySecurityReport(1 << BLIND_SIGNING_WARN);
716 }
717 else if (token == WARNING_BUTTON_TOKEN) {
718 // top-right button, display the security modal
719 reviewWithWarnCtx.securityReportLevel = 1;
720 // if preset is used
721 if (reviewWithWarnCtx.warning->predefinedSet) {
722 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
723 }
724 else {
725 // use customized warning
726 if (reviewWithWarnCtx.isIntro) {
727 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->introDetails);
728 }
729 else {
730 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->reviewDetails);
731 }
732 }
733 }
734 else if (token == TIP_BOX_TOKEN) {
735 // if warning context is valid and if W3C directly display same as top-right
736 if (genericContext.validWarningCtx && (reviewWithWarnCtx.warning->predefinedSet != 0)) {
737 reviewWithWarnCtx.securityReportLevel = 1;
738 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
739 }
740 else {
741 displayInfosListModal(activeTipBox.modalTitle, &activeTipBox.infos, false);
742 }
743 }
744 else { // probably a control provided by caller
745 if (onContentAction != NULL) {
746 onContentAction(token, index, navInfo.activePage);
747 }
748 if (onControls != NULL) {
749 onControls(token, index);
750 }
751 }
752}
753
754// callback used for confirmation
755static void tickerCallback(void)
756{
757 nbgl_pageRelease(pageContext);
758 if (onQuit != NULL) {
759 onQuit();
760 }
761}
762
763// function used to display the current page in review
764static void displaySettingsPage(uint8_t page, bool forceFullRefresh)
765{
766 nbgl_pageContent_t content = {0};
767
768 if ((onNav == NULL) || (onNav(page, &content) == false)) {
769 return;
770 }
771
772 // override some fields
773 content.title = pageTitle;
774 content.isTouchableTitle = true;
775 content.titleToken = QUIT_TOKEN;
776 content.tuneId = TUNE_TAP_CASUAL;
777
778 navInfo.activePage = page;
779 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
780
781 if (forceFullRefresh) {
783 }
784 else {
786 }
787}
788
789// function used to display the current page in review
790static void displayReviewPage(uint8_t page, bool forceFullRefresh)
791{
792 nbgl_pageContent_t content = {0};
793
794 // ensure the page is valid
795 if ((navInfo.nbPages != 0) && (page >= (navInfo.nbPages))) {
796 return;
797 }
798 navInfo.activePage = page;
799 if ((onNav == NULL) || (onNav(navInfo.activePage, &content) == false)) {
800 return;
801 }
802
803 // override some fields
804 content.title = NULL;
805 content.isTouchableTitle = false;
806 content.tuneId = TUNE_TAP_CASUAL;
807
808 if (content.type == INFO_LONG_PRESS) { // last page
809 // for forward only review without known length...
810 // if we don't do that we cannot remove the '>' in the navigation bar at the last page
811 navInfo.nbPages = navInfo.activePage + 1;
812 content.infoLongPress.longPressToken = CONFIRM_TOKEN;
813 if (forwardNavOnly) {
814 // remove the "Skip" button
815 navInfo.skipText = NULL;
816 }
817 }
818
819 // override smallCaseForValue for tag/value types to false
820 if (content.type == TAG_VALUE_DETAILS) {
822 // the maximum displayable number of lines for value is NB_MAX_LINES_IN_REVIEW (without More
823 // button)
825 }
826 else if (content.type == TAG_VALUE_LIST) {
827 content.tagValueList.smallCaseForValue = false;
828 }
829 else if (content.type == TAG_VALUE_CONFIRM) {
831 // use confirm token for black button
832 content.tagValueConfirm.confirmationToken = CONFIRM_TOKEN;
833 }
834
835 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
836
837 if (forceFullRefresh) {
839 }
840 else {
842 }
843}
844
845// Helper that does the computing of which nbgl_content_t and which element in the content should be
846// displayed for the next generic context navigation page
847static const nbgl_content_t *genericContextComputeNextPageParams(uint8_t pageIdx,
848 nbgl_content_t *content,
849 uint8_t *p_nbElementsInNextPage,
850 bool *p_flag)
851{
852 int8_t nextContentIdx = genericContext.currentContentIdx;
853 int16_t nextElementIdx = genericContext.currentElementIdx;
854 uint8_t nbElementsInNextPage;
855
856 // Retrieve info on the next page
857 genericContextGetPageInfo(pageIdx, &nbElementsInNextPage, p_flag);
858 *p_nbElementsInNextPage = nbElementsInNextPage;
859
860 // Handle forward navigation:
861 // add to current index the number of pairs of the currently active page
862 if (pageIdx > navInfo.activePage) {
863 uint8_t nbElementsInCurrentPage;
864
865 genericContextGetPageInfo(navInfo.activePage, &nbElementsInCurrentPage, NULL);
866 nextElementIdx += nbElementsInCurrentPage;
867
868 // Handle case where the content to be displayed is in the next content
869 // In such case start at element index 0.
870 // If currentContentElementNb == 0, means not initialized, so skip
871 if ((nextElementIdx >= genericContext.currentContentElementNb)
872 && (genericContext.currentContentElementNb > 0)) {
873 nextContentIdx += 1;
874 nextElementIdx = 0;
875 }
876 }
877
878 // Handle backward navigation:
879 // add to current index the number of pairs of the currently active page
880 if (pageIdx < navInfo.activePage) {
881 // Backward navigation: remove to current index the number of pairs of the current page
882 nextElementIdx -= nbElementsInNextPage;
883
884 // Handle case where the content to be displayed is in the previous content
885 // In such case set a negative number as element index so that it is handled
886 // later once the previous content is accessible so that its elements number
887 // can be retrieved.
888 if (nextElementIdx < 0) {
889 nextContentIdx -= 1;
890 nextElementIdx = -nbElementsInNextPage;
891 }
892 }
893
894 const nbgl_content_t *p_content;
895 // Retrieve next content
896 if ((nextContentIdx == -1) && (genericContext.hasStartingContent)) {
897 p_content = &STARTING_CONTENT;
898 }
899 else if ((nextContentIdx == genericContext.genericContents.nbContents)
900 && (genericContext.hasFinishingContent)) {
901 p_content = &FINISHING_CONTENT;
902 }
903 else {
904 p_content = getContentAtIdx(&genericContext.genericContents, nextContentIdx, content);
905
906 if (p_content == NULL) {
907 LOG_DEBUG(USE_CASE_LOGGER, "Fail to retrieve content\n");
908 return NULL;
909 }
910 }
911
912 // Handle cases where we are going to display data from a new content:
913 // - navigation to a previous or to the next content
914 // - First page display (genericContext.currentContentElementNb == 0)
915 //
916 // In such case we need to:
917 // - Update genericContext.currentContentIdx
918 // - Update genericContext.currentContentElementNb
919 // - Update onContentAction callback
920 // - Update nextElementIdx in case it was previously not calculable
921 if ((nextContentIdx != genericContext.currentContentIdx)
922 || (genericContext.currentContentElementNb == 0)) {
923 genericContext.currentContentIdx = nextContentIdx;
924 genericContext.currentContentElementNb = getContentNbElement(p_content);
925 onContentAction = PIC(p_content->contentActionCallback);
926 if (nextElementIdx < 0) {
927 nextElementIdx = genericContext.currentContentElementNb + nextElementIdx;
928 }
929 }
930
931 // Sanity check
932 if ((nextElementIdx < 0) || (nextElementIdx >= genericContext.currentContentElementNb)) {
934 "Invalid element index %d / %d\n",
935 nextElementIdx,
936 genericContext.currentContentElementNb);
937 return NULL;
938 }
939
940 // Update genericContext elements
941 genericContext.currentElementIdx = nextElementIdx;
942 navInfo.activePage = pageIdx;
943
944 return p_content;
945}
946
947// Helper that generates a nbgl_pageContent_t to be displayed for the next generic context
948// navigation page
949static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
950 uint8_t nbElementsInPage,
951 bool flag,
952 nbgl_pageContent_t *pageContent)
953{
954 uint8_t nextElementIdx = genericContext.currentElementIdx;
955
956 pageContent->title = pageTitle;
957 pageContent->isTouchableTitle = false;
958 pageContent->titleToken = QUIT_TOKEN;
959 pageContent->tuneId = TUNE_TAP_CASUAL;
960
961 pageContent->type = p_content->type;
962 switch (pageContent->type) {
963 case CENTERED_INFO:
964 memcpy(&pageContent->centeredInfo,
965 &p_content->content.centeredInfo,
966 sizeof(pageContent->centeredInfo));
967 break;
968 case EXTENDED_CENTER:
969 memcpy(&pageContent->extendedCenter,
970 &p_content->content.extendedCenter,
971 sizeof(pageContent->extendedCenter));
972 break;
973 case INFO_LONG_PRESS:
974 memcpy(&pageContent->infoLongPress,
975 &p_content->content.infoLongPress,
976 sizeof(pageContent->infoLongPress));
977 break;
978
979 case INFO_BUTTON:
980 memcpy(&pageContent->infoButton,
981 &p_content->content.infoButton,
982 sizeof(pageContent->infoButton));
983 break;
984
985 case TAG_VALUE_LIST: {
986 nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
987
988 // memorize pairs (or callback) for usage when alias is used
989 genericContext.currentPairs = p_content->content.tagValueList.pairs;
990 genericContext.currentCallback = p_content->content.tagValueList.callback;
991
992 if (flag) {
993 // Flag can be set if the pair is too long to fit or because it needs
994 // to be displayed as centered info.
995
996 // First retrieve the pair
997 const nbgl_layoutTagValue_t *pair;
998
999 if (p_content->content.tagValueList.pairs != NULL) {
1000 pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
1001 }
1002 else {
1003 pair = PIC(p_content->content.tagValueList.callback(nextElementIdx));
1004 }
1005
1006 if (pair->centeredInfo) {
1007 pageContent->type = EXTENDED_CENTER;
1008 prepareReviewFirstPage(&pageContent->extendedCenter.contentCenter,
1009 pair->valueIcon,
1010 pair->item,
1011 pair->value);
1012
1013 // Skip population of nbgl_contentTagValueList_t structure
1014 p_tagValueList = NULL;
1015 }
1016 else {
1017 // if the pair is too long to fit, we use a TAG_VALUE_DETAILS content
1018 pageContent->type = TAG_VALUE_DETAILS;
1019 pageContent->tagValueDetails.detailsButtonText = "More";
1020 pageContent->tagValueDetails.detailsButtonIcon = NULL;
1021 pageContent->tagValueDetails.detailsButtonToken = DETAILS_BUTTON_TOKEN;
1022
1023 p_tagValueList = &pageContent->tagValueDetails.tagValueList;
1024
1025 // Backup pair info for easy data access upon click on button
1026 genericContext.detailsItem = pair->item;
1027 genericContext.detailsvalue = pair->value;
1028 genericContext.detailsWrapping = p_content->content.tagValueList.wrapping;
1029 }
1030 }
1031
1032 if (p_tagValueList != NULL) {
1033 p_tagValueList->nbPairs = nbElementsInPage;
1034 p_tagValueList->token = p_content->content.tagValueList.token;
1035 if (p_content->content.tagValueList.pairs != NULL) {
1036 p_tagValueList->pairs
1037 = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
1038 // parse pairs to check if any contains an alias for value
1039 for (uint8_t i = 0; i < nbElementsInPage; i++) {
1040 if (p_tagValueList->pairs[i].aliasValue) {
1041 p_tagValueList->token = VALUE_ALIAS_TOKEN;
1042 break;
1043 }
1044 }
1045 }
1046 else {
1047 p_tagValueList->pairs = NULL;
1048 p_tagValueList->callback = p_content->content.tagValueList.callback;
1049 p_tagValueList->startIndex = nextElementIdx;
1050 // parse pairs to check if any contains an alias for value
1051 for (uint8_t i = 0; i < nbElementsInPage; i++) {
1052 const nbgl_layoutTagValue_t *pair
1053 = PIC(p_content->content.tagValueList.callback(nextElementIdx + i));
1054 if (pair->aliasValue) {
1055 p_tagValueList->token = VALUE_ALIAS_TOKEN;
1056 break;
1057 }
1058 }
1059 }
1060 p_tagValueList->smallCaseForValue = false;
1062 p_tagValueList->wrapping = p_content->content.tagValueList.wrapping;
1063 }
1064
1065 break;
1066 }
1067 case TAG_VALUE_CONFIRM:
1068 memcpy(&pageContent->tagValueConfirm,
1069 &p_content->content.tagValueConfirm,
1070 sizeof(pageContent->tagValueConfirm));
1071 break;
1072 case SWITCHES_LIST:
1073 pageContent->switchesList.nbSwitches = nbElementsInPage;
1074 pageContent->switchesList.switches
1075 = PIC(&p_content->content.switchesList.switches[nextElementIdx]);
1076 break;
1077 case INFOS_LIST:
1078 pageContent->infosList.nbInfos = nbElementsInPage;
1079 pageContent->infosList.infoTypes
1080 = PIC(&p_content->content.infosList.infoTypes[nextElementIdx]);
1081 pageContent->infosList.infoContents
1082 = PIC(&p_content->content.infosList.infoContents[nextElementIdx]);
1083 break;
1084 case CHOICES_LIST:
1085 memcpy(&pageContent->choicesList,
1086 &p_content->content.choicesList,
1087 sizeof(pageContent->choicesList));
1088 pageContent->choicesList.nbChoices = nbElementsInPage;
1089 pageContent->choicesList.names
1090 = PIC(&p_content->content.choicesList.names[nextElementIdx]);
1091 if ((p_content->content.choicesList.initChoice >= nextElementIdx)
1092 && (p_content->content.choicesList.initChoice
1093 < nextElementIdx + nbElementsInPage)) {
1094 pageContent->choicesList.initChoice
1095 = p_content->content.choicesList.initChoice - nextElementIdx;
1096 }
1097 else {
1098 pageContent->choicesList.initChoice = nbElementsInPage;
1099 }
1100 break;
1101 case BARS_LIST:
1102 pageContent->barsList.nbBars = nbElementsInPage;
1103 pageContent->barsList.barTexts
1104 = PIC(&p_content->content.barsList.barTexts[nextElementIdx]);
1105 pageContent->barsList.tokens = PIC(&p_content->content.barsList.tokens[nextElementIdx]);
1106 pageContent->barsList.tuneId = p_content->content.barsList.tuneId;
1107 break;
1108 default:
1109 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported type %d\n", pageContent->type);
1110 return false;
1111 }
1112
1113 bool isFirstOrLastPage
1114 = ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
1115 || (p_content->type == INFO_LONG_PRESS);
1116 nbgl_operationType_t operationType
1117 = (navType == STREAMING_NAV)
1118 ? bundleNavContext.reviewStreaming.operationType
1119 : ((navType == GENERIC_NAV) ? bundleNavContext.review.operationType : 0);
1120
1121 // if first or last page of review and blind/risky operation, add the warning top-right button
1122 if (isFirstOrLastPage
1123 && (operationType & (BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION))) {
1124 // if issue is only Web3Checks "no threat", use privacy icon
1125 if ((operationType & NO_THREAT_OPERATION)
1126 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
1127 pageContent->topRightIcon = &PRIVACY_ICON;
1128 }
1129 else {
1130 pageContent->topRightIcon = &WARNING_ICON;
1131 }
1132
1133 pageContent->topRightToken
1134 = (operationType & BLIND_OPERATION) ? BLIND_WARNING_TOKEN : WARNING_BUTTON_TOKEN;
1135 }
1136
1137 return true;
1138}
1139
1140// function used to display the current page in generic context navigation mode
1141static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh)
1142{
1143 // Retrieve next page parameters
1144 nbgl_content_t content;
1145 uint8_t nbElementsInPage;
1146 bool flag;
1147 const nbgl_content_t *p_content = NULL;
1148
1149 if (navType == STREAMING_NAV) {
1150 if (pageIdx == LAST_PAGE_FOR_REVIEW) {
1151 if (bundleNavContext.reviewStreaming.skipCallback != NULL) {
1152 bundleNavContext.reviewStreaming.skipCallback();
1153 }
1154 return;
1155 }
1156 else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) {
1157 bundleNavReviewStreamingChoice(true);
1158 return;
1159 }
1160 }
1161
1162 if (navInfo.activePage == pageIdx) {
1163 p_content
1164 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1165 }
1166 else if (navInfo.activePage < pageIdx) {
1167 // Support going more than one step forward.
1168 // It occurs when initializing a navigation on an arbitrary page
1169 for (int i = navInfo.activePage + 1; i <= pageIdx; i++) {
1170 p_content = genericContextComputeNextPageParams(i, &content, &nbElementsInPage, &flag);
1171 }
1172 }
1173 else {
1174 if (pageIdx - navInfo.activePage > 1) {
1175 // We don't support going more than one step backward as it doesn't occurs for now?
1176 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported navigation\n");
1177 return;
1178 }
1179 p_content
1180 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1181 }
1182
1183 if (p_content == NULL) {
1184 return;
1185 }
1186
1187 // Create next page content
1188 nbgl_pageContent_t pageContent = {0};
1189 if (!genericContextPreparePageContent(p_content, nbElementsInPage, flag, &pageContent)) {
1190 return;
1191 }
1192
1193 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &pageContent);
1194
1195 if (forceFullRefresh) {
1197 }
1198 else {
1200 }
1201}
1202
1203// from the current details context, return a pointer on the details at the given page
1204static const char *getDetailsPageAt(uint8_t detailsPage)
1205{
1206 uint8_t page = 0;
1207 const char *currentChar = detailsContext.value;
1208 while (page < detailsPage) {
1209 uint16_t nbLines
1210 = nbgl_getTextNbLinesInWidth(SMALL_BOLD_FONT, currentChar, AVAILABLE_WIDTH, false);
1211 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1212 uint16_t len;
1213 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1214 currentChar,
1217 &len,
1218 detailsContext.wrapping);
1219 len -= 3;
1220 currentChar = currentChar + len;
1221 }
1222 page++;
1223 }
1224 return currentChar;
1225}
1226
1227// function used to display the current page in details review mode
1228static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh)
1229{
1230 static nbgl_layoutTagValue_t currentPair;
1231 nbgl_pageNavigationInfo_t info = {.activePage = detailsPage,
1232 .nbPages = detailsContext.nbPages,
1233 .navType = NAV_WITH_BUTTONS,
1234 .quitToken = QUIT_TOKEN,
1235 .navWithButtons.navToken = NAV_TOKEN,
1236 .navWithButtons.quitButton = true,
1237 .navWithButtons.backButton = true,
1238 .navWithButtons.quitText = NULL,
1239 .progressIndicator = false,
1240 .tuneId = TUNE_TAP_CASUAL};
1242 .topRightIcon = NULL,
1243 .tagValueList.nbPairs = 1,
1244 .tagValueList.pairs = &currentPair,
1245 .tagValueList.smallCaseForValue = true,
1246 .tagValueList.wrapping = detailsContext.wrapping};
1247
1248 if (modalPageContext != NULL) {
1249 nbgl_pageRelease(modalPageContext);
1250 }
1251 currentPair.item = detailsContext.tag;
1252 // if move backward or first page
1253 if (detailsPage <= detailsContext.currentPage) {
1254 // recompute current start from beginning
1255 currentPair.value = getDetailsPageAt(detailsPage);
1256 forceFullRefresh = true;
1257 }
1258 // else move forward
1259 else {
1260 currentPair.value = detailsContext.nextPageStart;
1261 }
1262 detailsContext.currentPage = detailsPage;
1264 SMALL_BOLD_FONT, currentPair.value, AVAILABLE_WIDTH, detailsContext.wrapping);
1265
1266 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1267 uint16_t len;
1268 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1269 currentPair.value,
1272 &len,
1273 detailsContext.wrapping);
1274 len -= 3;
1275 // memorize next position to save processing
1276 detailsContext.nextPageStart = currentPair.value + len;
1277 // use special feature to keep only NB_MAX_LINES_IN_DETAILS lines and replace the last 3
1278 // chars by "..."
1280 }
1281 else {
1282 detailsContext.nextPageStart = NULL;
1283 content.tagValueList.nbMaxLinesForValue = 0;
1284 }
1285 if (info.nbPages == 1) {
1286 // if only one page, no navigation bar, and use a footer instead
1287 info.navWithButtons.quitText = "Close";
1288 }
1289 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1290
1291 if (forceFullRefresh) {
1293 }
1294 else {
1296 }
1297}
1298
1299// function used to display the content of a full value, when touching an alias of a tag/value pair
1300static void displayFullValuePage(const char *backText,
1301 const char *aliasText,
1302 const nbgl_contentValueExt_t *extension)
1303{
1304 const char *modalTitle
1305 = (extension->backText != NULL) ? PIC(extension->backText) : PIC(backText);
1306 if (extension->aliasType == INFO_LIST_ALIAS) {
1307 genericContext.currentInfos = extension->infolist;
1308 displayInfosListModal(modalTitle, extension->infolist, true);
1309 }
1310 else {
1311 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1312 .withLeftBorder = true,
1313 .onActionCallback = &modalLayoutTouchCallback,
1314 .tapActionText = NULL};
1316 .separationLine = false,
1317 .backAndText.token = 0,
1318 .backAndText.tuneId = TUNE_TAP_CASUAL,
1319 .backAndText.text = modalTitle};
1320 genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1321 // add header with the tag part of the pair, to go back
1322 nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1323 // add either QR Code or full value text
1324 if (extension->aliasType == QR_CODE_ALIAS) {
1325#ifdef NBGL_QRCODE
1326 nbgl_layoutQRCode_t qrCode
1327 = {.url = extension->fullValue,
1328 .text1 = (extension->title != NULL) ? extension->title : extension->fullValue,
1329 .text2 = extension->explanation,
1330 .centered = true,
1331 .offsetY = 0};
1332
1333 nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode);
1334#endif // NBGL_QRCODE
1335 }
1336 else {
1337 const char *info;
1338 // add full value text
1339 if (extension->aliasType == ENS_ALIAS) {
1340 info = "ENS names are resolved by Ledger backend.";
1341 }
1342 else if (extension->aliasType == ADDRESS_BOOK_ALIAS) {
1343 info = "This account label comes from your Address Book in Ledger Live.";
1344 }
1345 else {
1346 info = extension->explanation;
1347 }
1349 genericContext.modalLayout, aliasText, extension->fullValue, info);
1350 }
1351 // draw & refresh
1352 nbgl_layoutDraw(genericContext.modalLayout);
1353 nbgl_refresh();
1354 }
1355}
1356
1357// function used to display the modal containing tip-box infos
1358static void displayInfosListModal(const char *modalTitle,
1359 const nbgl_contentInfoList_t *infos,
1360 bool fromReview)
1361{
1363 .nbPages = 1,
1364 .navType = NAV_WITH_BUTTONS,
1365 .quitToken = QUIT_TIPBOX_MODAL_TOKEN,
1366 .navWithButtons.navToken = NAV_TOKEN,
1367 .navWithButtons.quitButton = false,
1368 .navWithButtons.backButton = true,
1369 .navWithButtons.quitText = NULL,
1370 .progressIndicator = false,
1371 .tuneId = TUNE_TAP_CASUAL};
1372 nbgl_pageContent_t content
1373 = {.type = INFOS_LIST,
1374 .topRightIcon = NULL,
1375 .infosList.nbInfos = infos->nbInfos,
1376 .infosList.withExtensions = infos->withExtensions,
1377 .infosList.infoTypes = infos->infoTypes,
1378 .infosList.infoContents = infos->infoContents,
1379 .infosList.infoExtensions = infos->infoExtensions,
1380 .infosList.token = fromReview ? INFO_ALIAS_TOKEN : INFOS_TIP_BOX_TOKEN,
1381 .title = modalTitle,
1382 .titleToken = QUIT_TIPBOX_MODAL_TOKEN,
1383 .tuneId = TUNE_TAP_CASUAL};
1384
1385 if (modalPageContext != NULL) {
1386 nbgl_pageRelease(modalPageContext);
1387 }
1388 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1389
1391}
1392
1393#ifdef NBGL_QRCODE
1394static void displayAddressQRCode(void)
1395{
1396 // display the address as QR Code
1397 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1398 .withLeftBorder = true,
1399 .onActionCallback = &modalLayoutTouchCallback,
1400 .tapActionText = NULL};
1401 nbgl_layoutHeader_t headerDesc = {
1402 .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1403 nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1404 .text1 = NULL,
1405 .centered = true,
1406 .offsetY = 0};
1407
1408 addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1409 // add empty header for better look
1410 nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1411 // compute nb lines to check whether it shall be shorten (max is 3 lines)
1413 SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1414
1415 if (nbLines <= QRCODE_NB_MAX_LINES) {
1416 qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1417 }
1418 else {
1419 // only keep beginning and end of text, and add ... in the middle
1420 nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1421 addressConfirmationContext.tagValuePair.value,
1423 QRCODE_NB_MAX_LINES,
1424 reducedAddress,
1425 QRCODE_REDUCED_ADDR_LEN);
1426 qrCode.text2 = reducedAddress; // in gray
1427 }
1428
1429 nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1430
1432 addressConfirmationContext.modalLayout, "Close", DISMISS_QR_TOKEN, TUNE_TAP_CASUAL);
1433 nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1434 nbgl_refresh();
1435}
1436
1437#endif // NBGL_QRCODE
1438
1439// called when header is touched on modal page, to dismiss it
1440static void modalLayoutTouchCallback(int token, uint8_t index)
1441{
1442 UNUSED(index);
1443 if (token == DISMISS_QR_TOKEN) {
1444 // dismiss modal
1445 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1446 addressConfirmationContext.modalLayout = NULL;
1448 }
1449 else if (token == DISMISS_WARNING_TOKEN) {
1450 // dismiss modal
1451 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1452 // if already at first level, simply redraw the background
1453 if (reviewWithWarnCtx.securityReportLevel <= 1) {
1454 reviewWithWarnCtx.modalLayout = NULL;
1456 }
1457 else {
1458 // We are at level 2 of warning modal, so re-display the level 1
1459 reviewWithWarnCtx.securityReportLevel = 1;
1460 // if preset is used
1461 if (reviewWithWarnCtx.warning->predefinedSet) {
1462 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1463 }
1464 else {
1465 // use customized warning
1466 const nbgl_warningDetails_t *details
1467 = (reviewWithWarnCtx.isIntro) ? reviewWithWarnCtx.warning->introDetails
1468 : reviewWithWarnCtx.warning->reviewDetails;
1469 displayCustomizedSecurityReport(details);
1470 }
1471 return;
1472 }
1473 }
1474 else if ((token >= FIRST_WARN_BAR_TOKEN) && (token <= LAST_WARN_BAR_TOKEN)) {
1475 // dismiss modal before creating a new one
1476 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1477 reviewWithWarnCtx.securityReportLevel = 2;
1478 // if preset is used
1479 if (reviewWithWarnCtx.warning->predefinedSet) {
1480 displaySecurityReport(1 << (token - FIRST_WARN_BAR_TOKEN));
1481 }
1482 else {
1483 // use customized warning
1484 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1485 ? reviewWithWarnCtx.warning->introDetails
1486 : reviewWithWarnCtx.warning->reviewDetails;
1487 if (details->type == BAR_LIST_WARNING) {
1488 displayCustomizedSecurityReport(
1489 &details->barList.details[token - FIRST_WARN_BAR_TOKEN]);
1490 }
1491 }
1492 return;
1493 }
1494 else {
1495 // dismiss modal
1496 nbgl_layoutRelease(genericContext.modalLayout);
1497 genericContext.modalLayout = NULL;
1499 }
1500 nbgl_refresh();
1501}
1502
1503// called when item are touched in layout
1504static void layoutTouchCallback(int token, uint8_t index)
1505{
1506 // choice buttons in initial warning page
1507 if (token == WARNING_CHOICE_TOKEN) {
1508 if (index == 0) { // top button to exit
1509 reviewWithWarnCtx.choiceCallback(false);
1510 }
1511 else { // bottom button to continue to review
1512 reviewWithWarnCtx.isIntro = false;
1513 if (reviewWithWarnCtx.isStreaming) {
1514 useCaseReviewStreamingStart(reviewWithWarnCtx.operationType,
1515 reviewWithWarnCtx.icon,
1516 reviewWithWarnCtx.reviewTitle,
1517 reviewWithWarnCtx.reviewSubTitle,
1518 reviewWithWarnCtx.choiceCallback,
1519 false);
1520 }
1521 else {
1522 useCaseReview(reviewWithWarnCtx.operationType,
1523 reviewWithWarnCtx.tagValueList,
1524 reviewWithWarnCtx.icon,
1525 reviewWithWarnCtx.reviewTitle,
1526 reviewWithWarnCtx.reviewSubTitle,
1527 reviewWithWarnCtx.finishTitle,
1528 &activeTipBox,
1529 reviewWithWarnCtx.choiceCallback,
1530 false,
1531 false);
1532 }
1533 }
1534 }
1535 // top-right button in initial warning page
1536 else if (token == WARNING_BUTTON_TOKEN) {
1537 // start directly at first level of security report
1538 reviewWithWarnCtx.securityReportLevel = 1;
1539 // if preset is used
1540 if (reviewWithWarnCtx.warning->predefinedSet) {
1541 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1542 }
1543 else {
1544 // use customized warning
1545 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1546 ? reviewWithWarnCtx.warning->introDetails
1547 : reviewWithWarnCtx.warning->reviewDetails;
1548 displayCustomizedSecurityReport(details);
1549 }
1550 }
1551}
1552
1553// called when skip button is touched in footer, during forward only review
1554static void displaySkipWarning(void)
1555{
1557 .cancelText = "Go back to review",
1558 .centeredInfo.text1 = "Skip review?",
1559 .centeredInfo.text2
1560 = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1561 .centeredInfo.text3 = NULL,
1562 .centeredInfo.style = LARGE_CASE_INFO,
1563 .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON,
1564 .centeredInfo.offsetY = 0,
1565 .confirmationText = "Yes, skip",
1566 .confirmationToken = SKIP_TOKEN,
1567 .tuneId = TUNE_TAP_CASUAL,
1568 .modal = true};
1569 if (modalPageContext != NULL) {
1570 nbgl_pageRelease(modalPageContext);
1571 }
1572 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1574}
1575
1576#ifdef NBGL_KEYPAD
1577// called to update the keypad and automatically show / hide:
1578// - backspace key if no digits are present
1579// - validation key if the min digit is reached
1580// - keypad if the max number of digit is reached
1581static void updateKeyPad(bool add)
1582{
1583 bool enableValidate, enableBackspace, enableDigits;
1584 bool redrawKeypad = false;
1586
1587 enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1588 enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1589 enableBackspace = (keypadContext.pinLen > 0);
1590 if (add) {
1591 if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1592 || // activate "validate" button on keypad
1593 (keypadContext.pinLen == keypadContext.pinMaxDigits)
1594 || // deactivate "digits" on keypad
1595 (keypadContext.pinLen == 1)) { // activate "backspace"
1596 redrawKeypad = true;
1597 }
1598 }
1599 else { // remove
1600 if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1601 (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1602 || // deactivate "validate" button on keypad
1603 (keypadContext.pinLen
1604 == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1605 redrawKeypad = true;
1606 }
1607 }
1608 if (keypadContext.hidden == true) {
1609 nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1610 }
1611 else {
1613 keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1614 }
1615 if (redrawKeypad) {
1617 keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1618 }
1619
1620 if ((!add) && (keypadContext.pinLen == 0)) {
1621 // Full refresh to fully clean the bullets when reaching 0 digits
1622 mode = FULL_COLOR_REFRESH;
1623 }
1625}
1626
1627// called when a key is touched on the keypad
1628static void keypadCallback(char touchedKey)
1629{
1630 switch (touchedKey) {
1631 case BACKSPACE_KEY:
1632 if (keypadContext.pinLen > 0) {
1633 keypadContext.pinLen--;
1634 keypadContext.pinEntry[keypadContext.pinLen] = 0;
1635 }
1636 updateKeyPad(false);
1637 break;
1638
1639 case VALIDATE_KEY:
1640 // Gray out keyboard / buttons as a first user feedback
1641 nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1644
1645 onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1646 break;
1647
1648 default:
1649 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1650 if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1651 keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1652 keypadContext.pinLen++;
1653 }
1654 updateKeyPad(true);
1655 }
1656 break;
1657 }
1658}
1659
1660// called to create a keypad, with either hidden or visible digits
1661static void keypadGenericUseCase(const char *title,
1662 uint8_t minDigits,
1663 uint8_t maxDigits,
1664 uint8_t backToken,
1665 bool shuffled,
1666 bool hidden,
1667 tune_index_e tuneId,
1668 nbgl_pinValidCallback_t validatePinCallback,
1669 nbgl_layoutTouchCallback_t actionCallback)
1670{
1671 nbgl_layoutDescription_t layoutDescription = {0};
1673 .separationLine = true,
1674 .backAndText.token = backToken,
1675 .backAndText.tuneId = tuneId,
1676 .backAndText.text = NULL};
1677 int status = -1;
1678
1679 if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1680 return;
1681 }
1682
1683 reset_callbacks();
1684 // reset the keypad context
1685 memset(&keypadContext, 0, sizeof(KeypadContext_t));
1686
1687 // get a layout
1688 layoutDescription.onActionCallback = actionCallback;
1689 layoutDescription.modal = false;
1690 layoutDescription.withLeftBorder = false;
1691 keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1692 keypadContext.hidden = hidden;
1693
1694 // set back key in header
1695 nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1696
1697 // add keypad
1698 status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1699 if (status < 0) {
1700 return;
1701 }
1702 // add keypad content
1704 keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1705
1706 if (status < 0) {
1707 return;
1708 }
1709
1710 // validation pin callback
1711 onValidatePin = validatePinCallback;
1712 // pin code acceptable lengths
1713 keypadContext.pinMinDigits = minDigits;
1714 keypadContext.pinMaxDigits = maxDigits;
1715
1716 nbgl_layoutDraw(keypadContext.layoutCtx);
1718}
1719#endif
1720
1721static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1722 uint8_t pageIdxStart,
1723 bool isLast,
1724 bool isSkippable)
1725{
1726 uint8_t nbElements = 0;
1727 uint8_t nbPages = 0;
1728 uint8_t nbElementsInPage;
1729 uint8_t elemIdx = 0;
1730 bool flag;
1731
1732 nbElements = getContentNbElement(content);
1733
1734 while (nbElements > 0) {
1735 flag = 0;
1736 // if the current page is not the first one (or last), a navigation bar exists
1737 bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1738 if (content->type == TAG_VALUE_LIST) {
1739 nbElementsInPage = nbgl_useCaseGetNbTagValuesInPageExt(
1740 nbElements, &content->content.tagValueList, elemIdx, isSkippable, &flag);
1741 }
1742 else if (content->type == INFOS_LIST) {
1743 nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1744 nbElements, &content->content.infosList, elemIdx, hasNav);
1745 }
1746 else if (content->type == SWITCHES_LIST) {
1747 nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1748 nbElements, &content->content.switchesList, elemIdx, hasNav);
1749 }
1750 else if (content->type == BARS_LIST) {
1751 nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1752 nbElements, &content->content.barsList, elemIdx, hasNav);
1753 }
1754 else if (content->type == CHOICES_LIST) {
1755 nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1756 nbElements, &content->content.choicesList, elemIdx, hasNav);
1757 }
1758 else {
1759 nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1760 }
1761
1762 elemIdx += nbElementsInPage;
1763 genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1764 nbElements -= nbElementsInPage;
1765 nbPages++;
1766 }
1767
1768 return nbPages;
1769}
1770
1771static uint8_t getNbPagesForGenericContents(const nbgl_genericContents_t *genericContents,
1772 uint8_t pageIdxStart,
1773 bool isSkippable)
1774{
1775 uint8_t nbPages = 0;
1776 nbgl_content_t content;
1777 const nbgl_content_t *p_content;
1778
1779 for (int i = 0; i < genericContents->nbContents; i++) {
1780 p_content = getContentAtIdx(genericContents, i, &content);
1781 if (p_content == NULL) {
1782 return 0;
1783 }
1784 nbPages += nbgl_useCaseGetNbPagesForContent(p_content,
1785 pageIdxStart + nbPages,
1786 (i == (genericContents->nbContents - 1)),
1787 isSkippable);
1788 }
1789
1790 return nbPages;
1791}
1792
1793static void prepareAddressConfirmationPages(const char *address,
1794 const nbgl_contentTagValueList_t *tagValueList,
1795 nbgl_content_t *firstPageContent,
1796 nbgl_content_t *secondPageContent)
1797{
1798 nbgl_contentTagValueConfirm_t *tagValueConfirm;
1799
1800 addressConfirmationContext.tagValuePair.item = "Address";
1801 addressConfirmationContext.tagValuePair.value = address;
1802
1803 // First page
1804 firstPageContent->type = TAG_VALUE_CONFIRM;
1805 tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1806
1807#ifdef NBGL_QRCODE
1808 tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1809 // only use "Show as QR" when it's not the last page
1810 if (tagValueList != NULL) {
1811 tagValueConfirm->detailsButtonText = "Show as QR";
1812 }
1813 else {
1814 tagValueConfirm->detailsButtonText = NULL;
1815 }
1816 tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1817#else // NBGL_QRCODE
1818 tagValueConfirm->detailsButtonText = NULL;
1819 tagValueConfirm->detailsButtonIcon = NULL;
1820#endif // NBGL_QRCODE
1821 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1822 tagValueConfirm->tagValueList.nbPairs = 1;
1823 tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1824 tagValueConfirm->tagValueList.smallCaseForValue = false;
1825 tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1826 tagValueConfirm->tagValueList.wrapping = false;
1827 // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1828 // no confirmation button
1829 if (tagValueList != NULL) {
1830 tagValueConfirm->confirmationText = NULL;
1831 }
1832 else {
1833 // otherwise no tap to continue but a confirmation button
1834 tagValueConfirm->confirmationText = "Confirm";
1835 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1836 }
1837
1838 // Second page if any:
1839 if (tagValueList != NULL) {
1840 // the second page is dedicated to the extended tag/value pairs
1841 secondPageContent->type = TAG_VALUE_CONFIRM;
1842 tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1843 tagValueConfirm->confirmationText = "Confirm";
1844 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1845 tagValueConfirm->detailsButtonText = NULL;
1846 tagValueConfirm->detailsButtonIcon = NULL;
1847 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1848 memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1849 }
1850}
1851
1852static void bundleNavStartHome(void)
1853{
1854 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1855
1856 useCaseHomeExt(context->appName,
1857 context->appIcon,
1858 context->tagline,
1859 context->settingContents != NULL ? true : false,
1860 &context->homeAction,
1861 bundleNavStartSettings,
1862 context->quitCallback);
1863}
1864
1865static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1866{
1867 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1868
1869 nbgl_useCaseGenericSettings(context->appName,
1870 initSettingPage,
1871 context->settingContents,
1872 context->infosList,
1873 bundleNavStartHome);
1874}
1875
1876static void bundleNavStartSettings(void)
1877{
1878 bundleNavStartSettingsAtPage(0);
1879}
1880
1881static void bundleNavReviewConfirmRejection(void)
1882{
1883 bundleNavContext.review.choiceCallback(false);
1884}
1885
1886static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1887 nbgl_callback_t callback)
1888{
1889 const char *title;
1890 const char *confirmText;
1891 // clear skip and blind bits
1892 operationType
1893 &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION);
1894 if (operationType == TYPE_TRANSACTION) {
1895 title = "Reject transaction?";
1896 confirmText = "Go back to transaction";
1897 }
1898 else if (operationType == TYPE_MESSAGE) {
1899 title = "Reject message?";
1900 confirmText = "Go back to message";
1901 }
1902 else {
1903 title = "Reject operation?";
1904 confirmText = "Go back to operation";
1905 }
1906
1907 // display a choice to confirm/cancel rejection
1908 nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1909}
1910
1911static void bundleNavReviewChoice(bool confirm)
1912{
1913 if (confirm) {
1914 bundleNavContext.review.choiceCallback(true);
1915 }
1916 else {
1917 bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1918 bundleNavReviewConfirmRejection);
1919 }
1920}
1921
1922static void bundleNavReviewStreamingConfirmRejection(void)
1923{
1924 bundleNavContext.reviewStreaming.choiceCallback(false);
1925}
1926
1927static void bundleNavReviewStreamingChoice(bool confirm)
1928{
1929 if (confirm) {
1930 // Display a spinner if it wasn't the finish step
1931 if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1932 nbgl_useCaseSpinner("Processing");
1933 }
1934 bundleNavContext.reviewStreaming.choiceCallback(true);
1935 }
1936 else {
1937 bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1938 bundleNavReviewStreamingConfirmRejection);
1939 }
1940}
1941
1942// function used to display the security level page in modal
1943// it can be either a single page with text or QR code, or a list of touchable bars leading
1944// to single pages
1945static void displaySecurityReport(uint32_t set)
1946{
1947 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1948 .withLeftBorder = true,
1949 .onActionCallback = modalLayoutTouchCallback,
1950 .tapActionText = NULL};
1952 .separationLine = true,
1953 .backAndText.icon = NULL,
1954 .backAndText.tuneId = TUNE_TAP_CASUAL,
1955 .backAndText.token = DISMISS_WARNING_TOKEN};
1956 nbgl_layoutFooter_t footerDesc
1957 = {.type = FOOTER_EMPTY, .separationLine = false, .emptySpace.height = 0};
1958 uint8_t i;
1959 uint8_t nbWarnings = 0;
1960 const char *provider;
1961
1962 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
1963
1964 // count the number of warnings
1965 for (i = 0; i < NB_WARNING_TYPES; i++) {
1966 if ((set & (1 << i)) && (i != W3C_NO_THREAT_WARN)) {
1967 nbWarnings++;
1968 }
1969 }
1970
1971 // display a list of touchable bars in some conditions:
1972 // - we must be in the first level of security report
1973 // - and be in the review itself (not from the intro/warning screen)
1974 //
1975 if ((reviewWithWarnCtx.securityReportLevel == 1) && (!reviewWithWarnCtx.isIntro)) {
1976 for (i = 0; i < NB_WARNING_TYPES; i++) {
1977 if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) {
1978 nbgl_layoutBar_t bar = {0};
1979 if ((i == BLIND_SIGNING_WARN) || (i == W3C_NO_THREAT_WARN) || (i == W3C_ISSUE_WARN)
1980 || (reviewWithWarnCtx.warning->providerMessage == NULL)) {
1981 bar.subText = securityReportItems[i].subText;
1982 }
1983 else {
1984 bar.subText = reviewWithWarnCtx.warning->providerMessage;
1985 }
1986 bar.text = securityReportItems[i].text;
1987 if (i != W3C_NO_THREAT_WARN) {
1988 bar.iconRight = &PUSH_ICON;
1989 bar.token = FIRST_WARN_BAR_TOKEN + i;
1990 bar.tuneId = TUNE_TAP_CASUAL;
1991 }
1992 else {
1994 }
1995 bar.iconLeft = securityReportItems[i].icon;
1996 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
1997 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
1998 }
1999 }
2000 headerDesc.backAndText.text = "Security report";
2001 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2002 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2003 nbgl_refresh();
2004 return;
2005 }
2006 if (reviewWithWarnCtx.warning && reviewWithWarnCtx.warning->reportProvider) {
2007 provider = reviewWithWarnCtx.warning->reportProvider;
2008 }
2009 else {
2010 provider = "[unknown]";
2011 }
2012 if ((set & (1 << W3C_THREAT_DETECTED_WARN)) || (set & (1 << W3C_RISK_DETECTED_WARN))) {
2013 size_t urlLen = 0;
2014 char *destStr
2015 = tmpString
2016 + W3C_DESCRIPTION_MAX_LEN / 2; // use the second half of tmpString for strings
2017#ifdef NBGL_QRCODE
2018 // display a QR Code
2019 nbgl_layoutQRCode_t qrCode = {.url = destStr,
2020 .text1 = reviewWithWarnCtx.warning->reportUrl,
2021 .text2 = "Scan to view full report",
2022 .centered = true,
2023 .offsetY = 0};
2024 // add "https://"" as prefix of the given URL
2025 snprintf(destStr,
2026 W3C_DESCRIPTION_MAX_LEN / 2,
2027 "https://%s",
2028 reviewWithWarnCtx.warning->reportUrl);
2029 urlLen = strlen(destStr) + 1;
2030 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode);
2031 footerDesc.emptySpace.height = 24;
2032#endif // NBGL_QRCODE
2033 // use the next part of destStr for back bar text
2034 snprintf(destStr + urlLen, W3C_DESCRIPTION_MAX_LEN / 2 - urlLen, "%s report", provider);
2035 headerDesc.backAndText.text = destStr + urlLen;
2036 }
2037 else if (set & (1 << BLIND_SIGNING_WARN)) {
2038 // display a centered if in review
2039 nbgl_contentCenter_t info = {0};
2040 info.icon = &LARGE_WARNING_ICON;
2041 info.title = "This transaction cannot be Clear Signed";
2042 info.description
2043 = "This transaction or message cannot be decoded fully. If you choose to sign, you "
2044 "could be authorizing malicious actions that can drain your wallet.\n\nLearn more: "
2045 "ledger.com/e8";
2046 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2047 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2048 headerDesc.separationLine = false;
2049 }
2050 else if (set & (1 << W3C_ISSUE_WARN)) {
2051 // if W3 Checks issue, display a centered info
2052 nbgl_contentCenter_t info = {0};
2053 info.icon = &LARGE_WARNING_ICON;
2054 info.title = "Transaction Check unavailable";
2055 info.description
2056 = "If you're not using the\nLedger Live app, Transaction Check might not work. If your "
2057 "are using Ledger Live, reject the transaction and try again.\n\nGet help at "
2058 "ledger.com/e11";
2059 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2060 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2061 headerDesc.separationLine = false;
2062 }
2063 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2064 if (footerDesc.emptySpace.height > 0) {
2065 nbgl_layoutAddExtendedFooter(reviewWithWarnCtx.modalLayout, &footerDesc);
2066 }
2067 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2068 nbgl_refresh();
2069}
2070
2071// function used to display the security level page in modal
2072// it can be either a single page with text or QR code, or a list of touchable bars leading
2073// to single pages
2074static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details)
2075{
2076 nbgl_layoutDescription_t layoutDescription = {.modal = true,
2077 .withLeftBorder = true,
2078 .onActionCallback = modalLayoutTouchCallback,
2079 .tapActionText = NULL};
2081 .separationLine = true,
2082 .backAndText.icon = NULL,
2083 .backAndText.tuneId = TUNE_TAP_CASUAL,
2084 .backAndText.token = DISMISS_WARNING_TOKEN};
2085 uint8_t i;
2086
2087 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
2088 headerDesc.backAndText.text = details->title;
2089 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2090 if (details->type == BAR_LIST_WARNING) {
2091 // if more than one warning warning, so use a list of touchable bars
2092 for (i = 0; i < details->barList.nbBars; i++) {
2093 nbgl_layoutBar_t bar;
2094 bar.text = details->barList.texts[i];
2095 bar.subText = details->barList.subTexts[i];
2096 bar.iconRight = &PUSH_ICON;
2097 bar.iconLeft = details->barList.icons[i];
2098 bar.token = FIRST_WARN_BAR_TOKEN + i;
2099 bar.tuneId = TUNE_TAP_CASUAL;
2100 bar.large = false;
2101 bar.inactive = false;
2102 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
2103 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
2104 }
2105 }
2106 else if (details->type == QRCODE_WARNING) {
2107#ifdef NBGL_QRCODE
2108 // display a QR Code
2109 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &details->qrCode);
2110#endif // NBGL_QRCODE
2111 headerDesc.backAndText.text = details->title;
2112 }
2113 else if (details->type == CENTERED_INFO_WARNING) {
2114 // display a centered info
2115 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &details->centeredInfo);
2116 headerDesc.separationLine = false;
2117 }
2118 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2119 nbgl_refresh();
2120}
2121
2122// function used to display the initial warning page when starting a "review with warning"
2123static void displayInitialWarning(void)
2124{
2125 // Play notification sound
2126#ifdef HAVE_PIEZO_SOUND
2127 tune_index_e tune = TUNE_RESERVED;
2128#endif // HAVE_PIEZO_SOUND
2129 nbgl_layoutDescription_t layoutDescription;
2130 nbgl_layoutChoiceButtons_t buttonsInfo = {.bottomText = "Continue anyway",
2131 .token = WARNING_CHOICE_TOKEN,
2132 .topText = "Back to safety",
2133 .style = ROUNDED_AND_FOOTER_STYLE,
2134 .tuneId = TUNE_TAP_CASUAL};
2135 nbgl_layoutHeader_t headerDesc = {.type = HEADER_EMPTY,
2136 .separationLine = false,
2137 .emptySpace.height = MEDIUM_CENTERING_HEADER};
2138 uint32_t set = reviewWithWarnCtx.warning->predefinedSet
2139 & ~((1 << W3C_NO_THREAT_WARN) | (1 << W3C_ISSUE_WARN));
2140
2141 reviewWithWarnCtx.isIntro = true;
2142
2143 layoutDescription.modal = false;
2144 layoutDescription.withLeftBorder = true;
2145
2146 layoutDescription.onActionCallback = layoutTouchCallback;
2147 layoutDescription.tapActionText = NULL;
2148
2149 layoutDescription.ticker.tickerCallback = NULL;
2150 reviewWithWarnCtx.layoutCtx = nbgl_layoutGet(&layoutDescription);
2151
2152 nbgl_layoutAddHeader(reviewWithWarnCtx.layoutCtx, &headerDesc);
2153 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2155 reviewWithWarnCtx.layoutCtx,
2156 (set == (1 << BLIND_SIGNING_WARN)) ? &INFO_I_ICON : &QRCODE_ICON,
2157 WARNING_BUTTON_TOKEN,
2158 TUNE_TAP_CASUAL);
2159 }
2160 else if (reviewWithWarnCtx.warning->introTopRightIcon != NULL) {
2161 nbgl_layoutAddTopRightButton(reviewWithWarnCtx.layoutCtx,
2162 reviewWithWarnCtx.warning->introTopRightIcon,
2163 WARNING_BUTTON_TOKEN,
2164 TUNE_TAP_CASUAL);
2165 }
2166 // add main content
2167 // if predefined content is configured, use it preferably
2168 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2169 nbgl_contentCenter_t info = {0};
2170
2171 const char *provider;
2172
2173 // default icon
2174 info.icon = &LARGE_WARNING_ICON;
2175
2176 // use small title only if not Blind signing only
2177 if (set != (1 << BLIND_SIGNING_WARN)) {
2178 if (reviewWithWarnCtx.warning->reportProvider) {
2179 provider = reviewWithWarnCtx.warning->reportProvider;
2180 }
2181 else {
2182 provider = "[unknown]";
2183 }
2184 info.smallTitle = tmpString;
2185 snprintf(tmpString, W3C_DESCRIPTION_MAX_LEN, "Detected by %s", provider);
2186 }
2187
2188 if (set == (1 << BLIND_SIGNING_WARN)) {
2189 info.title = "Blind signing ahead";
2190 info.description
2191 = "This transaction's details are not fully verifiable. If you sign, you could "
2192 "lose all your assets.";
2193 buttonsInfo.bottomText = "Continue anyway";
2194#ifdef HAVE_PIEZO_SOUND
2195 tune = TUNE_NEUTRAL;
2196#endif // HAVE_PIEZO_SOUND
2197 }
2198 else if (set & (1 << W3C_RISK_DETECTED_WARN)) {
2199 info.title = "Potential risk";
2200 if (reviewWithWarnCtx.warning->providerMessage == NULL) {
2201 info.description = "Unidentified risk";
2202 }
2203 else {
2204 info.description = reviewWithWarnCtx.warning->providerMessage;
2205 }
2206 buttonsInfo.bottomText = "Accept risk and continue";
2207#ifdef HAVE_PIEZO_SOUND
2208 tune = TUNE_NEUTRAL;
2209#endif // HAVE_PIEZO_SOUND
2210 }
2211 else if (set & (1 << W3C_THREAT_DETECTED_WARN)) {
2212 info.title = "Critical threat";
2213 if (reviewWithWarnCtx.warning->providerMessage == NULL) {
2214 info.description = "Unidentified threat";
2215 }
2216 else {
2217 info.description = reviewWithWarnCtx.warning->providerMessage;
2218 }
2219 buttonsInfo.bottomText = "Accept threat and continue";
2220#ifdef HAVE_PIEZO_SOUND
2221 tune = TUNE_ERROR;
2222#endif // HAVE_PIEZO_SOUND
2223 }
2224 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, &info);
2225 }
2226 else if (reviewWithWarnCtx.warning->info != NULL) {
2227 // if no predefined content, use custom one
2228 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, reviewWithWarnCtx.warning->info);
2229#ifdef HAVE_PIEZO_SOUND
2230 tune = TUNE_LOOK_AT_ME;
2231#endif // HAVE_PIEZO_SOUND
2232 }
2233 // add button and footer on bottom
2234 nbgl_layoutAddChoiceButtons(reviewWithWarnCtx.layoutCtx, &buttonsInfo);
2235
2236#ifdef HAVE_PIEZO_SOUND
2237 if (tune != TUNE_RESERVED) {
2238 io_seproxyhal_play_tune(tune);
2239 }
2240#endif // HAVE_PIEZO_SOUND
2241 nbgl_layoutDraw(reviewWithWarnCtx.layoutCtx);
2242 nbgl_refresh();
2243}
2244
2245// function to factorize code for reviews tipbox
2246static void initWarningTipBox(const nbgl_tipBox_t *tipBox)
2247{
2248 const char *predefinedTipBoxText = NULL;
2249
2250 // if warning is valid and a warning requires a tip-box
2251 if (reviewWithWarnCtx.warning) {
2252 if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_ISSUE_WARN)) {
2253 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2254 predefinedTipBoxText = "Transaction Check unavailable.\nBlind signing required.";
2255 }
2256 else {
2257 predefinedTipBoxText = "Transaction Check unavailable";
2258 }
2259 }
2260 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN)) {
2261 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2262 predefinedTipBoxText = "Critical threat detected.\nBlind signing required.";
2263 }
2264 else {
2265 predefinedTipBoxText = "Critical threat detected.";
2266 }
2267 }
2268 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN)) {
2269 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2270 predefinedTipBoxText = "Potential risk detected.\nBlind signing required.";
2271 }
2272 else {
2273 predefinedTipBoxText = "Potential risk detected.";
2274 }
2275 }
2276 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_NO_THREAT_WARN)) {
2277 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2278 predefinedTipBoxText
2279 = "No threat detected by Transaction Check but blind signing required.";
2280 }
2281 else {
2282 predefinedTipBoxText = "No threat detected by Transaction Check.";
2283 }
2284 }
2285 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2286 predefinedTipBoxText = "Blind signing required.";
2287 }
2288 }
2289
2290 if ((tipBox != NULL) || (predefinedTipBoxText != NULL)) {
2291 // do not display "Swipe to review" if a tip-box is displayed
2292 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL;
2293 if (predefinedTipBoxText != NULL) {
2294 genericContext.validWarningCtx = true;
2295 STARTING_CONTENT.content.extendedCenter.tipBox.icon = NULL;
2296 STARTING_CONTENT.content.extendedCenter.tipBox.text = predefinedTipBoxText;
2297 }
2298 else {
2299 STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
2300 STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
2301 }
2302 STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
2303 STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
2304 }
2305}
2306
2307// function to factorize code for all simple reviews
2308static void useCaseReview(nbgl_operationType_t operationType,
2309 const nbgl_contentTagValueList_t *tagValueList,
2310 const nbgl_icon_details_t *icon,
2311 const char *reviewTitle,
2312 const char *reviewSubTitle,
2313 const char *finishTitle,
2314 const nbgl_tipBox_t *tipBox,
2315 nbgl_choiceCallback_t choiceCallback,
2316 bool isLight,
2317 bool playNotifSound)
2318{
2319 reset_callbacks();
2320 memset(&genericContext, 0, sizeof(genericContext));
2321
2322 bundleNavContext.review.operationType = operationType;
2323 bundleNavContext.review.choiceCallback = choiceCallback;
2324
2325 // memorize context
2326 onChoice = bundleNavReviewChoice;
2327 navType = GENERIC_NAV;
2328 pageTitle = NULL;
2329
2330 genericContext.genericContents.contentsList = localContentsList;
2331 genericContext.genericContents.nbContents = 3;
2332 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
2333
2334 // First a centered info
2335 STARTING_CONTENT.type = EXTENDED_CENTER;
2336 prepareReviewFirstPage(
2337 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2338
2339 // Prepare un tipbox if needed
2340 initWarningTipBox(tipBox);
2341
2342 // Then the tag/value pairs
2343 localContentsList[1].type = TAG_VALUE_LIST;
2344 memcpy(&localContentsList[1].content.tagValueList,
2345 tagValueList,
2347 localContentsList[1].contentActionCallback = tagValueList->actionCallback;
2348
2349 // The last page
2350 if (isLight) {
2351 localContentsList[2].type = INFO_BUTTON;
2352 prepareReviewLightLastPage(
2353 operationType, &localContentsList[2].content.infoButton, icon, finishTitle);
2354 }
2355 else {
2356 localContentsList[2].type = INFO_LONG_PRESS;
2357 prepareReviewLastPage(
2358 operationType, &localContentsList[2].content.infoLongPress, icon, finishTitle);
2359 }
2360
2361 // compute number of pages & fill navigation structure
2362 uint8_t nbPages = getNbPagesForGenericContents(
2363 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2364 prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
2365
2366 // Play notification sound if required
2367 if (playNotifSound) {
2368#ifdef HAVE_PIEZO_SOUND
2369 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2370#endif // HAVE_PIEZO_SOUND
2371 }
2372
2373 displayGenericContextPage(0, true);
2374}
2375
2376// function to factorize code for all streaming reviews
2377static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
2378 const nbgl_icon_details_t *icon,
2379 const char *reviewTitle,
2380 const char *reviewSubTitle,
2381 nbgl_choiceCallback_t choiceCallback,
2382 bool playNotifSound)
2383{
2384 reset_callbacks();
2385 memset(&genericContext, 0, sizeof(genericContext));
2386
2387 bundleNavContext.reviewStreaming.operationType = operationType;
2388 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
2389 bundleNavContext.reviewStreaming.icon = icon;
2390
2391 // memorize context
2392 onChoice = bundleNavReviewStreamingChoice;
2393 navType = STREAMING_NAV;
2394 pageTitle = NULL;
2395
2396 genericContext.genericContents.contentsList = localContentsList;
2397 genericContext.genericContents.nbContents = 1;
2398 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
2399
2400 // First a centered info
2401 STARTING_CONTENT.type = EXTENDED_CENTER;
2402 prepareReviewFirstPage(
2403 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2404
2405 // Prepare un tipbox if needed
2406 initWarningTipBox(NULL);
2407
2408 // compute number of pages & fill navigation structure
2409 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
2410 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2411 prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
2412 // no back button on first page
2413 navInfo.navWithButtons.backButton = false;
2414
2415 // Play notification sound if required
2416 if (playNotifSound) {
2417#ifdef HAVE_PIEZO_SOUND
2418 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2419#endif // HAVE_PIEZO_SOUND
2420 }
2421
2422 displayGenericContextPage(0, true);
2423}
2424
2441static void useCaseHomeExt(const char *appName,
2442 const nbgl_icon_details_t *appIcon,
2443 const char *tagline,
2444 bool withSettings,
2445 nbgl_homeAction_t *homeAction,
2446 nbgl_callback_t topRightCallback,
2447 nbgl_callback_t quitCallback)
2448{
2449 reset_callbacks();
2450
2451 nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
2452 .centeredInfo.text1 = appName,
2453 .centeredInfo.text3 = NULL,
2454 .centeredInfo.style = LARGE_CASE_INFO,
2455 .centeredInfo.offsetY = 0,
2456 .footerText = NULL,
2457 .bottomButtonStyle = QUIT_APP_TEXT,
2458 .tapActionText = NULL,
2459 .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
2460 .topRightToken = CONTINUE_TOKEN,
2461 .tuneId = TUNE_TAP_CASUAL};
2462 if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
2463 // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
2464 info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
2465 onAction = homeAction->callback;
2466 info.actionButtonText = homeAction->text;
2467 info.actionButtonIcon = homeAction->icon;
2470 }
2471 else {
2472 info.bottomButtonsToken = QUIT_TOKEN;
2473 onAction = NULL;
2474 info.actionButtonText = NULL;
2475 info.actionButtonIcon = NULL;
2476 }
2477 if (tagline == NULL) {
2478 if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
2479 snprintf(tmpString,
2481 "This app enables signing\ntransactions on its network.");
2482 }
2483 else {
2484 snprintf(tmpString,
2486 "%s %s\n%s",
2488 appName,
2490 }
2491
2492 // If there is more than 3 lines, it means the appName was split, so we put it on the next
2493 // line
2494 if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, tmpString, AVAILABLE_WIDTH, false) > 3) {
2495 snprintf(tmpString,
2497 "%s\n%s %s",
2499 appName,
2501 }
2502 info.centeredInfo.text2 = tmpString;
2503 }
2504 else {
2505 info.centeredInfo.text2 = tagline;
2506 }
2507
2508 onContinue = topRightCallback;
2509 onQuit = quitCallback;
2510
2511 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2513}
2514
2523static void displayDetails(const char *tag, const char *value, bool wrapping)
2524{
2525 memset(&detailsContext, 0, sizeof(detailsContext));
2526
2527 uint16_t nbLines
2528 = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
2529
2530 // initialize context
2531 detailsContext.tag = tag;
2532 detailsContext.value = value;
2533 detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
2534 detailsContext.currentPage = 0;
2535 detailsContext.wrapping = wrapping;
2536 // add some spare for room lost with "..." substitution
2537 if (detailsContext.nbPages > 1) {
2538 uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
2539 uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
2540 / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
2541 uint8_t nbLinesInLastPage
2542 = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
2543
2544 detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
2545 if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
2547 detailsContext.nbPages++;
2548 }
2549 }
2550
2551 displayDetailsPage(0, true);
2552}
2553
2554/**********************
2555 * GLOBAL FUNCTIONS
2556 **********************/
2557
2571 const nbgl_contentTagValueList_t *tagValueList,
2572 uint8_t startIndex,
2573 bool *requireSpecificDisplay)
2574{
2576 nbPairs, tagValueList, startIndex, false, requireSpecificDisplay);
2577}
2578
2593 const nbgl_contentTagValueList_t *tagValueList,
2594 uint8_t startIndex,
2595 bool isSkippable,
2596 bool *requireSpecificDisplay)
2597{
2598 uint8_t nbPairsInPage = 0;
2599 uint16_t currentHeight = PRE_TAG_VALUE_MARGIN; // upper margin
2600 uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
2601
2602 // if the review is skippable, it means that there is less height for tag/value pairs
2603 // the small centering header becomes a touchable header
2604 if (isSkippable) {
2605 maxUsableHeight -= TOUCHABLE_HEADER_BAR_HEIGHT - SMALL_CENTERING_HEADER;
2606 }
2607
2608 *requireSpecificDisplay = false;
2609 while (nbPairsInPage < nbPairs) {
2610 const nbgl_layoutTagValue_t *pair;
2611 nbgl_font_id_e value_font;
2612 uint16_t nbLines;
2613
2614 // margin between pairs
2615 // 12 or 24 px between each tag/value pair
2616 if (nbPairsInPage > 0) {
2617 currentHeight += INTER_TAG_VALUE_MARGIN;
2618 }
2619 // fetch tag/value pair strings.
2620 if (tagValueList->pairs != NULL) {
2621 pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2622 }
2623 else {
2624 pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2625 }
2626
2627 if (pair->forcePageStart && nbPairsInPage > 0) {
2628 // This pair must be at the top of a page
2629 break;
2630 }
2631
2632 if (pair->centeredInfo) {
2633 if (nbPairsInPage > 0) {
2634 // This pair must be at the top of a page
2635 break;
2636 }
2637 else {
2638 // This pair is the only one of the page and has a specific display behavior
2639 nbPairsInPage = 1;
2640 *requireSpecificDisplay = true;
2641 break;
2642 }
2643 }
2644
2645 // tag height
2646 currentHeight += nbgl_getTextHeightInWidth(
2647 SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2648 // space between tag and value
2649 currentHeight += 4;
2650 // set value font
2651 if (tagValueList->smallCaseForValue) {
2652 value_font = SMALL_REGULAR_FONT;
2653 }
2654 else {
2655 value_font = LARGE_MEDIUM_FONT;
2656 }
2657 // value height
2658 currentHeight += nbgl_getTextHeightInWidth(
2659 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2660 // nb lines for value
2662 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2663 if ((currentHeight >= maxUsableHeight) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2664 if (nbPairsInPage == 0) {
2665 // Pair too long to fit in a single screen
2666 // It will be the only one of the page and has a specific display behavior
2667 nbPairsInPage = 1;
2668 *requireSpecificDisplay = true;
2669 }
2670 break;
2671 }
2672 nbPairsInPage++;
2673 }
2674 return nbPairsInPage;
2675}
2676
2687 const nbgl_contentInfoList_t *infosList,
2688 uint8_t startIndex,
2689 bool withNav)
2690{
2691 uint8_t nbInfosInPage = 0;
2692 uint16_t currentHeight = 0;
2693 uint16_t previousHeight;
2694 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2695 const char *const *infoTypes = PIC(infosList->infoTypes);
2696 const char *const *infoContents = PIC(infosList->infoContents);
2697
2698 while (nbInfosInPage < nbInfos) {
2699 // margin between infos
2700 currentHeight += PRE_TEXT_MARGIN;
2701
2702 // type height
2703 currentHeight += nbgl_getTextHeightInWidth(
2704 SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2705 // space between type and content
2706 currentHeight += TEXT_SUBTEXT_MARGIN;
2707
2708 // content height
2709 currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2710 PIC(infoContents[startIndex + nbInfosInPage]),
2712 true);
2713 currentHeight += POST_SUBTEXT_MARGIN; // under the content
2714 // if height is over the limit
2715 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2716 // if there was no nav, now there will be, so it can be necessary to remove the last
2717 // item
2718 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2719 nbInfosInPage--;
2720 }
2721 break;
2722 }
2723 previousHeight = currentHeight;
2724 nbInfosInPage++;
2725 }
2726 return nbInfosInPage;
2727}
2728
2739 const nbgl_contentSwitchesList_t *switchesList,
2740 uint8_t startIndex,
2741 bool withNav)
2742{
2743 uint8_t nbSwitchesInPage = 0;
2744 uint16_t currentHeight = 0;
2745 uint16_t previousHeight;
2746 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2747 nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2748
2749 while (nbSwitchesInPage < nbSwitches) {
2750 // margin between switches
2751 currentHeight += PRE_TEXT_MARGIN;
2752
2753 // text height
2754 currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2755 switchArray[startIndex + nbSwitchesInPage].text,
2757 true);
2758 // space between text and sub-text
2759 currentHeight += TEXT_SUBTEXT_MARGIN;
2760
2761 // sub-text height
2762 currentHeight
2763 += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2764 switchArray[startIndex + nbSwitchesInPage].subText,
2766 true);
2767 currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2768 // if height is over the limit
2769 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2770 break;
2771 }
2772 previousHeight = currentHeight;
2773 nbSwitchesInPage++;
2774 }
2775 // if there was no nav, now there will be, so it can be necessary to remove the last
2776 // item
2777 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2778 nbSwitchesInPage--;
2779 }
2780 return nbSwitchesInPage;
2781}
2782
2793 const nbgl_contentBarsList_t *barsList,
2794 uint8_t startIndex,
2795 bool withNav)
2796{
2797 uint8_t nbBarsInPage = 0;
2798 uint16_t currentHeight = 0;
2799 uint16_t previousHeight;
2800 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2801
2802 UNUSED(barsList);
2803 UNUSED(startIndex);
2804
2805 while (nbBarsInPage < nbBars) {
2806 currentHeight += TOUCHABLE_BAR_HEIGHT;
2807 // if height is over the limit
2808 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2809 break;
2810 }
2811 previousHeight = currentHeight;
2812 nbBarsInPage++;
2813 }
2814 // if there was no nav, now there may will be, so it can be necessary to remove the last
2815 // item
2816 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2817 nbBarsInPage--;
2818 }
2819 return nbBarsInPage;
2820}
2821
2832 const nbgl_contentRadioChoice_t *choicesList,
2833 uint8_t startIndex,
2834 bool withNav)
2835{
2836 uint8_t nbChoicesInPage = 0;
2837 uint16_t currentHeight = 0;
2838 uint16_t previousHeight;
2839 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2840
2841 UNUSED(choicesList);
2842 UNUSED(startIndex);
2843
2844 while (nbChoicesInPage < nbChoices) {
2845 currentHeight += TOUCHABLE_BAR_HEIGHT;
2846 // if height is over the limit
2847 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2848 // if there was no nav, now there will be, so it can be necessary to remove the last
2849 // item
2850 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2851 nbChoicesInPage--;
2852 }
2853 break;
2854 }
2855 previousHeight = currentHeight;
2856 nbChoicesInPage++;
2857 }
2858 return nbChoicesInPage;
2859}
2860
2868{
2869 uint8_t nbPages = 0;
2870 uint8_t nbPairs = tagValueList->nbPairs;
2871 uint8_t nbPairsInPage;
2872 uint8_t i = 0;
2873 bool flag;
2874
2875 while (i < tagValueList->nbPairs) {
2876 // upper margin
2877 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2878 i += nbPairsInPage;
2879 nbPairs -= nbPairsInPage;
2880 nbPages++;
2881 }
2882 return nbPages;
2883}
2884
2889void nbgl_useCaseHome(const char *appName,
2890 const nbgl_icon_details_t *appIcon,
2891 const char *tagline,
2892 bool withSettings,
2893 nbgl_callback_t topRightCallback,
2894 nbgl_callback_t quitCallback)
2895{
2896 nbgl_homeAction_t homeAction = {0};
2897 useCaseHomeExt(
2898 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2899}
2900
2905void nbgl_useCaseHomeExt(const char *appName,
2906 const nbgl_icon_details_t *appIcon,
2907 const char *tagline,
2908 bool withSettings,
2909 const char *actionButtonText,
2910 nbgl_callback_t actionCallback,
2911 nbgl_callback_t topRightCallback,
2912 nbgl_callback_t quitCallback)
2913{
2914 nbgl_homeAction_t homeAction = {.callback = actionCallback,
2915 .icon = NULL,
2916 .style = STRONG_HOME_ACTION,
2917 .text = actionButtonText};
2918
2919 useCaseHomeExt(
2920 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2921}
2922
2936void nbgl_useCaseNavigableContent(const char *title,
2937 uint8_t initPage,
2938 uint8_t nbPages,
2939 nbgl_callback_t quitCallback,
2940 nbgl_navCallback_t navCallback,
2941 nbgl_layoutTouchCallback_t controlsCallback)
2942{
2943 reset_callbacks();
2944
2945 // memorize context
2946 onQuit = quitCallback;
2947 onNav = navCallback;
2948 onControls = controlsCallback;
2949 pageTitle = title;
2950 navType = SETTINGS_NAV;
2951
2952 // fill navigation structure
2953 prepareNavInfo(false, nbPages, NULL);
2954
2955 displaySettingsPage(initPage, true);
2956}
2957
2963void nbgl_useCaseSettings(const char *title,
2964 uint8_t initPage,
2965 uint8_t nbPages,
2966 bool touchable,
2967 nbgl_callback_t quitCallback,
2968 nbgl_navCallback_t navCallback,
2969 nbgl_layoutTouchCallback_t controlsCallback)
2970{
2971 UNUSED(touchable);
2973 title, initPage, nbPages, quitCallback, navCallback, controlsCallback);
2974}
2975
2988void nbgl_useCaseGenericSettings(const char *appName,
2989 uint8_t initPage,
2990 const nbgl_genericContents_t *settingContents,
2991 const nbgl_contentInfoList_t *infosList,
2992 nbgl_callback_t quitCallback)
2993{
2994 reset_callbacks();
2995 memset(&genericContext, 0, sizeof(genericContext));
2996
2997 // memorize context
2998 onQuit = quitCallback;
2999 pageTitle = appName;
3000 navType = GENERIC_NAV;
3001
3002 if (settingContents != NULL) {
3003 memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
3004 }
3005 if (infosList != NULL) {
3006 genericContext.hasFinishingContent = true;
3007 memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
3008 FINISHING_CONTENT.type = INFOS_LIST;
3009 memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
3010 }
3011
3012 // fill navigation structure
3013 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3014 if (infosList != NULL) {
3015 nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true, false);
3016 }
3017
3018 prepareNavInfo(false, nbPages, NULL);
3019
3020 displayGenericContextPage(initPage, true);
3021}
3022
3034void nbgl_useCaseGenericConfiguration(const char *title,
3035 uint8_t initPage,
3036 const nbgl_genericContents_t *contents,
3037 nbgl_callback_t quitCallback)
3038{
3039 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
3040}
3041
3059 const char *appName,
3060 const nbgl_icon_details_t *appIcon,
3061 const char *tagline,
3062 const uint8_t
3063 initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
3064 const nbgl_genericContents_t *settingContents,
3065 const nbgl_contentInfoList_t *infosList,
3066 const nbgl_homeAction_t *action, // Set to NULL if no additional action
3067 nbgl_callback_t quitCallback)
3068{
3069 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
3070
3071 context->appName = appName;
3072 context->appIcon = appIcon;
3073 context->tagline = tagline;
3074 context->settingContents = settingContents;
3075 context->infosList = infosList;
3076 if (action != NULL) {
3077 memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
3078 }
3079 else {
3080 memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
3081 }
3082 context->quitCallback = quitCallback;
3083
3084 if (initSettingPage != INIT_HOME_PAGE) {
3085 bundleNavStartSettingsAtPage(initSettingPage);
3086 }
3087 else {
3088 bundleNavStartHome();
3089 }
3090}
3091
3099void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
3100{
3101 reset_callbacks();
3102
3103 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = &tickerCallback,
3104 .tickerIntervale = 0, // not periodic
3105 .tickerValue = STATUS_SCREEN_DURATION};
3106
3107 onQuit = quitCallback;
3108 if (isSuccess) {
3109#ifdef HAVE_PIEZO_SOUND
3110 io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
3111#endif // HAVE_PIEZO_SOUND
3112
3113 pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
3114 }
3115 else {
3117 .footerText = NULL,
3118 .centeredInfo.icon = &DENIED_CIRCLE_ICON,
3119 .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
3120 .centeredInfo.onTop = false,
3121 .centeredInfo.style = LARGE_CASE_INFO,
3122 .centeredInfo.text1 = message,
3123 .centeredInfo.text2 = NULL,
3124 .centeredInfo.text3 = NULL,
3125 .tapActionText = "",
3126 .isSwipeable = false,
3127 .tapActionToken = QUIT_TOKEN,
3128 .topRightStyle = NO_BUTTON_STYLE,
3129 .actionButtonText = NULL,
3130 .tuneId = TUNE_TAP_CASUAL};
3131 pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
3132 }
3134}
3135
3143 nbgl_callback_t quitCallback)
3144{
3145 const char *msg;
3146 bool isSuccess;
3147 switch (reviewStatusType) {
3149 msg = "Operation signed";
3150 isSuccess = true;
3151 break;
3153 msg = "Operation rejected";
3154 isSuccess = false;
3155 break;
3157 msg = "Transaction signed";
3158 isSuccess = true;
3159 break;
3161 msg = "Transaction rejected";
3162 isSuccess = false;
3163 break;
3165 msg = "Message signed";
3166 isSuccess = true;
3167 break;
3169 msg = "Message rejected";
3170 isSuccess = false;
3171 break;
3173 msg = "Address verified";
3174 isSuccess = true;
3175 break;
3177 msg = "Address verification\ncancelled";
3178 isSuccess = false;
3179 break;
3180 default:
3181 return;
3182 }
3183 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
3184}
3185
3199 const char *message,
3200 const char *subMessage,
3201 const char *confirmText,
3202 const char *cancelText,
3203 nbgl_choiceCallback_t callback)
3204{
3205 reset_callbacks();
3206
3207 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3208 .centeredInfo.text1 = message,
3209 .centeredInfo.text2 = subMessage,
3210 .centeredInfo.text3 = NULL,
3211 .centeredInfo.style = LARGE_CASE_INFO,
3212 .centeredInfo.icon = icon,
3213 .centeredInfo.offsetY = 0,
3214 .confirmationText = confirmText,
3215 .confirmationToken = CHOICE_TOKEN,
3216 .tuneId = TUNE_TAP_CASUAL,
3217 .modal = false};
3218 // check params
3219 if ((confirmText == NULL) || (cancelText == NULL)) {
3220 return;
3221 }
3222 onChoice = callback;
3223 pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
3225}
3226
3240void nbgl_useCaseConfirm(const char *message,
3241 const char *subMessage,
3242 const char *confirmText,
3243 const char *cancelText,
3244 nbgl_callback_t callback)
3245{
3246 // Don't reset callback or nav context as this is just a modal.
3247
3248 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3249 .centeredInfo.text1 = message,
3250 .centeredInfo.text2 = subMessage,
3251 .centeredInfo.text3 = NULL,
3252 .centeredInfo.style = LARGE_CASE_INFO,
3253 .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON,
3254 .centeredInfo.offsetY = 0,
3255 .confirmationText = confirmText,
3256 .confirmationToken = CHOICE_TOKEN,
3257 .tuneId = TUNE_TAP_CASUAL,
3258 .modal = true};
3259 onModalConfirm = callback;
3260 if (modalPageContext != NULL) {
3261 nbgl_pageRelease(modalPageContext);
3262 }
3263 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
3265}
3266
3278 const char *message,
3279 const char *actionText,
3280 nbgl_callback_t callback)
3281{
3282 nbgl_pageContent_t content = {0};
3283
3284 // memorize callback
3285 onAction = callback;
3286
3287 content.tuneId = TUNE_TAP_CASUAL;
3288 content.type = INFO_BUTTON;
3289 content.infoButton.buttonText = actionText;
3290 content.infoButton.text = message;
3291 content.infoButton.icon = icon;
3292 content.infoButton.buttonToken = ACTION_BUTTON_TOKEN;
3293
3294 pageContext = nbgl_pageDrawGenericContent(&pageCallback, NULL, &content);
3296}
3297
3310 const char *reviewTitle,
3311 const char *reviewSubTitle,
3312 const char *rejectText,
3313 nbgl_callback_t continueCallback,
3314 nbgl_callback_t rejectCallback)
3315{
3316 reset_callbacks();
3317
3318 nbgl_pageInfoDescription_t info = {.footerText = rejectText,
3319 .footerToken = QUIT_TOKEN,
3320 .tapActionText = NULL,
3321 .isSwipeable = true,
3322 .tapActionToken = CONTINUE_TOKEN,
3323 .topRightStyle = NO_BUTTON_STYLE,
3324 .actionButtonText = NULL,
3325 .tuneId = TUNE_TAP_CASUAL};
3326 info.centeredInfo.icon = icon;
3327 info.centeredInfo.text1 = reviewTitle;
3328 info.centeredInfo.text2 = reviewSubTitle;
3329 info.centeredInfo.text3 = "Swipe to review";
3331 info.centeredInfo.offsetY = 0;
3332 onQuit = rejectCallback;
3333 onContinue = continueCallback;
3334
3335#ifdef HAVE_PIEZO_SOUND
3336 // Play notification sound
3337 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3338#endif // HAVE_PIEZO_SOUND
3339
3340 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
3341 nbgl_refresh();
3342}
3343
3349 uint8_t nbPages,
3350 const char *rejectText,
3351 nbgl_layoutTouchCallback_t buttonCallback,
3352 nbgl_navCallback_t navCallback,
3353 nbgl_choiceCallback_t choiceCallback)
3354{
3355 reset_callbacks();
3356
3357 // memorize context
3358 onChoice = choiceCallback;
3359 onNav = navCallback;
3360 onControls = buttonCallback;
3361 forwardNavOnly = false;
3362 navType = REVIEW_NAV;
3363
3364 // fill navigation structure
3365 UNUSED(rejectText);
3366 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3367
3368 displayReviewPage(initPage, true);
3369}
3370
3384 const nbgl_pageInfoLongPress_t *infoLongPress,
3385 const char *rejectText,
3386 nbgl_choiceCallback_t callback)
3387{
3388 uint8_t offset = 0;
3389
3390 reset_callbacks();
3391 memset(&genericContext, 0, sizeof(genericContext));
3392
3393 // memorize context
3394 onChoice = callback;
3395 navType = GENERIC_NAV;
3396 pageTitle = NULL;
3397 bundleNavContext.review.operationType = TYPE_OPERATION;
3398
3399 genericContext.genericContents.contentsList = localContentsList;
3400 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3401
3402 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3403 localContentsList[offset].type = TAG_VALUE_LIST;
3404 memcpy(&localContentsList[offset].content.tagValueList,
3405 tagValueList,
3407 offset++;
3408 }
3409
3410 localContentsList[offset].type = INFO_LONG_PRESS;
3411 memcpy(&localContentsList[offset].content.infoLongPress,
3412 infoLongPress,
3413 sizeof(nbgl_pageInfoLongPress_t));
3414 localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
3415 offset++;
3416
3417 genericContext.genericContents.nbContents = offset;
3418
3419 // compute number of pages & fill navigation structure
3420 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3421 UNUSED(rejectText);
3422 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3423
3424 displayGenericContextPage(0, true);
3425}
3426
3441 const nbgl_pageInfoLongPress_t *infoLongPress,
3442 const char *rejectText,
3443 nbgl_choiceCallback_t callback)
3444{
3445 uint8_t offset = 0;
3446
3447 reset_callbacks();
3448 memset(&genericContext, 0, sizeof(genericContext));
3449
3450 // memorize context
3451 onChoice = callback;
3452 navType = GENERIC_NAV;
3453 pageTitle = NULL;
3454
3455 genericContext.genericContents.contentsList = localContentsList;
3456 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3457
3458 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3459 localContentsList[offset].type = TAG_VALUE_LIST;
3460 memcpy(&localContentsList[offset].content.tagValueList,
3461 tagValueList,
3463 offset++;
3464 }
3465
3466 localContentsList[offset].type = INFO_BUTTON;
3467 localContentsList[offset].content.infoButton.text = infoLongPress->text;
3468 localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
3469 localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
3470 localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
3471 localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
3472 offset++;
3473
3474 genericContext.genericContents.nbContents = offset;
3475
3476 // compute number of pages & fill navigation structure
3477 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3478 UNUSED(rejectText);
3479 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3480
3481 displayGenericContextPage(0, true);
3482}
3483
3500void nbgl_useCaseReview(nbgl_operationType_t operationType,
3501 const nbgl_contentTagValueList_t *tagValueList,
3502 const nbgl_icon_details_t *icon,
3503 const char *reviewTitle,
3504 const char *reviewSubTitle,
3505 const char *finishTitle,
3506 nbgl_choiceCallback_t choiceCallback)
3507{
3508 useCaseReview(operationType,
3509 tagValueList,
3510 icon,
3511 reviewTitle,
3512 reviewSubTitle,
3513 finishTitle,
3514 NULL,
3515 choiceCallback,
3516 false,
3517 true);
3518}
3519
3540 const nbgl_contentTagValueList_t *tagValueList,
3541 const nbgl_icon_details_t *icon,
3542 const char *reviewTitle,
3543 const char *reviewSubTitle,
3544 const char *finishTitle,
3545 const nbgl_tipBox_t *tipBox,
3546 nbgl_choiceCallback_t choiceCallback)
3547{
3548 nbgl_useCaseAdvancedReview(operationType,
3549 tagValueList,
3550 icon,
3551 reviewTitle,
3552 reviewSubTitle,
3553 finishTitle,
3554 tipBox,
3555 &blindSigningWarning,
3556 choiceCallback);
3557}
3558
3582 const nbgl_contentTagValueList_t *tagValueList,
3583 const nbgl_icon_details_t *icon,
3584 const char *reviewTitle,
3585 const char *reviewSubTitle,
3586 const char *finishTitle,
3587 const nbgl_tipBox_t *tipBox,
3588 const nbgl_warning_t *warning,
3589 nbgl_choiceCallback_t choiceCallback)
3590{
3591 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3592 // memorize tipBox because it can be in the call stack of the caller
3593 if (tipBox != NULL) {
3594 memcpy(&activeTipBox, tipBox, sizeof(activeTipBox));
3595 }
3596 else {
3597 memset(&activeTipBox, 0, sizeof(activeTipBox));
3598 }
3599 // if no warning at all, it's a simple review
3600 if ((warning == NULL)
3601 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
3602 && (warning->reviewDetails == NULL))) {
3603 useCaseReview(operationType,
3604 tagValueList,
3605 icon,
3606 reviewTitle,
3607 reviewSubTitle,
3608 finishTitle,
3609 tipBox,
3610 choiceCallback,
3611 false,
3612 true);
3613 return;
3614 }
3615 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
3616 operationType |= NO_THREAT_OPERATION;
3617 }
3618 else {
3619 operationType |= RISKY_OPERATION;
3620 }
3621
3622 reviewWithWarnCtx.isStreaming = false;
3623 reviewWithWarnCtx.operationType = operationType;
3624 reviewWithWarnCtx.tagValueList = tagValueList;
3625 reviewWithWarnCtx.icon = icon;
3626 reviewWithWarnCtx.reviewTitle = reviewTitle;
3627 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3628 reviewWithWarnCtx.finishTitle = finishTitle;
3629 reviewWithWarnCtx.warning = warning;
3630 reviewWithWarnCtx.choiceCallback = choiceCallback;
3631
3632 // display the initial warning only of a risk/threat or blind signing
3633 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
3634 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
3635 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
3636 useCaseReview(operationType,
3637 tagValueList,
3638 icon,
3639 reviewTitle,
3640 reviewSubTitle,
3641 finishTitle,
3642 tipBox,
3643 choiceCallback,
3644 false,
3645 true);
3646 return;
3647 }
3648
3649 displayInitialWarning();
3650}
3651
3669 const nbgl_contentTagValueList_t *tagValueList,
3670 const nbgl_icon_details_t *icon,
3671 const char *reviewTitle,
3672 const char *reviewSubTitle,
3673 const char *finishTitle,
3674 nbgl_choiceCallback_t choiceCallback)
3675{
3676 useCaseReview(operationType,
3677 tagValueList,
3678 icon,
3679 reviewTitle,
3680 reviewSubTitle,
3681 finishTitle,
3682 NULL,
3683 choiceCallback,
3684 true,
3685 true);
3686}
3687
3697 const char *rejectText,
3698 nbgl_callback_t rejectCallback)
3699{
3700 reset_callbacks();
3701 memset(&genericContext, 0, sizeof(genericContext));
3702
3703 // memorize context
3704 onQuit = rejectCallback;
3705 navType = GENERIC_NAV;
3706 pageTitle = NULL;
3707 bundleNavContext.review.operationType = TYPE_OPERATION;
3708
3709 memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3710
3711 // compute number of pages & fill navigation structure
3712 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3713 prepareNavInfo(true, nbPages, rejectText);
3714 navInfo.quitToken = QUIT_TOKEN;
3715
3716#ifdef HAVE_PIEZO_SOUND
3717 // Play notification sound
3718 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3719#endif // HAVE_PIEZO_SOUND
3720
3721 displayGenericContextPage(0, true);
3722}
3723
3737 const nbgl_icon_details_t *icon,
3738 const char *reviewTitle,
3739 const char *reviewSubTitle,
3740 nbgl_choiceCallback_t choiceCallback)
3741{
3742 useCaseReviewStreamingStart(
3743 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3744}
3745
3760 const nbgl_icon_details_t *icon,
3761 const char *reviewTitle,
3762 const char *reviewSubTitle,
3763 nbgl_choiceCallback_t choiceCallback)
3764{
3766 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
3767}
3768
3785 const nbgl_icon_details_t *icon,
3786 const char *reviewTitle,
3787 const char *reviewSubTitle,
3788 const nbgl_warning_t *warning,
3789 nbgl_choiceCallback_t choiceCallback)
3790{
3791 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3792 // if no warning at all, it's a simple review
3793 if ((warning == NULL)
3794 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
3795 && (warning->reviewDetails == NULL))) {
3796 useCaseReviewStreamingStart(
3797 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3798 return;
3799 }
3800 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
3801 operationType |= NO_THREAT_OPERATION;
3802 }
3803 else {
3804 operationType |= RISKY_OPERATION;
3805 }
3806
3807 reviewWithWarnCtx.isStreaming = true;
3808 reviewWithWarnCtx.operationType = operationType;
3809 reviewWithWarnCtx.icon = icon;
3810 reviewWithWarnCtx.reviewTitle = reviewTitle;
3811 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3812 reviewWithWarnCtx.choiceCallback = choiceCallback;
3813 reviewWithWarnCtx.warning = warning;
3814
3815 // display the initial warning only of a risk/threat or blind signing
3816 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
3817 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
3818 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
3819 useCaseReviewStreamingStart(
3820 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3821 return;
3822 }
3823 displayInitialWarning();
3824}
3825
3840 nbgl_choiceCallback_t choiceCallback,
3841 nbgl_callback_t skipCallback)
3842{
3843 // Should follow a call to nbgl_useCaseReviewStreamingStart
3844 memset(&genericContext, 0, sizeof(genericContext));
3845
3846 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3847 bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3848
3849 // memorize context
3850 onChoice = bundleNavReviewStreamingChoice;
3851 navType = STREAMING_NAV;
3852 pageTitle = NULL;
3853
3854 genericContext.genericContents.contentsList = localContentsList;
3855 genericContext.genericContents.nbContents = 1;
3856 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3857
3858 // Then the tag/value pairs
3859 STARTING_CONTENT.type = TAG_VALUE_LIST;
3860 memcpy(
3861 &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3862
3863 // compute number of pages & fill navigation structure
3864 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3865 &genericContext.genericContents,
3866 0,
3867 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3868 prepareNavInfo(true,
3870 getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3871 // if the operation is skippable
3872 if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3873 navInfo.progressIndicator = false;
3874 navInfo.skipText = "Skip";
3875 navInfo.skipToken = SKIP_TOKEN;
3876 }
3877
3878 displayGenericContextPage(0, true);
3879}
3880
3892 nbgl_choiceCallback_t choiceCallback)
3893{
3894 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3895}
3896
3905void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3906 nbgl_choiceCallback_t choiceCallback)
3907{
3908 // Should follow a call to nbgl_useCaseReviewStreamingContinue
3909 memset(&genericContext, 0, sizeof(genericContext));
3910
3911 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3912
3913 // memorize context
3914 onChoice = bundleNavReviewStreamingChoice;
3915 navType = STREAMING_NAV;
3916 pageTitle = NULL;
3917
3918 genericContext.genericContents.contentsList = localContentsList;
3919 genericContext.genericContents.nbContents = 1;
3920 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3921
3922 // Eventually the long press page
3923 STARTING_CONTENT.type = INFO_LONG_PRESS;
3924 prepareReviewLastPage(bundleNavContext.reviewStreaming.operationType,
3925 &STARTING_CONTENT.content.infoLongPress,
3926 bundleNavContext.reviewStreaming.icon,
3927 finishTitle);
3928
3929 // compute number of pages & fill navigation structure
3930 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3931 &genericContext.genericContents,
3932 0,
3933 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3934 prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3935
3936 displayGenericContextPage(0, true);
3937}
3938
3943void nbgl_useCaseAddressConfirmationExt(const char *address,
3944 nbgl_choiceCallback_t callback,
3945 const nbgl_contentTagValueList_t *tagValueList)
3946{
3947 reset_callbacks();
3948 memset(&genericContext, 0, sizeof(genericContext));
3949 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3950
3951 // save context
3952 onChoice = callback;
3953 navType = GENERIC_NAV;
3954 pageTitle = NULL;
3955
3956 genericContext.genericContents.contentsList = localContentsList;
3957 genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3958 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3959 prepareAddressConfirmationPages(
3960 address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3961
3962 // fill navigation structure, common to all pages
3963 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3964
3965 prepareNavInfo(true, nbPages, "Cancel");
3966
3967#ifdef HAVE_PIEZO_SOUND
3968 // Play notification sound
3969 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3970#endif // HAVE_PIEZO_SOUND
3971
3972 displayGenericContextPage(0, true);
3973}
3974
3991void nbgl_useCaseAddressReview(const char *address,
3992 const nbgl_contentTagValueList_t *additionalTagValueList,
3993 const nbgl_icon_details_t *icon,
3994 const char *reviewTitle,
3995 const char *reviewSubTitle,
3996 nbgl_choiceCallback_t choiceCallback)
3997{
3998 reset_callbacks();
3999 memset(&genericContext, 0, sizeof(genericContext));
4000 // release a potential modal
4001 if (addressConfirmationContext.modalLayout) {
4002 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
4003 }
4004 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
4005
4006 // save context
4007 onChoice = choiceCallback;
4008 navType = GENERIC_NAV;
4009 pageTitle = NULL;
4010 bundleNavContext.review.operationType = TYPE_OPERATION;
4011
4012 genericContext.genericContents.contentsList = localContentsList;
4013 genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
4014 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
4015
4016 // First a centered info
4017 STARTING_CONTENT.type = EXTENDED_CENTER;
4018 prepareReviewFirstPage(
4019 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
4020 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
4021
4022 // Then the address confirmation pages
4023 prepareAddressConfirmationPages(
4024 address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
4025
4026 // fill navigation structure, common to all pages
4027 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
4028
4029 prepareNavInfo(true, nbPages, "Cancel");
4030
4031#ifdef HAVE_PIEZO_SOUND
4032 // Play notification sound
4033 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
4034#endif // HAVE_PIEZO_SOUND
4035
4036 displayGenericContextPage(0, true);
4037}
4038
4047void nbgl_useCaseSpinner(const char *text)
4048{
4049 // if the previous Use Case was not Spinner, fresh start
4050 if (genericContext.type != USE_CASE_SPINNER) {
4051 memset(&genericContext, 0, sizeof(genericContext));
4052 genericContext.type = USE_CASE_SPINNER;
4053 nbgl_layoutDescription_t layoutDescription = {0};
4054
4055 layoutDescription.withLeftBorder = true;
4056
4057 genericContext.backgroundLayout = nbgl_layoutGet(&layoutDescription);
4058
4060 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
4061
4062 nbgl_layoutDraw(genericContext.backgroundLayout);
4064 }
4065 else {
4066 // otherwise increment spinner
4067 genericContext.spinnerPosition++;
4068 // there are only NB_SPINNER_POSITIONSpositions
4069 if (genericContext.spinnerPosition == NB_SPINNER_POSITIONS) {
4070 genericContext.spinnerPosition = 0;
4071 }
4072 int ret = nbgl_layoutUpdateSpinner(
4073 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
4074 if (ret == 1) {
4076 }
4077 else if (ret == 2) {
4079 }
4080 }
4081}
4082
4083#ifdef NBGL_KEYPAD
4103void nbgl_useCaseKeypadDigits(const char *title,
4104 uint8_t minDigits,
4105 uint8_t maxDigits,
4106 uint8_t backToken,
4107 bool shuffled,
4108 tune_index_e tuneId,
4109 nbgl_pinValidCallback_t validatePinCallback,
4110 nbgl_layoutTouchCallback_t actionCallback)
4111{
4112 keypadGenericUseCase(title,
4113 minDigits,
4114 maxDigits,
4115 backToken,
4116 shuffled,
4117 false,
4118 tuneId,
4119 validatePinCallback,
4120 actionCallback);
4121}
4141void nbgl_useCaseKeypadPIN(const char *title,
4142 uint8_t minDigits,
4143 uint8_t maxDigits,
4144 uint8_t backToken,
4145 bool shuffled,
4146 tune_index_e tuneId,
4147 nbgl_pinValidCallback_t validatePinCallback,
4148 nbgl_layoutTouchCallback_t actionCallback)
4149{
4150 keypadGenericUseCase(title,
4151 minDigits,
4152 maxDigits,
4153 backToken,
4154 shuffled,
4155 true,
4156 tuneId,
4157 validatePinCallback,
4158 actionCallback);
4159}
4160#endif // NBGL_KEYPAD
4161
4162#endif // HAVE_SE_TOUCH
4163#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
@ INFO_LIST_ALIAS
alias is list of infos
@ 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:94
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...
#define NBGL_INVALID_TOKEN
Definition nbgl_layout.h:33
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:185
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition nbgl_obj.c:1661
#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:1671
#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:1687
#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:336
#define MIN(x, y)
Definition nbgl_types.h:100
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:308
@ FULL_COLOR_CLEAN_REFRESH
to be used for lock screen display (cleaner but longer refresh)
Definition nbgl_types.h:311
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition nbgl_types.h:313
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition nbgl_types.h:310
@ FULL_COLOR_REFRESH
to be used for normal refresh
Definition nbgl_types.h:309
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)
#define NB_MAX_LINES_IN_REVIEW
maximum number of lines for value field in review pages
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_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)
void nbgl_useCaseAction(const nbgl_icon_details_t *icon, const char *message, const char *actionText, nbgl_callback_t callback)
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_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
#define NB_MAX_LINES_IN_DETAILS
maximum number of lines for value field in details pages
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: Threat detected (see reportRisk field)
@ W3C_ISSUE_WARN
Web3 Checks issue (not available)
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_NO_THREAT_WARN
Web3 Checks: No Threat detected.
@ W3C_RISK_DETECTED_WARN
Web3 Checks: Risk detected (see reportRisk field)
@ 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 STATUS_SCREEN_DURATION
#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_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)
#define REAL_TYPE_MASK
This is the mask to apply on nbgl_operationType_t to get the real type provided by app.
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)
void nbgl_useCaseAdvancedReviewStreamingStart(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_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, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
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.
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
uint8_t nbInfos
number of elements in infoTypes and infoContents array
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 and possible extensions.
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
const char * backText
used as title of the popping page, if not NULL, otherwise "item" is used
const struct nbgl_contentInfoList_s * infolist
if aliasType is INFO_LIST_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, if type is INFOS_LIST.
const char * text
text of the tip-box
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...
const nbgl_warningDetails_t * introDetails
const nbgl_warningDetails_t * reviewDetails
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