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