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 addressConfirmationContext sharedContext.addressConfirmation
53 #define blindSigningContext sharedContext.blindSigning
54 
55 /**********************
56  * TYPEDEFS
57  **********************/
58 enum {
59  BACK_TOKEN = 0,
60  NEXT_TOKEN,
61  QUIT_TOKEN,
62  NAV_TOKEN,
63  SKIP_TOKEN,
64  CONTINUE_TOKEN,
65  ADDRESS_QRCODE_BUTTON_TOKEN,
66  ACTION_BUTTON_TOKEN,
67  CHOICE_TOKEN,
68  DETAILS_BUTTON_TOKEN,
69  CONFIRM_TOKEN,
70  REJECT_TOKEN,
71  VALUE_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 three contexts cannot happen simultaneously
123 typedef union {
124 #ifdef NBGL_KEYPAD
125  KeypadContext_t keypad;
126 #endif
127  AddressConfirmationContext_t addressConfirmation;
128  BlindSigningContext_t blindSigning;
129 } SharedContext_t;
130 
131 typedef struct {
132  nbgl_genericContents_t genericContents;
133  int8_t currentContentIdx;
134  uint8_t currentContentElementNb;
135  uint8_t currentElementIdx;
136  bool hasStartingContent;
137  bool hasFinishingContent;
138  const char *detailsItem;
139  const char *detailsvalue;
140  bool detailsWrapping;
142  *currentPairs; // to be used to retrieve the pairs with value alias
144  currentCallback; // to be used to retrieve the pairs with value alias
145 
146  nbgl_layout_t modalLayout;
147 } GenericContext_t;
148 
149 typedef struct {
150  const char *appName;
151  const nbgl_icon_details_t *appIcon;
152  const char *tagline;
153  const nbgl_genericContents_t *settingContents;
154  const nbgl_contentInfoList_t *infosList;
155  nbgl_homeAction_t homeAction;
156  nbgl_callback_t quitCallback;
157 } nbgl_homeAndSettingsContext_t;
158 
159 typedef struct {
160  nbgl_operationType_t operationType;
161  nbgl_choiceCallback_t choiceCallback;
162 } nbgl_reviewContext_t;
163 
164 typedef struct {
165  nbgl_operationType_t operationType;
166  nbgl_choiceCallback_t choiceCallback;
167  nbgl_callback_t skipCallback;
168  const nbgl_icon_details_t *icon;
169  uint8_t stepPageNb;
170 } nbgl_reviewStreamingContext_t;
171 
172 typedef union {
173  nbgl_homeAndSettingsContext_t homeAndSettings;
174  nbgl_reviewContext_t review;
175  nbgl_reviewStreamingContext_t reviewStreaming;
176 } nbgl_BundleNavContext_t;
177 
178 /**********************
179  * STATIC VARIABLES
180  **********************/
181 
182 // char buffers to build some strings
183 static char appDescription[APP_DESCRIPTION_MAX_LEN];
184 static char plugInDescription[APP_DESCRIPTION_MAX_LEN];
185 
186 // multi-purposes callbacks
187 static nbgl_callback_t onQuit;
188 static nbgl_callback_t onContinue;
189 static nbgl_callback_t onAction;
190 static nbgl_navCallback_t onNav;
191 static nbgl_layoutTouchCallback_t onControls;
192 static nbgl_contentActionCallback_t onContentAction;
193 static nbgl_choiceCallback_t onChoice;
194 static nbgl_callback_t onModalConfirm;
195 #ifdef NBGL_KEYPAD
196 static nbgl_pinValidCallback_t onValidatePin;
197 #endif
198 
199 // contexts for background and modal pages
200 static nbgl_page_t *pageContext;
201 static nbgl_page_t *modalPageContext;
202 
203 // context for pages
204 static const char *pageTitle;
205 
206 // context for tip-box
207 static const char *tipBoxModalTitle;
208 static nbgl_contentInfoList_t tipBoxInfoList;
209 
210 // context for navigation use case
211 static nbgl_pageNavigationInfo_t navInfo;
212 static bool forwardNavOnly;
213 static NavType_t navType;
214 
215 static DetailsContext_t detailsContext;
216 
217 // multi-purpose context shared for non-concurrent usages
218 static SharedContext_t sharedContext;
219 
220 // contexts for generic navigation
221 static GenericContext_t genericContext;
222 static nbgl_content_t
223  localContentsList[3]; // 3 needed for nbgl_useCaseReview (starting page / tags / final page)
224 static uint8_t genericContextPagesInfo[MAX_PAGE_NB / PAGES_PER_UINT8];
225 
226 // contexts for bundle navigation
227 static nbgl_BundleNavContext_t bundleNavContext;
228 
229 // indexed by nbgl_contentType_t
230 static const uint8_t nbMaxElementsPerContentType[] = {
231  1, // CENTERED_INFO
232  1, // EXTENDED_CENTER
233  1, // INFO_LONG_PRESS
234  1, // INFO_BUTTON
235  1, // TAG_VALUE_LIST (computed dynamically)
236  1, // TAG_VALUE_DETAILS
237  1, // TAG_VALUE_CONFIRM
238  3, // SWITCHES_LIST (computed dynamically)
239  3, // INFOS_LIST (computed dynamically)
240  5, // CHOICES_LIST (computed dynamically)
241  5, // BARS_LIST (computed dynamically)
242 };
243 
244 #ifdef NBGL_QRCODE
245 /* buffer to store reduced address under QR Code */
246 static char reducedAddress[QRCODE_REDUCED_ADDR_LEN];
247 #endif // NBGL_QRCODE
248 
249 /**********************
250  * STATIC FUNCTIONS
251  **********************/
252 static void displayReviewPage(uint8_t page, bool forceFullRefresh);
253 static void displayDetailsPage(uint8_t page, bool forceFullRefresh);
254 static void displayFullValuePage(const nbgl_contentTagValue_t *pair);
255 static void displayBlindWarning(nbgl_opType_t opType);
256 static void displayTipBoxModal(void);
257 static void displaySettingsPage(uint8_t page, bool forceFullRefresh);
258 static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh);
259 static void pageCallback(int token, uint8_t index);
260 #ifdef NBGL_QRCODE
261 static void displayAddressQRCode(void);
262 static void addressLayoutTouchCallbackQR(int token, uint8_t index);
263 #endif // NBGL_QRCODE
264 static void modalLayoutTouchCallback(int token, uint8_t index);
265 static void displaySkipWarning(void);
266 
267 static void bundleNavStartHome(void);
268 static void bundleNavStartSettingsAtPage(uint8_t initSettingPage);
269 static void bundleNavStartSettings(void);
270 
271 static void bundleNavReviewStreamingChoice(bool confirm);
272 static void blindSigningWarning(void);
273 static void useCaseReview(nbgl_operationType_t operationType,
274  const nbgl_contentTagValueList_t *tagValueList,
275  const nbgl_icon_details_t *icon,
276  const char *reviewTitle,
277  const char *reviewSubTitle,
278  const char *finishTitle,
279  const nbgl_tipBox_t *tipBox,
280  nbgl_choiceCallback_t choiceCallback,
281  bool isLight,
282  bool playNotifSound);
283 static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
284  const nbgl_icon_details_t *icon,
285  const char *reviewTitle,
286  const char *reviewSubTitle,
287  nbgl_choiceCallback_t choiceCallback,
288  bool playNotifSound);
289 static void useCaseHomeExt(const char *appName,
290  const nbgl_icon_details_t *appIcon,
291  const char *tagline,
292  bool withSettings,
293  nbgl_homeAction_t *homeAction,
294  nbgl_callback_t topRightCallback,
295  nbgl_callback_t quitCallback);
296 
297 static void reset_callbacks(void)
298 {
299  onQuit = NULL;
300  onContinue = NULL;
301  onAction = NULL;
302  onNav = NULL;
303  onControls = NULL;
304  onContentAction = NULL;
305  onChoice = NULL;
306  onModalConfirm = NULL;
307 #ifdef NBGL_KEYPAD
308  onValidatePin = NULL;
309 #endif
310 }
311 
312 // Helper to set genericContext page info
313 static void genericContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements, bool flag)
314 {
315  uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements) + SET_PAGE_FLAG(flag);
316 
317  genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
318  &= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
319  genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
320  |= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
321 }
322 
323 // Helper to get genericContext page info
324 static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool *flag)
325 {
326  uint8_t pageData = genericContextPagesInfo[pageIdx / PAGES_PER_UINT8]
327  >> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
328  if (nbElements != NULL) {
329  *nbElements = GET_PAGE_NB_ELEMENTS(pageData);
330  }
331  if (flag != NULL) {
332  *flag = GET_PAGE_FLAG(pageData);
333  }
334 }
335 
336 // Simple helper to get the number of elements inside a nbgl_content_t
337 static uint8_t getContentNbElement(const nbgl_content_t *content)
338 {
339  switch (content->type) {
340  case TAG_VALUE_LIST:
341  return content->content.tagValueList.nbPairs;
342  case SWITCHES_LIST:
343  return content->content.switchesList.nbSwitches;
344  case INFOS_LIST:
345  return content->content.infosList.nbInfos;
346  case CHOICES_LIST:
347  return content->content.choicesList.nbChoices;
348  case BARS_LIST:
349  return content->content.barsList.nbBars;
350  default:
351  return 1;
352  }
353 }
354 
355 // Helper to retrieve the content inside a nbgl_genericContents_t using
356 // either the contentsList or using the contentGetterCallback
357 static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
358  int8_t contentIdx,
359  nbgl_content_t *content)
360 {
361  if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
362  LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
363  return NULL;
364  }
365 
366  if (genericContents->callbackCallNeeded) {
367  // Retrieve content through callback, but first memset the content.
368  memset(content, 0, sizeof(nbgl_content_t));
369  genericContents->contentGetterCallback(contentIdx, content);
370  return content;
371  }
372  else {
373  // Retrieve content through list
374  return PIC(&genericContents->contentsList[contentIdx]);
375  }
376 }
377 
378 static void prepareNavInfo(bool isReview, uint8_t nbPages, const char *rejectText)
379 {
380  memset(&navInfo, 0, sizeof(navInfo));
381 
382  navInfo.nbPages = nbPages;
383  navInfo.tuneId = TUNE_TAP_CASUAL;
384  navInfo.progressIndicator = false;
385  navInfo.navType = NAV_WITH_BUTTONS;
386 
387  if (isReview == false) {
388  navInfo.navWithButtons.navToken = NAV_TOKEN;
389  navInfo.navWithButtons.backButton = true;
390  }
391  else {
392  navInfo.quitToken = REJECT_TOKEN;
393  navInfo.navWithButtons.quitText = rejectText;
394  navInfo.navWithButtons.navToken = NAV_TOKEN;
395  navInfo.navWithButtons.backButton
396  = ((navType == STREAMING_NAV) && (nbPages < 2)) ? false : true;
397  navInfo.navWithButtons.visiblePageIndicator = (navType != STREAMING_NAV);
398  }
399 }
400 
401 static void prepareReviewFirstPage(nbgl_contentCenter_t *contentCenter,
402  const nbgl_icon_details_t *icon,
403  const char *reviewTitle,
404  const char *reviewSubTitle)
405 {
406  contentCenter->icon = icon;
407  contentCenter->title = reviewTitle;
408  contentCenter->description = reviewSubTitle;
409  contentCenter->subText = "Swipe to review";
410  contentCenter->smallTitle = NULL;
411  contentCenter->iconHug = 0;
412  contentCenter->padding = false;
413 }
414 
415 static void prepareReviewLastPage(nbgl_contentInfoLongPress_t *infoLongPress,
416  const nbgl_icon_details_t *icon,
417  const char *finishTitle)
418 {
419  infoLongPress->text = finishTitle;
420  infoLongPress->icon = icon;
421  infoLongPress->longPressText = "Hold to sign";
422  infoLongPress->longPressToken = CONFIRM_TOKEN;
423 }
424 
425 static void prepareReviewLightLastPage(nbgl_contentInfoButton_t *infoButton,
426  const nbgl_icon_details_t *icon,
427  const char *finishTitle)
428 {
429  infoButton->text = finishTitle;
430  infoButton->icon = icon;
431  infoButton->buttonText = "Approve";
432  infoButton->buttonToken = CONFIRM_TOKEN;
433 }
434 
435 static const char *getRejectReviewText(nbgl_operationType_t operationType)
436 {
437  UNUSED(operationType);
438  return "Reject";
439 }
440 
441 // function called when navigating (or exiting) modal details pages
442 // or when skip choice is displayed
443 static void pageModalCallback(int token, uint8_t index)
444 {
445  LOG_DEBUG(USE_CASE_LOGGER, "pageModalCallback, token = %d, index = %d\n", token, index);
446  nbgl_pageRelease(modalPageContext);
447  modalPageContext = NULL;
448  if (token == NAV_TOKEN) {
449  if (index == EXIT_PAGE) {
450  // redraw the background layer
452  nbgl_refresh();
453  }
454  else {
455  displayDetailsPage(index, false);
456  }
457  }
458  if (token == QUIT_TOKEN) {
459  // redraw the background layer
461  nbgl_refresh();
462  }
463  else if (token == SKIP_TOKEN) {
464  if (index == 0) {
465  // display the last forward only review page, whatever it is
466  displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true);
467  }
468  else {
469  // display background, which should be the page where skip has been touched
471  nbgl_refresh();
472  }
473  }
474  else if (token == CHOICE_TOKEN) {
475  if (index == 0) {
476  if (onModalConfirm != NULL) {
477  onModalConfirm();
478  }
479  }
480  else {
481  // display background, which should be the page where skip has been touched
483  nbgl_refresh();
484  }
485  }
486 }
487 
488 // generic callback for all pages except modal
489 static void pageCallback(int token, uint8_t index)
490 {
491  LOG_DEBUG(USE_CASE_LOGGER, "pageCallback, token = %d, index = %d\n", token, index);
492  if (token == QUIT_TOKEN) {
493  if (onQuit != NULL) {
494  onQuit();
495  }
496  }
497  else if (token == CONTINUE_TOKEN) {
498  if (onContinue != NULL) {
499  onContinue();
500  }
501  }
502  else if (token == CHOICE_TOKEN) {
503  if (onChoice != NULL) {
504  onChoice((index == 0) ? true : false);
505  }
506  }
507  else if (token == ACTION_BUTTON_TOKEN) {
508  if ((index == 0) && (onAction != NULL)) {
509  onAction();
510  }
511  else if ((index == 1) && (onQuit != NULL)) {
512  onQuit();
513  }
514  }
515 #ifdef NBGL_QRCODE
516  else if (token == ADDRESS_QRCODE_BUTTON_TOKEN) {
517  displayAddressQRCode();
518  }
519 #endif // NBGL_QRCODE
520  else if (token == CONFIRM_TOKEN) {
521  if (onChoice != NULL) {
522  onChoice(true);
523  }
524  }
525  else if (token == REJECT_TOKEN) {
526  if (onChoice != NULL) {
527  onChoice(false);
528  }
529  }
530  else if (token == DETAILS_BUTTON_TOKEN) {
531  nbgl_useCaseViewDetails(genericContext.detailsItem,
532  genericContext.detailsvalue,
533  genericContext.detailsWrapping);
534  }
535  else if (token == NAV_TOKEN) {
536  if (index == EXIT_PAGE) {
537  if (onQuit != NULL) {
538  onQuit();
539  }
540  }
541  else {
542  if (navType == GENERIC_NAV || navType == STREAMING_NAV) {
543  displayGenericContextPage(index, false);
544  }
545  else if (navType == REVIEW_NAV) {
546  displayReviewPage(index, false);
547  }
548  else {
549  displaySettingsPage(index, false);
550  }
551  }
552  }
553  else if (token == NEXT_TOKEN) {
554  if (onNav != NULL) {
555  displayReviewPage(navInfo.activePage + 1, false);
556  }
557  else {
558  displayGenericContextPage(navInfo.activePage + 1, false);
559  }
560  }
561  else if (token == BACK_TOKEN) {
562  if (onNav != NULL) {
563  displayReviewPage(navInfo.activePage - 1, true);
564  }
565  else {
566  displayGenericContextPage(navInfo.activePage - 1, false);
567  }
568  }
569  else if (token == SKIP_TOKEN) {
570  // display a modal warning to confirm skip
571  displaySkipWarning();
572  }
573  else if (token == VALUE_ALIAS_TOKEN) {
574  // the icon next to value alias has been touched
575  const nbgl_contentTagValue_t *pair;
576  if (genericContext.currentPairs != NULL) {
577  pair = &genericContext.currentPairs[genericContext.currentElementIdx + index];
578  }
579  else {
580  pair = genericContext.currentCallback(genericContext.currentElementIdx + index);
581  }
582  displayFullValuePage(pair);
583  }
584  else if (token == BLIND_WARNING_TOKEN) {
585  if (navType == STREAMING_NAV) {
586  displayBlindWarning(bundleNavContext.reviewStreaming.operationType
588  }
589  else {
590  displayBlindWarning(bundleNavContext.review.operationType
592  }
593  }
594  else if (token == TIP_BOX_TOKEN) {
595  displayTipBoxModal();
596  }
597  else { // probably a control provided by caller
598  if (onContentAction != NULL) {
599  onContentAction(token, index, navInfo.activePage);
600  }
601  if (onControls != NULL) {
602  onControls(token, index);
603  }
604  }
605 }
606 
607 // callback used for confirmation
608 static void tickerCallback(void)
609 {
610  nbgl_pageRelease(pageContext);
611  if (onQuit != NULL) {
612  onQuit();
613  }
614 }
615 
616 // function used to display the current page in review
617 static void displaySettingsPage(uint8_t page, bool forceFullRefresh)
618 {
619  nbgl_pageContent_t content = {0};
620 
621  if ((onNav == NULL) || (onNav(page, &content) == false)) {
622  return;
623  }
624 
625  // override some fields
626  content.title = pageTitle;
627  content.isTouchableTitle = true;
628  content.titleToken = QUIT_TOKEN;
629  content.tuneId = TUNE_TAP_CASUAL;
630 
631  navInfo.activePage = page;
632  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
633 
634  if (forceFullRefresh) {
636  }
637  else {
639  }
640 }
641 
642 // function used to display the current page in review
643 static void displayReviewPage(uint8_t page, bool forceFullRefresh)
644 {
645  nbgl_pageContent_t content = {0};
646 
647  // ensure the page is valid
648  if ((navInfo.nbPages != 0) && (page >= (navInfo.nbPages))) {
649  return;
650  }
651  navInfo.activePage = page;
652  if ((onNav == NULL) || (onNav(navInfo.activePage, &content) == false)) {
653  return;
654  }
655 
656  // override some fields
657  content.title = NULL;
658  content.isTouchableTitle = false;
659  content.tuneId = TUNE_TAP_CASUAL;
660 
661  if (content.type == INFO_LONG_PRESS) { // last page
662  // for forward only review without known length...
663  // if we don't do that we cannot remove the '>' in the navigation bar at the last page
664  navInfo.nbPages = navInfo.activePage + 1;
665  content.infoLongPress.longPressToken = CONFIRM_TOKEN;
666  if (forwardNavOnly) {
667  // remove the "Skip" button
668  navInfo.skipText = NULL;
669  }
670  }
671 
672  // override smallCaseForValue for tag/value types to false
673  if (content.type == TAG_VALUE_DETAILS) {
674  content.tagValueDetails.tagValueList.smallCaseForValue = false;
675  // the maximum displayable number of lines for value is NB_MAX_LINES_IN_REVIEW (without More
676  // button)
677  content.tagValueDetails.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
678  }
679  else if (content.type == TAG_VALUE_LIST) {
680  content.tagValueList.smallCaseForValue = false;
681  }
682  else if (content.type == TAG_VALUE_CONFIRM) {
683  content.tagValueConfirm.tagValueList.smallCaseForValue = false;
684  // use confirm token for black button
685  content.tagValueConfirm.confirmationToken = CONFIRM_TOKEN;
686  }
687 
688  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &content);
689 
690  if (forceFullRefresh) {
692  }
693  else {
695  }
696 }
697 
698 // Helper that does the computing of which nbgl_content_t and which element in the content should be
699 // displayed for the next generic context navigation page
700 static const nbgl_content_t *genericContextComputeNextPageParams(uint8_t pageIdx,
701  nbgl_content_t *content,
702  uint8_t *p_nbElementsInNextPage,
703  bool *p_flag)
704 {
705  int8_t nextContentIdx = genericContext.currentContentIdx;
706  int16_t nextElementIdx = genericContext.currentElementIdx;
707  uint8_t nbElementsInNextPage;
708 
709  // Retrieve info on the next page
710  genericContextGetPageInfo(pageIdx, &nbElementsInNextPage, p_flag);
711  *p_nbElementsInNextPage = nbElementsInNextPage;
712 
713  // Handle forward navigation:
714  // add to current index the number of pairs of the currently active page
715  if (pageIdx > navInfo.activePage) {
716  uint8_t nbElementsInCurrentPage;
717 
718  genericContextGetPageInfo(navInfo.activePage, &nbElementsInCurrentPage, NULL);
719  nextElementIdx += nbElementsInCurrentPage;
720 
721  // Handle case where the content to be displayed is in the next content
722  // In such case start at element index 0.
723  if (nextElementIdx >= genericContext.currentContentElementNb) {
724  nextContentIdx += 1;
725  nextElementIdx = 0;
726  }
727  }
728 
729  // Handle backward navigation:
730  // add to current index the number of pairs of the currently active page
731  if (pageIdx < navInfo.activePage) {
732  // Backward navigation: remove to current index the number of pairs of the current page
733  nextElementIdx -= nbElementsInNextPage;
734 
735  // Handle case where the content to be displayed is in the previous content
736  // In such case set a negative number as element index so that it is handled
737  // later once the previous content is accessible so that its elements number
738  // can be retrieved.
739  if (nextElementIdx < 0) {
740  nextContentIdx -= 1;
741  nextElementIdx = -nbElementsInNextPage;
742  }
743  }
744 
745  const nbgl_content_t *p_content;
746  // Retrieve next content
747  if ((nextContentIdx == -1) && (genericContext.hasStartingContent)) {
748  p_content = &STARTING_CONTENT;
749  }
750  else if ((nextContentIdx == genericContext.genericContents.nbContents)
751  && (genericContext.hasFinishingContent)) {
752  p_content = &FINISHING_CONTENT;
753  }
754  else {
755  p_content = getContentAtIdx(&genericContext.genericContents, nextContentIdx, content);
756 
757  if (p_content == NULL) {
758  LOG_DEBUG(USE_CASE_LOGGER, "Fail to retrieve content\n");
759  return NULL;
760  }
761  }
762 
763  // Handle cases where we are going to display data from a new content:
764  // - navigation to a previous or to the next content
765  // - First page display (genericContext.currentContentElementNb == 0)
766  //
767  // In such case we need to:
768  // - Update genericContext.currentContentIdx
769  // - Update genericContext.currentContentElementNb
770  // - Update onContentAction callback
771  // - Update nextElementIdx in case it was previously not calculable
772  if ((nextContentIdx != genericContext.currentContentIdx)
773  || (genericContext.currentContentElementNb == 0)) {
774  genericContext.currentContentIdx = nextContentIdx;
775  genericContext.currentContentElementNb = getContentNbElement(p_content);
776  onContentAction = PIC(p_content->contentActionCallback);
777  if (nextElementIdx < 0) {
778  nextElementIdx = genericContext.currentContentElementNb + nextElementIdx;
779  }
780  }
781 
782  // Sanity check
783  if ((nextElementIdx < 0) || (nextElementIdx >= genericContext.currentContentElementNb)) {
785  "Invalid element index %d / %d\n",
786  nextElementIdx,
787  genericContext.currentContentElementNb);
788  return NULL;
789  }
790 
791  // Update genericContext elements
792  genericContext.currentElementIdx = nextElementIdx;
793  navInfo.activePage = pageIdx;
794 
795  return p_content;
796 }
797 
798 // Helper that generates a nbgl_pageContent_t to be displayed for the next generic context
799 // navigation page
800 static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
801  uint8_t nbElementsInPage,
802  bool flag,
803  nbgl_pageContent_t *pageContent)
804 {
805  uint8_t nextElementIdx = genericContext.currentElementIdx;
806 
807  pageContent->title = pageTitle;
808  pageContent->isTouchableTitle = false;
809  pageContent->titleToken = QUIT_TOKEN;
810  pageContent->tuneId = TUNE_TAP_CASUAL;
811 
812  pageContent->type = p_content->type;
813  switch (pageContent->type) {
814  case CENTERED_INFO:
815  memcpy(&pageContent->centeredInfo,
816  &p_content->content.centeredInfo,
817  sizeof(pageContent->centeredInfo));
818  break;
819  case EXTENDED_CENTER:
820  memcpy(&pageContent->extendedCenter,
821  &p_content->content.extendedCenter,
822  sizeof(pageContent->extendedCenter));
823  break;
824  case INFO_LONG_PRESS:
825  memcpy(&pageContent->infoLongPress,
826  &p_content->content.infoLongPress,
827  sizeof(pageContent->infoLongPress));
828  break;
829 
830  case INFO_BUTTON:
831  memcpy(&pageContent->infoButton,
832  &p_content->content.infoButton,
833  sizeof(pageContent->infoButton));
834  break;
835 
836  case TAG_VALUE_LIST: {
837  nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
838 
839  // memorize pairs (or callback) for usage when alias is used
840  genericContext.currentPairs = p_content->content.tagValueList.pairs;
841  genericContext.currentCallback = p_content->content.tagValueList.callback;
842 
843  if (flag) {
844  // Flag can be set if the pair is too long to fit or because it needs
845  // to be displayed as centered info.
846 
847  // First retrieve the pair
848  const nbgl_layoutTagValue_t *pair;
849 
850  if (p_content->content.tagValueList.pairs != NULL) {
851  pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
852  }
853  else {
854  pair = PIC(p_content->content.tagValueList.callback(nextElementIdx));
855  }
856 
857  if (pair->centeredInfo) {
858  pageContent->type = EXTENDED_CENTER;
859  prepareReviewFirstPage(&pageContent->extendedCenter.contentCenter,
860  pair->valueIcon,
861  pair->item,
862  pair->value);
863 
864  // Skip population of nbgl_contentTagValueList_t structure
865  p_tagValueList = NULL;
866  }
867  else {
868  // if the pair is too long to fit, we use a TAG_VALUE_DETAILS content
869  pageContent->type = TAG_VALUE_DETAILS;
870  pageContent->tagValueDetails.detailsButtonText = "More";
871  pageContent->tagValueDetails.detailsButtonIcon = NULL;
872  pageContent->tagValueDetails.detailsButtonToken = DETAILS_BUTTON_TOKEN;
873 
874  p_tagValueList = &pageContent->tagValueDetails.tagValueList;
875 
876  // Backup pair info for easy data access upon click on button
877  genericContext.detailsItem = pair->item;
878  genericContext.detailsvalue = pair->value;
879  genericContext.detailsWrapping = p_content->content.tagValueList.wrapping;
880  }
881  }
882 
883  if (p_tagValueList != NULL) {
884  p_tagValueList->nbPairs = nbElementsInPage;
885  p_tagValueList->token = p_content->content.tagValueList.token;
886  if (p_content->content.tagValueList.pairs != NULL) {
887  p_tagValueList->pairs
888  = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]);
889  // parse pairs to check if any contains an alias for value
890  for (uint8_t i = 0; i < nbElementsInPage; i++) {
891  if (p_tagValueList->pairs[i].aliasValue) {
892  p_tagValueList->token = VALUE_ALIAS_TOKEN;
893  break;
894  }
895  }
896  }
897  else {
898  p_tagValueList->pairs = NULL;
899  p_tagValueList->callback = p_content->content.tagValueList.callback;
900  p_tagValueList->startIndex = nextElementIdx;
901  // parse pairs to check if any contains an alias for value
902  for (uint8_t i = 0; i < nbElementsInPage; i++) {
903  const nbgl_layoutTagValue_t *pair
904  = PIC(p_content->content.tagValueList.callback(nextElementIdx + i));
905  if (pair->aliasValue) {
906  p_tagValueList->token = VALUE_ALIAS_TOKEN;
907  break;
908  }
909  }
910  }
911  p_tagValueList->smallCaseForValue = false;
912  p_tagValueList->nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW;
913  p_tagValueList->wrapping = p_content->content.tagValueList.wrapping;
914  }
915 
916  break;
917  }
918  case TAG_VALUE_CONFIRM:
919  memcpy(&pageContent->tagValueConfirm,
920  &p_content->content.tagValueConfirm,
921  sizeof(pageContent->tagValueConfirm));
922  break;
923  case SWITCHES_LIST:
924  pageContent->switchesList.nbSwitches = nbElementsInPage;
925  pageContent->switchesList.switches
926  = PIC(&p_content->content.switchesList.switches[nextElementIdx]);
927  break;
928  case INFOS_LIST:
929  pageContent->infosList.nbInfos = nbElementsInPage;
930  pageContent->infosList.infoTypes
931  = PIC(&p_content->content.infosList.infoTypes[nextElementIdx]);
932  pageContent->infosList.infoContents
933  = PIC(&p_content->content.infosList.infoContents[nextElementIdx]);
934  break;
935  case CHOICES_LIST:
936  memcpy(&pageContent->choicesList,
937  &p_content->content.choicesList,
938  sizeof(pageContent->choicesList));
939  pageContent->choicesList.nbChoices = nbElementsInPage;
940  pageContent->choicesList.names
941  = PIC(&p_content->content.choicesList.names[nextElementIdx]);
942  if ((p_content->content.choicesList.initChoice >= nextElementIdx)
943  && (p_content->content.choicesList.initChoice
944  < nextElementIdx + nbElementsInPage)) {
945  pageContent->choicesList.initChoice
946  = p_content->content.choicesList.initChoice - nextElementIdx;
947  }
948  else {
949  pageContent->choicesList.initChoice = nbElementsInPage;
950  }
951  break;
952  case BARS_LIST:
953  pageContent->barsList.nbBars = nbElementsInPage;
954  pageContent->barsList.barTexts
955  = PIC(&p_content->content.barsList.barTexts[nextElementIdx]);
956  pageContent->barsList.tokens = PIC(&p_content->content.barsList.tokens[nextElementIdx]);
957  pageContent->barsList.tuneId = p_content->content.barsList.tuneId;
958  break;
959  default:
960  LOG_DEBUG(USE_CASE_LOGGER, "Unsupported type %d\n", pageContent->type);
961  return false;
962  }
963 
964  bool isFirstOrLastPage
965  = ((p_content->type == CENTERED_INFO) || (p_content->type == EXTENDED_CENTER))
966  || (p_content->type == INFO_LONG_PRESS);
967  bool isStreamingNavAndBlindOperation
968  = (navType == STREAMING_NAV)
969  && (bundleNavContext.reviewStreaming.operationType & BLIND_OPERATION);
970  bool isGenericNavAndBlindOperation
971  = (navType == GENERIC_NAV) && (bundleNavContext.review.operationType & BLIND_OPERATION);
972 
973  // if first or last page of review and blind operation, add the warning top-right button
974  if (isFirstOrLastPage && (isStreamingNavAndBlindOperation || isGenericNavAndBlindOperation)) {
975  pageContent->topRightIcon = &WARNING_ICON;
976  pageContent->topRightToken = BLIND_WARNING_TOKEN;
977  }
978 
979  return true;
980 }
981 
982 // function used to display the current page in generic context navigation mode
983 static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh)
984 {
985  // Retrieve next page parameters
986  nbgl_content_t content;
987  uint8_t nbElementsInPage;
988  bool flag;
989  const nbgl_content_t *p_content = NULL;
990 
991  if (navType == STREAMING_NAV) {
992  if (pageIdx == LAST_PAGE_FOR_REVIEW) {
993  if (bundleNavContext.reviewStreaming.skipCallback != NULL) {
994  bundleNavContext.reviewStreaming.skipCallback();
995  }
996  return;
997  }
998  else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) {
999  bundleNavReviewStreamingChoice(true);
1000  return;
1001  }
1002  }
1003 
1004  if (navInfo.activePage == pageIdx) {
1005  p_content
1006  = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1007  }
1008  else if (navInfo.activePage < pageIdx) {
1009  // Support going more than one step forward.
1010  // It occurs when initializing a navigation on an arbitrary page
1011  for (int i = navInfo.activePage + 1; i <= pageIdx; i++) {
1012  p_content = genericContextComputeNextPageParams(i, &content, &nbElementsInPage, &flag);
1013  }
1014  }
1015  else {
1016  if (pageIdx - navInfo.activePage > 1) {
1017  // We don't support going more than one step backward as it doesn't occurs for now?
1018  LOG_DEBUG(USE_CASE_LOGGER, "Unsupported navigation\n");
1019  return;
1020  }
1021  p_content
1022  = genericContextComputeNextPageParams(pageIdx, &content, &nbElementsInPage, &flag);
1023  }
1024 
1025  if (p_content == NULL) {
1026  return;
1027  }
1028 
1029  // Create next page content
1030  nbgl_pageContent_t pageContent = {0};
1031  if (!genericContextPreparePageContent(p_content, nbElementsInPage, flag, &pageContent)) {
1032  return;
1033  }
1034 
1035  pageContext = nbgl_pageDrawGenericContent(&pageCallback, &navInfo, &pageContent);
1036 
1037  if (forceFullRefresh) {
1039  }
1040  else {
1042  }
1043 }
1044 
1045 // from the current details context, return a pointer on the details at the given page
1046 static const char *getDetailsPageAt(uint8_t detailsPage)
1047 {
1048  uint8_t page = 0;
1049  const char *currentChar = detailsContext.value;
1050  while (page < detailsPage) {
1051  uint16_t nbLines
1052  = nbgl_getTextNbLinesInWidth(SMALL_BOLD_FONT, currentChar, AVAILABLE_WIDTH, false);
1053  if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1054  uint16_t len;
1055  nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1056  currentChar,
1059  &len,
1060  detailsContext.wrapping);
1061  len -= 3;
1062  currentChar = currentChar + len;
1063  }
1064  page++;
1065  }
1066  return currentChar;
1067 }
1068 
1069 // function used to display the current page in details review mode
1070 static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh)
1071 {
1072  static nbgl_layoutTagValue_t currentPair;
1073  nbgl_pageNavigationInfo_t info = {.activePage = detailsPage,
1074  .nbPages = detailsContext.nbPages,
1075  .navType = NAV_WITH_BUTTONS,
1076  .quitToken = QUIT_TOKEN,
1077  .navWithButtons.navToken = NAV_TOKEN,
1078  .navWithButtons.quitButton = true,
1079  .navWithButtons.backButton = true,
1080  .navWithButtons.quitText = NULL,
1081  .progressIndicator = false,
1082  .tuneId = TUNE_TAP_CASUAL};
1083  nbgl_pageContent_t content = {.type = TAG_VALUE_LIST,
1084  .topRightIcon = NULL,
1085  .tagValueList.nbPairs = 1,
1086  .tagValueList.pairs = &currentPair,
1087  .tagValueList.smallCaseForValue = true,
1088  .tagValueList.wrapping = detailsContext.wrapping};
1089 
1090  if (modalPageContext != NULL) {
1091  nbgl_pageRelease(modalPageContext);
1092  }
1093  currentPair.item = detailsContext.tag;
1094  // if move backward or first page
1095  if (detailsPage <= detailsContext.currentPage) {
1096  // recompute current start from beginning
1097  currentPair.value = getDetailsPageAt(detailsPage);
1098  forceFullRefresh = true;
1099  }
1100  // else move forward
1101  else {
1102  currentPair.value = detailsContext.nextPageStart;
1103  }
1104  detailsContext.currentPage = detailsPage;
1106  SMALL_BOLD_FONT, currentPair.value, AVAILABLE_WIDTH, detailsContext.wrapping);
1107 
1108  if (nbLines > NB_MAX_LINES_IN_DETAILS) {
1109  uint16_t len;
1110  nbgl_getTextMaxLenInNbLines(SMALL_BOLD_FONT,
1111  currentPair.value,
1114  &len,
1115  detailsContext.wrapping);
1116  len -= 3;
1117  // memorize next position to save processing
1118  detailsContext.nextPageStart = currentPair.value + len;
1119  // use special feature to keep only NB_MAX_LINES_IN_DETAILS lines and replace the last 3
1120  // chars by "..."
1121  content.tagValueList.nbMaxLinesForValue = NB_MAX_LINES_IN_DETAILS;
1122  }
1123  else {
1124  detailsContext.nextPageStart = NULL;
1125  content.tagValueList.nbMaxLinesForValue = 0;
1126  }
1127  if (info.nbPages == 1) {
1128  // if only one page, no navigation bar, and use a footer instead
1129  info.navWithButtons.quitText = "Close";
1130  }
1131  modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1132 
1133  if (forceFullRefresh) {
1135  }
1136  else {
1138  }
1139 }
1140 
1141 // function used to display the content of a full value, when touching an alias of a tag/value pair
1142 static void displayFullValuePage(const nbgl_contentTagValue_t *pair)
1143 {
1144  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1145  .withLeftBorder = true,
1146  .onActionCallback = &modalLayoutTouchCallback,
1147  .tapActionText = NULL};
1149  .separationLine = false,
1150  .backAndText.token = 0,
1151  .backAndText.tuneId = TUNE_TAP_CASUAL,
1152  .backAndText.text = PIC(pair->item)};
1153  const char *info;
1154  genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1155  // add header with the tag part of the pair, to go back
1156  nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1157  // add full value text
1158  if (pair->extension->explanation == NULL) {
1159  if (pair->extension->aliasType == ENS_ALIAS) {
1160  info = "ENS names are resolved by Ledger backend.";
1161  }
1162  else if (pair->extension->aliasType == ADDRESS_BOOK_ALIAS) {
1163  info = "This account label comes from your Address Book in Ledger Live.";
1164  }
1165  else {
1166  info = "";
1167  }
1168  }
1169  else {
1170  info = pair->extension->explanation;
1171  }
1173  genericContext.modalLayout, pair->value, pair->extension->fullValue, info);
1174 
1175  // draw & refresh
1176  nbgl_layoutDraw(genericContext.modalLayout);
1177  nbgl_refresh();
1178 }
1179 
1180 // function used to display the modal warning when touching the alert symbol of a blind review
1181 static void displayBlindWarning(nbgl_opType_t opType)
1182 {
1183  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1184  .withLeftBorder = true,
1185  .onActionCallback = &modalLayoutTouchCallback,
1186  .tapActionText = NULL};
1188  .separationLine = false,
1189  .backAndText.token = 0,
1190  .backAndText.tuneId = TUNE_TAP_CASUAL,
1191  .backAndText.text = NULL};
1192  nbgl_layoutCenteredInfo_t centeredInfo
1193  = {.icon = NULL, .text3 = NULL, .style = LARGE_CASE_INFO, .offsetY = 0, .onTop = false};
1194  centeredInfo.text1 = "Security risk detected";
1195  if (opType == TYPE_TRANSACTION) {
1196  centeredInfo.text2
1197  = "This transaction cannot be fully decoded. If you sign it, you could be authorizing "
1198  "malicious actions that can drain your wallet.\n\n"
1199  "Learn more: ledger.com/e8";
1200  }
1201  else {
1202  centeredInfo.text2
1203  = "This message cannot be fully decoded. If you sign it, you could be authorizing "
1204  "malicious actions that can drain your wallet.\n\n"
1205  "Learn more: ledger.com/e8";
1206  }
1207  genericContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1208  // add header with the tag part of the pair, to go back
1209  nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc);
1210  // add full value text
1211  nbgl_layoutAddCenteredInfo(genericContext.modalLayout, &centeredInfo);
1212 
1213  // draw & refresh
1214  nbgl_layoutDraw(genericContext.modalLayout);
1215  nbgl_refresh();
1216 }
1217 
1218 // function used to display the modal containing tip-box infos
1219 static void displayTipBoxModal(void)
1220 {
1222  .nbPages = 1,
1223  .navType = NAV_WITH_BUTTONS,
1224  .quitToken = QUIT_TOKEN,
1225  .navWithButtons.navToken = NAV_TOKEN,
1226  .navWithButtons.quitButton = false,
1227  .navWithButtons.backButton = true,
1228  .navWithButtons.quitText = NULL,
1229  .progressIndicator = false,
1230  .tuneId = TUNE_TAP_CASUAL};
1231  nbgl_pageContent_t content = {.type = INFOS_LIST,
1232  .topRightIcon = NULL,
1233  .infosList.nbInfos = tipBoxInfoList.nbInfos,
1234  .infosList.infoTypes = tipBoxInfoList.infoTypes,
1235  .infosList.infoContents = tipBoxInfoList.infoContents,
1236  .title = tipBoxModalTitle,
1237  .titleToken = QUIT_TOKEN};
1238 
1239  if (modalPageContext != NULL) {
1240  nbgl_pageRelease(modalPageContext);
1241  }
1242  modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1243 
1245 }
1246 
1247 #ifdef NBGL_QRCODE
1248 static void displayAddressQRCode(void)
1249 {
1250  // display the address as QR Code
1251  nbgl_layoutDescription_t layoutDescription = {.modal = true,
1252  .withLeftBorder = true,
1253  .onActionCallback = &addressLayoutTouchCallbackQR,
1254  .tapActionText = NULL};
1255  nbgl_layoutHeader_t headerDesc = {
1256  .type = HEADER_EMPTY, .separationLine = false, .emptySpace.height = SMALL_CENTERING_HEADER};
1257  nbgl_layoutQRCode_t qrCode = {.url = addressConfirmationContext.tagValuePair.value,
1258  .text1 = NULL,
1259  .centered = true,
1260  .offsetY = 0};
1261 
1262  addressConfirmationContext.modalLayout = nbgl_layoutGet(&layoutDescription);
1263  // add empty header for better look
1264  nbgl_layoutAddHeader(addressConfirmationContext.modalLayout, &headerDesc);
1265  // compute nb lines to check whether it shall be shorten (max is 3 lines)
1267  SMALL_REGULAR_FONT, addressConfirmationContext.tagValuePair.value, AVAILABLE_WIDTH, false);
1268 
1269  if (nbLines <= QRCODE_NB_MAX_LINES) {
1270  qrCode.text2 = addressConfirmationContext.tagValuePair.value; // in gray
1271  }
1272  else {
1273  // only keep beginning and end of text, and add ... in the middle
1274  nbgl_textReduceOnNbLines(SMALL_REGULAR_FONT,
1275  addressConfirmationContext.tagValuePair.value,
1277  QRCODE_NB_MAX_LINES,
1278  reducedAddress,
1279  QRCODE_REDUCED_ADDR_LEN);
1280  qrCode.text2 = reducedAddress; // in gray
1281  }
1282 
1283  nbgl_layoutAddQRCode(addressConfirmationContext.modalLayout, &qrCode);
1284 
1285  nbgl_layoutAddFooter(addressConfirmationContext.modalLayout, "Close", 0, TUNE_TAP_CASUAL);
1286  nbgl_layoutDraw(addressConfirmationContext.modalLayout);
1287  nbgl_refresh();
1288 }
1289 
1290 // called when quit button is touched on Address verification page
1291 static void addressLayoutTouchCallbackQR(int token, uint8_t index)
1292 {
1293  UNUSED(token);
1294  UNUSED(index);
1295 
1296  // dismiss modal
1297  nbgl_layoutRelease(addressConfirmationContext.modalLayout);
1299  nbgl_refresh();
1300 }
1301 #endif // NBGL_QRCODE
1302 
1303 // called when header is touched on modal page, to dismiss it
1304 static void modalLayoutTouchCallback(int token, uint8_t index)
1305 {
1306  UNUSED(token);
1307  UNUSED(index);
1308 
1309  // dismiss modal
1310  nbgl_layoutRelease(genericContext.modalLayout);
1312  nbgl_refresh();
1313 }
1314 
1315 // called when skip button is touched in footer, during forward only review
1316 static void displaySkipWarning(void)
1317 {
1319  .cancelText = "Go back to review",
1320  .centeredInfo.text1 = "Skip review?",
1321  .centeredInfo.text2
1322  = "If you're sure you don't need to review all fields, you can skip straight to signing.",
1323  .centeredInfo.text3 = NULL,
1324  .centeredInfo.style = LARGE_CASE_INFO,
1325  .centeredInfo.icon = &C_Important_Circle_64px,
1326  .centeredInfo.offsetY = 0,
1327  .confirmationText = "Yes, skip",
1328  .confirmationToken = SKIP_TOKEN,
1329  .tuneId = TUNE_TAP_CASUAL,
1330  .modal = true};
1331  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
1333 }
1334 
1335 #ifdef NBGL_KEYPAD
1336 // called to update the keypad and automatically show / hide:
1337 // - backspace key if no digits are present
1338 // - validation key if the min digit is reached
1339 // - keypad if the max number of digit is reached
1340 static void updateKeyPad(bool add)
1341 {
1342  bool enableValidate, enableBackspace, enableDigits;
1343  bool redrawKeypad = false;
1345 
1346  enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
1347  enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
1348  enableBackspace = (keypadContext.pinLen > 0);
1349  if (add) {
1350  if ((keypadContext.pinLen == keypadContext.pinMinDigits)
1351  || // activate "validate" button on keypad
1352  (keypadContext.pinLen == keypadContext.pinMaxDigits)
1353  || // deactivate "digits" on keypad
1354  (keypadContext.pinLen == 1)) { // activate "backspace"
1355  redrawKeypad = true;
1356  }
1357  }
1358  else { // remove
1359  if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
1360  (keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
1361  || // deactivate "validate" button on keypad
1362  (keypadContext.pinLen
1363  == (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
1364  redrawKeypad = true;
1365  }
1366  }
1367  if (keypadContext.hidden == true) {
1368  nbgl_layoutUpdateKeypadContent(keypadContext.layoutCtx, true, keypadContext.pinLen, NULL);
1369  }
1370  else {
1372  keypadContext.layoutCtx, false, 0, (const char *) keypadContext.pinEntry);
1373  }
1374  if (redrawKeypad) {
1376  keypadContext.layoutCtx, 0, enableValidate, enableBackspace, enableDigits);
1377  }
1378 
1379  if ((!add) && (keypadContext.pinLen == 0)) {
1380  // Full refresh to fully clean the bullets when reaching 0 digits
1381  mode = FULL_COLOR_REFRESH;
1382  }
1384 }
1385 
1386 // called when a key is touched on the keypad
1387 static void keypadCallback(char touchedKey)
1388 {
1389  switch (touchedKey) {
1390  case BACKSPACE_KEY:
1391  if (keypadContext.pinLen > 0) {
1392  keypadContext.pinLen--;
1393  keypadContext.pinEntry[keypadContext.pinLen] = 0;
1394  }
1395  updateKeyPad(false);
1396  break;
1397 
1398  case VALIDATE_KEY:
1399  // Gray out keyboard / buttons as a first user feedback
1400  nbgl_layoutUpdateKeypad(keypadContext.layoutCtx, 0, false, false, true);
1403 
1404  onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
1405  break;
1406 
1407  default:
1408  if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1409  if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
1410  keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
1411  keypadContext.pinLen++;
1412  }
1413  updateKeyPad(true);
1414  }
1415  break;
1416  }
1417 }
1418 
1419 // called to create a keypad, with either hidden or visible digits
1420 static void keypadGenericUseCase(const char *title,
1421  uint8_t minDigits,
1422  uint8_t maxDigits,
1423  uint8_t backToken,
1424  bool shuffled,
1425  bool hidden,
1426  tune_index_e tuneId,
1427  nbgl_pinValidCallback_t validatePinCallback,
1428  nbgl_layoutTouchCallback_t actionCallback)
1429 {
1430  nbgl_layoutDescription_t layoutDescription = {0};
1432  .separationLine = true,
1433  .backAndText.token = backToken,
1434  .backAndText.tuneId = tuneId,
1435  .backAndText.text = NULL};
1436  int status = -1;
1437 
1438  if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
1439  return;
1440  }
1441 
1442  reset_callbacks();
1443  // reset the keypad context
1444  memset(&keypadContext, 0, sizeof(KeypadContext_t));
1445 
1446  // get a layout
1447  layoutDescription.onActionCallback = actionCallback;
1448  layoutDescription.modal = false;
1449  layoutDescription.withLeftBorder = false;
1450  keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);
1451  keypadContext.hidden = hidden;
1452 
1453  // set back key in header
1454  nbgl_layoutAddHeader(keypadContext.layoutCtx, &headerDesc);
1455 
1456  // add keypad
1457  status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, keypadCallback, shuffled);
1458  if (status < 0) {
1459  return;
1460  }
1461  // add keypad content
1462  status = nbgl_layoutAddKeypadContent(
1463  keypadContext.layoutCtx, title, keypadContext.hidden, maxDigits, "");
1464 
1465  if (status < 0) {
1466  return;
1467  }
1468 
1469  // validation pin callback
1470  onValidatePin = validatePinCallback;
1471  // pin code acceptable lengths
1472  keypadContext.pinMinDigits = minDigits;
1473  keypadContext.pinMaxDigits = maxDigits;
1474 
1475  nbgl_layoutDraw(keypadContext.layoutCtx);
1477 }
1478 #endif
1479 
1480 static uint8_t nbgl_useCaseGetNbPagesForContent(const nbgl_content_t *content,
1481  uint8_t pageIdxStart,
1482  bool isLast)
1483 {
1484  uint8_t nbElements = 0;
1485  uint8_t nbPages = 0;
1486  uint8_t nbElementsInPage;
1487  uint8_t elemIdx = 0;
1488  bool flag;
1489 
1490  nbElements = getContentNbElement(content);
1491 
1492  while (nbElements > 0) {
1493  flag = 0;
1494  // if the current page is not the first one (or last), a navigation bar exists
1495  bool hasNav = !isLast || (pageIdxStart > 0) || (elemIdx > 0);
1496  if (content->type == TAG_VALUE_LIST) {
1497  nbElementsInPage = nbgl_useCaseGetNbTagValuesInPage(
1498  nbElements, &content->content.tagValueList, elemIdx, &flag);
1499  }
1500  else if (content->type == INFOS_LIST) {
1501  nbElementsInPage = nbgl_useCaseGetNbInfosInPage(
1502  nbElements, &content->content.infosList, elemIdx, hasNav);
1503  }
1504  else if (content->type == SWITCHES_LIST) {
1505  nbElementsInPage = nbgl_useCaseGetNbSwitchesInPage(
1506  nbElements, &content->content.switchesList, elemIdx, hasNav);
1507  }
1508  else if (content->type == BARS_LIST) {
1509  nbElementsInPage = nbgl_useCaseGetNbBarsInPage(
1510  nbElements, &content->content.barsList, elemIdx, hasNav);
1511  }
1512  else if (content->type == CHOICES_LIST) {
1513  nbElementsInPage = nbgl_useCaseGetNbChoicesInPage(
1514  nbElements, &content->content.choicesList, elemIdx, hasNav);
1515  }
1516  else {
1517  nbElementsInPage = MIN(nbMaxElementsPerContentType[content->type], nbElements);
1518  }
1519 
1520  elemIdx += nbElementsInPage;
1521  genericContextSetPageInfo(pageIdxStart + nbPages, nbElementsInPage, flag);
1522  nbElements -= nbElementsInPage;
1523  nbPages++;
1524  }
1525 
1526  return nbPages;
1527 }
1528 
1529 static uint8_t nbgl_useCaseGetNbPagesForGenericContents(
1530  const nbgl_genericContents_t *genericContents,
1531  uint8_t pageIdxStart)
1532 {
1533  uint8_t nbPages = 0;
1534  nbgl_content_t content;
1535  const nbgl_content_t *p_content;
1536 
1537  for (int i = 0; i < genericContents->nbContents; i++) {
1538  p_content = getContentAtIdx(genericContents, i, &content);
1539  if (p_content == NULL) {
1540  return 0;
1541  }
1542  nbPages += nbgl_useCaseGetNbPagesForContent(
1543  p_content, pageIdxStart + nbPages, (i == (genericContents->nbContents - 1)));
1544  }
1545 
1546  return nbPages;
1547 }
1548 
1549 static void prepareAddressConfirmationPages(const char *address,
1550  const nbgl_contentTagValueList_t *tagValueList,
1551  nbgl_content_t *firstPageContent,
1552  nbgl_content_t *secondPageContent)
1553 {
1554  nbgl_contentTagValueConfirm_t *tagValueConfirm;
1555 
1556  addressConfirmationContext.tagValuePair.item = "Address";
1557  addressConfirmationContext.tagValuePair.value = address;
1558 
1559  // First page
1560  firstPageContent->type = TAG_VALUE_CONFIRM;
1561  tagValueConfirm = &firstPageContent->content.tagValueConfirm;
1562 
1563 #ifdef NBGL_QRCODE
1564  tagValueConfirm->detailsButtonIcon = &QRCODE_ICON;
1565  // only use "Show as QR" when it's not the last page
1566  if (tagValueList != NULL) {
1567  tagValueConfirm->detailsButtonText = "Show as QR";
1568  }
1569  else {
1570  tagValueConfirm->detailsButtonText = NULL;
1571  }
1572  tagValueConfirm->detailsButtonToken = ADDRESS_QRCODE_BUTTON_TOKEN;
1573 #else // NBGL_QRCODE
1574  tagValueConfirm->detailsButtonText = NULL;
1575  tagValueConfirm->detailsButtonIcon = NULL;
1576 #endif // NBGL_QRCODE
1577  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1578  tagValueConfirm->tagValueList.nbPairs = 1;
1579  tagValueConfirm->tagValueList.pairs = &addressConfirmationContext.tagValuePair;
1580  tagValueConfirm->tagValueList.smallCaseForValue = false;
1581  tagValueConfirm->tagValueList.nbMaxLinesForValue = 0;
1582  tagValueConfirm->tagValueList.wrapping = false;
1583  // if it's an extended address verif, it takes 2 pages, so display a "Tap to continue", and
1584  // no confirmation button
1585  if (tagValueList != NULL) {
1586  tagValueConfirm->confirmationText = NULL;
1587  }
1588  else {
1589  // otherwise no tap to continue but a confirmation button
1590  tagValueConfirm->confirmationText = "Confirm";
1591  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1592  }
1593 
1594  // Second page if any:
1595  if (tagValueList != NULL) {
1596  // the second page is dedicated to the extended tag/value pairs
1597  secondPageContent->type = TAG_VALUE_CONFIRM;
1598  tagValueConfirm = &secondPageContent->content.tagValueConfirm;
1599  tagValueConfirm->confirmationText = "Confirm";
1600  tagValueConfirm->confirmationToken = CONFIRM_TOKEN;
1601  tagValueConfirm->detailsButtonText = NULL;
1602  tagValueConfirm->detailsButtonIcon = NULL;
1603  tagValueConfirm->tuneId = TUNE_TAP_CASUAL;
1604  memcpy(&tagValueConfirm->tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
1605  }
1606 }
1607 
1608 static void bundleNavStartHome(void)
1609 {
1610  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1611 
1612  useCaseHomeExt(context->appName,
1613  context->appIcon,
1614  context->tagline,
1615  context->settingContents != NULL ? true : false,
1616  &context->homeAction,
1617  bundleNavStartSettings,
1618  context->quitCallback);
1619 }
1620 
1621 static void bundleNavStartSettingsAtPage(uint8_t initSettingPage)
1622 {
1623  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
1624 
1625  nbgl_useCaseGenericSettings(context->appName,
1626  initSettingPage,
1627  context->settingContents,
1628  context->infosList,
1629  bundleNavStartHome);
1630 }
1631 
1632 static void bundleNavStartSettings(void)
1633 {
1634  bundleNavStartSettingsAtPage(0);
1635 }
1636 
1637 static void bundleNavReviewConfirmRejection(void)
1638 {
1639  bundleNavContext.review.choiceCallback(false);
1640 }
1641 
1642 static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operationType,
1643  nbgl_callback_t callback)
1644 {
1645  const char *title;
1646  const char *confirmText;
1647  // clear skip and blind bits
1648  operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION);
1649  if (operationType == TYPE_TRANSACTION) {
1650  title = "Reject transaction?";
1651  confirmText = "Go back to transaction";
1652  }
1653  else if (operationType == TYPE_MESSAGE) {
1654  title = "Reject message?";
1655  confirmText = "Go back to message";
1656  }
1657  else {
1658  title = "Reject operation?";
1659  confirmText = "Go back to operation";
1660  }
1661 
1662  // display a choice to confirm/cancel rejection
1663  nbgl_useCaseConfirm(title, NULL, "Yes, reject", confirmText, callback);
1664 }
1665 
1666 static void bundleNavReviewChoice(bool confirm)
1667 {
1668  if (confirm) {
1669  bundleNavContext.review.choiceCallback(true);
1670  }
1671  else {
1672  bundleNavReviewAskRejectionConfirmation(bundleNavContext.review.operationType,
1673  bundleNavReviewConfirmRejection);
1674  }
1675 }
1676 
1677 static void bundleNavReviewStreamingConfirmRejection(void)
1678 {
1679  bundleNavContext.reviewStreaming.choiceCallback(false);
1680 }
1681 
1682 static void bundleNavReviewStreamingChoice(bool confirm)
1683 {
1684  if (confirm) {
1685  // Display a spinner if it wasn't the finish step
1686  if (STARTING_CONTENT.type != INFO_LONG_PRESS) {
1687  nbgl_useCaseSpinner("Processing");
1688  }
1689  bundleNavContext.reviewStreaming.choiceCallback(true);
1690  }
1691  else {
1692  bundleNavReviewAskRejectionConfirmation(bundleNavContext.reviewStreaming.operationType,
1693  bundleNavReviewStreamingConfirmRejection);
1694  }
1695 }
1696 
1697 // function called when the warning page of Blind Signing review buttons are pressed
1698 static void blindSigningWarningCallback(bool confirm)
1699 {
1700  if (confirm) { // top button to exit
1701  blindSigningContext.choiceCallback(false);
1702  }
1703  else { // bottom button to continue to review
1704  if (blindSigningContext.isStreaming) {
1705  useCaseReviewStreamingStart(blindSigningContext.operationType,
1706  blindSigningContext.icon,
1707  blindSigningContext.reviewTitle,
1708  blindSigningContext.reviewSubTitle,
1709  blindSigningContext.choiceCallback,
1710  false);
1711  }
1712  else {
1713  useCaseReview(blindSigningContext.operationType,
1714  blindSigningContext.tagValueList,
1715  blindSigningContext.icon,
1716  blindSigningContext.reviewTitle,
1717  blindSigningContext.reviewSubTitle,
1718  blindSigningContext.finishTitle,
1719  blindSigningContext.tipBox,
1720  blindSigningContext.choiceCallback,
1721  false,
1722  false);
1723  }
1724  }
1725 }
1726 
1727 // function used to display the warning page when starting a Bling Signing review
1728 static void blindSigningWarning(void)
1729 {
1730  // Play notification sound
1731 #ifdef HAVE_PIEZO_SOUND
1732  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1733 #endif // HAVE_PIEZO_SOUND
1735  &C_Warning_64px,
1736  "Blind signing ahead",
1737  "This transaction's details are not fully verifiable. If you sign it, you could lose all "
1738  "your assets.",
1739  "Back to safety",
1740  "Continue anyway",
1741  blindSigningWarningCallback);
1742 }
1743 
1744 // function to factorize code for all simple reviews
1745 static void useCaseReview(nbgl_operationType_t operationType,
1746  const nbgl_contentTagValueList_t *tagValueList,
1747  const nbgl_icon_details_t *icon,
1748  const char *reviewTitle,
1749  const char *reviewSubTitle,
1750  const char *finishTitle,
1751  const nbgl_tipBox_t *tipBox,
1752  nbgl_choiceCallback_t choiceCallback,
1753  bool isLight,
1754  bool playNotifSound)
1755 {
1756  reset_callbacks();
1757  memset(&genericContext, 0, sizeof(genericContext));
1758 
1759  bundleNavContext.review.operationType = operationType;
1760  bundleNavContext.review.choiceCallback = choiceCallback;
1761 
1762  // memorize context
1763  onChoice = bundleNavReviewChoice;
1764  navType = GENERIC_NAV;
1765  pageTitle = NULL;
1766 
1767  genericContext.genericContents.contentsList = localContentsList;
1768  genericContext.genericContents.nbContents = 3;
1769  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
1770 
1771  // First a centered info
1772  STARTING_CONTENT.type = EXTENDED_CENTER;
1773  prepareReviewFirstPage(
1774  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1775  if (tipBox != NULL) {
1776  STARTING_CONTENT.content.extendedCenter.tipBox.icon = tipBox->icon;
1777  STARTING_CONTENT.content.extendedCenter.tipBox.text = tipBox->text;
1778  STARTING_CONTENT.content.extendedCenter.tipBox.token = TIP_BOX_TOKEN;
1779  STARTING_CONTENT.content.extendedCenter.tipBox.tuneId = TUNE_TAP_CASUAL;
1780  tipBoxModalTitle = tipBox->modalTitle;
1781  // the only supported type yet is @ref INFOS_LIST
1782  if (tipBox->type == INFOS_LIST) {
1783  memcpy(&tipBoxInfoList, &tipBox->infos, sizeof(nbgl_contentInfoList_t));
1784  }
1785  }
1786 
1787  // Then the tag/value pairs
1788  localContentsList[1].type = TAG_VALUE_LIST;
1789  memcpy(&localContentsList[1].content.tagValueList,
1790  tagValueList,
1791  sizeof(nbgl_contentTagValueList_t));
1792  localContentsList[1].contentActionCallback = tagValueList->actionCallback;
1793 
1794  // The last page
1795  if (isLight) {
1796  localContentsList[2].type = INFO_BUTTON;
1797  prepareReviewLightLastPage(&localContentsList[2].content.infoButton, icon, finishTitle);
1798  }
1799  else {
1800  localContentsList[2].type = INFO_LONG_PRESS;
1801  prepareReviewLastPage(&localContentsList[2].content.infoLongPress, icon, finishTitle);
1802  }
1803 
1804  // compute number of pages & fill navigation structure
1805  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
1806  prepareNavInfo(true, nbPages, getRejectReviewText(operationType));
1807 
1808  // Play notification sound if required
1809  if (playNotifSound) {
1810 #ifdef HAVE_PIEZO_SOUND
1811  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1812 #endif // HAVE_PIEZO_SOUND
1813  }
1814 
1815  displayGenericContextPage(0, true);
1816 }
1817 
1818 // function to factorize code for all streaming reviews
1819 static void useCaseReviewStreamingStart(nbgl_operationType_t operationType,
1820  const nbgl_icon_details_t *icon,
1821  const char *reviewTitle,
1822  const char *reviewSubTitle,
1823  nbgl_choiceCallback_t choiceCallback,
1824  bool playNotifSound)
1825 {
1826  reset_callbacks();
1827  memset(&genericContext, 0, sizeof(genericContext));
1828 
1829  bundleNavContext.reviewStreaming.operationType = operationType;
1830  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
1831  bundleNavContext.reviewStreaming.icon = icon;
1832 
1833  // memorize context
1834  onChoice = bundleNavReviewStreamingChoice;
1835  navType = STREAMING_NAV;
1836  pageTitle = NULL;
1837 
1838  genericContext.genericContents.contentsList = localContentsList;
1839  genericContext.genericContents.nbContents = 1;
1840  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
1841 
1842  // First a centered info
1843  STARTING_CONTENT.type = EXTENDED_CENTER;
1844  prepareReviewFirstPage(
1845  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
1846 
1847  // compute number of pages & fill navigation structure
1848  bundleNavContext.reviewStreaming.stepPageNb
1849  = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
1850  prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(operationType));
1851  // no back button on first page
1852  navInfo.navWithButtons.backButton = false;
1853 
1854  // Play notification sound if required
1855  if (playNotifSound) {
1856 #ifdef HAVE_PIEZO_SOUND
1857  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
1858 #endif // HAVE_PIEZO_SOUND
1859  }
1860 
1861  displayGenericContextPage(0, true);
1862 }
1863 
1880 static void useCaseHomeExt(const char *appName,
1881  const nbgl_icon_details_t *appIcon,
1882  const char *tagline,
1883  bool withSettings,
1884  nbgl_homeAction_t *homeAction,
1885  nbgl_callback_t topRightCallback,
1886  nbgl_callback_t quitCallback)
1887 {
1888  reset_callbacks();
1889 
1890  nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
1891  .centeredInfo.text1 = appName,
1892  .centeredInfo.text3 = NULL,
1893  .centeredInfo.style = LARGE_CASE_INFO,
1894  .centeredInfo.offsetY = 0,
1895  .footerText = NULL,
1896  .bottomButtonStyle = QUIT_APP_TEXT,
1897  .tapActionText = NULL,
1898  .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
1899  .topRightToken = CONTINUE_TOKEN,
1900  .tuneId = TUNE_TAP_CASUAL};
1901  if ((homeAction->text != NULL) || (homeAction->icon != NULL)) {
1902  // trick to use ACTION_BUTTON_TOKEN for action and quit, with index used to distinguish
1903  info.bottomButtonsToken = ACTION_BUTTON_TOKEN;
1904  onAction = homeAction->callback;
1905  info.actionButtonText = homeAction->text;
1906  info.actionButtonIcon = homeAction->icon;
1907  info.actionButtonStyle
1909  }
1910  else {
1911  info.bottomButtonsToken = QUIT_TOKEN;
1912  onAction = NULL;
1913  info.actionButtonText = NULL;
1914  info.actionButtonIcon = NULL;
1915  }
1916  if (tagline == NULL) {
1917  if (strlen(appName) > MAX_APP_NAME_FOR_SDK_TAGLINE) {
1918  snprintf(appDescription,
1920  "This app enables signing\ntransactions on its network.");
1921  }
1922  else {
1923  snprintf(appDescription,
1925  "%s %s\n%s",
1926  TAGLINE_PART1,
1927  appName,
1928  TAGLINE_PART2);
1929  }
1930 
1931  // If there is more than 3 lines, it means the appName was split, so we put it on the next
1932  // line
1933  if (nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, appDescription, AVAILABLE_WIDTH, false)
1934  > 3) {
1935  snprintf(appDescription,
1937  "%s\n%s %s",
1938  TAGLINE_PART1,
1939  appName,
1940  TAGLINE_PART2);
1941  }
1942  info.centeredInfo.text2 = appDescription;
1943  }
1944  else {
1945  info.centeredInfo.text2 = tagline;
1946  }
1947 
1948  onContinue = topRightCallback;
1949  onQuit = quitCallback;
1950 
1951  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
1953 }
1954 
1955 /**********************
1956  * GLOBAL FUNCTIONS
1957  **********************/
1958 
1972  const nbgl_contentTagValueList_t *tagValueList,
1973  uint8_t startIndex,
1974  bool *requireSpecificDisplay)
1975 {
1976  uint8_t nbPairsInPage = 0;
1977 #ifdef TARGET_STAX
1978  uint16_t currentHeight = 24; // upper margin
1979 #else // TARGET_STAX
1980  uint16_t currentHeight = 0; // upper margin
1981 #endif // TARGET_STAX
1982 
1983  *requireSpecificDisplay = false;
1984  while (nbPairsInPage < nbPairs) {
1985  const nbgl_layoutTagValue_t *pair;
1986  nbgl_font_id_e value_font;
1987  uint16_t nbLines;
1988 
1989  // margin between pairs
1990  // 12 or 24 px between each tag/value pair
1991  if (nbPairsInPage > 0) {
1992 #ifdef TARGET_STAX
1993  currentHeight += 12;
1994 #else // TARGET_STAX
1995  currentHeight += 24;
1996 #endif // TARGET_STAX
1997  }
1998  // fetch tag/value pair strings.
1999  if (tagValueList->pairs != NULL) {
2000  pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
2001  }
2002  else {
2003  pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
2004  }
2005 
2006  if (pair->forcePageStart && nbPairsInPage > 0) {
2007  // This pair must be at the top of a page
2008  break;
2009  }
2010 
2011  if (pair->centeredInfo) {
2012  if (nbPairsInPage > 0) {
2013  // This pair must be at the top of a page
2014  break;
2015  }
2016  else {
2017  // This pair is the only one of the page and has a specific display behavior
2018  nbPairsInPage = 1;
2019  *requireSpecificDisplay = true;
2020  break;
2021  }
2022  }
2023 
2024  // tag height
2025  currentHeight += nbgl_getTextHeightInWidth(
2026  SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2027  // space between tag and value
2028  currentHeight += 4;
2029  // set value font
2030  if (tagValueList->smallCaseForValue) {
2031  value_font = SMALL_REGULAR_FONT;
2032  }
2033  else {
2034  value_font = LARGE_MEDIUM_FONT;
2035  }
2036  // value height
2037  currentHeight += nbgl_getTextHeightInWidth(
2038  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2039  // nb lines for value
2040  nbLines = nbgl_getTextNbLinesInWidth(
2041  value_font, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2042  if ((currentHeight >= TAG_VALUE_AREA_HEIGHT) || (nbLines > NB_MAX_LINES_IN_REVIEW)) {
2043  if (nbPairsInPage == 0) {
2044  // Pair too long to fit in a single screen
2045  // It will be the only one of the page and has a specific display behavior
2046  nbPairsInPage = 1;
2047  *requireSpecificDisplay = true;
2048  }
2049  break;
2050  }
2051  nbPairsInPage++;
2052  }
2053  return nbPairsInPage;
2054 }
2055 
2066  const nbgl_contentInfoList_t *infosList,
2067  uint8_t startIndex,
2068  bool withNav)
2069 {
2070  uint8_t nbInfosInPage = 0;
2071  uint16_t currentHeight = 0;
2072  uint16_t previousHeight;
2073  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2074  const char *const *infoTypes = PIC(infosList->infoTypes);
2075  const char *const *infoContents = PIC(infosList->infoContents);
2076 
2077  while (nbInfosInPage < nbInfos) {
2078  // margin between infos
2079  currentHeight += PRE_TEXT_MARGIN;
2080 
2081  // type height
2082  currentHeight += nbgl_getTextHeightInWidth(
2083  SMALL_BOLD_FONT, PIC(infoTypes[startIndex + nbInfosInPage]), AVAILABLE_WIDTH, true);
2084  // space between type and content
2085  currentHeight += TEXT_SUBTEXT_MARGIN;
2086 
2087  // content height
2088  currentHeight += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2089  PIC(infoContents[startIndex + nbInfosInPage]),
2091  true);
2092  currentHeight += POST_SUBTEXT_MARGIN; // under the content
2093  // if height is over the limit
2094  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2095  // if there was no nav, now there will be, so it can be necessary to remove the last
2096  // item
2097  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2098  nbInfosInPage--;
2099  }
2100  break;
2101  }
2102  previousHeight = currentHeight;
2103  nbInfosInPage++;
2104  }
2105  return nbInfosInPage;
2106 }
2107 
2118  const nbgl_contentSwitchesList_t *switchesList,
2119  uint8_t startIndex,
2120  bool withNav)
2121 {
2122  uint8_t nbSwitchesInPage = 0;
2123  uint16_t currentHeight = 0;
2124  uint16_t previousHeight;
2125  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2126  nbgl_contentSwitch_t *switchArray = (nbgl_contentSwitch_t *) PIC(switchesList->switches);
2127 
2128  while (nbSwitchesInPage < nbSwitches) {
2129  // margin between switches
2130  currentHeight += PRE_TEXT_MARGIN;
2131 
2132  // text height
2133  currentHeight += nbgl_getTextHeightInWidth(SMALL_BOLD_FONT,
2134  switchArray[startIndex + nbSwitchesInPage].text,
2136  true);
2137  // space between text and sub-text
2138  currentHeight += TEXT_SUBTEXT_MARGIN;
2139 
2140  // sub-text height
2141  currentHeight
2142  += nbgl_getTextHeightInWidth(SMALL_REGULAR_FONT,
2143  switchArray[startIndex + nbSwitchesInPage].subText,
2145  true);
2146  currentHeight += POST_SUBTEXT_MARGIN; // under the sub-text
2147  // if height is over the limit
2148  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2149  // if there was no nav, now there will be, so it can be necessary to remove the last
2150  // item
2151  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2152  nbSwitchesInPage--;
2153  }
2154  break;
2155  }
2156  previousHeight = currentHeight;
2157  nbSwitchesInPage++;
2158  }
2159  return nbSwitchesInPage;
2160 }
2161 
2172  const nbgl_contentBarsList_t *barsList,
2173  uint8_t startIndex,
2174  bool withNav)
2175 {
2176  uint8_t nbBarsInPage = 0;
2177  uint16_t currentHeight = 0;
2178  uint16_t previousHeight;
2179  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2180 
2181  UNUSED(barsList);
2182  UNUSED(startIndex);
2183 
2184  while (nbBarsInPage < nbBars) {
2185  currentHeight += TOUCHABLE_BAR_HEIGHT;
2186  // if height is over the limit
2187  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2188  // if there was no nav, now there will be, so it can be necessary to remove the last
2189  // item
2190  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2191  nbBarsInPage--;
2192  }
2193  break;
2194  }
2195  previousHeight = currentHeight;
2196  nbBarsInPage++;
2197  }
2198  return nbBarsInPage;
2199 }
2200 
2211  const nbgl_contentRadioChoice_t *choicesList,
2212  uint8_t startIndex,
2213  bool withNav)
2214 {
2215  uint8_t nbChoicesInPage = 0;
2216  uint16_t currentHeight = 0;
2217  uint16_t previousHeight;
2218  uint16_t navHeight = withNav ? SIMPLE_FOOTER_HEIGHT : 0;
2219 
2220  UNUSED(choicesList);
2221  UNUSED(startIndex);
2222 
2223  while (nbChoicesInPage < nbChoices) {
2224  currentHeight += TOUCHABLE_BAR_HEIGHT;
2225  // if height is over the limit
2226  if (currentHeight >= (INFOS_AREA_HEIGHT - navHeight)) {
2227  // if there was no nav, now there will be, so it can be necessary to remove the last
2228  // item
2229  if (!withNav && (previousHeight >= (INFOS_AREA_HEIGHT - SIMPLE_FOOTER_HEIGHT))) {
2230  nbChoicesInPage--;
2231  }
2232  break;
2233  }
2234  previousHeight = currentHeight;
2235  nbChoicesInPage++;
2236  }
2237  return nbChoicesInPage;
2238 }
2239 
2247 {
2248  uint8_t nbPages = 0;
2249  uint8_t nbPairs = tagValueList->nbPairs;
2250  uint8_t nbPairsInPage;
2251  uint8_t i = 0;
2252  bool flag;
2253 
2254  while (i < tagValueList->nbPairs) {
2255  // upper margin
2256  nbPairsInPage = nbgl_useCaseGetNbTagValuesInPage(nbPairs, tagValueList, i, &flag);
2257  i += nbPairsInPage;
2258  nbPairs -= nbPairsInPage;
2259  nbPages++;
2260  }
2261  return nbPages;
2262 }
2263 
2276 void nbgl_useCaseHome(const char *appName,
2277  const nbgl_icon_details_t *appIcon,
2278  const char *tagline,
2279  bool withSettings,
2280  nbgl_callback_t topRightCallback,
2281  nbgl_callback_t quitCallback)
2282 {
2284  appName, appIcon, tagline, withSettings, NULL, NULL, topRightCallback, quitCallback);
2285 }
2286 
2305 void nbgl_useCaseHomeExt(const char *appName,
2306  const nbgl_icon_details_t *appIcon,
2307  const char *tagline,
2308  bool withSettings,
2309  const char *actionButtonText,
2310  nbgl_callback_t actionCallback,
2311  nbgl_callback_t topRightCallback,
2312  nbgl_callback_t quitCallback)
2313 {
2314  nbgl_homeAction_t homeAction = {.callback = actionCallback,
2315  .icon = NULL,
2316  .style = STRONG_HOME_ACTION,
2317  .text = actionButtonText};
2318 
2319  useCaseHomeExt(
2320  appName, appIcon, tagline, withSettings, &homeAction, topRightCallback, quitCallback);
2321 }
2322 
2339 void nbgl_useCasePlugInHome(const char *plugInName,
2340  const char *appName,
2341  const nbgl_icon_details_t *appIcon,
2342  const char *tagline,
2343  const char *subTagline,
2344  bool withSettings,
2345  nbgl_callback_t topRightCallback,
2346  nbgl_callback_t quitCallback)
2347 {
2348  reset_callbacks();
2349 
2350  nbgl_pageInfoDescription_t info = {.centeredInfo.icon = appIcon,
2351  .centeredInfo.text1 = plugInName,
2352  .centeredInfo.style = PLUGIN_INFO,
2353  .centeredInfo.offsetY = -16,
2354  .footerText = NULL,
2355  .bottomButtonStyle = QUIT_APP_TEXT,
2356  .tapActionText = NULL,
2357  .topRightStyle = withSettings ? SETTINGS_ICON : INFO_ICON,
2358  .topRightToken = CONTINUE_TOKEN,
2359  .actionButtonText = NULL,
2360  .tuneId = TUNE_TAP_CASUAL};
2361  info.bottomButtonsToken = QUIT_TOKEN;
2362  onAction = NULL;
2363  if (tagline == NULL) {
2364  snprintf(appDescription,
2366  "This app confirms actions for\n%s.",
2367  plugInName);
2368  info.centeredInfo.text2 = appDescription;
2369  }
2370  else {
2371  info.centeredInfo.text2 = tagline;
2372  }
2373  if (subTagline == NULL) {
2374  snprintf(plugInDescription, APP_DESCRIPTION_MAX_LEN, "This app relies on\n%s", appName);
2375  info.centeredInfo.text3 = plugInDescription;
2376  }
2377  else {
2378  info.centeredInfo.text3 = subTagline;
2379  }
2380 
2381  onContinue = topRightCallback;
2382  onQuit = quitCallback;
2383  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2384  nbgl_refresh();
2385 }
2386 
2401 void nbgl_useCaseSettings(const char *title,
2402  uint8_t initPage,
2403  uint8_t nbPages,
2404  bool touchable,
2405  nbgl_callback_t quitCallback,
2406  nbgl_navCallback_t navCallback,
2407  nbgl_layoutTouchCallback_t controlsCallback)
2408 {
2409  UNUSED(touchable);
2410  reset_callbacks();
2411 
2412  // memorize context
2413  onQuit = quitCallback;
2414  onNav = navCallback;
2415  onControls = controlsCallback;
2416  pageTitle = title;
2417  navType = SETTINGS_NAV;
2418 
2419  // fill navigation structure
2420  prepareNavInfo(false, nbPages, NULL);
2421 
2422  displaySettingsPage(initPage, true);
2423 }
2424 
2437 void nbgl_useCaseGenericSettings(const char *appName,
2438  uint8_t initPage,
2439  const nbgl_genericContents_t *settingContents,
2440  const nbgl_contentInfoList_t *infosList,
2441  nbgl_callback_t quitCallback)
2442 {
2443  reset_callbacks();
2444  memset(&genericContext, 0, sizeof(genericContext));
2445 
2446  // memorize context
2447  onQuit = quitCallback;
2448  pageTitle = appName;
2449  navType = GENERIC_NAV;
2450 
2451  if (settingContents != NULL) {
2452  memcpy(&genericContext.genericContents, settingContents, sizeof(nbgl_genericContents_t));
2453  }
2454  if (infosList != NULL) {
2455  genericContext.hasFinishingContent = true;
2456  memset(&FINISHING_CONTENT, 0, sizeof(nbgl_content_t));
2457  FINISHING_CONTENT.type = INFOS_LIST;
2458  memcpy(&FINISHING_CONTENT.content, infosList, sizeof(nbgl_content_u));
2459  }
2460 
2461  // fill navigation structure
2462  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
2463  if (infosList != NULL) {
2464  nbPages += nbgl_useCaseGetNbPagesForContent(&FINISHING_CONTENT, nbPages, true);
2465  }
2466 
2467  prepareNavInfo(false, nbPages, NULL);
2468 
2469  displayGenericContextPage(initPage, true);
2470 }
2471 
2483 void nbgl_useCaseGenericConfiguration(const char *title,
2484  uint8_t initPage,
2485  const nbgl_genericContents_t *contents,
2486  nbgl_callback_t quitCallback)
2487 {
2488  nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2489 }
2490 
2508  const char *appName,
2509  const nbgl_icon_details_t *appIcon,
2510  const char *tagline,
2511  const uint8_t
2512  initSettingPage, // if not INIT_HOME_PAGE, start directly the corresponding setting page
2513  const nbgl_genericContents_t *settingContents,
2514  const nbgl_contentInfoList_t *infosList,
2515  const nbgl_homeAction_t *action, // Set to NULL if no additional action
2516  nbgl_callback_t quitCallback)
2517 {
2518  nbgl_homeAndSettingsContext_t *context = &bundleNavContext.homeAndSettings;
2519 
2520  context->appName = appName;
2521  context->appIcon = appIcon;
2522  context->tagline = tagline;
2523  context->settingContents = settingContents;
2524  context->infosList = infosList;
2525  if (action != NULL) {
2526  memcpy(&context->homeAction, action, sizeof(nbgl_homeAction_t));
2527  }
2528  else {
2529  memset(&context->homeAction, 0, sizeof(nbgl_homeAction_t));
2530  }
2531  context->quitCallback = quitCallback;
2532 
2533  if (initSettingPage != INIT_HOME_PAGE) {
2534  bundleNavStartSettingsAtPage(initSettingPage);
2535  }
2536  else {
2537  bundleNavStartHome();
2538  }
2539 }
2540 
2548 void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2549 {
2550  reset_callbacks();
2551 
2553  .tickerCallback = &tickerCallback,
2554  .tickerIntervale = 0, // not periodic
2555  .tickerValue = 3000 // 3 seconds
2556  };
2557  onQuit = quitCallback;
2558  if (isSuccess) {
2559 #ifdef HAVE_PIEZO_SOUND
2560  io_seproxyhal_play_tune(TUNE_LEDGER_MOMENT);
2561 #endif // HAVE_PIEZO_SOUND
2562 
2563  pageContext = nbgl_pageDrawLedgerInfo(&pageCallback, &ticker, message, QUIT_TOKEN);
2564  }
2565  else {
2567  .footerText = NULL,
2568  .centeredInfo.icon = &C_Denied_Circle_64px,
2569  .centeredInfo.offsetY = SMALL_FOOTER_HEIGHT / 2,
2570  .centeredInfo.onTop = false,
2571  .centeredInfo.style = LARGE_CASE_INFO,
2572  .centeredInfo.text1 = message,
2573  .centeredInfo.text2 = NULL,
2574  .centeredInfo.text3 = NULL,
2575  .tapActionText = "",
2576  .isSwipeable = false,
2577  .tapActionToken = QUIT_TOKEN,
2578  .topRightStyle = NO_BUTTON_STYLE,
2579  .actionButtonText = NULL,
2580  .tuneId = TUNE_TAP_CASUAL};
2581  pageContext = nbgl_pageDrawInfo(&pageCallback, &ticker, &info);
2582  }
2584 }
2585 
2593  nbgl_callback_t quitCallback)
2594 {
2595  const char *msg;
2596  bool isSuccess;
2597  switch (reviewStatusType) {
2599  msg = "Operation signed";
2600  isSuccess = true;
2601  break;
2603  msg = "Operation rejected";
2604  isSuccess = false;
2605  break;
2607  msg = "Transaction signed";
2608  isSuccess = true;
2609  break;
2611  msg = "Transaction rejected";
2612  isSuccess = false;
2613  break;
2615  msg = "Message signed";
2616  isSuccess = true;
2617  break;
2619  msg = "Message rejected";
2620  isSuccess = false;
2621  break;
2623  msg = "Address verified";
2624  isSuccess = true;
2625  break;
2627  msg = "Address verification\ncancelled";
2628  isSuccess = false;
2629  break;
2630  default:
2631  return;
2632  }
2633  nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2634 }
2635 
2648 void nbgl_useCaseChoice(const nbgl_icon_details_t *icon,
2649  const char *message,
2650  const char *subMessage,
2651  const char *confirmText,
2652  const char *cancelText,
2653  nbgl_choiceCallback_t callback)
2654 {
2655  reset_callbacks();
2656 
2657  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2658  .centeredInfo.text1 = message,
2659  .centeredInfo.text2 = subMessage,
2660  .centeredInfo.text3 = NULL,
2661  .centeredInfo.style = LARGE_CASE_INFO,
2662  .centeredInfo.icon = icon,
2663  .centeredInfo.offsetY = 0,
2664  .confirmationText = confirmText,
2665  .confirmationToken = CHOICE_TOKEN,
2666  .tuneId = TUNE_TAP_CASUAL,
2667  .modal = false};
2668  // check params
2669  if ((confirmText == NULL) || (cancelText == NULL)) {
2670  return;
2671  }
2672  onChoice = callback;
2673  pageContext = nbgl_pageDrawConfirmation(&pageCallback, &info);
2675 }
2676 
2690 void nbgl_useCaseConfirm(const char *message,
2691  const char *subMessage,
2692  const char *confirmText,
2693  const char *cancelText,
2694  nbgl_callback_t callback)
2695 {
2696  // Don't reset callback or nav context as this is just a modal.
2697 
2698  nbgl_pageConfirmationDescription_t info = {.cancelText = cancelText,
2699  .centeredInfo.text1 = message,
2700  .centeredInfo.text2 = subMessage,
2701  .centeredInfo.text3 = NULL,
2702  .centeredInfo.style = LARGE_CASE_INFO,
2703  .centeredInfo.icon = &C_Important_Circle_64px,
2704  .centeredInfo.offsetY = 0,
2705  .confirmationText = confirmText,
2706  .confirmationToken = CHOICE_TOKEN,
2707  .tuneId = TUNE_TAP_CASUAL,
2708  .modal = true};
2709  onModalConfirm = callback;
2710  modalPageContext = nbgl_pageDrawConfirmation(&pageModalCallback, &info);
2712 }
2713 
2726  const char *reviewTitle,
2727  const char *reviewSubTitle,
2728  const char *rejectText,
2729  nbgl_callback_t continueCallback,
2730  nbgl_callback_t rejectCallback)
2731 {
2732  reset_callbacks();
2733 
2734  nbgl_pageInfoDescription_t info = {.footerText = rejectText,
2735  .footerToken = QUIT_TOKEN,
2736  .tapActionText = NULL,
2737  .isSwipeable = true,
2738  .tapActionToken = CONTINUE_TOKEN,
2739  .topRightStyle = NO_BUTTON_STYLE,
2740  .actionButtonText = NULL,
2741  .tuneId = TUNE_TAP_CASUAL};
2742  info.centeredInfo.icon = icon;
2743  info.centeredInfo.text1 = reviewTitle;
2744  info.centeredInfo.text2 = reviewSubTitle;
2745  info.centeredInfo.text3 = "Swipe to review";
2747  info.centeredInfo.offsetY = 0;
2748  onQuit = rejectCallback;
2749  onContinue = continueCallback;
2750 
2751 #ifdef HAVE_PIEZO_SOUND
2752  // Play notification sound
2753  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
2754 #endif // HAVE_PIEZO_SOUND
2755 
2756  pageContext = nbgl_pageDrawInfo(&pageCallback, NULL, &info);
2757  nbgl_refresh();
2758 }
2773 void nbgl_useCaseRegularReview(uint8_t initPage,
2774  uint8_t nbPages,
2775  const char *rejectText,
2776  nbgl_layoutTouchCallback_t buttonCallback,
2777  nbgl_navCallback_t navCallback,
2778  nbgl_choiceCallback_t choiceCallback)
2779 {
2780  reset_callbacks();
2781 
2782  // memorize context
2783  onChoice = choiceCallback;
2784  onNav = navCallback;
2785  onControls = buttonCallback;
2786  forwardNavOnly = false;
2787  navType = REVIEW_NAV;
2788 
2789  // fill navigation structure
2790  UNUSED(rejectText);
2791  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2792 
2793  displayReviewPage(initPage, true);
2794 }
2795 
2812 void nbgl_useCaseForwardOnlyReview(const char *rejectText,
2813  nbgl_layoutTouchCallback_t buttonCallback,
2814  nbgl_navCallback_t navCallback,
2815  nbgl_choiceCallback_t choiceCallback)
2816 {
2817  reset_callbacks();
2818 
2819  // memorize context
2820  onChoice = choiceCallback;
2821  onNav = navCallback;
2822  onControls = buttonCallback;
2823  forwardNavOnly = true;
2824  navType = REVIEW_NAV;
2825 
2826  // fill navigation structure
2827  UNUSED(rejectText);
2828  prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(TYPE_OPERATION));
2829 
2830  navInfo.progressIndicator = false;
2831  navInfo.skipText = "Skip";
2832  navInfo.skipToken = SKIP_TOKEN;
2833 
2834  displayReviewPage(0, true);
2835 }
2836 
2853 void nbgl_useCaseForwardOnlyReviewNoSkip(const char *rejectText,
2854  nbgl_layoutTouchCallback_t buttonCallback,
2855  nbgl_navCallback_t navCallback,
2856  nbgl_choiceCallback_t choiceCallback)
2857 {
2858  reset_callbacks();
2859 
2860  // memorize context
2861  onChoice = choiceCallback;
2862  onNav = navCallback;
2863  onControls = buttonCallback;
2864  forwardNavOnly = true;
2865  navType = REVIEW_NAV;
2866 
2867  // fill navigation structure
2868  UNUSED(rejectText);
2869  prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(TYPE_OPERATION));
2870  navInfo.progressIndicator = false;
2871  displayReviewPage(0, false);
2872 }
2873 
2886 void nbgl_useCaseStaticReview(const nbgl_contentTagValueList_t *tagValueList,
2887  const nbgl_pageInfoLongPress_t *infoLongPress,
2888  const char *rejectText,
2889  nbgl_choiceCallback_t callback)
2890 {
2891  uint8_t offset = 0;
2892 
2893  reset_callbacks();
2894  memset(&genericContext, 0, sizeof(genericContext));
2895 
2896  // memorize context
2897  onChoice = callback;
2898  navType = GENERIC_NAV;
2899  pageTitle = NULL;
2900  bundleNavContext.review.operationType = TYPE_OPERATION;
2901 
2902  genericContext.genericContents.contentsList = localContentsList;
2903  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2904 
2905  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2906  localContentsList[offset].type = TAG_VALUE_LIST;
2907  memcpy(&localContentsList[offset].content.tagValueList,
2908  tagValueList,
2909  sizeof(nbgl_contentTagValueList_t));
2910  offset++;
2911  }
2912 
2913  localContentsList[offset].type = INFO_LONG_PRESS;
2914  memcpy(&localContentsList[offset].content.infoLongPress,
2915  infoLongPress,
2916  sizeof(nbgl_pageInfoLongPress_t));
2917  localContentsList[offset].content.infoLongPress.longPressToken = CONFIRM_TOKEN;
2918  offset++;
2919 
2920  genericContext.genericContents.nbContents = offset;
2921 
2922  // compute number of pages & fill navigation structure
2923  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
2924  UNUSED(rejectText);
2925  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2926 
2927  displayGenericContextPage(0, true);
2928 }
2929 
2944  const nbgl_pageInfoLongPress_t *infoLongPress,
2945  const char *rejectText,
2946  nbgl_choiceCallback_t callback)
2947 {
2948  uint8_t offset = 0;
2949 
2950  reset_callbacks();
2951  memset(&genericContext, 0, sizeof(genericContext));
2952 
2953  // memorize context
2954  onChoice = callback;
2955  navType = GENERIC_NAV;
2956  pageTitle = NULL;
2957 
2958  genericContext.genericContents.contentsList = localContentsList;
2959  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
2960 
2961  if (tagValueList != NULL && tagValueList->nbPairs != 0) {
2962  localContentsList[offset].type = TAG_VALUE_LIST;
2963  memcpy(&localContentsList[offset].content.tagValueList,
2964  tagValueList,
2965  sizeof(nbgl_contentTagValueList_t));
2966  offset++;
2967  }
2968 
2969  localContentsList[offset].type = INFO_BUTTON;
2970  localContentsList[offset].content.infoButton.text = infoLongPress->text;
2971  localContentsList[offset].content.infoButton.icon = infoLongPress->icon;
2972  localContentsList[offset].content.infoButton.buttonText = infoLongPress->longPressText;
2973  localContentsList[offset].content.infoButton.buttonToken = CONFIRM_TOKEN;
2974  localContentsList[offset].content.infoButton.tuneId = TUNE_TAP_CASUAL;
2975  offset++;
2976 
2977  genericContext.genericContents.nbContents = offset;
2978 
2979  // compute number of pages & fill navigation structure
2980  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
2981  UNUSED(rejectText);
2982  prepareNavInfo(true, nbPages, getRejectReviewText(TYPE_OPERATION));
2983 
2984  displayGenericContextPage(0, true);
2985 }
2986 
3003 void nbgl_useCaseReview(nbgl_operationType_t operationType,
3004  const nbgl_contentTagValueList_t *tagValueList,
3005  const nbgl_icon_details_t *icon,
3006  const char *reviewTitle,
3007  const char *reviewSubTitle,
3008  const char *finishTitle,
3009  nbgl_choiceCallback_t choiceCallback)
3010 {
3011  useCaseReview(operationType,
3012  tagValueList,
3013  icon,
3014  reviewTitle,
3015  reviewSubTitle,
3016  finishTitle,
3017  NULL,
3018  choiceCallback,
3019  false,
3020  true);
3021 }
3022 
3041  const nbgl_contentTagValueList_t *tagValueList,
3042  const nbgl_icon_details_t *icon,
3043  const char *reviewTitle,
3044  const char *reviewSubTitle,
3045  const char *finishTitle,
3046  const nbgl_tipBox_t *tipBox,
3047  nbgl_choiceCallback_t choiceCallback)
3048 {
3049  useCaseReview(operationType,
3050  tagValueList,
3051  icon,
3052  reviewTitle,
3053  reviewSubTitle,
3054  finishTitle,
3055  tipBox,
3056  choiceCallback,
3057  false,
3058  true);
3059 }
3060 
3081  const nbgl_contentTagValueList_t *tagValueList,
3082  const nbgl_icon_details_t *icon,
3083  const char *reviewTitle,
3084  const char *reviewSubTitle,
3085  const char *finishTitle,
3086  const nbgl_tipBox_t *tipBox,
3087  nbgl_choiceCallback_t choiceCallback)
3088 {
3089  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3090 
3091  blindSigningContext.isStreaming = false;
3092  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3093  blindSigningContext.tagValueList = tagValueList;
3094  blindSigningContext.icon = icon;
3095  blindSigningContext.reviewTitle = reviewTitle;
3096  blindSigningContext.reviewSubTitle = reviewSubTitle;
3097  blindSigningContext.finishTitle = finishTitle;
3098  blindSigningContext.tipBox = tipBox;
3099  blindSigningContext.choiceCallback = choiceCallback;
3100 
3101  blindSigningWarning();
3102 }
3120  const nbgl_contentTagValueList_t *tagValueList,
3121  const nbgl_icon_details_t *icon,
3122  const char *reviewTitle,
3123  const char *reviewSubTitle,
3124  const char *finishTitle,
3125  nbgl_choiceCallback_t choiceCallback)
3126 {
3127  useCaseReview(operationType,
3128  tagValueList,
3129  icon,
3130  reviewTitle,
3131  reviewSubTitle,
3132  finishTitle,
3133  NULL,
3134  choiceCallback,
3135  true,
3136  true);
3137 }
3138 
3148  const char *rejectText,
3149  nbgl_callback_t rejectCallback)
3150 {
3151  reset_callbacks();
3152  memset(&genericContext, 0, sizeof(genericContext));
3153 
3154  // memorize context
3155  onQuit = rejectCallback;
3156  navType = GENERIC_NAV;
3157  pageTitle = NULL;
3158  bundleNavContext.review.operationType = TYPE_OPERATION;
3159 
3160  memcpy(&genericContext.genericContents, contents, sizeof(nbgl_genericContents_t));
3161 
3162  // compute number of pages & fill navigation structure
3163  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
3164  prepareNavInfo(true, nbPages, rejectText);
3165  navInfo.quitToken = QUIT_TOKEN;
3166 
3167 #ifdef HAVE_PIEZO_SOUND
3168  // Play notification sound
3169  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3170 #endif // HAVE_PIEZO_SOUND
3171 
3172  displayGenericContextPage(0, true);
3173 }
3174 
3188  const nbgl_icon_details_t *icon,
3189  const char *reviewTitle,
3190  const char *reviewSubTitle,
3191  nbgl_choiceCallback_t choiceCallback)
3192 {
3193  useCaseReviewStreamingStart(
3194  operationType, icon, reviewTitle, reviewSubTitle, choiceCallback, true);
3195 }
3196 
3211  const nbgl_icon_details_t *icon,
3212  const char *reviewTitle,
3213  const char *reviewSubTitle,
3214  nbgl_choiceCallback_t choiceCallback)
3215 {
3216  memset(&blindSigningContext, 0, sizeof(blindSigningContext));
3217 
3218  blindSigningContext.isStreaming = true;
3219  blindSigningContext.operationType = operationType | BLIND_OPERATION;
3220  blindSigningContext.icon = icon;
3221  blindSigningContext.reviewTitle = reviewTitle;
3222  blindSigningContext.reviewSubTitle = reviewSubTitle;
3223  blindSigningContext.choiceCallback = choiceCallback;
3224 
3225  blindSigningWarning();
3226 }
3227 
3242  nbgl_choiceCallback_t choiceCallback,
3243  nbgl_callback_t skipCallback)
3244 {
3245  // Should follow a call to nbgl_useCaseReviewStreamingStart
3246  memset(&genericContext, 0, sizeof(genericContext));
3247 
3248  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3249  bundleNavContext.reviewStreaming.skipCallback = skipCallback;
3250 
3251  // memorize context
3252  onChoice = bundleNavReviewStreamingChoice;
3253  navType = STREAMING_NAV;
3254  pageTitle = NULL;
3255 
3256  genericContext.genericContents.contentsList = localContentsList;
3257  genericContext.genericContents.nbContents = 1;
3258  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3259 
3260  // Then the tag/value pairs
3261  STARTING_CONTENT.type = TAG_VALUE_LIST;
3262  memcpy(
3263  &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t));
3264 
3265  // compute number of pages & fill navigation structure
3266  bundleNavContext.reviewStreaming.stepPageNb
3267  = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
3268  prepareNavInfo(true,
3270  getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3271  // if the operation is skippable
3272  if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) {
3273  navInfo.progressIndicator = false;
3274  navInfo.skipText = "Skip";
3275  navInfo.skipToken = SKIP_TOKEN;
3276  }
3277 
3278  displayGenericContextPage(0, true);
3279 }
3280 
3292  nbgl_choiceCallback_t choiceCallback)
3293 {
3294  nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3295 }
3296 
3305 void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3306  nbgl_choiceCallback_t choiceCallback)
3307 {
3308  // Should follow a call to nbgl_useCaseReviewStreamingContinue
3309  memset(&genericContext, 0, sizeof(genericContext));
3310 
3311  bundleNavContext.reviewStreaming.choiceCallback = choiceCallback;
3312 
3313  // memorize context
3314  onChoice = bundleNavReviewStreamingChoice;
3315  navType = STREAMING_NAV;
3316  pageTitle = NULL;
3317 
3318  genericContext.genericContents.contentsList = localContentsList;
3319  genericContext.genericContents.nbContents = 1;
3320  memset(localContentsList, 0, 1 * sizeof(nbgl_content_t));
3321 
3322  // Eventually the long press page
3323  STARTING_CONTENT.type = INFO_LONG_PRESS;
3324  prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress,
3325  bundleNavContext.reviewStreaming.icon,
3326  finishTitle);
3327 
3328  // compute number of pages & fill navigation structure
3329  bundleNavContext.reviewStreaming.stepPageNb
3330  = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
3331  prepareNavInfo(true, 1, getRejectReviewText(bundleNavContext.reviewStreaming.operationType));
3332 
3333  displayGenericContextPage(0, true);
3334 }
3335 
3344 void nbgl_useCaseViewDetails(const char *tag, const char *value, bool wrapping)
3345 {
3346  memset(&detailsContext, 0, sizeof(detailsContext));
3347 
3348  uint16_t nbLines
3349  = nbgl_getTextNbLinesInWidth(SMALL_REGULAR_FONT, value, AVAILABLE_WIDTH, wrapping);
3350 
3351  // initialize context
3352  detailsContext.tag = tag;
3353  detailsContext.value = value;
3354  detailsContext.nbPages = (nbLines + NB_MAX_LINES_IN_DETAILS - 1) / NB_MAX_LINES_IN_DETAILS;
3355  detailsContext.currentPage = 0;
3356  detailsContext.wrapping = wrapping;
3357  // add some spare for room lost with "..." substitution
3358  if (detailsContext.nbPages > 1) {
3359  uint16_t nbLostChars = (detailsContext.nbPages - 1) * 3;
3360  uint16_t nbLostLines = (nbLostChars + ((AVAILABLE_WIDTH) / 16) - 1)
3361  / ((AVAILABLE_WIDTH) / 16); // 16 for average char width
3362  uint8_t nbLinesInLastPage
3363  = nbLines - ((detailsContext.nbPages - 1) * NB_MAX_LINES_IN_DETAILS);
3364 
3365  detailsContext.nbPages += nbLostLines / NB_MAX_LINES_IN_DETAILS;
3366  if ((nbLinesInLastPage + (nbLostLines % NB_MAX_LINES_IN_DETAILS))
3368  detailsContext.nbPages++;
3369  }
3370  }
3371 
3372  displayDetailsPage(0, true);
3373 }
3374 
3384 void nbgl_useCaseAddressConfirmation(const char *address, nbgl_choiceCallback_t callback)
3385 {
3386  nbgl_useCaseAddressConfirmationExt(address, callback, NULL);
3387 }
3388 
3401 void nbgl_useCaseAddressConfirmationExt(const char *address,
3402  nbgl_choiceCallback_t callback,
3403  const nbgl_contentTagValueList_t *tagValueList)
3404 {
3405  reset_callbacks();
3406  memset(&genericContext, 0, sizeof(genericContext));
3407  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3408 
3409  // save context
3410  onChoice = callback;
3411  navType = GENERIC_NAV;
3412  pageTitle = NULL;
3413 
3414  genericContext.genericContents.contentsList = localContentsList;
3415  genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2;
3416  memset(localContentsList, 0, 2 * sizeof(nbgl_content_t));
3417  prepareAddressConfirmationPages(
3418  address, tagValueList, &STARTING_CONTENT, &localContentsList[1]);
3419 
3420  // fill navigation structure, common to all pages
3421  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
3422 
3423  prepareNavInfo(true, nbPages, "Cancel");
3424 
3425 #ifdef HAVE_PIEZO_SOUND
3426  // Play notification sound
3427  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3428 #endif // HAVE_PIEZO_SOUND
3429 
3430  displayGenericContextPage(0, true);
3431 }
3432 
3449 void nbgl_useCaseAddressReview(const char *address,
3450  const nbgl_contentTagValueList_t *additionalTagValueList,
3451  const nbgl_icon_details_t *icon,
3452  const char *reviewTitle,
3453  const char *reviewSubTitle,
3454  nbgl_choiceCallback_t choiceCallback)
3455 {
3456  reset_callbacks();
3457  memset(&genericContext, 0, sizeof(genericContext));
3458  memset(&addressConfirmationContext, 0, sizeof(addressConfirmationContext));
3459 
3460  // save context
3461  onChoice = choiceCallback;
3462  navType = GENERIC_NAV;
3463  pageTitle = NULL;
3464  bundleNavContext.review.operationType = TYPE_OPERATION;
3465 
3466  genericContext.genericContents.contentsList = localContentsList;
3467  genericContext.genericContents.nbContents = (additionalTagValueList == NULL) ? 2 : 3;
3468  memset(localContentsList, 0, 3 * sizeof(nbgl_content_t));
3469 
3470  // First a centered info
3471  STARTING_CONTENT.type = EXTENDED_CENTER;
3472  prepareReviewFirstPage(
3473  &STARTING_CONTENT.content.extendedCenter.contentCenter, icon, reviewTitle, reviewSubTitle);
3474  STARTING_CONTENT.content.extendedCenter.contentCenter.subText = "Swipe to continue";
3475 
3476  // Then the address confirmation pages
3477  prepareAddressConfirmationPages(
3478  address, additionalTagValueList, &localContentsList[1], &localContentsList[2]);
3479 
3480  // fill navigation structure, common to all pages
3481  uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0);
3482 
3483  prepareNavInfo(true, nbPages, "Cancel");
3484 
3485 #ifdef HAVE_PIEZO_SOUND
3486  // Play notification sound
3487  io_seproxyhal_play_tune(TUNE_LOOK_AT_ME);
3488 #endif // HAVE_PIEZO_SOUND
3489 
3490  displayGenericContextPage(0, true);
3491 }
3492 
3499 void nbgl_useCaseSpinner(const char *text)
3500 {
3501  pageContext = nbgl_pageDrawSpinner(NULL, (const char *) text);
3503 }
3504 
3505 #ifdef NBGL_KEYPAD
3525 void nbgl_useCaseKeypadDigits(const char *title,
3526  uint8_t minDigits,
3527  uint8_t maxDigits,
3528  uint8_t backToken,
3529  bool shuffled,
3530  tune_index_e tuneId,
3531  nbgl_pinValidCallback_t validatePinCallback,
3532  nbgl_layoutTouchCallback_t actionCallback)
3533 {
3534  keypadGenericUseCase(title,
3535  minDigits,
3536  maxDigits,
3537  backToken,
3538  shuffled,
3539  false,
3540  tuneId,
3541  validatePinCallback,
3542  actionCallback);
3543 }
3563 void nbgl_useCaseKeypadPIN(const char *title,
3564  uint8_t minDigits,
3565  uint8_t maxDigits,
3566  uint8_t backToken,
3567  bool shuffled,
3568  tune_index_e tuneId,
3569  nbgl_pinValidCallback_t validatePinCallback,
3570  nbgl_layoutTouchCallback_t actionCallback)
3571 {
3572  keypadGenericUseCase(title,
3573  minDigits,
3574  maxDigits,
3575  backToken,
3576  shuffled,
3577  true,
3578  tuneId,
3579  validatePinCallback,
3580  actionCallback);
3581 }
3582 #endif // NBGL_KEYPAD
3583 
3584 #endif // HAVE_SE_TOUCH
3585 #endif // NBGL_USE_CASE
@ PLUGIN_INFO
Definition: nbgl_content.h:43
@ 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:167
@ INFO_LONG_PRESS
a centered info and a long press button
Definition: nbgl_content.h:332
@ EXTENDED_CENTER
a centered content and a possible tip-box
Definition: nbgl_content.h:331
@ CHOICES_LIST
list of choices through radio buttons
Definition: nbgl_content.h:339
@ CENTERED_INFO
a centered info
Definition: nbgl_content.h:330
@ SWITCHES_LIST
list of switches with descriptions
Definition: nbgl_content.h:337
@ TAG_VALUE_DETAILS
a tag/value pair and a small button to get details.
Definition: nbgl_content.h:335
@ INFOS_LIST
list of infos with titles
Definition: nbgl_content.h:338
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
Definition: nbgl_content.h:336
@ TAG_VALUE_LIST
list of tag/value pairs
Definition: nbgl_content.h:334
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
Definition: nbgl_content.h:340
@ INFO_BUTTON
a centered info and a simple black button
Definition: nbgl_content.h:333
@ ENS_ALIAS
alias comes from ENS
Definition: nbgl_content.h:126
@ ADDRESS_BOOK_ALIAS
alias comes from Address Book
Definition: nbgl_content.h:127
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:176
debug traces management
#define LOG_DEBUG(__logger,...)
Definition: nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition: nbgl_debug.h:35
bool nbgl_getTextMaxLenInNbLines(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, uint16_t maxNbLines, uint16_t *len, bool wrapping)
compute the len of the given text (in bytes) fitting in the given maximum nb lines,...
Definition: nbgl_fonts.c:572
nbgl_font_id_e
Definition: nbgl_fonts.h:140
void nbgl_textReduceOnNbLines(nbgl_font_id_e fontId, const char *origText, uint16_t maxWidth, uint8_t nbLines, char *reducedText, uint16_t reducedTextLen)
Create a reduced version of given ASCII text to wrap it on the given max width (in pixels),...
Definition: nbgl_fonts.c:1179
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
return the height of the given multiline text, with the given font.
Definition: nbgl_fonts.c:1048
uint16_t nbgl_getTextNbLinesInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
compute the number of lines of the given text fitting in the given maxWidth
Definition: nbgl_fonts.c:731
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
Definition: nbgl_layout.h:104
#define SIMPLE_FOOTER_HEIGHT
Definition: nbgl_layout.h:60
int nbgl_layoutAddTextContent(nbgl_layout_t *layout, const char *title, const char *description, const char *info)
Creates in the main container three text areas:
Definition: nbgl_layout.c:1372
#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:1638
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:1551
#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:3286
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
Definition: nbgl_layout.h:327
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
Definition: nbgl_layout.h:325
#define AVAILABLE_WIDTH
Definition: nbgl_layout.h:66
void * nbgl_layout_t
type shared externally
Definition: nbgl_layout.h:96
@ HEADER_EMPTY
empty space, to have a better vertical centering of centered info
Definition: nbgl_layout.h:406
@ HEADER_BACK_AND_TEXT
back key and optional text
Definition: nbgl_layout.h:407
#define SMALL_FOOTER_HEIGHT
Definition: nbgl_layout.h:59
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition: nbgl_layout.h:30
#define POST_SUBTEXT_MARGIN
Definition: nbgl_layout.h:77
int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *headerDesc)
Creates a touchable (or not) area at the header of the screen, containing various controls,...
Definition: nbgl_layout.c:2235
#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:3317
#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:2182
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:891
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition: nbgl_obj.c:1560
#define KEYPAD_MAX_DIGITS
Definition: nbgl_obj.h:59
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
This functions refreshes the actual screen on display with what has changed since the last refresh,...
Definition: nbgl_obj.c:1570
#define WARNING_ICON
Definition: nbgl_obj.h:148
#define BACKSPACE_KEY
Definition: nbgl_obj.h:26
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition: nbgl_obj.c:1586
#define VALIDATE_KEY
Definition: nbgl_obj.h:27
#define QRCODE_ICON
Definition: nbgl_obj.h:146
nbgl_page_t * nbgl_pageDrawGenericContent(nbgl_layoutTouchCallback_t onActionCallback, const nbgl_pageNavigationInfo_t *nav, nbgl_pageContent_t *content)
draw a generic content page, with the given content, and if nav parameter is not NULL,...
Definition: nbgl_page.c:611
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:293
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:250
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:482
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:323
int nbgl_pageRelease(nbgl_page_t *)
Release the page obtained with any of the nbgl_pageDrawXXX() functions.
Definition: nbgl_page.c:624
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:441
@ 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.
#define NB_MAX_LINES_IN_REVIEW
maximum number of lines for value field in review pages
Definition: nbgl_use_case.h:55
void(* nbgl_callback_t)(void)
prototype of generic callback function
void nbgl_useCasePlugInHome(const char *plugInName, const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, const char *subTagline, bool withSettings, nbgl_callback_t topRightCallback, nbgl_callback_t quitCallback)
#define SKIPPABLE_OPERATION
This is to use in nbgl_operationType_t when the operation is skippable. This is used.
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_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)
void nbgl_useCaseAddressConfirmationExt(const char *address, nbgl_choiceCallback_t callback, const nbgl_contentTagValueList_t *tagValueList)
uint8_t nbgl_useCaseGetNbPagesForTagValueList(const nbgl_contentTagValueList_t *tagValueList)
void nbgl_useCaseViewDetails(const char *tag, const char *value, bool wrapping)
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)
#define INFOS_AREA_HEIGHT
height available for infos pairs display
Definition: nbgl_use_case.h:73
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)
@ 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)
void nbgl_useCaseForwardOnlyReview(const char *rejectText, nbgl_layoutTouchCallback_t buttonCallback, nbgl_navCallback_t navCallback, nbgl_choiceCallback_t choiceCallback)
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_useCaseForwardOnlyReviewNoSkip(const char *rejectText, nbgl_layoutTouchCallback_t buttonCallback, nbgl_navCallback_t navCallback, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
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)
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 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_useCaseAddressConfirmation(const char *address, nbgl_choiceCallback_t callback)
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)
nbgl_opType_t
The different types of operation to review.
@ 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:295
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
Definition: nbgl_content.h:297
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
Definition: nbgl_content.h:296
uint8_t nbBars
number of elements in barTexts and tokens array
Definition: nbgl_content.h:298
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:81
uint16_t iconHug
vertical margin to apply on top and bottom of the icon
Definition: nbgl_content.h:87
const nbgl_icon_details_t * icon
the icon (can be null)
Definition: nbgl_content.h:82
const char * title
title in black large (can be null)
Definition: nbgl_content.h:83
const char * description
description in black small regular case (can be null)
Definition: nbgl_content.h:85
const char * subText
sub-text in dark gray regular small case
Definition: nbgl_content.h:86
bool padding
if true, apply a padding of 40px at the bottom
Definition: nbgl_content.h:88
const char * smallTitle
sub-title in black small bold case (can be null)
Definition: nbgl_content.h:84
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:57
const char * text2
second text (can be null)
Definition: nbgl_content.h:59
const char * text1
first text (can be null)
Definition: nbgl_content.h:58
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
Definition: nbgl_content.h:65
int16_t offsetY
vertical shift to apply to this info (if >0, shift to bottom)
Definition: nbgl_content.h:67
const char * text3
third text (can be null)
Definition: nbgl_content.h:61
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:63
This structure contains data to build a centered info + simple black button content.
Definition: nbgl_content.h:109
const char * buttonText
text of the long press button
Definition: nbgl_content.h:112
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:111
const char * text
centered text in large case
Definition: nbgl_content.h:110
This structure contains data to build a INFOS_LIST content.
Definition: nbgl_content.h:264
uint8_t nbInfos
number of elements in infoTypes and infoContents array
Definition: nbgl_content.h:267
const char *const * infoContents
array of contents of infos (in black)
Definition: nbgl_content.h:266
const char *const * infoTypes
array of types of infos (in black/bold)
Definition: nbgl_content.h:265
This structure contains data to build a centered info + long press button content.
Definition: nbgl_content.h:94
const char * longPressText
text of the long press button
Definition: nbgl_content.h:97
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:96
const char * text
centered text in large case
Definition: nbgl_content.h:95
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
Definition: nbgl_content.h:275
uint8_t initChoice
index of the current choice
Definition: nbgl_content.h:284
const char *const * names
array of strings giving the choices (nbChoices)
Definition: nbgl_content.h:277
uint8_t nbChoices
number of choices
Definition: nbgl_content.h:283
This structure contains info to build a switch (on the right) with a description (on the left),...
Definition: nbgl_content.h:242
This structure contains [item,value] pair(s) and info about a potential "details" button,...
Definition: nbgl_content.h:218
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
Definition: nbgl_content.h:229
uint8_t confirmationToken
the token used as argument of the onActionCallback
Definition: nbgl_content.h:232
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
Definition: nbgl_content.h:219
const char * detailsButtonText
this text is used for "details" button (if NULL, no button)
Definition: nbgl_content.h:221
const nbgl_icon_details_t * detailsButtonIcon
icon to use in details button
Definition: nbgl_content.h:220
This structure contains a list of [tag,value] pairs.
Definition: nbgl_content.h:181
nbgl_contentActionCallback_t actionCallback
called when a valueIcon is touched on a given pair
Definition: nbgl_content.h:196
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
Definition: nbgl_content.h:183
bool wrapping
if set to true, value text will be wrapped on ' ' to avoid cutting words
Definition: nbgl_content.h:194
uint8_t startIndex
index of the first pair to get with callback
Definition: nbgl_content.h:187
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
Definition: nbgl_content.h:184
This structure contains a [tag,value] pair.
Definition: nbgl_content.h:144
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
Definition: nbgl_content.h:152
const nbgl_icon_details_t * valueIcon
Definition: nbgl_content.h:149
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
Definition: nbgl_content.h:156
const char * value
string giving the value name
Definition: nbgl_content.h:146
const char * item
string giving the tag name
Definition: nbgl_content.h:145
const char * fullValue
full string of the value when used as an alias
Definition: nbgl_content.h:135
nbgl_contentValueAliasType_t aliasType
type of alias
Definition: nbgl_content.h:138
const char * explanation
Definition: nbgl_content.h:136
This structure contains data to build a content.
Definition: nbgl_content.h:363
nbgl_content_u content
Definition: nbgl_content.h:365
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
Definition: nbgl_content.h:367
nbgl_contentType_t type
type of page content in the content union
Definition: nbgl_content.h:364
uint8_t nbContents
number of contents
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
nbgl_contentCallback_t contentGetterCallback
function to call to retrieve a given content
Structure describing the action button in Home Screen.
const nbgl_icon_details_t * icon
icon to use in action button in Home page
nbgl_callback_t callback
function to call when action button is touched in Home page
const char * text
text to use in action button in Home page
nbgl_homeActionStyle_t style
style of action button
Structure containing all information when creating a layout. This structure must be passed as argumen...
Definition: nbgl_layout.h:171
nbgl_layoutTouchCallback_t onActionCallback
the callback to be called on any action on the layout
Definition: nbgl_layout.h:185
This structure contains info to build a header.
Definition: nbgl_layout.h:419
nbgl_layoutHeaderType_t type
type of header
Definition: nbgl_layout.h:420
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_layout.h:266
const char * text2
second text (can be null)
Definition: nbgl_layout.h:269
const char * url
URL for QR code.
Definition: nbgl_layout.h:267
Structure containing all specific information when creating a confirmation page.
Definition: nbgl_page.h:149
const char * cancelText
the text used for cancel action, if NULL a simple X button is used
Definition: nbgl_page.h:152
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:256
uint8_t nbSwitches
number of elements in switches and tokens array
Definition: nbgl_content.h:258
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
Definition: nbgl_content.h:257
Structure containing all specific information when creating a NBGL step.
Definition: nbgl_flow.h:43
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
const nbgl_contentInfoList_t infos
infos pairs displayed in modal.
const char * modalTitle
title given to modal window displayed when tip-box is touched
const char * text
text of the tip-box
nbgl_contentType_t type
type of page content in the following union
const nbgl_icon_details_t * icon
icon of the tip-box
Union of the different type of contents.
Definition: nbgl_content.h:346
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition: nbgl_content.h:355
nbgl_contentInfoLongPress_t infoLongPress
INFO_LONG_PRESS type
Definition: nbgl_content.h:349
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition: nbgl_content.h:353
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition: nbgl_content.h:351
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition: nbgl_content.h:347
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition: nbgl_content.h:357
nbgl_contentExtendedCenter_t extendedCenter
EXTENDED_CENTER type
Definition: nbgl_content.h:348
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition: nbgl_content.h:354
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition: nbgl_content.h:350
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition: nbgl_content.h:356
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