Embedded SDK
Embedded SDK
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 blindSigningContext sharedContext.blindSigning
53 
54 /**********************
55  * TYPEDEFS
56  **********************/
57 enum {
58  BACK_TOKEN = 0,
59  NEXT_TOKEN,
60  QUIT_TOKEN,
61  NAV_TOKEN,
62  SKIP_TOKEN,
63  CONTINUE_TOKEN,
64  ADDRESS_QRCODE_BUTTON_TOKEN,
65  ACTION_BUTTON_TOKEN,
66  CHOICE_TOKEN,
67  DETAILS_BUTTON_TOKEN,
68  CONFIRM_TOKEN,
69  REJECT_TOKEN,
70  VALUE_ALIAS_TOKEN,
71  INFO_ALIAS_TOKEN,
72  BLIND_WARNING_TOKEN,
73  TIP_BOX_TOKEN
74 };
75 
76 typedef enum {
77  REVIEW_NAV = 0,
78  SETTINGS_NAV,
79  GENERIC_NAV,
80  STREAMING_NAV
81 } NavType_t;
82 
83 typedef struct DetailsContext_s {
84  uint8_t nbPages;
85  uint8_t currentPage;
86  bool wrapping;
87  const char *tag;
88  const char *value;
89  const char *nextPageStart;
90 } DetailsContext_t;
91 
92 typedef struct AddressConfirmationContext_s {
93  nbgl_layoutTagValue_t tagValuePair;
94  nbgl_layout_t modalLayout;
95 } AddressConfirmationContext_t;
96 
97 #ifdef NBGL_KEYPAD
98 typedef struct KeypadContext_s {
99  uint8_t pinEntry[KEYPAD_MAX_DIGITS];
100  uint8_t pinLen;
101  uint8_t pinMinDigits;
102  uint8_t pinMaxDigits;
103  nbgl_layout_t *layoutCtx;
104  bool hidden;
105 } KeypadContext_t;
106 #endif
107 
108 typedef struct BlindSigningContext_s {
109  bool isStreaming;
110  nbgl_operationType_t operationType;
111  const nbgl_contentTagValueList_t *tagValueList;
112  const nbgl_icon_details_t *icon;
113  const char *reviewTitle;
114  const char *reviewSubTitle;
115  const char *finishTitle;
116  const nbgl_tipBox_t *tipBox;
117  nbgl_choiceCallback_t choiceCallback;
118  nbgl_layout_t *layoutCtx;
119 } BlindSigningContext_t;
120 
121 // this union is intended to save RAM for context storage
122 // indeed, these 2 contexts cannot happen simultaneously
123 typedef union {
124 #ifdef NBGL_KEYPAD
125  KeypadContext_t keypad;
126 #endif
127  BlindSigningContext_t blindSigning;
128 } SharedContext_t;
129 
130 typedef struct {
131  nbgl_genericContents_t genericContents;
132  int8_t currentContentIdx;
133  uint8_t currentContentElementNb;
134  uint8_t currentElementIdx;
135  bool hasStartingContent;
136  bool hasFinishingContent;
137  const char *detailsItem;
138  const char *detailsvalue;
139  bool detailsWrapping;
141  *currentPairs; // to be used to retrieve the pairs with value alias
143  currentCallback; // to be used to retrieve the pairs with value alias
144 
145  nbgl_layout_t modalLayout;
146 } GenericContext_t;
147 
148 typedef struct {
149  const char *appName;
150  const nbgl_icon_details_t *appIcon;
151  const char *tagline;
152  const nbgl_genericContents_t *settingContents;
153  const nbgl_contentInfoList_t *infosList;
154  nbgl_homeAction_t homeAction;
155  nbgl_callback_t quitCallback;
156 } nbgl_homeAndSettingsContext_t;
157 
158 typedef struct {
159  nbgl_operationType_t operationType;
160  nbgl_choiceCallback_t choiceCallback;
161 } nbgl_reviewContext_t;
162 
163 typedef struct {
164  nbgl_operationType_t operationType;
165  nbgl_choiceCallback_t choiceCallback;
166  nbgl_callback_t skipCallback;
167  const nbgl_icon_details_t *icon;
168  uint8_t stepPageNb;
169 } nbgl_reviewStreamingContext_t;
170 
171 typedef union {
172  nbgl_homeAndSettingsContext_t homeAndSettings;
173  nbgl_reviewContext_t review;
174  nbgl_reviewStreamingContext_t reviewStreaming;
175 } nbgl_BundleNavContext_t;
176 
177 /**********************
178  * STATIC VARIABLES
179  **********************/
180 
181 // char buffers to build some strings
182 static char appDescription[APP_DESCRIPTION_MAX_LEN];
183 
184 // multi-purposes callbacks
185 static nbgl_callback_t onQuit;
186 static nbgl_callback_t onContinue;
187 static nbgl_callback_t onAction;
188 static nbgl_navCallback_t onNav;
189 static nbgl_layoutTouchCallback_t onControls;
190 static nbgl_contentActionCallback_t onContentAction;
191 static nbgl_choiceCallback_t onChoice;
192 static nbgl_callback_t onModalConfirm;
193 #ifdef NBGL_KEYPAD
194 static nbgl_pinValidCallback_t onValidatePin;
195 #endif
196 
197 // contexts for background and modal pages
198 static nbgl_page_t *pageContext;
199 static nbgl_page_t *modalPageContext;
200 
201 // context for pages
202 static const char *pageTitle;
203 
204 // context for tip-box
205 static const char *tipBoxModalTitle;
206 static nbgl_contentInfoList_t tipBoxInfoList;
207 
208 // context for navigation use case
209 static nbgl_pageNavigationInfo_t navInfo;
210 static bool forwardNavOnly;
211 static NavType_t navType;
212 
213 static DetailsContext_t detailsContext;
214 
215 // multi-purpose context shared for non-concurrent usages
216 static SharedContext_t sharedContext;
217 
218 // context for address review
219 static AddressConfirmationContext_t addressConfirmationContext;
220 
221 // contexts for generic navigation
222 static GenericContext_t genericContext;
223 static nbgl_content_t
224  localContentsList[3]; // 3 needed for nbgl_useCaseReview (starting page / tags / final page)
225 static uint8_t genericContextPagesInfo[MAX_PAGE_NB / PAGES_PER_UINT8];
226 
227 // contexts for bundle navigation
228 static nbgl_BundleNavContext_t bundleNavContext;
229 
230 // indexed by nbgl_contentType_t
231 static const uint8_t nbMaxElementsPerContentType[] = {
232  1, // CENTERED_INFO
233  1, // EXTENDED_CENTER
234  1, // INFO_LONG_PRESS
235  1, // INFO_BUTTON
236  1, // TAG_VALUE_LIST (computed dynamically)
237  1, // TAG_VALUE_DETAILS
238  1, // TAG_VALUE_CONFIRM
239  3, // SWITCHES_LIST (computed dynamically)
240  3, // INFOS_LIST (computed dynamically)
241  5, // CHOICES_LIST (computed dynamically)
242  5, // BARS_LIST (computed dynamically)
243 };
244 
245 #ifdef NBGL_QRCODE
246 /* buffer to store reduced address under QR Code */
247 static char reducedAddress[QRCODE_REDUCED_ADDR_LEN];
248 #endif // NBGL_QRCODE
249 
250 /**********************
251  * STATIC FUNCTIONS
252  **********************/
253 static void displayReviewPage(uint8_t page, bool forceFullRefresh);
254 static void displayDetailsPage(uint8_t page, bool forceFullRefresh);
255 static void displayFullValuePage(const char *backText,
256  const char *aliasText,
257  const nbgl_contentValueExt_t *extension);
258 static void displayBlindWarning(void);
259 static void displayTipBoxModal(void);
260 static void displaySettingsPage(uint8_t page, bool forceFullRefresh);
261 static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh);
262 static void pageCallback(int token, uint8_t index);
263 #ifdef NBGL_QRCODE
264 static void displayAddressQRCode(void);
265 static void addressLayoutTouchCallbackQR(int token, uint8_t index);
266 #endif // NBGL_QRCODE
267 static void modalLayoutTouchCallback(int token, uint8_t index);
268 static void displaySkipWarning(void);
269 
270 static void bundleNavStartHome(void);
271 static void bundleNavStartSettingsAtPage(uint8_t initSettingPage);
272 static void bundleNavStartSettings(void);
273 
274 static void bundleNavReviewStreamingChoice(bool confirm);
275 static void blindSigningWarning(void);
276 static void useCaseReview(nbgl_operationType_t operationType,
277  const nbgl_contentTagValueList_t *tagValueList,
278  const nbgl_icon_details_t *icon,
279  const char *reviewTitle,
280  const char *reviewSubTitle,
281  const char *finishTitle,
282  const nbgl_tipBox_t *tipBox,
283  nbgl_choiceCallback_t choiceCallback,
284  bool isLight,
285  bool playNotifSound);
286 static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
287  const nbgl_icon_details_t *icon,
288  const char *reviewTitle,
289  const char *reviewSubTitle,
290  nbgl_choiceCallback_t choiceCallback,
291  bool playNotifSound);
292 static void useCaseHomeExt(const char *appName,
293  const nbgl_icon_details_t *appIcon,
294  const char *tagline,
295  bool withSettings,
296  nbgl_homeAction_t *homeAction,
297  nbgl_callback_t topRightCallback,
298  nbgl_callback_t quitCallback);
299 static void displayDetails(const char *tag, const char *value, bool wrapping);
300 
301 static void reset_callbacks(void)
302 {
303  onQuit = NULL;
304  onContinue = NULL;
305  onAction = NULL;
306  onNav = NULL;
307  onControls = NULL;
308  onContentAction = NULL;
309  onChoice = NULL;
310  onModalConfirm = NULL;
311 #ifdef NBGL_KEYPAD
312  onValidatePin = NULL;
313 #endif
314 }
315 
316 // Helper to set genericContext page info
317 static void genericContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements, bool flag)
318 {
319  uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements) + SET_PAGE_FLAG(flag);
320 
321  genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
322  &= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
323  genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
324  |= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
325 }
326 
327 // Helper to get genericContext page info
328 static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool *flag)
329 {
330  uint8_t pageData = genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
331  >> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
332  if (nbElements != NULL) {
333  *nbElements = GET_PAGE_NB_ELEMENTS(pageData);
334  }
335  if (flag != NULL) {
336  *flag = GET_PAGE_FLAG(pageData);
337  }
338 }
339 
340 // Simple helper to get the number of elements inside a nbgl_content_t
341 static uint8_t getContentNbElement(const nbgl_content_t *content)
342 {
343  switch (content->type) {
344  case TAG_VALUE_LIST:
345  return content->content.tagValueList.nbPairs;
346  case SWITCHES_LIST:
347  return content->content.switchesList.nbSwitches;
348  case INFOS_LIST:
349  return content->content.infosList.nbInfos;
350  case CHOICES_LIST:
351  return content->content.choicesList.nbChoices;
352  case BARS_LIST:
353  return content->content.barsList.nbBars;
354  default:
355  return 1;
356  }
357 }
358 
359 // Helper to retrieve the content inside a nbgl_genericContents_t using
360 // either the contentsList or using the contentGetterCallback
361 static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
362  int8_t contentIdx,
363  nbgl_content_t *content)
364 {
365  if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
366  LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
367  return NULL;
368  }
369 
370  if (genericContents->callbackCallNeeded) {
371  // Retrieve content through callback, but first memset the content.
372  memset(content, 0, sizeof(nbgl_content_t));
373  genericContents->contentGetterCallback(contentIdx, content);
374  return content;
375  }
376  else {
377  // Retrieve content through list
378  return PIC(&genericContents->contentsList[contentIdx]);
379  }
380 }
381 
382 static void prepareNavInfo(bool isReview, uint8_t nbPages, const char *rejectText)
383 {
384  memset(&navInfo, 0, sizeof(navInfo));
385 
386  navInfo.nbPages = nbPages;
387  navInfo.tuneId = TUNE_TAP_CASUAL;
388  navInfo.progressIndicator = false;
389  navInfo.navType = NAV_WITH_BUTTONS;
390 
391  if (isReview == false) {
392  navInfo.navWithButtons.navToken = NAV_TOKEN;
393  navInfo.navWithButtons.backButton = true;
394  }
395  else {
396  navInfo.quitToken = REJECT_TOKEN;
397  navInfo.navWithButtons.quitText = rejectText;
398  navInfo.navWithButtons.navToken = NAV_TOKEN;
399  navInfo.navWithButtons.backButton
400  = ((navType == STREAMING_NAV) && (nbPages < 2)) ? false : true;
401  navInfo.navWithButtons.visiblePageIndicator = (navType != STREAMING_NAV);
402  }
403 }
404 
405 static void prepareReviewFirstPage(nbgl_contentCenter_t *contentCenter,
406  const nbgl_icon_details_t *icon,
407  const char *reviewTitle,
408  const char *reviewSubTitle)
409 {
410  contentCenter->icon = icon;
411  contentCenter->title = reviewTitle;
412  contentCenter->description = reviewSubTitle;
413  contentCenter->subText = "Swipe to review";
414  contentCenter->smallTitle = NULL;
415  contentCenter->iconHug = 0;
416  contentCenter->padding = false;
417 }
418 
419 static void prepareReviewLastPage(nbgl_contentInfoLongPress_t *infoLongPress,
420  const nbgl_icon_details_t *icon,
421  const char *finishTitle)
422 {
423  infoLongPress->text = finishTitle;
424  infoLongPress->icon = icon;
425  infoLongPress->longPressText = "Hold to sign";
426  infoLongPress->longPressToken = CONFIRM_TOKEN;
427 }
428 
429 static void prepareReviewLightLastPage(nbgl_contentInfoButton_t *infoButton,
430  const nbgl_icon_details_t *icon,
431  const char *finishTitle)
432 {
433  infoButton->text = finishTitle;
434  infoButton->icon = icon;
435  infoButton->buttonText = "Approve";
436  infoButton->buttonToken = CONFIRM_TOKEN;
437 }
438 
439 static const char *getRejectReviewText(nbgl_operationType_t operationType)
440 {
441  UNUSED(operationType);
442  return "Reject";
443 }
444 
445 // function called when navigating (or exiting) modal details pages
446 // or when skip choice is displayed
447 static void pageModalCallback(int token, uint8_t index)
448 {
449  LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index);
450 
451  if (token == INFO_ALIAS_TOKEN) {
452  // the icon next to info type alias has been touched
453  displayFullValuePage(tipBoxInfoList.infoTypes[index],
454  tipBoxInfoList.infoContents[index],
455  &tipBoxInfoList.infoExtensions[index]);
456  return;
457  }
458  nbgl_pageRelease(modalPageContext);
459  modalPageContext = NULL;
460  if (token == NAV_TOKEN) {
461  if (index == EXIT_PAGE) {
462  // redraw the background layer
464  nbgl_refresh();
465  }
466  else {
467  displayDetailsPage(index, false);
468  }
469  }
470  if (token == QUIT_TOKEN) {
471  // redraw the background layer
473  nbgl_refresh();
474  }
475  else if (token == SKIP_TOKEN) {
476  if (index == 0) {
477  // display the last forward only review page, whatever it is
478  displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true);
479  }
480  else {
481  // display background, which should be the page where skip has been touched
483  nbgl_refresh();
484  }
485  }
486  else if (token == CHOICE_TOKEN) {
487  if (index == 0) {
488  if (onModalConfirm != NULL) {
489  onModalConfirm();
490  }
491  }
492  else {
493  // display background, which should be the page where skip has been touched
495  nbgl_refresh();
496  }
497  }
498 }
499 
500 // generic callback for all pages except modal
501 static void pageCallback(int token, uint8_t index)
502 {
503  LOG_DEBUG(USE_CASE_LOGGER, "pageCallback, token = %d, index = %d\n", token, index);
504  if (token == QUIT_TOKEN) {
505  if (onQuit != NULL) {
506  onQuit();
507  }
508  }
509  else if (token == CONTINUE_TOKEN) {
510  if (onContinue != NULL) {
511  onContinue();
512  }
513  }
514  else if (token == CHOICE_TOKEN) {
515  if (onChoice != NULL) {
516  onChoice((index == 0) ? true : false);
517  }
518  }
519  else if (token == ACTION_BUTTON_TOKEN) {
520  if ((index == 0) && (onAction != NULL)) {
521  onAction();
522  }
523  else if ((index == 1) && (onQuit != NULL)) {
524  onQuit();
525  }
526  }
527 #ifdef NBGL_QRCODE
528  else if (token == ADDRESS_QRCODE_BUTTON_TOKEN) {
529  displayAddressQRCode();
530  }
531 #endif // NBGL_QRCODE
532  else if (token == CONFIRM_TOKEN) {
533  if (onChoice != NULL) {
534  onChoice(true);
535  }
536  }
537  else if (token == REJECT_TOKEN) {
538  if (onChoice != NULL) {
539  onChoice(false);
540  }
541  }
542  else if (token == DETAILS_BUTTON_TOKEN) {
543  displayDetails(genericContext.detailsItem,
544  genericContext.detailsvalue,
545  genericContext.detailsWrapping);
546  }
547  else if (token == NAV_TOKEN) {
548  if (index == EXIT_PAGE) {
549  if (onQuit != NULL) {
550  onQuit();
551  }
552  }
553  else {
554  if (navType == GENERIC_NAV || navType == STREAMING_NAV) {
555  displayGenericContextPage(index, false);
556  }
557  else if (navType == REVIEW_NAV) {
558  displayReviewPage(index, false);
559  }
560  else {
561  displaySettingsPage(index, false);
562  }
563  }
564  }
565  else if (token == NEXT_TOKEN) {
566  if (onNav != NULL) {
567  displayReviewPage(navInfo.activePage + 1, false);
568  }
569  else {
570  displayGenericContextPage(navInfo.activePage + 1, false);
571  }
572  }
573  else if (token == BACK_TOKEN) {
574  if (onNav != NULL) {
575  displayReviewPage(navInfo.activePage - 1, true);
576  }
577  else {
578  displayGenericContextPage(navInfo.activePage - 1, false);
579  }
580  }
581  else if (token == SKIP_TOKEN) {
582  // display a modal warning to confirm skip
583  displaySkipWarning();
584  }
585  else if (token == VALUE_ALIAS_TOKEN) {
586  // the icon next to value alias has been touched
587  const nbgl_contentTagValue_t *pair;
588  if (genericContext.currentPairs != NULL) {
589  pair = &genericContext.currentPairs[genericContext.currentElementIdx + index];
590  }
591  else {
592  pair = genericContext.currentCallback(genericContext.currentElementIdx + index);
593  }
594  displayFullValuePage(pair->item, pair->value, pair->extension);
595  }
596  else if (token == BLIND_WARNING_TOKEN) {
597  displayBlindWarning();
598  }
599  else if (token == TIP_BOX_TOKEN) {
600  displayTipBoxModal();
601  }
602  else { // probably a control provided by caller
603  if (onContentAction != NULL) {
604  onContentAction(token, index, navInfo.activePage);
605  }
606  if (onControls != NULL) {
607  onControls(token, index);
608  }
609  }
610 }
611 
612 // callback used for confirmation
613 static void tickerCallback(void)
614 {
615  nbgl_pageRelease(pageContext);
616  if (onQuit != NULL) {
617  onQuit();
618  }
619 }
620 
621 // function used to display the current page in review
622 static void displaySettingsPage(uint8_t page, bool forceFullRefresh)
623 {
624  nbgl_pageContent_t content = {0};
625 
626  if ((onNav == NULL) || (onNav(page, &content) == false)) {
627  return;
628  }
629 
630  // override some fields
631  content.title = pageTitle;
632  content.isTouchableTitle = true;
633  content.titleToken = QUIT_TOKEN;
634  content.tuneId = TUNE_TAP_CASUAL;
635 
636  navInfo.activePage = page;
637  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
638 
639  if (forceFullRefresh) {
641  }
642  else {
644  }
645 }
646 
647 // function used to display the current page in review
648 static void displayReviewPage(uint8_t page, bool forceFullRefresh)
649 {
650  nbgl_pageContent_t content = {0};
651 
652  // ensure the page is valid
653  if ((navInfo.nbPages != 0) && (page >= (navInfo.nbPages))) {
654  return;
655  }
656  navInfo.activePage = page;
657  if ((onNav == NULL) || (onNav(navInfo.activePage, &content) == false)) {
658  return;
659  }
660 
661  // override some fields
662  content.title = NULL;
663  content.isTouchableTitle = false;
664  content.tuneId = TUNE_TAP_CASUAL;
665 
666  if (content.type == INFO_LONG_PRESS) { // last page
667  // for forward only review without known length...
668  // if we don't do that we cannot remove the '>' in the navigation bar at the last page
669  navInfo.nbPages = navInfo.activePage + 1;
670  content.infoLongPress.longPressToken = CONFIRM_TOKEN;
671  if (forwardNavOnly) {
672  // remove the "Skip" button
673  navInfo.skipText = NULL;
674  }
675  }
676 
677  // override smallCaseForValue for tag/value types to false
678  if (content.type == TAG_VALUE_DETAILS) {
679  content.tagValueDetails.tagValueList.smallCaseForValue = false;
680  // the maximum displayable number of lines for value is NB_MAX_LINES_IN_REVIEW (without More
681  // button)
682  content.tagValueDetails.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
683  }
684  else if (content.type == TAG_VALUE_LIST) {
685  content.tagValueList.smallCaseForValue = false;
686  }
687  else if (content.type == TAG_VALUE_CONFIRM) {
688  content.tagValueConfirm.tagValueList.smallCaseForValue = false;
689  // use confirm token for black button
690  content.tagValueConfirm.confirmationToken = CONFIRM_TOKEN;
691  }
692 
693  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
694 
695  if (forceFullRefresh) {
697  }
698  else {
700  }
701 }
702 
703 // Helper that does the computing of which nbgl_content_t and which element in the content should be
704 // displayed for the next generic context navigation page
705 static const nbgl_content_t *genericContextComputeNextPageParams(uint8_t pageIdx,
706  nbgl_content_t *content,
707  uint8_t *p_nbElementsInNextPage,
708  bool *p_flag)
709 {
710  int8_t nextContentIdx = genericContext.currentContentIdx;
711  int16_t nextElementIdx = genericContext.currentElementIdx;
712  uint8_t nbElementsInNextPage;
713 
714  // Retrieve info on the next page
715  genericContextGetPageInfo(pageIdx, &nbElementsInNextPage, p_flag);
716  *p_nbElementsInNextPage = nbElementsInNextPage;
717 
718  // Handle forward navigation:
719  // add to current index the number of pairs of the currently active page
720  if (pageIdx > navInfo.activePage) {
721  uint8_t nbElementsInCurrentPage;
722 
723  genericContextGetPageInfo(navInfo.activePage, &nbElementsInCurrentPage, NULL);
724  nextElementIdx += nbElementsInCurrentPage;
725 
726  // Handle case where the content to be displayed is in the next content
727  // In such case start at element index 0.
728  // If currentContentElementNb == 0, means not initialized, so skip
729  if ((nextElementIdx >= genericContext.currentContentElementNb)
730  && (genericContext.currentContentElementNb > 0)) {
731  nextContentIdx += 1;
732  nextElementIdx = 0;
733  }
734  }
735 
736  // Handle backward navigation:
737  // add to current index the number of pairs of the currently active page
738  if (pageIdx < navInfo.activePage) {
739  // Backward navigation: remove to current index the number of pairs of the current page
740  nextElementIdx -= nbElementsInNextPage;
741 
742  // Handle case where the content to be displayed is in the previous content
743  // In such case set a negative number as element index so that it is handled
744  // later once the previous content is accessible so that its elements number
745  // can be retrieved.
746  if (nextElementIdx < 0) {
747  nextContentIdx -= 1;
748  nextElementIdx = -nbElementsInNextPage;
749  }
750  }
751 
752  const nbgl_content_t *p_content;
753  // Retrieve next content
754  if ((nextContentIdx == -1) && (genericContext.hasStartingContent)) {
755  p_content = &STARTING_CONTENT;
756  }
757  else if ((nextContentIdx == genericContext.genericContents.nbContents)
758  && (genericContext.hasFinishingContent)) {
759  p_content = &FINISHING_CONTENT;
760  }
761  else {
762  p_content = getContentAtIdx(&genericContext.genericContents, nextContentIdx, content);
763 
764  if (p_content == NULL) {
765  LOG_DEBUG(USE_CASE_LOGGER, "Fail to retrieve content\n");
766  return NULL;
767  }
768  }
769 
770  // Handle cases where we are going to display data from a new content:
771  // - navigation to a previous or to the next content
772  // - First page display (genericContext.currentContentElementNb == 0)
773  //
774  // In such case we need to:
775  // - Update genericContext.currentContentIdx
776  // - Update genericContext.currentContentElementNb
777  // - Update onContentAction callback
778  // - Update nextElementIdx in case it was previously not calculable
779  if ((nextContentIdx != genericContext.currentContentIdx)
780  || (genericContext.currentContentElementNb == 0)) {
781  genericContext.currentContentIdx = nextContentIdx;
782  genericContext.currentContentElementNb = getContentNbElement(p_content);
783  onContentAction = PIC(p_content->contentActionCallback);
784  if (nextElementIdx < 0) {
785  nextElementIdx = genericContext.currentContentElementNb + nextElementIdx;
786  }
787  }
788 
789  // Sanity check
790  if ((nextElementIdx < 0) || (nextElementIdx >= genericContext.currentContentElementNb)) {
792  "Invalid element index %d / %d\n",
793  nextElementIdx,
794  genericContext.currentContentElementNb);
795  return NULL;
796  }
797 
798  // Update genericContext elements
799  genericContext.currentElementIdx = nextElementIdx;
800  navInfo.activePage = pageIdx;
801 
802  return p_content;
803 }
804 
805 // Helper that generates a nbgl_pageContent_t to be displayed for the next generic context
806 // navigation page
807 static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
808  uint8_t nbElementsInPage,
809  bool flag,
810  nbgl_pageContent_t *pageContent)
811 {
812  uint8_t nextElementIdx = genericContext.currentElementIdx;
813 
814  pageContent->title = pageTitle;
815  pageContent->isTouchableTitle = false;
816  pageContent->titleToken = QUIT_TOKEN;
817  pageContent->tuneId = TUNE_TAP_CASUAL;
818 
819  pageContent->type = p_content->type;
820  switch (pageContent->type) {
821  case CENTERED_INFO:
822  memcpy(&pageContent->centeredInfo,
823  &p_content->content.centeredInfo,
824  sizeof(pageContent->centeredInfo));
825  break;
826  case EXTENDED_CENTER:
827  memcpy(&pageContent->extendedCenter,
828  &p_content->content.extendedCenter,
829  sizeof(pageContent->extendedCenter));
830  break;
831  case INFO_LONG_PRESS:
832  memcpy(&pageContent->infoLongPress,
833  &p_content->content.infoLongPress,
834  sizeof(pageContent->infoLongPress));
835  break;
836 
837  case INFO_BUTTON:
838  memcpy(&pageContent->infoButton,
839  &p_content->content.infoButton,
840  sizeof(pageContent->infoButton));
841  break;
842 
843  case TAG_VALUE_LIST: {
844  nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
845 
846  // memorize pairs (or callback) for usage when alias is used
847  genericContext.currentPairs = p_content->content.tagValueList.pairs;
848  genericContext.currentCallback = p_content->content.tagValueList.callback;
849 
850  if (flag) {
851  // Flag can be set if the pair is too long to fit or because it needs
852  // to be displayed as centered info.
853 
854  // First retrieve the pair
855  const nbgl_layoutTagValue_t *pair;
856 
857  if (p_content->content.tagValueList.pairs != NULL) {
858  pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
859  }
860  else {
861  pair = PIC(p_content->content.tagValueList.callback(nextElementIdx));
862  }
863 
864  if (pair->centeredInfo) {
865  pageContent->type = EXTENDED_CENTER;
866  prepareReviewFirstPage(&pageContent->extendedCenter.contentCenter,
867  pair->valueIcon,
868  pair->item,
869  pair->value);
870 
871  // Skip population of nbgl_contentTagValueList_t structure
872  p_tagValueList = NULL;
873  }
874  else {
875  // if the pair is too long to fit, we use a TAG_VALUE_DETAILS content
876  pageContent->type = TAG_VALUE_DETAILS;
877  pageContent->tagValueDetails.detailsButtonText = "More";
878  pageContent->tagValueDetails.detailsButtonIcon = NULL;
879  pageContent->tagValueDetails.detailsButtonToken = DETAILS_BUTTON_TOKEN;
880 
881  p_tagValueList = &pageContent->tagValueDetails.tagValueList;
882 
883  // Backup pair info for easy data access upon click on button
884  genericContext.detailsItem = pair->item;
885  genericContext.detailsvalue = pair->value;
886  genericContext.detailsWrapping = p_content->content.tagValueList.wrapping;
887  }
888  }
889 
890  if (p_tagValueList != NULL) {
891  p_tagValueList->nbPairs = nbElementsInPage;
892  p_tagValueList->token = p_content->content.tagValueList.token;
893  if (p_content->content.tagValueList.pairs != NULL) {
894  p_tagValueList->pairs
895  = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
896  // parse pairs to check if any contains an alias for value
897  for (uint8_t i = 0; i < nbElementsInPage; i++) {
898  if (p_tagValueList->pairs[i].aliasValue) {
899  p_tagValueList->token = VALUE_ALIAS_TOKEN;
900  break;
901  }
902  }
903  }
904  else {
905  p_tagValueList->pairs = NULL;
906  p_tagValueList->callback = p_content->content.tagValueList.callback;
907  p_tagValueList->startIndex = nextElementIdx;
908  // parse pairs to check if any contains an alias for value
909  for (uint8_t i = 0; i < nbElementsInPage; i++) {
910  const nbgl_layoutTagValue_t *pair
911  = PIC(p_content->content.tagValueList.callback(nextElementIdx + i));
912  if (pair->aliasValue) {
913  p_tagValueList->token = VALUE_ALIAS_TOKEN;
914  break;
915  }
916  }
917  }
918  p_tagValueList->smallCaseForValue = false;
919  p_tagValueList->nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
920  p_tagValueList->wrapping = p_content->content.tagValueList.wrapping;
921  }
922 
923  break;
924  }
925  case TAG_VALUE_CONFIRM:
926  memcpy(&pageContent->tagValueConfirm,
927  &p_content->content.tagValueConfirm,
928  sizeof(pageContent->tagValueConfirm));
929  break;
930  case SWITCHES_LIST:
931  pageContent->switchesList.nbSwitches = nbElementsInPage;
932  pageContent->switchesList.switches
933  = PIC(&p_content->content.switchesList.switches[nextElementIdx]);
934  break;
935  case INFOS_LIST:
936  pageContent->infosList.nbInfos = nbElementsInPage;
937  pageContent->infosList.infoTypes
938  = PIC(&p_content->content.infosList.infoTypes[nextElementIdx]);
939  pageContent->infosList.infoContents
940  = PIC(&p_content->content.infosList.infoContents[nextElementIdx]);
941  break;
942  case CHOICES_LIST:
943  memcpy(&pageContent->choicesList,
944  &p_content->content.choicesList,
945  sizeof(pageContent->choicesList));
946  pageContent->choicesList.nbChoices = nbElementsInPage;
947  pageContent->choicesList.names
948  = PIC(&p_content->content.choicesList.names[nextElementIdx]);
949  if ((p_content->content.choicesList.initChoice >= nextElementIdx)
950  && (p_content->content.choicesList.initChoice
951  < nextElementIdx + nbElementsInPage)) {
952  pageContent->choicesList.initChoice
953  = p_content->content.choicesList.initChoice - nextElementIdx;
954  }
955  else {
956  pageContent->choicesList.initChoice = nbElementsInPage;
957  }
958  break;
959  case BARS_LIST:
960  pageContent->barsList.nbBars = nbElementsInPage;
961  pageContent->barsList.barTexts
962  = PIC(&p_content->content.barsList.barTexts[nextElementIdx]);
963  pageContent->barsList.tokens = PIC(&p_content->content.barsList.tokens[nextElementIdx]);
964  pageContent->barsList.tuneId = p_content->content.barsList.tuneId;
965  break;
966  default:
967  LOG_DEBUG(USE_CASE_LOGGER, "Unsupported type %d\n", pageContent->type);
968  return false;
969  }
970 
971  bool isFirstOrLastPage
972  = ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
973  || (p_content->type == INFO_LONG_PRESS);
974  bool isStreamingNavAndBlindOperation
975  = (navType == STREAMING_NAV)
976  && (bundleNavContext.reviewStreaming.operationType & BLIND_OPERATION);
977  bool isGenericNavAndBlindOperation
978  = (navType == GENERIC_NAV) && (bundleNavContext.review.operationType & BLIND_OPERATION);
979 
980  // if first or last page of review and blind operation, add the warning top-right button
981  if (isFirstOrLastPage && (isStreamingNavAndBlindOperation || isGenericNavAndBlindOperation)) {
982  pageContent->topRightIcon = &WARNING_ICON;
983  pageContent->topRightToken = BLIND_WARNING_TOKEN;
984  }
985 
986  return true;
987 }
988 
989 // function used to display the current page in generic context navigation mode
990 static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh)
991 {
992  // Retrieve next page parameters
993  nbgl_content_t content;
994  uint8_t nbElementsInPage;
995  bool flag;
996  const nbgl_content_t *p_content = NULL;
997 
998  if (navType == STREAMING_NAV) {
999  if (pageIdx == LAST_PAGE_FOR_REVIEW) {
1000  if (bundleNavContext.reviewStreaming.skipCallback != NULL) {
1001  bundleNavContext.reviewStreaming.skipCallback();
1002  }
1003  return;
1004  }
1005  else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) {
1006  bundleNavReviewStreamingChoice(true);
1007  return;
1008  }
1009  }
1010 
1011  if (navInfo.activePage == pageIdx) {
1012  p_content
1013  = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1014  }
1015  else if (navInfo.activePage < pageIdx) {
1016  // Support going more than one step forward.
1017  // It occurs when initializing a navigation on an arbitrary page
1018  for (int i = navInfo.activePage + 1; i <= pageIdx; i++) {
1019  p_content = genericContextComputeNextPageParams(i, &content, &nbElementsInPage, &flag);
1020  }
1021  }
1022  else {
1023  if (pageIdx - navInfo.activePage > 1) {
1024  // We don't support going more than one step backward as it doesn't occurs for now?
1025  LOG_DEBUG(USE_CASE_LOGGER, "Unsupported navigation\n");
1026  return;
1027  }
1028  p_content
1029  = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1030  }
1031 
1032  if (p_content == NULL) {
1033  return;
1034  }
1035 
1036  // Create next page content
1037  nbgl_pageContent_t pageContent = {0};
1038  if (!genericContextPreparePageContent(p_content, nbElementsInPage, flag, &pageContent)) {
1039  return;
1040  }
1041 
1042  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &pageContent);
1043 
1044  if (forceFullRefresh) {
1046  }
1047  else {
1049  }
1050 }
1051 
1052 // from the current details context, return a pointer on the details at the given page
1053 static const char *getDetailsPageAt(uint8_t detailsPage)
1054 {
1055  uint8_t page = 0;
1056  const char *currentChar = detailsContext.value;
1057  while (page < detailsPage) {
1058  uint16_t nbLines
1059  = nbgl_getTextNbLinesInWidth(SMALL_BOLD_FONT, currentChar, AVAILABLE_WIDTH, false);
1060  if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1061  uint16_t len;
1062  nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1063  currentChar,
1066  &len,
1067  detailsContext.wrapping);
1068  len -= 3;
1069  currentChar = currentChar + len;
1070  }
1071  page++;
1072  }
1073  return currentChar;
1074 }
1075 
1076 // function used to display the current page in details review mode
1077 static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh)
1078 {
1079  static nbgl_layoutTagValue_t currentPair;
1080  nbgl_pageNavigationInfo_t info = {.activePage = detailsPage,
1081  .nbPages = detailsContext.nbPages,
1082  .navType = NAV_WITH_BUTTONS,
1083  .quitToken = QUIT_TOKEN,
1084  .navWithButtons.navToken = NAV_TOKEN,
1085  .navWithButtons.quitButton = true,
1086  .navWithButtons.backButton = true,
1087  .navWithButtons.quitText = NULL,
1088  .progressIndicator = false,
1089  .tuneId = TUNE_TAP_CASUAL};
1090  nbgl_pageContent_t content = {.type = TAG_VALUE_LIST,
1091  .topRightIcon = NULL,
1092  .tagValueList.nbPairs = 1,
1093  .tagValueList.pairs = &currentPair,
1094  .tagValueList.smallCaseForValue = true,
1095  .tagValueList.wrapping = detailsContext.wrapping};
1096 
1097  if (modalPageContext != NULL) {
1098  nbgl_pageRelease(modalPageContext);
1099  }
1100  currentPair.item = detailsContext.tag;
1101  // if move backward or first page
1102  if (detailsPage <= detailsContext.currentPage) {
1103  // recompute current start from beginning
1104  currentPair.value = getDetailsPageAt(detailsPage);
1105  forceFullRefresh = true;
1106  }
1107  // else move forward
1108  else {
1109  currentPair.value = detailsContext.nextPageStart;
1110  }
1111  detailsContext.currentPage = detailsPage;
1113  SMALL_BOLD_FONT, currentPair.value, AVAILABLE_WIDTH, detailsContext.wrapping);
1114 
1115  if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1116  uint16_t len;
1117  nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1118  currentPair.value,
1121  &len,
1122  detailsContext.wrapping);
1123  len -= 3;
1124  // memorize next position to save processing
1125  detailsContext.nextPageStart = currentPair.value + len;
1126  // use special feature to keep only NB_MAX_LINES_IN_DETAILS lines and replace the last 3
1127  // chars by "..."
1128  content.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_DETAILS;
1129  }
1130  else {
1131  detailsContext.nextPageStart = NULL;
1132  content.tagValueList.nbMaxLinesForValue = 0;
1133  }
1134  if (info.nbPages == 1) {
1135  // if only one page, no navigation bar, and use a footer instead
1136  info.navWithButtons.quitText = "Close";
1137  }
1138  modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1139 
1140  if (forceFullRefresh) {
1142  }
1143  else {
1145  }
1146 }
1147 
1148 // function used to display the content of a full value, when touching an alias of a tag/value pair
1149 static void displayFullValuePage(const char *backText,
1150  const char *aliasText,
1151  const nbgl_contentValueExt_t *extension)
1152 {
1153  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1154  .withLeftBorder = true,
1155  .onActionCallback = &modalLayoutTouchCallback,
1156  .tapActionText = NULL};
1158  .separationLine = false,
1159  .backAndText.token = 0,
1160  .backAndText.tuneId = TUNE_TAP_CASUAL,
1161  .backAndText.text = PIC(backText)};
1162  genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1163  // add header with the tag part of the pair, to go back
1164  nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1165  // add either QR Code or full value text
1166  if (extension->aliasType == QR_CODE_ALIAS) {
1167 #ifdef NBGL_QRCODE
1168  nbgl_layoutQRCode_t qrCode
1169  = {.url = extension->fullValue,
1170  .text1 = (extension->title != NULL) ? extension->title : extension->fullValue,
1171  .text2 = extension->explanation,
1172  .centered = true,
1173  .offsetY = 0};
1174 
1175  nbgl_layoutAddQRCode(genericContext.modalLayout, &qrCode);
1176 #endif // NBGL_QRCODE
1177  }
1178  else {
1179  const char *info;
1180  // add full value text
1181  if (extension->aliasType == ENS_ALIAS) {
1182  info = "ENS names are resolved by Ledger backend.";
1183  }
1184  else if (extension->aliasType == ADDRESS_BOOK_ALIAS) {
1185  info = "This account label comes from your Address Book in Ledger Live.";
1186  }
1187  else {
1188  info = extension->explanation;
1189  }
1191  genericContext.modalLayout, aliasText, extension->fullValue, info);
1192  }
1193  // draw & refresh
1194  nbgl_layoutDraw(genericContext.modalLayout);
1195  nbgl_refresh();
1196 }
1197 
1198 // function used to display the modal warning when touching the alert symbol of a blind review
1199 static void displayBlindWarning(void)
1200 {
1201  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1202  .withLeftBorder = true,
1203  .onActionCallback = &modalLayoutTouchCallback,
1204  .tapActionText = NULL};
1206  .separationLine = false,
1207  .backAndText.token = 0,
1208  .backAndText.tuneId = TUNE_TAP_CASUAL,
1209  .backAndText.text = NULL};
1210  nbgl_layoutCenteredInfo_t centeredInfo
1211  = {.icon = NULL, .text3 = NULL, .style = LARGE_CASE_INFO, .offsetY = 0, .onTop = false};
1212  centeredInfo.text1 = "Security risk detected";
1213  centeredInfo.text2
1214  = "This transaction or message cannot be decoded fully. If you choose to sign, you could "
1215  "be authorizing malicious actions that can drain your wallet.\n\n"
1216  "Learn more: ledger.com/e8";
1217 
1218  genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1219  // add header with the tag part of the pair, to go back
1220  nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1221  // add full value text
1222  nbgl_layoutAddCenteredInfo(genericContext.modalLayout, &centeredInfo);
1223 
1224  // draw & refresh
1225  nbgl_layoutDraw(genericContext.modalLayout);
1226  nbgl_refresh();
1227 }
1228 
1229 // function used to display the modal containing tip-box infos
1230 static void displayTipBoxModal(void)
1231 {
1233  .nbPages = 1,
1234  .navType = NAV_WITH_BUTTONS,
1235  .quitToken = QUIT_TOKEN,
1236  .navWithButtons.navToken = NAV_TOKEN,
1237  .navWithButtons.quitButton = false,
1238  .navWithButtons.backButton = true,
1239  .navWithButtons.quitText = NULL,
1240  .progressIndicator = false,
1241  .tuneId = TUNE_TAP_CASUAL};
1242  nbgl_pageContent_t content = {.type = INFOS_LIST,
1243  .topRightIcon = NULL,
1244  .infosList.nbInfos = tipBoxInfoList.nbInfos,
1245  .infosList.withExtensions = tipBoxInfoList.withExtensions,
1246  .infosList.infoTypes = tipBoxInfoList.infoTypes,
1247  .infosList.infoContents = tipBoxInfoList.infoContents,
1248  .infosList.infoExtensions = tipBoxInfoList.infoExtensions,
1249  .infosList.token = INFO_ALIAS_TOKEN,
1250  .title = tipBoxModalTitle,
1251  .titleToken = QUIT_TOKEN};
1252 
1253  if (modalPageContext != NULL) {
1254  nbgl_pageRelease(modalPageContext);
1255  }
1256  modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1257 
1259 }
1260 
1261 #ifdef NBGL_QRCODE
1262 static void displayAddressQRCode(void)
1263 {
1264  // display the address as QR Code
1265  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1266  .withLeftBorder = true,
1267  .onActionCallback = &addressLayoutTouchCallbackQR,
1268  .tapActionText = NULL};
1269  nbgl_layoutHeader_t headerDesc = {
1270  .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1271  nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1272  .text1 = NULL,
1273  .centered = true,
1274  .offsetY = 0};
1275 
1276  addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1277  // add empty header for better look
1278  nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1279  // compute nb lines to check whether it shall be shorten (max is 3 lines)
1281  SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1282 
1283  if (nbLines <= QRCODE_NB_MAX_LINES) {
1284  qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1285  }
1286  else {
1287  // only keep beginning and end of text, and add ... in the middle
1288  nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1289  addressConfirmationContext.tagValuePair.value,
1291  QRCODE_NB_MAX_LINES,
1292  reducedAddress,
1293  QRCODE_REDUCED_ADDR_LEN);
1294  qrCode.text2 = reducedAddress; // in gray
1295  }
1296 
1297  nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1298 
1299  nbgl_layoutAddFooter(addressConfirmationContext.modalLayout, "Close", 0, TUNE_TAP_CASUAL);
1300  nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1301  nbgl_refresh();
1302 }
1303 
1304 // called when quit button is touched on Address verification page
1305 static void addressLayoutTouchCallbackQR(int token, uint8_t index)
1306 {
1307  UNUSED(token);
1308  UNUSED(index);
1309 
1310  // dismiss modal
1311  nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1312  addressConfirmationContext.modalLayout = NULL;
1314  nbgl_refresh();
1315 }
1316 #endif // NBGL_QRCODE
1317 
1318 // called when header is touched on modal page, to dismiss it
1319 static void modalLayoutTouchCallback(int token, uint8_t index)
1320 {
1321  UNUSED(token);
1322  UNUSED(index);
1323 
1324  // dismiss modal
1325  nbgl_layoutRelease(genericContext.modalLayout);
1326  genericContext.modalLayout = NULL;
1328  nbgl_refresh();
1329 }
1330 
1331 // called when skip button is touched in footer, during forward only review
1332 static void displaySkipWarning(void)
1333 {
1335  .cancelText = "Go back to review",
1336  .centeredInfo.text1 = "Skip review?",
1337  .centeredInfo.text2
1338  = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1339  .centeredInfo.text3 = NULL,
1340  .centeredInfo.style = LARGE_CASE_INFO,
1341  .centeredInfo.icon = &C_Important_Circle_64px,
1342  .centeredInfo.offsetY = 0,
1343  .confirmationText = "Yes, skip",
1344  .confirmationToken = SKIP_TOKEN,
1345  .tuneId = TUNE_TAP_CASUAL,
1346  .modal = true};
1347  if (modalPageContext != NULL) {
1348  nbgl_pageRelease(modalPageContext);
1349  }
1350  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1352 }
1353 
1354 #ifdef NBGL_KEYPAD
1355 // called to update the keypad and automatically show / hide:
1356 // - backspace key if no digits are present
1357 // - validation key if the min digit is reached
1358 // - keypad if the max number of digit is reached
1359 static void updateKeyPad(bool add)
1360 {
1361  bool enableValidate, enableBackspace, enableDigits;
1362  bool redrawKeypad = false;
1364 
1365  enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1366  enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1367  enableBackspace = (keypadContext.pinLen > 0);
1368  if (add) {
1369  if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1370  || // activate "validate" button on keypad
1371  (keypadContext.pinLen == keypadContext.pinMaxDigits)
1372  || // deactivate "digits" on keypad
1373  (keypadContext.pinLen == 1)) { // activate "backspace"
1374  redrawKeypad = true;
1375  }
1376  }
1377  else { // remove
1378  if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1379  (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1380  || // deactivate "validate" button on keypad
1381  (keypadContext.pinLen
1382  == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1383  redrawKeypad = true;
1384  }
1385  }
1386  if (keypadContext.hidden == true) {
1387  nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1388  }
1389  else {
1391  keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1392  }
1393  if (redrawKeypad) {
1395  keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1396  }
1397 
1398  if ((!add) && (keypadContext.pinLen == 0)) {
1399  // Full refresh to fully clean the bullets when reaching 0 digits
1400  mode = FULL_COLOR_REFRESH;
1401  }
1403 }
1404 
1405 // called when a key is touched on the keypad
1406 static void keypadCallback(char touchedKey)
1407 {
1408  switch (touchedKey) {
1409  case BACKSPACE_KEY:
1410  if (keypadContext.pinLen > 0) {
1411  keypadContext.pinLen--;
1412  keypadContext.pinEntry[keypadContext.pinLen] = 0;
1413  }
1414  updateKeyPad(false);
1415  break;
1416 
1417  case VALIDATE_KEY:
1418  // Gray out keyboard / buttons as a first user feedback
1419  nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1422 
1423  onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1424  break;
1425 
1426  default:
1427  if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1428  if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1429  keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1430  keypadContext.pinLen++;
1431  }
1432  updateKeyPad(true);
1433  }
1434  break;
1435  }
1436 }
1437 
1438 // called to create a keypad, with either hidden or visible digits
1439 static void keypadGenericUseCase(const char *title,
1440  uint8_t minDigits,
1441  uint8_t maxDigits,
1442  uint8_t backToken,
1443  bool shuffled,
1444  bool hidden,
1445  tune_index_e tuneId,
1446  nbgl_pinValidCallback_t validatePinCallback,
1447  nbgl_layoutTouchCallback_t actionCallback)
1448 {
1449  nbgl_layoutDescription_t layoutDescription = {0};
1451  .separationLine = true,
1452  .backAndText.token = backToken,
1453  .backAndText.tuneId = tuneId,
1454  .backAndText.text = NULL};
1455  int status = -1;
1456 
1457  if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1458  return;
1459  }
1460 
1461  reset_callbacks();
1462  // reset the keypad context
1463  memset(&keypadContext, 0, sizeof(KeypadContext_t));
1464 
1465  // get a layout
1466  layoutDescription.onActionCallback = actionCallback;
1467  layoutDescription.modal = false;
1468  layoutDescription.withLeftBorder = false;
1469  keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1470  keypadContext.hidden = hidden;
1471 
1472  // set back key in header
1473  nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1474 
1475  // add keypad
1476  status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1477  if (status < 0) {
1478  return;
1479  }
1480  // add keypad content
1481  status = nbgl_layoutAddKeypadContent(
1482  keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1483 
1484  if (status < 0) {
1485  return;
1486  }
1487 
1488  // validation pin callback
1489  onValidatePin = validatePinCallback;
1490  // pin code acceptable lengths
1491  keypadContext.pinMinDigits = minDigits;
1492  keypadContext.pinMaxDigits = maxDigits;
1493 
1494  nbgl_layoutDraw(keypadContext.layoutCtx);
1496 }
1497 #endif
1498 
1499 static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1500  uint8_t pageIdxStart,
1501  bool isLast,
1502  bool isSkippable)
1503 {
1504  uint8_t nbElements = 0;
1505  uint8_t nbPages = 0;
1506  uint8_t nbElementsInPage;
1507  uint8_t elemIdx = 0;
1508  bool flag;
1509 
1510  nbElements = getContentNbElement(content);
1511 
1512  while (nbElements > 0) {
1513  flag = 0;
1514  // if the current page is not the first one (or last), a navigation bar exists
1515  bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1516  if (content->type == TAG_VALUE_LIST) {
1517  nbElementsInPage = nbgl_useCaseGetNbTagValuesInPageExt(
1518  nbElements, &content->content.tagValueList, elemIdx, isSkippable, &flag);
1519  }
1520  else if (content->type == INFOS_LIST) {
1521  nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1522  nbElements, &content->content.infosList, elemIdx, hasNav);
1523  }
1524  else if (content->type == SWITCHES_LIST) {
1525  nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1526  nbElements, &content->content.switchesList, elemIdx, hasNav);
1527  }
1528  else if (content->type == BARS_LIST) {
1529  nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1530  nbElements, &content->content.barsList, elemIdx, hasNav);
1531  }
1532  else if (content->type == CHOICES_LIST) {
1533  nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1534  nbElements, &content->content.choicesList, elemIdx, hasNav);
1535  }
1536  else {
1537  nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1538  }
1539 
1540  elemIdx += nbElementsInPage;
1541  genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1542  nbElements -= nbElementsInPage;
1543  nbPages++;
1544  }
1545 
1546  return nbPages;
1547 }
1548 
1549 static uint8_t getNbPagesForGenericContents(const nbgl_genericContents_t *genericContents,
1550  uint8_t pageIdxStart,
1551  bool isSkippable)
1552 {
1553  uint8_t nbPages = 0;
1554  nbgl_content_t content;
1555  const nbgl_content_t *p_content;
1556 
1557  for (int i = 0; i < genericContents->nbContents; i++) {
1558  p_content = getContentAtIdx(genericContents, i, &content);
1559  if (p_content == NULL) {
1560  return 0;
1561  }
1562  nbPages += nbgl_useCaseGetNbPagesForContent(p_content,
1563  pageIdxStart + nbPages,
1564  (i == (genericContents->nbContents - 1)),
1565  isSkippable);
1566  }
1567 
1568  return nbPages;
1569 }
1570 
1571 static void prepareAddressConfirmationPages(const char *address,
1572  const nbgl_contentTagValueList_t *tagValueList,
1573  nbgl_content_t *firstPageContent,
1574  nbgl_content_t *secondPageContent)
1575 {
1576  nbgl_contentTagValueConfirm_t *tagValueConfirm;
1577 
1578  addressConfirmationContext.tagValuePair.item = "Address";
1579  addressConfirmationContext.tagValuePair.value = address;
1580 
1581  // First page
1582  firstPageContent->type = TAG_VALUE_CONFIRM;
1583  tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1584 
1585 #ifdef NBGL_QRCODE
1586  tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1587  // only use "Show as QR" when it's not the last page
1588  if (tagValueList != NULL) {
1589  tagValueConfirm->detailsButtonText = "Show as QR";
1590  }
1591  else {
1592  tagValueConfirm->detailsButtonText = NULL;
1593  }
1594  tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1595 #else // NBGL_QRCODE
1596  tagValueConfirm->detailsButtonText = NULL;
1597  tagValueConfirm->detailsButtonIcon = NULL;
1598 #endif // NBGL_QRCODE
1599  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1600  tagValueConfirm->tagValueList.nbPairs = 1;
1601  tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1602  tagValueConfirm->tagValueList.smallCaseForValue = false;
1603  tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1604  tagValueConfirm->tagValueList.wrapping = false;
1605  // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1606  // no confirmation button
1607  if (tagValueList != NULL) {
1608  tagValueConfirm->confirmationText = NULL;
1609  }
1610  else {
1611  // otherwise no tap to continue but a confirmation button
1612  tagValueConfirm->confirmationText = "Confirm";
1613  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1614  }
1615 
1616  // Second page if any:
1617  if (tagValueList != NULL) {
1618  // the second page is dedicated to the extended tag/value pairs
1619  secondPageContent->type = TAG_VALUE_CONFIRM;
1620  tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1621  tagValueConfirm->confirmationText = "Confirm";
1622  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1623  tagValueConfirm->detailsButtonText = NULL;
1624  tagValueConfirm->detailsButtonIcon = NULL;
1625  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1626  memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1627  }
1628 }
1629 
1630 static void bundleNavStartHome(void)
1631 {
1632  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1633 
1634  useCaseHomeExt(context->appName,
1635  context->appIcon,
1636  context->tagline,
1637  context->settingContents != NULL ? true : false,
1638  &context->homeAction,
1639  bundleNavStartSettings,
1640  context->quitCallback);
1641 }
1642 
1643 static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1644 {
1645  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1646 
1647  nbgl_useCaseGenericSettings(context->appName,
1648  initSettingPage,
1649  context->settingContents,
1650  context->infosList,
1651  bundleNavStartHome);
1652 }
1653 
1654 static void bundleNavStartSettings(void)
1655 {
1656  bundleNavStartSettingsAtPage(0);
1657 }
1658 
1659 static void bundleNavReviewConfirmRejection(void)
1660 {
1661  bundleNavContext.review.choiceCallback(false);
1662 }
1663 
1664 static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1665  nbgl_callback_t callback)
1666 {
1667  const char *title;
1668  const char *confirmText;
1669  // clear skip and blind bits
1670  operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION);
1671  if (operationType == TYPE_TRANSACTION) {
1672  title = "Reject transaction?";
1673  confirmText = "Go back to transaction";
1674  }
1675  else if (operationType == TYPE_MESSAGE) {
1676  title = "Reject message?";
1677  confirmText = "Go back to message";
1678  }
1679  else {
1680  title = "Reject operation?";
1681  confirmText = "Go back to operation";
1682  }
1683 
1684  // display a choice to confirm/cancel rejection
1685  nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1686 }
1687 
1688 static void bundleNavReviewChoice(bool confirm)
1689 {
1690  if (confirm) {
1691  bundleNavContext.review.choiceCallback(true);
1692  }
1693  else {
1694  bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1695  bundleNavReviewConfirmRejection);
1696  }
1697 }
1698 
1699 static void bundleNavReviewStreamingConfirmRejection(void)
1700 {
1701  bundleNavContext.reviewStreaming.choiceCallback(false);
1702 }
1703 
1704 static void bundleNavReviewStreamingChoice(bool confirm)
1705 {
1706  if (confirm) {
1707  // Display a spinner if it wasn't the finish step
1708  if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1709  nbgl_useCaseSpinner("Processing");
1710  }
1711  bundleNavContext.reviewStreaming.choiceCallback(true);
1712  }
1713  else {
1714  bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1715  bundleNavReviewStreamingConfirmRejection);
1716  }
1717 }
1718 
1719 // function called when the warning page of Blind Signing review buttons are pressed
1720 static void blindSigningWarningCallback(bool confirm)
1721 {
1722  if (confirm) { // top button to exit
1723  blindSigningContext.choiceCallback(false);
1724  }
1725  else { // bottom button to continue to review
1726  if (blindSigningContext.isStreaming) {
1727  useCaseReviewStreamingStart(blindSigningContext.operationType,
1728  blindSigningContext.icon,
1729  blindSigningContext.reviewTitle,
1730  blindSigningContext.reviewSubTitle,
1731  blindSigningContext.choiceCallback,
1732  false);
1733  }
1734  else {
1735  useCaseReview(blindSigningContext.operationType,
1736  blindSigningContext.tagValueList,
1737  blindSigningContext.icon,
1738  blindSigningContext.reviewTitle,
1739  blindSigningContext.reviewSubTitle,
1740  blindSigningContext.finishTitle,
1741  blindSigningContext.tipBox,
1742  blindSigningContext.choiceCallback,
1743  false,
1744  false);
1745  }
1746  }
1747 }
1748 
1749 // function used to display the warning page when starting a Bling Signing review
1750 static void blindSigningWarning(void)
1751 {
1752  // Play notification sound
1753 #ifdef HAVE_PIEZO_SOUND
1754  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1755 #endif // HAVE_PIEZO_SOUND
1756  nbgl_useCaseChoice(&C_Warning_64px,
1757  "Blind signing ahead",
1758  "The details of this transaction or message are not fully verifiable. If "
1759  "you sign it, you could lose all "
1760  "your assets.",
1761  "Back to safety",
1762  "Continue anyway",
1763  blindSigningWarningCallback);
1764 }
1765 
1766 // function to factorize code for all simple reviews
1767 static void useCaseReview(nbgl_operationType_t operationType,
1768  const nbgl_contentTagValueList_t *tagValueList,
1769  const nbgl_icon_details_t *icon,
1770  const char *reviewTitle,
1771  const char *reviewSubTitle,
1772  const char *finishTitle,
1773  const nbgl_tipBox_t *tipBox,
1774  nbgl_choiceCallback_t choiceCallback,
1775  bool isLight,
1776  bool playNotifSound)
1777 {
1778  reset_callbacks();
1779  memset(&genericContext, 0, sizeof(genericContext));
1780 
1781  bundleNavContext.review.operationType = operationType;
1782  bundleNavContext.review.choiceCallback = choiceCallback;
1783 
1784  // memorize context
1785  onChoice = bundleNavReviewChoice;
1786  navType = GENERIC_NAV;
1787  pageTitle = NULL;
1788 
1789  genericContext.genericContents.contentsList = localContentsList;
1790  genericContext.genericContents.nbContents = 3;
1791  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
1792 
1793  // First a centered info
1794  STARTING_CONTENT.type = EXTENDED_CENTER;
1795  prepareReviewFirstPage(
1796  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1797  if (tipBox != NULL) {
1798  // do not display "Swipe to review" if a tip-box is displayed
1799  STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL;
1800 
1801  STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
1802  STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
1803  STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
1804  STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
1805  tipBoxModalTitle = tipBox->modalTitle;
1806  // the only supported type yet is @ref INFOS_LIST
1807  if (tipBox->type == INFOS_LIST) {
1808  tipBoxInfoList.nbInfos = tipBox->infos.nbInfos;
1809  tipBoxInfoList.withExtensions = tipBox->infos.withExtensions;
1810  tipBoxInfoList.infoTypes = PIC(tipBox->infos.infoTypes);
1811  tipBoxInfoList.infoContents = PIC(tipBox->infos.infoContents);
1812  tipBoxInfoList.infoExtensions = PIC(tipBox->infos.infoExtensions);
1813  }
1814  }
1815 
1816  // Then the tag/value pairs
1817  localContentsList[1].type = TAG_VALUE_LIST;
1818  memcpy(&localContentsList[1].content.tagValueList,
1819  tagValueList,
1820  sizeof(nbgl_contentTagValueList_t));
1821  localContentsList[1].contentActionCallback = tagValueList->actionCallback;
1822 
1823  // The last page
1824  if (isLight) {
1825  localContentsList[2].type = INFO_BUTTON;
1826  prepareReviewLightLastPage(&localContentsList[2].content.infoButton, icon, finishTitle);
1827  }
1828  else {
1829  localContentsList[2].type = INFO_LONG_PRESS;
1830  prepareReviewLastPage(&localContentsList[2].content.infoLongPress, icon, finishTitle);
1831  }
1832 
1833  // compute number of pages & fill navigation structure
1834  uint8_t nbPages = getNbPagesForGenericContents(
1835  &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
1836  prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
1837 
1838  // Play notification sound if required
1839  if (playNotifSound) {
1840 #ifdef HAVE_PIEZO_SOUND
1841  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1842 #endif // HAVE_PIEZO_SOUND
1843  }
1844 
1845  displayGenericContextPage(0, true);
1846 }
1847 
1848 // function to factorize code for all streaming reviews
1849 static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
1850  const nbgl_icon_details_t *icon,
1851  const char *reviewTitle,
1852  const char *reviewSubTitle,
1853  nbgl_choiceCallback_t choiceCallback,
1854  bool playNotifSound)
1855 {
1856  reset_callbacks();
1857  memset(&genericContext, 0, sizeof(genericContext));
1858 
1859  bundleNavContext.reviewStreaming.operationType = operationType;
1860  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
1861  bundleNavContext.reviewStreaming.icon = icon;
1862 
1863  // memorize context
1864  onChoice = bundleNavReviewStreamingChoice;
1865  navType = STREAMING_NAV;
1866  pageTitle = NULL;
1867 
1868  genericContext.genericContents.contentsList = localContentsList;
1869  genericContext.genericContents.nbContents = 1;
1870  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
1871 
1872  // First a centered info
1873  STARTING_CONTENT.type = EXTENDED_CENTER;
1874  prepareReviewFirstPage(
1875  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1876 
1877  // compute number of pages & fill navigation structure
1878  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
1879  &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
1880  prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
1881  // no back button on first page
1882  navInfo.navWithButtons.backButton = false;
1883 
1884  // Play notification sound if required
1885  if (playNotifSound) {
1886 #ifdef HAVE_PIEZO_SOUND
1887  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1888 #endif // HAVE_PIEZO_SOUND
1889  }
1890 
1891  displayGenericContextPage(0, true);
1892 }
1893 
1910 static void useCaseHomeExt(const char *appName,
1911  const nbgl_icon_details_t *appIcon,
1912  const char *tagline,
1913  bool withSettings,
1914  nbgl_homeAction_t *homeAction,
1915  nbgl_callback_t topRightCallback,
1916  nbgl_callback_t quitCallback)
1917 {
1918  reset_callbacks();
1919 
1920  nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
1921  .centeredInfo.text1 = appName,
1922  .centeredInfo.text3 = NULL,
1923  .centeredInfo.style = LARGE_CASE_INFO,
1924  .centeredInfo.offsetY = 0,
1925  .footerText = NULL,
1926  .bottomButtonStyle = QUIT_APP_TEXT,
1927  .tapActionText = NULL,
1928  .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
1929  .topRightToken = CONTINUE_TOKEN,
1930  .tuneId = TUNE_TAP_CASUAL};
1931  if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
1932  // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
1933  info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
1934  onAction = homeAction->callback;
1935  info.actionButtonText = homeAction->text;
1936  info.actionButtonIcon = homeAction->icon;
1937  info.actionButtonStyle
1939  }
1940  else {
1941  info.bottomButtonsToken = QUIT_TOKEN;
1942  onAction = NULL;
1943  info.actionButtonText = NULL;
1944  info.actionButtonIcon = NULL;
1945  }
1946  if (tagline == NULL) {
1947  if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
1948  snprintf(appDescription,
1950  "This app enables signing\ntransactions on its network.");
1951  }
1952  else {
1953  snprintf(appDescription,
1955  "%s %s\n%s",
1956  TAGLINE_PART1,
1957  appName,
1958  TAGLINE_PART2);
1959  }
1960 
1961  // If there is more than 3 lines, it means the appName was split, so we put it on the next
1962  // line
1963  if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, appDescription, AVAILABLE_WIDTH, false)
1964  > 3) {
1965  snprintf(appDescription,
1967  "%s\n%s %s",
1968  TAGLINE_PART1,
1969  appName,
1970  TAGLINE_PART2);
1971  }
1972  info.centeredInfo.text2 = appDescription;
1973  }
1974  else {
1975  info.centeredInfo.text2 = tagline;
1976  }
1977 
1978  onContinue = topRightCallback;
1979  onQuit = quitCallback;
1980 
1981  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
1983 }
1984 
1993 static void displayDetails(const char *tag, const char *value, bool wrapping)
1994 {
1995  memset(&detailsContext, 0, sizeof(detailsContext));
1996 
1997  uint16_t nbLines
1998  = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
1999 
2000  // initialize context
2001  detailsContext.tag = tag;
2002  detailsContext.value = value;
2003  detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
2004  detailsContext.currentPage = 0;
2005  detailsContext.wrapping = wrapping;
2006  // add some spare for room lost with "..." substitution
2007  if (detailsContext.nbPages > 1) {
2008  uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
2009  uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
2010  / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
2011  uint8_t nbLinesInLastPage
2012  = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
2013 
2014  detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
2015  if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
2017  detailsContext.nbPages++;
2018  }
2019  }
2020 
2021  displayDetailsPage(0, true);
2022 }
2023 
2024 /**********************
2025  * GLOBAL FUNCTIONS
2026  **********************/
2027 
2041  const nbgl_contentTagValueList_t *tagValueList,
2042  uint8_t startIndex,
2043  bool *requireSpecificDisplay)
2044 {
2046  nbPairs, tagValueList, startIndex, false, requireSpecificDisplay);
2047 }
2048 
2063  const nbgl_contentTagValueList_t *tagValueList,
2064  uint8_t startIndex,
2065  bool isSkippable,
2066  bool *requireSpecificDisplay)
2067 {
2068  uint8_t nbPairsInPage = 0;
2069 #ifdef TARGET_STAX
2070  uint16_t currentHeight = 24; // upper margin
2071 #else // TARGET_STAX
2072  uint16_t currentHeight = 0; // upper margin
2073 #endif // TARGET_STAX
2074  uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
2075 
2076  // if the review is skippable, it means that there is less height for tag/value pairs
2077  // the small centering header becomes a touchable header
2078  if (isSkippable) {
2080  }
2081 
2082  *requireSpecificDisplay = false;
2083  while (nbPairsInPage < nbPairs) {
2084  const nbgl_layoutTagValue_t *pair;
2085  nbgl_font_id_e value_font;
2086  uint16_t nbLines;
2087 
2088  // margin between pairs
2089  // 12 or 24 px between each tag/value pair
2090  if (nbPairsInPage > 0) {
2091 #ifdef TARGET_STAX
2092  currentHeight += 12;
2093 #else // TARGET_STAX
2094  currentHeight += 24;
2095 #endif // TARGET_STAX
2096  }
2097  // fetch tag/value pair strings.
2098  if (tagValueList->pairs != NULL) {
2099  pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2100  }
2101  else {
2102  pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2103  }
2104 
2105  if (pair->forcePageStart && nbPairsInPage > 0) {
2106  // This pair must be at the top of a page
2107  break;
2108  }
2109 
2110  if (pair->centeredInfo) {
2111  if (nbPairsInPage > 0) {
2112  // This pair must be at the top of a page
2113  break;
2114  }
2115  else {
2116  // This pair is the only one of the page and has a specific display behavior
2117  nbPairsInPage = 1;
2118  *requireSpecificDisplay = true;
2119  break;
2120  }
2121  }
2122 
2123  // tag height
2124  currentHeight += nbgl_getTextHeightInWidth(
2125  SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2126  // space between tag and value
2127  currentHeight += 4;
2128  // set value font
2129  if (tagValueList->smallCaseForValue) {
2130  value_font = SMALL_REGULAR_FONT;
2131  }
2132  else {
2133  value_font = LARGE_MEDIUM_FONT;
2134  }
2135  // value height
2136  currentHeight += nbgl_getTextHeightInWidth(
2137  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2138  // nb lines for value
2139  nbLines = nbgl_getTextNbLinesInWidth(
2140  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2141  if ((currentHeight >= maxUsableHeight) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2142  if (nbPairsInPage == 0) {
2143  // Pair too long to fit in a single screen
2144  // It will be the only one of the page and has a specific display behavior
2145  nbPairsInPage = 1;
2146  *requireSpecificDisplay = true;
2147  }
2148  break;
2149  }
2150  nbPairsInPage++;
2151  }
2152  return nbPairsInPage;
2153 }
2154 
2165  const nbgl_contentInfoList_t *infosList,
2166  uint8_t startIndex,
2167  bool withNav)
2168 {
2169  uint8_t nbInfosInPage = 0;
2170  uint16_t currentHeight = 0;
2171  uint16_t previousHeight;
2172  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2173  const char *const *infoTypes = PIC(infosList->infoTypes);
2174  const char *const *infoContents = PIC(infosList->infoContents);
2175 
2176  while (nbInfosInPage < nbInfos) {
2177  // margin between infos
2178  currentHeight += PRE_TEXT_MARGIN;
2179 
2180  // type height
2181  currentHeight += nbgl_getTextHeightInWidth(
2182  SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2183  // space between type and content
2184  currentHeight += TEXT_SUBTEXT_MARGIN;
2185 
2186  // content height
2187  currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2188  PIC(infoContents[startIndex + nbInfosInPage]),
2190  true);
2191  currentHeight += POST_SUBTEXT_MARGIN; // under the content
2192  // if height is over the limit
2193  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2194  // if there was no nav, now there will be, so it can be necessary to remove the last
2195  // item
2196  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2197  nbInfosInPage--;
2198  }
2199  break;
2200  }
2201  previousHeight = currentHeight;
2202  nbInfosInPage++;
2203  }
2204  return nbInfosInPage;
2205 }
2206 
2217  const nbgl_contentSwitchesList_t *switchesList,
2218  uint8_t startIndex,
2219  bool withNav)
2220 {
2221  uint8_t nbSwitchesInPage = 0;
2222  uint16_t currentHeight = 0;
2223  uint16_t previousHeight;
2224  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2225  nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2226 
2227  while (nbSwitchesInPage < nbSwitches) {
2228  // margin between switches
2229  currentHeight += PRE_TEXT_MARGIN;
2230 
2231  // text height
2232  currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2233  switchArray[startIndex + nbSwitchesInPage].text,
2235  true);
2236  // space between text and sub-text
2237  currentHeight += TEXT_SUBTEXT_MARGIN;
2238 
2239  // sub-text height
2240  currentHeight
2241  += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2242  switchArray[startIndex + nbSwitchesInPage].subText,
2244  true);
2245  currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2246  // if height is over the limit
2247  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2248  // if there was no nav, now there will be, so it can be necessary to remove the last
2249  // item
2250  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2251  nbSwitchesInPage--;
2252  }
2253  break;
2254  }
2255  previousHeight = currentHeight;
2256  nbSwitchesInPage++;
2257  }
2258  return nbSwitchesInPage;
2259 }
2260 
2271  const nbgl_contentBarsList_t *barsList,
2272  uint8_t startIndex,
2273  bool withNav)
2274 {
2275  uint8_t nbBarsInPage = 0;
2276  uint16_t currentHeight = 0;
2277  uint16_t previousHeight;
2278  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2279 
2280  UNUSED(barsList);
2281  UNUSED(startIndex);
2282 
2283  while (nbBarsInPage < nbBars) {
2284  currentHeight += TOUCHABLE_BAR_HEIGHT;
2285  // if height is over the limit
2286  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2287  break;
2288  }
2289  previousHeight = currentHeight;
2290  nbBarsInPage++;
2291  }
2292  // if there was no nav, now there may will be, so it can be necessary to remove the last
2293  // item
2294  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2295  nbBarsInPage--;
2296  }
2297  return nbBarsInPage;
2298 }
2299 
2310  const nbgl_contentRadioChoice_t *choicesList,
2311  uint8_t startIndex,
2312  bool withNav)
2313 {
2314  uint8_t nbChoicesInPage = 0;
2315  uint16_t currentHeight = 0;
2316  uint16_t previousHeight;
2317  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2318 
2319  UNUSED(choicesList);
2320  UNUSED(startIndex);
2321 
2322  while (nbChoicesInPage < nbChoices) {
2323  currentHeight += TOUCHABLE_BAR_HEIGHT;
2324  // if height is over the limit
2325  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2326  // if there was no nav, now there will be, so it can be necessary to remove the last
2327  // item
2328  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2329  nbChoicesInPage--;
2330  }
2331  break;
2332  }
2333  previousHeight = currentHeight;
2334  nbChoicesInPage++;
2335  }
2336  return nbChoicesInPage;
2337 }
2338 
2346 {
2347  uint8_t nbPages = 0;
2348  uint8_t nbPairs = tagValueList->nbPairs;
2349  uint8_t nbPairsInPage;
2350  uint8_t i = 0;
2351  bool flag;
2352 
2353  while (i < tagValueList->nbPairs) {
2354  // upper margin
2355  nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2356  i += nbPairsInPage;
2357  nbPairs -= nbPairsInPage;
2358  nbPages++;
2359  }
2360  return nbPages;
2361 }
2362 
2367 void nbgl_useCaseHome(const char *appName,
2368  const nbgl_icon_details_t *appIcon,
2369  const char *tagline,
2370  bool withSettings,
2371  nbgl_callback_t topRightCallback,
2372  nbgl_callback_t quitCallback)
2373 {
2374  nbgl_homeAction_t homeAction = {0};
2375  useCaseHomeExt(
2376  appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2377 }
2378 
2383 void nbgl_useCaseHomeExt(const char *appName,
2384  const nbgl_icon_details_t *appIcon,
2385  const char *tagline,
2386  bool withSettings,
2387  const char *actionButtonText,
2388  nbgl_callback_t actionCallback,
2389  nbgl_callback_t topRightCallback,
2390  nbgl_callback_t quitCallback)
2391 {
2392  nbgl_homeAction_t homeAction = {.callback = actionCallback,
2393  .icon = NULL,
2394  .style = STRONG_HOME_ACTION,
2395  .text = actionButtonText};
2396 
2397  useCaseHomeExt(
2398  appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2399 }
2400 
2414 void nbgl_useCaseNavigableContent(const char *title,
2415  uint8_t initPage,
2416  uint8_t nbPages,
2417  nbgl_callback_t quitCallback,
2418  nbgl_navCallback_t navCallback,
2419  nbgl_layoutTouchCallback_t controlsCallback)
2420 {
2421  reset_callbacks();
2422 
2423  // memorize context
2424  onQuit = quitCallback;
2425  onNav = navCallback;
2426  onControls = controlsCallback;
2427  pageTitle = title;
2428  navType = SETTINGS_NAV;
2429 
2430  // fill navigation structure
2431  prepareNavInfo(false, nbPages, NULL);
2432 
2433  displaySettingsPage(initPage, true);
2434 }
2435 
2441 void nbgl_useCaseSettings(const char *title,
2442  uint8_t initPage,
2443  uint8_t nbPages,
2444  bool touchable,
2445  nbgl_callback_t quitCallback,
2446  nbgl_navCallback_t navCallback,
2447  nbgl_layoutTouchCallback_t controlsCallback)
2448 {
2449  UNUSED(touchable);
2451  title, initPage, nbPages, quitCallback, navCallback, controlsCallback);
2452 }
2453 
2466 void nbgl_useCaseGenericSettings(const char *appName,
2467  uint8_t initPage,
2468  const nbgl_genericContents_t *settingContents,
2469  const nbgl_contentInfoList_t *infosList,
2470  nbgl_callback_t quitCallback)
2471 {
2472  reset_callbacks();
2473  memset(&genericContext, 0, sizeof(genericContext));
2474 
2475  // memorize context
2476  onQuit = quitCallback;
2477  pageTitle = appName;
2478  navType = GENERIC_NAV;
2479 
2480  if (settingContents != NULL) {
2481  memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
2482  }
2483  if (infosList != NULL) {
2484  genericContext.hasFinishingContent = true;
2485  memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
2486  FINISHING_CONTENT.type = INFOS_LIST;
2487  memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
2488  }
2489 
2490  // fill navigation structure
2491  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2492  if (infosList != NULL) {
2493  nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true, false);
2494  }
2495 
2496  prepareNavInfo(false, nbPages, NULL);
2497 
2498  displayGenericContextPage(initPage, true);
2499 }
2500 
2512 void nbgl_useCaseGenericConfiguration(const char *title,
2513  uint8_t initPage,
2514  const nbgl_genericContents_t *contents,
2515  nbgl_callback_t quitCallback)
2516 {
2517  nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2518 }
2519 
2537  const char *appName,
2538  const nbgl_icon_details_t *appIcon,
2539  const char *tagline,
2540  const uint8_t
2541  initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
2542  const nbgl_genericContents_t *settingContents,
2543  const nbgl_contentInfoList_t *infosList,
2544  const nbgl_homeAction_t *action, // Set to NULL if no additional action
2545  nbgl_callback_t quitCallback)
2546 {
2547  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
2548 
2549  context->appName = appName;
2550  context->appIcon = appIcon;
2551  context->tagline = tagline;
2552  context->settingContents = settingContents;
2553  context->infosList = infosList;
2554  if (action != NULL) {
2555  memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
2556  }
2557  else {
2558  memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
2559  }
2560  context->quitCallback = quitCallback;
2561 
2562  if (initSettingPage != INIT_HOME_PAGE) {
2563  bundleNavStartSettingsAtPage(initSettingPage);
2564  }
2565  else {
2566  bundleNavStartHome();
2567  }
2568 }
2569 
2577 void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2578 {
2579  reset_callbacks();
2580 
2582  .tickerCallback = &tickerCallback,
2583  .tickerIntervale = 0, // not periodic
2584  .tickerValue = 3000 // 3 seconds
2585  };
2586  onQuit = quitCallback;
2587  if (isSuccess) {
2588 #ifdef HAVE_PIEZO_SOUND
2589  io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
2590 #endif // HAVE_PIEZO_SOUND
2591 
2592  pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
2593  }
2594  else {
2596  .footerText = NULL,
2597  .centeredInfo.icon = &C_Denied_Circle_64px,
2598  .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
2599  .centeredInfo.onTop = false,
2600  .centeredInfo.style = LARGE_CASE_INFO,
2601  .centeredInfo.text1 = message,
2602  .centeredInfo.text2 = NULL,
2603  .centeredInfo.text3 = NULL,
2604  .tapActionText = "",
2605  .isSwipeable = false,
2606  .tapActionToken = QUIT_TOKEN,
2607  .topRightStyle = NO_BUTTON_STYLE,
2608  .actionButtonText = NULL,
2609  .tuneId = TUNE_TAP_CASUAL};
2610  pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
2611  }
2613 }
2614 
2622  nbgl_callback_t quitCallback)
2623 {
2624  const char *msg;
2625  bool isSuccess;
2626  switch (reviewStatusType) {
2628  msg = "Operation signed";
2629  isSuccess = true;
2630  break;
2632  msg = "Operation rejected";
2633  isSuccess = false;
2634  break;
2636  msg = "Transaction signed";
2637  isSuccess = true;
2638  break;
2640  msg = "Transaction rejected";
2641  isSuccess = false;
2642  break;
2644  msg = "Message signed";
2645  isSuccess = true;
2646  break;
2648  msg = "Message rejected";
2649  isSuccess = false;
2650  break;
2652  msg = "Address verified";
2653  isSuccess = true;
2654  break;
2656  msg = "Address verification\ncancelled";
2657  isSuccess = false;
2658  break;
2659  default:
2660  return;
2661  }
2662  nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2663 }
2664 
2677 void nbgl_useCaseChoice(const nbgl_icon_details_t *icon,
2678  const char *message,
2679  const char *subMessage,
2680  const char *confirmText,
2681  const char *cancelText,
2682  nbgl_choiceCallback_t callback)
2683 {
2684  reset_callbacks();
2685 
2686  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2687  .centeredInfo.text1 = message,
2688  .centeredInfo.text2 = subMessage,
2689  .centeredInfo.text3 = NULL,
2690  .centeredInfo.style = LARGE_CASE_INFO,
2691  .centeredInfo.icon = icon,
2692  .centeredInfo.offsetY = 0,
2693  .confirmationText = confirmText,
2694  .confirmationToken = CHOICE_TOKEN,
2695  .tuneId = TUNE_TAP_CASUAL,
2696  .modal = false};
2697  // check params
2698  if ((confirmText == NULL) || (cancelText == NULL)) {
2699  return;
2700  }
2701  onChoice = callback;
2702  pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
2704 }
2705 
2719 void nbgl_useCaseConfirm(const char *message,
2720  const char *subMessage,
2721  const char *confirmText,
2722  const char *cancelText,
2723  nbgl_callback_t callback)
2724 {
2725  // Don't reset callback or nav context as this is just a modal.
2726 
2727  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2728  .centeredInfo.text1 = message,
2729  .centeredInfo.text2 = subMessage,
2730  .centeredInfo.text3 = NULL,
2731  .centeredInfo.style = LARGE_CASE_INFO,
2732  .centeredInfo.icon = &C_Important_Circle_64px,
2733  .centeredInfo.offsetY = 0,
2734  .confirmationText = confirmText,
2735  .confirmationToken = CHOICE_TOKEN,
2736  .tuneId = TUNE_TAP_CASUAL,
2737  .modal = true};
2738  onModalConfirm = callback;
2739  if (modalPageContext != NULL) {
2740  nbgl_pageRelease(modalPageContext);
2741  }
2742  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
2744 }
2745 
2758  const char *reviewTitle,
2759  const char *reviewSubTitle,
2760  const char *rejectText,
2761  nbgl_callback_t continueCallback,
2762  nbgl_callback_t rejectCallback)
2763 {
2764  reset_callbacks();
2765 
2766  nbgl_pageInfoDescription_t info = {.footerText = rejectText,
2767  .footerToken = QUIT_TOKEN,
2768  .tapActionText = NULL,
2769  .isSwipeable = true,
2770  .tapActionToken = CONTINUE_TOKEN,
2771  .topRightStyle = NO_BUTTON_STYLE,
2772  .actionButtonText = NULL,
2773  .tuneId = TUNE_TAP_CASUAL};
2774  info.centeredInfo.icon = icon;
2775  info.centeredInfo.text1 = reviewTitle;
2776  info.centeredInfo.text2 = reviewSubTitle;
2777  info.centeredInfo.text3 = "Swipe to review";
2779  info.centeredInfo.offsetY = 0;
2780  onQuit = rejectCallback;
2781  onContinue = continueCallback;
2782 
2783 #ifdef HAVE_PIEZO_SOUND
2784  // Play notification sound
2785  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2786 #endif // HAVE_PIEZO_SOUND
2787 
2788  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2789  nbgl_refresh();
2790 }
2805 void nbgl_useCaseRegularReview(uint8_t initPage,
2806  uint8_t nbPages,
2807  const char *rejectText,
2808  nbgl_layoutTouchCallback_t buttonCallback,
2809  nbgl_navCallback_t navCallback,
2810  nbgl_choiceCallback_t choiceCallback)
2811 {
2812  reset_callbacks();
2813 
2814  // memorize context
2815  onChoice = choiceCallback;
2816  onNav = navCallback;
2817  onControls = buttonCallback;
2818  forwardNavOnly = false;
2819  navType = REVIEW_NAV;
2820 
2821  // fill navigation structure
2822  UNUSED(rejectText);
2823  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2824 
2825  displayReviewPage(initPage, true);
2826 }
2827 
2840 void nbgl_useCaseStaticReview(const nbgl_contentTagValueList_t *tagValueList,
2841  const nbgl_pageInfoLongPress_t *infoLongPress,
2842  const char *rejectText,
2843  nbgl_choiceCallback_t callback)
2844 {
2845  uint8_t offset = 0;
2846 
2847  reset_callbacks();
2848  memset(&genericContext, 0, sizeof(genericContext));
2849 
2850  // memorize context
2851  onChoice = callback;
2852  navType = GENERIC_NAV;
2853  pageTitle = NULL;
2854  bundleNavContext.review.operationType = TYPE_OPERATION;
2855 
2856  genericContext.genericContents.contentsList = localContentsList;
2857  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2858 
2859  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2860  localContentsList[offset].type = TAG_VALUE_LIST;
2861  memcpy(&localContentsList[offset].content.tagValueList,
2862  tagValueList,
2863  sizeof(nbgl_contentTagValueList_t));
2864  offset++;
2865  }
2866 
2867  localContentsList[offset].type = INFO_LONG_PRESS;
2868  memcpy(&localContentsList[offset].content.infoLongPress,
2869  infoLongPress,
2870  sizeof(nbgl_pageInfoLongPress_t));
2871  localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
2872  offset++;
2873 
2874  genericContext.genericContents.nbContents = offset;
2875 
2876  // compute number of pages & fill navigation structure
2877  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2878  UNUSED(rejectText);
2879  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2880 
2881  displayGenericContextPage(0, true);
2882 }
2883 
2898  const nbgl_pageInfoLongPress_t *infoLongPress,
2899  const char *rejectText,
2900  nbgl_choiceCallback_t callback)
2901 {
2902  uint8_t offset = 0;
2903 
2904  reset_callbacks();
2905  memset(&genericContext, 0, sizeof(genericContext));
2906 
2907  // memorize context
2908  onChoice = callback;
2909  navType = GENERIC_NAV;
2910  pageTitle = NULL;
2911 
2912  genericContext.genericContents.contentsList = localContentsList;
2913  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2914 
2915  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2916  localContentsList[offset].type = TAG_VALUE_LIST;
2917  memcpy(&localContentsList[offset].content.tagValueList,
2918  tagValueList,
2919  sizeof(nbgl_contentTagValueList_t));
2920  offset++;
2921  }
2922 
2923  localContentsList[offset].type = INFO_BUTTON;
2924  localContentsList[offset].content.infoButton.text = infoLongPress->text;
2925  localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
2926  localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
2927  localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
2928  localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
2929  offset++;
2930 
2931  genericContext.genericContents.nbContents = offset;
2932 
2933  // compute number of pages & fill navigation structure
2934  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2935  UNUSED(rejectText);
2936  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2937 
2938  displayGenericContextPage(0, true);
2939 }
2940 
2957 void nbgl_useCaseReview(nbgl_operationType_t operationType,
2958  const nbgl_contentTagValueList_t *tagValueList,
2959  const nbgl_icon_details_t *icon,
2960  const char *reviewTitle,
2961  const char *reviewSubTitle,
2962  const char *finishTitle,
2963  nbgl_choiceCallback_t choiceCallback)
2964 {
2965  useCaseReview(operationType,
2966  tagValueList,
2967  icon,
2968  reviewTitle,
2969  reviewSubTitle,
2970  finishTitle,
2971  NULL,
2972  choiceCallback,
2973  false,
2974  true);
2975 }
2976 
2995  const nbgl_contentTagValueList_t *tagValueList,
2996  const nbgl_icon_details_t *icon,
2997  const char *reviewTitle,
2998  const char *reviewSubTitle,
2999  const char *finishTitle,
3000  const nbgl_tipBox_t *tipBox,
3001  nbgl_choiceCallback_t choiceCallback)
3002 {
3003  useCaseReview(operationType,
3004  tagValueList,
3005  icon,
3006  reviewTitle,
3007  reviewSubTitle,
3008  finishTitle,
3009  tipBox,
3010  choiceCallback,
3011  false,
3012  true);
3013 }
3014 
3035  const nbgl_contentTagValueList_t *tagValueList,
3036  const nbgl_icon_details_t *icon,
3037  const char *reviewTitle,
3038  const char *reviewSubTitle,
3039  const char *finishTitle,
3040  const nbgl_tipBox_t *tipBox,
3041  nbgl_choiceCallback_t choiceCallback)
3042 {
3043  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3044 
3045  blindSigningContext.isStreaming = false;
3046  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3047  blindSigningContext.tagValueList = tagValueList;
3048  blindSigningContext.icon = icon;
3049  blindSigningContext.reviewTitle = reviewTitle;
3050  blindSigningContext.reviewSubTitle = reviewSubTitle;
3051  blindSigningContext.finishTitle = finishTitle;
3052  blindSigningContext.tipBox = tipBox;
3053  blindSigningContext.choiceCallback = choiceCallback;
3054 
3055  blindSigningWarning();
3056 }
3074  const nbgl_contentTagValueList_t *tagValueList,
3075  const nbgl_icon_details_t *icon,
3076  const char *reviewTitle,
3077  const char *reviewSubTitle,
3078  const char *finishTitle,
3079  nbgl_choiceCallback_t choiceCallback)
3080 {
3081  useCaseReview(operationType,
3082  tagValueList,
3083  icon,
3084  reviewTitle,
3085  reviewSubTitle,
3086  finishTitle,
3087  NULL,
3088  choiceCallback,
3089  true,
3090  true);
3091 }
3092 
3102  const char *rejectText,
3103  nbgl_callback_t rejectCallback)
3104 {
3105  reset_callbacks();
3106  memset(&genericContext, 0, sizeof(genericContext));
3107 
3108  // memorize context
3109  onQuit = rejectCallback;
3110  navType = GENERIC_NAV;
3111  pageTitle = NULL;
3112  bundleNavContext.review.operationType = TYPE_OPERATION;
3113 
3114  memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3115 
3116  // compute number of pages & fill navigation structure
3117  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3118  prepareNavInfo(true, nbPages, rejectText);
3119  navInfo.quitToken = QUIT_TOKEN;
3120 
3121 #ifdef HAVE_PIEZO_SOUND
3122  // Play notification sound
3123  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3124 #endif // HAVE_PIEZO_SOUND
3125 
3126  displayGenericContextPage(0, true);
3127 }
3128 
3142  const nbgl_icon_details_t *icon,
3143  const char *reviewTitle,
3144  const char *reviewSubTitle,
3145  nbgl_choiceCallback_t choiceCallback)
3146 {
3147  useCaseReviewStreamingStart(
3148  operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3149 }
3150 
3165  const nbgl_icon_details_t *icon,
3166  const char *reviewTitle,
3167  const char *reviewSubTitle,
3168  nbgl_choiceCallback_t choiceCallback)
3169 {
3170  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3171 
3172  blindSigningContext.isStreaming = true;
3173  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3174  blindSigningContext.icon = icon;
3175  blindSigningContext.reviewTitle = reviewTitle;
3176  blindSigningContext.reviewSubTitle = reviewSubTitle;
3177  blindSigningContext.choiceCallback = choiceCallback;
3178 
3179  blindSigningWarning();
3180 }
3181 
3196  nbgl_choiceCallback_t choiceCallback,
3197  nbgl_callback_t skipCallback)
3198 {
3199  // Should follow a call to nbgl_useCaseReviewStreamingStart
3200  memset(&genericContext, 0, sizeof(genericContext));
3201 
3202  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3203  bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3204 
3205  // memorize context
3206  onChoice = bundleNavReviewStreamingChoice;
3207  navType = STREAMING_NAV;
3208  pageTitle = NULL;
3209 
3210  genericContext.genericContents.contentsList = localContentsList;
3211  genericContext.genericContents.nbContents = 1;
3212  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3213 
3214  // Then the tag/value pairs
3215  STARTING_CONTENT.type = TAG_VALUE_LIST;
3216  memcpy(
3217  &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3218 
3219  // compute number of pages & fill navigation structure
3220  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3221  &genericContext.genericContents,
3222  0,
3223  (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3224  prepareNavInfo(true,
3226  getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3227  // if the operation is skippable
3228  if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3229  navInfo.progressIndicator = false;
3230  navInfo.skipText = "Skip";
3231  navInfo.skipToken = SKIP_TOKEN;
3232  }
3233 
3234  displayGenericContextPage(0, true);
3235 }
3236 
3248  nbgl_choiceCallback_t choiceCallback)
3249 {
3250  nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3251 }
3252 
3261 void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3262  nbgl_choiceCallback_t choiceCallback)
3263 {
3264  // Should follow a call to nbgl_useCaseReviewStreamingContinue
3265  memset(&genericContext, 0, sizeof(genericContext));
3266 
3267  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3268 
3269  // memorize context
3270  onChoice = bundleNavReviewStreamingChoice;
3271  navType = STREAMING_NAV;
3272  pageTitle = NULL;
3273 
3274  genericContext.genericContents.contentsList = localContentsList;
3275  genericContext.genericContents.nbContents = 1;
3276  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3277 
3278  // Eventually the long press page
3279  STARTING_CONTENT.type = INFO_LONG_PRESS;
3280  prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress,
3281  bundleNavContext.reviewStreaming.icon,
3282  finishTitle);
3283 
3284  // compute number of pages & fill navigation structure
3285  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3286  &genericContext.genericContents,
3287  0,
3288  (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3289  prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3290 
3291  displayGenericContextPage(0, true);
3292 }
3293 
3298 void nbgl_useCaseAddressConfirmationExt(const char *address,
3299  nbgl_choiceCallback_t callback,
3300  const nbgl_contentTagValueList_t *tagValueList)
3301 {
3302  reset_callbacks();
3303  memset(&genericContext, 0, sizeof(genericContext));
3304  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3305 
3306  // save context
3307  onChoice = callback;
3308  navType = GENERIC_NAV;
3309  pageTitle = NULL;
3310 
3311  genericContext.genericContents.contentsList = localContentsList;
3312  genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3313  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3314  prepareAddressConfirmationPages(
3315  address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3316 
3317  // fill navigation structure, common to all pages
3318  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3319 
3320  prepareNavInfo(true, nbPages, "Cancel");
3321 
3322 #ifdef HAVE_PIEZO_SOUND
3323  // Play notification sound
3324  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3325 #endif // HAVE_PIEZO_SOUND
3326 
3327  displayGenericContextPage(0, true);
3328 }
3329 
3346 void nbgl_useCaseAddressReview(const char *address,
3347  const nbgl_contentTagValueList_t *additionalTagValueList,
3348  const nbgl_icon_details_t *icon,
3349  const char *reviewTitle,
3350  const char *reviewSubTitle,
3351  nbgl_choiceCallback_t choiceCallback)
3352 {
3353  reset_callbacks();
3354  memset(&genericContext, 0, sizeof(genericContext));
3355  // release a potential modal
3356  if (addressConfirmationContext.modalLayout) {
3357  nbgl_layoutRelease(addressConfirmationContext.modalLayout);
3358  }
3359  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3360 
3361  // save context
3362  onChoice = choiceCallback;
3363  navType = GENERIC_NAV;
3364  pageTitle = NULL;
3365  bundleNavContext.review.operationType = TYPE_OPERATION;
3366 
3367  genericContext.genericContents.contentsList = localContentsList;
3368  genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
3369  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
3370 
3371  // First a centered info
3372  STARTING_CONTENT.type = EXTENDED_CENTER;
3373  prepareReviewFirstPage(
3374  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
3375  STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
3376 
3377  // Then the address confirmation pages
3378  prepareAddressConfirmationPages(
3379  address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
3380 
3381  // fill navigation structure, common to all pages
3382  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3383 
3384  prepareNavInfo(true, nbPages, "Cancel");
3385 
3386 #ifdef HAVE_PIEZO_SOUND
3387  // Play notification sound
3388  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3389 #endif // HAVE_PIEZO_SOUND
3390 
3391  displayGenericContextPage(0, true);
3392 }
3393 
3400 void nbgl_useCaseSpinner(const char *text)
3401 {
3402  pageContext = nbgl_pageDrawSpinner(NULL, (const char *) text);
3404 }
3405 
3406 #ifdef NBGL_KEYPAD
3426 void nbgl_useCaseKeypadDigits(const char *title,
3427  uint8_t minDigits,
3428  uint8_t maxDigits,
3429  uint8_t backToken,
3430  bool shuffled,
3431  tune_index_e tuneId,
3432  nbgl_pinValidCallback_t validatePinCallback,
3433  nbgl_layoutTouchCallback_t actionCallback)
3434 {
3435  keypadGenericUseCase(title,
3436  minDigits,
3437  maxDigits,
3438  backToken,
3439  shuffled,
3440  false,
3441  tuneId,
3442  validatePinCallback,
3443  actionCallback);
3444 }
3464 void nbgl_useCaseKeypadPIN(const char *title,
3465  uint8_t minDigits,
3466  uint8_t maxDigits,
3467  uint8_t backToken,
3468  bool shuffled,
3469  tune_index_e tuneId,
3470  nbgl_pinValidCallback_t validatePinCallback,
3471  nbgl_layoutTouchCallback_t actionCallback)
3472 {
3473  keypadGenericUseCase(title,
3474  minDigits,
3475  maxDigits,
3476  backToken,
3477  shuffled,
3478  true,
3479  tuneId,
3480  validatePinCallback,
3481  actionCallback);
3482 }
3483 #endif // NBGL_KEYPAD
3484 
3485 #endif // HAVE_SE_TOUCH
3486 #endif // NBGL_USE_CASE
@ LARGE_CASE_GRAY_INFO
Definition: nbgl_content.h:39
@ LARGE_CASE_INFO
text in BLACK and large case (INTER 32px), subText in black in Inter24px
Definition: nbgl_content.h:36
nbgl_contentTagValue_t *(* nbgl_contentTagValueCallback_t)(uint8_t pairIndex)
prototype of tag/value pair retrieval callback
Definition: nbgl_content.h:171
@ INFO_LONG_PRESS
a centered info and a long press button
Definition: nbgl_content.h:343
@ EXTENDED_CENTER
a centered content and a possible tip-box
Definition: nbgl_content.h:342
@ CHOICES_LIST
list of choices through radio buttons
Definition: nbgl_content.h:350
@ CENTERED_INFO
a centered info
Definition: nbgl_content.h:341
@ SWITCHES_LIST
list of switches with descriptions
Definition: nbgl_content.h:348
@ TAG_VALUE_DETAILS
a tag/value pair and a small button to get details.
Definition: nbgl_content.h:346
@ INFOS_LIST
list of infos with titles
Definition: nbgl_content.h:349
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
Definition: nbgl_content.h:347
@ TAG_VALUE_LIST
list of tag/value pairs
Definition: nbgl_content.h:345
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
Definition: nbgl_content.h:351
@ INFO_BUTTON
a centered info and a simple black button
Definition: nbgl_content.h:344
@ ENS_ALIAS
alias comes from ENS
Definition: nbgl_content.h:127
@ ADDRESS_BOOK_ALIAS
alias comes from Address Book
Definition: nbgl_content.h:128
@ QR_CODE_ALIAS
alias is an address to be displayed as a QR Code
Definition: nbgl_content.h:129
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
Definition: nbgl_content.h:180
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:572
nbgl_font_id_e
Definition: nbgl_fonts.h:140
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),...
Definition: nbgl_fonts.c:1177
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.
Definition: nbgl_fonts.c:1048
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:731
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
Definition: nbgl_layout.h:104
#define SIMPLE_FOOTER_HEIGHT
Definition: nbgl_layout.h:60
int nbgl_layoutAddTextContent(nbgl_layout_t *layout, const char *title, const char *description, const char *info)
Creates in the main container three text areas:
Definition: nbgl_layout.c:1422
#define PRE_TEXT_MARGIN
Definition: nbgl_layout.h:75
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.
#define TEXT_SUBTEXT_MARGIN
Definition: nbgl_layout.h:76
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...
Definition: nbgl_layout.c:1688
int nbgl_layoutAddCenteredInfo(nbgl_layout_t *layout, const nbgl_layoutCenteredInfo_t *info)
Creates an area on the center of the main panel, with a possible icon/image, a possible text in black...
Definition: nbgl_layout.c:1601
#define TOUCHABLE_BAR_HEIGHT
Definition: nbgl_layout.h:58
int nbgl_layoutDraw(nbgl_layout_t *layout)
Applies given layout. The screen will be redrawn.
Definition: nbgl_layout.c:3336
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
Definition: nbgl_layout.h:327
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
Definition: nbgl_layout.h:325
#define AVAILABLE_WIDTH
Definition: nbgl_layout.h:66
void * nbgl_layout_t
type shared externally
Definition: nbgl_layout.h:96
@ HEADER_EMPTY
empty space, to have a better vertical centering of centered info
Definition: nbgl_layout.h:406
@ HEADER_BACK_AND_TEXT
back key and optional text
Definition: nbgl_layout.h:407
#define SMALL_FOOTER_HEIGHT
Definition: nbgl_layout.h:59
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition: nbgl_layout.h:30
#define POST_SUBTEXT_MARGIN
Definition: nbgl_layout.h:77
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,...
Definition: nbgl_layout.c:2285
#define SMALL_CENTERING_HEADER
Definition: nbgl_layout.h:61
int nbgl_layoutRelease(nbgl_layout_t *layout)
Release the layout obtained with nbgl_layoutGet()
Definition: nbgl_layout.c:3367
#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...
Definition: nbgl_layout.c:2232
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
Definition: nbgl_layout.c:994
#define TOUCHABLE_HEADER_BAR_HEIGHT
Definition: nbgl_layout.h:56
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition: nbgl_obj.c:1560
#define KEYPAD_MAX_DIGITS
Definition: nbgl_obj.h:59
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:1570
#define WARNING_ICON
Definition: nbgl_obj.h:148
#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:1586
#define VALIDATE_KEY
Definition: nbgl_obj.h:27
#define QRCODE_ICON
Definition: nbgl_obj.h:146
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:625
nbgl_page_t * nbgl_pageDrawSpinner(nbgl_layoutTouchCallback_t onActionCallback, const char *text)
draw a spinner page with the given parameters. The spinner will "rotate" automatically every 800 ms
Definition: nbgl_page.c:307
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:264
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_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:496
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:337
int nbgl_pageRelease(nbgl_page_t *)
Release the page obtained with any of the nbgl_pageDrawXXX() functions.
Definition: nbgl_page.c:638
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:455
@ 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:317
#define MIN(x, y)
Definition: nbgl_types.h:79
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:287
@ FULL_COLOR_CLEAN_REFRESH
to be used for lock screen display (cleaner but longer refresh)
Definition: nbgl_types.h:290
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition: nbgl_types.h:292
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition: nbgl_types.h:289
@ FULL_COLOR_REFRESH
to be used for normal refresh
Definition: nbgl_types.h:288
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
DEPRECATED void nbgl_useCaseHome(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, bool withSettings, nbgl_callback_t topRightCallback, nbgl_callback_t quitCallback)
#define NB_MAX_LINES_IN_REVIEW
maximum number of lines for value field in review pages
Definition: nbgl_use_case.h:55
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)
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
Definition: nbgl_use_case.h:73
@ STRONG_HOME_ACTION
Black button, implicating the main action of the App.
void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
#define NB_MAX_LINES_IN_DETAILS
maximum number of lines for value field in details pages
Definition: nbgl_use_case.h:46
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.
Definition: nbgl_use_case.h:84
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
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...
Definition: nbgl_use_case.h:97
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
Definition: nbgl_use_case.h:68
#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.
Definition: nbgl_use_case.h:78
void nbgl_useCaseGenericReview(const nbgl_genericContents_t *contents, const char *rejectText, nbgl_callback_t rejectCallback)
void nbgl_useCaseRegularReview(uint8_t initPage, uint8_t nbPages, const char *rejectText, nbgl_layoutTouchCallback_t buttonCallback, nbgl_navCallback_t navCallback, nbgl_choiceCallback_t choiceCallback)
#define TAGLINE_PART2
Definition: nbgl_use_case.h:79
void nbgl_useCaseStaticReview(const nbgl_contentTagValueList_t *tagValueList, const nbgl_pageInfoLongPress_t *infoLongPress, const char *rejectText, nbgl_choiceCallback_t callback)
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices, const nbgl_contentRadioChoice_t *choicesList, uint8_t startIndex, bool withNav)
void nbgl_useCaseReviewStatus(nbgl_reviewStatusType_t reviewStatusType, nbgl_callback_t quitCallback)
#define LAST_PAGE_FOR_REVIEW
value of page parameter used with navigation callback when "skip" button is touched,...
Definition: nbgl_use_case.h:38
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.
Definition: nbgl_use_case.h:89
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_pinValidCallback_t)(const uint8_t *content, uint8_t page)
prototype of pin validation callback function
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.
Definition: nbgl_content.h:306
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
Definition: nbgl_content.h:308
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
Definition: nbgl_content.h:307
uint8_t nbBars
number of elements in barTexts and tokens array
Definition: nbgl_content.h:309
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:81
uint16_t iconHug
vertical margin to apply on top and bottom of the icon
Definition: nbgl_content.h:87
const nbgl_icon_details_t * icon
the icon (can be null)
Definition: nbgl_content.h:82
const char * title
title in black large (can be null)
Definition: nbgl_content.h:83
const char * description
description in black small regular case (can be null)
Definition: nbgl_content.h:85
const char * subText
sub-text in dark gray regular small case
Definition: nbgl_content.h:86
bool padding
if true, apply a padding of 40px at the bottom
Definition: nbgl_content.h:88
const char * smallTitle
sub-title in black small bold case (can be null)
Definition: nbgl_content.h:84
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:57
const char * text2
second text (can be null)
Definition: nbgl_content.h:59
const char * text1
first text (can be null)
Definition: nbgl_content.h:58
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
Definition: nbgl_content.h:65
int16_t offsetY
vertical shift to apply to this info (if >0, shift to bottom)
Definition: nbgl_content.h:67
const char * text3
third text (can be null)
Definition: nbgl_content.h:61
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:63
This structure contains data to build a centered info + simple black button content.
Definition: nbgl_content.h:109
const char * buttonText
text of the long press button
Definition: nbgl_content.h:112
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:111
const char * text
centered text in large case
Definition: nbgl_content.h:110
This structure contains data to build a INFOS_LIST content.
Definition: nbgl_content.h:268
uint8_t nbInfos
number of elements in infoTypes and infoContents array
Definition: nbgl_content.h:274
const char *const * infoContents
array of contents of infos (in black)
Definition: nbgl_content.h:270
const char *const * infoTypes
array of types of infos (in black/bold)
Definition: nbgl_content.h:269
const nbgl_contentValueExt_t * infoExtensions
Definition: nbgl_content.h:272
This structure contains data to build a centered info + long press button content.
Definition: nbgl_content.h:94
const char * longPressText
text of the long press button
Definition: nbgl_content.h:97
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:96
const char * text
centered text in large case
Definition: nbgl_content.h:95
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
Definition: nbgl_content.h:286
uint8_t initChoice
index of the current choice
Definition: nbgl_content.h:295
const char *const * names
array of strings giving the choices (nbChoices)
Definition: nbgl_content.h:288
uint8_t nbChoices
number of choices
Definition: nbgl_content.h:294
This structure contains info to build a switch (on the right) with a description (on the left),...
Definition: nbgl_content.h:246
This structure contains [item,value] pair(s) and info about a potential "details" button,...
Definition: nbgl_content.h:222
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
Definition: nbgl_content.h:233
uint8_t confirmationToken
the token used as argument of the onActionCallback
Definition: nbgl_content.h:236
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
Definition: nbgl_content.h:223
const char * detailsButtonText
this text is used for "details" button (if NULL, no button)
Definition: nbgl_content.h:225
const nbgl_icon_details_t * detailsButtonIcon
icon to use in details button
Definition: nbgl_content.h:224
This structure contains a list of [tag,value] pairs.
Definition: nbgl_content.h:185
nbgl_contentActionCallback_t actionCallback
called when a valueIcon is touched on a given pair
Definition: nbgl_content.h:200
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
Definition: nbgl_content.h:187
bool wrapping
if set to true, value text will be wrapped on ' ' to avoid cutting words
Definition: nbgl_content.h:198
uint8_t startIndex
index of the first pair to get with callback
Definition: nbgl_content.h:191
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
Definition: nbgl_content.h:188
This structure contains a [tag,value] pair.
Definition: nbgl_content.h:148
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
Definition: nbgl_content.h:156
const nbgl_icon_details_t * valueIcon
Definition: nbgl_content.h:153
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
Definition: nbgl_content.h:160
const char * value
string giving the value name
Definition: nbgl_content.h:150
const char * item
string giving the tag name
Definition: nbgl_content.h:149
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
Definition: nbgl_content.h:136
const char * fullValue
full string of the value when used as an alias
Definition: nbgl_content.h:137
nbgl_contentValueAliasType_t aliasType
type of alias
Definition: nbgl_content.h:142
const char * explanation
Definition: nbgl_content.h:138
This structure contains data to build a content.
Definition: nbgl_content.h:374
nbgl_content_u content
Definition: nbgl_content.h:376
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
Definition: nbgl_content.h:378
nbgl_contentType_t type
type of page content in the content union
Definition: nbgl_content.h:375
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
Structure containing all information when creating a layout. This structure must be passed as argumen...
Definition: nbgl_layout.h:171
nbgl_layoutTouchCallback_t onActionCallback
the callback to be called on any action on the layout
Definition: nbgl_layout.h:185
This structure contains info to build a header.
Definition: nbgl_layout.h:419
nbgl_layoutHeaderType_t type
type of header
Definition: nbgl_layout.h:420
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_layout.h:266
const char * text2
second text (can be null)
Definition: nbgl_layout.h:269
const char * url
URL for QR code.
Definition: nbgl_layout.h:267
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
nbgl_layoutCenteredInfo_t centeredInfo
description of the centered info to be used
Definition: nbgl_page.h:150
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.
Definition: nbgl_content.h:260
uint8_t nbSwitches
number of elements in switches and tokens array
Definition: nbgl_content.h:262
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
Definition: nbgl_content.h:261
Structure containing all specific information when creating a NBGL step.
Definition: nbgl_flow.h:43
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
const nbgl_contentInfoList_t infos
infos pairs displayed in modal.
const char * modalTitle
title given to modal window displayed when tip-box is touched
const char * text
text of the tip-box
nbgl_contentType_t type
type of page content in the following union
const nbgl_icon_details_t * icon
icon of the tip-box
Union of the different type of contents.
Definition: nbgl_content.h:357
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition: nbgl_content.h:366
nbgl_contentInfoLongPress_t infoLongPress
INFO_LONG_PRESS type
Definition: nbgl_content.h:360
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition: nbgl_content.h:364
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition: nbgl_content.h:362
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition: nbgl_content.h:358
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition: nbgl_content.h:368
nbgl_contentExtendedCenter_t extendedCenter
EXTENDED_CENTER type
Definition: nbgl_content.h:359
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition: nbgl_content.h:365
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition: nbgl_content.h:361
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition: nbgl_content.h:367
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