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,
365 bool validWarningCtx);
366static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
367 const nbgl_icon_details_t *icon,
368 const char *reviewTitle,
369 const char *reviewSubTitle,
370 nbgl_choiceCallback_t choiceCallback,
371 bool playNotifSound);
372static void useCaseHomeExt(const char *appName,
373 const nbgl_icon_details_t *appIcon,
374 const char *tagline,
375 bool withSettings,
376 nbgl_homeAction_t *homeAction,
377 nbgl_callback_t topRightCallback,
378 nbgl_callback_t quitCallback);
379static void displayDetails(const char *tag, const char *value, bool wrapping);
380
381static void reset_callbacks(void)
382{
383 onQuit = NULL;
384 onContinue = NULL;
385 onAction = NULL;
386 onNav = NULL;
387 onControls = NULL;
388 onContentAction = NULL;
389 onChoice = NULL;
390 onModalConfirm = NULL;
391#ifdef NBGL_KEYPAD
392 onValidatePin = NULL;
393#endif
394}
395
396// Helper to set genericContext page info
397static void genericContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements, bool flag)
398{
399 uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements) + SET_PAGE_FLAG(flag);
400
401 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
402 &= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
403 genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
404 |= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
405}
406
407// Helper to get genericContext page info
408static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool *flag)
409{
410 uint8_t pageData = genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
411 >> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
412 if (nbElements != NULL) {
413 *nbElements = GET_PAGE_NB_ELEMENTS(pageData);
414 }
415 if (flag != NULL) {
416 *flag = GET_PAGE_FLAG(pageData);
417 }
418}
419
420// Simple helper to get the number of elements inside a nbgl_content_t
421static uint8_t getContentNbElement(const nbgl_content_t *content)
422{
423 switch (content->type) {
424 case TAG_VALUE_LIST:
425 return content->content.tagValueList.nbPairs;
426 case SWITCHES_LIST:
427 return content->content.switchesList.nbSwitches;
428 case INFOS_LIST:
429 return content->content.infosList.nbInfos;
430 case CHOICES_LIST:
431 return content->content.choicesList.nbChoices;
432 case BARS_LIST:
433 return content->content.barsList.nbBars;
434 default:
435 return 1;
436 }
437}
438
439// Helper to retrieve the content inside a nbgl_genericContents_t using
440// either the contentsList or using the contentGetterCallback
441static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
442 int8_t contentIdx,
443 nbgl_content_t *content)
444{
445 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
446 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
447 return NULL;
448 }
449
450 if (genericContents->callbackCallNeeded) {
451 // Retrieve content through callback, but first memset the content.
452 memset(content, 0, sizeof(nbgl_content_t));
453 genericContents->contentGetterCallback(contentIdx, content);
454 return content;
455 }
456 else {
457 // Retrieve content through list
458 return PIC(&genericContents->contentsList[contentIdx]);
459 }
460}
461
462static void prepareNavInfo(bool isReview, uint8_t nbPages, const char *rejectText)
463{
464 memset(&navInfo, 0, sizeof(navInfo));
465
466 navInfo.nbPages = nbPages;
467 navInfo.tuneId = TUNE_TAP_CASUAL;
468 navInfo.progressIndicator = false;
469 navInfo.navType = NAV_WITH_BUTTONS;
470
471 if (isReview == false) {
472 navInfo.navWithButtons.navToken = NAV_TOKEN;
473 navInfo.navWithButtons.backButton = true;
474 }
475 else {
476 navInfo.quitToken = REJECT_TOKEN;
477 navInfo.navWithButtons.quitText = rejectText;
478 navInfo.navWithButtons.navToken = NAV_TOKEN;
480 = ((navType == STREAMING_NAV) && (nbPages < 2)) ? false : true;
481 navInfo.navWithButtons.visiblePageIndicator = (navType != STREAMING_NAV);
482 }
483}
484
485static void prepareReviewFirstPage(nbgl_contentCenter_t *contentCenter,
486 const nbgl_icon_details_t *icon,
487 const char *reviewTitle,
488 const char *reviewSubTitle)
489{
490 contentCenter->icon = icon;
491 contentCenter->title = reviewTitle;
492 contentCenter->description = reviewSubTitle;
493 contentCenter->subText = "Swipe to review";
494 contentCenter->smallTitle = NULL;
495 contentCenter->iconHug = 0;
496 contentCenter->padding = false;
497 contentCenter->illustrType = ICON_ILLUSTRATION;
498}
499
500static void prepareReviewLastPage(nbgl_contentInfoLongPress_t *infoLongPress,
501 const nbgl_icon_details_t *icon,
502 const char *finishTitle)
503{
504 infoLongPress->text = finishTitle;
505 infoLongPress->icon = icon;
506 infoLongPress->longPressText = "Hold to sign";
507 infoLongPress->longPressToken = CONFIRM_TOKEN;
508}
509
510static void prepareReviewLightLastPage(nbgl_contentInfoButton_t *infoButton,
511 const nbgl_icon_details_t *icon,
512 const char *finishTitle)
513{
514 infoButton->text = finishTitle;
515 infoButton->icon = icon;
516 infoButton->buttonText = "Approve";
517 infoButton->buttonToken = CONFIRM_TOKEN;
518}
519
520static const char *getRejectReviewText(nbgl_operationType_t operationType)
521{
522 UNUSED(operationType);
523 return "Reject";
524}
525
526// function called when navigating (or exiting) modal details pages
527// or when skip choice is displayed
528static void pageModalCallback(int token, uint8_t index)
529{
530 LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index);
531
532 if (token == INFOS_TIP_BOX_TOKEN) {
533 // the icon next to info type alias has been touched
534 displayFullValuePage(activeTipBox.infos.infoTypes[index],
535 activeTipBox.infos.infoContents[index],
536 &activeTipBox.infos.infoExtensions[index]);
537 return;
538 }
539 else if (token == INFO_ALIAS_TOKEN) {
540 // the icon next to info type alias has been touched, but coming from review
541 displayFullValuePage(genericContext.currentInfos->infoTypes[index],
542 genericContext.currentInfos->infoContents[index],
543 &genericContext.currentInfos->infoExtensions[index]);
544 return;
545 }
546 nbgl_pageRelease(modalPageContext);
547 modalPageContext = NULL;
548 if (token == NAV_TOKEN) {
549 if (index == EXIT_PAGE) {
550 // redraw the background layer
552 nbgl_refresh();
553 }
554 else {
555 displayDetailsPage(index, false);
556 }
557 }
558 else if (token == QUIT_TOKEN) {
559 // redraw the background layer
561 nbgl_refresh();
562 }
563 else if (token == QUIT_TIPBOX_MODAL_TOKEN) {
564 // if in "warning context", go back to security report
565 if (reviewWithWarnCtx.securityReportLevel == 2) {
566 reviewWithWarnCtx.securityReportLevel = 1;
567 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
568 }
569 else {
570 // redraw the background layer
572 nbgl_refresh();
573 }
574 }
575 else if (token == SKIP_TOKEN) {
576 if (index == 0) {
577 // display the last forward only review page, whatever it is
578 displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true);
579 }
580 else {
581 // display background, which should be the page where skip has been touched
583 nbgl_refresh();
584 }
585 }
586 else if (token == CHOICE_TOKEN) {
587 if (index == 0) {
588 if (onModalConfirm != NULL) {
589 onModalConfirm();
590 }
591 }
592 else {
593 // display background, which should be the page where skip has been touched
595 nbgl_refresh();
596 }
597 }
598}
599
600// generic callback for all pages except modal
601static void pageCallback(int token, uint8_t index)
602{
603 LOG_DEBUG(USE_CASE_LOGGER, "pageCallback, token = %d, index = %d\n", token, index);
604 if (token == QUIT_TOKEN) {
605 if (onQuit != NULL) {
606 onQuit();
607 }
608 }
609 else if (token == CONTINUE_TOKEN) {
610 if (onContinue != NULL) {
611 onContinue();
612 }
613 }
614 else if (token == CHOICE_TOKEN) {
615 if (onChoice != NULL) {
616 onChoice((index == 0) ? true : false);
617 }
618 }
619 else if (token == ACTION_BUTTON_TOKEN) {
620 if ((index == 0) && (onAction != NULL)) {
621 onAction();
622 }
623 else if ((index == 1) && (onQuit != NULL)) {
624 onQuit();
625 }
626 }
627#ifdef NBGL_QRCODE
628 else if (token == ADDRESS_QRCODE_BUTTON_TOKEN) {
629 displayAddressQRCode();
630 }
631#endif // NBGL_QRCODE
632 else if (token == CONFIRM_TOKEN) {
633 if (onChoice != NULL) {
634 onChoice(true);
635 }
636 }
637 else if (token == REJECT_TOKEN) {
638 if (onChoice != NULL) {
639 onChoice(false);
640 }
641 }
642 else if (token == DETAILS_BUTTON_TOKEN) {
643 displayDetails(genericContext.detailsItem,
644 genericContext.detailsvalue,
645 genericContext.detailsWrapping);
646 }
647 else if (token == NAV_TOKEN) {
648 if (index == EXIT_PAGE) {
649 if (onQuit != NULL) {
650 onQuit();
651 }
652 }
653 else {
654 if (navType == GENERIC_NAV || navType == STREAMING_NAV) {
655 displayGenericContextPage(index, false);
656 }
657 else if (navType == REVIEW_NAV) {
658 displayReviewPage(index, false);
659 }
660 else {
661 displaySettingsPage(index, false);
662 }
663 }
664 }
665 else if (token == NEXT_TOKEN) {
666 if (onNav != NULL) {
667 displayReviewPage(navInfo.activePage + 1, false);
668 }
669 else {
670 displayGenericContextPage(navInfo.activePage + 1, false);
671 }
672 }
673 else if (token == BACK_TOKEN) {
674 if (onNav != NULL) {
675 displayReviewPage(navInfo.activePage - 1, true);
676 }
677 else {
678 displayGenericContextPage(navInfo.activePage - 1, false);
679 }
680 }
681 else if (token == SKIP_TOKEN) {
682 // display a modal warning to confirm skip
683 displaySkipWarning();
684 }
685 else if (token == VALUE_ALIAS_TOKEN) {
686 // the icon next to value alias has been touched
687 const nbgl_contentTagValue_t *pair;
688 if (genericContext.currentPairs != NULL) {
689 pair = &genericContext.currentPairs[genericContext.currentElementIdx + index];
690 }
691 else {
692 pair = genericContext.currentCallback(genericContext.currentElementIdx + index);
693 }
694 displayFullValuePage(pair->item, pair->value, pair->extension);
695 }
696 else if (token == BLIND_WARNING_TOKEN) {
697 reviewWithWarnCtx.isIntro = false;
698 reviewWithWarnCtx.warning = NULL;
699 displaySecurityReport(1 << BLIND_SIGNING_WARN);
700 }
701 else if (token == WARNING_BUTTON_TOKEN) {
702 // top-right button, display the security modal
703 reviewWithWarnCtx.securityReportLevel = 1;
704 // if preset is used
705 if (reviewWithWarnCtx.warning->predefinedSet) {
706 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
707 }
708 else {
709 // use customized warning
710 if (reviewWithWarnCtx.isIntro) {
711 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->introDetails);
712 }
713 else {
714 displayCustomizedSecurityReport(reviewWithWarnCtx.warning->reviewDetails);
715 }
716 }
717 }
718 else if (token == TIP_BOX_TOKEN) {
719 // if warning context is valid and if W3C directly display same as top-right
720 if (genericContext.validWarningCtx && (reviewWithWarnCtx.warning->predefinedSet != 0)) {
721 reviewWithWarnCtx.securityReportLevel = 1;
722 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
723 }
724 else {
725 displayInfosListModal(activeTipBox.modalTitle, &activeTipBox.infos, false);
726 }
727 }
728 else { // probably a control provided by caller
729 if (onContentAction != NULL) {
730 onContentAction(token, index, navInfo.activePage);
731 }
732 if (onControls != NULL) {
733 onControls(token, index);
734 }
735 }
736}
737
738// callback used for confirmation
739static void tickerCallback(void)
740{
741 nbgl_pageRelease(pageContext);
742 if (onQuit != NULL) {
743 onQuit();
744 }
745}
746
747// function used to display the current page in review
748static void displaySettingsPage(uint8_t page, bool forceFullRefresh)
749{
750 nbgl_pageContent_t content = {0};
751
752 if ((onNav == NULL) || (onNav(page, &content) == false)) {
753 return;
754 }
755
756 // override some fields
757 content.title = pageTitle;
758 content.isTouchableTitle = true;
759 content.titleToken = QUIT_TOKEN;
760 content.tuneId = TUNE_TAP_CASUAL;
761
762 navInfo.activePage = page;
763 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
764
765 if (forceFullRefresh) {
767 }
768 else {
770 }
771}
772
773// function used to display the current page in review
774static void displayReviewPage(uint8_t page, bool forceFullRefresh)
775{
776 nbgl_pageContent_t content = {0};
777
778 // ensure the page is valid
779 if ((navInfo.nbPages != 0) && (page >= (navInfo.nbPages))) {
780 return;
781 }
782 navInfo.activePage = page;
783 if ((onNav == NULL) || (onNav(navInfo.activePage, &content) == false)) {
784 return;
785 }
786
787 // override some fields
788 content.title = NULL;
789 content.isTouchableTitle = false;
790 content.tuneId = TUNE_TAP_CASUAL;
791
792 if (content.type == INFO_LONG_PRESS) { // last page
793 // for forward only review without known length...
794 // if we don't do that we cannot remove the '>' in the navigation bar at the last page
795 navInfo.nbPages = navInfo.activePage + 1;
796 content.infoLongPress.longPressToken = CONFIRM_TOKEN;
797 if (forwardNavOnly) {
798 // remove the "Skip" button
799 navInfo.skipText = NULL;
800 }
801 }
802
803 // override smallCaseForValue for tag/value types to false
804 if (content.type == TAG_VALUE_DETAILS) {
806 // the maximum displayable number of lines for value is NB_MAX_LINES_IN_REVIEW (without More
807 // button)
808 content.tagValueDetails.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
809 }
810 else if (content.type == TAG_VALUE_LIST) {
811 content.tagValueList.smallCaseForValue = false;
812 }
813 else if (content.type == TAG_VALUE_CONFIRM) {
815 // use confirm token for black button
816 content.tagValueConfirm.confirmationToken = CONFIRM_TOKEN;
817 }
818
819 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
820
821 if (forceFullRefresh) {
823 }
824 else {
826 }
827}
828
829// Helper that does the computing of which nbgl_content_t and which element in the content should be
830// displayed for the next generic context navigation page
831static const nbgl_content_t *genericContextComputeNextPageParams(uint8_t pageIdx,
832 nbgl_content_t *content,
833 uint8_t *p_nbElementsInNextPage,
834 bool *p_flag)
835{
836 int8_t nextContentIdx = genericContext.currentContentIdx;
837 int16_t nextElementIdx = genericContext.currentElementIdx;
838 uint8_t nbElementsInNextPage;
839
840 // Retrieve info on the next page
841 genericContextGetPageInfo(pageIdx, &nbElementsInNextPage, p_flag);
842 *p_nbElementsInNextPage = nbElementsInNextPage;
843
844 // Handle forward navigation:
845 // add to current index the number of pairs of the currently active page
846 if (pageIdx > navInfo.activePage) {
847 uint8_t nbElementsInCurrentPage;
848
849 genericContextGetPageInfo(navInfo.activePage, &nbElementsInCurrentPage, NULL);
850 nextElementIdx += nbElementsInCurrentPage;
851
852 // Handle case where the content to be displayed is in the next content
853 // In such case start at element index 0.
854 // If currentContentElementNb == 0, means not initialized, so skip
855 if ((nextElementIdx >= genericContext.currentContentElementNb)
856 && (genericContext.currentContentElementNb > 0)) {
857 nextContentIdx += 1;
858 nextElementIdx = 0;
859 }
860 }
861
862 // Handle backward navigation:
863 // add to current index the number of pairs of the currently active page
864 if (pageIdx < navInfo.activePage) {
865 // Backward navigation: remove to current index the number of pairs of the current page
866 nextElementIdx -= nbElementsInNextPage;
867
868 // Handle case where the content to be displayed is in the previous content
869 // In such case set a negative number as element index so that it is handled
870 // later once the previous content is accessible so that its elements number
871 // can be retrieved.
872 if (nextElementIdx < 0) {
873 nextContentIdx -= 1;
874 nextElementIdx = -nbElementsInNextPage;
875 }
876 }
877
878 const nbgl_content_t *p_content;
879 // Retrieve next content
880 if ((nextContentIdx == -1) && (genericContext.hasStartingContent)) {
881 p_content = &STARTING_CONTENT;
882 }
883 else if ((nextContentIdx == genericContext.genericContents.nbContents)
884 && (genericContext.hasFinishingContent)) {
885 p_content = &FINISHING_CONTENT;
886 }
887 else {
888 p_content = getContentAtIdx(&genericContext.genericContents, nextContentIdx, content);
889
890 if (p_content == NULL) {
891 LOG_DEBUG(USE_CASE_LOGGER, "Fail to retrieve content\n");
892 return NULL;
893 }
894 }
895
896 // Handle cases where we are going to display data from a new content:
897 // - navigation to a previous or to the next content
898 // - First page display (genericContext.currentContentElementNb == 0)
899 //
900 // In such case we need to:
901 // - Update genericContext.currentContentIdx
902 // - Update genericContext.currentContentElementNb
903 // - Update onContentAction callback
904 // - Update nextElementIdx in case it was previously not calculable
905 if ((nextContentIdx != genericContext.currentContentIdx)
906 || (genericContext.currentContentElementNb == 0)) {
907 genericContext.currentContentIdx = nextContentIdx;
908 genericContext.currentContentElementNb = getContentNbElement(p_content);
909 onContentAction = PIC(p_content->contentActionCallback);
910 if (nextElementIdx < 0) {
911 nextElementIdx = genericContext.currentContentElementNb + nextElementIdx;
912 }
913 }
914
915 // Sanity check
916 if ((nextElementIdx < 0) || (nextElementIdx >= genericContext.currentContentElementNb)) {
918 "Invalid element index %d / %d\n",
919 nextElementIdx,
920 genericContext.currentContentElementNb);
921 return NULL;
922 }
923
924 // Update genericContext elements
925 genericContext.currentElementIdx = nextElementIdx;
926 navInfo.activePage = pageIdx;
927
928 return p_content;
929}
930
931// Helper that generates a nbgl_pageContent_t to be displayed for the next generic context
932// navigation page
933static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
934 uint8_t nbElementsInPage,
935 bool flag,
936 nbgl_pageContent_t *pageContent)
937{
938 uint8_t nextElementIdx = genericContext.currentElementIdx;
939
940 pageContent->title = pageTitle;
941 pageContent->isTouchableTitle = false;
942 pageContent->titleToken = QUIT_TOKEN;
943 pageContent->tuneId = TUNE_TAP_CASUAL;
944
945 pageContent->type = p_content->type;
946 switch (pageContent->type) {
947 case CENTERED_INFO:
948 memcpy(&pageContent->centeredInfo,
949 &p_content->content.centeredInfo,
950 sizeof(pageContent->centeredInfo));
951 break;
952 case EXTENDED_CENTER:
953 memcpy(&pageContent->extendedCenter,
954 &p_content->content.extendedCenter,
955 sizeof(pageContent->extendedCenter));
956 break;
957 case INFO_LONG_PRESS:
958 memcpy(&pageContent->infoLongPress,
959 &p_content->content.infoLongPress,
960 sizeof(pageContent->infoLongPress));
961 break;
962
963 case INFO_BUTTON:
964 memcpy(&pageContent->infoButton,
965 &p_content->content.infoButton,
966 sizeof(pageContent->infoButton));
967 break;
968
969 case TAG_VALUE_LIST: {
970 nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
971
972 // memorize pairs (or callback) for usage when alias is used
973 genericContext.currentPairs = p_content->content.tagValueList.pairs;
974 genericContext.currentCallback = p_content->content.tagValueList.callback;
975
976 if (flag) {
977 // Flag can be set if the pair is too long to fit or because it needs
978 // to be displayed as centered info.
979
980 // First retrieve the pair
981 const nbgl_layoutTagValue_t *pair;
982
983 if (p_content->content.tagValueList.pairs != NULL) {
984 pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
985 }
986 else {
987 pair = PIC(p_content->content.tagValueList.callback(nextElementIdx));
988 }
989
990 if (pair->centeredInfo) {
991 pageContent->type = EXTENDED_CENTER;
992 prepareReviewFirstPage(&pageContent->extendedCenter.contentCenter,
993 pair->valueIcon,
994 pair->item,
995 pair->value);
996
997 // Skip population of nbgl_contentTagValueList_t structure
998 p_tagValueList = NULL;
999 }
1000 else {
1001 // if the pair is too long to fit, we use a TAG_VALUE_DETAILS content
1002 pageContent->type = TAG_VALUE_DETAILS;
1003 pageContent->tagValueDetails.detailsButtonText = "More";
1004 pageContent->tagValueDetails.detailsButtonIcon = NULL;
1005 pageContent->tagValueDetails.detailsButtonToken = DETAILS_BUTTON_TOKEN;
1006
1007 p_tagValueList = &pageContent->tagValueDetails.tagValueList;
1008
1009 // Backup pair info for easy data access upon click on button
1010 genericContext.detailsItem = pair->item;
1011 genericContext.detailsvalue = pair->value;
1012 genericContext.detailsWrapping = p_content->content.tagValueList.wrapping;
1013 }
1014 }
1015
1016 if (p_tagValueList != NULL) {
1017 p_tagValueList->nbPairs = nbElementsInPage;
1018 p_tagValueList->token = p_content->content.tagValueList.token;
1019 if (p_content->content.tagValueList.pairs != NULL) {
1020 p_tagValueList->pairs
1021 = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
1022 // parse pairs to check if any contains an alias for value
1023 for (uint8_t i = 0; i < nbElementsInPage; i++) {
1024 if (p_tagValueList->pairs[i].aliasValue) {
1025 p_tagValueList->token = VALUE_ALIAS_TOKEN;
1026 break;
1027 }
1028 }
1029 }
1030 else {
1031 p_tagValueList->pairs = NULL;
1032 p_tagValueList->callback = p_content->content.tagValueList.callback;
1033 p_tagValueList->startIndex = nextElementIdx;
1034 // parse pairs to check if any contains an alias for value
1035 for (uint8_t i = 0; i < nbElementsInPage; i++) {
1036 const nbgl_layoutTagValue_t *pair
1037 = PIC(p_content->content.tagValueList.callback(nextElementIdx + i));
1038 if (pair->aliasValue) {
1039 p_tagValueList->token = VALUE_ALIAS_TOKEN;
1040 break;
1041 }
1042 }
1043 }
1044 p_tagValueList->smallCaseForValue = false;
1045 p_tagValueList->nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
1046 p_tagValueList->wrapping = p_content->content.tagValueList.wrapping;
1047 }
1048
1049 break;
1050 }
1051 case TAG_VALUE_CONFIRM:
1052 memcpy(&pageContent->tagValueConfirm,
1053 &p_content->content.tagValueConfirm,
1054 sizeof(pageContent->tagValueConfirm));
1055 break;
1056 case SWITCHES_LIST:
1057 pageContent->switchesList.nbSwitches = nbElementsInPage;
1058 pageContent->switchesList.switches
1059 = PIC(&p_content->content.switchesList.switches[nextElementIdx]);
1060 break;
1061 case INFOS_LIST:
1062 pageContent->infosList.nbInfos = nbElementsInPage;
1063 pageContent->infosList.infoTypes
1064 = PIC(&p_content->content.infosList.infoTypes[nextElementIdx]);
1065 pageContent->infosList.infoContents
1066 = PIC(&p_content->content.infosList.infoContents[nextElementIdx]);
1067 break;
1068 case CHOICES_LIST:
1069 memcpy(&pageContent->choicesList,
1070 &p_content->content.choicesList,
1071 sizeof(pageContent->choicesList));
1072 pageContent->choicesList.nbChoices = nbElementsInPage;
1073 pageContent->choicesList.names
1074 = PIC(&p_content->content.choicesList.names[nextElementIdx]);
1075 if ((p_content->content.choicesList.initChoice >= nextElementIdx)
1076 && (p_content->content.choicesList.initChoice
1077 < nextElementIdx + nbElementsInPage)) {
1078 pageContent->choicesList.initChoice
1079 = p_content->content.choicesList.initChoice - nextElementIdx;
1080 }
1081 else {
1082 pageContent->choicesList.initChoice = nbElementsInPage;
1083 }
1084 break;
1085 case BARS_LIST:
1086 pageContent->barsList.nbBars = nbElementsInPage;
1087 pageContent->barsList.barTexts
1088 = PIC(&p_content->content.barsList.barTexts[nextElementIdx]);
1089 pageContent->barsList.tokens = PIC(&p_content->content.barsList.tokens[nextElementIdx]);
1090 pageContent->barsList.tuneId = p_content->content.barsList.tuneId;
1091 break;
1092 default:
1093 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported type %d\n", pageContent->type);
1094 return false;
1095 }
1096
1097 bool isFirstOrLastPage
1098 = ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
1099 || (p_content->type == INFO_LONG_PRESS);
1100 nbgl_operationType_t operationType
1101 = (navType == STREAMING_NAV)
1102 ? bundleNavContext.reviewStreaming.operationType
1103 : ((navType == GENERIC_NAV) ? bundleNavContext.review.operationType : 0);
1104
1105 // if first or last page of review and blind/risky operation, add the warning top-right button
1106 if (isFirstOrLastPage
1107 && (operationType & (BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION))) {
1108 // if issue is only Web3Checks "no threat", use privacy icon
1109 if ((operationType & NO_THREAT_OPERATION)
1110 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
1111 pageContent->topRightIcon = &PRIVACY_ICON;
1112 }
1113 else {
1114 pageContent->topRightIcon = &WARNING_ICON;
1115 }
1116
1117 pageContent->topRightToken
1118 = (operationType & BLIND_OPERATION) ? BLIND_WARNING_TOKEN : WARNING_BUTTON_TOKEN;
1119 }
1120
1121 return true;
1122}
1123
1124// function used to display the current page in generic context navigation mode
1125static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh)
1126{
1127 // Retrieve next page parameters
1128 nbgl_content_t content;
1129 uint8_t nbElementsInPage;
1130 bool flag;
1131 const nbgl_content_t *p_content = NULL;
1132
1133 if (navType == STREAMING_NAV) {
1134 if (pageIdx == LAST_PAGE_FOR_REVIEW) {
1135 if (bundleNavContext.reviewStreaming.skipCallback != NULL) {
1136 bundleNavContext.reviewStreaming.skipCallback();
1137 }
1138 return;
1139 }
1140 else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) {
1141 bundleNavReviewStreamingChoice(true);
1142 return;
1143 }
1144 }
1145
1146 if (navInfo.activePage == pageIdx) {
1147 p_content
1148 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1149 }
1150 else if (navInfo.activePage < pageIdx) {
1151 // Support going more than one step forward.
1152 // It occurs when initializing a navigation on an arbitrary page
1153 for (int i = navInfo.activePage + 1; i <= pageIdx; i++) {
1154 p_content = genericContextComputeNextPageParams(i, &content, &nbElementsInPage, &flag);
1155 }
1156 }
1157 else {
1158 if (pageIdx - navInfo.activePage > 1) {
1159 // We don't support going more than one step backward as it doesn't occurs for now?
1160 LOG_DEBUG(USE_CASE_LOGGER, "Unsupported navigation\n");
1161 return;
1162 }
1163 p_content
1164 = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1165 }
1166
1167 if (p_content == NULL) {
1168 return;
1169 }
1170
1171 // Create next page content
1172 nbgl_pageContent_t pageContent = {0};
1173 if (!genericContextPreparePageContent(p_content, nbElementsInPage, flag, &pageContent)) {
1174 return;
1175 }
1176
1177 pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &pageContent);
1178
1179 if (forceFullRefresh) {
1181 }
1182 else {
1184 }
1185}
1186
1187// from the current details context, return a pointer on the details at the given page
1188static const char *getDetailsPageAt(uint8_t detailsPage)
1189{
1190 uint8_t page = 0;
1191 const char *currentChar = detailsContext.value;
1192 while (page < detailsPage) {
1193 uint16_t nbLines
1194 = nbgl_getTextNbLinesInWidth(SMALL_BOLD_FONT, currentChar, AVAILABLE_WIDTH, false);
1195 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1196 uint16_t len;
1197 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1198 currentChar,
1200 NB_MAX_LINES_IN_DETAILS,
1201 &len,
1202 detailsContext.wrapping);
1203 len -= 3;
1204 currentChar = currentChar + len;
1205 }
1206 page++;
1207 }
1208 return currentChar;
1209}
1210
1211// function used to display the current page in details review mode
1212static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh)
1213{
1214 static nbgl_layoutTagValue_t currentPair;
1215 nbgl_pageNavigationInfo_t info = {.activePage = detailsPage,
1216 .nbPages = detailsContext.nbPages,
1217 .navType = NAV_WITH_BUTTONS,
1218 .quitToken = QUIT_TOKEN,
1219 .navWithButtons.navToken = NAV_TOKEN,
1220 .navWithButtons.quitButton = true,
1221 .navWithButtons.backButton = true,
1222 .navWithButtons.quitText = NULL,
1223 .progressIndicator = false,
1224 .tuneId = TUNE_TAP_CASUAL};
1226 .topRightIcon = NULL,
1227 .tagValueList.nbPairs = 1,
1228 .tagValueList.pairs = &currentPair,
1229 .tagValueList.smallCaseForValue = true,
1230 .tagValueList.wrapping = detailsContext.wrapping};
1231
1232 if (modalPageContext != NULL) {
1233 nbgl_pageRelease(modalPageContext);
1234 }
1235 currentPair.item = detailsContext.tag;
1236 // if move backward or first page
1237 if (detailsPage <= detailsContext.currentPage) {
1238 // recompute current start from beginning
1239 currentPair.value = getDetailsPageAt(detailsPage);
1240 forceFullRefresh = true;
1241 }
1242 // else move forward
1243 else {
1244 currentPair.value = detailsContext.nextPageStart;
1245 }
1246 detailsContext.currentPage = detailsPage;
1248 SMALL_BOLD_FONT, currentPair.value, AVAILABLE_WIDTH, detailsContext.wrapping);
1249
1250 if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1251 uint16_t len;
1252 nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1253 currentPair.value,
1255 NB_MAX_LINES_IN_DETAILS,
1256 &len,
1257 detailsContext.wrapping);
1258 len -= 3;
1259 // memorize next position to save processing
1260 detailsContext.nextPageStart = currentPair.value + len;
1261 // use special feature to keep only NB_MAX_LINES_IN_DETAILS lines and replace the last 3
1262 // chars by "..."
1263 content.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_DETAILS;
1264 }
1265 else {
1266 detailsContext.nextPageStart = NULL;
1267 content.tagValueList.nbMaxLinesForValue = 0;
1268 }
1269 if (info.nbPages == 1) {
1270 // if only one page, no navigation bar, and use a footer instead
1271 info.navWithButtons.quitText = "Close";
1272 }
1273 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1274
1275 if (forceFullRefresh) {
1277 }
1278 else {
1280 }
1281}
1282
1283// function used to display the content of a full value, when touching an alias of a tag/value pair
1284static void displayFullValuePage(const char *backText,
1285 const char *aliasText,
1286 const nbgl_contentValueExt_t *extension)
1287{
1288 const char *modalTitle
1289 = (extension->backText != NULL) ? PIC(extension->backText) : PIC(backText);
1290 if (extension->aliasType == INFO_LIST_ALIAS) {
1291 genericContext.currentInfos = extension->infolist;
1292 displayInfosListModal(modalTitle, extension->infolist, true);
1293 }
1294 else {
1295 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1296 .withLeftBorder = true,
1297 .onActionCallback = &modalLayoutTouchCallback,
1298 .tapActionText = NULL};
1300 .separationLine = false,
1301 .backAndText.token = 0,
1302 .backAndText.tuneId = TUNE_TAP_CASUAL,
1303 .backAndText.text = modalTitle};
1304 genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1305 // add header with the tag part of the pair, to go back
1306 nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1307 // add either QR Code or full value text
1308 if (extension->aliasType == QR_CODE_ALIAS) {
1309#ifdef NBGL_QRCODE
1310 nbgl_layoutQRCode_t qrCode
1311 = {.url = extension->fullValue,
1312 .text1 = (extension->title != NULL) ? extension->title : extension->fullValue,
1313 .text2 = extension->explanation,
1314 .centered = true,
1315 .offsetY = 0};
1316
1317 nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode);
1318#endif // NBGL_QRCODE
1319 }
1320 else {
1321 const char *info;
1322 // add full value text
1323 if (extension->aliasType == ENS_ALIAS) {
1324 info = "ENS names are resolved by Ledger backend.";
1325 }
1326 else if (extension->aliasType == ADDRESS_BOOK_ALIAS) {
1327 info = "This account label comes from your Address Book in Ledger Live.";
1328 }
1329 else {
1330 info = extension->explanation;
1331 }
1333 genericContext.modalLayout, aliasText, extension->fullValue, info);
1334 }
1335 // draw & refresh
1336 nbgl_layoutDraw(genericContext.modalLayout);
1337 nbgl_refresh();
1338 }
1339}
1340
1341// function used to display the modal containing tip-box infos
1342static void displayInfosListModal(const char *modalTitle,
1343 const nbgl_contentInfoList_t *infos,
1344 bool fromReview)
1345{
1347 .nbPages = 1,
1348 .navType = NAV_WITH_BUTTONS,
1349 .quitToken = QUIT_TIPBOX_MODAL_TOKEN,
1350 .navWithButtons.navToken = NAV_TOKEN,
1351 .navWithButtons.quitButton = false,
1352 .navWithButtons.backButton = true,
1353 .navWithButtons.quitText = NULL,
1354 .progressIndicator = false,
1355 .tuneId = TUNE_TAP_CASUAL};
1356 nbgl_pageContent_t content
1357 = {.type = INFOS_LIST,
1358 .topRightIcon = NULL,
1359 .infosList.nbInfos = infos->nbInfos,
1360 .infosList.withExtensions = infos->withExtensions,
1361 .infosList.infoTypes = infos->infoTypes,
1362 .infosList.infoContents = infos->infoContents,
1363 .infosList.infoExtensions = infos->infoExtensions,
1364 .infosList.token = fromReview ? INFO_ALIAS_TOKEN : INFOS_TIP_BOX_TOKEN,
1365 .title = modalTitle,
1366 .titleToken = QUIT_TIPBOX_MODAL_TOKEN,
1367 .tuneId = TUNE_TAP_CASUAL};
1368
1369 if (modalPageContext != NULL) {
1370 nbgl_pageRelease(modalPageContext);
1371 }
1372 modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1373
1375}
1376
1377#ifdef NBGL_QRCODE
1378static void displayAddressQRCode(void)
1379{
1380 // display the address as QR Code
1381 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1382 .withLeftBorder = true,
1383 .onActionCallback = &modalLayoutTouchCallback,
1384 .tapActionText = NULL};
1385 nbgl_layoutHeader_t headerDesc = {
1386 .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1387 nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1388 .text1 = NULL,
1389 .centered = true,
1390 .offsetY = 0};
1391
1392 addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1393 // add empty header for better look
1394 nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1395 // compute nb lines to check whether it shall be shorten (max is 3 lines)
1397 SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1398
1399 if (nbLines <= QRCODE_NB_MAX_LINES) {
1400 qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1401 }
1402 else {
1403 // only keep beginning and end of text, and add ... in the middle
1404 nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1405 addressConfirmationContext.tagValuePair.value,
1407 QRCODE_NB_MAX_LINES,
1408 reducedAddress,
1409 QRCODE_REDUCED_ADDR_LEN);
1410 qrCode.text2 = reducedAddress; // in gray
1411 }
1412
1413 nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1414
1416 addressConfirmationContext.modalLayout, "Close", DISMISS_QR_TOKEN, TUNE_TAP_CASUAL);
1417 nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1418 nbgl_refresh();
1419}
1420
1421#endif // NBGL_QRCODE
1422
1423// called when header is touched on modal page, to dismiss it
1424static void modalLayoutTouchCallback(int token, uint8_t index)
1425{
1426 UNUSED(index);
1427 if (token == DISMISS_QR_TOKEN) {
1428 // dismiss modal
1429 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1430 addressConfirmationContext.modalLayout = NULL;
1432 }
1433 else if (token == DISMISS_WARNING_TOKEN) {
1434 // dismiss modal
1435 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1436 // if already at first level, simply redraw the background
1437 if (reviewWithWarnCtx.securityReportLevel <= 1) {
1438 reviewWithWarnCtx.modalLayout = NULL;
1440 }
1441 else {
1442 // We are at level 2 of warning modal, so re-display the level 1
1443 reviewWithWarnCtx.securityReportLevel = 1;
1444 // if preset is used
1445 if (reviewWithWarnCtx.warning->predefinedSet) {
1446 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1447 }
1448 else {
1449 // use customized warning
1450 const nbgl_warningDetails_t *details
1451 = (reviewWithWarnCtx.isIntro) ? reviewWithWarnCtx.warning->introDetails
1452 : reviewWithWarnCtx.warning->reviewDetails;
1453 displayCustomizedSecurityReport(details);
1454 }
1455 return;
1456 }
1457 }
1458 else if ((token >= FIRST_WARN_BAR_TOKEN) && (token <= LAST_WARN_BAR_TOKEN)) {
1459 // dismiss modal before creating a new one
1460 nbgl_layoutRelease(reviewWithWarnCtx.modalLayout);
1461 reviewWithWarnCtx.securityReportLevel = 2;
1462 // if preset is used
1463 if (reviewWithWarnCtx.warning->predefinedSet) {
1464 displaySecurityReport(1 << (token - FIRST_WARN_BAR_TOKEN));
1465 }
1466 else {
1467 // use customized warning
1468 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1469 ? reviewWithWarnCtx.warning->introDetails
1470 : reviewWithWarnCtx.warning->reviewDetails;
1471 if (details->type == BAR_LIST_WARNING) {
1472 displayCustomizedSecurityReport(
1473 &details->barList.details[token - FIRST_WARN_BAR_TOKEN]);
1474 }
1475 }
1476 return;
1477 }
1478 else {
1479 // dismiss modal
1480 nbgl_layoutRelease(genericContext.modalLayout);
1481 genericContext.modalLayout = NULL;
1483 }
1484 nbgl_refresh();
1485}
1486
1487// called when item are touched in layout
1488static void layoutTouchCallback(int token, uint8_t index)
1489{
1490 // choice buttons in initial warning page
1491 if (token == WARNING_CHOICE_TOKEN) {
1492 if (index == 0) { // top button to exit
1493 reviewWithWarnCtx.choiceCallback(false);
1494 }
1495 else { // bottom button to continue to review
1496 reviewWithWarnCtx.isIntro = false;
1497 if (reviewWithWarnCtx.isStreaming) {
1498 useCaseReviewStreamingStart(reviewWithWarnCtx.operationType,
1499 reviewWithWarnCtx.icon,
1500 reviewWithWarnCtx.reviewTitle,
1501 reviewWithWarnCtx.reviewSubTitle,
1502 reviewWithWarnCtx.choiceCallback,
1503 false);
1504 }
1505 else {
1506 useCaseReview(reviewWithWarnCtx.operationType,
1507 reviewWithWarnCtx.tagValueList,
1508 reviewWithWarnCtx.icon,
1509 reviewWithWarnCtx.reviewTitle,
1510 reviewWithWarnCtx.reviewSubTitle,
1511 reviewWithWarnCtx.finishTitle,
1512 &activeTipBox,
1513 reviewWithWarnCtx.choiceCallback,
1514 false,
1515 false,
1516 true);
1517 }
1518 }
1519 }
1520 // top-right button in initial warning page
1521 else if (token == WARNING_BUTTON_TOKEN) {
1522 // start directly at first level of security report
1523 reviewWithWarnCtx.securityReportLevel = 1;
1524 // if preset is used
1525 if (reviewWithWarnCtx.warning->predefinedSet) {
1526 displaySecurityReport(reviewWithWarnCtx.warning->predefinedSet);
1527 }
1528 else {
1529 // use customized warning
1530 const nbgl_warningDetails_t *details = (reviewWithWarnCtx.isIntro)
1531 ? reviewWithWarnCtx.warning->introDetails
1532 : reviewWithWarnCtx.warning->reviewDetails;
1533 displayCustomizedSecurityReport(details);
1534 }
1535 }
1536}
1537
1538// called when skip button is touched in footer, during forward only review
1539static void displaySkipWarning(void)
1540{
1542 .cancelText = "Go back to review",
1543 .centeredInfo.text1 = "Skip review?",
1544 .centeredInfo.text2
1545 = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1546 .centeredInfo.text3 = NULL,
1547 .centeredInfo.style = LARGE_CASE_INFO,
1548 .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON,
1549 .centeredInfo.offsetY = 0,
1550 .confirmationText = "Yes, skip",
1551 .confirmationToken = SKIP_TOKEN,
1552 .tuneId = TUNE_TAP_CASUAL,
1553 .modal = true};
1554 if (modalPageContext != NULL) {
1555 nbgl_pageRelease(modalPageContext);
1556 }
1557 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1559}
1560
1561#ifdef NBGL_KEYPAD
1562// called to update the keypad and automatically show / hide:
1563// - backspace key if no digits are present
1564// - validation key if the min digit is reached
1565// - keypad if the max number of digit is reached
1566static void updateKeyPad(bool add)
1567{
1568 bool enableValidate, enableBackspace, enableDigits;
1569 bool redrawKeypad = false;
1571
1572 enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1573 enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1574 enableBackspace = (keypadContext.pinLen > 0);
1575 if (add) {
1576 if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1577 || // activate "validate" button on keypad
1578 (keypadContext.pinLen == keypadContext.pinMaxDigits)
1579 || // deactivate "digits" on keypad
1580 (keypadContext.pinLen == 1)) { // activate "backspace"
1581 redrawKeypad = true;
1582 }
1583 }
1584 else { // remove
1585 if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1586 (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1587 || // deactivate "validate" button on keypad
1588 (keypadContext.pinLen
1589 == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1590 redrawKeypad = true;
1591 }
1592 }
1593 if (keypadContext.hidden == true) {
1594 nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1595 }
1596 else {
1598 keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1599 }
1600 if (redrawKeypad) {
1602 keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1603 }
1604
1605 if ((!add) && (keypadContext.pinLen == 0)) {
1606 // Full refresh to fully clean the bullets when reaching 0 digits
1607 mode = FULL_COLOR_REFRESH;
1608 }
1610}
1611
1612// called when a key is touched on the keypad
1613static void keypadCallback(char touchedKey)
1614{
1615 switch (touchedKey) {
1616 case BACKSPACE_KEY:
1617 if (keypadContext.pinLen > 0) {
1618 keypadContext.pinLen--;
1619 keypadContext.pinEntry[keypadContext.pinLen] = 0;
1620 }
1621 updateKeyPad(false);
1622 break;
1623
1624 case VALIDATE_KEY:
1625 // Gray out keyboard / buttons as a first user feedback
1626 nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1629
1630 onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1631 break;
1632
1633 default:
1634 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1635 if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1636 keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1637 keypadContext.pinLen++;
1638 }
1639 updateKeyPad(true);
1640 }
1641 break;
1642 }
1643}
1644
1645// called to create a keypad, with either hidden or visible digits
1646static void keypadGenericUseCase(const char *title,
1647 uint8_t minDigits,
1648 uint8_t maxDigits,
1649 uint8_t backToken,
1650 bool shuffled,
1651 bool hidden,
1652 tune_index_e tuneId,
1653 nbgl_pinValidCallback_t validatePinCallback,
1654 nbgl_layoutTouchCallback_t actionCallback)
1655{
1656 nbgl_layoutDescription_t layoutDescription = {0};
1658 .separationLine = true,
1659 .backAndText.token = backToken,
1660 .backAndText.tuneId = tuneId,
1661 .backAndText.text = NULL};
1662 int status = -1;
1663
1664 if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1665 return;
1666 }
1667
1668 reset_callbacks();
1669 // reset the keypad context
1670 memset(&keypadContext, 0, sizeof(KeypadContext_t));
1671
1672 // get a layout
1673 layoutDescription.onActionCallback = actionCallback;
1674 layoutDescription.modal = false;
1675 layoutDescription.withLeftBorder = false;
1676 keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1677 keypadContext.hidden = hidden;
1678
1679 // set back key in header
1680 nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1681
1682 // add keypad
1683 status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1684 if (status < 0) {
1685 return;
1686 }
1687 // add keypad content
1689 keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1690
1691 if (status < 0) {
1692 return;
1693 }
1694
1695 // validation pin callback
1696 onValidatePin = validatePinCallback;
1697 // pin code acceptable lengths
1698 keypadContext.pinMinDigits = minDigits;
1699 keypadContext.pinMaxDigits = maxDigits;
1700
1701 nbgl_layoutDraw(keypadContext.layoutCtx);
1703}
1704#endif
1705
1706static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1707 uint8_t pageIdxStart,
1708 bool isLast,
1709 bool isSkippable)
1710{
1711 uint8_t nbElements = 0;
1712 uint8_t nbPages = 0;
1713 uint8_t nbElementsInPage;
1714 uint8_t elemIdx = 0;
1715 bool flag;
1716
1717 nbElements = getContentNbElement(content);
1718
1719 while (nbElements > 0) {
1720 flag = 0;
1721 // if the current page is not the first one (or last), a navigation bar exists
1722 bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1723 if (content->type == TAG_VALUE_LIST) {
1724 nbElementsInPage = nbgl_useCaseGetNbTagValuesInPageExt(
1725 nbElements, &content->content.tagValueList, elemIdx, isSkippable, &flag);
1726 }
1727 else if (content->type == INFOS_LIST) {
1728 nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1729 nbElements, &content->content.infosList, elemIdx, hasNav);
1730 }
1731 else if (content->type == SWITCHES_LIST) {
1732 nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1733 nbElements, &content->content.switchesList, elemIdx, hasNav);
1734 }
1735 else if (content->type == BARS_LIST) {
1736 nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1737 nbElements, &content->content.barsList, elemIdx, hasNav);
1738 }
1739 else if (content->type == CHOICES_LIST) {
1740 nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1741 nbElements, &content->content.choicesList, elemIdx, hasNav);
1742 }
1743 else {
1744 nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1745 }
1746
1747 elemIdx += nbElementsInPage;
1748 genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1749 nbElements -= nbElementsInPage;
1750 nbPages++;
1751 }
1752
1753 return nbPages;
1754}
1755
1756static uint8_t getNbPagesForGenericContents(const nbgl_genericContents_t *genericContents,
1757 uint8_t pageIdxStart,
1758 bool isSkippable)
1759{
1760 uint8_t nbPages = 0;
1761 nbgl_content_t content;
1762 const nbgl_content_t *p_content;
1763
1764 for (int i = 0; i < genericContents->nbContents; i++) {
1765 p_content = getContentAtIdx(genericContents, i, &content);
1766 if (p_content == NULL) {
1767 return 0;
1768 }
1769 nbPages += nbgl_useCaseGetNbPagesForContent(p_content,
1770 pageIdxStart + nbPages,
1771 (i == (genericContents->nbContents - 1)),
1772 isSkippable);
1773 }
1774
1775 return nbPages;
1776}
1777
1778static void prepareAddressConfirmationPages(const char *address,
1779 const nbgl_contentTagValueList_t *tagValueList,
1780 nbgl_content_t *firstPageContent,
1781 nbgl_content_t *secondPageContent)
1782{
1783 nbgl_contentTagValueConfirm_t *tagValueConfirm;
1784
1785 addressConfirmationContext.tagValuePair.item = "Address";
1786 addressConfirmationContext.tagValuePair.value = address;
1787
1788 // First page
1789 firstPageContent->type = TAG_VALUE_CONFIRM;
1790 tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1791
1792#ifdef NBGL_QRCODE
1793 tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1794 // only use "Show as QR" when it's not the last page
1795 if (tagValueList != NULL) {
1796 tagValueConfirm->detailsButtonText = "Show as QR";
1797 }
1798 else {
1799 tagValueConfirm->detailsButtonText = NULL;
1800 }
1801 tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1802#else // NBGL_QRCODE
1803 tagValueConfirm->detailsButtonText = NULL;
1804 tagValueConfirm->detailsButtonIcon = NULL;
1805#endif // NBGL_QRCODE
1806 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1807 tagValueConfirm->tagValueList.nbPairs = 1;
1808 tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1809 tagValueConfirm->tagValueList.smallCaseForValue = false;
1810 tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1811 tagValueConfirm->tagValueList.wrapping = false;
1812 // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1813 // no confirmation button
1814 if (tagValueList != NULL) {
1815 tagValueConfirm->confirmationText = NULL;
1816 }
1817 else {
1818 // otherwise no tap to continue but a confirmation button
1819 tagValueConfirm->confirmationText = "Confirm";
1820 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1821 }
1822
1823 // Second page if any:
1824 if (tagValueList != NULL) {
1825 // the second page is dedicated to the extended tag/value pairs
1826 secondPageContent->type = TAG_VALUE_CONFIRM;
1827 tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1828 tagValueConfirm->confirmationText = "Confirm";
1829 tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1830 tagValueConfirm->detailsButtonText = NULL;
1831 tagValueConfirm->detailsButtonIcon = NULL;
1832 tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1833 memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1834 }
1835}
1836
1837static void bundleNavStartHome(void)
1838{
1839 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1840
1841 useCaseHomeExt(context->appName,
1842 context->appIcon,
1843 context->tagline,
1844 context->settingContents != NULL ? true : false,
1845 &context->homeAction,
1846 bundleNavStartSettings,
1847 context->quitCallback);
1848}
1849
1850static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1851{
1852 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1853
1854 nbgl_useCaseGenericSettings(context->appName,
1855 initSettingPage,
1856 context->settingContents,
1857 context->infosList,
1858 bundleNavStartHome);
1859}
1860
1861static void bundleNavStartSettings(void)
1862{
1863 bundleNavStartSettingsAtPage(0);
1864}
1865
1866static void bundleNavReviewConfirmRejection(void)
1867{
1868 bundleNavContext.review.choiceCallback(false);
1869}
1870
1871static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1872 nbgl_callback_t callback)
1873{
1874 const char *title;
1875 const char *confirmText;
1876 // clear skip and blind bits
1877 operationType
1878 &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION | RISKY_OPERATION | NO_THREAT_OPERATION);
1879 if (operationType == TYPE_TRANSACTION) {
1880 title = "Reject transaction?";
1881 confirmText = "Go back to transaction";
1882 }
1883 else if (operationType == TYPE_MESSAGE) {
1884 title = "Reject message?";
1885 confirmText = "Go back to message";
1886 }
1887 else {
1888 title = "Reject operation?";
1889 confirmText = "Go back to operation";
1890 }
1891
1892 // display a choice to confirm/cancel rejection
1893 nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1894}
1895
1896static void bundleNavReviewChoice(bool confirm)
1897{
1898 if (confirm) {
1899 bundleNavContext.review.choiceCallback(true);
1900 }
1901 else {
1902 bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1903 bundleNavReviewConfirmRejection);
1904 }
1905}
1906
1907static void bundleNavReviewStreamingConfirmRejection(void)
1908{
1909 bundleNavContext.reviewStreaming.choiceCallback(false);
1910}
1911
1912static void bundleNavReviewStreamingChoice(bool confirm)
1913{
1914 if (confirm) {
1915 // Display a spinner if it wasn't the finish step
1916 if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1917 nbgl_useCaseSpinner("Processing");
1918 }
1919 bundleNavContext.reviewStreaming.choiceCallback(true);
1920 }
1921 else {
1922 bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1923 bundleNavReviewStreamingConfirmRejection);
1924 }
1925}
1926
1927// function used to display the security level page in modal
1928// it can be either a single page with text or QR code, or a list of touchable bars leading
1929// to single pages
1930static void displaySecurityReport(uint32_t set)
1931{
1932 nbgl_layoutDescription_t layoutDescription = {.modal = true,
1933 .withLeftBorder = true,
1934 .onActionCallback = modalLayoutTouchCallback,
1935 .tapActionText = NULL};
1937 .separationLine = true,
1938 .backAndText.icon = NULL,
1939 .backAndText.tuneId = TUNE_TAP_CASUAL,
1940 .backAndText.token = DISMISS_WARNING_TOKEN};
1941 nbgl_layoutFooter_t footerDesc
1942 = {.type = FOOTER_EMPTY, .separationLine = false, .emptySpace.height = 0};
1943 uint8_t i;
1944 uint8_t nbWarnings = 0;
1945 const char *provider;
1946
1947 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
1948
1949 // count the number of warnings
1950 for (i = 0; i < NB_WARNING_TYPES; i++) {
1951 if ((set & (1 << i)) && (i != W3C_NO_THREAT_WARN)) {
1952 nbWarnings++;
1953 }
1954 }
1955
1956 // display a list of touchable bars in some conditions:
1957 // - we must be in the first level of security report
1958 // - and be in the review itself (not from the intro/warning screen)
1959 //
1960 if ((reviewWithWarnCtx.securityReportLevel == 1) && (!reviewWithWarnCtx.isIntro)) {
1961 for (i = 0; i < NB_WARNING_TYPES; i++) {
1962 if (reviewWithWarnCtx.warning->predefinedSet & (1 << i)) {
1963 nbgl_layoutBar_t bar = {0};
1964 if ((i == BLIND_SIGNING_WARN) || (i == W3C_NO_THREAT_WARN) || (i == W3C_ISSUE_WARN)
1965 || (reviewWithWarnCtx.warning->providerMessage == NULL)) {
1966 bar.subText = securityReportItems[i].subText;
1967 }
1968 else {
1969 bar.subText = reviewWithWarnCtx.warning->providerMessage;
1970 }
1971 bar.text = securityReportItems[i].text;
1972 if (i != W3C_NO_THREAT_WARN) {
1973 bar.iconRight = &PUSH_ICON;
1974 bar.token = FIRST_WARN_BAR_TOKEN + i;
1975 bar.tuneId = TUNE_TAP_CASUAL;
1976 }
1977 else {
1979 }
1980 bar.iconLeft = securityReportItems[i].icon;
1981 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
1982 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
1983 }
1984 }
1985 headerDesc.backAndText.text = "Security report";
1986 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
1987 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
1988 nbgl_refresh();
1989 return;
1990 }
1991 if (reviewWithWarnCtx.warning && reviewWithWarnCtx.warning->reportProvider) {
1992 provider = reviewWithWarnCtx.warning->reportProvider;
1993 }
1994 else {
1995 provider = "[unknown]";
1996 }
1997 if ((set & (1 << W3C_THREAT_DETECTED_WARN)) || (set & (1 << W3C_RISK_DETECTED_WARN))) {
1998 size_t urlLen = 0;
1999 char *destStr
2000 = tmpString
2001 + W3C_DESCRIPTION_MAX_LEN / 2; // use the second half of tmpString for strings
2002#ifdef NBGL_QRCODE
2003 // display a QR Code
2004 nbgl_layoutQRCode_t qrCode = {.url = destStr,
2005 .text1 = reviewWithWarnCtx.warning->reportUrl,
2006 .text2 = "Scan to view full report",
2007 .centered = true,
2008 .offsetY = 0};
2009 // add "https://"" as prefix of the given URL
2010 snprintf(destStr,
2011 W3C_DESCRIPTION_MAX_LEN / 2,
2012 "https://%s",
2013 reviewWithWarnCtx.warning->reportUrl);
2014 urlLen = strlen(destStr) + 1;
2015 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &qrCode);
2016 footerDesc.emptySpace.height = 24;
2017#endif // NBGL_QRCODE
2018 // use the next part of destStr for back bar text
2019 snprintf(destStr + urlLen, W3C_DESCRIPTION_MAX_LEN / 2 - urlLen, "%s report", provider);
2020 headerDesc.backAndText.text = destStr + urlLen;
2021 }
2022 else if (set & (1 << BLIND_SIGNING_WARN)) {
2023 // display a centered if in review
2024 nbgl_contentCenter_t info = {0};
2025 info.icon = &LARGE_WARNING_ICON;
2026 info.title = "This transaction cannot be Clear Signed";
2027 info.description
2028 = "This transaction or message cannot be decoded fully. If you choose to sign, you "
2029 "could be authorizing malicious actions that can drain your wallet.\n\nLearn more: "
2030 "ledger.com/e8";
2031 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2032 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2033 headerDesc.separationLine = false;
2034 }
2035 else if (set & (1 << W3C_ISSUE_WARN)) {
2036 // if W3 Checks issue, display a centered info
2037 nbgl_contentCenter_t info = {0};
2038 info.icon = &LARGE_WARNING_ICON;
2039 info.title = "Transaction Check unavailable";
2040 info.description
2041 = "If you're not using the\nLedger Live app, Transaction Check might not work. If your "
2042 "are using Ledger Live, reject the transaction and try again.\n\nGet help at "
2043 "ledger.com/e11";
2044 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &info);
2045 footerDesc.emptySpace.height = MEDIUM_CENTERING_HEADER;
2046 headerDesc.separationLine = false;
2047 }
2048 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2049 if (footerDesc.emptySpace.height > 0) {
2050 nbgl_layoutAddExtendedFooter(reviewWithWarnCtx.modalLayout, &footerDesc);
2051 }
2052 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2053 nbgl_refresh();
2054}
2055
2056// function used to display the security level page in modal
2057// it can be either a single page with text or QR code, or a list of touchable bars leading
2058// to single pages
2059static void displayCustomizedSecurityReport(const nbgl_warningDetails_t *details)
2060{
2061 nbgl_layoutDescription_t layoutDescription = {.modal = true,
2062 .withLeftBorder = true,
2063 .onActionCallback = modalLayoutTouchCallback,
2064 .tapActionText = NULL};
2066 .separationLine = true,
2067 .backAndText.icon = NULL,
2068 .backAndText.tuneId = TUNE_TAP_CASUAL,
2069 .backAndText.token = DISMISS_WARNING_TOKEN};
2070 uint8_t i;
2071
2072 reviewWithWarnCtx.modalLayout = nbgl_layoutGet(&layoutDescription);
2073 headerDesc.backAndText.text = details->title;
2074 nbgl_layoutAddHeader(reviewWithWarnCtx.modalLayout, &headerDesc);
2075 if (details->type == BAR_LIST_WARNING) {
2076 // if more than one warning warning, so use a list of touchable bars
2077 for (i = 0; i < details->barList.nbBars; i++) {
2078 nbgl_layoutBar_t bar;
2079 bar.text = details->barList.texts[i];
2080 bar.subText = details->barList.subTexts[i];
2081 bar.iconRight = &PUSH_ICON;
2082 bar.iconLeft = details->barList.icons[i];
2083 bar.token = FIRST_WARN_BAR_TOKEN + i;
2084 bar.tuneId = TUNE_TAP_CASUAL;
2085 bar.large = false;
2086 bar.inactive = false;
2087 nbgl_layoutAddTouchableBar(reviewWithWarnCtx.modalLayout, &bar);
2088 nbgl_layoutAddSeparationLine(reviewWithWarnCtx.modalLayout);
2089 }
2090 }
2091 else if (details->type == QRCODE_WARNING) {
2092#ifdef NBGL_QRCODE
2093 // display a QR Code
2094 nbgl_layoutAddQRCode(reviewWithWarnCtx.modalLayout, &details->qrCode);
2095#endif // NBGL_QRCODE
2096 headerDesc.backAndText.text = details->title;
2097 }
2098 else if (details->type == CENTERED_INFO_WARNING) {
2099 // display a centered info
2100 nbgl_layoutAddContentCenter(reviewWithWarnCtx.modalLayout, &details->centeredInfo);
2101 headerDesc.separationLine = false;
2102 }
2103 nbgl_layoutDraw(reviewWithWarnCtx.modalLayout);
2104 nbgl_refresh();
2105}
2106
2107// function used to display the initial warning page when starting a "review with warning"
2108static void displayInitialWarning(void)
2109{
2110 // Play notification sound
2111#ifdef HAVE_PIEZO_SOUND
2112 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2113#endif // HAVE_PIEZO_SOUND
2114 nbgl_layoutDescription_t layoutDescription;
2115 nbgl_layoutChoiceButtons_t buttonsInfo = {.bottomText = "Continue anyway",
2116 .token = WARNING_CHOICE_TOKEN,
2117 .topText = "Back to safety",
2118 .style = ROUNDED_AND_FOOTER_STYLE,
2119 .tuneId = TUNE_TAP_CASUAL};
2120 nbgl_layoutHeader_t headerDesc = {.type = HEADER_EMPTY,
2121 .separationLine = false,
2122 .emptySpace.height = MEDIUM_CENTERING_HEADER};
2123 uint32_t set = reviewWithWarnCtx.warning->predefinedSet
2124 & ~((1 << W3C_NO_THREAT_WARN) | (1 << W3C_ISSUE_WARN));
2125
2126 reviewWithWarnCtx.isIntro = true;
2127
2128 layoutDescription.modal = false;
2129 layoutDescription.withLeftBorder = true;
2130
2131 layoutDescription.onActionCallback = layoutTouchCallback;
2132 layoutDescription.tapActionText = NULL;
2133
2134 layoutDescription.ticker.tickerCallback = NULL;
2135 reviewWithWarnCtx.layoutCtx = nbgl_layoutGet(&layoutDescription);
2136
2137 nbgl_layoutAddHeader(reviewWithWarnCtx.layoutCtx, &headerDesc);
2138 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2140 reviewWithWarnCtx.layoutCtx,
2141 (set == (1 << BLIND_SIGNING_WARN)) ? &INFO_I_ICON : &QRCODE_ICON,
2142 WARNING_BUTTON_TOKEN,
2143 TUNE_TAP_CASUAL);
2144 }
2145 else if (reviewWithWarnCtx.warning->introTopRightIcon != NULL) {
2146 nbgl_layoutAddTopRightButton(reviewWithWarnCtx.layoutCtx,
2147 reviewWithWarnCtx.warning->introTopRightIcon,
2148 WARNING_BUTTON_TOKEN,
2149 TUNE_TAP_CASUAL);
2150 }
2151 // add main content
2152 // if predefined content is configured, use it preferably
2153 if (reviewWithWarnCtx.warning->predefinedSet != 0) {
2154 nbgl_contentCenter_t info = {0};
2155
2156 const char *provider;
2157
2158 // default icon
2159 info.icon = &LARGE_WARNING_ICON;
2160
2161 // use small title only if not Blind signing only
2162 if (set != (1 << BLIND_SIGNING_WARN)) {
2163 if (reviewWithWarnCtx.warning->reportProvider) {
2164 provider = reviewWithWarnCtx.warning->reportProvider;
2165 }
2166 else {
2167 provider = "[unknown]";
2168 }
2169 info.smallTitle = tmpString;
2170 snprintf(tmpString, W3C_DESCRIPTION_MAX_LEN, "Detected by %s", provider);
2171 }
2172
2173 if (set == (1 << BLIND_SIGNING_WARN)) {
2174 info.title = "Blind signing ahead";
2175 info.description
2176 = "This transaction's details are not fully verifiable. If you sign, you could "
2177 "lose all your assets.";
2178 buttonsInfo.bottomText = "Continue anyway";
2179 }
2180 else if (set & (1 << W3C_RISK_DETECTED_WARN)) {
2181 info.title = "Potential risk";
2182 if (reviewWithWarnCtx.warning->providerMessage == NULL) {
2183 info.description = "Unidentified risk";
2184 }
2185 else {
2186 info.description = reviewWithWarnCtx.warning->providerMessage;
2187 }
2188 buttonsInfo.bottomText = "Accept risk and continue";
2189 }
2190 else if (set & (1 << W3C_THREAT_DETECTED_WARN)) {
2191 info.title = "Critical threat";
2192 if (reviewWithWarnCtx.warning->providerMessage == NULL) {
2193 info.description = "Unidentified threat";
2194 }
2195 else {
2196 info.description = reviewWithWarnCtx.warning->providerMessage;
2197 }
2198 buttonsInfo.bottomText = "Accept threat and continue";
2199 }
2200 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, &info);
2201 }
2202 else if (reviewWithWarnCtx.warning->info != NULL) {
2203 // if no predefined content, use custom one
2204 nbgl_layoutAddContentCenter(reviewWithWarnCtx.layoutCtx, reviewWithWarnCtx.warning->info);
2205 }
2206 // add button and footer on bottom
2207 nbgl_layoutAddChoiceButtons(reviewWithWarnCtx.layoutCtx, &buttonsInfo);
2208
2209 nbgl_layoutDraw(reviewWithWarnCtx.layoutCtx);
2210 nbgl_refresh();
2211}
2212
2213// function to factorize code for all simple reviews
2214static void useCaseReview(nbgl_operationType_t operationType,
2215 const nbgl_contentTagValueList_t *tagValueList,
2216 const nbgl_icon_details_t *icon,
2217 const char *reviewTitle,
2218 const char *reviewSubTitle,
2219 const char *finishTitle,
2220 const nbgl_tipBox_t *tipBox,
2221 nbgl_choiceCallback_t choiceCallback,
2222 bool isLight,
2223 bool playNotifSound,
2224 bool validWarningCtx)
2225{
2226 const char *predefinedTipBoxText = NULL;
2227 reset_callbacks();
2228 memset(&genericContext, 0, sizeof(genericContext));
2229
2230 bundleNavContext.review.operationType = operationType;
2231 bundleNavContext.review.choiceCallback = choiceCallback;
2232
2233 // memorize context
2234 onChoice = bundleNavReviewChoice;
2235 navType = GENERIC_NAV;
2236 pageTitle = NULL;
2237
2238 genericContext.validWarningCtx = validWarningCtx;
2239 genericContext.genericContents.contentsList = localContentsList;
2240 genericContext.genericContents.nbContents = 3;
2241 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
2242
2243 // First a centered info
2244 STARTING_CONTENT.type = EXTENDED_CENTER;
2245 prepareReviewFirstPage(
2246 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2247
2248 // if warning is valid and a warning requiring a tip box
2249 if (validWarningCtx) {
2250 if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_ISSUE_WARN)) {
2251 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2252 predefinedTipBoxText = "Transaction Check unavailable.\nBlind signing required.";
2253 }
2254 else {
2255 predefinedTipBoxText = "Transaction Check unavailable";
2256 }
2257 }
2258 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN)) {
2259 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2260 predefinedTipBoxText = "Critical threat detected.\nBlind signing required.";
2261 }
2262 else {
2263 predefinedTipBoxText = "Critical threat detected.";
2264 }
2265 }
2266 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN)) {
2267 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2268 predefinedTipBoxText = "Potential risk detected.\nBlind signing required.";
2269 }
2270 else {
2271 predefinedTipBoxText = "Potential risk detected.";
2272 }
2273 }
2274 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_NO_THREAT_WARN)) {
2275 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2276 predefinedTipBoxText
2277 = "No threat detected by Transaction Check but blind signing required.";
2278 }
2279 else {
2280 predefinedTipBoxText = "No threat detected by Transaction Check.";
2281 }
2282 }
2283 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2284 predefinedTipBoxText = "Blind signing required.";
2285 }
2286 }
2287 if ((tipBox != NULL) || (predefinedTipBoxText != NULL)) {
2288 // do not display "Swipe to review" if a tip-box is displayed
2289 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL;
2290 if (predefinedTipBoxText != NULL) {
2291 STARTING_CONTENT.content.extendedCenter.tipBox.icon = NULL;
2292 STARTING_CONTENT.content.extendedCenter.tipBox.text = predefinedTipBoxText;
2293 }
2294 else {
2295 STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
2296 STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
2297 }
2298 STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
2299 STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
2300 }
2301
2302 // Then the tag/value pairs
2303 localContentsList[1].type = TAG_VALUE_LIST;
2304 memcpy(&localContentsList[1].content.tagValueList,
2305 tagValueList,
2307 localContentsList[1].contentActionCallback = tagValueList->actionCallback;
2308
2309 // The last page
2310 if (isLight) {
2311 localContentsList[2].type = INFO_BUTTON;
2312 prepareReviewLightLastPage(&localContentsList[2].content.infoButton, icon, finishTitle);
2313 }
2314 else {
2315 localContentsList[2].type = INFO_LONG_PRESS;
2316 prepareReviewLastPage(&localContentsList[2].content.infoLongPress, icon, finishTitle);
2317 }
2318
2319 // compute number of pages & fill navigation structure
2320 uint8_t nbPages = getNbPagesForGenericContents(
2321 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2322 prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
2323
2324 // Play notification sound if required
2325 if (playNotifSound) {
2326#ifdef HAVE_PIEZO_SOUND
2327 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2328#endif // HAVE_PIEZO_SOUND
2329 }
2330
2331 displayGenericContextPage(0, true);
2332}
2333
2334// function to factorize code for all streaming reviews
2335static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
2336 const nbgl_icon_details_t *icon,
2337 const char *reviewTitle,
2338 const char *reviewSubTitle,
2339 nbgl_choiceCallback_t choiceCallback,
2340 bool playNotifSound)
2341{
2342 reset_callbacks();
2343 memset(&genericContext, 0, sizeof(genericContext));
2344
2345 bundleNavContext.reviewStreaming.operationType = operationType;
2346 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
2347 bundleNavContext.reviewStreaming.icon = icon;
2348
2349 // memorize context
2350 onChoice = bundleNavReviewStreamingChoice;
2351 navType = STREAMING_NAV;
2352 pageTitle = NULL;
2353
2354 genericContext.genericContents.contentsList = localContentsList;
2355 genericContext.genericContents.nbContents = 1;
2356 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
2357
2358 // First a centered info
2359 STARTING_CONTENT.type = EXTENDED_CENTER;
2360 prepareReviewFirstPage(
2361 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
2362
2363 // compute number of pages & fill navigation structure
2364 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
2365 &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
2366 prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
2367 // no back button on first page
2368 navInfo.navWithButtons.backButton = false;
2369
2370 // Play notification sound if required
2371 if (playNotifSound) {
2372#ifdef HAVE_PIEZO_SOUND
2373 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2374#endif // HAVE_PIEZO_SOUND
2375 }
2376
2377 displayGenericContextPage(0, true);
2378}
2379
2396static void useCaseHomeExt(const char *appName,
2397 const nbgl_icon_details_t *appIcon,
2398 const char *tagline,
2399 bool withSettings,
2400 nbgl_homeAction_t *homeAction,
2401 nbgl_callback_t topRightCallback,
2402 nbgl_callback_t quitCallback)
2403{
2404 reset_callbacks();
2405
2406 nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
2407 .centeredInfo.text1 = appName,
2408 .centeredInfo.text3 = NULL,
2409 .centeredInfo.style = LARGE_CASE_INFO,
2410 .centeredInfo.offsetY = 0,
2411 .footerText = NULL,
2412 .bottomButtonStyle = QUIT_APP_TEXT,
2413 .tapActionText = NULL,
2414 .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
2415 .topRightToken = CONTINUE_TOKEN,
2416 .tuneId = TUNE_TAP_CASUAL};
2417 if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
2418 // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
2419 info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
2420 onAction = homeAction->callback;
2421 info.actionButtonText = homeAction->text;
2422 info.actionButtonIcon = homeAction->icon;
2425 }
2426 else {
2427 info.bottomButtonsToken = QUIT_TOKEN;
2428 onAction = NULL;
2429 info.actionButtonText = NULL;
2430 info.actionButtonIcon = NULL;
2431 }
2432 if (tagline == NULL) {
2433 if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
2434 snprintf(tmpString,
2436 "This app enables signing\ntransactions on its network.");
2437 }
2438 else {
2439 snprintf(tmpString,
2441 "%s %s\n%s",
2443 appName,
2445 }
2446
2447 // If there is more than 3 lines, it means the appName was split, so we put it on the next
2448 // line
2449 if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, tmpString, AVAILABLE_WIDTH, false) > 3) {
2450 snprintf(tmpString,
2452 "%s\n%s %s",
2454 appName,
2456 }
2457 info.centeredInfo.text2 = tmpString;
2458 }
2459 else {
2460 info.centeredInfo.text2 = tagline;
2461 }
2462
2463 onContinue = topRightCallback;
2464 onQuit = quitCallback;
2465
2466 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2468}
2469
2478static void displayDetails(const char *tag, const char *value, bool wrapping)
2479{
2480 memset(&detailsContext, 0, sizeof(detailsContext));
2481
2482 uint16_t nbLines
2483 = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
2484
2485 // initialize context
2486 detailsContext.tag = tag;
2487 detailsContext.value = value;
2488 detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
2489 detailsContext.currentPage = 0;
2490 detailsContext.wrapping = wrapping;
2491 // add some spare for room lost with "..." substitution
2492 if (detailsContext.nbPages > 1) {
2493 uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
2494 uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
2495 / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
2496 uint8_t nbLinesInLastPage
2497 = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
2498
2499 detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
2500 if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
2501 > NB_MAX_LINES_IN_DETAILS) {
2502 detailsContext.nbPages++;
2503 }
2504 }
2505
2506 displayDetailsPage(0, true);
2507}
2508
2509/**********************
2510 * GLOBAL FUNCTIONS
2511 **********************/
2512
2526 const nbgl_contentTagValueList_t *tagValueList,
2527 uint8_t startIndex,
2528 bool *requireSpecificDisplay)
2529{
2531 nbPairs, tagValueList, startIndex, false, requireSpecificDisplay);
2532}
2533
2548 const nbgl_contentTagValueList_t *tagValueList,
2549 uint8_t startIndex,
2550 bool isSkippable,
2551 bool *requireSpecificDisplay)
2552{
2553 uint8_t nbPairsInPage = 0;
2554 uint16_t currentHeight = PRE_TAG_VALUE_MARGIN; // upper margin
2555 uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
2556
2557 // if the review is skippable, it means that there is less height for tag/value pairs
2558 // the small centering header becomes a touchable header
2559 if (isSkippable) {
2560 maxUsableHeight -= TOUCHABLE_HEADER_BAR_HEIGHT - SMALL_CENTERING_HEADER;
2561 }
2562
2563 *requireSpecificDisplay = false;
2564 while (nbPairsInPage < nbPairs) {
2565 const nbgl_layoutTagValue_t *pair;
2566 nbgl_font_id_e value_font;
2567 uint16_t nbLines;
2568
2569 // margin between pairs
2570 // 12 or 24 px between each tag/value pair
2571 if (nbPairsInPage > 0) {
2572 currentHeight += INTER_TAG_VALUE_MARGIN;
2573 }
2574 // fetch tag/value pair strings.
2575 if (tagValueList->pairs != NULL) {
2576 pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2577 }
2578 else {
2579 pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2580 }
2581
2582 if (pair->forcePageStart && nbPairsInPage > 0) {
2583 // This pair must be at the top of a page
2584 break;
2585 }
2586
2587 if (pair->centeredInfo) {
2588 if (nbPairsInPage > 0) {
2589 // This pair must be at the top of a page
2590 break;
2591 }
2592 else {
2593 // This pair is the only one of the page and has a specific display behavior
2594 nbPairsInPage = 1;
2595 *requireSpecificDisplay = true;
2596 break;
2597 }
2598 }
2599
2600 // tag height
2601 currentHeight += nbgl_getTextHeightInWidth(
2602 SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2603 // space between tag and value
2604 currentHeight += 4;
2605 // set value font
2606 if (tagValueList->smallCaseForValue) {
2607 value_font = SMALL_REGULAR_FONT;
2608 }
2609 else {
2610 value_font = LARGE_MEDIUM_FONT;
2611 }
2612 // value height
2613 currentHeight += nbgl_getTextHeightInWidth(
2614 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2615 // nb lines for value
2617 value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2618 if ((currentHeight >= maxUsableHeight) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2619 if (nbPairsInPage == 0) {
2620 // Pair too long to fit in a single screen
2621 // It will be the only one of the page and has a specific display behavior
2622 nbPairsInPage = 1;
2623 *requireSpecificDisplay = true;
2624 }
2625 break;
2626 }
2627 nbPairsInPage++;
2628 }
2629 return nbPairsInPage;
2630}
2631
2642 const nbgl_contentInfoList_t *infosList,
2643 uint8_t startIndex,
2644 bool withNav)
2645{
2646 uint8_t nbInfosInPage = 0;
2647 uint16_t currentHeight = 0;
2648 uint16_t previousHeight;
2649 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2650 const char *const *infoTypes = PIC(infosList->infoTypes);
2651 const char *const *infoContents = PIC(infosList->infoContents);
2652
2653 while (nbInfosInPage < nbInfos) {
2654 // margin between infos
2655 currentHeight += PRE_TEXT_MARGIN;
2656
2657 // type height
2658 currentHeight += nbgl_getTextHeightInWidth(
2659 SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2660 // space between type and content
2661 currentHeight += TEXT_SUBTEXT_MARGIN;
2662
2663 // content height
2664 currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2665 PIC(infoContents[startIndex + nbInfosInPage]),
2667 true);
2668 currentHeight += POST_SUBTEXT_MARGIN; // under the content
2669 // if height is over the limit
2670 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2671 // if there was no nav, now there will be, so it can be necessary to remove the last
2672 // item
2673 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2674 nbInfosInPage--;
2675 }
2676 break;
2677 }
2678 previousHeight = currentHeight;
2679 nbInfosInPage++;
2680 }
2681 return nbInfosInPage;
2682}
2683
2694 const nbgl_contentSwitchesList_t *switchesList,
2695 uint8_t startIndex,
2696 bool withNav)
2697{
2698 uint8_t nbSwitchesInPage = 0;
2699 uint16_t currentHeight = 0;
2700 uint16_t previousHeight;
2701 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2702 nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2703
2704 while (nbSwitchesInPage < nbSwitches) {
2705 // margin between switches
2706 currentHeight += PRE_TEXT_MARGIN;
2707
2708 // text height
2709 currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2710 switchArray[startIndex + nbSwitchesInPage].text,
2712 true);
2713 // space between text and sub-text
2714 currentHeight += TEXT_SUBTEXT_MARGIN;
2715
2716 // sub-text height
2717 currentHeight
2718 += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2719 switchArray[startIndex + nbSwitchesInPage].subText,
2721 true);
2722 currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2723 // if height is over the limit
2724 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2725 break;
2726 }
2727 previousHeight = currentHeight;
2728 nbSwitchesInPage++;
2729 }
2730 // if there was no nav, now there will be, so it can be necessary to remove the last
2731 // item
2732 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2733 nbSwitchesInPage--;
2734 }
2735 return nbSwitchesInPage;
2736}
2737
2748 const nbgl_contentBarsList_t *barsList,
2749 uint8_t startIndex,
2750 bool withNav)
2751{
2752 uint8_t nbBarsInPage = 0;
2753 uint16_t currentHeight = 0;
2754 uint16_t previousHeight;
2755 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2756
2757 UNUSED(barsList);
2758 UNUSED(startIndex);
2759
2760 while (nbBarsInPage < nbBars) {
2761 currentHeight += TOUCHABLE_BAR_HEIGHT;
2762 // if height is over the limit
2763 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2764 break;
2765 }
2766 previousHeight = currentHeight;
2767 nbBarsInPage++;
2768 }
2769 // if there was no nav, now there may will be, so it can be necessary to remove the last
2770 // item
2771 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2772 nbBarsInPage--;
2773 }
2774 return nbBarsInPage;
2775}
2776
2787 const nbgl_contentRadioChoice_t *choicesList,
2788 uint8_t startIndex,
2789 bool withNav)
2790{
2791 uint8_t nbChoicesInPage = 0;
2792 uint16_t currentHeight = 0;
2793 uint16_t previousHeight;
2794 uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2795
2796 UNUSED(choicesList);
2797 UNUSED(startIndex);
2798
2799 while (nbChoicesInPage < nbChoices) {
2800 currentHeight += TOUCHABLE_BAR_HEIGHT;
2801 // if height is over the limit
2802 if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2803 // if there was no nav, now there will be, so it can be necessary to remove the last
2804 // item
2805 if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2806 nbChoicesInPage--;
2807 }
2808 break;
2809 }
2810 previousHeight = currentHeight;
2811 nbChoicesInPage++;
2812 }
2813 return nbChoicesInPage;
2814}
2815
2823{
2824 uint8_t nbPages = 0;
2825 uint8_t nbPairs = tagValueList->nbPairs;
2826 uint8_t nbPairsInPage;
2827 uint8_t i = 0;
2828 bool flag;
2829
2830 while (i < tagValueList->nbPairs) {
2831 // upper margin
2832 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2833 i += nbPairsInPage;
2834 nbPairs -= nbPairsInPage;
2835 nbPages++;
2836 }
2837 return nbPages;
2838}
2839
2844void nbgl_useCaseHome(const char *appName,
2845 const nbgl_icon_details_t *appIcon,
2846 const char *tagline,
2847 bool withSettings,
2848 nbgl_callback_t topRightCallback,
2849 nbgl_callback_t quitCallback)
2850{
2851 nbgl_homeAction_t homeAction = {0};
2852 useCaseHomeExt(
2853 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2854}
2855
2860void nbgl_useCaseHomeExt(const char *appName,
2861 const nbgl_icon_details_t *appIcon,
2862 const char *tagline,
2863 bool withSettings,
2864 const char *actionButtonText,
2865 nbgl_callback_t actionCallback,
2866 nbgl_callback_t topRightCallback,
2867 nbgl_callback_t quitCallback)
2868{
2869 nbgl_homeAction_t homeAction = {.callback = actionCallback,
2870 .icon = NULL,
2871 .style = STRONG_HOME_ACTION,
2872 .text = actionButtonText};
2873
2874 useCaseHomeExt(
2875 appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2876}
2877
2891void nbgl_useCaseNavigableContent(const char *title,
2892 uint8_t initPage,
2893 uint8_t nbPages,
2894 nbgl_callback_t quitCallback,
2895 nbgl_navCallback_t navCallback,
2896 nbgl_layoutTouchCallback_t controlsCallback)
2897{
2898 reset_callbacks();
2899
2900 // memorize context
2901 onQuit = quitCallback;
2902 onNav = navCallback;
2903 onControls = controlsCallback;
2904 pageTitle = title;
2905 navType = SETTINGS_NAV;
2906
2907 // fill navigation structure
2908 prepareNavInfo(false, nbPages, NULL);
2909
2910 displaySettingsPage(initPage, true);
2911}
2912
2918void nbgl_useCaseSettings(const char *title,
2919 uint8_t initPage,
2920 uint8_t nbPages,
2921 bool touchable,
2922 nbgl_callback_t quitCallback,
2923 nbgl_navCallback_t navCallback,
2924 nbgl_layoutTouchCallback_t controlsCallback)
2925{
2926 UNUSED(touchable);
2928 title, initPage, nbPages, quitCallback, navCallback, controlsCallback);
2929}
2930
2943void nbgl_useCaseGenericSettings(const char *appName,
2944 uint8_t initPage,
2945 const nbgl_genericContents_t *settingContents,
2946 const nbgl_contentInfoList_t *infosList,
2947 nbgl_callback_t quitCallback)
2948{
2949 reset_callbacks();
2950 memset(&genericContext, 0, sizeof(genericContext));
2951
2952 // memorize context
2953 onQuit = quitCallback;
2954 pageTitle = appName;
2955 navType = GENERIC_NAV;
2956
2957 if (settingContents != NULL) {
2958 memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
2959 }
2960 if (infosList != NULL) {
2961 genericContext.hasFinishingContent = true;
2962 memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
2963 FINISHING_CONTENT.type = INFOS_LIST;
2964 memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
2965 }
2966
2967 // fill navigation structure
2968 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2969 if (infosList != NULL) {
2970 nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true, false);
2971 }
2972
2973 prepareNavInfo(false, nbPages, NULL);
2974
2975 displayGenericContextPage(initPage, true);
2976}
2977
2989void nbgl_useCaseGenericConfiguration(const char *title,
2990 uint8_t initPage,
2991 const nbgl_genericContents_t *contents,
2992 nbgl_callback_t quitCallback)
2993{
2994 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2995}
2996
3014 const char *appName,
3015 const nbgl_icon_details_t *appIcon,
3016 const char *tagline,
3017 const uint8_t
3018 initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
3019 const nbgl_genericContents_t *settingContents,
3020 const nbgl_contentInfoList_t *infosList,
3021 const nbgl_homeAction_t *action, // Set to NULL if no additional action
3022 nbgl_callback_t quitCallback)
3023{
3024 nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
3025
3026 context->appName = appName;
3027 context->appIcon = appIcon;
3028 context->tagline = tagline;
3029 context->settingContents = settingContents;
3030 context->infosList = infosList;
3031 if (action != NULL) {
3032 memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
3033 }
3034 else {
3035 memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
3036 }
3037 context->quitCallback = quitCallback;
3038
3039 if (initSettingPage != INIT_HOME_PAGE) {
3040 bundleNavStartSettingsAtPage(initSettingPage);
3041 }
3042 else {
3043 bundleNavStartHome();
3044 }
3045}
3046
3054void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
3055{
3056 reset_callbacks();
3057
3058 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = &tickerCallback,
3059 .tickerIntervale = 0, // not periodic
3060 .tickerValue = STATUS_SCREEN_DURATION};
3061
3062 onQuit = quitCallback;
3063 if (isSuccess) {
3064#ifdef HAVE_PIEZO_SOUND
3065 io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
3066#endif // HAVE_PIEZO_SOUND
3067
3068 pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
3069 }
3070 else {
3072 .footerText = NULL,
3073 .centeredInfo.icon = &DENIED_CIRCLE_ICON,
3074 .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
3075 .centeredInfo.onTop = false,
3076 .centeredInfo.style = LARGE_CASE_INFO,
3077 .centeredInfo.text1 = message,
3078 .centeredInfo.text2 = NULL,
3079 .centeredInfo.text3 = NULL,
3080 .tapActionText = "",
3081 .isSwipeable = false,
3082 .tapActionToken = QUIT_TOKEN,
3083 .topRightStyle = NO_BUTTON_STYLE,
3084 .actionButtonText = NULL,
3085 .tuneId = TUNE_TAP_CASUAL};
3086 pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
3087 }
3089}
3090
3098 nbgl_callback_t quitCallback)
3099{
3100 const char *msg;
3101 bool isSuccess;
3102 switch (reviewStatusType) {
3104 msg = "Operation signed";
3105 isSuccess = true;
3106 break;
3108 msg = "Operation rejected";
3109 isSuccess = false;
3110 break;
3112 msg = "Transaction signed";
3113 isSuccess = true;
3114 break;
3116 msg = "Transaction rejected";
3117 isSuccess = false;
3118 break;
3120 msg = "Message signed";
3121 isSuccess = true;
3122 break;
3124 msg = "Message rejected";
3125 isSuccess = false;
3126 break;
3128 msg = "Address verified";
3129 isSuccess = true;
3130 break;
3132 msg = "Address verification\ncancelled";
3133 isSuccess = false;
3134 break;
3135 default:
3136 return;
3137 }
3138 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
3139}
3140
3154 const char *message,
3155 const char *subMessage,
3156 const char *confirmText,
3157 const char *cancelText,
3158 nbgl_choiceCallback_t callback)
3159{
3160 reset_callbacks();
3161
3162 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3163 .centeredInfo.text1 = message,
3164 .centeredInfo.text2 = subMessage,
3165 .centeredInfo.text3 = NULL,
3166 .centeredInfo.style = LARGE_CASE_INFO,
3167 .centeredInfo.icon = icon,
3168 .centeredInfo.offsetY = 0,
3169 .confirmationText = confirmText,
3170 .confirmationToken = CHOICE_TOKEN,
3171 .tuneId = TUNE_TAP_CASUAL,
3172 .modal = false};
3173 // check params
3174 if ((confirmText == NULL) || (cancelText == NULL)) {
3175 return;
3176 }
3177 onChoice = callback;
3178 pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
3180}
3181
3195void nbgl_useCaseConfirm(const char *message,
3196 const char *subMessage,
3197 const char *confirmText,
3198 const char *cancelText,
3199 nbgl_callback_t callback)
3200{
3201 // Don't reset callback or nav context as this is just a modal.
3202
3203 nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
3204 .centeredInfo.text1 = message,
3205 .centeredInfo.text2 = subMessage,
3206 .centeredInfo.text3 = NULL,
3207 .centeredInfo.style = LARGE_CASE_INFO,
3208 .centeredInfo.icon = &IMPORTANT_CIRCLE_ICON,
3209 .centeredInfo.offsetY = 0,
3210 .confirmationText = confirmText,
3211 .confirmationToken = CHOICE_TOKEN,
3212 .tuneId = TUNE_TAP_CASUAL,
3213 .modal = true};
3214 onModalConfirm = callback;
3215 if (modalPageContext != NULL) {
3216 nbgl_pageRelease(modalPageContext);
3217 }
3218 modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
3220}
3221
3234 const char *reviewTitle,
3235 const char *reviewSubTitle,
3236 const char *rejectText,
3237 nbgl_callback_t continueCallback,
3238 nbgl_callback_t rejectCallback)
3239{
3240 reset_callbacks();
3241
3242 nbgl_pageInfoDescription_t info = {.footerText = rejectText,
3243 .footerToken = QUIT_TOKEN,
3244 .tapActionText = NULL,
3245 .isSwipeable = true,
3246 .tapActionToken = CONTINUE_TOKEN,
3247 .topRightStyle = NO_BUTTON_STYLE,
3248 .actionButtonText = NULL,
3249 .tuneId = TUNE_TAP_CASUAL};
3250 info.centeredInfo.icon = icon;
3251 info.centeredInfo.text1 = reviewTitle;
3252 info.centeredInfo.text2 = reviewSubTitle;
3253 info.centeredInfo.text3 = "Swipe to review";
3255 info.centeredInfo.offsetY = 0;
3256 onQuit = rejectCallback;
3257 onContinue = continueCallback;
3258
3259#ifdef HAVE_PIEZO_SOUND
3260 // Play notification sound
3261 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3262#endif // HAVE_PIEZO_SOUND
3263
3264 pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
3265 nbgl_refresh();
3266}
3267
3273 uint8_t nbPages,
3274 const char *rejectText,
3275 nbgl_layoutTouchCallback_t buttonCallback,
3276 nbgl_navCallback_t navCallback,
3277 nbgl_choiceCallback_t choiceCallback)
3278{
3279 reset_callbacks();
3280
3281 // memorize context
3282 onChoice = choiceCallback;
3283 onNav = navCallback;
3284 onControls = buttonCallback;
3285 forwardNavOnly = false;
3286 navType = REVIEW_NAV;
3287
3288 // fill navigation structure
3289 UNUSED(rejectText);
3290 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3291
3292 displayReviewPage(initPage, true);
3293}
3294
3308 const nbgl_pageInfoLongPress_t *infoLongPress,
3309 const char *rejectText,
3310 nbgl_choiceCallback_t callback)
3311{
3312 uint8_t offset = 0;
3313
3314 reset_callbacks();
3315 memset(&genericContext, 0, sizeof(genericContext));
3316
3317 // memorize context
3318 onChoice = callback;
3319 navType = GENERIC_NAV;
3320 pageTitle = NULL;
3321 bundleNavContext.review.operationType = TYPE_OPERATION;
3322
3323 genericContext.genericContents.contentsList = localContentsList;
3324 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3325
3326 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3327 localContentsList[offset].type = TAG_VALUE_LIST;
3328 memcpy(&localContentsList[offset].content.tagValueList,
3329 tagValueList,
3331 offset++;
3332 }
3333
3334 localContentsList[offset].type = INFO_LONG_PRESS;
3335 memcpy(&localContentsList[offset].content.infoLongPress,
3336 infoLongPress,
3337 sizeof(nbgl_pageInfoLongPress_t));
3338 localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
3339 offset++;
3340
3341 genericContext.genericContents.nbContents = offset;
3342
3343 // compute number of pages & fill navigation structure
3344 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3345 UNUSED(rejectText);
3346 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3347
3348 displayGenericContextPage(0, true);
3349}
3350
3365 const nbgl_pageInfoLongPress_t *infoLongPress,
3366 const char *rejectText,
3367 nbgl_choiceCallback_t callback)
3368{
3369 uint8_t offset = 0;
3370
3371 reset_callbacks();
3372 memset(&genericContext, 0, sizeof(genericContext));
3373
3374 // memorize context
3375 onChoice = callback;
3376 navType = GENERIC_NAV;
3377 pageTitle = NULL;
3378
3379 genericContext.genericContents.contentsList = localContentsList;
3380 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3381
3382 if (tagValueList != NULL && tagValueList->nbPairs != 0) {
3383 localContentsList[offset].type = TAG_VALUE_LIST;
3384 memcpy(&localContentsList[offset].content.tagValueList,
3385 tagValueList,
3387 offset++;
3388 }
3389
3390 localContentsList[offset].type = INFO_BUTTON;
3391 localContentsList[offset].content.infoButton.text = infoLongPress->text;
3392 localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
3393 localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
3394 localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
3395 localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
3396 offset++;
3397
3398 genericContext.genericContents.nbContents = offset;
3399
3400 // compute number of pages & fill navigation structure
3401 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3402 UNUSED(rejectText);
3403 prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
3404
3405 displayGenericContextPage(0, true);
3406}
3407
3424void nbgl_useCaseReview(nbgl_operationType_t operationType,
3425 const nbgl_contentTagValueList_t *tagValueList,
3426 const nbgl_icon_details_t *icon,
3427 const char *reviewTitle,
3428 const char *reviewSubTitle,
3429 const char *finishTitle,
3430 nbgl_choiceCallback_t choiceCallback)
3431{
3432 useCaseReview(operationType,
3433 tagValueList,
3434 icon,
3435 reviewTitle,
3436 reviewSubTitle,
3437 finishTitle,
3438 NULL,
3439 choiceCallback,
3440 false,
3441 true,
3442 false);
3443}
3444
3465 const nbgl_contentTagValueList_t *tagValueList,
3466 const nbgl_icon_details_t *icon,
3467 const char *reviewTitle,
3468 const char *reviewSubTitle,
3469 const char *finishTitle,
3470 const nbgl_tipBox_t *tipBox,
3471 nbgl_choiceCallback_t choiceCallback)
3472{
3473 nbgl_useCaseAdvancedReview(operationType,
3474 tagValueList,
3475 icon,
3476 reviewTitle,
3477 reviewSubTitle,
3478 finishTitle,
3479 tipBox,
3480 &blindSigningWarning,
3481 choiceCallback);
3482}
3483
3507 const nbgl_contentTagValueList_t *tagValueList,
3508 const nbgl_icon_details_t *icon,
3509 const char *reviewTitle,
3510 const char *reviewSubTitle,
3511 const char *finishTitle,
3512 const nbgl_tipBox_t *tipBox,
3513 const nbgl_warning_t *warning,
3514 nbgl_choiceCallback_t choiceCallback)
3515{
3516 // memorize tipBox because it can be in the call stack of the caller
3517 if (tipBox != NULL) {
3518 memcpy(&activeTipBox, tipBox, sizeof(activeTipBox));
3519 }
3520 else {
3521 memset(&activeTipBox, 0, sizeof(activeTipBox));
3522 }
3523 // if no warning at all, it's a simple review
3524 if ((warning == NULL)
3525 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
3526 && (warning->reviewDetails == NULL))) {
3527 useCaseReview(operationType,
3528 tagValueList,
3529 icon,
3530 reviewTitle,
3531 reviewSubTitle,
3532 finishTitle,
3533 tipBox,
3534 choiceCallback,
3535 false,
3536 true,
3537 false);
3538 return;
3539 }
3540 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
3541 operationType |= NO_THREAT_OPERATION;
3542 }
3543 else {
3544 operationType |= RISKY_OPERATION;
3545 }
3546
3547 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3548 reviewWithWarnCtx.isStreaming = false;
3549 reviewWithWarnCtx.operationType = operationType;
3550 reviewWithWarnCtx.tagValueList = tagValueList;
3551 reviewWithWarnCtx.icon = icon;
3552 reviewWithWarnCtx.reviewTitle = reviewTitle;
3553 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3554 reviewWithWarnCtx.finishTitle = finishTitle;
3555 reviewWithWarnCtx.warning = warning;
3556 reviewWithWarnCtx.choiceCallback = choiceCallback;
3557
3558 // display the initial warning only of a risk/threat or blind signing
3559 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
3560 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
3561 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
3562 useCaseReview(operationType,
3563 tagValueList,
3564 icon,
3565 reviewTitle,
3566 reviewSubTitle,
3567 finishTitle,
3568 tipBox,
3569 choiceCallback,
3570 false,
3571 true,
3572 true);
3573 return;
3574 }
3575
3576 displayInitialWarning();
3577}
3578
3596 const nbgl_contentTagValueList_t *tagValueList,
3597 const nbgl_icon_details_t *icon,
3598 const char *reviewTitle,
3599 const char *reviewSubTitle,
3600 const char *finishTitle,
3601 nbgl_choiceCallback_t choiceCallback)
3602{
3603 useCaseReview(operationType,
3604 tagValueList,
3605 icon,
3606 reviewTitle,
3607 reviewSubTitle,
3608 finishTitle,
3609 NULL,
3610 choiceCallback,
3611 true,
3612 true,
3613 false);
3614}
3615
3625 const char *rejectText,
3626 nbgl_callback_t rejectCallback)
3627{
3628 reset_callbacks();
3629 memset(&genericContext, 0, sizeof(genericContext));
3630
3631 // memorize context
3632 onQuit = rejectCallback;
3633 navType = GENERIC_NAV;
3634 pageTitle = NULL;
3635 bundleNavContext.review.operationType = TYPE_OPERATION;
3636
3637 memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3638
3639 // compute number of pages & fill navigation structure
3640 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3641 prepareNavInfo(true, nbPages, rejectText);
3642 navInfo.quitToken = QUIT_TOKEN;
3643
3644#ifdef HAVE_PIEZO_SOUND
3645 // Play notification sound
3646 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3647#endif // HAVE_PIEZO_SOUND
3648
3649 displayGenericContextPage(0, true);
3650}
3651
3665 const nbgl_icon_details_t *icon,
3666 const char *reviewTitle,
3667 const char *reviewSubTitle,
3668 nbgl_choiceCallback_t choiceCallback)
3669{
3670 useCaseReviewStreamingStart(
3671 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3672}
3673
3688 const nbgl_icon_details_t *icon,
3689 const char *reviewTitle,
3690 const char *reviewSubTitle,
3691 nbgl_choiceCallback_t choiceCallback)
3692{
3694 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
3695}
3696
3713 const nbgl_icon_details_t *icon,
3714 const char *reviewTitle,
3715 const char *reviewSubTitle,
3716 const nbgl_warning_t *warning,
3717 nbgl_choiceCallback_t choiceCallback)
3718{
3719 // if no warning at all, it's a simple review
3720 if ((warning == NULL)
3721 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
3722 && (warning->reviewDetails == NULL))) {
3723 useCaseReviewStreamingStart(
3724 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3725 return;
3726 }
3727 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
3728 operationType |= NO_THREAT_OPERATION;
3729 }
3730 else {
3731 operationType |= RISKY_OPERATION;
3732 }
3733 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
3734
3735 reviewWithWarnCtx.isStreaming = true;
3736 reviewWithWarnCtx.operationType = operationType;
3737 reviewWithWarnCtx.icon = icon;
3738 reviewWithWarnCtx.reviewTitle = reviewTitle;
3739 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
3740 reviewWithWarnCtx.choiceCallback = choiceCallback;
3741 reviewWithWarnCtx.warning = warning;
3742
3743 // display the initial warning only of a risk/threat or blind signing
3744 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
3745 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
3746 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
3747 useCaseReviewStreamingStart(
3748 operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3749 return;
3750 }
3751 displayInitialWarning();
3752}
3753
3768 nbgl_choiceCallback_t choiceCallback,
3769 nbgl_callback_t skipCallback)
3770{
3771 // Should follow a call to nbgl_useCaseReviewStreamingStart
3772 memset(&genericContext, 0, sizeof(genericContext));
3773
3774 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3775 bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3776
3777 // memorize context
3778 onChoice = bundleNavReviewStreamingChoice;
3779 navType = STREAMING_NAV;
3780 pageTitle = NULL;
3781
3782 genericContext.genericContents.contentsList = localContentsList;
3783 genericContext.genericContents.nbContents = 1;
3784 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3785
3786 // Then the tag/value pairs
3787 STARTING_CONTENT.type = TAG_VALUE_LIST;
3788 memcpy(
3789 &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3790
3791 // compute number of pages & fill navigation structure
3792 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3793 &genericContext.genericContents,
3794 0,
3795 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3796 prepareNavInfo(true,
3798 getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3799 // if the operation is skippable
3800 if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3801 navInfo.progressIndicator = false;
3802 navInfo.skipText = "Skip";
3803 navInfo.skipToken = SKIP_TOKEN;
3804 }
3805
3806 displayGenericContextPage(0, true);
3807}
3808
3820 nbgl_choiceCallback_t choiceCallback)
3821{
3822 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3823}
3824
3833void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3834 nbgl_choiceCallback_t choiceCallback)
3835{
3836 // Should follow a call to nbgl_useCaseReviewStreamingContinue
3837 memset(&genericContext, 0, sizeof(genericContext));
3838
3839 bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3840
3841 // memorize context
3842 onChoice = bundleNavReviewStreamingChoice;
3843 navType = STREAMING_NAV;
3844 pageTitle = NULL;
3845
3846 genericContext.genericContents.contentsList = localContentsList;
3847 genericContext.genericContents.nbContents = 1;
3848 memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3849
3850 // Eventually the long press page
3851 STARTING_CONTENT.type = INFO_LONG_PRESS;
3852 prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress,
3853 bundleNavContext.reviewStreaming.icon,
3854 finishTitle);
3855
3856 // compute number of pages & fill navigation structure
3857 bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3858 &genericContext.genericContents,
3859 0,
3860 (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3861 prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3862
3863 displayGenericContextPage(0, true);
3864}
3865
3870void nbgl_useCaseAddressConfirmationExt(const char *address,
3871 nbgl_choiceCallback_t callback,
3872 const nbgl_contentTagValueList_t *tagValueList)
3873{
3874 reset_callbacks();
3875 memset(&genericContext, 0, sizeof(genericContext));
3876 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3877
3878 // save context
3879 onChoice = callback;
3880 navType = GENERIC_NAV;
3881 pageTitle = NULL;
3882
3883 genericContext.genericContents.contentsList = localContentsList;
3884 genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3885 memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3886 prepareAddressConfirmationPages(
3887 address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3888
3889 // fill navigation structure, common to all pages
3890 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3891
3892 prepareNavInfo(true, nbPages, "Cancel");
3893
3894#ifdef HAVE_PIEZO_SOUND
3895 // Play notification sound
3896 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3897#endif // HAVE_PIEZO_SOUND
3898
3899 displayGenericContextPage(0, true);
3900}
3901
3918void nbgl_useCaseAddressReview(const char *address,
3919 const nbgl_contentTagValueList_t *additionalTagValueList,
3920 const nbgl_icon_details_t *icon,
3921 const char *reviewTitle,
3922 const char *reviewSubTitle,
3923 nbgl_choiceCallback_t choiceCallback)
3924{
3925 reset_callbacks();
3926 memset(&genericContext, 0, sizeof(genericContext));
3927 // release a potential modal
3928 if (addressConfirmationContext.modalLayout) {
3929 nbgl_layoutRelease(addressConfirmationContext.modalLayout);
3930 }
3931 memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3932
3933 // save context
3934 onChoice = choiceCallback;
3935 navType = GENERIC_NAV;
3936 pageTitle = NULL;
3937 bundleNavContext.review.operationType = TYPE_OPERATION;
3938
3939 genericContext.genericContents.contentsList = localContentsList;
3940 genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
3941 memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
3942
3943 // First a centered info
3944 STARTING_CONTENT.type = EXTENDED_CENTER;
3945 prepareReviewFirstPage(
3946 &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
3947 STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
3948
3949 // Then the address confirmation pages
3950 prepareAddressConfirmationPages(
3951 address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
3952
3953 // fill navigation structure, common to all pages
3954 uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3955
3956 prepareNavInfo(true, nbPages, "Cancel");
3957
3958#ifdef HAVE_PIEZO_SOUND
3959 // Play notification sound
3960 io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3961#endif // HAVE_PIEZO_SOUND
3962
3963 displayGenericContextPage(0, true);
3964}
3965
3974void nbgl_useCaseSpinner(const char *text)
3975{
3976 // if the previous Use Case was not Spinner, fresh start
3977 if (genericContext.type != USE_CASE_SPINNER) {
3978 memset(&genericContext, 0, sizeof(genericContext));
3979 genericContext.type = USE_CASE_SPINNER;
3980 nbgl_layoutDescription_t layoutDescription = {0};
3981
3982 layoutDescription.withLeftBorder = true;
3983
3984 genericContext.backgroundLayout = nbgl_layoutGet(&layoutDescription);
3985
3987 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
3988
3989 nbgl_layoutDraw(genericContext.backgroundLayout);
3991 }
3992 else {
3993 // otherwise increment spinner
3994 genericContext.spinnerPosition++;
3995 // there are only NB_SPINNER_POSITIONSpositions
3996 if (genericContext.spinnerPosition == NB_SPINNER_POSITIONS) {
3997 genericContext.spinnerPosition = 0;
3998 }
3999 int ret = nbgl_layoutUpdateSpinner(
4000 genericContext.backgroundLayout, text, NULL, genericContext.spinnerPosition);
4001 if (ret == 1) {
4003 }
4004 else if (ret == 2) {
4006 }
4007 }
4008}
4009
4010#ifdef NBGL_KEYPAD
4030void nbgl_useCaseKeypadDigits(const char *title,
4031 uint8_t minDigits,
4032 uint8_t maxDigits,
4033 uint8_t backToken,
4034 bool shuffled,
4035 tune_index_e tuneId,
4036 nbgl_pinValidCallback_t validatePinCallback,
4037 nbgl_layoutTouchCallback_t actionCallback)
4038{
4039 keypadGenericUseCase(title,
4040 minDigits,
4041 maxDigits,
4042 backToken,
4043 shuffled,
4044 false,
4045 tuneId,
4046 validatePinCallback,
4047 actionCallback);
4048}
4068void nbgl_useCaseKeypadPIN(const char *title,
4069 uint8_t minDigits,
4070 uint8_t maxDigits,
4071 uint8_t backToken,
4072 bool shuffled,
4073 tune_index_e tuneId,
4074 nbgl_pinValidCallback_t validatePinCallback,
4075 nbgl_layoutTouchCallback_t actionCallback)
4076{
4077 keypadGenericUseCase(title,
4078 minDigits,
4079 maxDigits,
4080 backToken,
4081 shuffled,
4082 true,
4083 tuneId,
4084 validatePinCallback,
4085 actionCallback);
4086}
4087#endif // NBGL_KEYPAD
4088
4089#endif // HAVE_SE_TOUCH
4090#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:86
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:1650
#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:1660
#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:1676
#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)
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)
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)
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)
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