Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_use_case_nanos.c
Go to the documentation of this file.
1
6#ifdef NBGL_USE_CASE
7#ifndef 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#include "ux.h"
20
21/*********************
22 * DEFINES
23 *********************/
24#define WITH_HORIZONTAL_CHOICES_LIST
25#define WITH_HORIZONTAL_BARS_LIST
26
32#define RISKY_OPERATION (1 << 6)
33
39#define NO_THREAT_OPERATION (1 << 7)
40
41/**********************
42 * TYPEDEFS
43 **********************/
44
45typedef struct ReviewContext_s {
46 nbgl_choiceCallback_t onChoice;
47 const nbgl_contentTagValueList_t *tagValueList;
48 const nbgl_icon_details_t *icon;
49 const char *reviewTitle;
50 const char *reviewSubTitle;
51 const char *finishTitle;
52 const char *address; // for address confirmation review
53 nbgl_callback_t skipCallback; // callback provided by used
54 uint8_t nbDataSets; // number of sets of data received by StreamingContinue
55 bool skipDisplay; // if set to true, means that we are displaying the skip page
56 uint8_t dataDirection; // used to know whether the skip page is reached from back or forward
57 uint8_t currentTagValueIndex;
58 uint8_t currentExtensionPage;
59 uint8_t nbExtensionPages;
60 const nbgl_contentValueExt_t *extension;
61 nbgl_step_t extensionStepCtx;
62
63} ReviewContext_t;
64
65typedef struct ChoiceContext_s {
66 const nbgl_icon_details_t *icon;
67 const char *message;
68 const char *subMessage;
69 const char *confirmText;
70 const char *cancelText;
71 nbgl_choiceCallback_t onChoice;
72 const nbgl_genericDetails_t *details;
73} ChoiceContext_t;
74
75typedef struct ConfirmContext_s {
76 const char *message;
77 const char *subMessage;
78 const char *confirmText;
79 const char *cancelText;
80 nbgl_callback_t onConfirm;
81 nbgl_step_t currentStep;
82} ConfirmContext_t;
83
84typedef struct ContentContext_s {
85 const char *title; // For CHOICES_LIST /BARS_LIST
86 nbgl_genericContents_t genericContents;
87 const char *rejectText;
88 nbgl_layoutTouchCallback_t controlsCallback;
89 nbgl_navCallback_t navCallback;
90 nbgl_callback_t quitCallback;
91} ContentContext_t;
92
93typedef struct HomeContext_s {
94 const char *appName;
95 const nbgl_icon_details_t *appIcon;
96 const char *tagline;
97 const nbgl_genericContents_t *settingContents;
98 const nbgl_contentInfoList_t *infosList;
99 const nbgl_homeAction_t *homeAction;
100 nbgl_callback_t quitCallback;
101} HomeContext_t;
102
103typedef struct ActionContext_s {
104 nbgl_callback_t actionCallback;
105} ActionContext_t;
106
107#ifdef NBGL_KEYPAD
108typedef struct KeypadContext_s {
109 uint8_t pinEntry[8];
110 uint8_t pinLen;
111 uint8_t pinMinDigits;
112 uint8_t pinMaxDigits;
113 nbgl_layout_t *layoutCtx;
114 bool hidden;
115 uint8_t keypadIndex;
116 nbgl_pinValidCallback_t validatePin;
117 nbgl_callback_t backCallback;
118} KeypadContext_t;
119#endif
120
121#ifdef NBGL_KEYBOARD
122typedef struct KeyboardContext_s {
124 char *entryBuffer;
125 uint16_t entryMaxLen;
126 nbgl_layout_t *layoutCtx;
127 uint8_t keyboardIndex;
128 uint8_t textIndex;
129 nbgl_callback_t actionCallback;
130 nbgl_callback_t backCallback;
131 nbgl_keyboardButtonsCallback_t getSuggestButtons;
132 nbgl_layoutTouchCallback_t onButtonCallback;
133} KeyboardContext_t;
134#endif
135
136typedef enum {
137 NONE_USE_CASE,
138 SPINNER_USE_CASE,
139 REVIEW_USE_CASE,
140 GENERIC_REVIEW_USE_CASE,
141 ADDRESS_REVIEW_USE_CASE,
142 STREAMING_START_REVIEW_USE_CASE,
143 STREAMING_CONTINUE_REVIEW_USE_CASE,
144 STREAMING_FINISH_REVIEW_USE_CASE,
145 CHOICE_USE_CASE,
146 STATUS_USE_CASE,
147 CONFIRM_USE_CASE,
148 KEYPAD_USE_CASE,
149 KEYBOARD_USE_CASE,
150 HOME_USE_CASE,
151 INFO_USE_CASE,
152 SETTINGS_USE_CASE,
153 GENERIC_SETTINGS,
154 CONTENT_USE_CASE,
155 ACTION_USE_CASE
156} ContextType_t;
157
158typedef struct UseCaseContext_s {
159 ContextType_t type;
160 nbgl_operationType_t operationType;
161 uint8_t nbPages;
162 uint8_t currentPage;
163 uint8_t firstPairPage;
164 bool forceAction;
166 stepCallback;
167 union {
168 ReviewContext_t review;
169 ChoiceContext_t choice;
170 ConfirmContext_t confirm;
171 HomeContext_t home;
172 ContentContext_t content;
173#ifdef NBGL_KEYPAD
174 KeypadContext_t keypad;
175#endif
176#ifdef NBGL_KEYBOARD
177 KeyboardContext_t keyboard;
178#endif
179 ActionContext_t action;
180 };
181} UseCaseContext_t;
182
183typedef struct PageContent_s {
184 bool isSwitch;
185 const char *text;
186 const char *subText;
187 const nbgl_icon_details_t *icon;
188 const nbgl_contentValueExt_t *extension;
189 nbgl_state_t state;
190 bool isCenteredInfo;
191 bool isAction;
192} PageContent_t;
193
194typedef struct ReviewWithWarningContext_s {
195 ContextType_t type;
196 nbgl_operationType_t operationType;
197 const nbgl_contentTagValueList_t *tagValueList;
198 const nbgl_icon_details_t *icon;
199 const char *reviewTitle;
200 const char *reviewSubTitle;
201 const char *finishTitle;
202 const nbgl_warning_t *warning;
203 nbgl_choiceCallback_t choiceCallback;
204 uint8_t securityReportLevel; // level 1 is the first level of menus
205 bool isIntro; // set to true during intro (before actual review)
206 uint8_t warningPage;
207 uint8_t nbWarningPages;
208 uint8_t firstWarningPage;
209 uint8_t barDetailIdx; // index of the bar whose detail is displayed
210} ReviewWithWarningContext_t;
211
212typedef enum {
213 NO_FORCED_TYPE = 0,
214 FORCE_BUTTON,
215 FORCE_CENTERED_INFO
216} ForcedType_t;
217
218/**********************
219 * STATIC VARIABLES
220 **********************/
221static UseCaseContext_t context;
222
223static ReviewWithWarningContext_t reviewWithWarnCtx;
224// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
225static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
226
227// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
228nbgl_operationType_t streamingOpType;
229
230/**********************
231 * STATIC FUNCTIONS
232 **********************/
233static void displayReviewPage(nbgl_stepPosition_t pos);
234static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
235static void displayHomePage(nbgl_stepPosition_t pos);
236static void displayInfoPage(nbgl_stepPosition_t pos);
237static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
238static void displayChoicePage(nbgl_stepPosition_t pos);
239static void displayConfirm(nbgl_stepPosition_t pos);
240static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
241static void displaySpinner(const char *text);
242
243static void startUseCaseHome(void);
244static void startUseCaseInfo(void);
245static void startUseCaseSettings(void);
246static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
247static void startUseCaseContent(void);
248
249static void statusTickerCallback(void);
250
251static void displayExtensionStep(nbgl_stepPosition_t pos);
252static void displayWarningStep(void);
253
254// Simple helper to get the number of elements inside a nbgl_content_t
255static uint8_t getContentNbElement(const nbgl_content_t *content)
256{
257 switch (content->type) {
258 case CENTERED_INFO:
259 return 1;
260 case INFO_BUTTON:
261 return 1;
262 case TAG_VALUE_LIST:
263 return content->content.tagValueList.nbPairs;
265 // last element is for Confirm page
266 return content->content.tagValueConfirm.tagValueList.nbPairs + 1;
267 case SWITCHES_LIST:
268 return content->content.switchesList.nbSwitches;
269 case INFOS_LIST:
270 return content->content.infosList.nbInfos;
271 case CHOICES_LIST:
272 return content->content.choicesList.nbChoices;
273 case BARS_LIST:
274 return content->content.barsList.nbBars;
275 default:
276 return 0;
277 }
278}
279
280// Helper to retrieve the content inside a nbgl_genericContents_t using
281// either the contentsList or using the contentGetterCallback
282static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
283 uint8_t contentIdx,
284 nbgl_content_t *content)
285{
286 nbgl_pageContent_t pageContent = {0};
287 if (contentIdx >= genericContents->nbContents) {
288 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
289 return NULL;
290 }
291
292 if (genericContents->callbackCallNeeded) {
293 if (content == NULL) {
294 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
295 return NULL;
296 }
297 // Retrieve content through callback, but first memset the content.
298 memset(content, 0, sizeof(nbgl_content_t));
299 if (context.content.navCallback) {
300 if (context.content.navCallback(contentIdx, &pageContent) == true) {
301 // Copy the Page Content to the Content variable
302 content->type = pageContent.type;
303 switch (content->type) {
304 case CENTERED_INFO:
305 content->content.centeredInfo = pageContent.centeredInfo;
306 break;
307 case INFO_BUTTON:
308 content->content.infoButton = pageContent.infoButton;
309 break;
310 case TAG_VALUE_LIST:
311 content->content.tagValueList = pageContent.tagValueList;
312 break;
314 content->content.tagValueConfirm = pageContent.tagValueConfirm;
315 break;
316 case SWITCHES_LIST:
317 content->content.switchesList = pageContent.switchesList;
318 break;
319 case INFOS_LIST:
320 content->content.infosList = pageContent.infosList;
321 break;
322 case CHOICES_LIST:
323 content->content.choicesList = pageContent.choicesList;
324 break;
325 case BARS_LIST:
326 content->content.barsList = pageContent.barsList;
327 break;
328 default:
329 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
330 return NULL;
331 }
332 }
333 else {
334 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
335 return NULL;
336 }
337 }
338 else {
339 genericContents->contentGetterCallback(contentIdx, content);
340 }
341 return content;
342 }
343 else {
344 // Retrieve content through list
345 return PIC(&genericContents->contentsList[contentIdx]);
346 }
347}
348
349// Helper to retrieve the content inside a nbgl_genericContents_t using
350// either the contentsList or using the contentGetterCallback
351static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
352 uint8_t *elemContentIdx,
353 nbgl_content_t *content)
354{
355 const nbgl_genericContents_t *genericContents = NULL;
356 const nbgl_content_t *p_content = NULL;
357 uint8_t nbPages = 0;
358 uint8_t elemNbPages = 0;
359
360 switch (context.type) {
361 case SETTINGS_USE_CASE:
362 case HOME_USE_CASE:
363 case GENERIC_SETTINGS:
364 genericContents = context.home.settingContents;
365 break;
366 case CONTENT_USE_CASE:
367 case GENERIC_REVIEW_USE_CASE:
368 genericContents = &context.content.genericContents;
369 break;
370 default:
371 return NULL;
372 }
373 for (uint i = 0; i < genericContents->nbContents; i++) {
374 p_content = getContentAtIdx(genericContents, i, content);
375 elemNbPages = getContentNbElement(p_content);
376 if (nbPages + elemNbPages > elemIdx) {
377 *elemContentIdx = context.currentPage - nbPages;
378 break;
379 }
380 nbPages += elemNbPages;
381 }
382
383 return p_content;
384}
385
386static const char *getChoiceName(uint8_t choiceIndex)
387{
388 uint8_t elemIdx;
389 uint8_t nbValues;
390 const nbgl_content_t *p_content = NULL;
391 nbgl_content_t content = {0};
392 nbgl_contentRadioChoice_t *contentChoices = NULL;
393 nbgl_contentBarsList_t *contentBars = NULL;
394 char **names = NULL;
395
396 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
397 if (p_content == NULL) {
398 return NULL;
399 }
400 switch (p_content->type) {
401 case CHOICES_LIST:
402 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
403 names = (char **) PIC(contentChoices->names);
404 nbValues = contentChoices->nbChoices;
405 break;
406 case BARS_LIST:
407 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
408 names = (char **) PIC(contentBars->barTexts);
409 nbValues = contentBars->nbBars;
410 break;
411 default:
412 // Not supported as vertical MenuList
413 return NULL;
414 }
415 if (choiceIndex >= nbValues) {
416 // Last item is always "Back" button
417 return "Back";
418 }
419 return (const char *) PIC(names[choiceIndex]);
420}
421
422static void onChoiceSelected(uint8_t choiceIndex)
423{
424 uint8_t elemIdx;
425 uint8_t token = 255;
426 const nbgl_content_t *p_content = NULL;
427 nbgl_content_t content = {0};
428 nbgl_contentRadioChoice_t *contentChoices = NULL;
429 nbgl_contentBarsList_t *contentBars = NULL;
430
431 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
432 if (p_content == NULL) {
433 return;
434 }
435 switch (p_content->type) {
436 case CHOICES_LIST:
437 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
438 if (choiceIndex < contentChoices->nbChoices) {
439 token = contentChoices->token;
440 }
441 break;
442 case BARS_LIST:
443 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
444 if (choiceIndex < contentBars->nbBars) {
445 token = contentBars->tokens[choiceIndex];
446 }
447 break;
448 default:
449 // Not supported as vertical MenuList
450 break;
451 }
452 if ((token != 255) && (context.content.controlsCallback != NULL)) {
453 context.content.controlsCallback(token, 0);
454 }
455 else if (context.content.quitCallback != NULL) {
456 context.content.quitCallback();
457 }
458}
459
460static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
461 uint8_t index,
462 const char **item,
463 const char **value,
464 const nbgl_contentValueExt_t **extension,
465 const nbgl_icon_details_t **icon,
466 bool *isCenteredInfo)
467{
468 const nbgl_contentTagValue_t *pair;
469
470 if (tagValueList->pairs != NULL) {
471 pair = PIC(&tagValueList->pairs[index]);
472 }
473 else {
474 pair = PIC(tagValueList->callback(index));
475 }
476 *item = pair->item;
477 *value = pair->value;
478 if (pair->aliasValue) {
479 *extension = pair->extension;
480 }
481 else if (pair->centeredInfo) {
482 *isCenteredInfo = true;
483 *icon = pair->valueIcon;
484 }
485 else {
486 *extension = NULL;
487 }
488}
489
490static void onReviewAccept(void)
491{
492 if (context.review.onChoice) {
493 context.review.onChoice(true);
494 }
495}
496
497static void onReviewReject(void)
498{
499 if (context.review.onChoice) {
500 context.review.onChoice(false);
501 }
502}
503
504static void onChoiceAccept(void)
505{
506 if (context.choice.onChoice) {
507 context.choice.onChoice(true);
508 }
509}
510
511static void onChoiceReject(void)
512{
513 if (context.choice.onChoice) {
514 context.choice.onChoice(false);
515 }
516}
517
518static void onConfirmAccept(void)
519{
520 if (context.confirm.currentStep) {
521 nbgl_stepRelease(context.confirm.currentStep);
522 }
523 if (context.confirm.onConfirm) {
524 context.confirm.onConfirm();
525 }
526}
527
528static void onConfirmReject(void)
529{
530 if (context.confirm.currentStep) {
531 nbgl_stepRelease(context.confirm.currentStep);
533 nbgl_refresh();
534 }
535}
536
537static void onSwitchAction(void)
538{
539 const nbgl_contentSwitch_t *contentSwitch = NULL;
540 const nbgl_content_t *p_content = NULL;
541 nbgl_content_t content = {0};
542 uint8_t elemIdx;
543
544 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
545 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
546 return;
547 }
548 contentSwitch
549 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
550 switch (context.type) {
551 case SETTINGS_USE_CASE:
552 case HOME_USE_CASE:
553 case GENERIC_SETTINGS:
554 displaySettingsPage(FORWARD_DIRECTION, true);
555 break;
556 case CONTENT_USE_CASE:
557 case GENERIC_REVIEW_USE_CASE:
558 displayContent(FORWARD_DIRECTION, true);
559 break;
560 default:
561 break;
562 }
563 if (p_content->contentActionCallback != NULL) {
564 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
565 actionCallback(contentSwitch->token,
566 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
567 context.currentPage);
568 }
569 else if (context.content.controlsCallback != NULL) {
570 context.content.controlsCallback(contentSwitch->token, 0);
571 }
572}
573
574static void drawStep(nbgl_stepPosition_t pos,
575 const nbgl_icon_details_t *icon,
576 const char *txt,
577 const char *subTxt,
578 nbgl_stepButtonCallback_t onActionCallback,
579 bool modal,
580 ForcedType_t forcedType)
581{
582 uint8_t elemIdx;
583 nbgl_step_t newStep = NULL;
584 const nbgl_content_t *p_content = NULL;
585 nbgl_content_t content = {0};
586 nbgl_contentRadioChoice_t *contentChoices = NULL;
587 nbgl_contentBarsList_t *contentBars = NULL;
588 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
589 nbgl_layoutMenuList_t list = {0};
590 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
591 .tickerIntervale = 0, // not periodic
592 .tickerValue = STATUS_SCREEN_DURATION};
593
594 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
595 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
596 // (except the first set) because the set starts with a "skip" page
597 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
598 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
599 pos |= LAST_STEP;
600 }
601 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
602 p_ticker = &ticker;
603 }
604 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
605 nbgl_stepRelease(context.confirm.currentStep);
606 }
607
608 if (txt == NULL) {
609 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
610 if (p_content) {
611 switch (p_content->type) {
612 case CHOICES_LIST:
613 contentChoices
614 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
615 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
616 list.selectedChoice = contentChoices->initChoice;
617 list.callback = getChoiceName;
618 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
619 break;
620 case BARS_LIST:
621 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
622 list.nbChoices = contentBars->nbBars + 1; // For Back button
623 list.selectedChoice = 0;
624 list.callback = getChoiceName;
625 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
626 break;
627 default:
628 // Not supported as vertical MenuList
629 break;
630 }
631 }
632 }
633 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
635 if (subTxt != NULL) {
636 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
637 }
638 else {
639 style = REGULAR_INFO;
640 }
641 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
642 }
643 else {
645 info.icon = icon;
646 info.text1 = txt;
647 info.text2 = subTxt;
648 info.onTop = false;
649 if ((subTxt != NULL) || (context.stepCallback != NULL) || context.forceAction) {
650 info.style = BOLD_TEXT1_INFO;
651 }
652 else {
653 info.style = REGULAR_INFO;
654 }
655 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
656 }
657 if (context.type == CONFIRM_USE_CASE) {
658 context.confirm.currentStep = newStep;
659 }
660}
661
662static void drawSwitchStep(nbgl_stepPosition_t pos,
663 const char *title,
664 const char *description,
665 bool state,
666 nbgl_stepButtonCallback_t onActionCallback,
667 bool modal)
668{
669 nbgl_layoutSwitch_t switchInfo;
670
671 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
672 switchInfo.initState = state;
673 switchInfo.text = title;
674 switchInfo.subText = description;
675 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
676}
677
678static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
679{
680 uint8_t elemIdx;
681 uint8_t token = 0;
682 uint8_t index = 0;
683 const nbgl_content_t *p_content = NULL;
684 nbgl_content_t content = {0};
685
686 if (event == BUTTON_LEFT_PRESSED) {
687 if (context.currentPage > 0) {
688 context.currentPage--;
689 }
690 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
691 // the first set
692 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
693 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
694 // Drop the event
695 return false;
696 }
697 *pos = BACKWARD_DIRECTION;
698 }
699 else if (event == BUTTON_RIGHT_PRESSED) {
700 if (context.currentPage < (int) (context.nbPages - 1)) {
701 context.currentPage++;
702 }
703 else {
704 // Drop the event
705 return false;
706 }
707 *pos = FORWARD_DIRECTION;
708 }
709 else {
710 if (event == BUTTON_BOTH_PRESSED) {
711 if (context.stepCallback != NULL) {
712 context.stepCallback();
713 }
714 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
715 || (context.type == GENERIC_SETTINGS)
716 || (context.type == GENERIC_REVIEW_USE_CASE)) {
717 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
718 if (p_content != NULL) {
719 switch (p_content->type) {
720 case CENTERED_INFO:
721 // No associated callback
722 return false;
723 case INFO_BUTTON:
724 token = p_content->content.infoButton.buttonToken;
725 break;
726 case SWITCHES_LIST:
727 token = p_content->content.switchesList.switches->token;
728 break;
729 case BARS_LIST:
730 token = p_content->content.barsList.tokens[context.currentPage];
731 break;
732 case CHOICES_LIST:
733 token = p_content->content.choicesList.token;
734 index = context.currentPage;
735 break;
736 case TAG_VALUE_LIST:
737 return false;
739 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
740 return false;
741 }
742 token = p_content->content.tagValueConfirm.confirmationToken;
743 break;
744 default:
745 break;
746 }
747
748 if ((p_content) && (p_content->contentActionCallback != NULL)) {
749 p_content->contentActionCallback(token, 0, context.currentPage);
750 }
751 else if (context.content.controlsCallback != NULL) {
752 context.content.controlsCallback(token, index);
753 }
754 }
755 }
756 }
757 return false;
758 }
759 return true;
760}
761
762static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
763{
764 UNUSED(stepCtx);
766
767 if (!buttonGenericCallback(event, &pos)) {
768 return;
769 }
770 else {
771 // memorize last direction
772 context.review.dataDirection = pos;
773 }
774 displayReviewPage(pos);
775}
776
777// this is the callback used when button action on the "skip" page
778static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
779{
780 UNUSED(stepCtx);
782
783 if (event == BUTTON_LEFT_PRESSED) {
784 // only decrement page if we are going backward but coming from forward (back & forth)
785 if ((context.review.dataDirection == FORWARD_DIRECTION)
786 && (context.currentPage > context.firstPairPage)) {
787 context.currentPage--;
788 }
789 pos = BACKWARD_DIRECTION;
790 }
791 else if (event == BUTTON_RIGHT_PRESSED) {
792 // only increment page if we are going forward but coming from backward (back & forth)
793 if ((context.review.dataDirection == BACKWARD_DIRECTION)
794 && (context.currentPage < (int) (context.nbPages - 1))
795 && (context.currentPage > context.firstPairPage)) {
796 context.currentPage++;
797 }
798 pos = FORWARD_DIRECTION;
799 }
800 else if (event == BUTTON_BOTH_PRESSED) {
801 // the first tag/value page is at page 0 only in streaming case
802 if (context.firstPairPage == 0) {
803 // in streaming, we have to call the provided callback
804 context.review.skipCallback();
805 }
806 else {
807 // if not in streaming, go directly to the "approve" page
808 context.currentPage = context.nbPages - 2;
809 displayReviewPage(FORWARD_DIRECTION);
810 }
811 return;
812 }
813 else {
814 return;
815 }
816 // the first tag/value page is at page 0 only in streaming case
817 if (context.firstPairPage == 0) {
818 displayStreamingReviewPage(pos);
819 }
820 else {
821 displayReviewPage(pos);
822 }
823}
824
825// this is the callback used when buttons in "Action" use case are pressed
826static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
827{
828 UNUSED(stepCtx);
829
830 if (event == BUTTON_BOTH_PRESSED) {
831 context.action.actionCallback();
832 }
833}
834
835static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
836{
838 UNUSED(stepCtx);
839
840 if (!buttonGenericCallback(event, &pos)) {
841 return;
842 }
843 else {
844 // memorize last direction
845 context.review.dataDirection = pos;
846 }
847
848 displayStreamingReviewPage(pos);
849}
850
851static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
852{
853 UNUSED(stepCtx);
855
856 if (!buttonGenericCallback(event, &pos)) {
857 return;
858 }
859
860 displaySettingsPage(pos, false);
861}
862
863static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
864{
865 UNUSED(stepCtx);
867
868 if (!buttonGenericCallback(event, &pos)) {
869 return;
870 }
871
872 displayInfoPage(pos);
873}
874
875static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
876{
877 UNUSED(stepCtx);
879
880 if (!buttonGenericCallback(event, &pos)) {
881 return;
882 }
883
884 displayHomePage(pos);
885}
886
887static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
888{
889 UNUSED(stepCtx);
891
892 if (!buttonGenericCallback(event, &pos)) {
893 return;
894 }
895
896 displayChoicePage(pos);
897}
898
899static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
900{
901 UNUSED(stepCtx);
903
904 if (!buttonGenericCallback(event, &pos)) {
905 return;
906 }
907
908 displayConfirm(pos);
909}
910
911static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
912{
913 UNUSED(stepCtx);
914 // any button press should dismiss the status screen
915 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
916 || (event == BUTTON_RIGHT_PRESSED)) {
917 if (context.stepCallback != NULL) {
918 context.stepCallback();
919 }
920 }
921}
922
923static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
924{
925 UNUSED(stepCtx);
927
928 if (!buttonGenericCallback(event, &pos)) {
929 return;
930 }
931
932 displayContent(pos, false);
933}
934
935// callback used for timeout
936static void statusTickerCallback(void)
937{
938 if (context.stepCallback != NULL) {
939 context.stepCallback();
940 }
941}
942
943// this is the callback used when navigating in extension pages
944static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
945{
947 UNUSED(stepCtx);
948
949 if (event == BUTTON_LEFT_PRESSED) {
950 // only decrement page if we are not at the first page
951 if (context.review.currentExtensionPage > 0) {
952 context.review.currentExtensionPage--;
953 }
954 pos = BACKWARD_DIRECTION;
955 }
956 else if (event == BUTTON_RIGHT_PRESSED) {
957 // only increment page if not at last page
958 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
959 context.review.currentExtensionPage++;
960 }
961 pos = FORWARD_DIRECTION;
962 }
963 else if (event == BUTTON_BOTH_PRESSED) {
964 // if at last page, leave modal context
965 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
966 nbgl_stepRelease(context.review.extensionStepCtx);
968 nbgl_refresh();
969 }
970 return;
971 }
972 else {
973 return;
974 }
975 displayExtensionStep(pos);
976}
977
978// function used to display the extension pages
979static void displayExtensionStep(nbgl_stepPosition_t pos)
980{
981 nbgl_layoutCenteredInfo_t info = {0};
982 const nbgl_contentTagValueList_t *tagValueList = NULL;
983 const nbgl_contentInfoList_t *infoList = NULL;
984 const char *text = NULL;
985 const char *subText = NULL;
986
987 if (context.review.extensionStepCtx != NULL) {
988 nbgl_stepRelease(context.review.extensionStepCtx);
989 }
990 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
991 if (context.review.currentExtensionPage == 0) {
992 pos |= FIRST_STEP;
993 }
994 else {
996 }
997
998 switch (context.review.extension->aliasType) {
999 case ENS_ALIAS:
1000 text = context.review.extension->title;
1001 subText = context.review.extension->fullValue;
1002 break;
1003 case INFO_LIST_ALIAS:
1004 infoList = context.review.extension->infolist;
1005 text = PIC(infoList->infoTypes[context.review.currentExtensionPage]);
1006 subText = PIC(infoList->infoContents[context.review.currentExtensionPage]);
1007 break;
1009 tagValueList = context.review.extension->tagValuelist;
1010 text = PIC(tagValueList->pairs[context.review.currentExtensionPage].item);
1011 subText = PIC(tagValueList->pairs[context.review.currentExtensionPage].value);
1012 break;
1013 default:
1014 break;
1015 }
1016 if (text != NULL) {
1017 context.review.extensionStepCtx = nbgl_stepDrawText(
1018 pos, extensionNavigate, NULL, text, subText, BOLD_TEXT1_INFO, true);
1019 }
1020 }
1021 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
1022 // draw the back page
1023 info.icon = &C_icon_back_x;
1024 info.text1 = "Back";
1025 info.style = BOLD_TEXT1_INFO;
1026 pos |= LAST_STEP;
1027 context.review.extensionStepCtx
1028 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
1029 }
1030 nbgl_refresh();
1031}
1032
1033static void displayAliasFullValue(void)
1034{
1035 const char *text = NULL;
1036 const char *subText = NULL;
1037 const nbgl_icon_details_t *icon;
1038 bool isCenteredInfo;
1039
1040 getPairData(context.review.tagValueList,
1041 context.review.currentTagValueIndex,
1042 &text,
1043 &subText,
1044 &context.review.extension,
1045 &icon,
1046 &isCenteredInfo);
1047 if (context.review.extension == NULL) {
1048 // probably an error
1050 "displayAliasFullValue: extension nor found for pair %d\n",
1051 context.review.currentTagValueIndex);
1052 return;
1053 }
1054 context.review.currentExtensionPage = 0;
1055 context.review.extensionStepCtx = NULL;
1056 // create a modal flow to display this extension
1057 switch (context.review.extension->aliasType) {
1058 case ENS_ALIAS:
1059 context.review.nbExtensionPages = 2;
1060 break;
1061 case INFO_LIST_ALIAS:
1062 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
1063 break;
1065 context.review.nbExtensionPages = context.review.extension->tagValuelist->nbPairs + 1;
1066 break;
1067 default:
1069 "displayAliasFullValue: unsupported alias type %d\n",
1070 context.review.extension->aliasType);
1071 return;
1072 }
1073 displayExtensionStep(FORWARD_DIRECTION);
1074}
1075
1076static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1077{
1078 if (approve) {
1079 // Approve page
1080 *icon = &C_icon_validate_14;
1081 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1082 *text = "Confirm";
1083 }
1084 else {
1085 // if finish title is provided, use it
1086 if (context.review.finishTitle != NULL) {
1087 *text = context.review.finishTitle;
1088 }
1089 else {
1090 switch (context.operationType & REAL_TYPE_MASK) {
1091 case TYPE_TRANSACTION:
1092 if (context.operationType & RISKY_OPERATION) {
1093 *text = "Accept risk and sign transaction";
1094 }
1095 else {
1096 *text = "Sign transaction";
1097 }
1098 break;
1099 case TYPE_MESSAGE:
1100 if (context.operationType & RISKY_OPERATION) {
1101 *text = "Accept risk and sign message";
1102 }
1103 else {
1104 *text = "Sign message";
1105 }
1106 break;
1107 default:
1108 if (context.operationType & RISKY_OPERATION) {
1109 *text = "Accept risk and sign operation";
1110 }
1111 else {
1112 *text = "Sign operation";
1113 }
1114 break;
1115 }
1116 }
1117 }
1118 context.stepCallback = onReviewAccept;
1119 }
1120 else {
1121 // Reject page
1122 *icon = &C_icon_crossmark;
1123 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1124 *text = "Cancel";
1125 }
1126 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1127 *text = "Reject transaction";
1128 }
1129 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1130 *text = "Reject message";
1131 }
1132 else {
1133 *text = "Reject operation";
1134 }
1135 context.stepCallback = onReviewReject;
1136 }
1137}
1138
1139// function used to display the current page in review
1140static void displayReviewPage(nbgl_stepPosition_t pos)
1141{
1142 uint8_t reviewPages = 0;
1143 uint8_t finalPages = 0;
1144 uint8_t pairIndex = 0;
1145 const char *text = NULL;
1146 const char *subText = NULL;
1147 const nbgl_icon_details_t *icon = NULL;
1148 uint8_t currentIndex = 0;
1149 uint8_t titleIndex = 255;
1150 uint8_t subIndex = 255;
1151 uint8_t approveIndex = 255;
1152 uint8_t rejectIndex = 255;
1153 const nbgl_contentValueExt_t *extension = NULL;
1154 ForcedType_t forcedType = NO_FORCED_TYPE;
1155
1156 context.stepCallback = NULL;
1157
1158 // Determine the 1st page to display tag/values
1159 // Title page to display
1160 titleIndex = currentIndex++;
1161 reviewPages++;
1162 if (context.review.reviewSubTitle) {
1163 // subtitle page to display
1164 subIndex = currentIndex++;
1165 reviewPages++;
1166 }
1167 approveIndex = context.nbPages - 2;
1168 rejectIndex = context.nbPages - 1;
1169 finalPages = approveIndex;
1170
1171 // Determine which page to display
1172 if (context.currentPage >= finalPages) {
1173 if (context.currentPage == approveIndex) {
1174 // Approve page
1175 getLastPageInfo(true, &icon, &text);
1176 }
1177 else if (context.currentPage == rejectIndex) {
1178 // Reject page
1179 getLastPageInfo(false, &icon, &text);
1180 }
1181 }
1182 else if (context.currentPage < reviewPages) {
1183 if (context.currentPage == titleIndex) {
1184 // Title page
1185 icon = context.review.icon;
1186 text = context.review.reviewTitle;
1187 }
1188 else if (context.currentPage == subIndex) {
1189 // SubTitle page
1190 text = context.review.reviewSubTitle;
1191 }
1192 }
1193 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1194 // address confirmation and 2nd page
1195 text = "Address";
1196 subText = context.review.address;
1197 }
1198 else {
1199 // if there is a skip, and we are not already displaying the "skip" page
1200 // and we are not at the first tag/value of the first set of data (except if going
1201 // backward) then display the "skip" page
1202 if ((context.operationType & SKIPPABLE_OPERATION) && (context.review.skipDisplay == false)
1203 && ((context.currentPage > reviewPages)
1204 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1205 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1206 nbgl_layoutCenteredInfo_t info = {0};
1207 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1208 directions |= LAST_STEP;
1209 }
1210 info.icon = &C_Information_circle_14px;
1211 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1212 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1213 nbgl_refresh();
1214 context.review.skipDisplay = true;
1215 context.firstPairPage = reviewPages;
1216 return;
1217 }
1218 context.review.skipDisplay = false;
1219 bool isCenteredInfo = false;
1220 pairIndex = context.currentPage - reviewPages;
1221 if (context.review.address != NULL) {
1222 pairIndex--;
1223 }
1224 getPairData(context.review.tagValueList,
1225 pairIndex,
1226 &text,
1227 &subText,
1228 &extension,
1229 &icon,
1230 &isCenteredInfo);
1231 if (extension != NULL) {
1232 context.stepCallback = displayAliasFullValue;
1233 context.review.currentTagValueIndex = pairIndex;
1234 forcedType = FORCE_BUTTON;
1235 }
1236 else {
1237 if (isCenteredInfo) {
1238 forcedType = FORCE_CENTERED_INFO;
1239 }
1240 }
1241 }
1242
1243 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1244 nbgl_refresh();
1245}
1246
1247// function used to display the current page in review
1248static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1249{
1250 const char *text = NULL;
1251 const char *subText = NULL;
1252 const nbgl_icon_details_t *icon = NULL;
1253 uint8_t reviewPages = 0;
1254 uint8_t titleIndex = 255;
1255 uint8_t subIndex = 255;
1256 const nbgl_contentValueExt_t *extension = NULL;
1257 ForcedType_t forcedType = NO_FORCED_TYPE;
1258
1259 context.stepCallback = NULL;
1260 switch (context.type) {
1261 case STREAMING_START_REVIEW_USE_CASE:
1262 // Title page to display
1263 titleIndex = reviewPages++;
1264 if (context.review.reviewSubTitle) {
1265 // subtitle page to display
1266 subIndex = reviewPages++;
1267 }
1268 // Determine which page to display
1269 if (context.currentPage >= reviewPages) {
1270 onReviewAccept();
1271 return;
1272 }
1273 // header page(s)
1274 if (context.currentPage == titleIndex) {
1275 // title page
1276 icon = context.review.icon;
1277 text = context.review.reviewTitle;
1278 }
1279 else if (context.currentPage == subIndex) {
1280 // subtitle page
1281 text = context.review.reviewSubTitle;
1282 }
1283 break;
1284
1285 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1286 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1287 onReviewAccept();
1288 return;
1289 }
1290 // if there is a skip, and we are not already displaying the "skip" page
1291 // and we are not at the first tag/value of the first set of data (except if going
1292 // backward) then display the "skip" page
1293 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1294 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1295 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1296 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1297 nbgl_layoutCenteredInfo_t info = {0};
1298 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1299 directions |= LAST_STEP;
1300 }
1301 info.icon = &C_Information_circle_14px;
1302 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1303 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1304 nbgl_refresh();
1305 context.review.skipDisplay = true;
1306 return;
1307 }
1308 context.review.skipDisplay = false;
1309 bool isCenteredInfo = false;
1310 getPairData(context.review.tagValueList,
1311 context.currentPage,
1312 &text,
1313 &subText,
1314 &extension,
1315 &icon,
1316 &isCenteredInfo);
1317 if (extension != NULL) {
1318 forcedType = FORCE_BUTTON;
1319 }
1320 else {
1321 if (isCenteredInfo) {
1322 forcedType = FORCE_CENTERED_INFO;
1323 }
1324 }
1325 break;
1326
1327 case STREAMING_FINISH_REVIEW_USE_CASE:
1328 default:
1329 if (context.currentPage == 0) {
1330 // accept page
1331 getLastPageInfo(true, &icon, &text);
1332 }
1333 else {
1334 // reject page
1335 getLastPageInfo(false, &icon, &text);
1336 }
1337 break;
1338 }
1339
1340 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1341 nbgl_refresh();
1342}
1343
1344// function used to display the current page in info
1345static void displayInfoPage(nbgl_stepPosition_t pos)
1346{
1347 const char *text = NULL;
1348 const char *subText = NULL;
1349 const nbgl_icon_details_t *icon = NULL;
1350
1351 context.stepCallback = NULL;
1352
1353 if (context.currentPage < (context.nbPages - 1)) {
1354 text = PIC(
1355 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1356 subText = PIC(
1357 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1358 }
1359 else {
1360 icon = &C_icon_back_x;
1361 text = "Back";
1362 context.stepCallback = startUseCaseHome;
1363 }
1364
1365 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1366 nbgl_refresh();
1367}
1368
1369// function used to get the current page content
1370static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1371{
1372 uint8_t elemIdx = 0;
1373 const nbgl_content_t *p_content = NULL;
1374 nbgl_content_t content = {0};
1375 nbgl_contentSwitch_t *contentSwitch = NULL;
1376#ifdef WITH_HORIZONTAL_CHOICES_LIST
1377 nbgl_contentRadioChoice_t *contentChoices = NULL;
1378 char **names = NULL;
1379#endif
1380#ifdef WITH_HORIZONTAL_BARS_LIST
1381 nbgl_contentBarsList_t *contentBars = NULL;
1382 char **texts = NULL;
1383#endif
1384 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1385 if (p_content == NULL) {
1386 return;
1387 }
1388 switch (p_content->type) {
1389 case CENTERED_INFO:
1390 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1391 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1392 break;
1393 case INFO_BUTTON:
1394 contentPage->icon = PIC(p_content->content.infoButton.icon);
1395 contentPage->text = PIC(p_content->content.infoButton.text);
1396 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1397 break;
1398 case TAG_VALUE_LIST:
1399 getPairData(&p_content->content.tagValueList,
1400 elemIdx,
1401 &contentPage->text,
1402 &contentPage->subText,
1403 &contentPage->extension,
1404 &contentPage->icon,
1405 &contentPage->isCenteredInfo);
1406 break;
1407 case TAG_VALUE_CONFIRM:
1408 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
1409 getPairData(&p_content->content.tagValueConfirm.tagValueList,
1410 elemIdx,
1411 &contentPage->text,
1412 &contentPage->subText,
1413 &contentPage->extension,
1414 &contentPage->icon,
1415 &contentPage->isCenteredInfo);
1416 }
1417 else {
1418 contentPage->text = p_content->content.tagValueConfirm.confirmationText;
1419 contentPage->icon = &C_icon_validate_14;
1420 contentPage->isAction = true;
1421 }
1422 break;
1423 case SWITCHES_LIST:
1424 contentPage->isSwitch = true;
1425 contentSwitch = &(
1426 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1427 contentPage->text = contentSwitch->text;
1428 contentPage->state = contentSwitch->initState;
1429 if (toogle_state) {
1430 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1431 }
1432 context.stepCallback = onSwitchAction;
1433 contentPage->subText = contentSwitch->subText;
1434 break;
1435 case INFOS_LIST:
1436 contentPage->text
1437 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1438 contentPage->subText
1439 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1440 break;
1441 case CHOICES_LIST:
1442#ifdef WITH_HORIZONTAL_CHOICES_LIST
1443 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1444 names = (char **) PIC(contentChoices->names);
1445 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1446 contentPage->text = PIC(context.content.title);
1447 contentPage->subText = (const char *) PIC(names[elemIdx]);
1448 }
1449 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1450 contentPage->text = PIC(context.home.appName);
1451 contentPage->subText = (const char *) PIC(names[elemIdx]);
1452 }
1453 else {
1454 contentPage->text = (const char *) PIC(names[elemIdx]);
1455 }
1456#endif
1457 break;
1458 case BARS_LIST:
1459#ifdef WITH_HORIZONTAL_BARS_LIST
1460 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1461 texts = (char **) PIC(contentBars->barTexts);
1462 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1463 contentPage->text = PIC(context.content.title);
1464 contentPage->subText = PIC(texts[elemIdx]);
1465 }
1466 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1467 contentPage->text = PIC(context.home.appName);
1468 contentPage->subText = PIC(texts[elemIdx]);
1469 }
1470 else {
1471 contentPage->text = PIC(texts[elemIdx]);
1472 }
1473#endif
1474 break;
1475 default:
1476 break;
1477 }
1478}
1479
1480// function used to display the current page in settings
1481static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1482{
1483 PageContent_t contentPage = {0};
1484
1485 context.stepCallback = NULL;
1486
1487 if (context.currentPage < (context.nbPages - 1)) {
1488 getContentPage(toogle_state, &contentPage);
1489 }
1490 else { // last page is for quit
1491 contentPage.icon = &C_icon_back_x;
1492 contentPage.text = "Back";
1493 if (context.type == GENERIC_SETTINGS) {
1494 context.stepCallback = context.home.quitCallback;
1495 }
1496 else {
1497 context.stepCallback = startUseCaseHome;
1498 }
1499 }
1500
1501 if (contentPage.isSwitch) {
1502 drawSwitchStep(
1503 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1504 }
1505 else {
1506 drawStep(pos,
1507 contentPage.icon,
1508 contentPage.text,
1509 contentPage.subText,
1510 settingsCallback,
1511 false,
1512 NO_FORCED_TYPE);
1513 }
1514
1515 nbgl_refresh();
1516}
1517
1518static void startUseCaseHome(void)
1519{
1520 switch (context.type) {
1521 case SETTINGS_USE_CASE:
1522 // Settings page index
1523 context.currentPage = 1;
1524 if (context.home.homeAction) {
1525 // Action page is before Settings page
1526 context.currentPage++;
1527 }
1528 break;
1529 case INFO_USE_CASE:
1530 // Info page index
1531 context.currentPage = 1;
1532 if (context.home.homeAction) {
1533 // Action page is before Settings and Info pages
1534 context.currentPage++;
1535 }
1536 if (context.home.settingContents) {
1537 // Settings page is before Info pages
1538 context.currentPage++;
1539 }
1540 break;
1541 default:
1542 // Home page index
1543 context.currentPage = 0;
1544 break;
1545 }
1546
1547 context.type = HOME_USE_CASE;
1548 context.nbPages = 2; // Home + Quit
1549 if (context.home.settingContents) {
1550 context.nbPages++;
1551 }
1552 if (context.home.infosList) {
1553 context.nbPages++;
1554 }
1555 if (context.home.homeAction) {
1556 context.nbPages++;
1557 }
1558 displayHomePage(FORWARD_DIRECTION);
1559}
1560
1561static void startUseCaseInfo(void)
1562{
1563 context.type = INFO_USE_CASE;
1564 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1565 context.currentPage = 0;
1566
1567 displayInfoPage(FORWARD_DIRECTION);
1568}
1569
1570static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1571{
1572 nbgl_content_t content = {0};
1573 const nbgl_content_t *p_content = NULL;
1574
1575 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1576 if (context.type != GENERIC_SETTINGS) {
1577 context.type = SETTINGS_USE_CASE;
1578 }
1579
1580 context.nbPages = 1; // For back screen
1581 for (uint i = 0; i < context.home.settingContents->nbContents; i++) {
1582 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1583 context.nbPages += getContentNbElement(p_content);
1584 }
1585 context.currentPage = initSettingPage;
1586
1587 displaySettingsPage(FORWARD_DIRECTION, false);
1588}
1589
1590static void startUseCaseSettings(void)
1591{
1592 startUseCaseSettingsAtPage(0);
1593}
1594
1595static void startUseCaseContent(void)
1596{
1597 uint8_t contentIdx = 0;
1598 const nbgl_content_t *p_content = NULL;
1599 nbgl_content_t content = {0};
1600
1601 context.nbPages = 1; // Quit
1602
1603 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1604 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1605 context.nbPages += getContentNbElement(p_content);
1606 }
1607
1608 // Ensure currentPage is valid
1609 if (context.currentPage >= context.nbPages) {
1610 return;
1611 }
1612
1613 displayContent(FORWARD_DIRECTION, false);
1614}
1615
1616// function used to display the current page in home
1617static void displayHomePage(nbgl_stepPosition_t pos)
1618{
1619 const char *text = NULL;
1620 const char *subText = NULL;
1621 const nbgl_icon_details_t *icon = NULL;
1622 uint8_t currentIndex = 0;
1623 uint8_t homeIndex = 255;
1624 uint8_t actionIndex = 255;
1625 uint8_t settingsIndex = 255;
1626 uint8_t infoIndex = 255;
1627
1628 context.stepCallback = NULL;
1629
1630 // Determine which pages are present
1631 homeIndex = currentIndex++;
1632 if (context.home.homeAction) {
1633 actionIndex = currentIndex++;
1634 }
1635 if (context.home.settingContents) {
1636 settingsIndex = currentIndex++;
1637 }
1638 if (context.home.infosList) {
1639 infoIndex = currentIndex++;
1640 }
1641
1642 if (context.currentPage == homeIndex) {
1643 // Home page
1644 icon = context.home.appIcon;
1645 if (context.home.tagline != NULL) {
1646 text = context.home.tagline;
1647 }
1648 else {
1649 text = context.home.appName;
1650 subText = "app is ready";
1651 }
1652 }
1653 else if (context.currentPage == actionIndex) {
1654 // Action page
1655 icon = context.home.homeAction->icon;
1656 text = PIC(context.home.homeAction->text);
1657 context.stepCallback = context.home.homeAction->callback;
1658 }
1659 else if (context.currentPage == settingsIndex) {
1660 // Settings page
1661 icon = &C_icon_coggle;
1662 text = "App settings";
1663 context.stepCallback = startUseCaseSettings;
1664 }
1665 else if (context.currentPage == infoIndex) {
1666 // About page
1667 icon = &C_Information_circle_14px;
1668 text = "App info";
1669 context.stepCallback = startUseCaseInfo;
1670 }
1671 else {
1672 icon = &C_Quit_14px;
1673 text = "Quit app";
1674 context.stepCallback = context.home.quitCallback;
1675 }
1676
1677 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1678 nbgl_refresh();
1679}
1680
1681// function used to display the current page in choice
1682static void displayChoicePage(nbgl_stepPosition_t pos)
1683{
1684 const char *text = NULL;
1685 const char *subText = NULL;
1686 const nbgl_icon_details_t *icon = NULL;
1687 // set to 1 if there is only one page for intro (if either icon or subMessage is NULL)
1688 uint8_t acceptPage = 0;
1689
1690 if (context.choice.message != NULL) {
1691 if ((context.choice.icon == NULL) || (context.choice.subMessage == NULL)) {
1692 acceptPage = 1;
1693 }
1694 else {
1695 acceptPage = 2;
1696 }
1697 }
1698 context.stepCallback = NULL;
1699
1700 if (context.currentPage < acceptPage) {
1701 if (context.currentPage == 0) { // title page
1702 text = context.choice.message;
1703 if (context.choice.icon != NULL) {
1704 icon = context.choice.icon;
1705 }
1706 else {
1707 subText = context.choice.subMessage;
1708 }
1709 }
1710 else if ((acceptPage == 2) && (context.currentPage == 1)) { // sub-title page
1711 // displayed only if there is both icon and submessage
1712 text = context.choice.message;
1713 subText = context.choice.subMessage;
1714 }
1715 }
1716 else if (context.currentPage == acceptPage) { // confirm page
1717 icon = &C_icon_validate_14;
1718 text = context.choice.confirmText;
1719 context.stepCallback = onChoiceAccept;
1720 }
1721 else if (context.currentPage == (acceptPage + 1)) { // cancel page
1722 icon = &C_icon_crossmark;
1723 text = context.choice.cancelText;
1724 context.stepCallback = onChoiceReject;
1725 }
1726 else if (context.choice.details != NULL) {
1727 // only the first level of details and BAR_LIST type are supported
1728 if (context.choice.details->type == BAR_LIST_WARNING) {
1729 text = context.choice.details->barList.texts[context.currentPage - (acceptPage + 2)];
1730 subText
1731 = context.choice.details->barList.subTexts[context.currentPage - (acceptPage + 2)];
1732 }
1733 }
1734
1735 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1736 nbgl_refresh();
1737}
1738
1739// function used to display the Confirm page
1740static void displayConfirm(nbgl_stepPosition_t pos)
1741{
1742 const char *text = NULL;
1743 const char *subText = NULL;
1744 const nbgl_icon_details_t *icon = NULL;
1745
1746 context.stepCallback = NULL;
1747 switch (context.currentPage) {
1748 case 0:
1749 // title page
1750 text = context.confirm.message;
1751 subText = context.confirm.subMessage;
1752 break;
1753 case 1:
1754 // confirm page
1755 icon = &C_icon_validate_14;
1756 text = context.confirm.confirmText;
1757 context.stepCallback = onConfirmAccept;
1758 break;
1759 case 2:
1760 // cancel page
1761 icon = &C_icon_crossmark;
1762 text = context.confirm.cancelText;
1763 context.stepCallback = onConfirmReject;
1764 break;
1765 }
1766
1767 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1768 nbgl_refresh();
1769}
1770
1771// function used to display the current navigable content
1772static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1773{
1774 PageContent_t contentPage = {0};
1775 ForcedType_t forcedType = NO_FORCED_TYPE;
1776
1777 context.stepCallback = NULL;
1778
1779 if (context.currentPage < (context.nbPages - 1)) {
1780 getContentPage(toogle_state, &contentPage);
1781 if (contentPage.isCenteredInfo) {
1782 forcedType = FORCE_CENTERED_INFO;
1783 }
1784 context.forceAction = contentPage.isAction;
1785 }
1786 else { // last page is for quit
1787 if (context.content.rejectText) {
1788 contentPage.text = context.content.rejectText;
1789 }
1790 else {
1791 contentPage.text = "Back";
1792 }
1793 if (context.type == GENERIC_REVIEW_USE_CASE) {
1794 contentPage.icon = &C_icon_crossmark;
1795 }
1796 else {
1797 contentPage.icon = &C_icon_back_x;
1798 }
1799 context.stepCallback = context.content.quitCallback;
1800 }
1801
1802 if (contentPage.isSwitch) {
1803 drawSwitchStep(
1804 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1805 }
1806 else {
1807 drawStep(pos,
1808 contentPage.icon,
1809 contentPage.text,
1810 contentPage.subText,
1811 contentCallback,
1812 false,
1813 forcedType);
1814 }
1815 context.forceAction = false;
1816 nbgl_refresh();
1817}
1818
1819static void displaySpinner(const char *text)
1820{
1821 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1822 nbgl_refresh();
1823}
1824
1825// function to factorize code for all simple reviews
1826static void useCaseReview(ContextType_t type,
1827 nbgl_operationType_t operationType,
1828 const nbgl_contentTagValueList_t *tagValueList,
1829 const nbgl_icon_details_t *icon,
1830 const char *reviewTitle,
1831 const char *reviewSubTitle,
1832 const char *finishTitle,
1833 nbgl_choiceCallback_t choiceCallback)
1834{
1835 memset(&context, 0, sizeof(UseCaseContext_t));
1836 context.type = type;
1837 context.operationType = operationType;
1838 context.review.tagValueList = tagValueList;
1839 context.review.reviewTitle = reviewTitle;
1840 context.review.reviewSubTitle = reviewSubTitle;
1841 context.review.finishTitle = finishTitle;
1842 context.review.icon = icon;
1843 context.review.onChoice = choiceCallback;
1844 context.currentPage = 0;
1845 // 1 page for title and 2 pages at the end for accept/reject
1846 context.nbPages = tagValueList->nbPairs + 3;
1847 if (reviewSubTitle) {
1848 context.nbPages++; // 1 page for subtitle page
1849 }
1850
1851 displayReviewPage(FORWARD_DIRECTION);
1852}
1853
1854#ifdef NBGL_KEYPAD
1855static void setPinCodeText(void)
1856{
1857 bool enableValidate = false;
1858 bool enableBackspace = true;
1859
1860 // pin can be validated when min digits is entered
1861 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1862 // backspace is disabled when no digit is entered and back vallback is not provided
1863 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1864 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1865 context.keypad.hidden,
1866 context.keypad.pinLen,
1867 (const char *) context.keypad.pinEntry);
1869 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1870 nbgl_layoutDraw(context.keypad.layoutCtx);
1871 nbgl_refresh();
1872}
1873
1874// called when a key is touched on the keypad
1875static void keypadCallback(char touchedKey)
1876{
1877 switch (touchedKey) {
1878 case BACKSPACE_KEY:
1879 if (context.keypad.pinLen > 0) {
1880 context.keypad.pinLen--;
1881 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1882 }
1883 else if (context.keypad.backCallback != NULL) {
1884 context.keypad.backCallback();
1885 break;
1886 }
1887 setPinCodeText();
1888 break;
1889
1890 case VALIDATE_KEY:
1891 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1892 break;
1893
1894 default:
1895 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1896 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1897 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1898 context.keypad.pinLen++;
1899 }
1900 setPinCodeText();
1901 }
1902 break;
1903 }
1904}
1905#endif // NBGL_KEYPAD
1906
1907#ifdef NBGL_KEYBOARD
1908// Saved keyboard context for suggestion selection
1909// (needed because switching to CONTENT_USE_CASE overwrites the union context)
1910static struct {
1911 const char **buttons;
1912 int firstButtonToken;
1913 uint8_t nbUsedButtons;
1914 nbgl_layoutTouchCallback_t onButtonCallback;
1915 nbgl_callback_t backCallback;
1916} savedKeyboardContext;
1917
1918// Navigation callback to fill the suggestion choices page
1919static bool suggestionNavCallback(uint8_t page, nbgl_pageContent_t *content)
1920{
1921 UNUSED(page);
1922 content->type = CHOICES_LIST;
1923 content->choicesList.names = savedKeyboardContext.buttons;
1924 content->choicesList.token = savedKeyboardContext.firstButtonToken;
1925 content->choicesList.initChoice = 0;
1926 content->choicesList.nbChoices = savedKeyboardContext.nbUsedButtons;
1927 return true;
1928}
1929
1930// Display suggestion selection page
1931static void displaySuggestionSelection(void)
1932{
1933 char title[20] = {0};
1934 // Save keyboard context before it gets overwritten
1935 savedKeyboardContext.buttons = context.keyboard.content.suggestionButtons.buttons;
1936 savedKeyboardContext.firstButtonToken
1937 = context.keyboard.content.suggestionButtons.firstButtonToken;
1938 savedKeyboardContext.nbUsedButtons = context.keyboard.content.suggestionButtons.nbUsedButtons;
1939 savedKeyboardContext.onButtonCallback = context.keyboard.onButtonCallback;
1940 savedKeyboardContext.backCallback = context.keyboard.backCallback;
1941
1942 // Release the keyboard layout
1943 nbgl_layoutRelease(context.keyboard.layoutCtx);
1944 context.keyboard.layoutCtx = NULL;
1945
1946 snprintf(title, sizeof(title), "Select word #%d", context.keyboard.content.number);
1948 0,
1949 1,
1950 savedKeyboardContext.backCallback,
1951 suggestionNavCallback,
1952 savedKeyboardContext.onButtonCallback);
1953}
1954
1955// called when a key is touched on the keyboard
1956static void keyboardCallback(char touchedKey)
1957{
1958 uint32_t mask = 0;
1959 size_t textLen = strlen(context.keyboard.entryBuffer);
1960 PRINTF("[keyboardCallback] touchedKey: '%c'\n", touchedKey);
1961 if (touchedKey == BACKSPACE_KEY) {
1962 if (textLen == 0) {
1963 // Used to exit the keyboard when backspace is pressed on an empty entry
1964 context.keyboard.backCallback();
1965 return;
1966 }
1967 context.keyboard.entryBuffer[--textLen] = '\0';
1968 }
1969 else if (touchedKey == VALIDATE_KEY) {
1970 context.keyboard.actionCallback();
1971 return;
1972 }
1973 else {
1974 context.keyboard.entryBuffer[textLen] = touchedKey;
1975 context.keyboard.entryBuffer[++textLen] = '\0';
1976 }
1977 // Set the keyMask to disable some keys
1978 if (context.keyboard.content.type == KEYBOARD_WITH_SUGGESTIONS) {
1979 // if suggestions are displayed, we update them at each key press
1980 context.keyboard.getSuggestButtons(&context.keyboard.content, &mask);
1981 if ((context.keyboard.content.suggestionButtons.nbUsedButtons > 0)
1982 && (context.keyboard.content.suggestionButtons.nbUsedButtons
1983 < NB_MAX_SUGGESTION_BUTTONS)) {
1984 // On Nano, when we have few suggestions, display a selection page
1985 // instead of continuing with keyboard entry
1986 displaySuggestionSelection();
1987 return; // Don't update keyboard, we're in suggestion mode
1988 }
1989 }
1990 else if (textLen >= context.keyboard.entryMaxLen) {
1991 // entry length can't be greater, so we mask every characters
1992 mask = -1;
1993 }
1994 nbgl_layoutUpdateKeyboard(context.keyboard.layoutCtx, context.keyboard.keyboardIndex, mask);
1996 context.keyboard.layoutCtx, context.keyboard.textIndex, context.keyboard.entryBuffer);
1997 nbgl_refresh();
1998}
1999#endif
2000
2001// this is the function called to start the actual review, from the initial warning pages
2002static void launchReviewAfterWarning(void)
2003{
2004 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
2005 useCaseReview(reviewWithWarnCtx.type,
2006 reviewWithWarnCtx.operationType,
2007 reviewWithWarnCtx.tagValueList,
2008 reviewWithWarnCtx.icon,
2009 reviewWithWarnCtx.reviewTitle,
2010 reviewWithWarnCtx.reviewSubTitle,
2011 reviewWithWarnCtx.finishTitle,
2012 reviewWithWarnCtx.choiceCallback);
2013 }
2014 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
2015 displayStreamingReviewPage(FORWARD_DIRECTION);
2016 }
2017}
2018
2019// callback used when navigating in a bar detail sub-page
2020static void barDetailNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
2021{
2022 UNUSED(stepCtx);
2023 if (event == BUTTON_LEFT_PRESSED) {
2024 displayWarningStep();
2025 }
2026}
2027
2028// display a bar detail sub-page (CENTERED_INFO_WARNING type)
2029static void displayBarDetailStep(void)
2030{
2031 const nbgl_genericDetails_t *detail
2032 = &reviewWithWarnCtx.warning->introDetails->barList.details[reviewWithWarnCtx.barDetailIdx];
2033
2034 if (detail->type == CENTERED_INFO_WARNING) {
2035 nbgl_layoutCenteredInfo_t info = {0};
2036 info.icon = detail->centeredInfo.icon;
2037 info.text1 = detail->centeredInfo.title;
2038 info.text2 = detail->centeredInfo.description;
2039 info.style = BOLD_TEXT1_INFO;
2040 // LAST_STEP: only left arrow shown, pressing LEFT goes back via barDetailNavigate
2042 LAST_STEP | BACKWARD_DIRECTION, barDetailNavigate, NULL, &info, false);
2043 nbgl_refresh();
2044 }
2045}
2046
2047// this is the callback used when navigating in warning pages
2048static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
2049{
2050 UNUSED(stepCtx);
2051 uint8_t barIdx = 0;
2052
2053 if (event == BUTTON_LEFT_PRESSED) {
2054 // only decrement page if we are not at the first page
2055 if (reviewWithWarnCtx.warningPage > 0) {
2056 reviewWithWarnCtx.warningPage--;
2057 }
2058 }
2059 else if (event == BUTTON_RIGHT_PRESSED) {
2060 // only increment page if not at last page
2061 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
2062 reviewWithWarnCtx.warningPage++;
2063 }
2064 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2065 && (reviewWithWarnCtx.warning->info != NULL)) {
2066 launchReviewAfterWarning();
2067 return;
2068 }
2069 }
2070 else if ((event == BUTTON_BOTH_PRESSED)
2071 && (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
2072 // if at first page, double press leads to start of review
2073 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2074 launchReviewAfterWarning();
2075 }
2076 // if at last page, reject operation
2077 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2078 reviewWithWarnCtx.choiceCallback(false);
2079 }
2080 return;
2081 }
2082 else if ((event == BUTTON_BOTH_PRESSED) && (reviewWithWarnCtx.warning->introDetails != NULL)
2083 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)
2084 && (reviewWithWarnCtx.warningPage > reviewWithWarnCtx.firstWarningPage)) {
2085 // enter the detail sub-page for the current bar (if it has one)
2086 barIdx = reviewWithWarnCtx.warningPage - reviewWithWarnCtx.firstWarningPage - 1;
2087 if ((reviewWithWarnCtx.warning->introDetails->barList.details != NULL)
2088 && (reviewWithWarnCtx.warning->introDetails->barList.details[barIdx].type
2089 != NO_TYPE_WARNING)) {
2090 reviewWithWarnCtx.barDetailIdx = barIdx;
2091 displayBarDetailStep();
2092 }
2093 return;
2094 }
2095 else {
2096 return;
2097 }
2098 displayWarningStep();
2099}
2100
2101// function used to display the initial warning pages when starting a "review with warning"
2102static void displayWarningStep(void)
2103{
2104 nbgl_layoutCenteredInfo_t info = {0};
2105 nbgl_stepPosition_t pos = 0;
2106 uint8_t barIdx = 0;
2107 if ((reviewWithWarnCtx.warning->prelude) && (reviewWithWarnCtx.warningPage == 0)) {
2108 // for prelude, only draw text as a single step
2110 warningNavigate,
2111 NULL,
2112 reviewWithWarnCtx.warning->prelude->title,
2113 reviewWithWarnCtx.warning->prelude->description,
2114 REGULAR_INFO,
2115 false);
2116 nbgl_refresh();
2117 return;
2118 }
2119 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2120 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2121 // draw the main warning page
2122 info.icon = &C_icon_warning;
2123 info.text1 = "Blind signing ahead";
2124 info.text2 = "To accept risk, press both buttons";
2125 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2127 pos |= FORWARD_DIRECTION;
2128 }
2129 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2130 getLastPageInfo(false, &info.icon, &info.text1);
2132 }
2133 }
2134 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2135 && (reviewWithWarnCtx.warning->info != NULL)) {
2136 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2137 info.icon = reviewWithWarnCtx.warning->info->icon;
2138 info.text1 = reviewWithWarnCtx.warning->info->title;
2139 info.text2 = reviewWithWarnCtx.warning->info->description;
2140 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2142 pos |= FORWARD_DIRECTION;
2143 }
2144 else if ((reviewWithWarnCtx.warning->introDetails != NULL)
2145 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)) {
2146 // intermediate or last bar page
2147 barIdx = reviewWithWarnCtx.warningPage - reviewWithWarnCtx.firstWarningPage - 1;
2148 if (reviewWithWarnCtx.warning->introDetails->barList.icons) {
2149 info.icon = reviewWithWarnCtx.warning->introDetails->barList.icons[barIdx];
2150 }
2151 info.text1 = reviewWithWarnCtx.warning->introDetails->barList.texts[barIdx];
2152 if (reviewWithWarnCtx.warning->introDetails->barList.subTexts) {
2153 info.text2 = reviewWithWarnCtx.warning->introDetails->barList.subTexts[barIdx];
2154 }
2156 }
2157 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2158 if ((reviewWithWarnCtx.warning->introDetails != NULL)
2159 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING)) {
2160 info.icon = reviewWithWarnCtx.warning->introDetails->centeredInfo.icon;
2161 info.text1 = reviewWithWarnCtx.warning->introDetails->centeredInfo.title;
2162 info.text2 = reviewWithWarnCtx.warning->introDetails->centeredInfo.description;
2164 }
2165 else {
2166 // not supported
2167 return;
2168 }
2169 }
2170 }
2171 else {
2172 // not supported
2173 return;
2174 }
2175 info.style = BOLD_TEXT1_INFO;
2176 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
2177 nbgl_refresh();
2178}
2179
2180// function used to display the initial warning page when starting a "review with warning"
2181static void displayInitialWarning(void)
2182{
2183 // draw the main warning page
2184 reviewWithWarnCtx.warningPage = 0;
2185 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2186 || ((reviewWithWarnCtx.warning->introDetails)
2187 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2188 reviewWithWarnCtx.nbWarningPages = 2;
2189 }
2190 else if ((reviewWithWarnCtx.warning->introDetails)
2191 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)) {
2192 reviewWithWarnCtx.nbWarningPages
2193 = reviewWithWarnCtx.warning->introDetails->barList.nbBars + 1;
2194 }
2195 else {
2196 // if no intro details and not Blind Signing warning, only one page
2197 reviewWithWarnCtx.nbWarningPages = 1;
2198 }
2199
2200 reviewWithWarnCtx.firstWarningPage = 0;
2201 displayWarningStep();
2202}
2203
2204// function used to display the prelude page when starting a "review with warning"
2205static void displayPrelude(void)
2206{
2207 // draw the main warning page
2208 reviewWithWarnCtx.warningPage = 0;
2209 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2210 || ((reviewWithWarnCtx.warning->introDetails)
2211 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2212 reviewWithWarnCtx.nbWarningPages = 3;
2213 }
2214 else {
2215 // if no intro details and not Blind Signing warning, only 2 pages
2216 reviewWithWarnCtx.nbWarningPages = 2;
2217 }
2218 reviewWithWarnCtx.firstWarningPage = 1;
2219 displayWarningStep();
2220}
2221
2222/**********************
2223 * GLOBAL FUNCTIONS
2224 **********************/
2225
2237uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs,
2238 const nbgl_contentTagValueList_t *tagValueList,
2239 uint8_t startIndex,
2240 bool *requireSpecificDisplay)
2241{
2242 UNUSED(nbPairs);
2243 UNUSED(tagValueList);
2244 UNUSED(startIndex);
2245 *requireSpecificDisplay = true;
2246 return 1;
2247}
2248
2261uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs,
2262 const nbgl_contentTagValueList_t *tagValueList,
2263 uint8_t startIndex,
2264 bool isSkippable,
2265 bool *requireSpecificDisplay)
2266{
2267 UNUSED(nbPairs);
2268 UNUSED(tagValueList);
2269 UNUSED(startIndex);
2270 UNUSED(isSkippable);
2271 *requireSpecificDisplay = true;
2272 return 1;
2273}
2274
2283uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos,
2284 const nbgl_contentInfoList_t *infosList,
2285 uint8_t startIndex,
2286 bool withNav)
2287{
2288 UNUSED(nbInfos);
2289 UNUSED(infosList);
2290 UNUSED(startIndex);
2291 UNUSED(withNav);
2292 return 1;
2293}
2294
2303uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches,
2304 const nbgl_contentSwitchesList_t *switchesList,
2305 uint8_t startIndex,
2306 bool withNav)
2307{
2308 UNUSED(nbSwitches);
2309 UNUSED(switchesList);
2310 UNUSED(startIndex);
2311 UNUSED(withNav);
2312 return 1;
2313}
2314
2323uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars,
2324 const nbgl_contentBarsList_t *barsList,
2325 uint8_t startIndex,
2326 bool withNav)
2327{
2328 UNUSED(nbBars);
2329 UNUSED(barsList);
2330 UNUSED(startIndex);
2331 UNUSED(withNav);
2332 return 1;
2333}
2334
2343uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices,
2344 const nbgl_contentRadioChoice_t *choicesList,
2345 uint8_t startIndex,
2346 bool withNav)
2347{
2348 UNUSED(nbChoices);
2349 UNUSED(choicesList);
2350 UNUSED(startIndex);
2351 UNUSED(withNav);
2352 return 1;
2353}
2354
2362{
2363 uint8_t nbPages = 0;
2364 uint8_t nbPairs = tagValueList->nbPairs;
2365 uint8_t nbPairsInPage;
2366 uint8_t i = 0;
2367 bool flag;
2368
2369 while (i < tagValueList->nbPairs) {
2370 // upper margin
2371 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2372 i += nbPairsInPage;
2373 nbPairs -= nbPairsInPage;
2374 nbPages++;
2375 }
2376 return nbPages;
2377}
2378
2392void nbgl_useCaseNavigableContent(const char *title,
2393 uint8_t initPage,
2394 uint8_t nbPages,
2395 nbgl_callback_t quitCallback,
2396 nbgl_navCallback_t navCallback,
2397 nbgl_layoutTouchCallback_t controlsCallback)
2398{
2399 memset(&context, 0, sizeof(UseCaseContext_t));
2400 context.type = CONTENT_USE_CASE;
2401 context.currentPage = initPage;
2402 context.content.title = title;
2403 context.content.quitCallback = quitCallback;
2404 context.content.navCallback = navCallback;
2405 context.content.controlsCallback = controlsCallback;
2406 context.content.genericContents.callbackCallNeeded = true;
2407 context.content.genericContents.nbContents = nbPages;
2408
2409 startUseCaseContent();
2410}
2411
2426void nbgl_useCaseHomeAndSettings(const char *appName,
2427 const nbgl_icon_details_t *appIcon,
2428 const char *tagline,
2429 const uint8_t initSettingPage,
2430 const nbgl_genericContents_t *settingContents,
2431 const nbgl_contentInfoList_t *infosList,
2432 const nbgl_homeAction_t *action,
2433 nbgl_callback_t quitCallback)
2434{
2435 memset(&context, 0, sizeof(UseCaseContext_t));
2436 context.home.appName = appName;
2437 context.home.appIcon = appIcon;
2438 context.home.tagline = tagline;
2439 context.home.settingContents = PIC(settingContents);
2440 context.home.infosList = PIC(infosList);
2441 context.home.homeAction = action;
2442 context.home.quitCallback = quitCallback;
2443
2444 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2445 startUseCaseSettingsAtPage(initSettingPage);
2446 }
2447 else {
2448 startUseCaseHome();
2449 }
2450}
2451
2464void nbgl_useCaseGenericSettings(const char *appName,
2465 uint8_t initPage,
2466 const nbgl_genericContents_t *settingContents,
2467 const nbgl_contentInfoList_t *infosList,
2468 nbgl_callback_t quitCallback)
2469{
2470 memset(&context, 0, sizeof(UseCaseContext_t));
2471 context.type = GENERIC_SETTINGS;
2472 context.home.appName = appName;
2473 context.home.settingContents = PIC(settingContents);
2474 context.home.infosList = PIC(infosList);
2475 context.home.quitCallback = quitCallback;
2476
2477 startUseCaseSettingsAtPage(initPage);
2478}
2479
2491void nbgl_useCaseGenericConfiguration(const char *title,
2492 uint8_t initPage,
2493 const nbgl_genericContents_t *contents,
2494 nbgl_callback_t quitCallback)
2495{
2496 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2497}
2498
2513void nbgl_useCaseReview(nbgl_operationType_t operationType,
2514 const nbgl_contentTagValueList_t *tagValueList,
2515 const nbgl_icon_details_t *icon,
2516 const char *reviewTitle,
2517 const char *reviewSubTitle,
2518 const char *finishTitle,
2519 nbgl_choiceCallback_t choiceCallback)
2520{
2521 useCaseReview(REVIEW_USE_CASE,
2522 operationType,
2523 tagValueList,
2524 icon,
2525 reviewTitle,
2526 reviewSubTitle,
2527 finishTitle,
2528 choiceCallback);
2529}
2530
2554 const nbgl_contentTagValueList_t *tagValueList,
2555 const nbgl_icon_details_t *icon,
2556 const char *reviewTitle,
2557 const char *reviewSubTitle,
2558 const char *finishTitle,
2559 const nbgl_tipBox_t *tipBox,
2560 const nbgl_warning_t *warning,
2561 nbgl_choiceCallback_t choiceCallback)
2562{
2563 UNUSED(tipBox);
2564 ContextType_t type = REVIEW_USE_CASE;
2565
2566 // if no warning at all, it's a simple review
2567 if ((warning == NULL)
2568 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2569 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2570 useCaseReview(type,
2571 operationType,
2572 tagValueList,
2573 icon,
2574 reviewTitle,
2575 reviewSubTitle,
2576 finishTitle,
2577 choiceCallback);
2578 return;
2579 }
2580 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2581 operationType |= NO_THREAT_OPERATION;
2582 }
2583 else if (warning->predefinedSet != 0) {
2584 operationType |= RISKY_OPERATION;
2585 }
2586
2587 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2588 reviewWithWarnCtx.type = type;
2589 reviewWithWarnCtx.operationType = operationType;
2590 reviewWithWarnCtx.tagValueList = tagValueList;
2591 reviewWithWarnCtx.icon = icon;
2592 reviewWithWarnCtx.reviewTitle = reviewTitle;
2593 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2594 reviewWithWarnCtx.finishTitle = finishTitle;
2595 reviewWithWarnCtx.warning = warning;
2596 reviewWithWarnCtx.choiceCallback = choiceCallback;
2597
2598 // if the warning contains a prelude, display it first
2599 if (reviewWithWarnCtx.warning->prelude) {
2600 displayPrelude();
2601 }
2602 // display the initial warning only of a risk/threat or blind signing or prelude
2603 else if (HAS_INITIAL_WARNING(warning)) {
2604 displayInitialWarning();
2605 }
2606 else {
2607 useCaseReview(type,
2608 operationType,
2609 tagValueList,
2610 icon,
2611 reviewTitle,
2612 reviewSubTitle,
2613 finishTitle,
2614 choiceCallback);
2615 }
2616}
2617
2638 const nbgl_contentTagValueList_t *tagValueList,
2639 const nbgl_icon_details_t *icon,
2640 const char *reviewTitle,
2641 const char *reviewSubTitle,
2642 const char *finishTitle,
2643 const nbgl_tipBox_t *dummy,
2644 nbgl_choiceCallback_t choiceCallback)
2645{
2646 nbgl_useCaseAdvancedReview(operationType,
2647 tagValueList,
2648 icon,
2649 reviewTitle,
2650 reviewSubTitle,
2651 finishTitle,
2652 dummy,
2653 &blindSigningWarning,
2654 choiceCallback);
2655}
2656
2672 const nbgl_contentTagValueList_t *tagValueList,
2673 const nbgl_icon_details_t *icon,
2674 const char *reviewTitle,
2675 const char *reviewSubTitle,
2676 const char *finishTitle,
2677 nbgl_choiceCallback_t choiceCallback)
2678{
2679 nbgl_useCaseReview(operationType,
2680 tagValueList,
2681 icon,
2682 reviewTitle,
2683 reviewSubTitle,
2684 finishTitle,
2685 choiceCallback);
2686}
2687
2704void nbgl_useCaseAddressReview(const char *address,
2705 const nbgl_contentTagValueList_t *additionalTagValueList,
2706 const nbgl_icon_details_t *icon,
2707 const char *reviewTitle,
2708 const char *reviewSubTitle,
2709 nbgl_choiceCallback_t choiceCallback)
2710{
2711 memset(&context, 0, sizeof(UseCaseContext_t));
2712 context.type = ADDRESS_REVIEW_USE_CASE;
2713 context.review.address = address;
2714 context.review.reviewTitle = reviewTitle;
2715 context.review.reviewSubTitle = reviewSubTitle;
2716 context.review.icon = icon;
2717 context.review.onChoice = choiceCallback;
2718 context.currentPage = 0;
2719 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2720 // + 1 if sub Title
2721 context.nbPages = reviewSubTitle ? 5 : 4;
2722 if (additionalTagValueList) {
2723 context.review.tagValueList = PIC(additionalTagValueList);
2724 context.nbPages += additionalTagValueList->nbPairs;
2725 }
2726
2727 displayReviewPage(FORWARD_DIRECTION);
2728}
2729
2739 const char *rejectText,
2740 nbgl_callback_t rejectCallback)
2741{
2742 memset(&context, 0, sizeof(UseCaseContext_t));
2743 context.type = GENERIC_REVIEW_USE_CASE;
2744 context.content.rejectText = rejectText;
2745 context.content.quitCallback = rejectCallback;
2746 context.content.genericContents.nbContents = contents->nbContents;
2747 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2748 if (contents->callbackCallNeeded) {
2749 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2750 }
2751 else {
2752 context.content.genericContents.contentsList = PIC(contents->contentsList);
2753 }
2754
2755 startUseCaseContent();
2756}
2757
2766void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2767{
2768 UNUSED(isSuccess);
2769 memset(&context, 0, sizeof(UseCaseContext_t));
2770 context.type = STATUS_USE_CASE;
2771 context.stepCallback = quitCallback;
2772 context.currentPage = 0;
2773 context.nbPages = 1;
2774
2776 NULL,
2777 message,
2778 NULL,
2779 statusButtonCallback,
2780 false,
2781 NO_FORCED_TYPE);
2782}
2783
2791 nbgl_callback_t quitCallback)
2792{
2793 const char *msg;
2794 bool isSuccess;
2795 switch (reviewStatusType) {
2797 msg = "Operation signed";
2798 isSuccess = true;
2799 break;
2801 msg = "Operation rejected";
2802 isSuccess = false;
2803 break;
2805 msg = "Transaction signed";
2806 isSuccess = true;
2807 break;
2809 msg = "Transaction rejected";
2810 isSuccess = false;
2811 break;
2813 msg = "Message signed";
2814 isSuccess = true;
2815 break;
2817 msg = "Message rejected";
2818 isSuccess = false;
2819 break;
2821 msg = "Address verified";
2822 isSuccess = true;
2823 break;
2825 msg = "Address verification cancelled";
2826 isSuccess = false;
2827 break;
2828 default:
2829 return;
2830 }
2831 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2832}
2833
2847 const nbgl_icon_details_t *icon,
2848 const char *reviewTitle,
2849 const char *reviewSubTitle,
2850 nbgl_choiceCallback_t choiceCallback)
2851{
2852 // memorize streaming operation type for future API calls
2853 streamingOpType = operationType;
2854
2855 memset(&context, 0, sizeof(UseCaseContext_t));
2856 context.type = STREAMING_START_REVIEW_USE_CASE;
2857 context.operationType = operationType;
2858 context.review.reviewTitle = reviewTitle;
2859 context.review.reviewSubTitle = reviewSubTitle;
2860 context.review.icon = icon;
2861 context.review.onChoice = choiceCallback;
2862 context.currentPage = 0;
2863 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2864
2865 displayStreamingReviewPage(FORWARD_DIRECTION);
2866}
2867
2882 const nbgl_icon_details_t *icon,
2883 const char *reviewTitle,
2884 const char *reviewSubTitle,
2885 nbgl_choiceCallback_t choiceCallback)
2886{
2888 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2889}
2890
2907 const nbgl_icon_details_t *icon,
2908 const char *reviewTitle,
2909 const char *reviewSubTitle,
2910 const nbgl_warning_t *warning,
2911 nbgl_choiceCallback_t choiceCallback)
2912{
2913 memset(&context, 0, sizeof(UseCaseContext_t));
2914 context.type = STREAMING_START_REVIEW_USE_CASE;
2915 context.operationType = operationType;
2916 context.review.reviewTitle = reviewTitle;
2917 context.review.reviewSubTitle = reviewSubTitle;
2918 context.review.icon = icon;
2919 context.review.onChoice = choiceCallback;
2920 context.currentPage = 0;
2921 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2922
2923 // memorize streaming operation type for future API calls
2924 streamingOpType = operationType;
2925
2926 // if no warning at all, it's a simple review
2927 if ((warning == NULL)
2928 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2929 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2930 displayStreamingReviewPage(FORWARD_DIRECTION);
2931 return;
2932 }
2933 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2934 operationType |= NO_THREAT_OPERATION;
2935 }
2936 else if (warning->predefinedSet != 0) {
2937 operationType |= RISKY_OPERATION;
2938 }
2939 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2940
2941 reviewWithWarnCtx.type = context.type;
2942 reviewWithWarnCtx.operationType = operationType;
2943 reviewWithWarnCtx.icon = icon;
2944 reviewWithWarnCtx.reviewTitle = reviewTitle;
2945 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2946 reviewWithWarnCtx.choiceCallback = choiceCallback;
2947 reviewWithWarnCtx.warning = warning;
2948
2949 // if the warning contains a prelude, display it first
2950 if (reviewWithWarnCtx.warning->prelude) {
2951 displayPrelude();
2952 }
2953 // display the initial warning only of a risk/threat or blind signing or prelude
2954 else if (HAS_INITIAL_WARNING(warning)) {
2955 displayInitialWarning();
2956 }
2957 else {
2958 displayStreamingReviewPage(FORWARD_DIRECTION);
2959 }
2960}
2961
2976 nbgl_choiceCallback_t choiceCallback,
2977 nbgl_callback_t skipCallback)
2978{
2979 uint8_t curNbDataSets = context.review.nbDataSets;
2980
2981 memset(&context, 0, sizeof(UseCaseContext_t));
2982 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
2983 context.operationType = streamingOpType;
2984 context.review.tagValueList = tagValueList;
2985 context.review.onChoice = choiceCallback;
2986 context.currentPage = 0;
2987 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
2988 context.review.skipCallback = skipCallback;
2989 context.review.nbDataSets = curNbDataSets + 1;
2990
2991 displayStreamingReviewPage(FORWARD_DIRECTION);
2992}
2993
3005 nbgl_choiceCallback_t choiceCallback)
3006{
3007 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3008}
3009
3010void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3011 nbgl_choiceCallback_t choiceCallback)
3012{
3013 memset(&context, 0, sizeof(UseCaseContext_t));
3014 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
3015 context.operationType = streamingOpType;
3016 context.review.onChoice = choiceCallback;
3017 context.review.finishTitle = finishTitle;
3018 context.currentPage = 0;
3019 context.nbPages = 2; // 2 pages at the end for accept/reject
3020
3021 displayStreamingReviewPage(FORWARD_DIRECTION);
3022}
3023
3029void nbgl_useCaseSpinner(const char *text)
3030{
3031 memset(&context, 0, sizeof(UseCaseContext_t));
3032 context.type = SPINNER_USE_CASE;
3033 context.currentPage = 0;
3034 context.nbPages = 1;
3035
3036 displaySpinner(text);
3037}
3038
3052 const char *message,
3053 const char *subMessage,
3054 const char *confirmText,
3055 const char *cancelText,
3056 nbgl_choiceCallback_t callback)
3057{
3059 icon, message, subMessage, confirmText, cancelText, NULL, callback);
3060};
3061
3077 const char *message,
3078 const char *subMessage,
3079 const char *confirmText,
3080 const char *cancelText,
3081 nbgl_genericDetails_t *details,
3082 nbgl_choiceCallback_t callback)
3083{
3084 memset(&context, 0, sizeof(UseCaseContext_t));
3085 context.type = CHOICE_USE_CASE;
3086 context.choice.icon = icon;
3087 context.choice.message = message;
3088 context.choice.subMessage = subMessage;
3089 context.choice.confirmText = confirmText;
3090 context.choice.cancelText = cancelText;
3091 context.choice.onChoice = callback;
3092 context.choice.details = details;
3093 context.currentPage = 0;
3094 context.nbPages = 2; // 2 pages for confirm/cancel
3095 if (message != NULL) {
3096 context.nbPages++;
3097 // if both icon and subMessage are non NULL, add a page
3098 if ((icon != NULL) && (subMessage != NULL)) {
3099 context.nbPages++;
3100 }
3101 }
3102 if (details != NULL) {
3103 // only the first level of details and BAR_LIST type are supported
3104 if (details->type == BAR_LIST_WARNING) {
3105 context.nbPages += details->barList.nbBars;
3106 }
3107 }
3108
3109 displayChoicePage(FORWARD_DIRECTION);
3110};
3111
3125void nbgl_useCaseConfirm(const char *message,
3126 const char *subMessage,
3127 const char *confirmText,
3128 const char *cancelText,
3129 nbgl_callback_t callback)
3130{
3131 memset(&context, 0, sizeof(UseCaseContext_t));
3132 context.type = CONFIRM_USE_CASE;
3133 context.confirm.message = message;
3134 context.confirm.subMessage = subMessage;
3135 context.confirm.confirmText = confirmText;
3136 context.confirm.cancelText = cancelText;
3137 context.confirm.onConfirm = callback;
3138 context.currentPage = 0;
3139 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
3140
3141 displayConfirm(FORWARD_DIRECTION);
3142}
3143
3154 const char *message,
3155 const char *actionText,
3156 nbgl_callback_t callback)
3157{
3158 nbgl_layoutCenteredInfo_t centeredInfo = {0};
3159
3160 UNUSED(actionText);
3161
3162 // memorize callback in context
3163 memset(&context, 0, sizeof(UseCaseContext_t));
3164 context.type = ACTION_USE_CASE;
3165 context.action.actionCallback = callback;
3166
3167 centeredInfo.icon = icon;
3168 centeredInfo.text1 = message;
3169 centeredInfo.style = BOLD_TEXT1_INFO;
3170 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
3171}
3172
3173#ifdef NBGL_KEYPAD
3192void nbgl_useCaseKeypad(const char *title,
3193 uint8_t minDigits,
3194 uint8_t maxDigits,
3195 bool shuffled,
3196 bool hidden,
3197 nbgl_pinValidCallback_t validatePinCallback,
3198 nbgl_callback_t backCallback)
3199{
3200 nbgl_layoutDescription_t layoutDescription = {0};
3201 int status = -1;
3202
3203 // reset the keypad context
3204 memset(&context, 0, sizeof(KeypadContext_t));
3205 context.type = KEYPAD_USE_CASE;
3206 context.currentPage = 0;
3207 context.nbPages = 1;
3208 context.keypad.validatePin = validatePinCallback;
3209 context.keypad.backCallback = backCallback;
3210 context.keypad.pinMinDigits = minDigits;
3211 context.keypad.pinMaxDigits = maxDigits;
3212 context.keypad.hidden = hidden;
3213 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
3214
3215 // add keypad
3216 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
3217 if (status < 0) {
3218 return;
3219 }
3220 context.keypad.keypadIndex = status;
3221 // add digits
3222 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
3223 if (status < 0) {
3224 return;
3225 }
3226
3227 nbgl_layoutDraw(context.keypad.layoutCtx);
3228 if (context.keypad.backCallback != NULL) {
3229 // force backspace to be visible at first digit, to be used as quit
3230 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
3231 }
3232 nbgl_refresh();
3233}
3234#endif // NBGL_KEYPAD
3235
3236#ifdef NBGL_KEYBOARD
3260void nbgl_useCaseKeyboard(const nbgl_keyboardParams_t *params, nbgl_callback_t backCallback)
3261{
3262 // clang-format off
3263 nbgl_layoutDescription_t layoutDescription = {0};
3264 nbgl_layoutCenteredInfo_t centeredInfo = {.text1 = params->title,
3265 .onTop = true};
3267 .indication = LEFT_ARROW | RIGHT_ARROW};
3268 nbgl_layoutKbd_t kbdInfo = {.callback = &keyboardCallback,
3269 .mode = params->mode,
3270 .lettersOnly = params->lettersOnly};
3271 int status = -1;
3272 // clang-format on
3273
3274 // reset the keypad context
3275 memset(&context, 0, sizeof(UseCaseContext_t));
3276 context.type = KEYBOARD_USE_CASE;
3277 context.currentPage = 0;
3278 context.nbPages = 1;
3279 context.keyboard.entryBuffer = PIC(params->entryBuffer);
3280 context.keyboard.entryMaxLen = params->entryMaxLen;
3281 context.keyboard.entryBuffer[0] = '\0';
3282
3283 context.keyboard.content.type = params->type;
3284
3285 // memorize context
3286 context.keyboard.backCallback = PIC(backCallback);
3287
3288 switch (params->type) {
3290 context.keyboard.actionCallback = PIC(params->confirmationParams.onButtonCallback);
3291 break;
3293 // No need for 'Validate' or 'Case switch' buttons
3294 kbdInfo.keyMask = (1 << VALIDATE_INDEX) | (1 << SHIFT_KEY_INDEX);
3295 context.keyboard.getSuggestButtons
3297 context.keyboard.onButtonCallback = PIC(params->suggestionParams.onButtonCallback);
3298 context.keyboard.content.suggestionButtons = (nbgl_layoutSuggestionButtons_t){
3299 .buttons = PIC(params->suggestionParams.buttons),
3300 .firstButtonToken = params->suggestionParams.firstButtonToken,
3301 };
3302 break;
3303 default:
3304 return;
3305 }
3306
3307 // get a layout
3308 context.keyboard.layoutCtx = nbgl_layoutGet(&layoutDescription);
3309
3310 // add description
3311 nbgl_layoutAddCenteredInfo(context.keyboard.layoutCtx, &centeredInfo);
3312
3313 // Add keyboard
3314 status = nbgl_layoutAddKeyboard(context.keyboard.layoutCtx, &kbdInfo);
3315 if (status < 0) {
3316 return;
3317 }
3318 context.keyboard.keyboardIndex = status;
3319
3320 // add empty entered text
3321 status = nbgl_layoutAddEnteredText(context.keyboard.layoutCtx, "", true);
3322 if (status < 0) {
3323 return;
3324 }
3325 context.keyboard.textIndex = status;
3326
3327 nbgl_layoutAddNavigation(context.keyboard.layoutCtx, &navInfo);
3328
3329 nbgl_layoutDraw(context.keyboard.layoutCtx);
3330 nbgl_refresh();
3331}
3332#endif // NBGL_KEYBOARD
3333
3334#endif // HAVE_SE_TOUCH
3335#endif // NBGL_USE_CASE
nbgl_contentCenteredInfoStyle_t
possible styles for Centered Info Area
@ CHOICES_LIST
list of choices through radio buttons
@ CENTERED_INFO
a centered info
@ SWITCHES_LIST
list of switches with descriptions
@ INFOS_LIST
list of infos with titles
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
@ TAG_VALUE_LIST
list of tag/value pairs
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
@ INFO_BUTTON
a centered info and a simple black button
@ INFO_LIST_ALIAS
alias is list of infos
@ TAG_VALUE_LIST_ALIAS
@ ENS_ALIAS
alias comes from ENS
void(* nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
prototype of function to be called when an action on a content object occurs
debug traces management
#define LOG_WARN(__logger,...)
Definition nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition nbgl_debug.h:35
void(* nbgl_stepCallback_t)(void)
prototype of function to be called when a step is using a callback on "double-key" action
Definition nbgl_flow.h:38
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
DEPRECATED int nbgl_layoutAddEnteredText(nbgl_layout_t *layout, bool numbered, uint8_t number, const char *text, bool grayedOut, int offsetY, int token)
Adds a "text entry" area under the previously entered object. This area can be preceded (beginning of...
int nbgl_layoutUpdateKeyboard(nbgl_layout_t *layout, uint8_t index, uint32_t keyMask, bool updateCasing, keyboardCase_t casing)
Updates an existing keyboard on bottom of the screen, with the given configuration.
int nbgl_layoutAddKeyboard(nbgl_layout_t *layout, const nbgl_layoutKbd_t *kbdInfo)
Creates a keyboard on bottom of the screen, with the given configuration.
@ HORIZONTAL_NAV
'<' and '>' are displayed, to navigate between pages and steps
@ LEFT_ARROW
left arrow is used
@ RIGHT_ARROW
right arrow is used
int nbgl_layoutUpdateKeypad(nbgl_layout_t *layout, uint8_t index, bool enableValidate, bool enableBackspace, bool enableDigits)
Updates an existing keypad on bottom of the screen, with the given configuration.
int nbgl_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...
int nbgl_layoutDraw(nbgl_layout_t *layout)
Applies given layout. The screen will be redrawn.
void * nbgl_layout_t
type shared externally
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.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
@ KEYBOARD_WITH_BUTTON
text entry area + confirmation button
@ KEYBOARD_WITH_SUGGESTIONS
text entry area + suggestion buttons
int nbgl_layoutRelease(nbgl_layout_t *layout)
Release the layout obtained with nbgl_layoutGet()
DEPRECATED int nbgl_layoutUpdateEnteredText(nbgl_layout_t *layout, uint8_t index, bool numbered, uint8_t number, const char *text, bool grayedOut)
Updates an existing "text entry" area, created with nbgl_layoutAddEnteredText()
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
#define SHIFT_KEY_INDEX
Definition nbgl_obj.h:41
void nbgl_refresh(void)
nbgl_buttonEvent_t
Definition nbgl_obj.h:311
@ BUTTON_BOTH_PRESSED
Sent when both buttons are released.
Definition nbgl_obj.h:318
@ BUTTON_LEFT_PRESSED
Sent when Left button is released.
Definition nbgl_obj.h:312
@ BUTTON_RIGHT_PRESSED
Send when Right button is released.
Definition nbgl_obj.h:313
#define BACKSPACE_KEY
Definition nbgl_obj.h:26
#define VALIDATE_KEY
Definition nbgl_obj.h:27
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
void(* nbgl_stepButtonCallback_t)(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
prototype of function to be called when buttons are touched on a screen
Definition nbgl_step.h:57
#define GET_POS_OF_STEP(_step, _nb_steps)
Definition nbgl_step.h:30
nbgl_step_t nbgl_stepDrawSwitch(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutSwitch_t *switchInfo, bool modal)
void * nbgl_step_t
type shared externally
Definition nbgl_step.h:44
uint8_t nbgl_stepPosition_t
this type is a bitfield containing:
Definition nbgl_step.h:88
nbgl_step_t nbgl_stepDrawMenuList(nbgl_stepMenuListCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutMenuList_t *list, bool modal)
#define ACTION_ON_ANY_BUTTON
When action callback applies only on both button press.
Definition nbgl_step.h:75
@ NEITHER_FIRST_NOR_LAST_STEP
neither first nor last in a multiple steps flow
Definition nbgl_step.h:67
@ SINGLE_STEP
single step flow
Definition nbgl_step.h:64
@ LAST_STEP
last in a multiple steps flow
Definition nbgl_step.h:66
@ FIRST_STEP
first in a multiple steps flow
Definition nbgl_step.h:65
int nbgl_stepRelease(nbgl_step_t step)
#define FORWARD_DIRECTION
When the flow is navigated from last to first step.
Definition nbgl_step.h:71
nbgl_step_t nbgl_stepDrawText(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, const char *text, const char *subText, nbgl_contentCenteredInfoStyle_t style, bool modal)
nbgl_step_t nbgl_stepDrawCenteredInfo(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutCenteredInfo_t *info, bool modal)
#define BACKWARD_DIRECTION
When action callback applies on any button press.
Definition nbgl_step.h:73
nbgl_state_t
to represent a boolean state.
Definition nbgl_types.h:199
@ ON_STATE
Definition nbgl_types.h:201
@ OFF_STATE
Definition nbgl_types.h:200
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
@ NO_TYPE_WARNING
Invalid type (to use for bars leading to nothing)
@ CENTERED_INFO_WARNING
Centered info.
@ BAR_LIST_WARNING
list of touchable bars, to display sub-pages
uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool isSkippable, bool *requireSpecificDisplay)
void(* nbgl_callback_t)(void)
prototype of generic callback function
#define SKIPPABLE_OPERATION
This is to use in nbgl_operationType_t when the operation is skippable. This is used.
void nbgl_useCaseGenericSettings(const char *appName, uint8_t initPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, nbgl_callback_t quitCallback)
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)
void nbgl_useCaseAction(const nbgl_icon_details_t *icon, const char *message, const char *actionText, nbgl_callback_t callback)
uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool *requireSpecificDisplay)
uint8_t nbgl_useCaseGetNbPagesForTagValueList(const nbgl_contentTagValueList_t *tagValueList)
void(* nbgl_pinValidCallback_t)(const uint8_t *pin, uint8_t pinLen)
prototype of pin validation callback function
void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseHomeAndSettings(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, const uint8_t initSettingPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, const nbgl_homeAction_t *action, nbgl_callback_t quitCallback)
uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos, const nbgl_contentInfoList_t *infosList, uint8_t startIndex, bool withNav)
void(* nbgl_choiceCallback_t)(bool confirm)
prototype of choice callback function
uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars, const nbgl_contentBarsList_t *barsList, uint8_t startIndex, bool withNav)
void nbgl_useCaseNavigableContent(const char *title, uint8_t initPage, uint8_t nbPages, nbgl_callback_t quitCallback, nbgl_navCallback_t navCallback, nbgl_layoutTouchCallback_t controlsCallback)
void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseChoiceWithDetails(const nbgl_icon_details_t *icon, const char *message, const char *subMessage, const char *confirmText, const char *cancelText, nbgl_warningDetails_t *details, nbgl_choiceCallback_t callback)
void nbgl_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_NO_THREAT_WARN
Web3 Checks: No Threat detected.
void nbgl_useCaseGenericConfiguration(const char *title, uint8_t initPage, const nbgl_genericContents_t *contents, nbgl_callback_t quitCallback)
#define INIT_HOME_PAGE
Value to pass to nbgl_useCaseHomeAndSettings() initSettingPage parameter to initialize the use case o...
void nbgl_useCaseKeypad(const char *title, uint8_t minDigits, uint8_t maxDigits, bool shuffled, bool hidden, nbgl_pinValidCallback_t validatePinCallback, nbgl_callback_t backCallback)
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_useCaseGenericReview(const nbgl_genericContents_t *contents, const char *rejectText, nbgl_callback_t rejectCallback)
#define STATUS_SCREEN_DURATION
void nbgl_useCaseKeyboard(const nbgl_keyboardParams_t *params, nbgl_callback_t backCallback)
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices, const nbgl_contentRadioChoice_t *choicesList, uint8_t startIndex, bool withNav)
void(* nbgl_keyboardButtonsCallback_t)(nbgl_layoutKeyboardContent_t *content, uint32_t *mask)
prototype of keyboard buttons callback function
#define REAL_TYPE_MASK
This is the mask to apply on nbgl_operationType_t to get the real type provided by app.
void nbgl_useCaseReviewStatus(nbgl_reviewStatusType_t reviewStatusType, nbgl_callback_t quitCallback)
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 HAS_INITIAL_WARNING(_warning)
void nbgl_useCaseAddressReview(const char *address, const nbgl_contentTagValueList_t *additionalTagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
@ TYPE_MESSAGE
@ TYPE_TRANSACTION
For operations transferring a coin or taken from an account to another.
void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches, const nbgl_contentSwitchesList_t *switchesList, uint8_t startIndex, bool withNav)
This structure contains data to build a BARS_LIST content.
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
uint8_t nbBars
number of elements in barTexts and tokens array
const nbgl_icon_details_t * icon
the icon (can be null)
const char * title
title in black large (can be null)
const char * description
description in black small regular case (can be null)
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
bool onTop
if set to true, align only horizontally
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * buttonText
text of the long press button
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * text
centered text in large case
This structure contains data to build a INFOS_LIST content.
const char *const * infoContents
array of contents of infos (in black)
const char *const * infoTypes
array of types of infos (in black/bold)
uint8_t nbInfos
number of elements in infoTypes and infoContents array
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
uint8_t token
the token that will be used as argument of the callback
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
const char * text
main text for the switch
uint8_t token
the token that will be used as argument of the callback (unused on Nano)
nbgl_state_t initState
initial state of the switch
const char * subText
description under main text (NULL terminated, single line, may be null)
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
uint8_t confirmationToken
the token used as argument of the onActionCallback
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
This structure contains a list of [tag,value] pairs.
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair and possible extensions.
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
const nbgl_icon_details_t * valueIcon
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
const char * value
string giving the value name
const char * item
string giving the tag name
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
This structure contains data to build a content.
nbgl_content_u content
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
nbgl_contentType_t type
type of page content in the content union
const struct nbgl_genericDetails_s * details
array of nbBars structures giving what to display when each bar is touched.
uint8_t nbBars
number of touchable bars
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
The necessary parameters to build the page(s) displayed when the top-right button is touched in intro...
nbgl_genericBarList_t barList
touchable bars list, if type == BAR_LIST_WARNING
nbgl_genericDetailsType_t type
type of content in the page, determining what to use in the following union
nbgl_contentCenter_t centeredInfo
centered info, if type == CENTERED_INFO_WARNING
Structure describing the action button in Home Screen.
nbgl_callback_t onButtonCallback
callback to call when the button is pressed
nbgl_keyboardButtonsCallback_t updateButtonsCallback
callback to call when a key is pressed to update suggestions
nbgl_layoutTouchCallback_t onButtonCallback
callback to call when one of the buttons is pressed
const char ** buttons
array of strings for buttons (last ones can be NULL)
int firstButtonToken
first token used for buttons, provided in onButtonCallback
Structure containing all parameters for keyboard use case.
keyboardMode_t mode
keyboard mode to start with
nbgl_kbdSuggestParams_t suggestionParams
nbgl_kbdButtonParams_t confirmationParams
used if type is KEYBOARD_WITH_SUGGESTIONS
bool lettersOnly
if true, only display letter keys and Backspace
uint8_t entryMaxLen
maximum length of text that can be entered
nbgl_layoutKeyboardContentType_t type
type of content
const char * title
centered title explaining the screen
char * entryBuffer
already entered text
Structure containing all information when creating a layout. This structure must be passed as argumen...
This structure contains info to build a keyboard with nbgl_layoutAddKeyboard()
keyboardCallback_t callback
function called when an active key is pressed
This structure contains info to build a keyboard content (controls that are linked to keyboard)
This structure contains a list of names to build a menu list on Nanos, with for each item a descripti...
nbgl_menuListCallback_t callback
function to call to retrieve a menu list item text
uint8_t nbChoices
total number of choices in the menu list
uint8_t selectedChoice
index of the selected choice (centered, in bold)
This structure contains info to build a navigation bar at the bottom of the screen.
nbgl_layoutNavDirection_t direction
vertical or horizontal navigation
This structure contains info to build suggestion buttons.
This structure contains data to build a page in multi-pages mode (nbgl_pageDrawGenericContent)
Definition nbgl_flow.h:58
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition nbgl_flow.h:67
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:65
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:68
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:66
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition nbgl_flow.h:63
nbgl_contentType_t type
type of page content in the following union
Definition nbgl_flow.h:59
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition nbgl_flow.h:61
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition nbgl_flow.h:64
This structure contains data to build a SWITCHES_LIST content.
uint8_t nbSwitches
number of elements in switches and tokens array
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
The necessary parameters to build a warning page preceding a review. One can either use predefinedSet...
const nbgl_preludeDetails_t * prelude
if not null, means that the review can start by a prelude
const nbgl_warningDetails_t * reviewDetails
uint32_t predefinedSet
const nbgl_contentCenter_t * info
parameters to build the intro warning page, if not using pre-defined
nbgl_contentInfoList_t infosList
INFOS_LIST type
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
nbgl_contentBarsList_t barsList
BARS_LIST type
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type