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  .tuneId = TUNE_TAP_CASUAL};
1253 
1254  if (modalPageContext != NULL) {
1255  nbgl_pageRelease(modalPageContext);
1256  }
1257  modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1258 
1260 }
1261 
1262 #ifdef NBGL_QRCODE
1263 static void displayAddressQRCode(void)
1264 {
1265  // display the address as QR Code
1266  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1267  .withLeftBorder = true,
1268  .onActionCallback = &addressLayoutTouchCallbackQR,
1269  .tapActionText = NULL};
1270  nbgl_layoutHeader_t headerDesc = {
1271  .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1272  nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1273  .text1 = NULL,
1274  .centered = true,
1275  .offsetY = 0};
1276 
1277  addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1278  // add empty header for better look
1279  nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1280  // compute nb lines to check whether it shall be shorten (max is 3 lines)
1282  SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1283 
1284  if (nbLines <= QRCODE_NB_MAX_LINES) {
1285  qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1286  }
1287  else {
1288  // only keep beginning and end of text, and add ... in the middle
1289  nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1290  addressConfirmationContext.tagValuePair.value,
1292  QRCODE_NB_MAX_LINES,
1293  reducedAddress,
1294  QRCODE_REDUCED_ADDR_LEN);
1295  qrCode.text2 = reducedAddress; // in gray
1296  }
1297 
1298  nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1299 
1300  nbgl_layoutAddFooter(addressConfirmationContext.modalLayout, "Close", 0, TUNE_TAP_CASUAL);
1301  nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1302  nbgl_refresh();
1303 }
1304 
1305 // called when quit button is touched on Address verification page
1306 static void addressLayoutTouchCallbackQR(int token, uint8_t index)
1307 {
1308  UNUSED(token);
1309  UNUSED(index);
1310 
1311  // dismiss modal
1312  nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1313  addressConfirmationContext.modalLayout = NULL;
1315  nbgl_refresh();
1316 }
1317 #endif // NBGL_QRCODE
1318 
1319 // called when header is touched on modal page, to dismiss it
1320 static void modalLayoutTouchCallback(int token, uint8_t index)
1321 {
1322  UNUSED(token);
1323  UNUSED(index);
1324 
1325  // dismiss modal
1326  nbgl_layoutRelease(genericContext.modalLayout);
1327  genericContext.modalLayout = NULL;
1329  nbgl_refresh();
1330 }
1331 
1332 // called when skip button is touched in footer, during forward only review
1333 static void displaySkipWarning(void)
1334 {
1336  .cancelText = "Go back to review",
1337  .centeredInfo.text1 = "Skip review?",
1338  .centeredInfo.text2
1339  = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1340  .centeredInfo.text3 = NULL,
1341  .centeredInfo.style = LARGE_CASE_INFO,
1342  .centeredInfo.icon = &C_Important_Circle_64px,
1343  .centeredInfo.offsetY = 0,
1344  .confirmationText = "Yes, skip",
1345  .confirmationToken = SKIP_TOKEN,
1346  .tuneId = TUNE_TAP_CASUAL,
1347  .modal = true};
1348  if (modalPageContext != NULL) {
1349  nbgl_pageRelease(modalPageContext);
1350  }
1351  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1353 }
1354 
1355 #ifdef NBGL_KEYPAD
1356 // called to update the keypad and automatically show / hide:
1357 // - backspace key if no digits are present
1358 // - validation key if the min digit is reached
1359 // - keypad if the max number of digit is reached
1360 static void updateKeyPad(bool add)
1361 {
1362  bool enableValidate, enableBackspace, enableDigits;
1363  bool redrawKeypad = false;
1365 
1366  enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1367  enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1368  enableBackspace = (keypadContext.pinLen > 0);
1369  if (add) {
1370  if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1371  || // activate "validate" button on keypad
1372  (keypadContext.pinLen == keypadContext.pinMaxDigits)
1373  || // deactivate "digits" on keypad
1374  (keypadContext.pinLen == 1)) { // activate "backspace"
1375  redrawKeypad = true;
1376  }
1377  }
1378  else { // remove
1379  if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1380  (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1381  || // deactivate "validate" button on keypad
1382  (keypadContext.pinLen
1383  == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1384  redrawKeypad = true;
1385  }
1386  }
1387  if (keypadContext.hidden == true) {
1388  nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1389  }
1390  else {
1392  keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1393  }
1394  if (redrawKeypad) {
1396  keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1397  }
1398 
1399  if ((!add) && (keypadContext.pinLen == 0)) {
1400  // Full refresh to fully clean the bullets when reaching 0 digits
1401  mode = FULL_COLOR_REFRESH;
1402  }
1404 }
1405 
1406 // called when a key is touched on the keypad
1407 static void keypadCallback(char touchedKey)
1408 {
1409  switch (touchedKey) {
1410  case BACKSPACE_KEY:
1411  if (keypadContext.pinLen > 0) {
1412  keypadContext.pinLen--;
1413  keypadContext.pinEntry[keypadContext.pinLen] = 0;
1414  }
1415  updateKeyPad(false);
1416  break;
1417 
1418  case VALIDATE_KEY:
1419  // Gray out keyboard / buttons as a first user feedback
1420  nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1423 
1424  onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1425  break;
1426 
1427  default:
1428  if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1429  if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1430  keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1431  keypadContext.pinLen++;
1432  }
1433  updateKeyPad(true);
1434  }
1435  break;
1436  }
1437 }
1438 
1439 // called to create a keypad, with either hidden or visible digits
1440 static void keypadGenericUseCase(const char *title,
1441  uint8_t minDigits,
1442  uint8_t maxDigits,
1443  uint8_t backToken,
1444  bool shuffled,
1445  bool hidden,
1446  tune_index_e tuneId,
1447  nbgl_pinValidCallback_t validatePinCallback,
1448  nbgl_layoutTouchCallback_t actionCallback)
1449 {
1450  nbgl_layoutDescription_t layoutDescription = {0};
1452  .separationLine = true,
1453  .backAndText.token = backToken,
1454  .backAndText.tuneId = tuneId,
1455  .backAndText.text = NULL};
1456  int status = -1;
1457 
1458  if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1459  return;
1460  }
1461 
1462  reset_callbacks();
1463  // reset the keypad context
1464  memset(&keypadContext, 0, sizeof(KeypadContext_t));
1465 
1466  // get a layout
1467  layoutDescription.onActionCallback = actionCallback;
1468  layoutDescription.modal = false;
1469  layoutDescription.withLeftBorder = false;
1470  keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1471  keypadContext.hidden = hidden;
1472 
1473  // set back key in header
1474  nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1475 
1476  // add keypad
1477  status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1478  if (status < 0) {
1479  return;
1480  }
1481  // add keypad content
1482  status = nbgl_layoutAddKeypadContent(
1483  keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1484 
1485  if (status < 0) {
1486  return;
1487  }
1488 
1489  // validation pin callback
1490  onValidatePin = validatePinCallback;
1491  // pin code acceptable lengths
1492  keypadContext.pinMinDigits = minDigits;
1493  keypadContext.pinMaxDigits = maxDigits;
1494 
1495  nbgl_layoutDraw(keypadContext.layoutCtx);
1497 }
1498 #endif
1499 
1500 static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1501  uint8_t pageIdxStart,
1502  bool isLast,
1503  bool isSkippable)
1504 {
1505  uint8_t nbElements = 0;
1506  uint8_t nbPages = 0;
1507  uint8_t nbElementsInPage;
1508  uint8_t elemIdx = 0;
1509  bool flag;
1510 
1511  nbElements = getContentNbElement(content);
1512 
1513  while (nbElements > 0) {
1514  flag = 0;
1515  // if the current page is not the first one (or last), a navigation bar exists
1516  bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1517  if (content->type == TAG_VALUE_LIST) {
1518  nbElementsInPage = nbgl_useCaseGetNbTagValuesInPageExt(
1519  nbElements, &content->content.tagValueList, elemIdx, isSkippable, &flag);
1520  }
1521  else if (content->type == INFOS_LIST) {
1522  nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1523  nbElements, &content->content.infosList, elemIdx, hasNav);
1524  }
1525  else if (content->type == SWITCHES_LIST) {
1526  nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1527  nbElements, &content->content.switchesList, elemIdx, hasNav);
1528  }
1529  else if (content->type == BARS_LIST) {
1530  nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1531  nbElements, &content->content.barsList, elemIdx, hasNav);
1532  }
1533  else if (content->type == CHOICES_LIST) {
1534  nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1535  nbElements, &content->content.choicesList, elemIdx, hasNav);
1536  }
1537  else {
1538  nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1539  }
1540 
1541  elemIdx += nbElementsInPage;
1542  genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1543  nbElements -= nbElementsInPage;
1544  nbPages++;
1545  }
1546 
1547  return nbPages;
1548 }
1549 
1550 static uint8_t getNbPagesForGenericContents(const nbgl_genericContents_t *genericContents,
1551  uint8_t pageIdxStart,
1552  bool isSkippable)
1553 {
1554  uint8_t nbPages = 0;
1555  nbgl_content_t content;
1556  const nbgl_content_t *p_content;
1557 
1558  for (int i = 0; i < genericContents->nbContents; i++) {
1559  p_content = getContentAtIdx(genericContents, i, &content);
1560  if (p_content == NULL) {
1561  return 0;
1562  }
1563  nbPages += nbgl_useCaseGetNbPagesForContent(p_content,
1564  pageIdxStart + nbPages,
1565  (i == (genericContents->nbContents - 1)),
1566  isSkippable);
1567  }
1568 
1569  return nbPages;
1570 }
1571 
1572 static void prepareAddressConfirmationPages(const char *address,
1573  const nbgl_contentTagValueList_t *tagValueList,
1574  nbgl_content_t *firstPageContent,
1575  nbgl_content_t *secondPageContent)
1576 {
1577  nbgl_contentTagValueConfirm_t *tagValueConfirm;
1578 
1579  addressConfirmationContext.tagValuePair.item = "Address";
1580  addressConfirmationContext.tagValuePair.value = address;
1581 
1582  // First page
1583  firstPageContent->type = TAG_VALUE_CONFIRM;
1584  tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1585 
1586 #ifdef NBGL_QRCODE
1587  tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1588  // only use "Show as QR" when it's not the last page
1589  if (tagValueList != NULL) {
1590  tagValueConfirm->detailsButtonText = "Show as QR";
1591  }
1592  else {
1593  tagValueConfirm->detailsButtonText = NULL;
1594  }
1595  tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1596 #else // NBGL_QRCODE
1597  tagValueConfirm->detailsButtonText = NULL;
1598  tagValueConfirm->detailsButtonIcon = NULL;
1599 #endif // NBGL_QRCODE
1600  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1601  tagValueConfirm->tagValueList.nbPairs = 1;
1602  tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1603  tagValueConfirm->tagValueList.smallCaseForValue = false;
1604  tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1605  tagValueConfirm->tagValueList.wrapping = false;
1606  // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1607  // no confirmation button
1608  if (tagValueList != NULL) {
1609  tagValueConfirm->confirmationText = NULL;
1610  }
1611  else {
1612  // otherwise no tap to continue but a confirmation button
1613  tagValueConfirm->confirmationText = "Confirm";
1614  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1615  }
1616 
1617  // Second page if any:
1618  if (tagValueList != NULL) {
1619  // the second page is dedicated to the extended tag/value pairs
1620  secondPageContent->type = TAG_VALUE_CONFIRM;
1621  tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1622  tagValueConfirm->confirmationText = "Confirm";
1623  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1624  tagValueConfirm->detailsButtonText = NULL;
1625  tagValueConfirm->detailsButtonIcon = NULL;
1626  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1627  memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1628  }
1629 }
1630 
1631 static void bundleNavStartHome(void)
1632 {
1633  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1634 
1635  useCaseHomeExt(context->appName,
1636  context->appIcon,
1637  context->tagline,
1638  context->settingContents != NULL ? true : false,
1639  &context->homeAction,
1640  bundleNavStartSettings,
1641  context->quitCallback);
1642 }
1643 
1644 static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1645 {
1646  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1647 
1648  nbgl_useCaseGenericSettings(context->appName,
1649  initSettingPage,
1650  context->settingContents,
1651  context->infosList,
1652  bundleNavStartHome);
1653 }
1654 
1655 static void bundleNavStartSettings(void)
1656 {
1657  bundleNavStartSettingsAtPage(0);
1658 }
1659 
1660 static void bundleNavReviewConfirmRejection(void)
1661 {
1662  bundleNavContext.review.choiceCallback(false);
1663 }
1664 
1665 static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1666  nbgl_callback_t callback)
1667 {
1668  const char *title;
1669  const char *confirmText;
1670  // clear skip and blind bits
1671  operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION);
1672  if (operationType == TYPE_TRANSACTION) {
1673  title = "Reject transaction?";
1674  confirmText = "Go back to transaction";
1675  }
1676  else if (operationType == TYPE_MESSAGE) {
1677  title = "Reject message?";
1678  confirmText = "Go back to message";
1679  }
1680  else {
1681  title = "Reject operation?";
1682  confirmText = "Go back to operation";
1683  }
1684 
1685  // display a choice to confirm/cancel rejection
1686  nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1687 }
1688 
1689 static void bundleNavReviewChoice(bool confirm)
1690 {
1691  if (confirm) {
1692  bundleNavContext.review.choiceCallback(true);
1693  }
1694  else {
1695  bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1696  bundleNavReviewConfirmRejection);
1697  }
1698 }
1699 
1700 static void bundleNavReviewStreamingConfirmRejection(void)
1701 {
1702  bundleNavContext.reviewStreaming.choiceCallback(false);
1703 }
1704 
1705 static void bundleNavReviewStreamingChoice(bool confirm)
1706 {
1707  if (confirm) {
1708  // Display a spinner if it wasn't the finish step
1709  if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1710  nbgl_useCaseSpinner("Processing");
1711  }
1712  bundleNavContext.reviewStreaming.choiceCallback(true);
1713  }
1714  else {
1715  bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1716  bundleNavReviewStreamingConfirmRejection);
1717  }
1718 }
1719 
1720 // function called when the warning page of Blind Signing review buttons are pressed
1721 static void blindSigningWarningCallback(bool confirm)
1722 {
1723  if (confirm) { // top button to exit
1724  blindSigningContext.choiceCallback(false);
1725  }
1726  else { // bottom button to continue to review
1727  if (blindSigningContext.isStreaming) {
1728  useCaseReviewStreamingStart(blindSigningContext.operationType,
1729  blindSigningContext.icon,
1730  blindSigningContext.reviewTitle,
1731  blindSigningContext.reviewSubTitle,
1732  blindSigningContext.choiceCallback,
1733  false);
1734  }
1735  else {
1736  useCaseReview(blindSigningContext.operationType,
1737  blindSigningContext.tagValueList,
1738  blindSigningContext.icon,
1739  blindSigningContext.reviewTitle,
1740  blindSigningContext.reviewSubTitle,
1741  blindSigningContext.finishTitle,
1742  blindSigningContext.tipBox,
1743  blindSigningContext.choiceCallback,
1744  false,
1745  false);
1746  }
1747  }
1748 }
1749 
1750 // function used to display the warning page when starting a Bling Signing review
1751 static void blindSigningWarning(void)
1752 {
1753  // Play notification sound
1754 #ifdef HAVE_PIEZO_SOUND
1755  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1756 #endif // HAVE_PIEZO_SOUND
1757  nbgl_useCaseChoice(&C_Warning_64px,
1758  "Blind signing ahead",
1759  "The details of this transaction or message are not fully verifiable. If "
1760  "you sign it, you could lose all "
1761  "your assets.",
1762  "Back to safety",
1763  "Continue anyway",
1764  blindSigningWarningCallback);
1765 }
1766 
1767 // function to factorize code for all simple reviews
1768 static void useCaseReview(nbgl_operationType_t operationType,
1769  const nbgl_contentTagValueList_t *tagValueList,
1770  const nbgl_icon_details_t *icon,
1771  const char *reviewTitle,
1772  const char *reviewSubTitle,
1773  const char *finishTitle,
1774  const nbgl_tipBox_t *tipBox,
1775  nbgl_choiceCallback_t choiceCallback,
1776  bool isLight,
1777  bool playNotifSound)
1778 {
1779  reset_callbacks();
1780  memset(&genericContext, 0, sizeof(genericContext));
1781 
1782  bundleNavContext.review.operationType = operationType;
1783  bundleNavContext.review.choiceCallback = choiceCallback;
1784 
1785  // memorize context
1786  onChoice = bundleNavReviewChoice;
1787  navType = GENERIC_NAV;
1788  pageTitle = NULL;
1789 
1790  genericContext.genericContents.contentsList = localContentsList;
1791  genericContext.genericContents.nbContents = 3;
1792  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
1793 
1794  // First a centered info
1795  STARTING_CONTENT.type = EXTENDED_CENTER;
1796  prepareReviewFirstPage(
1797  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1798  if (tipBox != NULL) {
1799  // do not display "Swipe to review" if a tip-box is displayed
1800  STARTING_CONTENT.content.extendedCenter.contentCenter.subText = NULL;
1801 
1802  STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
1803  STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
1804  STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
1805  STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
1806  tipBoxModalTitle = tipBox->modalTitle;
1807  // the only supported type yet is @ref INFOS_LIST
1808  if (tipBox->type == INFOS_LIST) {
1809  tipBoxInfoList.nbInfos = tipBox->infos.nbInfos;
1810  tipBoxInfoList.withExtensions = tipBox->infos.withExtensions;
1811  tipBoxInfoList.infoTypes = PIC(tipBox->infos.infoTypes);
1812  tipBoxInfoList.infoContents = PIC(tipBox->infos.infoContents);
1813  tipBoxInfoList.infoExtensions = PIC(tipBox->infos.infoExtensions);
1814  }
1815  }
1816 
1817  // Then the tag/value pairs
1818  localContentsList[1].type = TAG_VALUE_LIST;
1819  memcpy(&localContentsList[1].content.tagValueList,
1820  tagValueList,
1821  sizeof(nbgl_contentTagValueList_t));
1822  localContentsList[1].contentActionCallback = tagValueList->actionCallback;
1823 
1824  // The last page
1825  if (isLight) {
1826  localContentsList[2].type = INFO_BUTTON;
1827  prepareReviewLightLastPage(&localContentsList[2].content.infoButton, icon, finishTitle);
1828  }
1829  else {
1830  localContentsList[2].type = INFO_LONG_PRESS;
1831  prepareReviewLastPage(&localContentsList[2].content.infoLongPress, icon, finishTitle);
1832  }
1833 
1834  // compute number of pages & fill navigation structure
1835  uint8_t nbPages = getNbPagesForGenericContents(
1836  &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
1837  prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
1838 
1839  // Play notification sound if required
1840  if (playNotifSound) {
1841 #ifdef HAVE_PIEZO_SOUND
1842  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1843 #endif // HAVE_PIEZO_SOUND
1844  }
1845 
1846  displayGenericContextPage(0, true);
1847 }
1848 
1849 // function to factorize code for all streaming reviews
1850 static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
1851  const nbgl_icon_details_t *icon,
1852  const char *reviewTitle,
1853  const char *reviewSubTitle,
1854  nbgl_choiceCallback_t choiceCallback,
1855  bool playNotifSound)
1856 {
1857  reset_callbacks();
1858  memset(&genericContext, 0, sizeof(genericContext));
1859 
1860  bundleNavContext.reviewStreaming.operationType = operationType;
1861  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
1862  bundleNavContext.reviewStreaming.icon = icon;
1863 
1864  // memorize context
1865  onChoice = bundleNavReviewStreamingChoice;
1866  navType = STREAMING_NAV;
1867  pageTitle = NULL;
1868 
1869  genericContext.genericContents.contentsList = localContentsList;
1870  genericContext.genericContents.nbContents = 1;
1871  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
1872 
1873  // First a centered info
1874  STARTING_CONTENT.type = EXTENDED_CENTER;
1875  prepareReviewFirstPage(
1876  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1877 
1878  // compute number of pages & fill navigation structure
1879  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
1880  &genericContext.genericContents, 0, (operationType & SKIPPABLE_OPERATION));
1881  prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
1882  // no back button on first page
1883  navInfo.navWithButtons.backButton = false;
1884 
1885  // Play notification sound if required
1886  if (playNotifSound) {
1887 #ifdef HAVE_PIEZO_SOUND
1888  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1889 #endif // HAVE_PIEZO_SOUND
1890  }
1891 
1892  displayGenericContextPage(0, true);
1893 }
1894 
1911 static void useCaseHomeExt(const char *appName,
1912  const nbgl_icon_details_t *appIcon,
1913  const char *tagline,
1914  bool withSettings,
1915  nbgl_homeAction_t *homeAction,
1916  nbgl_callback_t topRightCallback,
1917  nbgl_callback_t quitCallback)
1918 {
1919  reset_callbacks();
1920 
1921  nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
1922  .centeredInfo.text1 = appName,
1923  .centeredInfo.text3 = NULL,
1924  .centeredInfo.style = LARGE_CASE_INFO,
1925  .centeredInfo.offsetY = 0,
1926  .footerText = NULL,
1927  .bottomButtonStyle = QUIT_APP_TEXT,
1928  .tapActionText = NULL,
1929  .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
1930  .topRightToken = CONTINUE_TOKEN,
1931  .tuneId = TUNE_TAP_CASUAL};
1932  if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
1933  // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
1934  info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
1935  onAction = homeAction->callback;
1936  info.actionButtonText = homeAction->text;
1937  info.actionButtonIcon = homeAction->icon;
1938  info.actionButtonStyle
1940  }
1941  else {
1942  info.bottomButtonsToken = QUIT_TOKEN;
1943  onAction = NULL;
1944  info.actionButtonText = NULL;
1945  info.actionButtonIcon = NULL;
1946  }
1947  if (tagline == NULL) {
1948  if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
1949  snprintf(appDescription,
1951  "This app enables signing\ntransactions on its network.");
1952  }
1953  else {
1954  snprintf(appDescription,
1956  "%s %s\n%s",
1957  TAGLINE_PART1,
1958  appName,
1959  TAGLINE_PART2);
1960  }
1961 
1962  // If there is more than 3 lines, it means the appName was split, so we put it on the next
1963  // line
1964  if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, appDescription, AVAILABLE_WIDTH, false)
1965  > 3) {
1966  snprintf(appDescription,
1968  "%s\n%s %s",
1969  TAGLINE_PART1,
1970  appName,
1971  TAGLINE_PART2);
1972  }
1973  info.centeredInfo.text2 = appDescription;
1974  }
1975  else {
1976  info.centeredInfo.text2 = tagline;
1977  }
1978 
1979  onContinue = topRightCallback;
1980  onQuit = quitCallback;
1981 
1982  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
1984 }
1985 
1994 static void displayDetails(const char *tag, const char *value, bool wrapping)
1995 {
1996  memset(&detailsContext, 0, sizeof(detailsContext));
1997 
1998  uint16_t nbLines
1999  = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
2000 
2001  // initialize context
2002  detailsContext.tag = tag;
2003  detailsContext.value = value;
2004  detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
2005  detailsContext.currentPage = 0;
2006  detailsContext.wrapping = wrapping;
2007  // add some spare for room lost with "..." substitution
2008  if (detailsContext.nbPages > 1) {
2009  uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
2010  uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
2011  / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
2012  uint8_t nbLinesInLastPage
2013  = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
2014 
2015  detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
2016  if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
2018  detailsContext.nbPages++;
2019  }
2020  }
2021 
2022  displayDetailsPage(0, true);
2023 }
2024 
2025 /**********************
2026  * GLOBAL FUNCTIONS
2027  **********************/
2028 
2042  const nbgl_contentTagValueList_t *tagValueList,
2043  uint8_t startIndex,
2044  bool *requireSpecificDisplay)
2045 {
2047  nbPairs, tagValueList, startIndex, false, requireSpecificDisplay);
2048 }
2049 
2064  const nbgl_contentTagValueList_t *tagValueList,
2065  uint8_t startIndex,
2066  bool isSkippable,
2067  bool *requireSpecificDisplay)
2068 {
2069  uint8_t nbPairsInPage = 0;
2070 #ifdef TARGET_STAX
2071  uint16_t currentHeight = 24; // upper margin
2072 #else // TARGET_STAX
2073  uint16_t currentHeight = 0; // upper margin
2074 #endif // TARGET_STAX
2075  uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
2076 
2077  // if the review is skippable, it means that there is less height for tag/value pairs
2078  // the small centering header becomes a touchable header
2079  if (isSkippable) {
2081  }
2082 
2083  *requireSpecificDisplay = false;
2084  while (nbPairsInPage < nbPairs) {
2085  const nbgl_layoutTagValue_t *pair;
2086  nbgl_font_id_e value_font;
2087  uint16_t nbLines;
2088 
2089  // margin between pairs
2090  // 12 or 24 px between each tag/value pair
2091  if (nbPairsInPage > 0) {
2092 #ifdef TARGET_STAX
2093  currentHeight += 12;
2094 #else // TARGET_STAX
2095  currentHeight += 24;
2096 #endif // TARGET_STAX
2097  }
2098  // fetch tag/value pair strings.
2099  if (tagValueList->pairs != NULL) {
2100  pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2101  }
2102  else {
2103  pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2104  }
2105 
2106  if (pair->forcePageStart && nbPairsInPage > 0) {
2107  // This pair must be at the top of a page
2108  break;
2109  }
2110 
2111  if (pair->centeredInfo) {
2112  if (nbPairsInPage > 0) {
2113  // This pair must be at the top of a page
2114  break;
2115  }
2116  else {
2117  // This pair is the only one of the page and has a specific display behavior
2118  nbPairsInPage = 1;
2119  *requireSpecificDisplay = true;
2120  break;
2121  }
2122  }
2123 
2124  // tag height
2125  currentHeight += nbgl_getTextHeightInWidth(
2126  SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2127  // space between tag and value
2128  currentHeight += 4;
2129  // set value font
2130  if (tagValueList->smallCaseForValue) {
2131  value_font = SMALL_REGULAR_FONT;
2132  }
2133  else {
2134  value_font = LARGE_MEDIUM_FONT;
2135  }
2136  // value height
2137  currentHeight += nbgl_getTextHeightInWidth(
2138  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2139  // nb lines for value
2140  nbLines = nbgl_getTextNbLinesInWidth(
2141  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2142  if ((currentHeight >= maxUsableHeight) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2143  if (nbPairsInPage == 0) {
2144  // Pair too long to fit in a single screen
2145  // It will be the only one of the page and has a specific display behavior
2146  nbPairsInPage = 1;
2147  *requireSpecificDisplay = true;
2148  }
2149  break;
2150  }
2151  nbPairsInPage++;
2152  }
2153  return nbPairsInPage;
2154 }
2155 
2166  const nbgl_contentInfoList_t *infosList,
2167  uint8_t startIndex,
2168  bool withNav)
2169 {
2170  uint8_t nbInfosInPage = 0;
2171  uint16_t currentHeight = 0;
2172  uint16_t previousHeight;
2173  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2174  const char *const *infoTypes = PIC(infosList->infoTypes);
2175  const char *const *infoContents = PIC(infosList->infoContents);
2176 
2177  while (nbInfosInPage < nbInfos) {
2178  // margin between infos
2179  currentHeight += PRE_TEXT_MARGIN;
2180 
2181  // type height
2182  currentHeight += nbgl_getTextHeightInWidth(
2183  SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2184  // space between type and content
2185  currentHeight += TEXT_SUBTEXT_MARGIN;
2186 
2187  // content height
2188  currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2189  PIC(infoContents[startIndex + nbInfosInPage]),
2191  true);
2192  currentHeight += POST_SUBTEXT_MARGIN; // under the content
2193  // if height is over the limit
2194  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2195  // if there was no nav, now there will be, so it can be necessary to remove the last
2196  // item
2197  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2198  nbInfosInPage--;
2199  }
2200  break;
2201  }
2202  previousHeight = currentHeight;
2203  nbInfosInPage++;
2204  }
2205  return nbInfosInPage;
2206 }
2207 
2218  const nbgl_contentSwitchesList_t *switchesList,
2219  uint8_t startIndex,
2220  bool withNav)
2221 {
2222  uint8_t nbSwitchesInPage = 0;
2223  uint16_t currentHeight = 0;
2224  uint16_t previousHeight;
2225  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2226  nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2227 
2228  while (nbSwitchesInPage < nbSwitches) {
2229  // margin between switches
2230  currentHeight += PRE_TEXT_MARGIN;
2231 
2232  // text height
2233  currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2234  switchArray[startIndex + nbSwitchesInPage].text,
2236  true);
2237  // space between text and sub-text
2238  currentHeight += TEXT_SUBTEXT_MARGIN;
2239 
2240  // sub-text height
2241  currentHeight
2242  += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2243  switchArray[startIndex + nbSwitchesInPage].subText,
2245  true);
2246  currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2247  // if height is over the limit
2248  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2249  // if there was no nav, now there will be, so it can be necessary to remove the last
2250  // item
2251  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2252  nbSwitchesInPage--;
2253  }
2254  break;
2255  }
2256  previousHeight = currentHeight;
2257  nbSwitchesInPage++;
2258  }
2259  return nbSwitchesInPage;
2260 }
2261 
2272  const nbgl_contentBarsList_t *barsList,
2273  uint8_t startIndex,
2274  bool withNav)
2275 {
2276  uint8_t nbBarsInPage = 0;
2277  uint16_t currentHeight = 0;
2278  uint16_t previousHeight;
2279  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2280 
2281  UNUSED(barsList);
2282  UNUSED(startIndex);
2283 
2284  while (nbBarsInPage < nbBars) {
2285  currentHeight += TOUCHABLE_BAR_HEIGHT;
2286  // if height is over the limit
2287  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2288  break;
2289  }
2290  previousHeight = currentHeight;
2291  nbBarsInPage++;
2292  }
2293  // if there was no nav, now there may will be, so it can be necessary to remove the last
2294  // item
2295  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2296  nbBarsInPage--;
2297  }
2298  return nbBarsInPage;
2299 }
2300 
2311  const nbgl_contentRadioChoice_t *choicesList,
2312  uint8_t startIndex,
2313  bool withNav)
2314 {
2315  uint8_t nbChoicesInPage = 0;
2316  uint16_t currentHeight = 0;
2317  uint16_t previousHeight;
2318  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2319 
2320  UNUSED(choicesList);
2321  UNUSED(startIndex);
2322 
2323  while (nbChoicesInPage < nbChoices) {
2324  currentHeight += TOUCHABLE_BAR_HEIGHT;
2325  // if height is over the limit
2326  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2327  // if there was no nav, now there will be, so it can be necessary to remove the last
2328  // item
2329  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2330  nbChoicesInPage--;
2331  }
2332  break;
2333  }
2334  previousHeight = currentHeight;
2335  nbChoicesInPage++;
2336  }
2337  return nbChoicesInPage;
2338 }
2339 
2347 {
2348  uint8_t nbPages = 0;
2349  uint8_t nbPairs = tagValueList->nbPairs;
2350  uint8_t nbPairsInPage;
2351  uint8_t i = 0;
2352  bool flag;
2353 
2354  while (i < tagValueList->nbPairs) {
2355  // upper margin
2356  nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2357  i += nbPairsInPage;
2358  nbPairs -= nbPairsInPage;
2359  nbPages++;
2360  }
2361  return nbPages;
2362 }
2363 
2368 void nbgl_useCaseHome(const char *appName,
2369  const nbgl_icon_details_t *appIcon,
2370  const char *tagline,
2371  bool withSettings,
2372  nbgl_callback_t topRightCallback,
2373  nbgl_callback_t quitCallback)
2374 {
2375  nbgl_homeAction_t homeAction = {0};
2376  useCaseHomeExt(
2377  appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2378 }
2379 
2384 void nbgl_useCaseHomeExt(const char *appName,
2385  const nbgl_icon_details_t *appIcon,
2386  const char *tagline,
2387  bool withSettings,
2388  const char *actionButtonText,
2389  nbgl_callback_t actionCallback,
2390  nbgl_callback_t topRightCallback,
2391  nbgl_callback_t quitCallback)
2392 {
2393  nbgl_homeAction_t homeAction = {.callback = actionCallback,
2394  .icon = NULL,
2395  .style = STRONG_HOME_ACTION,
2396  .text = actionButtonText};
2397 
2398  useCaseHomeExt(
2399  appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2400 }
2401 
2415 void nbgl_useCaseNavigableContent(const char *title,
2416  uint8_t initPage,
2417  uint8_t nbPages,
2418  nbgl_callback_t quitCallback,
2419  nbgl_navCallback_t navCallback,
2420  nbgl_layoutTouchCallback_t controlsCallback)
2421 {
2422  reset_callbacks();
2423 
2424  // memorize context
2425  onQuit = quitCallback;
2426  onNav = navCallback;
2427  onControls = controlsCallback;
2428  pageTitle = title;
2429  navType = SETTINGS_NAV;
2430 
2431  // fill navigation structure
2432  prepareNavInfo(false, nbPages, NULL);
2433 
2434  displaySettingsPage(initPage, true);
2435 }
2436 
2442 void nbgl_useCaseSettings(const char *title,
2443  uint8_t initPage,
2444  uint8_t nbPages,
2445  bool touchable,
2446  nbgl_callback_t quitCallback,
2447  nbgl_navCallback_t navCallback,
2448  nbgl_layoutTouchCallback_t controlsCallback)
2449 {
2450  UNUSED(touchable);
2452  title, initPage, nbPages, quitCallback, navCallback, controlsCallback);
2453 }
2454 
2467 void nbgl_useCaseGenericSettings(const char *appName,
2468  uint8_t initPage,
2469  const nbgl_genericContents_t *settingContents,
2470  const nbgl_contentInfoList_t *infosList,
2471  nbgl_callback_t quitCallback)
2472 {
2473  reset_callbacks();
2474  memset(&genericContext, 0, sizeof(genericContext));
2475 
2476  // memorize context
2477  onQuit = quitCallback;
2478  pageTitle = appName;
2479  navType = GENERIC_NAV;
2480 
2481  if (settingContents != NULL) {
2482  memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
2483  }
2484  if (infosList != NULL) {
2485  genericContext.hasFinishingContent = true;
2486  memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
2487  FINISHING_CONTENT.type = INFOS_LIST;
2488  memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
2489  }
2490 
2491  // fill navigation structure
2492  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2493  if (infosList != NULL) {
2494  nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true, false);
2495  }
2496 
2497  prepareNavInfo(false, nbPages, NULL);
2498 
2499  displayGenericContextPage(initPage, true);
2500 }
2501 
2513 void nbgl_useCaseGenericConfiguration(const char *title,
2514  uint8_t initPage,
2515  const nbgl_genericContents_t *contents,
2516  nbgl_callback_t quitCallback)
2517 {
2518  nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2519 }
2520 
2538  const char *appName,
2539  const nbgl_icon_details_t *appIcon,
2540  const char *tagline,
2541  const uint8_t
2542  initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
2543  const nbgl_genericContents_t *settingContents,
2544  const nbgl_contentInfoList_t *infosList,
2545  const nbgl_homeAction_t *action, // Set to NULL if no additional action
2546  nbgl_callback_t quitCallback)
2547 {
2548  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
2549 
2550  context->appName = appName;
2551  context->appIcon = appIcon;
2552  context->tagline = tagline;
2553  context->settingContents = settingContents;
2554  context->infosList = infosList;
2555  if (action != NULL) {
2556  memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
2557  }
2558  else {
2559  memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
2560  }
2561  context->quitCallback = quitCallback;
2562 
2563  if (initSettingPage != INIT_HOME_PAGE) {
2564  bundleNavStartSettingsAtPage(initSettingPage);
2565  }
2566  else {
2567  bundleNavStartHome();
2568  }
2569 }
2570 
2578 void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2579 {
2580  reset_callbacks();
2581 
2583  .tickerCallback = &tickerCallback,
2584  .tickerIntervale = 0, // not periodic
2585  .tickerValue = 3000 // 3 seconds
2586  };
2587  onQuit = quitCallback;
2588  if (isSuccess) {
2589 #ifdef HAVE_PIEZO_SOUND
2590  io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
2591 #endif // HAVE_PIEZO_SOUND
2592 
2593  pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
2594  }
2595  else {
2597  .footerText = NULL,
2598  .centeredInfo.icon = &C_Denied_Circle_64px,
2599  .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
2600  .centeredInfo.onTop = false,
2601  .centeredInfo.style = LARGE_CASE_INFO,
2602  .centeredInfo.text1 = message,
2603  .centeredInfo.text2 = NULL,
2604  .centeredInfo.text3 = NULL,
2605  .tapActionText = "",
2606  .isSwipeable = false,
2607  .tapActionToken = QUIT_TOKEN,
2608  .topRightStyle = NO_BUTTON_STYLE,
2609  .actionButtonText = NULL,
2610  .tuneId = TUNE_TAP_CASUAL};
2611  pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
2612  }
2614 }
2615 
2623  nbgl_callback_t quitCallback)
2624 {
2625  const char *msg;
2626  bool isSuccess;
2627  switch (reviewStatusType) {
2629  msg = "Operation signed";
2630  isSuccess = true;
2631  break;
2633  msg = "Operation rejected";
2634  isSuccess = false;
2635  break;
2637  msg = "Transaction signed";
2638  isSuccess = true;
2639  break;
2641  msg = "Transaction rejected";
2642  isSuccess = false;
2643  break;
2645  msg = "Message signed";
2646  isSuccess = true;
2647  break;
2649  msg = "Message rejected";
2650  isSuccess = false;
2651  break;
2653  msg = "Address verified";
2654  isSuccess = true;
2655  break;
2657  msg = "Address verification\ncancelled";
2658  isSuccess = false;
2659  break;
2660  default:
2661  return;
2662  }
2663  nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2664 }
2665 
2678 void nbgl_useCaseChoice(const nbgl_icon_details_t *icon,
2679  const char *message,
2680  const char *subMessage,
2681  const char *confirmText,
2682  const char *cancelText,
2683  nbgl_choiceCallback_t callback)
2684 {
2685  reset_callbacks();
2686 
2687  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2688  .centeredInfo.text1 = message,
2689  .centeredInfo.text2 = subMessage,
2690  .centeredInfo.text3 = NULL,
2691  .centeredInfo.style = LARGE_CASE_INFO,
2692  .centeredInfo.icon = icon,
2693  .centeredInfo.offsetY = 0,
2694  .confirmationText = confirmText,
2695  .confirmationToken = CHOICE_TOKEN,
2696  .tuneId = TUNE_TAP_CASUAL,
2697  .modal = false};
2698  // check params
2699  if ((confirmText == NULL) || (cancelText == NULL)) {
2700  return;
2701  }
2702  onChoice = callback;
2703  pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
2705 }
2706 
2720 void nbgl_useCaseConfirm(const char *message,
2721  const char *subMessage,
2722  const char *confirmText,
2723  const char *cancelText,
2724  nbgl_callback_t callback)
2725 {
2726  // Don't reset callback or nav context as this is just a modal.
2727 
2728  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2729  .centeredInfo.text1 = message,
2730  .centeredInfo.text2 = subMessage,
2731  .centeredInfo.text3 = NULL,
2732  .centeredInfo.style = LARGE_CASE_INFO,
2733  .centeredInfo.icon = &C_Important_Circle_64px,
2734  .centeredInfo.offsetY = 0,
2735  .confirmationText = confirmText,
2736  .confirmationToken = CHOICE_TOKEN,
2737  .tuneId = TUNE_TAP_CASUAL,
2738  .modal = true};
2739  onModalConfirm = callback;
2740  if (modalPageContext != NULL) {
2741  nbgl_pageRelease(modalPageContext);
2742  }
2743  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
2745 }
2746 
2759  const char *reviewTitle,
2760  const char *reviewSubTitle,
2761  const char *rejectText,
2762  nbgl_callback_t continueCallback,
2763  nbgl_callback_t rejectCallback)
2764 {
2765  reset_callbacks();
2766 
2767  nbgl_pageInfoDescription_t info = {.footerText = rejectText,
2768  .footerToken = QUIT_TOKEN,
2769  .tapActionText = NULL,
2770  .isSwipeable = true,
2771  .tapActionToken = CONTINUE_TOKEN,
2772  .topRightStyle = NO_BUTTON_STYLE,
2773  .actionButtonText = NULL,
2774  .tuneId = TUNE_TAP_CASUAL};
2775  info.centeredInfo.icon = icon;
2776  info.centeredInfo.text1 = reviewTitle;
2777  info.centeredInfo.text2 = reviewSubTitle;
2778  info.centeredInfo.text3 = "Swipe to review";
2780  info.centeredInfo.offsetY = 0;
2781  onQuit = rejectCallback;
2782  onContinue = continueCallback;
2783 
2784 #ifdef HAVE_PIEZO_SOUND
2785  // Play notification sound
2786  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2787 #endif // HAVE_PIEZO_SOUND
2788 
2789  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2790  nbgl_refresh();
2791 }
2806 void nbgl_useCaseRegularReview(uint8_t initPage,
2807  uint8_t nbPages,
2808  const char *rejectText,
2809  nbgl_layoutTouchCallback_t buttonCallback,
2810  nbgl_navCallback_t navCallback,
2811  nbgl_choiceCallback_t choiceCallback)
2812 {
2813  reset_callbacks();
2814 
2815  // memorize context
2816  onChoice = choiceCallback;
2817  onNav = navCallback;
2818  onControls = buttonCallback;
2819  forwardNavOnly = false;
2820  navType = REVIEW_NAV;
2821 
2822  // fill navigation structure
2823  UNUSED(rejectText);
2824  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2825 
2826  displayReviewPage(initPage, true);
2827 }
2828 
2841 void nbgl_useCaseStaticReview(const nbgl_contentTagValueList_t *tagValueList,
2842  const nbgl_pageInfoLongPress_t *infoLongPress,
2843  const char *rejectText,
2844  nbgl_choiceCallback_t callback)
2845 {
2846  uint8_t offset = 0;
2847 
2848  reset_callbacks();
2849  memset(&genericContext, 0, sizeof(genericContext));
2850 
2851  // memorize context
2852  onChoice = callback;
2853  navType = GENERIC_NAV;
2854  pageTitle = NULL;
2855  bundleNavContext.review.operationType = TYPE_OPERATION;
2856 
2857  genericContext.genericContents.contentsList = localContentsList;
2858  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2859 
2860  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2861  localContentsList[offset].type = TAG_VALUE_LIST;
2862  memcpy(&localContentsList[offset].content.tagValueList,
2863  tagValueList,
2864  sizeof(nbgl_contentTagValueList_t));
2865  offset++;
2866  }
2867 
2868  localContentsList[offset].type = INFO_LONG_PRESS;
2869  memcpy(&localContentsList[offset].content.infoLongPress,
2870  infoLongPress,
2871  sizeof(nbgl_pageInfoLongPress_t));
2872  localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
2873  offset++;
2874 
2875  genericContext.genericContents.nbContents = offset;
2876 
2877  // compute number of pages & fill navigation structure
2878  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2879  UNUSED(rejectText);
2880  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2881 
2882  displayGenericContextPage(0, true);
2883 }
2884 
2899  const nbgl_pageInfoLongPress_t *infoLongPress,
2900  const char *rejectText,
2901  nbgl_choiceCallback_t callback)
2902 {
2903  uint8_t offset = 0;
2904 
2905  reset_callbacks();
2906  memset(&genericContext, 0, sizeof(genericContext));
2907 
2908  // memorize context
2909  onChoice = callback;
2910  navType = GENERIC_NAV;
2911  pageTitle = NULL;
2912 
2913  genericContext.genericContents.contentsList = localContentsList;
2914  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2915 
2916  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2917  localContentsList[offset].type = TAG_VALUE_LIST;
2918  memcpy(&localContentsList[offset].content.tagValueList,
2919  tagValueList,
2920  sizeof(nbgl_contentTagValueList_t));
2921  offset++;
2922  }
2923 
2924  localContentsList[offset].type = INFO_BUTTON;
2925  localContentsList[offset].content.infoButton.text = infoLongPress->text;
2926  localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
2927  localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
2928  localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
2929  localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
2930  offset++;
2931 
2932  genericContext.genericContents.nbContents = offset;
2933 
2934  // compute number of pages & fill navigation structure
2935  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
2936  UNUSED(rejectText);
2937  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2938 
2939  displayGenericContextPage(0, true);
2940 }
2941 
2958 void nbgl_useCaseReview(nbgl_operationType_t operationType,
2959  const nbgl_contentTagValueList_t *tagValueList,
2960  const nbgl_icon_details_t *icon,
2961  const char *reviewTitle,
2962  const char *reviewSubTitle,
2963  const char *finishTitle,
2964  nbgl_choiceCallback_t choiceCallback)
2965 {
2966  useCaseReview(operationType,
2967  tagValueList,
2968  icon,
2969  reviewTitle,
2970  reviewSubTitle,
2971  finishTitle,
2972  NULL,
2973  choiceCallback,
2974  false,
2975  true);
2976 }
2977 
2996  const nbgl_contentTagValueList_t *tagValueList,
2997  const nbgl_icon_details_t *icon,
2998  const char *reviewTitle,
2999  const char *reviewSubTitle,
3000  const char *finishTitle,
3001  const nbgl_tipBox_t *tipBox,
3002  nbgl_choiceCallback_t choiceCallback)
3003 {
3004  useCaseReview(operationType,
3005  tagValueList,
3006  icon,
3007  reviewTitle,
3008  reviewSubTitle,
3009  finishTitle,
3010  tipBox,
3011  choiceCallback,
3012  false,
3013  true);
3014 }
3015 
3036  const nbgl_contentTagValueList_t *tagValueList,
3037  const nbgl_icon_details_t *icon,
3038  const char *reviewTitle,
3039  const char *reviewSubTitle,
3040  const char *finishTitle,
3041  const nbgl_tipBox_t *tipBox,
3042  nbgl_choiceCallback_t choiceCallback)
3043 {
3044  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3045 
3046  blindSigningContext.isStreaming = false;
3047  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3048  blindSigningContext.tagValueList = tagValueList;
3049  blindSigningContext.icon = icon;
3050  blindSigningContext.reviewTitle = reviewTitle;
3051  blindSigningContext.reviewSubTitle = reviewSubTitle;
3052  blindSigningContext.finishTitle = finishTitle;
3053  blindSigningContext.tipBox = tipBox;
3054  blindSigningContext.choiceCallback = choiceCallback;
3055 
3056  blindSigningWarning();
3057 }
3075  const nbgl_contentTagValueList_t *tagValueList,
3076  const nbgl_icon_details_t *icon,
3077  const char *reviewTitle,
3078  const char *reviewSubTitle,
3079  const char *finishTitle,
3080  nbgl_choiceCallback_t choiceCallback)
3081 {
3082  useCaseReview(operationType,
3083  tagValueList,
3084  icon,
3085  reviewTitle,
3086  reviewSubTitle,
3087  finishTitle,
3088  NULL,
3089  choiceCallback,
3090  true,
3091  true);
3092 }
3093 
3103  const char *rejectText,
3104  nbgl_callback_t rejectCallback)
3105 {
3106  reset_callbacks();
3107  memset(&genericContext, 0, sizeof(genericContext));
3108 
3109  // memorize context
3110  onQuit = rejectCallback;
3111  navType = GENERIC_NAV;
3112  pageTitle = NULL;
3113  bundleNavContext.review.operationType = TYPE_OPERATION;
3114 
3115  memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3116 
3117  // compute number of pages & fill navigation structure
3118  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3119  prepareNavInfo(true, nbPages, rejectText);
3120  navInfo.quitToken = QUIT_TOKEN;
3121 
3122 #ifdef HAVE_PIEZO_SOUND
3123  // Play notification sound
3124  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3125 #endif // HAVE_PIEZO_SOUND
3126 
3127  displayGenericContextPage(0, true);
3128 }
3129 
3143  const nbgl_icon_details_t *icon,
3144  const char *reviewTitle,
3145  const char *reviewSubTitle,
3146  nbgl_choiceCallback_t choiceCallback)
3147 {
3148  useCaseReviewStreamingStart(
3149  operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3150 }
3151 
3166  const nbgl_icon_details_t *icon,
3167  const char *reviewTitle,
3168  const char *reviewSubTitle,
3169  nbgl_choiceCallback_t choiceCallback)
3170 {
3171  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3172 
3173  blindSigningContext.isStreaming = true;
3174  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3175  blindSigningContext.icon = icon;
3176  blindSigningContext.reviewTitle = reviewTitle;
3177  blindSigningContext.reviewSubTitle = reviewSubTitle;
3178  blindSigningContext.choiceCallback = choiceCallback;
3179 
3180  blindSigningWarning();
3181 }
3182 
3197  nbgl_choiceCallback_t choiceCallback,
3198  nbgl_callback_t skipCallback)
3199 {
3200  // Should follow a call to nbgl_useCaseReviewStreamingStart
3201  memset(&genericContext, 0, sizeof(genericContext));
3202 
3203  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3204  bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3205 
3206  // memorize context
3207  onChoice = bundleNavReviewStreamingChoice;
3208  navType = STREAMING_NAV;
3209  pageTitle = NULL;
3210 
3211  genericContext.genericContents.contentsList = localContentsList;
3212  genericContext.genericContents.nbContents = 1;
3213  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3214 
3215  // Then the tag/value pairs
3216  STARTING_CONTENT.type = TAG_VALUE_LIST;
3217  memcpy(
3218  &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3219 
3220  // compute number of pages & fill navigation structure
3221  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3222  &genericContext.genericContents,
3223  0,
3224  (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3225  prepareNavInfo(true,
3227  getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3228  // if the operation is skippable
3229  if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3230  navInfo.progressIndicator = false;
3231  navInfo.skipText = "Skip";
3232  navInfo.skipToken = SKIP_TOKEN;
3233  }
3234 
3235  displayGenericContextPage(0, true);
3236 }
3237 
3249  nbgl_choiceCallback_t choiceCallback)
3250 {
3251  nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3252 }
3253 
3262 void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3263  nbgl_choiceCallback_t choiceCallback)
3264 {
3265  // Should follow a call to nbgl_useCaseReviewStreamingContinue
3266  memset(&genericContext, 0, sizeof(genericContext));
3267 
3268  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3269 
3270  // memorize context
3271  onChoice = bundleNavReviewStreamingChoice;
3272  navType = STREAMING_NAV;
3273  pageTitle = NULL;
3274 
3275  genericContext.genericContents.contentsList = localContentsList;
3276  genericContext.genericContents.nbContents = 1;
3277  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3278 
3279  // Eventually the long press page
3280  STARTING_CONTENT.type = INFO_LONG_PRESS;
3281  prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress,
3282  bundleNavContext.reviewStreaming.icon,
3283  finishTitle);
3284 
3285  // compute number of pages & fill navigation structure
3286  bundleNavContext.reviewStreaming.stepPageNb = getNbPagesForGenericContents(
3287  &genericContext.genericContents,
3288  0,
3289  (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION));
3290  prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3291 
3292  displayGenericContextPage(0, true);
3293 }
3294 
3299 void nbgl_useCaseAddressConfirmationExt(const char *address,
3300  nbgl_choiceCallback_t callback,
3301  const nbgl_contentTagValueList_t *tagValueList)
3302 {
3303  reset_callbacks();
3304  memset(&genericContext, 0, sizeof(genericContext));
3305  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3306 
3307  // save context
3308  onChoice = callback;
3309  navType = GENERIC_NAV;
3310  pageTitle = NULL;
3311 
3312  genericContext.genericContents.contentsList = localContentsList;
3313  genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3314  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3315  prepareAddressConfirmationPages(
3316  address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3317 
3318  // fill navigation structure, common to all pages
3319  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3320 
3321  prepareNavInfo(true, nbPages, "Cancel");
3322 
3323 #ifdef HAVE_PIEZO_SOUND
3324  // Play notification sound
3325  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3326 #endif // HAVE_PIEZO_SOUND
3327 
3328  displayGenericContextPage(0, true);
3329 }
3330 
3347 void nbgl_useCaseAddressReview(const char *address,
3348  const nbgl_contentTagValueList_t *additionalTagValueList,
3349  const nbgl_icon_details_t *icon,
3350  const char *reviewTitle,
3351  const char *reviewSubTitle,
3352  nbgl_choiceCallback_t choiceCallback)
3353 {
3354  reset_callbacks();
3355  memset(&genericContext, 0, sizeof(genericContext));
3356  // release a potential modal
3357  if (addressConfirmationContext.modalLayout) {
3358  nbgl_layoutRelease(addressConfirmationContext.modalLayout);
3359  }
3360  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3361 
3362  // save context
3363  onChoice = choiceCallback;
3364  navType = GENERIC_NAV;
3365  pageTitle = NULL;
3366  bundleNavContext.review.operationType = TYPE_OPERATION;
3367 
3368  genericContext.genericContents.contentsList = localContentsList;
3369  genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
3370  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
3371 
3372  // First a centered info
3373  STARTING_CONTENT.type = EXTENDED_CENTER;
3374  prepareReviewFirstPage(
3375  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
3376  STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
3377 
3378  // Then the address confirmation pages
3379  prepareAddressConfirmationPages(
3380  address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
3381 
3382  // fill navigation structure, common to all pages
3383  uint8_t nbPages = getNbPagesForGenericContents(&genericContext.genericContents, 0, false);
3384 
3385  prepareNavInfo(true, nbPages, "Cancel");
3386 
3387 #ifdef HAVE_PIEZO_SOUND
3388  // Play notification sound
3389  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3390 #endif // HAVE_PIEZO_SOUND
3391 
3392  displayGenericContextPage(0, true);
3393 }
3394 
3401 void nbgl_useCaseSpinner(const char *text)
3402 {
3403  pageContext = nbgl_pageDrawSpinner(NULL, (const char *) text);
3405 }
3406 
3407 #ifdef NBGL_KEYPAD
3427 void nbgl_useCaseKeypadDigits(const char *title,
3428  uint8_t minDigits,
3429  uint8_t maxDigits,
3430  uint8_t backToken,
3431  bool shuffled,
3432  tune_index_e tuneId,
3433  nbgl_pinValidCallback_t validatePinCallback,
3434  nbgl_layoutTouchCallback_t actionCallback)
3435 {
3436  keypadGenericUseCase(title,
3437  minDigits,
3438  maxDigits,
3439  backToken,
3440  shuffled,
3441  false,
3442  tuneId,
3443  validatePinCallback,
3444  actionCallback);
3445 }
3465 void nbgl_useCaseKeypadPIN(const char *title,
3466  uint8_t minDigits,
3467  uint8_t maxDigits,
3468  uint8_t backToken,
3469  bool shuffled,
3470  tune_index_e tuneId,
3471  nbgl_pinValidCallback_t validatePinCallback,
3472  nbgl_layoutTouchCallback_t actionCallback)
3473 {
3474  keypadGenericUseCase(title,
3475  minDigits,
3476  maxDigits,
3477  backToken,
3478  shuffled,
3479  true,
3480  tuneId,
3481  validatePinCallback,
3482  actionCallback);
3483 }
3484 #endif // NBGL_KEYPAD
3485 
3486 #endif // HAVE_SE_TOUCH
3487 #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:564
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:1169
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:1040
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:723
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:1431
#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:1789
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:1610
#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:3437
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
Definition: nbgl_layout.h:338
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
Definition: nbgl_layout.h:336
#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:417
@ HEADER_BACK_AND_TEXT
back key and optional text
Definition: nbgl_layout.h:418
#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:2386
#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:3468
#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:2333
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:995
#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:1561
#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:1571
#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:1587
#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:430
nbgl_layoutHeaderType_t type
type of header
Definition: nbgl_layout.h:431
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_layout.h:277
const char * text2
second text (can be null)
Definition: nbgl_layout.h:280
const char * url
URL for QR code.
Definition: nbgl_layout.h:278
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