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} ReviewWithWarningContext_t;
210
211typedef enum {
212 NO_FORCED_TYPE = 0,
213 FORCE_BUTTON,
214 FORCE_CENTERED_INFO
215} ForcedType_t;
216
217/**********************
218 * STATIC VARIABLES
219 **********************/
220static UseCaseContext_t context;
221
222static ReviewWithWarningContext_t reviewWithWarnCtx;
223// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
224static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
225
226// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
227nbgl_operationType_t streamingOpType;
228
229/**********************
230 * STATIC FUNCTIONS
231 **********************/
232static void displayReviewPage(nbgl_stepPosition_t pos);
233static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
234static void displayHomePage(nbgl_stepPosition_t pos);
235static void displayInfoPage(nbgl_stepPosition_t pos);
236static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
237static void displayChoicePage(nbgl_stepPosition_t pos);
238static void displayConfirm(nbgl_stepPosition_t pos);
239static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
240static void displaySpinner(const char *text);
241
242static void startUseCaseHome(void);
243static void startUseCaseInfo(void);
244static void startUseCaseSettings(void);
245static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
246static void startUseCaseContent(void);
247
248static void statusTickerCallback(void);
249
250static void displayExtensionStep(nbgl_stepPosition_t pos);
251static void displayWarningStep(void);
252
253// Simple helper to get the number of elements inside a nbgl_content_t
254static uint8_t getContentNbElement(const nbgl_content_t *content)
255{
256 switch (content->type) {
257 case CENTERED_INFO:
258 return 1;
259 case INFO_BUTTON:
260 return 1;
261 case TAG_VALUE_LIST:
262 return content->content.tagValueList.nbPairs;
264 // last element is for Confirm page
265 return content->content.tagValueConfirm.tagValueList.nbPairs + 1;
266 case SWITCHES_LIST:
267 return content->content.switchesList.nbSwitches;
268 case INFOS_LIST:
269 return content->content.infosList.nbInfos;
270 case CHOICES_LIST:
271 return content->content.choicesList.nbChoices;
272 case BARS_LIST:
273 return content->content.barsList.nbBars;
274 default:
275 return 0;
276 }
277}
278
279// Helper to retrieve the content inside a nbgl_genericContents_t using
280// either the contentsList or using the contentGetterCallback
281static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
282 uint8_t contentIdx,
283 nbgl_content_t *content)
284{
285 nbgl_pageContent_t pageContent = {0};
286 if (contentIdx >= genericContents->nbContents) {
287 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
288 return NULL;
289 }
290
291 if (genericContents->callbackCallNeeded) {
292 if (content == NULL) {
293 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
294 return NULL;
295 }
296 // Retrieve content through callback, but first memset the content.
297 memset(content, 0, sizeof(nbgl_content_t));
298 if (context.content.navCallback) {
299 if (context.content.navCallback(contentIdx, &pageContent) == true) {
300 // Copy the Page Content to the Content variable
301 content->type = pageContent.type;
302 switch (content->type) {
303 case CENTERED_INFO:
304 content->content.centeredInfo = pageContent.centeredInfo;
305 break;
306 case INFO_BUTTON:
307 content->content.infoButton = pageContent.infoButton;
308 break;
309 case TAG_VALUE_LIST:
310 content->content.tagValueList = pageContent.tagValueList;
311 break;
313 content->content.tagValueConfirm = pageContent.tagValueConfirm;
314 break;
315 case SWITCHES_LIST:
316 content->content.switchesList = pageContent.switchesList;
317 break;
318 case INFOS_LIST:
319 content->content.infosList = pageContent.infosList;
320 break;
321 case CHOICES_LIST:
322 content->content.choicesList = pageContent.choicesList;
323 break;
324 case BARS_LIST:
325 content->content.barsList = pageContent.barsList;
326 break;
327 default:
328 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
329 return NULL;
330 }
331 }
332 else {
333 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
334 return NULL;
335 }
336 }
337 else {
338 genericContents->contentGetterCallback(contentIdx, content);
339 }
340 return content;
341 }
342 else {
343 // Retrieve content through list
344 return PIC(&genericContents->contentsList[contentIdx]);
345 }
346}
347
348// Helper to retrieve the content inside a nbgl_genericContents_t using
349// either the contentsList or using the contentGetterCallback
350static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
351 uint8_t *elemContentIdx,
352 nbgl_content_t *content)
353{
354 const nbgl_genericContents_t *genericContents = NULL;
355 const nbgl_content_t *p_content = NULL;
356 uint8_t nbPages = 0;
357 uint8_t elemNbPages = 0;
358
359 switch (context.type) {
360 case SETTINGS_USE_CASE:
361 case HOME_USE_CASE:
362 case GENERIC_SETTINGS:
363 genericContents = context.home.settingContents;
364 break;
365 case CONTENT_USE_CASE:
366 case GENERIC_REVIEW_USE_CASE:
367 genericContents = &context.content.genericContents;
368 break;
369 default:
370 return NULL;
371 }
372 for (uint i = 0; i < genericContents->nbContents; i++) {
373 p_content = getContentAtIdx(genericContents, i, content);
374 elemNbPages = getContentNbElement(p_content);
375 if (nbPages + elemNbPages > elemIdx) {
376 *elemContentIdx = context.currentPage - nbPages;
377 break;
378 }
379 nbPages += elemNbPages;
380 }
381
382 return p_content;
383}
384
385static const char *getChoiceName(uint8_t choiceIndex)
386{
387 uint8_t elemIdx;
388 uint8_t nbValues;
389 const nbgl_content_t *p_content = NULL;
390 nbgl_content_t content = {0};
391 nbgl_contentRadioChoice_t *contentChoices = NULL;
392 nbgl_contentBarsList_t *contentBars = NULL;
393 char **names = NULL;
394
395 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
396 if (p_content == NULL) {
397 return NULL;
398 }
399 switch (p_content->type) {
400 case CHOICES_LIST:
401 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
402 names = (char **) PIC(contentChoices->names);
403 nbValues = contentChoices->nbChoices;
404 break;
405 case BARS_LIST:
406 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
407 names = (char **) PIC(contentBars->barTexts);
408 nbValues = contentBars->nbBars;
409 break;
410 default:
411 // Not supported as vertical MenuList
412 return NULL;
413 }
414 if (choiceIndex >= nbValues) {
415 // Last item is always "Back" button
416 return "Back";
417 }
418 return (const char *) PIC(names[choiceIndex]);
419}
420
421static void onChoiceSelected(uint8_t choiceIndex)
422{
423 uint8_t elemIdx;
424 uint8_t token = 255;
425 const nbgl_content_t *p_content = NULL;
426 nbgl_content_t content = {0};
427 nbgl_contentRadioChoice_t *contentChoices = NULL;
428 nbgl_contentBarsList_t *contentBars = NULL;
429
430 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
431 if (p_content == NULL) {
432 return;
433 }
434 switch (p_content->type) {
435 case CHOICES_LIST:
436 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
437 if (choiceIndex < contentChoices->nbChoices) {
438 token = contentChoices->token;
439 }
440 break;
441 case BARS_LIST:
442 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
443 if (choiceIndex < contentBars->nbBars) {
444 token = contentBars->tokens[choiceIndex];
445 }
446 break;
447 default:
448 // Not supported as vertical MenuList
449 break;
450 }
451 if ((token != 255) && (context.content.controlsCallback != NULL)) {
452 context.content.controlsCallback(token, 0);
453 }
454 else if (context.content.quitCallback != NULL) {
455 context.content.quitCallback();
456 }
457}
458
459static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
460 uint8_t index,
461 const char **item,
462 const char **value,
463 const nbgl_contentValueExt_t **extension,
464 const nbgl_icon_details_t **icon,
465 bool *isCenteredInfo)
466{
467 const nbgl_contentTagValue_t *pair;
468
469 if (tagValueList->pairs != NULL) {
470 pair = PIC(&tagValueList->pairs[index]);
471 }
472 else {
473 pair = PIC(tagValueList->callback(index));
474 }
475 *item = pair->item;
476 *value = pair->value;
477 if (pair->aliasValue) {
478 *extension = pair->extension;
479 }
480 else if (pair->centeredInfo) {
481 *isCenteredInfo = true;
482 *icon = pair->valueIcon;
483 }
484 else {
485 *extension = NULL;
486 }
487}
488
489static void onReviewAccept(void)
490{
491 if (context.review.onChoice) {
492 context.review.onChoice(true);
493 }
494}
495
496static void onReviewReject(void)
497{
498 if (context.review.onChoice) {
499 context.review.onChoice(false);
500 }
501}
502
503static void onChoiceAccept(void)
504{
505 if (context.choice.onChoice) {
506 context.choice.onChoice(true);
507 }
508}
509
510static void onChoiceReject(void)
511{
512 if (context.choice.onChoice) {
513 context.choice.onChoice(false);
514 }
515}
516
517static void onConfirmAccept(void)
518{
519 if (context.confirm.currentStep) {
520 nbgl_stepRelease(context.confirm.currentStep);
521 }
522 if (context.confirm.onConfirm) {
523 context.confirm.onConfirm();
524 }
525}
526
527static void onConfirmReject(void)
528{
529 if (context.confirm.currentStep) {
530 nbgl_stepRelease(context.confirm.currentStep);
532 nbgl_refresh();
533 }
534}
535
536static void onSwitchAction(void)
537{
538 const nbgl_contentSwitch_t *contentSwitch = NULL;
539 const nbgl_content_t *p_content = NULL;
540 nbgl_content_t content = {0};
541 uint8_t elemIdx;
542
543 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
544 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
545 return;
546 }
547 contentSwitch
548 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
549 switch (context.type) {
550 case SETTINGS_USE_CASE:
551 case HOME_USE_CASE:
552 case GENERIC_SETTINGS:
553 displaySettingsPage(FORWARD_DIRECTION, true);
554 break;
555 case CONTENT_USE_CASE:
556 case GENERIC_REVIEW_USE_CASE:
557 displayContent(FORWARD_DIRECTION, true);
558 break;
559 default:
560 break;
561 }
562 if (p_content->contentActionCallback != NULL) {
563 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
564 actionCallback(contentSwitch->token,
565 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
566 context.currentPage);
567 }
568 else if (context.content.controlsCallback != NULL) {
569 context.content.controlsCallback(contentSwitch->token, 0);
570 }
571}
572
573static void drawStep(nbgl_stepPosition_t pos,
574 const nbgl_icon_details_t *icon,
575 const char *txt,
576 const char *subTxt,
577 nbgl_stepButtonCallback_t onActionCallback,
578 bool modal,
579 ForcedType_t forcedType)
580{
581 uint8_t elemIdx;
582 nbgl_step_t newStep = NULL;
583 const nbgl_content_t *p_content = NULL;
584 nbgl_content_t content = {0};
585 nbgl_contentRadioChoice_t *contentChoices = NULL;
586 nbgl_contentBarsList_t *contentBars = NULL;
587 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
588 nbgl_layoutMenuList_t list = {0};
589 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
590 .tickerIntervale = 0, // not periodic
591 .tickerValue = STATUS_SCREEN_DURATION};
592
593 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
594 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
595 // (except the first set) because the set starts with a "skip" page
596 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
597 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
598 pos |= LAST_STEP;
599 }
600 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
601 p_ticker = &ticker;
602 }
603 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
604 nbgl_stepRelease(context.confirm.currentStep);
605 }
606
607 if (txt == NULL) {
608 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
609 if (p_content) {
610 switch (p_content->type) {
611 case CHOICES_LIST:
612 contentChoices
613 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
614 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
615 list.selectedChoice = contentChoices->initChoice;
616 list.callback = getChoiceName;
617 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
618 break;
619 case BARS_LIST:
620 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
621 list.nbChoices = contentBars->nbBars + 1; // For Back button
622 list.selectedChoice = 0;
623 list.callback = getChoiceName;
624 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
625 break;
626 default:
627 // Not supported as vertical MenuList
628 break;
629 }
630 }
631 }
632 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
634 if (subTxt != NULL) {
635 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
636 }
637 else {
638 style = REGULAR_INFO;
639 }
640 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
641 }
642 else {
644 info.icon = icon;
645 info.text1 = txt;
646 info.text2 = subTxt;
647 info.onTop = false;
648 if ((subTxt != NULL) || (context.stepCallback != NULL) || context.forceAction) {
649 info.style = BOLD_TEXT1_INFO;
650 }
651 else {
652 info.style = REGULAR_INFO;
653 }
654 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
655 }
656 if (context.type == CONFIRM_USE_CASE) {
657 context.confirm.currentStep = newStep;
658 }
659}
660
661static void drawSwitchStep(nbgl_stepPosition_t pos,
662 const char *title,
663 const char *description,
664 bool state,
665 nbgl_stepButtonCallback_t onActionCallback,
666 bool modal)
667{
668 nbgl_layoutSwitch_t switchInfo;
669
670 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
671 switchInfo.initState = state;
672 switchInfo.text = title;
673 switchInfo.subText = description;
674 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
675}
676
677static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
678{
679 uint8_t elemIdx;
680 uint8_t token = 0;
681 uint8_t index = 0;
682 const nbgl_content_t *p_content = NULL;
683 nbgl_content_t content = {0};
684
685 if (event == BUTTON_LEFT_PRESSED) {
686 if (context.currentPage > 0) {
687 context.currentPage--;
688 }
689 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
690 // the first set
691 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
692 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
693 // Drop the event
694 return false;
695 }
696 *pos = BACKWARD_DIRECTION;
697 }
698 else if (event == BUTTON_RIGHT_PRESSED) {
699 if (context.currentPage < (int) (context.nbPages - 1)) {
700 context.currentPage++;
701 }
702 else {
703 // Drop the event
704 return false;
705 }
706 *pos = FORWARD_DIRECTION;
707 }
708 else {
709 if (event == BUTTON_BOTH_PRESSED) {
710 if (context.stepCallback != NULL) {
711 context.stepCallback();
712 }
713 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
714 || (context.type == GENERIC_SETTINGS)
715 || (context.type == GENERIC_REVIEW_USE_CASE)) {
716 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
717 if (p_content != NULL) {
718 switch (p_content->type) {
719 case CENTERED_INFO:
720 // No associated callback
721 return false;
722 case INFO_BUTTON:
723 token = p_content->content.infoButton.buttonToken;
724 break;
725 case SWITCHES_LIST:
726 token = p_content->content.switchesList.switches->token;
727 break;
728 case BARS_LIST:
729 token = p_content->content.barsList.tokens[context.currentPage];
730 break;
731 case CHOICES_LIST:
732 token = p_content->content.choicesList.token;
733 index = context.currentPage;
734 break;
735 case TAG_VALUE_LIST:
736 return false;
738 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
739 return false;
740 }
741 token = p_content->content.tagValueConfirm.confirmationToken;
742 break;
743 default:
744 break;
745 }
746
747 if ((p_content) && (p_content->contentActionCallback != NULL)) {
748 p_content->contentActionCallback(token, 0, context.currentPage);
749 }
750 else if (context.content.controlsCallback != NULL) {
751 context.content.controlsCallback(token, index);
752 }
753 }
754 }
755 }
756 return false;
757 }
758 return true;
759}
760
761static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
762{
763 UNUSED(stepCtx);
765
766 if (!buttonGenericCallback(event, &pos)) {
767 return;
768 }
769 else {
770 // memorize last direction
771 context.review.dataDirection = pos;
772 }
773 displayReviewPage(pos);
774}
775
776// this is the callback used when button action on the "skip" page
777static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
778{
779 UNUSED(stepCtx);
781
782 if (event == BUTTON_LEFT_PRESSED) {
783 // only decrement page if we are going backward but coming from forward (back & forth)
784 if ((context.review.dataDirection == FORWARD_DIRECTION)
785 && (context.currentPage > context.firstPairPage)) {
786 context.currentPage--;
787 }
788 pos = BACKWARD_DIRECTION;
789 }
790 else if (event == BUTTON_RIGHT_PRESSED) {
791 // only increment page if we are going forward but coming from backward (back & forth)
792 if ((context.review.dataDirection == BACKWARD_DIRECTION)
793 && (context.currentPage < (int) (context.nbPages - 1))
794 && (context.currentPage > context.firstPairPage)) {
795 context.currentPage++;
796 }
797 pos = FORWARD_DIRECTION;
798 }
799 else if (event == BUTTON_BOTH_PRESSED) {
800 // the first tag/value page is at page 0 only in streaming case
801 if (context.firstPairPage == 0) {
802 // in streaming, we have to call the provided callback
803 context.review.skipCallback();
804 }
805 else {
806 // if not in streaming, go directly to the "approve" page
807 context.currentPage = context.nbPages - 2;
808 displayReviewPage(FORWARD_DIRECTION);
809 }
810 return;
811 }
812 else {
813 return;
814 }
815 // the first tag/value page is at page 0 only in streaming case
816 if (context.firstPairPage == 0) {
817 displayStreamingReviewPage(pos);
818 }
819 else {
820 displayReviewPage(pos);
821 }
822}
823
824// this is the callback used when buttons in "Action" use case are pressed
825static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
826{
827 UNUSED(stepCtx);
828
829 if (event == BUTTON_BOTH_PRESSED) {
830 context.action.actionCallback();
831 }
832}
833
834static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
835{
837 UNUSED(stepCtx);
838
839 if (!buttonGenericCallback(event, &pos)) {
840 return;
841 }
842 else {
843 // memorize last direction
844 context.review.dataDirection = pos;
845 }
846
847 displayStreamingReviewPage(pos);
848}
849
850static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
851{
852 UNUSED(stepCtx);
854
855 if (!buttonGenericCallback(event, &pos)) {
856 return;
857 }
858
859 displaySettingsPage(pos, false);
860}
861
862static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
863{
864 UNUSED(stepCtx);
866
867 if (!buttonGenericCallback(event, &pos)) {
868 return;
869 }
870
871 displayInfoPage(pos);
872}
873
874static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
875{
876 UNUSED(stepCtx);
878
879 if (!buttonGenericCallback(event, &pos)) {
880 return;
881 }
882
883 displayHomePage(pos);
884}
885
886static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
887{
888 UNUSED(stepCtx);
890
891 if (!buttonGenericCallback(event, &pos)) {
892 return;
893 }
894
895 displayChoicePage(pos);
896}
897
898static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
899{
900 UNUSED(stepCtx);
902
903 if (!buttonGenericCallback(event, &pos)) {
904 return;
905 }
906
907 displayConfirm(pos);
908}
909
910static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
911{
912 UNUSED(stepCtx);
913 // any button press should dismiss the status screen
914 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
915 || (event == BUTTON_RIGHT_PRESSED)) {
916 if (context.stepCallback != NULL) {
917 context.stepCallback();
918 }
919 }
920}
921
922static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
923{
924 UNUSED(stepCtx);
926
927 if (!buttonGenericCallback(event, &pos)) {
928 return;
929 }
930
931 displayContent(pos, false);
932}
933
934// callback used for timeout
935static void statusTickerCallback(void)
936{
937 if (context.stepCallback != NULL) {
938 context.stepCallback();
939 }
940}
941
942// this is the callback used when navigating in extension pages
943static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
944{
946 UNUSED(stepCtx);
947
948 if (event == BUTTON_LEFT_PRESSED) {
949 // only decrement page if we are not at the first page
950 if (context.review.currentExtensionPage > 0) {
951 context.review.currentExtensionPage--;
952 }
953 pos = BACKWARD_DIRECTION;
954 }
955 else if (event == BUTTON_RIGHT_PRESSED) {
956 // only increment page if not at last page
957 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
958 context.review.currentExtensionPage++;
959 }
960 pos = FORWARD_DIRECTION;
961 }
962 else if (event == BUTTON_BOTH_PRESSED) {
963 // if at last page, leave modal context
964 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
965 nbgl_stepRelease(context.review.extensionStepCtx);
967 nbgl_refresh();
968 }
969 return;
970 }
971 else {
972 return;
973 }
974 displayExtensionStep(pos);
975}
976
977// function used to display the extension pages
978static void displayExtensionStep(nbgl_stepPosition_t pos)
979{
980 nbgl_layoutCenteredInfo_t info = {0};
981 const nbgl_contentTagValueList_t *tagValueList = NULL;
982 const nbgl_contentInfoList_t *infoList = NULL;
983 const char *text = NULL;
984 const char *subText = NULL;
985
986 if (context.review.extensionStepCtx != NULL) {
987 nbgl_stepRelease(context.review.extensionStepCtx);
988 }
989 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
990 if (context.review.currentExtensionPage == 0) {
991 pos |= FIRST_STEP;
992 }
993 else {
995 }
996
997 switch (context.review.extension->aliasType) {
998 case ENS_ALIAS:
999 text = context.review.extension->title;
1000 subText = context.review.extension->fullValue;
1001 break;
1002 case INFO_LIST_ALIAS:
1003 infoList = context.review.extension->infolist;
1004 text = PIC(infoList->infoTypes[context.review.currentExtensionPage]);
1005 subText = PIC(infoList->infoContents[context.review.currentExtensionPage]);
1006 break;
1008 tagValueList = context.review.extension->tagValuelist;
1009 text = PIC(tagValueList->pairs[context.review.currentExtensionPage].item);
1010 subText = PIC(tagValueList->pairs[context.review.currentExtensionPage].value);
1011 break;
1012 default:
1013 break;
1014 }
1015 if (text != NULL) {
1016 context.review.extensionStepCtx = nbgl_stepDrawText(
1017 pos, extensionNavigate, NULL, text, subText, BOLD_TEXT1_INFO, true);
1018 }
1019 }
1020 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
1021 // draw the back page
1022 info.icon = &C_icon_back_x;
1023 info.text1 = "Back";
1024 info.style = BOLD_TEXT1_INFO;
1025 pos |= LAST_STEP;
1026 context.review.extensionStepCtx
1027 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
1028 }
1029 nbgl_refresh();
1030}
1031
1032static void displayAliasFullValue(void)
1033{
1034 const char *text = NULL;
1035 const char *subText = NULL;
1036 const nbgl_icon_details_t *icon;
1037 bool isCenteredInfo;
1038
1039 getPairData(context.review.tagValueList,
1040 context.review.currentTagValueIndex,
1041 &text,
1042 &subText,
1043 &context.review.extension,
1044 &icon,
1045 &isCenteredInfo);
1046 if (context.review.extension == NULL) {
1047 // probably an error
1049 "displayAliasFullValue: extension nor found for pair %d\n",
1050 context.review.currentTagValueIndex);
1051 return;
1052 }
1053 context.review.currentExtensionPage = 0;
1054 context.review.extensionStepCtx = NULL;
1055 // create a modal flow to display this extension
1056 switch (context.review.extension->aliasType) {
1057 case ENS_ALIAS:
1058 context.review.nbExtensionPages = 2;
1059 break;
1060 case INFO_LIST_ALIAS:
1061 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
1062 break;
1064 context.review.nbExtensionPages = context.review.extension->tagValuelist->nbPairs + 1;
1065 break;
1066 default:
1068 "displayAliasFullValue: unsupported alias type %d\n",
1069 context.review.extension->aliasType);
1070 return;
1071 }
1072 displayExtensionStep(FORWARD_DIRECTION);
1073}
1074
1075static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1076{
1077 if (approve) {
1078 // Approve page
1079 *icon = &C_icon_validate_14;
1080 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1081 *text = "Confirm";
1082 }
1083 else {
1084 // if finish title is provided, use it
1085 if (context.review.finishTitle != NULL) {
1086 *text = context.review.finishTitle;
1087 }
1088 else {
1089 switch (context.operationType & REAL_TYPE_MASK) {
1090 case TYPE_TRANSACTION:
1091 if (context.operationType & RISKY_OPERATION) {
1092 *text = "Accept risk and sign transaction";
1093 }
1094 else {
1095 *text = "Sign transaction";
1096 }
1097 break;
1098 case TYPE_MESSAGE:
1099 if (context.operationType & RISKY_OPERATION) {
1100 *text = "Accept risk and sign message";
1101 }
1102 else {
1103 *text = "Sign message";
1104 }
1105 break;
1106 default:
1107 if (context.operationType & RISKY_OPERATION) {
1108 *text = "Accept risk and sign operation";
1109 }
1110 else {
1111 *text = "Sign operation";
1112 }
1113 break;
1114 }
1115 }
1116 }
1117 context.stepCallback = onReviewAccept;
1118 }
1119 else {
1120 // Reject page
1121 *icon = &C_icon_crossmark;
1122 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1123 *text = "Cancel";
1124 }
1125 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1126 *text = "Reject transaction";
1127 }
1128 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1129 *text = "Reject message";
1130 }
1131 else {
1132 *text = "Reject operation";
1133 }
1134 context.stepCallback = onReviewReject;
1135 }
1136}
1137
1138// function used to display the current page in review
1139static void displayReviewPage(nbgl_stepPosition_t pos)
1140{
1141 uint8_t reviewPages = 0;
1142 uint8_t finalPages = 0;
1143 uint8_t pairIndex = 0;
1144 const char *text = NULL;
1145 const char *subText = NULL;
1146 const nbgl_icon_details_t *icon = NULL;
1147 uint8_t currentIndex = 0;
1148 uint8_t titleIndex = 255;
1149 uint8_t subIndex = 255;
1150 uint8_t approveIndex = 255;
1151 uint8_t rejectIndex = 255;
1152 const nbgl_contentValueExt_t *extension = NULL;
1153 ForcedType_t forcedType = NO_FORCED_TYPE;
1154
1155 context.stepCallback = NULL;
1156
1157 // Determine the 1st page to display tag/values
1158 // Title page to display
1159 titleIndex = currentIndex++;
1160 reviewPages++;
1161 if (context.review.reviewSubTitle) {
1162 // subtitle page to display
1163 subIndex = currentIndex++;
1164 reviewPages++;
1165 }
1166 approveIndex = context.nbPages - 2;
1167 rejectIndex = context.nbPages - 1;
1168 finalPages = approveIndex;
1169
1170 // Determine which page to display
1171 if (context.currentPage >= finalPages) {
1172 if (context.currentPage == approveIndex) {
1173 // Approve page
1174 getLastPageInfo(true, &icon, &text);
1175 }
1176 else if (context.currentPage == rejectIndex) {
1177 // Reject page
1178 getLastPageInfo(false, &icon, &text);
1179 }
1180 }
1181 else if (context.currentPage < reviewPages) {
1182 if (context.currentPage == titleIndex) {
1183 // Title page
1184 icon = context.review.icon;
1185 text = context.review.reviewTitle;
1186 }
1187 else if (context.currentPage == subIndex) {
1188 // SubTitle page
1189 text = context.review.reviewSubTitle;
1190 }
1191 }
1192 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1193 // address confirmation and 2nd page
1194 text = "Address";
1195 subText = context.review.address;
1196 }
1197 else {
1198 // if there is a skip, and we are not already displaying the "skip" page
1199 // and we are not at the first tag/value of the first set of data (except if going
1200 // backward) then display the "skip" page
1201 if ((context.operationType & SKIPPABLE_OPERATION) && (context.review.skipDisplay == false)
1202 && ((context.currentPage > reviewPages)
1203 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1204 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1205 nbgl_layoutCenteredInfo_t info = {0};
1206 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1207 directions |= LAST_STEP;
1208 }
1209 info.icon = &C_Information_circle_14px;
1210 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1211 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1212 nbgl_refresh();
1213 context.review.skipDisplay = true;
1214 context.firstPairPage = reviewPages;
1215 return;
1216 }
1217 context.review.skipDisplay = false;
1218 bool isCenteredInfo = false;
1219 pairIndex = context.currentPage - reviewPages;
1220 if (context.review.address != NULL) {
1221 pairIndex--;
1222 }
1223 getPairData(context.review.tagValueList,
1224 pairIndex,
1225 &text,
1226 &subText,
1227 &extension,
1228 &icon,
1229 &isCenteredInfo);
1230 if (extension != NULL) {
1231 context.stepCallback = displayAliasFullValue;
1232 context.review.currentTagValueIndex = pairIndex;
1233 forcedType = FORCE_BUTTON;
1234 }
1235 else {
1236 if (isCenteredInfo) {
1237 forcedType = FORCE_CENTERED_INFO;
1238 }
1239 }
1240 }
1241
1242 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1243 nbgl_refresh();
1244}
1245
1246// function used to display the current page in review
1247static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1248{
1249 const char *text = NULL;
1250 const char *subText = NULL;
1251 const nbgl_icon_details_t *icon = NULL;
1252 uint8_t reviewPages = 0;
1253 uint8_t titleIndex = 255;
1254 uint8_t subIndex = 255;
1255 const nbgl_contentValueExt_t *extension = NULL;
1256 ForcedType_t forcedType = NO_FORCED_TYPE;
1257
1258 context.stepCallback = NULL;
1259 switch (context.type) {
1260 case STREAMING_START_REVIEW_USE_CASE:
1261 // Title page to display
1262 titleIndex = reviewPages++;
1263 if (context.review.reviewSubTitle) {
1264 // subtitle page to display
1265 subIndex = reviewPages++;
1266 }
1267 // Determine which page to display
1268 if (context.currentPage >= reviewPages) {
1269 onReviewAccept();
1270 return;
1271 }
1272 // header page(s)
1273 if (context.currentPage == titleIndex) {
1274 // title page
1275 icon = context.review.icon;
1276 text = context.review.reviewTitle;
1277 }
1278 else if (context.currentPage == subIndex) {
1279 // subtitle page
1280 text = context.review.reviewSubTitle;
1281 }
1282 break;
1283
1284 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1285 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1286 onReviewAccept();
1287 return;
1288 }
1289 // if there is a skip, and we are not already displaying the "skip" page
1290 // and we are not at the first tag/value of the first set of data (except if going
1291 // backward) then display the "skip" page
1292 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1293 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1294 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1295 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1296 nbgl_layoutCenteredInfo_t info = {0};
1297 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1298 directions |= LAST_STEP;
1299 }
1300 info.icon = &C_Information_circle_14px;
1301 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1302 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1303 nbgl_refresh();
1304 context.review.skipDisplay = true;
1305 return;
1306 }
1307 context.review.skipDisplay = false;
1308 bool isCenteredInfo = false;
1309 getPairData(context.review.tagValueList,
1310 context.currentPage,
1311 &text,
1312 &subText,
1313 &extension,
1314 &icon,
1315 &isCenteredInfo);
1316 if (extension != NULL) {
1317 forcedType = FORCE_BUTTON;
1318 }
1319 else {
1320 if (isCenteredInfo) {
1321 forcedType = FORCE_CENTERED_INFO;
1322 }
1323 }
1324 break;
1325
1326 case STREAMING_FINISH_REVIEW_USE_CASE:
1327 default:
1328 if (context.currentPage == 0) {
1329 // accept page
1330 getLastPageInfo(true, &icon, &text);
1331 }
1332 else {
1333 // reject page
1334 getLastPageInfo(false, &icon, &text);
1335 }
1336 break;
1337 }
1338
1339 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1340 nbgl_refresh();
1341}
1342
1343// function used to display the current page in info
1344static void displayInfoPage(nbgl_stepPosition_t pos)
1345{
1346 const char *text = NULL;
1347 const char *subText = NULL;
1348 const nbgl_icon_details_t *icon = NULL;
1349
1350 context.stepCallback = NULL;
1351
1352 if (context.currentPage < (context.nbPages - 1)) {
1353 text = PIC(
1354 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1355 subText = PIC(
1356 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1357 }
1358 else {
1359 icon = &C_icon_back_x;
1360 text = "Back";
1361 context.stepCallback = startUseCaseHome;
1362 }
1363
1364 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1365 nbgl_refresh();
1366}
1367
1368// function used to get the current page content
1369static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1370{
1371 uint8_t elemIdx = 0;
1372 const nbgl_content_t *p_content = NULL;
1373 nbgl_content_t content = {0};
1374 nbgl_contentSwitch_t *contentSwitch = NULL;
1375#ifdef WITH_HORIZONTAL_CHOICES_LIST
1376 nbgl_contentRadioChoice_t *contentChoices = NULL;
1377 char **names = NULL;
1378#endif
1379#ifdef WITH_HORIZONTAL_BARS_LIST
1380 nbgl_contentBarsList_t *contentBars = NULL;
1381 char **texts = NULL;
1382#endif
1383 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1384 if (p_content == NULL) {
1385 return;
1386 }
1387 switch (p_content->type) {
1388 case CENTERED_INFO:
1389 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1390 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1391 break;
1392 case INFO_BUTTON:
1393 contentPage->icon = PIC(p_content->content.infoButton.icon);
1394 contentPage->text = PIC(p_content->content.infoButton.text);
1395 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1396 break;
1397 case TAG_VALUE_LIST:
1398 getPairData(&p_content->content.tagValueList,
1399 elemIdx,
1400 &contentPage->text,
1401 &contentPage->subText,
1402 &contentPage->extension,
1403 &contentPage->icon,
1404 &contentPage->isCenteredInfo);
1405 break;
1406 case TAG_VALUE_CONFIRM:
1407 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
1408 getPairData(&p_content->content.tagValueConfirm.tagValueList,
1409 elemIdx,
1410 &contentPage->text,
1411 &contentPage->subText,
1412 &contentPage->extension,
1413 &contentPage->icon,
1414 &contentPage->isCenteredInfo);
1415 }
1416 else {
1417 contentPage->text = p_content->content.tagValueConfirm.confirmationText;
1418 contentPage->icon = &C_icon_validate_14;
1419 contentPage->isAction = true;
1420 }
1421 break;
1422 case SWITCHES_LIST:
1423 contentPage->isSwitch = true;
1424 contentSwitch = &(
1425 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1426 contentPage->text = contentSwitch->text;
1427 contentPage->state = contentSwitch->initState;
1428 if (toogle_state) {
1429 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1430 }
1431 context.stepCallback = onSwitchAction;
1432 contentPage->subText = contentSwitch->subText;
1433 break;
1434 case INFOS_LIST:
1435 contentPage->text
1436 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1437 contentPage->subText
1438 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1439 break;
1440 case CHOICES_LIST:
1441#ifdef WITH_HORIZONTAL_CHOICES_LIST
1442 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1443 names = (char **) PIC(contentChoices->names);
1444 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1445 contentPage->text = PIC(context.content.title);
1446 contentPage->subText = (const char *) PIC(names[elemIdx]);
1447 }
1448 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1449 contentPage->text = PIC(context.home.appName);
1450 contentPage->subText = (const char *) PIC(names[elemIdx]);
1451 }
1452 else {
1453 contentPage->text = (const char *) PIC(names[elemIdx]);
1454 }
1455#endif
1456 break;
1457 case BARS_LIST:
1458#ifdef WITH_HORIZONTAL_BARS_LIST
1459 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1460 texts = (char **) PIC(contentBars->barTexts);
1461 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1462 contentPage->text = PIC(context.content.title);
1463 contentPage->subText = PIC(texts[elemIdx]);
1464 }
1465 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1466 contentPage->text = PIC(context.home.appName);
1467 contentPage->subText = PIC(texts[elemIdx]);
1468 }
1469 else {
1470 contentPage->text = PIC(texts[elemIdx]);
1471 }
1472#endif
1473 break;
1474 default:
1475 break;
1476 }
1477}
1478
1479// function used to display the current page in settings
1480static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1481{
1482 PageContent_t contentPage = {0};
1483
1484 context.stepCallback = NULL;
1485
1486 if (context.currentPage < (context.nbPages - 1)) {
1487 getContentPage(toogle_state, &contentPage);
1488 }
1489 else { // last page is for quit
1490 contentPage.icon = &C_icon_back_x;
1491 contentPage.text = "Back";
1492 if (context.type == GENERIC_SETTINGS) {
1493 context.stepCallback = context.home.quitCallback;
1494 }
1495 else {
1496 context.stepCallback = startUseCaseHome;
1497 }
1498 }
1499
1500 if (contentPage.isSwitch) {
1501 drawSwitchStep(
1502 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1503 }
1504 else {
1505 drawStep(pos,
1506 contentPage.icon,
1507 contentPage.text,
1508 contentPage.subText,
1509 settingsCallback,
1510 false,
1511 NO_FORCED_TYPE);
1512 }
1513
1514 nbgl_refresh();
1515}
1516
1517static void startUseCaseHome(void)
1518{
1519 switch (context.type) {
1520 case SETTINGS_USE_CASE:
1521 // Settings page index
1522 context.currentPage = 1;
1523 if (context.home.homeAction) {
1524 // Action page is before Settings page
1525 context.currentPage++;
1526 }
1527 break;
1528 case INFO_USE_CASE:
1529 // Info page index
1530 context.currentPage = 1;
1531 if (context.home.homeAction) {
1532 // Action page is before Settings and Info pages
1533 context.currentPage++;
1534 }
1535 if (context.home.settingContents) {
1536 // Settings page is before Info pages
1537 context.currentPage++;
1538 }
1539 break;
1540 default:
1541 // Home page index
1542 context.currentPage = 0;
1543 break;
1544 }
1545
1546 context.type = HOME_USE_CASE;
1547 context.nbPages = 2; // Home + Quit
1548 if (context.home.settingContents) {
1549 context.nbPages++;
1550 }
1551 if (context.home.infosList) {
1552 context.nbPages++;
1553 }
1554 if (context.home.homeAction) {
1555 context.nbPages++;
1556 }
1557 displayHomePage(FORWARD_DIRECTION);
1558}
1559
1560static void startUseCaseInfo(void)
1561{
1562 context.type = INFO_USE_CASE;
1563 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1564 context.currentPage = 0;
1565
1566 displayInfoPage(FORWARD_DIRECTION);
1567}
1568
1569static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1570{
1571 nbgl_content_t content = {0};
1572 const nbgl_content_t *p_content = NULL;
1573
1574 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1575 if (context.type != GENERIC_SETTINGS) {
1576 context.type = SETTINGS_USE_CASE;
1577 }
1578
1579 context.nbPages = 1; // For back screen
1580 for (uint i = 0; i < context.home.settingContents->nbContents; i++) {
1581 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1582 context.nbPages += getContentNbElement(p_content);
1583 }
1584 context.currentPage = initSettingPage;
1585
1586 displaySettingsPage(FORWARD_DIRECTION, false);
1587}
1588
1589static void startUseCaseSettings(void)
1590{
1591 startUseCaseSettingsAtPage(0);
1592}
1593
1594static void startUseCaseContent(void)
1595{
1596 uint8_t contentIdx = 0;
1597 const nbgl_content_t *p_content = NULL;
1598 nbgl_content_t content = {0};
1599
1600 context.nbPages = 1; // Quit
1601
1602 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1603 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1604 context.nbPages += getContentNbElement(p_content);
1605 }
1606
1607 // Ensure currentPage is valid
1608 if (context.currentPage >= context.nbPages) {
1609 return;
1610 }
1611
1612 displayContent(FORWARD_DIRECTION, false);
1613}
1614
1615// function used to display the current page in home
1616static void displayHomePage(nbgl_stepPosition_t pos)
1617{
1618 const char *text = NULL;
1619 const char *subText = NULL;
1620 const nbgl_icon_details_t *icon = NULL;
1621 uint8_t currentIndex = 0;
1622 uint8_t homeIndex = 255;
1623 uint8_t actionIndex = 255;
1624 uint8_t settingsIndex = 255;
1625 uint8_t infoIndex = 255;
1626
1627 context.stepCallback = NULL;
1628
1629 // Determine which pages are present
1630 homeIndex = currentIndex++;
1631 if (context.home.homeAction) {
1632 actionIndex = currentIndex++;
1633 }
1634 if (context.home.settingContents) {
1635 settingsIndex = currentIndex++;
1636 }
1637 if (context.home.infosList) {
1638 infoIndex = currentIndex++;
1639 }
1640
1641 if (context.currentPage == homeIndex) {
1642 // Home page
1643 icon = context.home.appIcon;
1644 if (context.home.tagline != NULL) {
1645 text = context.home.tagline;
1646 }
1647 else {
1648 text = context.home.appName;
1649 subText = "app is ready";
1650 }
1651 }
1652 else if (context.currentPage == actionIndex) {
1653 // Action page
1654 icon = context.home.homeAction->icon;
1655 text = PIC(context.home.homeAction->text);
1656 context.stepCallback = context.home.homeAction->callback;
1657 }
1658 else if (context.currentPage == settingsIndex) {
1659 // Settings page
1660 icon = &C_icon_coggle;
1661 text = "App settings";
1662 context.stepCallback = startUseCaseSettings;
1663 }
1664 else if (context.currentPage == infoIndex) {
1665 // About page
1666 icon = &C_Information_circle_14px;
1667 text = "App info";
1668 context.stepCallback = startUseCaseInfo;
1669 }
1670 else {
1671 icon = &C_Quit_14px;
1672 text = "Quit app";
1673 context.stepCallback = context.home.quitCallback;
1674 }
1675
1676 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1677 nbgl_refresh();
1678}
1679
1680// function used to display the current page in choice
1681static void displayChoicePage(nbgl_stepPosition_t pos)
1682{
1683 const char *text = NULL;
1684 const char *subText = NULL;
1685 const nbgl_icon_details_t *icon = NULL;
1686 // set to 1 if there is only one page for intro (if either icon or subMessage is NULL)
1687 uint8_t acceptPage = 0;
1688
1689 if (context.choice.message != NULL) {
1690 if ((context.choice.icon == NULL) || (context.choice.subMessage == NULL)) {
1691 acceptPage = 1;
1692 }
1693 else {
1694 acceptPage = 2;
1695 }
1696 }
1697 context.stepCallback = NULL;
1698
1699 if (context.currentPage < acceptPage) {
1700 if (context.currentPage == 0) { // title page
1701 text = context.choice.message;
1702 if (context.choice.icon != NULL) {
1703 icon = context.choice.icon;
1704 }
1705 else {
1706 subText = context.choice.subMessage;
1707 }
1708 }
1709 else if ((acceptPage == 2) && (context.currentPage == 1)) { // sub-title page
1710 // displayed only if there is both icon and submessage
1711 text = context.choice.message;
1712 subText = context.choice.subMessage;
1713 }
1714 }
1715 else if (context.currentPage == acceptPage) { // confirm page
1716 icon = &C_icon_validate_14;
1717 text = context.choice.confirmText;
1718 context.stepCallback = onChoiceAccept;
1719 }
1720 else if (context.currentPage == (acceptPage + 1)) { // cancel page
1721 icon = &C_icon_crossmark;
1722 text = context.choice.cancelText;
1723 context.stepCallback = onChoiceReject;
1724 }
1725 else if (context.choice.details != NULL) {
1726 // only the first level of details and BAR_LIST type are supported
1727 if (context.choice.details->type == BAR_LIST_WARNING) {
1728 text = context.choice.details->barList.texts[context.currentPage - (acceptPage + 2)];
1729 subText
1730 = context.choice.details->barList.subTexts[context.currentPage - (acceptPage + 2)];
1731 }
1732 }
1733
1734 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1735 nbgl_refresh();
1736}
1737
1738// function used to display the Confirm page
1739static void displayConfirm(nbgl_stepPosition_t pos)
1740{
1741 const char *text = NULL;
1742 const char *subText = NULL;
1743 const nbgl_icon_details_t *icon = NULL;
1744
1745 context.stepCallback = NULL;
1746 switch (context.currentPage) {
1747 case 0:
1748 // title page
1749 text = context.confirm.message;
1750 subText = context.confirm.subMessage;
1751 break;
1752 case 1:
1753 // confirm page
1754 icon = &C_icon_validate_14;
1755 text = context.confirm.confirmText;
1756 context.stepCallback = onConfirmAccept;
1757 break;
1758 case 2:
1759 // cancel page
1760 icon = &C_icon_crossmark;
1761 text = context.confirm.cancelText;
1762 context.stepCallback = onConfirmReject;
1763 break;
1764 }
1765
1766 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1767 nbgl_refresh();
1768}
1769
1770// function used to display the current navigable content
1771static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1772{
1773 PageContent_t contentPage = {0};
1774 ForcedType_t forcedType = NO_FORCED_TYPE;
1775
1776 context.stepCallback = NULL;
1777
1778 if (context.currentPage < (context.nbPages - 1)) {
1779 getContentPage(toogle_state, &contentPage);
1780 if (contentPage.isCenteredInfo) {
1781 forcedType = FORCE_CENTERED_INFO;
1782 }
1783 context.forceAction = contentPage.isAction;
1784 }
1785 else { // last page is for quit
1786 if (context.content.rejectText) {
1787 contentPage.text = context.content.rejectText;
1788 }
1789 else {
1790 contentPage.text = "Back";
1791 }
1792 if (context.type == GENERIC_REVIEW_USE_CASE) {
1793 contentPage.icon = &C_icon_crossmark;
1794 }
1795 else {
1796 contentPage.icon = &C_icon_back_x;
1797 }
1798 context.stepCallback = context.content.quitCallback;
1799 }
1800
1801 if (contentPage.isSwitch) {
1802 drawSwitchStep(
1803 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1804 }
1805 else {
1806 drawStep(pos,
1807 contentPage.icon,
1808 contentPage.text,
1809 contentPage.subText,
1810 contentCallback,
1811 false,
1812 forcedType);
1813 }
1814 context.forceAction = false;
1815 nbgl_refresh();
1816}
1817
1818static void displaySpinner(const char *text)
1819{
1820 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1821 nbgl_refresh();
1822}
1823
1824// function to factorize code for all simple reviews
1825static void useCaseReview(ContextType_t type,
1826 nbgl_operationType_t operationType,
1827 const nbgl_contentTagValueList_t *tagValueList,
1828 const nbgl_icon_details_t *icon,
1829 const char *reviewTitle,
1830 const char *reviewSubTitle,
1831 const char *finishTitle,
1832 nbgl_choiceCallback_t choiceCallback)
1833{
1834 memset(&context, 0, sizeof(UseCaseContext_t));
1835 context.type = type;
1836 context.operationType = operationType;
1837 context.review.tagValueList = tagValueList;
1838 context.review.reviewTitle = reviewTitle;
1839 context.review.reviewSubTitle = reviewSubTitle;
1840 context.review.finishTitle = finishTitle;
1841 context.review.icon = icon;
1842 context.review.onChoice = choiceCallback;
1843 context.currentPage = 0;
1844 // 1 page for title and 2 pages at the end for accept/reject
1845 context.nbPages = tagValueList->nbPairs + 3;
1846 if (reviewSubTitle) {
1847 context.nbPages++; // 1 page for subtitle page
1848 }
1849
1850 displayReviewPage(FORWARD_DIRECTION);
1851}
1852
1853#ifdef NBGL_KEYPAD
1854static void setPinCodeText(void)
1855{
1856 bool enableValidate = false;
1857 bool enableBackspace = true;
1858
1859 // pin can be validated when min digits is entered
1860 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1861 // backspace is disabled when no digit is entered and back vallback is not provided
1862 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1863 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1864 context.keypad.hidden,
1865 context.keypad.pinLen,
1866 (const char *) context.keypad.pinEntry);
1868 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1869 nbgl_layoutDraw(context.keypad.layoutCtx);
1870 nbgl_refresh();
1871}
1872
1873// called when a key is touched on the keypad
1874static void keypadCallback(char touchedKey)
1875{
1876 switch (touchedKey) {
1877 case BACKSPACE_KEY:
1878 if (context.keypad.pinLen > 0) {
1879 context.keypad.pinLen--;
1880 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1881 }
1882 else if (context.keypad.backCallback != NULL) {
1883 context.keypad.backCallback();
1884 break;
1885 }
1886 setPinCodeText();
1887 break;
1888
1889 case VALIDATE_KEY:
1890 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1891 break;
1892
1893 default:
1894 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1895 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1896 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1897 context.keypad.pinLen++;
1898 }
1899 setPinCodeText();
1900 }
1901 break;
1902 }
1903}
1904#endif // NBGL_KEYPAD
1905
1906#ifdef NBGL_KEYBOARD
1907// Saved keyboard context for suggestion selection
1908// (needed because switching to CONTENT_USE_CASE overwrites the union context)
1909static struct {
1910 const char **buttons;
1911 int firstButtonToken;
1912 uint8_t nbUsedButtons;
1913 nbgl_layoutTouchCallback_t onButtonCallback;
1914 nbgl_callback_t backCallback;
1915} savedKeyboardContext;
1916
1917// Navigation callback to fill the suggestion choices page
1918static bool suggestionNavCallback(uint8_t page, nbgl_pageContent_t *content)
1919{
1920 UNUSED(page);
1921 content->type = CHOICES_LIST;
1922 content->choicesList.names = savedKeyboardContext.buttons;
1923 content->choicesList.token = savedKeyboardContext.firstButtonToken;
1924 content->choicesList.initChoice = 0;
1925 content->choicesList.nbChoices = savedKeyboardContext.nbUsedButtons;
1926 return true;
1927}
1928
1929// Display suggestion selection page
1930static void displaySuggestionSelection(void)
1931{
1932 char title[20] = {0};
1933 // Save keyboard context before it gets overwritten
1934 savedKeyboardContext.buttons = context.keyboard.content.suggestionButtons.buttons;
1935 savedKeyboardContext.firstButtonToken
1936 = context.keyboard.content.suggestionButtons.firstButtonToken;
1937 savedKeyboardContext.nbUsedButtons = context.keyboard.content.suggestionButtons.nbUsedButtons;
1938 savedKeyboardContext.onButtonCallback = context.keyboard.onButtonCallback;
1939 savedKeyboardContext.backCallback = context.keyboard.backCallback;
1940
1941 // Release the keyboard layout
1942 nbgl_layoutRelease(context.keyboard.layoutCtx);
1943 context.keyboard.layoutCtx = NULL;
1944
1945 snprintf(title, sizeof(title), "Select word #%d", context.keyboard.content.number);
1947 0,
1948 1,
1949 savedKeyboardContext.backCallback,
1950 suggestionNavCallback,
1951 savedKeyboardContext.onButtonCallback);
1952}
1953
1954// called when a key is touched on the keyboard
1955static void keyboardCallback(char touchedKey)
1956{
1957 uint32_t mask = 0;
1958 size_t textLen = strlen(context.keyboard.entryBuffer);
1959 PRINTF("[keyboardCallback] touchedKey: '%c'\n", touchedKey);
1960 if (touchedKey == BACKSPACE_KEY) {
1961 if (textLen == 0) {
1962 // Used to exit the keyboard when backspace is pressed on an empty entry
1963 context.keyboard.backCallback();
1964 return;
1965 }
1966 context.keyboard.entryBuffer[--textLen] = '\0';
1967 }
1968 else if (touchedKey == VALIDATE_KEY) {
1969 context.keyboard.actionCallback();
1970 return;
1971 }
1972 else {
1973 context.keyboard.entryBuffer[textLen] = touchedKey;
1974 context.keyboard.entryBuffer[++textLen] = '\0';
1975 }
1976 // Set the keyMask to disable some keys
1977 if (context.keyboard.content.type == KEYBOARD_WITH_SUGGESTIONS) {
1978 // if suggestions are displayed, we update them at each key press
1979 context.keyboard.getSuggestButtons(&context.keyboard.content, &mask);
1980 if ((context.keyboard.content.suggestionButtons.nbUsedButtons > 0)
1981 && (context.keyboard.content.suggestionButtons.nbUsedButtons
1982 < NB_MAX_SUGGESTION_BUTTONS)) {
1983 // On Nano, when we have few suggestions, display a selection page
1984 // instead of continuing with keyboard entry
1985 displaySuggestionSelection();
1986 return; // Don't update keyboard, we're in suggestion mode
1987 }
1988 }
1989 else if (textLen >= context.keyboard.entryMaxLen) {
1990 // entry length can't be greater, so we mask every characters
1991 mask = -1;
1992 }
1993 nbgl_layoutUpdateKeyboard(context.keyboard.layoutCtx, context.keyboard.keyboardIndex, mask);
1995 context.keyboard.layoutCtx, context.keyboard.textIndex, context.keyboard.entryBuffer);
1996 nbgl_refresh();
1997}
1998#endif
1999
2000// this is the function called to start the actual review, from the initial warning pages
2001static void launchReviewAfterWarning(void)
2002{
2003 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
2004 useCaseReview(reviewWithWarnCtx.type,
2005 reviewWithWarnCtx.operationType,
2006 reviewWithWarnCtx.tagValueList,
2007 reviewWithWarnCtx.icon,
2008 reviewWithWarnCtx.reviewTitle,
2009 reviewWithWarnCtx.reviewSubTitle,
2010 reviewWithWarnCtx.finishTitle,
2011 reviewWithWarnCtx.choiceCallback);
2012 }
2013 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
2014 displayStreamingReviewPage(FORWARD_DIRECTION);
2015 }
2016}
2017
2018// this is the callback used when navigating in warning pages
2019static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
2020{
2021 UNUSED(stepCtx);
2022
2023 if (event == BUTTON_LEFT_PRESSED) {
2024 // only decrement page if we are not at the first page
2025 if (reviewWithWarnCtx.warningPage > 0) {
2026 reviewWithWarnCtx.warningPage--;
2027 }
2028 }
2029 else if (event == BUTTON_RIGHT_PRESSED) {
2030 // only increment page if not at last page
2031 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
2032 reviewWithWarnCtx.warningPage++;
2033 }
2034 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2035 && (reviewWithWarnCtx.warning->info != NULL)) {
2036 launchReviewAfterWarning();
2037 return;
2038 }
2039 }
2040 else if ((event == BUTTON_BOTH_PRESSED)
2041 && (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
2042 // if at first page, double press leads to start of review
2043 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2044 launchReviewAfterWarning();
2045 }
2046 // if at last page, reject operation
2047 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2048 reviewWithWarnCtx.choiceCallback(false);
2049 }
2050 return;
2051 }
2052 else {
2053 return;
2054 }
2055 displayWarningStep();
2056}
2057
2058// function used to display the initial warning pages when starting a "review with warning"
2059static void displayWarningStep(void)
2060{
2061 nbgl_layoutCenteredInfo_t info = {0};
2062 nbgl_stepPosition_t pos = 0;
2063 if ((reviewWithWarnCtx.warning->prelude) && (reviewWithWarnCtx.warningPage == 0)) {
2064 // for prelude, only draw text as a single step
2066 warningNavigate,
2067 NULL,
2068 reviewWithWarnCtx.warning->prelude->title,
2069 reviewWithWarnCtx.warning->prelude->description,
2070 REGULAR_INFO,
2071 false);
2072 nbgl_refresh();
2073 return;
2074 }
2075 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2076 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2077 // draw the main warning page
2078 info.icon = &C_icon_warning;
2079 info.text1 = "Blind signing ahead";
2080 info.text2 = "To accept risk, press both buttons";
2081 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2083 pos |= FORWARD_DIRECTION;
2084 }
2085 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2086 getLastPageInfo(false, &info.icon, &info.text1);
2088 }
2089 }
2090 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2091 && (reviewWithWarnCtx.warning->info != NULL)) {
2092 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2093 info.icon = reviewWithWarnCtx.warning->info->icon;
2094 info.text1 = reviewWithWarnCtx.warning->info->title;
2095 info.text2 = reviewWithWarnCtx.warning->info->description;
2096 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2098 pos |= FORWARD_DIRECTION;
2099 }
2100 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2101 if (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING) {
2102 info.icon = reviewWithWarnCtx.warning->introDetails->centeredInfo.icon;
2103 info.text1 = reviewWithWarnCtx.warning->introDetails->centeredInfo.title;
2104 info.text2 = reviewWithWarnCtx.warning->introDetails->centeredInfo.description;
2106 }
2107 else {
2108 // not supported
2109 return;
2110 }
2111 }
2112 }
2113 else {
2114 // not supported
2115 return;
2116 }
2117 info.style = BOLD_TEXT1_INFO;
2118 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
2119 nbgl_refresh();
2120}
2121
2122// function used to display the initial warning page when starting a "review with warning"
2123static void displayInitialWarning(void)
2124{
2125 // draw the main warning page
2126 reviewWithWarnCtx.warningPage = 0;
2127 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2128 || ((reviewWithWarnCtx.warning->introDetails)
2129 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2130 reviewWithWarnCtx.nbWarningPages = 2;
2131 }
2132 else {
2133 // if no intro details and not Blind Signing warning, only one page
2134 reviewWithWarnCtx.nbWarningPages = 1;
2135 }
2136
2137 reviewWithWarnCtx.firstWarningPage = 0;
2138 displayWarningStep();
2139}
2140
2141// function used to display the prelude page when starting a "review with warning"
2142static void displayPrelude(void)
2143{
2144 // draw the main warning page
2145 reviewWithWarnCtx.warningPage = 0;
2146 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2147 || ((reviewWithWarnCtx.warning->introDetails)
2148 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2149 reviewWithWarnCtx.nbWarningPages = 3;
2150 }
2151 else {
2152 // if no intro details and not Blind Signing warning, only 2 pages
2153 reviewWithWarnCtx.nbWarningPages = 2;
2154 }
2155 reviewWithWarnCtx.firstWarningPage = 1;
2156 displayWarningStep();
2157}
2158
2159/**********************
2160 * GLOBAL FUNCTIONS
2161 **********************/
2162
2174uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs,
2175 const nbgl_contentTagValueList_t *tagValueList,
2176 uint8_t startIndex,
2177 bool *requireSpecificDisplay)
2178{
2179 UNUSED(nbPairs);
2180 UNUSED(tagValueList);
2181 UNUSED(startIndex);
2182 *requireSpecificDisplay = true;
2183 return 1;
2184}
2185
2198uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs,
2199 const nbgl_contentTagValueList_t *tagValueList,
2200 uint8_t startIndex,
2201 bool isSkippable,
2202 bool *requireSpecificDisplay)
2203{
2204 UNUSED(nbPairs);
2205 UNUSED(tagValueList);
2206 UNUSED(startIndex);
2207 UNUSED(isSkippable);
2208 *requireSpecificDisplay = true;
2209 return 1;
2210}
2211
2220uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos,
2221 const nbgl_contentInfoList_t *infosList,
2222 uint8_t startIndex,
2223 bool withNav)
2224{
2225 UNUSED(nbInfos);
2226 UNUSED(infosList);
2227 UNUSED(startIndex);
2228 UNUSED(withNav);
2229 return 1;
2230}
2231
2240uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches,
2241 const nbgl_contentSwitchesList_t *switchesList,
2242 uint8_t startIndex,
2243 bool withNav)
2244{
2245 UNUSED(nbSwitches);
2246 UNUSED(switchesList);
2247 UNUSED(startIndex);
2248 UNUSED(withNav);
2249 return 1;
2250}
2251
2260uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars,
2261 const nbgl_contentBarsList_t *barsList,
2262 uint8_t startIndex,
2263 bool withNav)
2264{
2265 UNUSED(nbBars);
2266 UNUSED(barsList);
2267 UNUSED(startIndex);
2268 UNUSED(withNav);
2269 return 1;
2270}
2271
2280uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices,
2281 const nbgl_contentRadioChoice_t *choicesList,
2282 uint8_t startIndex,
2283 bool withNav)
2284{
2285 UNUSED(nbChoices);
2286 UNUSED(choicesList);
2287 UNUSED(startIndex);
2288 UNUSED(withNav);
2289 return 1;
2290}
2291
2299{
2300 uint8_t nbPages = 0;
2301 uint8_t nbPairs = tagValueList->nbPairs;
2302 uint8_t nbPairsInPage;
2303 uint8_t i = 0;
2304 bool flag;
2305
2306 while (i < tagValueList->nbPairs) {
2307 // upper margin
2308 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2309 i += nbPairsInPage;
2310 nbPairs -= nbPairsInPage;
2311 nbPages++;
2312 }
2313 return nbPages;
2314}
2315
2329void nbgl_useCaseNavigableContent(const char *title,
2330 uint8_t initPage,
2331 uint8_t nbPages,
2332 nbgl_callback_t quitCallback,
2333 nbgl_navCallback_t navCallback,
2334 nbgl_layoutTouchCallback_t controlsCallback)
2335{
2336 memset(&context, 0, sizeof(UseCaseContext_t));
2337 context.type = CONTENT_USE_CASE;
2338 context.currentPage = initPage;
2339 context.content.title = title;
2340 context.content.quitCallback = quitCallback;
2341 context.content.navCallback = navCallback;
2342 context.content.controlsCallback = controlsCallback;
2343 context.content.genericContents.callbackCallNeeded = true;
2344 context.content.genericContents.nbContents = nbPages;
2345
2346 startUseCaseContent();
2347}
2348
2363void nbgl_useCaseHomeAndSettings(const char *appName,
2364 const nbgl_icon_details_t *appIcon,
2365 const char *tagline,
2366 const uint8_t initSettingPage,
2367 const nbgl_genericContents_t *settingContents,
2368 const nbgl_contentInfoList_t *infosList,
2369 const nbgl_homeAction_t *action,
2370 nbgl_callback_t quitCallback)
2371{
2372 memset(&context, 0, sizeof(UseCaseContext_t));
2373 context.home.appName = appName;
2374 context.home.appIcon = appIcon;
2375 context.home.tagline = tagline;
2376 context.home.settingContents = PIC(settingContents);
2377 context.home.infosList = PIC(infosList);
2378 context.home.homeAction = action;
2379 context.home.quitCallback = quitCallback;
2380
2381 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2382 startUseCaseSettingsAtPage(initSettingPage);
2383 }
2384 else {
2385 startUseCaseHome();
2386 }
2387}
2388
2401void nbgl_useCaseGenericSettings(const char *appName,
2402 uint8_t initPage,
2403 const nbgl_genericContents_t *settingContents,
2404 const nbgl_contentInfoList_t *infosList,
2405 nbgl_callback_t quitCallback)
2406{
2407 memset(&context, 0, sizeof(UseCaseContext_t));
2408 context.type = GENERIC_SETTINGS;
2409 context.home.appName = appName;
2410 context.home.settingContents = PIC(settingContents);
2411 context.home.infosList = PIC(infosList);
2412 context.home.quitCallback = quitCallback;
2413
2414 startUseCaseSettingsAtPage(initPage);
2415}
2416
2428void nbgl_useCaseGenericConfiguration(const char *title,
2429 uint8_t initPage,
2430 const nbgl_genericContents_t *contents,
2431 nbgl_callback_t quitCallback)
2432{
2433 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2434}
2435
2450void nbgl_useCaseReview(nbgl_operationType_t operationType,
2451 const nbgl_contentTagValueList_t *tagValueList,
2452 const nbgl_icon_details_t *icon,
2453 const char *reviewTitle,
2454 const char *reviewSubTitle,
2455 const char *finishTitle,
2456 nbgl_choiceCallback_t choiceCallback)
2457{
2458 useCaseReview(REVIEW_USE_CASE,
2459 operationType,
2460 tagValueList,
2461 icon,
2462 reviewTitle,
2463 reviewSubTitle,
2464 finishTitle,
2465 choiceCallback);
2466}
2467
2491 const nbgl_contentTagValueList_t *tagValueList,
2492 const nbgl_icon_details_t *icon,
2493 const char *reviewTitle,
2494 const char *reviewSubTitle,
2495 const char *finishTitle,
2496 const nbgl_tipBox_t *tipBox,
2497 const nbgl_warning_t *warning,
2498 nbgl_choiceCallback_t choiceCallback)
2499{
2500 UNUSED(tipBox);
2501 ContextType_t type = REVIEW_USE_CASE;
2502
2503 // if no warning at all, it's a simple review
2504 if ((warning == NULL)
2505 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2506 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2507 useCaseReview(type,
2508 operationType,
2509 tagValueList,
2510 icon,
2511 reviewTitle,
2512 reviewSubTitle,
2513 finishTitle,
2514 choiceCallback);
2515 return;
2516 }
2517 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2518 operationType |= NO_THREAT_OPERATION;
2519 }
2520 else if (warning->predefinedSet != 0) {
2521 operationType |= RISKY_OPERATION;
2522 }
2523
2524 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2525 reviewWithWarnCtx.type = type;
2526 reviewWithWarnCtx.operationType = operationType;
2527 reviewWithWarnCtx.tagValueList = tagValueList;
2528 reviewWithWarnCtx.icon = icon;
2529 reviewWithWarnCtx.reviewTitle = reviewTitle;
2530 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2531 reviewWithWarnCtx.finishTitle = finishTitle;
2532 reviewWithWarnCtx.warning = warning;
2533 reviewWithWarnCtx.choiceCallback = choiceCallback;
2534
2535 // if the warning contains a prelude, display it first
2536 if (reviewWithWarnCtx.warning->prelude) {
2537 displayPrelude();
2538 }
2539 // display the initial warning only of a risk/threat or blind signing or prelude
2540 else if (HAS_INITIAL_WARNING(warning)) {
2541 displayInitialWarning();
2542 }
2543 else {
2544 useCaseReview(type,
2545 operationType,
2546 tagValueList,
2547 icon,
2548 reviewTitle,
2549 reviewSubTitle,
2550 finishTitle,
2551 choiceCallback);
2552 }
2553}
2554
2575 const nbgl_contentTagValueList_t *tagValueList,
2576 const nbgl_icon_details_t *icon,
2577 const char *reviewTitle,
2578 const char *reviewSubTitle,
2579 const char *finishTitle,
2580 const nbgl_tipBox_t *dummy,
2581 nbgl_choiceCallback_t choiceCallback)
2582{
2583 nbgl_useCaseAdvancedReview(operationType,
2584 tagValueList,
2585 icon,
2586 reviewTitle,
2587 reviewSubTitle,
2588 finishTitle,
2589 dummy,
2590 &blindSigningWarning,
2591 choiceCallback);
2592}
2593
2609 const nbgl_contentTagValueList_t *tagValueList,
2610 const nbgl_icon_details_t *icon,
2611 const char *reviewTitle,
2612 const char *reviewSubTitle,
2613 const char *finishTitle,
2614 nbgl_choiceCallback_t choiceCallback)
2615{
2616 nbgl_useCaseReview(operationType,
2617 tagValueList,
2618 icon,
2619 reviewTitle,
2620 reviewSubTitle,
2621 finishTitle,
2622 choiceCallback);
2623}
2624
2641void nbgl_useCaseAddressReview(const char *address,
2642 const nbgl_contentTagValueList_t *additionalTagValueList,
2643 const nbgl_icon_details_t *icon,
2644 const char *reviewTitle,
2645 const char *reviewSubTitle,
2646 nbgl_choiceCallback_t choiceCallback)
2647{
2648 memset(&context, 0, sizeof(UseCaseContext_t));
2649 context.type = ADDRESS_REVIEW_USE_CASE;
2650 context.review.address = address;
2651 context.review.reviewTitle = reviewTitle;
2652 context.review.reviewSubTitle = reviewSubTitle;
2653 context.review.icon = icon;
2654 context.review.onChoice = choiceCallback;
2655 context.currentPage = 0;
2656 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2657 // + 1 if sub Title
2658 context.nbPages = reviewSubTitle ? 5 : 4;
2659 if (additionalTagValueList) {
2660 context.review.tagValueList = PIC(additionalTagValueList);
2661 context.nbPages += additionalTagValueList->nbPairs;
2662 }
2663
2664 displayReviewPage(FORWARD_DIRECTION);
2665}
2666
2676 const char *rejectText,
2677 nbgl_callback_t rejectCallback)
2678{
2679 memset(&context, 0, sizeof(UseCaseContext_t));
2680 context.type = GENERIC_REVIEW_USE_CASE;
2681 context.content.rejectText = rejectText;
2682 context.content.quitCallback = rejectCallback;
2683 context.content.genericContents.nbContents = contents->nbContents;
2684 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2685 if (contents->callbackCallNeeded) {
2686 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2687 }
2688 else {
2689 context.content.genericContents.contentsList = PIC(contents->contentsList);
2690 }
2691
2692 startUseCaseContent();
2693}
2694
2703void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2704{
2705 UNUSED(isSuccess);
2706 memset(&context, 0, sizeof(UseCaseContext_t));
2707 context.type = STATUS_USE_CASE;
2708 context.stepCallback = quitCallback;
2709 context.currentPage = 0;
2710 context.nbPages = 1;
2711
2713 NULL,
2714 message,
2715 NULL,
2716 statusButtonCallback,
2717 false,
2718 NO_FORCED_TYPE);
2719}
2720
2728 nbgl_callback_t quitCallback)
2729{
2730 const char *msg;
2731 bool isSuccess;
2732 switch (reviewStatusType) {
2734 msg = "Operation signed";
2735 isSuccess = true;
2736 break;
2738 msg = "Operation rejected";
2739 isSuccess = false;
2740 break;
2742 msg = "Transaction signed";
2743 isSuccess = true;
2744 break;
2746 msg = "Transaction rejected";
2747 isSuccess = false;
2748 break;
2750 msg = "Message signed";
2751 isSuccess = true;
2752 break;
2754 msg = "Message rejected";
2755 isSuccess = false;
2756 break;
2758 msg = "Address verified";
2759 isSuccess = true;
2760 break;
2762 msg = "Address verification cancelled";
2763 isSuccess = false;
2764 break;
2765 default:
2766 return;
2767 }
2768 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2769}
2770
2784 const nbgl_icon_details_t *icon,
2785 const char *reviewTitle,
2786 const char *reviewSubTitle,
2787 nbgl_choiceCallback_t choiceCallback)
2788{
2789 // memorize streaming operation type for future API calls
2790 streamingOpType = operationType;
2791
2792 memset(&context, 0, sizeof(UseCaseContext_t));
2793 context.type = STREAMING_START_REVIEW_USE_CASE;
2794 context.operationType = operationType;
2795 context.review.reviewTitle = reviewTitle;
2796 context.review.reviewSubTitle = reviewSubTitle;
2797 context.review.icon = icon;
2798 context.review.onChoice = choiceCallback;
2799 context.currentPage = 0;
2800 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2801
2802 displayStreamingReviewPage(FORWARD_DIRECTION);
2803}
2804
2819 const nbgl_icon_details_t *icon,
2820 const char *reviewTitle,
2821 const char *reviewSubTitle,
2822 nbgl_choiceCallback_t choiceCallback)
2823{
2825 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2826}
2827
2844 const nbgl_icon_details_t *icon,
2845 const char *reviewTitle,
2846 const char *reviewSubTitle,
2847 const nbgl_warning_t *warning,
2848 nbgl_choiceCallback_t choiceCallback)
2849{
2850 memset(&context, 0, sizeof(UseCaseContext_t));
2851 context.type = STREAMING_START_REVIEW_USE_CASE;
2852 context.operationType = operationType;
2853 context.review.reviewTitle = reviewTitle;
2854 context.review.reviewSubTitle = reviewSubTitle;
2855 context.review.icon = icon;
2856 context.review.onChoice = choiceCallback;
2857 context.currentPage = 0;
2858 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2859
2860 // memorize streaming operation type for future API calls
2861 streamingOpType = operationType;
2862
2863 // if no warning at all, it's a simple review
2864 if ((warning == NULL)
2865 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2866 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2867 displayStreamingReviewPage(FORWARD_DIRECTION);
2868 return;
2869 }
2870 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2871 operationType |= NO_THREAT_OPERATION;
2872 }
2873 else if (warning->predefinedSet != 0) {
2874 operationType |= RISKY_OPERATION;
2875 }
2876 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2877
2878 reviewWithWarnCtx.type = context.type;
2879 reviewWithWarnCtx.operationType = operationType;
2880 reviewWithWarnCtx.icon = icon;
2881 reviewWithWarnCtx.reviewTitle = reviewTitle;
2882 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2883 reviewWithWarnCtx.choiceCallback = choiceCallback;
2884 reviewWithWarnCtx.warning = warning;
2885
2886 // if the warning contains a prelude, display it first
2887 if (reviewWithWarnCtx.warning->prelude) {
2888 displayPrelude();
2889 }
2890 // display the initial warning only of a risk/threat or blind signing or prelude
2891 else if (HAS_INITIAL_WARNING(warning)) {
2892 displayInitialWarning();
2893 }
2894 else {
2895 displayStreamingReviewPage(FORWARD_DIRECTION);
2896 }
2897}
2898
2913 nbgl_choiceCallback_t choiceCallback,
2914 nbgl_callback_t skipCallback)
2915{
2916 uint8_t curNbDataSets = context.review.nbDataSets;
2917
2918 memset(&context, 0, sizeof(UseCaseContext_t));
2919 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
2920 context.operationType = streamingOpType;
2921 context.review.tagValueList = tagValueList;
2922 context.review.onChoice = choiceCallback;
2923 context.currentPage = 0;
2924 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
2925 context.review.skipCallback = skipCallback;
2926 context.review.nbDataSets = curNbDataSets + 1;
2927
2928 displayStreamingReviewPage(FORWARD_DIRECTION);
2929}
2930
2942 nbgl_choiceCallback_t choiceCallback)
2943{
2944 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
2945}
2946
2947void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
2948 nbgl_choiceCallback_t choiceCallback)
2949{
2950 memset(&context, 0, sizeof(UseCaseContext_t));
2951 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
2952 context.operationType = streamingOpType;
2953 context.review.onChoice = choiceCallback;
2954 context.review.finishTitle = finishTitle;
2955 context.currentPage = 0;
2956 context.nbPages = 2; // 2 pages at the end for accept/reject
2957
2958 displayStreamingReviewPage(FORWARD_DIRECTION);
2959}
2960
2966void nbgl_useCaseSpinner(const char *text)
2967{
2968 memset(&context, 0, sizeof(UseCaseContext_t));
2969 context.type = SPINNER_USE_CASE;
2970 context.currentPage = 0;
2971 context.nbPages = 1;
2972
2973 displaySpinner(text);
2974}
2975
2989 const char *message,
2990 const char *subMessage,
2991 const char *confirmText,
2992 const char *cancelText,
2993 nbgl_choiceCallback_t callback)
2994{
2996 icon, message, subMessage, confirmText, cancelText, NULL, callback);
2997};
2998
3014 const char *message,
3015 const char *subMessage,
3016 const char *confirmText,
3017 const char *cancelText,
3018 nbgl_genericDetails_t *details,
3019 nbgl_choiceCallback_t callback)
3020{
3021 memset(&context, 0, sizeof(UseCaseContext_t));
3022 context.type = CHOICE_USE_CASE;
3023 context.choice.icon = icon;
3024 context.choice.message = message;
3025 context.choice.subMessage = subMessage;
3026 context.choice.confirmText = confirmText;
3027 context.choice.cancelText = cancelText;
3028 context.choice.onChoice = callback;
3029 context.choice.details = details;
3030 context.currentPage = 0;
3031 context.nbPages = 2; // 2 pages for confirm/cancel
3032 if (message != NULL) {
3033 context.nbPages++;
3034 // if both icon and subMessage are non NULL, add a page
3035 if ((icon != NULL) && (subMessage != NULL)) {
3036 context.nbPages++;
3037 }
3038 }
3039 if (details != NULL) {
3040 // only the first level of details and BAR_LIST type are supported
3041 if (details->type == BAR_LIST_WARNING) {
3042 context.nbPages += details->barList.nbBars;
3043 }
3044 }
3045
3046 displayChoicePage(FORWARD_DIRECTION);
3047};
3048
3062void nbgl_useCaseConfirm(const char *message,
3063 const char *subMessage,
3064 const char *confirmText,
3065 const char *cancelText,
3066 nbgl_callback_t callback)
3067{
3068 memset(&context, 0, sizeof(UseCaseContext_t));
3069 context.type = CONFIRM_USE_CASE;
3070 context.confirm.message = message;
3071 context.confirm.subMessage = subMessage;
3072 context.confirm.confirmText = confirmText;
3073 context.confirm.cancelText = cancelText;
3074 context.confirm.onConfirm = callback;
3075 context.currentPage = 0;
3076 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
3077
3078 displayConfirm(FORWARD_DIRECTION);
3079}
3080
3091 const char *message,
3092 const char *actionText,
3093 nbgl_callback_t callback)
3094{
3095 nbgl_layoutCenteredInfo_t centeredInfo = {0};
3096
3097 UNUSED(actionText);
3098
3099 // memorize callback in context
3100 memset(&context, 0, sizeof(UseCaseContext_t));
3101 context.type = ACTION_USE_CASE;
3102 context.action.actionCallback = callback;
3103
3104 centeredInfo.icon = icon;
3105 centeredInfo.text1 = message;
3106 centeredInfo.style = BOLD_TEXT1_INFO;
3107 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
3108}
3109
3110#ifdef NBGL_KEYPAD
3129void nbgl_useCaseKeypad(const char *title,
3130 uint8_t minDigits,
3131 uint8_t maxDigits,
3132 bool shuffled,
3133 bool hidden,
3134 nbgl_pinValidCallback_t validatePinCallback,
3135 nbgl_callback_t backCallback)
3136{
3137 nbgl_layoutDescription_t layoutDescription = {0};
3138 int status = -1;
3139
3140 // reset the keypad context
3141 memset(&context, 0, sizeof(KeypadContext_t));
3142 context.type = KEYPAD_USE_CASE;
3143 context.currentPage = 0;
3144 context.nbPages = 1;
3145 context.keypad.validatePin = validatePinCallback;
3146 context.keypad.backCallback = backCallback;
3147 context.keypad.pinMinDigits = minDigits;
3148 context.keypad.pinMaxDigits = maxDigits;
3149 context.keypad.hidden = hidden;
3150 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
3151
3152 // add keypad
3153 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
3154 if (status < 0) {
3155 return;
3156 }
3157 context.keypad.keypadIndex = status;
3158 // add digits
3159 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
3160 if (status < 0) {
3161 return;
3162 }
3163
3164 nbgl_layoutDraw(context.keypad.layoutCtx);
3165 if (context.keypad.backCallback != NULL) {
3166 // force backspace to be visible at first digit, to be used as quit
3167 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
3168 }
3169 nbgl_refresh();
3170}
3171#endif // NBGL_KEYPAD
3172
3173#ifdef NBGL_KEYBOARD
3197void nbgl_useCaseKeyboard(const nbgl_keyboardParams_t *params, nbgl_callback_t backCallback)
3198{
3199 // clang-format off
3200 nbgl_layoutDescription_t layoutDescription = {0};
3201 nbgl_layoutCenteredInfo_t centeredInfo = {.text1 = params->title,
3202 .onTop = true};
3204 .indication = LEFT_ARROW | RIGHT_ARROW};
3205 nbgl_layoutKbd_t kbdInfo = {.callback = &keyboardCallback,
3206 .mode = params->mode,
3207 .lettersOnly = params->lettersOnly};
3208 int status = -1;
3209 // clang-format on
3210
3211 // reset the keypad context
3212 memset(&context, 0, sizeof(UseCaseContext_t));
3213 context.type = KEYBOARD_USE_CASE;
3214 context.currentPage = 0;
3215 context.nbPages = 1;
3216 context.keyboard.entryBuffer = PIC(params->entryBuffer);
3217 context.keyboard.entryMaxLen = params->entryMaxLen;
3218 context.keyboard.entryBuffer[0] = '\0';
3219
3220 context.keyboard.content.type = params->type;
3221
3222 // memorize context
3223 context.keyboard.backCallback = PIC(backCallback);
3224
3225 switch (params->type) {
3227 context.keyboard.actionCallback = PIC(params->confirmationParams.onButtonCallback);
3228 break;
3230 // No need for 'Validate' or 'Case switch' buttons
3231 kbdInfo.keyMask = (1 << VALIDATE_INDEX) | (1 << SHIFT_KEY_INDEX);
3232 context.keyboard.getSuggestButtons
3234 context.keyboard.onButtonCallback = PIC(params->suggestionParams.onButtonCallback);
3235 context.keyboard.content.suggestionButtons = (nbgl_layoutSuggestionButtons_t){
3236 .buttons = PIC(params->suggestionParams.buttons),
3237 .firstButtonToken = params->suggestionParams.firstButtonToken,
3238 };
3239 break;
3240 default:
3241 return;
3242 }
3243
3244 // get a layout
3245 context.keyboard.layoutCtx = nbgl_layoutGet(&layoutDescription);
3246
3247 // add description
3248 nbgl_layoutAddCenteredInfo(context.keyboard.layoutCtx, &centeredInfo);
3249
3250 // Add keyboard
3251 status = nbgl_layoutAddKeyboard(context.keyboard.layoutCtx, &kbdInfo);
3252 if (status < 0) {
3253 return;
3254 }
3255 context.keyboard.keyboardIndex = status;
3256
3257 // add empty entered text
3258 status = nbgl_layoutAddEnteredText(context.keyboard.layoutCtx, "", true);
3259 if (status < 0) {
3260 return;
3261 }
3262 context.keyboard.textIndex = status;
3263
3264 nbgl_layoutAddNavigation(context.keyboard.layoutCtx, &navInfo);
3265
3266 nbgl_layoutDraw(context.keyboard.layoutCtx);
3267 nbgl_refresh();
3268}
3269#endif // NBGL_KEYBOARD
3270
3271#endif // HAVE_SE_TOUCH
3272#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.
@ 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
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
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
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