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_helpers.h"
18#include "ux.h"
19
20/*********************
21 * DEFINES
22 *********************/
23#define WITH_HORIZONTAL_CHOICES_LIST
24#define WITH_HORIZONTAL_BARS_LIST
25
31#define RISKY_OPERATION (1 << 6)
32
38#define NO_THREAT_OPERATION (1 << 7)
39
40/**********************
41 * TYPEDEFS
42 **********************/
43
44typedef struct ReviewContext_s {
45 nbgl_choiceCallback_t onChoice;
46 const nbgl_contentTagValueList_t *tagValueList;
47 const nbgl_icon_details_t *icon;
48 const char *reviewTitle;
49 const char *reviewSubTitle;
50 const char *finishTitle;
51 const char *address; // for address confirmation review
52 nbgl_callback_t skipCallback; // callback provided by used
53 uint8_t nbDataSets; // number of sets of data received by StreamingContinue
54 bool skipDisplay; // if set to true, means that we are displaying the skip page
55 uint8_t dataDirection; // used to know whether the skip page is reached from back or forward
56 uint8_t currentTagValueIndex;
57 uint8_t currentExtensionPage;
58 uint8_t nbExtensionPages;
59 const nbgl_contentValueExt_t *extension;
60 nbgl_step_t extensionStepCtx;
61
62} ReviewContext_t;
63
64typedef struct ChoiceContext_s {
65 const nbgl_icon_details_t *icon;
66 const char *message;
67 const char *subMessage;
68 const char *confirmText;
69 const char *cancelText;
70 nbgl_choiceCallback_t onChoice;
71} ChoiceContext_t;
72
73typedef struct ConfirmContext_s {
74 const char *message;
75 const char *subMessage;
76 const char *confirmText;
77 const char *cancelText;
78 nbgl_callback_t onConfirm;
79 nbgl_step_t currentStep;
80} ConfirmContext_t;
81
82typedef struct ContentContext_s {
83 const char *title; // For CHOICES_LIST /BARS_LIST
84 nbgl_genericContents_t genericContents;
85 const char *rejectText;
86 nbgl_layoutTouchCallback_t controlsCallback;
87 nbgl_navCallback_t navCallback;
88 nbgl_callback_t quitCallback;
89} ContentContext_t;
90
91typedef struct HomeContext_s {
92 const char *appName;
93 const nbgl_icon_details_t *appIcon;
94 const char *tagline;
95 const nbgl_genericContents_t *settingContents;
96 const nbgl_contentInfoList_t *infosList;
97 const nbgl_homeAction_t *homeAction;
98 nbgl_callback_t quitCallback;
99} HomeContext_t;
100
101typedef struct ActionContext_s {
102 nbgl_callback_t actionCallback;
103} ActionContext_t;
104
105#ifdef NBGL_KEYPAD
106typedef struct KeypadContext_s {
107 uint8_t pinEntry[8];
108 uint8_t pinLen;
109 uint8_t pinMinDigits;
110 uint8_t pinMaxDigits;
111 nbgl_layout_t *layoutCtx;
112 bool hidden;
113 uint8_t keypadIndex;
114 nbgl_pinValidCallback_t validatePin;
115 nbgl_callback_t backCallback;
116} KeypadContext_t;
117#endif
118
119typedef enum {
120 NONE_USE_CASE,
121 SPINNER_USE_CASE,
122 REVIEW_USE_CASE,
123 GENERIC_REVIEW_USE_CASE,
124 ADDRESS_REVIEW_USE_CASE,
125 STREAMING_START_REVIEW_USE_CASE,
126 STREAMING_CONTINUE_REVIEW_USE_CASE,
127 STREAMING_FINISH_REVIEW_USE_CASE,
128 CHOICE_USE_CASE,
129 STATUS_USE_CASE,
130 CONFIRM_USE_CASE,
131 KEYPAD_USE_CASE,
132 HOME_USE_CASE,
133 INFO_USE_CASE,
134 SETTINGS_USE_CASE,
135 GENERIC_SETTINGS,
136 CONTENT_USE_CASE,
137 ACTION_USE_CASE
138} ContextType_t;
139
140typedef struct UseCaseContext_s {
141 ContextType_t type;
142 nbgl_operationType_t operationType;
143 uint8_t nbPages;
144 int8_t currentPage;
146 stepCallback;
147 union {
148 ReviewContext_t review;
149 ChoiceContext_t choice;
150 ConfirmContext_t confirm;
151 HomeContext_t home;
152 ContentContext_t content;
153#ifdef NBGL_KEYPAD
154 KeypadContext_t keypad;
155#endif
156 ActionContext_t action;
157 };
158} UseCaseContext_t;
159
160typedef struct PageContent_s {
161 bool isSwitch;
162 const char *text;
163 const char *subText;
164 const nbgl_icon_details_t *icon;
165 const nbgl_contentValueExt_t *extension;
166 nbgl_state_t state;
167 bool isCenteredInfo;
168} PageContent_t;
169
170typedef struct ReviewWithWarningContext_s {
171 ContextType_t type;
172 nbgl_operationType_t operationType;
173 const nbgl_contentTagValueList_t *tagValueList;
174 const nbgl_icon_details_t *icon;
175 const char *reviewTitle;
176 const char *reviewSubTitle;
177 const char *finishTitle;
178 const nbgl_warning_t *warning;
179 nbgl_choiceCallback_t choiceCallback;
180 uint8_t securityReportLevel; // level 1 is the first level of menus
181 bool isIntro; // set to true during intro (before actual review)
182 uint8_t warningPage;
183 uint8_t nbWarningPages;
184} ReviewWithWarningContext_t;
185
186typedef enum {
187 NO_FORCED_TYPE = 0,
188 FORCE_BUTTON,
189 FORCE_CENTERED_INFO
190} ForcedType_t;
191
192/**********************
193 * STATIC VARIABLES
194 **********************/
195static UseCaseContext_t context;
196
197static ReviewWithWarningContext_t reviewWithWarnCtx;
198// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
199static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
200
201// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
202nbgl_operationType_t streamingOpType;
203
204/**********************
205 * STATIC FUNCTIONS
206 **********************/
207static void displayReviewPage(nbgl_stepPosition_t pos);
208static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
209static void displayHomePage(nbgl_stepPosition_t pos);
210static void displayInfoPage(nbgl_stepPosition_t pos);
211static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
212static void displayChoicePage(nbgl_stepPosition_t pos);
213static void displayConfirm(nbgl_stepPosition_t pos);
214static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
215static void displaySpinner(const char *text);
216
217static void startUseCaseHome(void);
218static void startUseCaseInfo(void);
219static void startUseCaseSettings(void);
220static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
221static void startUseCaseContent(void);
222
223static void statusTickerCallback(void);
224
225static void displayExtensionStep(nbgl_stepPosition_t pos);
226static void displayWarningStep(void);
227
228// Simple helper to get the number of elements inside a nbgl_content_t
229static uint8_t getContentNbElement(const nbgl_content_t *content)
230{
231 switch (content->type) {
232 case CENTERED_INFO:
233 return 1;
234 case INFO_BUTTON:
235 return 1;
236 case TAG_VALUE_LIST:
237 return content->content.tagValueList.nbPairs;
238 case SWITCHES_LIST:
239 return content->content.switchesList.nbSwitches;
240 case INFOS_LIST:
241 return content->content.infosList.nbInfos;
242 case CHOICES_LIST:
243 return content->content.choicesList.nbChoices;
244 case BARS_LIST:
245 return content->content.barsList.nbBars;
246 default:
247 return 0;
248 }
249}
250
251// Helper to retrieve the content inside a nbgl_genericContents_t using
252// either the contentsList or using the contentGetterCallback
253static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
254 int8_t contentIdx,
255 nbgl_content_t *content)
256{
257 nbgl_pageContent_t pageContent = {0};
258 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
259 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
260 return NULL;
261 }
262
263 if (genericContents->callbackCallNeeded) {
264 if (content == NULL) {
265 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
266 return NULL;
267 }
268 // Retrieve content through callback, but first memset the content.
269 memset(content, 0, sizeof(nbgl_content_t));
270 if (context.content.navCallback) {
271 if (context.content.navCallback(contentIdx, &pageContent) == true) {
272 // Copy the Page Content to the Content variable
273 content->type = pageContent.type;
274 switch (content->type) {
275 case CENTERED_INFO:
276 content->content.centeredInfo = pageContent.centeredInfo;
277 break;
278 case INFO_BUTTON:
279 content->content.infoButton = pageContent.infoButton;
280 break;
281 case TAG_VALUE_LIST:
282 content->content.tagValueList = pageContent.tagValueList;
283 break;
284 case SWITCHES_LIST:
285 content->content.switchesList = pageContent.switchesList;
286 break;
287 case INFOS_LIST:
288 content->content.infosList = pageContent.infosList;
289 break;
290 case CHOICES_LIST:
291 content->content.choicesList = pageContent.choicesList;
292 break;
293 case BARS_LIST:
294 content->content.barsList = pageContent.barsList;
295 break;
296 default:
297 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
298 return NULL;
299 }
300 }
301 else {
302 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
303 return NULL;
304 }
305 }
306 else {
307 genericContents->contentGetterCallback(contentIdx, content);
308 }
309 return content;
310 }
311 else {
312 // Retrieve content through list
313 return PIC(&genericContents->contentsList[contentIdx]);
314 }
315}
316
317// Helper to retrieve the content inside a nbgl_genericContents_t using
318// either the contentsList or using the contentGetterCallback
319static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
320 uint8_t *elemContentIdx,
321 nbgl_content_t *content)
322{
323 const nbgl_genericContents_t *genericContents = NULL;
324 const nbgl_content_t *p_content = NULL;
325 uint8_t nbPages = 0;
326 uint8_t elemNbPages = 0;
327
328 switch (context.type) {
329 case SETTINGS_USE_CASE:
330 case HOME_USE_CASE:
331 case GENERIC_SETTINGS:
332 genericContents = context.home.settingContents;
333 break;
334 case CONTENT_USE_CASE:
335 case GENERIC_REVIEW_USE_CASE:
336 genericContents = &context.content.genericContents;
337 break;
338 default:
339 return NULL;
340 }
341 for (int i = 0; i < genericContents->nbContents; i++) {
342 p_content = getContentAtIdx(genericContents, i, content);
343 elemNbPages = getContentNbElement(p_content);
344 if (nbPages + elemNbPages > elemIdx) {
345 *elemContentIdx = context.currentPage - nbPages;
346 break;
347 }
348 nbPages += elemNbPages;
349 }
350
351 return p_content;
352}
353
354static const char *getChoiceName(uint8_t choiceIndex)
355{
356 uint8_t elemIdx;
357 uint8_t nbValues;
358 const nbgl_content_t *p_content = NULL;
359 nbgl_content_t content = {0};
360 nbgl_contentRadioChoice_t *contentChoices = NULL;
361 nbgl_contentBarsList_t *contentBars = NULL;
362 char **names = NULL;
363
364 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
365 if (p_content == NULL) {
366 return NULL;
367 }
368 switch (p_content->type) {
369 case CHOICES_LIST:
370 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
371 names = (char **) PIC(contentChoices->names);
372 nbValues = contentChoices->nbChoices;
373 break;
374 case BARS_LIST:
375 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
376 names = (char **) PIC(contentBars->barTexts);
377 nbValues = contentBars->nbBars;
378 break;
379 default:
380 // Not supported as vertical MenuList
381 return NULL;
382 }
383 if (choiceIndex >= nbValues) {
384 // Last item is always "Back" button
385 return "Back";
386 }
387 return (const char *) PIC(names[choiceIndex]);
388}
389
390static void onChoiceSelected(uint8_t choiceIndex)
391{
392 uint8_t elemIdx;
393 uint8_t token = 255;
394 const nbgl_content_t *p_content = NULL;
395 nbgl_content_t content = {0};
396 nbgl_contentRadioChoice_t *contentChoices = NULL;
397 nbgl_contentBarsList_t *contentBars = NULL;
398
399 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
400 if (p_content == NULL) {
401 return;
402 }
403 switch (p_content->type) {
404 case CHOICES_LIST:
405 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
406 if (choiceIndex < contentChoices->nbChoices) {
407 token = contentChoices->token;
408 }
409 break;
410 case BARS_LIST:
411 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
412 if (choiceIndex < contentBars->nbBars) {
413 token = contentBars->tokens[choiceIndex];
414 }
415 break;
416 default:
417 // Not supported as vertical MenuList
418 break;
419 }
420 if ((token != 255) && (context.content.controlsCallback != NULL)) {
421 context.content.controlsCallback(token, 0);
422 }
423 else if (context.content.quitCallback != NULL) {
424 context.content.quitCallback();
425 }
426}
427
428static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
429 uint8_t index,
430 const char **item,
431 const char **value,
432 const nbgl_contentValueExt_t **extension,
433 const nbgl_icon_details_t **icon,
434 bool *isCenteredInfo)
435{
436 const nbgl_contentTagValue_t *pair;
437
438 if (tagValueList->pairs != NULL) {
439 pair = PIC(&tagValueList->pairs[index]);
440 }
441 else {
442 pair = PIC(tagValueList->callback(index));
443 }
444 *item = pair->item;
445 *value = pair->value;
446 if (pair->aliasValue) {
447 *extension = pair->extension;
448 }
449 else if (pair->centeredInfo) {
450 *isCenteredInfo = true;
451 *icon = pair->valueIcon;
452 }
453 else {
454 *extension = NULL;
455 }
456}
457
458static void onReviewAccept(void)
459{
460 if (context.review.onChoice) {
461 context.review.onChoice(true);
462 }
463}
464
465static void onReviewReject(void)
466{
467 if (context.review.onChoice) {
468 context.review.onChoice(false);
469 }
470}
471
472static void onChoiceAccept(void)
473{
474 if (context.choice.onChoice) {
475 context.choice.onChoice(true);
476 }
477}
478
479static void onChoiceReject(void)
480{
481 if (context.choice.onChoice) {
482 context.choice.onChoice(false);
483 }
484}
485
486static void onConfirmAccept(void)
487{
488 if (context.confirm.currentStep) {
489 nbgl_stepRelease(context.confirm.currentStep);
490 }
491 if (context.confirm.onConfirm) {
492 context.confirm.onConfirm();
493 }
494}
495
496static void onConfirmReject(void)
497{
498 if (context.confirm.currentStep) {
499 nbgl_stepRelease(context.confirm.currentStep);
501 nbgl_refresh();
502 }
503}
504
505static void onSwitchAction(void)
506{
507 const nbgl_contentSwitch_t *contentSwitch = NULL;
508 const nbgl_content_t *p_content = NULL;
509 nbgl_content_t content = {0};
510 uint8_t elemIdx;
511
512 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
513 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
514 return;
515 }
516 contentSwitch
517 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
518 switch (context.type) {
519 case SETTINGS_USE_CASE:
520 case HOME_USE_CASE:
521 case GENERIC_SETTINGS:
522 displaySettingsPage(FORWARD_DIRECTION, true);
523 break;
524 case CONTENT_USE_CASE:
525 case GENERIC_REVIEW_USE_CASE:
526 displayContent(FORWARD_DIRECTION, true);
527 break;
528 default:
529 break;
530 }
531 if (p_content->contentActionCallback != NULL) {
532 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
533 actionCallback(contentSwitch->token,
534 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
535 context.currentPage);
536 }
537 else if (context.content.controlsCallback != NULL) {
538 context.content.controlsCallback(contentSwitch->token, 0);
539 }
540}
541
542static void drawStep(nbgl_stepPosition_t pos,
543 const nbgl_icon_details_t *icon,
544 const char *txt,
545 const char *subTxt,
546 nbgl_stepButtonCallback_t onActionCallback,
547 bool modal,
548 ForcedType_t forcedType)
549{
550 uint8_t elemIdx;
551 nbgl_step_t newStep = NULL;
552 const nbgl_content_t *p_content = NULL;
553 nbgl_content_t content = {0};
554 nbgl_contentRadioChoice_t *contentChoices = NULL;
555 nbgl_contentBarsList_t *contentBars = NULL;
556 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
557 nbgl_layoutMenuList_t list = {0};
558 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
559 .tickerIntervale = 0, // not periodic
560 .tickerValue = STATUS_SCREEN_DURATION};
561
562 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
563 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
564 // (except the first set) because the set starts with a "skip" page
565 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
566 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
567 pos |= LAST_STEP;
568 }
569 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
570 p_ticker = &ticker;
571 }
572 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
573 nbgl_stepRelease(context.confirm.currentStep);
574 }
575
576 if (txt == NULL) {
577 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
578 if (p_content) {
579 switch (p_content->type) {
580 case CHOICES_LIST:
581 contentChoices
582 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
583 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
584 list.selectedChoice = contentChoices->initChoice;
585 list.callback = getChoiceName;
586 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
587 break;
588 case BARS_LIST:
589 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
590 list.nbChoices = contentBars->nbBars + 1; // For Back button
591 list.selectedChoice = 0;
592 list.callback = getChoiceName;
593 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
594 break;
595 default:
596 // Not supported as vertical MenuList
597 break;
598 }
599 }
600 }
601 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
603 if (subTxt != NULL) {
604 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
605 }
606 else {
607 style = REGULAR_INFO;
608 }
609 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
610 }
611 else {
613 info.icon = icon;
614 info.text1 = txt;
615 info.text2 = subTxt;
616 info.onTop = false;
617 if ((subTxt != NULL) || (context.stepCallback != NULL)) {
618 info.style = BOLD_TEXT1_INFO;
619 }
620 else {
621 info.style = REGULAR_INFO;
622 }
623 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
624 }
625 if (context.type == CONFIRM_USE_CASE) {
626 context.confirm.currentStep = newStep;
627 }
628}
629
630static void drawSwitchStep(nbgl_stepPosition_t pos,
631 const char *title,
632 const char *description,
633 bool state,
634 nbgl_stepButtonCallback_t onActionCallback,
635 bool modal)
636{
637 nbgl_layoutSwitch_t switchInfo;
638
639 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
640 switchInfo.initState = state;
641 switchInfo.text = title;
642 switchInfo.subText = description;
643 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
644}
645
646static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
647{
648 uint8_t elemIdx;
649 uint8_t token = 0;
650 uint8_t index = 0;
651 const nbgl_content_t *p_content = NULL;
652 nbgl_content_t content = {0};
653
654 if (event == BUTTON_LEFT_PRESSED) {
655 if (context.currentPage > 0) {
656 context.currentPage--;
657 }
658 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
659 // the first set
660 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
661 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
662 // Drop the event
663 return false;
664 }
665 *pos = BACKWARD_DIRECTION;
666 }
667 else if (event == BUTTON_RIGHT_PRESSED) {
668 if (context.currentPage < (int) (context.nbPages - 1)) {
669 context.currentPage++;
670 }
671 else {
672 // Drop the event
673 return false;
674 }
675 *pos = FORWARD_DIRECTION;
676 }
677 else {
678 if (event == BUTTON_BOTH_PRESSED) {
679 if (context.stepCallback != NULL) {
680 context.stepCallback();
681 }
682 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
683 || (context.type == GENERIC_SETTINGS)
684 || (context.type == GENERIC_REVIEW_USE_CASE)) {
685 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
686 if (p_content != NULL) {
687 switch (p_content->type) {
688 case CENTERED_INFO:
689 // No associated callback
690 return false;
691 case INFO_BUTTON:
692 token = p_content->content.infoButton.buttonToken;
693 break;
694 case SWITCHES_LIST:
695 token = p_content->content.switchesList.switches->token;
696 break;
697 case BARS_LIST:
698 token = p_content->content.barsList.tokens[context.currentPage];
699 break;
700 case CHOICES_LIST:
701 token = p_content->content.choicesList.token;
702 index = context.currentPage;
703 break;
704 default:
705 break;
706 }
707
708 if ((p_content) && (p_content->contentActionCallback != NULL)) {
709 p_content->contentActionCallback(token, 0, context.currentPage);
710 }
711 else if (context.content.controlsCallback != NULL) {
712 context.content.controlsCallback(token, index);
713 }
714 }
715 }
716 }
717 return false;
718 }
719 return true;
720}
721
722static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
723{
724 UNUSED(stepCtx);
726
727 if (!buttonGenericCallback(event, &pos)) {
728 return;
729 }
730
731 displayReviewPage(pos);
732}
733
734// this is the callback used when button action on the "skip" page
735static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
736{
737 UNUSED(stepCtx);
739
740 if (event == BUTTON_LEFT_PRESSED) {
741 // only decrement page if we are going backward but coming from forward (back & forth)
742 if ((context.review.dataDirection == FORWARD_DIRECTION) && (context.currentPage > 0)) {
743 context.currentPage--;
744 }
745 pos = BACKWARD_DIRECTION;
746 }
747 else if (event == BUTTON_RIGHT_PRESSED) {
748 // only increment page if we are going forward but coming from backward (back & forth)
749 if ((context.review.dataDirection == BACKWARD_DIRECTION)
750 && (context.currentPage < (int) (context.nbPages - 1)) && (context.currentPage > 0)) {
751 context.currentPage++;
752 }
753 pos = FORWARD_DIRECTION;
754 }
755 else if (event == BUTTON_BOTH_PRESSED) {
756 context.review.skipCallback();
757 return;
758 }
759 else {
760 return;
761 }
762 displayStreamingReviewPage(pos);
763}
764
765// this is the callback used when buttons in "Action" use case are pressed
766static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
767{
768 UNUSED(stepCtx);
769
770 if (event == BUTTON_BOTH_PRESSED) {
771 context.action.actionCallback();
772 }
773}
774
775static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
776{
778 UNUSED(stepCtx);
779
780 if (!buttonGenericCallback(event, &pos)) {
781 return;
782 }
783 else {
784 // memorize last direction
785 context.review.dataDirection = pos;
786 }
787
788 displayStreamingReviewPage(pos);
789}
790
791static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
792{
793 UNUSED(stepCtx);
795
796 if (!buttonGenericCallback(event, &pos)) {
797 return;
798 }
799
800 displaySettingsPage(pos, false);
801}
802
803static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
804{
805 UNUSED(stepCtx);
807
808 if (!buttonGenericCallback(event, &pos)) {
809 return;
810 }
811
812 displayInfoPage(pos);
813}
814
815static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
816{
817 UNUSED(stepCtx);
819
820 if (!buttonGenericCallback(event, &pos)) {
821 return;
822 }
823
824 displayHomePage(pos);
825}
826
827static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
828{
829 UNUSED(stepCtx);
831
832 if (!buttonGenericCallback(event, &pos)) {
833 return;
834 }
835
836 displayChoicePage(pos);
837}
838
839static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
840{
841 UNUSED(stepCtx);
843
844 if (!buttonGenericCallback(event, &pos)) {
845 return;
846 }
847
848 displayConfirm(pos);
849}
850
851static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
852{
853 UNUSED(stepCtx);
854 // any button press should dismiss the status screen
855 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
856 || (event == BUTTON_RIGHT_PRESSED)) {
857 if (context.stepCallback != NULL) {
858 context.stepCallback();
859 }
860 }
861}
862
863static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
864{
865 UNUSED(stepCtx);
867
868 if (!buttonGenericCallback(event, &pos)) {
869 return;
870 }
871
872 displayContent(pos, false);
873}
874
875// callback used for timeout
876static void statusTickerCallback(void)
877{
878 if (context.stepCallback != NULL) {
879 context.stepCallback();
880 }
881}
882
883// this is the callback used when navigating in extension pages
884static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
885{
887 UNUSED(stepCtx);
888
889 if (event == BUTTON_LEFT_PRESSED) {
890 // only decrement page if we are not at the first page
891 if (context.review.currentExtensionPage > 0) {
892 context.review.currentExtensionPage--;
893 }
894 pos = BACKWARD_DIRECTION;
895 }
896 else if (event == BUTTON_RIGHT_PRESSED) {
897 // only increment page if not at last page
898 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
899 context.review.currentExtensionPage++;
900 }
901 pos = FORWARD_DIRECTION;
902 }
903 else if (event == BUTTON_BOTH_PRESSED) {
904 // if at last page, leave modal context
905 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
906 nbgl_stepRelease(context.review.extensionStepCtx);
908 nbgl_refresh();
909 }
910 return;
911 }
912 else {
913 return;
914 }
915 displayExtensionStep(pos);
916}
917
918// function used to display the extension pages
919static void displayExtensionStep(nbgl_stepPosition_t pos)
920{
921 nbgl_layoutCenteredInfo_t info = {0};
922
923 if (context.review.extensionStepCtx != NULL) {
924 nbgl_stepRelease(context.review.extensionStepCtx);
925 }
926 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
927 if (context.review.currentExtensionPage == 0) {
928 pos |= FIRST_STEP;
929 }
930 else {
932 }
933
934 if (context.review.extension->aliasType == ENS_ALIAS) {
935 context.review.extensionStepCtx = nbgl_stepDrawText(pos,
936 extensionNavigate,
937 NULL,
938 context.review.extension->title,
939 context.review.extension->fullValue,
940 BOLD_TEXT1_INFO,
941 true);
942 }
943 else if (context.review.extension->aliasType == INFO_LIST_ALIAS) {
944 context.review.extensionStepCtx = nbgl_stepDrawText(
945 pos,
946 extensionNavigate,
947 NULL,
948 context.review.extension->infolist->infoTypes[context.review.currentExtensionPage],
949 context.review.extension->infolist
950 ->infoContents[context.review.currentExtensionPage],
951 BOLD_TEXT1_INFO,
952 true);
953 }
954 }
955 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
956 // draw the back page
957 info.icon = &C_icon_back_x;
958 info.text1 = "Back";
959 info.style = BOLD_TEXT1_INFO;
960 pos |= LAST_STEP;
961 context.review.extensionStepCtx
962 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
963 }
964 nbgl_refresh();
965}
966
967static void displayAliasFullValue(void)
968{
969 const char *text = NULL;
970 const char *subText = NULL;
971 const nbgl_icon_details_t *icon;
972 bool isCenteredInfo;
973
974 getPairData(context.review.tagValueList,
975 context.review.currentTagValueIndex,
976 &text,
977 &subText,
978 &context.review.extension,
979 &icon,
980 &isCenteredInfo);
981 if (context.review.extension == NULL) {
982 // probably an error
984 "displayAliasFullValue: extension nor found for pair %d\n",
985 context.review.currentTagValueIndex);
986 return;
987 }
988 context.review.currentExtensionPage = 0;
989 context.review.extensionStepCtx = NULL;
990 // create a modal flow to display this extension
991 if (context.review.extension->aliasType == ENS_ALIAS) {
992 context.review.nbExtensionPages = 2;
993 }
994 else if (context.review.extension->aliasType == INFO_LIST_ALIAS) {
995 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
996 }
997 else {
999 "displayAliasFullValue: unsupported alias type %d\n",
1000 context.review.extension->aliasType);
1001 return;
1002 }
1003 displayExtensionStep(FORWARD_DIRECTION);
1004}
1005
1006static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1007{
1008 if (approve) {
1009 // Approve page
1010 *icon = &C_icon_validate_14;
1011 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1012 *text = "Confirm";
1013 }
1014 else {
1015 // if finish title is provided, use it
1016 if (context.review.finishTitle != NULL) {
1017 *text = context.review.finishTitle;
1018 }
1019 else {
1020 switch (context.operationType & REAL_TYPE_MASK) {
1021 case TYPE_TRANSACTION:
1022 if (context.operationType & RISKY_OPERATION) {
1023 *text = "Accept risk and sign transaction";
1024 }
1025 else {
1026 *text = "Sign transaction";
1027 }
1028 break;
1029 case TYPE_MESSAGE:
1030 if (context.operationType & RISKY_OPERATION) {
1031 *text = "Accept risk and sign message";
1032 }
1033 else {
1034 *text = "Sign message";
1035 }
1036 break;
1037 default:
1038 if (context.operationType & RISKY_OPERATION) {
1039 *text = "Accept risk and sign operation";
1040 }
1041 else {
1042 *text = "Sign operation";
1043 }
1044 break;
1045 }
1046 }
1047 }
1048 context.stepCallback = onReviewAccept;
1049 }
1050 else {
1051 // Reject page
1052 *icon = &C_icon_crossmark;
1053 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1054 *text = "Cancel";
1055 }
1056 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1057 *text = "Reject transaction";
1058 }
1059 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1060 *text = "Reject message";
1061 }
1062 else {
1063 *text = "Reject operation";
1064 }
1065 context.stepCallback = onReviewReject;
1066 }
1067}
1068
1069// function used to display the current page in review
1070static void displayReviewPage(nbgl_stepPosition_t pos)
1071{
1072 uint8_t reviewPages = 0;
1073 uint8_t finalPages = 0;
1074 uint8_t pairIndex = 0;
1075 const char *text = NULL;
1076 const char *subText = NULL;
1077 const nbgl_icon_details_t *icon = NULL;
1078 uint8_t currentIndex = 0;
1079 uint8_t titleIndex = 255;
1080 uint8_t subIndex = 255;
1081 uint8_t approveIndex = 255;
1082 uint8_t rejectIndex = 255;
1083 const nbgl_contentValueExt_t *extension = NULL;
1084 ForcedType_t forcedType = NO_FORCED_TYPE;
1085
1086 context.stepCallback = NULL;
1087
1088 // Determine the 1st page to display tag/values
1089 // Title page to display
1090 titleIndex = currentIndex++;
1091 reviewPages++;
1092 if (context.review.reviewSubTitle) {
1093 // subtitle page to display
1094 subIndex = currentIndex++;
1095 reviewPages++;
1096 }
1097 approveIndex = context.nbPages - 2;
1098 rejectIndex = context.nbPages - 1;
1099 finalPages = approveIndex;
1100
1101 // Determine which page to display
1102 if (context.currentPage >= finalPages) {
1103 if (context.currentPage == approveIndex) {
1104 // Approve page
1105 getLastPageInfo(true, &icon, &text);
1106 }
1107 else if (context.currentPage == rejectIndex) {
1108 // Reject page
1109 getLastPageInfo(false, &icon, &text);
1110 }
1111 }
1112 else if (context.currentPage < reviewPages) {
1113 if (context.currentPage == titleIndex) {
1114 // Title page
1115 icon = context.review.icon;
1116 text = context.review.reviewTitle;
1117 }
1118 else if (context.currentPage == subIndex) {
1119 // SubTitle page
1120 text = context.review.reviewSubTitle;
1121 }
1122 }
1123 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1124 // address confirmation and 2nd page
1125 text = "Address";
1126 subText = context.review.address;
1127 }
1128 else {
1129 bool isCenteredInfo = false;
1130 pairIndex = context.currentPage - reviewPages;
1131 if (context.review.address != NULL) {
1132 pairIndex--;
1133 }
1134 getPairData(context.review.tagValueList,
1135 pairIndex,
1136 &text,
1137 &subText,
1138 &extension,
1139 &icon,
1140 &isCenteredInfo);
1141 if (extension != NULL) {
1142 context.stepCallback = displayAliasFullValue;
1143 context.review.currentTagValueIndex = pairIndex;
1144 forcedType = FORCE_BUTTON;
1145 }
1146 else {
1147 if (isCenteredInfo) {
1148 forcedType = FORCE_CENTERED_INFO;
1149 }
1150 }
1151 }
1152
1153 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1154 nbgl_refresh();
1155}
1156
1157// function used to display the current page in review
1158static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1159{
1160 const char *text = NULL;
1161 const char *subText = NULL;
1162 const nbgl_icon_details_t *icon = NULL;
1163 uint8_t reviewPages = 0;
1164 uint8_t titleIndex = 255;
1165 uint8_t subIndex = 255;
1166 const nbgl_contentValueExt_t *extension = NULL;
1167 ForcedType_t forcedType = NO_FORCED_TYPE;
1168
1169 context.stepCallback = NULL;
1170 switch (context.type) {
1171 case STREAMING_START_REVIEW_USE_CASE:
1172 // Title page to display
1173 titleIndex = reviewPages++;
1174 if (context.review.reviewSubTitle) {
1175 // subtitle page to display
1176 subIndex = reviewPages++;
1177 }
1178 // Determine which page to display
1179 if (context.currentPage >= reviewPages) {
1180 onReviewAccept();
1181 return;
1182 }
1183 // header page(s)
1184 if (context.currentPage == titleIndex) {
1185 // title page
1186 icon = context.review.icon;
1187 text = context.review.reviewTitle;
1188 }
1189 else if (context.currentPage == subIndex) {
1190 // subtitle page
1191 text = context.review.reviewSubTitle;
1192 }
1193 break;
1194
1195 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1196 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1197 onReviewAccept();
1198 return;
1199 }
1200 // if there is a skip, and we are not already displaying the "skip" page
1201 // and we are not at the first tag/value of the first set of data (except if going
1202 // backward) then display the "skip" page
1203 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1204 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1205 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1206 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1207 nbgl_layoutCenteredInfo_t info = {0};
1208 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1209 directions |= LAST_STEP;
1210 }
1211 info.icon = &C_Information_circle_14px;
1212 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1213 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1214 nbgl_refresh();
1215 context.review.skipDisplay = true;
1216 return;
1217 }
1218 context.review.skipDisplay = false;
1219 bool isCenteredInfo = false;
1220 getPairData(context.review.tagValueList,
1221 context.currentPage,
1222 &text,
1223 &subText,
1224 &extension,
1225 &icon,
1226 &isCenteredInfo);
1227 if (extension != NULL) {
1228 forcedType = FORCE_BUTTON;
1229 }
1230 else {
1231 if (isCenteredInfo) {
1232 forcedType = FORCE_CENTERED_INFO;
1233 }
1234 }
1235 break;
1236
1237 case STREAMING_FINISH_REVIEW_USE_CASE:
1238 default:
1239 if (context.currentPage == 0) {
1240 // accept page
1241 getLastPageInfo(true, &icon, &text);
1242 }
1243 else {
1244 // reject page
1245 getLastPageInfo(false, &icon, &text);
1246 }
1247 break;
1248 }
1249
1250 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1251 nbgl_refresh();
1252}
1253
1254// function used to display the current page in info
1255static void displayInfoPage(nbgl_stepPosition_t pos)
1256{
1257 const char *text = NULL;
1258 const char *subText = NULL;
1259 const nbgl_icon_details_t *icon = NULL;
1260
1261 context.stepCallback = NULL;
1262
1263 if (context.currentPage < (context.nbPages - 1)) {
1264 text = PIC(
1265 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1266 subText = PIC(
1267 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1268 }
1269 else {
1270 icon = &C_icon_back_x;
1271 text = "Back";
1272 context.stepCallback = startUseCaseHome;
1273 }
1274
1275 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1276 nbgl_refresh();
1277}
1278
1279// function used to get the current page content
1280static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1281{
1282 uint8_t elemIdx = 0;
1283 const nbgl_content_t *p_content = NULL;
1284 nbgl_content_t content = {0};
1285 nbgl_contentSwitch_t *contentSwitch = NULL;
1286#ifdef WITH_HORIZONTAL_CHOICES_LIST
1287 nbgl_contentRadioChoice_t *contentChoices = NULL;
1288 char **names = NULL;
1289#endif
1290#ifdef WITH_HORIZONTAL_BARS_LIST
1291 nbgl_contentBarsList_t *contentBars = NULL;
1292 char **texts = NULL;
1293#endif
1294 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1295 if (p_content == NULL) {
1296 return;
1297 }
1298 switch (p_content->type) {
1299 case CENTERED_INFO:
1300 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1301 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1302 break;
1303 case INFO_BUTTON:
1304 contentPage->icon = PIC(p_content->content.infoButton.icon);
1305 contentPage->text = PIC(p_content->content.infoButton.text);
1306 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1307 break;
1308 case TAG_VALUE_LIST:
1309 getPairData(&p_content->content.tagValueList,
1310 elemIdx,
1311 &contentPage->text,
1312 &contentPage->subText,
1313 &contentPage->extension,
1314 &contentPage->icon,
1315 &contentPage->isCenteredInfo);
1316 break;
1317 case SWITCHES_LIST:
1318 contentPage->isSwitch = true;
1319 contentSwitch = &(
1320 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1321 contentPage->text = contentSwitch->text;
1322 contentPage->state = contentSwitch->initState;
1323 if (toogle_state) {
1324 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1325 }
1326 context.stepCallback = onSwitchAction;
1327 contentPage->subText = contentSwitch->subText;
1328 break;
1329 case INFOS_LIST:
1330 contentPage->text
1331 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1332 contentPage->subText
1333 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1334 break;
1335 case CHOICES_LIST:
1336#ifdef WITH_HORIZONTAL_CHOICES_LIST
1337 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1338 names = (char **) PIC(contentChoices->names);
1339 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1340 contentPage->text = PIC(context.content.title);
1341 contentPage->subText = (const char *) PIC(names[elemIdx]);
1342 }
1343 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1344 contentPage->text = PIC(context.home.appName);
1345 contentPage->subText = (const char *) PIC(names[elemIdx]);
1346 }
1347 else {
1348 contentPage->text = (const char *) PIC(names[elemIdx]);
1349 }
1350#endif
1351 break;
1352 case BARS_LIST:
1353#ifdef WITH_HORIZONTAL_BARS_LIST
1354 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1355 texts = (char **) PIC(contentBars->barTexts);
1356 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1357 contentPage->text = PIC(context.content.title);
1358 contentPage->subText = PIC(texts[elemIdx]);
1359 }
1360 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1361 contentPage->text = PIC(context.home.appName);
1362 contentPage->subText = PIC(texts[elemIdx]);
1363 }
1364 else {
1365 contentPage->text = PIC(texts[elemIdx]);
1366 }
1367#endif
1368 break;
1369 default:
1370 break;
1371 }
1372}
1373
1374// function used to display the current page in settings
1375static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1376{
1377 PageContent_t contentPage = {0};
1378
1379 context.stepCallback = NULL;
1380
1381 if (context.currentPage < (context.nbPages - 1)) {
1382 getContentPage(toogle_state, &contentPage);
1383 }
1384 else { // last page is for quit
1385 contentPage.icon = &C_icon_back_x;
1386 contentPage.text = "Back";
1387 if (context.type == GENERIC_SETTINGS) {
1388 context.stepCallback = context.home.quitCallback;
1389 }
1390 else {
1391 context.stepCallback = startUseCaseHome;
1392 }
1393 }
1394
1395 if (contentPage.isSwitch) {
1396 drawSwitchStep(
1397 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1398 }
1399 else {
1400 drawStep(pos,
1401 contentPage.icon,
1402 contentPage.text,
1403 contentPage.subText,
1404 settingsCallback,
1405 false,
1406 NO_FORCED_TYPE);
1407 }
1408
1409 nbgl_refresh();
1410}
1411
1412static void startUseCaseHome(void)
1413{
1414 switch (context.type) {
1415 case SETTINGS_USE_CASE:
1416 // Settings page index
1417 context.currentPage = 1;
1418 if (context.home.homeAction) {
1419 // Action page is before Settings page
1420 context.currentPage++;
1421 }
1422 break;
1423 case INFO_USE_CASE:
1424 // Info page index
1425 context.currentPage = 1;
1426 if (context.home.homeAction) {
1427 // Action page is before Settings and Info pages
1428 context.currentPage++;
1429 }
1430 if (context.home.settingContents) {
1431 // Settings page is before Info pages
1432 context.currentPage++;
1433 }
1434 break;
1435 default:
1436 // Home page index
1437 context.currentPage = 0;
1438 break;
1439 }
1440
1441 context.type = HOME_USE_CASE;
1442 context.nbPages = 2; // Home + Quit
1443 if (context.home.settingContents) {
1444 context.nbPages++;
1445 }
1446 if (context.home.infosList) {
1447 context.nbPages++;
1448 }
1449 if (context.home.homeAction) {
1450 context.nbPages++;
1451 }
1452 displayHomePage(FORWARD_DIRECTION);
1453}
1454
1455static void startUseCaseInfo(void)
1456{
1457 context.type = INFO_USE_CASE;
1458 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1459 context.currentPage = 0;
1460
1461 displayInfoPage(FORWARD_DIRECTION);
1462}
1463
1464static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1465{
1466 nbgl_content_t content = {0};
1467 const nbgl_content_t *p_content = NULL;
1468
1469 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1470 if (context.type != GENERIC_SETTINGS) {
1471 context.type = SETTINGS_USE_CASE;
1472 }
1473
1474 context.nbPages = 1; // For back screen
1475 for (int i = 0; i < context.home.settingContents->nbContents; i++) {
1476 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1477 context.nbPages += getContentNbElement(p_content);
1478 }
1479 context.currentPage = initSettingPage;
1480
1481 displaySettingsPage(FORWARD_DIRECTION, false);
1482}
1483
1484static void startUseCaseSettings(void)
1485{
1486 startUseCaseSettingsAtPage(0);
1487}
1488
1489static void startUseCaseContent(void)
1490{
1491 uint8_t contentIdx = 0;
1492 const nbgl_content_t *p_content = NULL;
1493 nbgl_content_t content = {0};
1494
1495 context.nbPages = 1; // Quit
1496
1497 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1498 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1499 context.nbPages += getContentNbElement(p_content);
1500 }
1501
1502 // Ensure currentPage is valid
1503 if (context.currentPage >= context.nbPages) {
1504 return;
1505 }
1506
1507 displayContent(FORWARD_DIRECTION, false);
1508}
1509
1510// function used to display the current page in home
1511static void displayHomePage(nbgl_stepPosition_t pos)
1512{
1513 const char *text = NULL;
1514 const char *subText = NULL;
1515 const nbgl_icon_details_t *icon = NULL;
1516 uint8_t currentIndex = 0;
1517 uint8_t homeIndex = 255;
1518 uint8_t actionIndex = 255;
1519 uint8_t settingsIndex = 255;
1520 uint8_t infoIndex = 255;
1521
1522 context.stepCallback = NULL;
1523
1524 // Determine which pages are present
1525 homeIndex = currentIndex++;
1526 if (context.home.homeAction) {
1527 actionIndex = currentIndex++;
1528 }
1529 if (context.home.settingContents) {
1530 settingsIndex = currentIndex++;
1531 }
1532 if (context.home.infosList) {
1533 infoIndex = currentIndex++;
1534 }
1535
1536 if (context.currentPage == homeIndex) {
1537 // Home page
1538 icon = context.home.appIcon;
1539 if (context.home.tagline != NULL) {
1540 text = context.home.tagline;
1541 }
1542 else {
1543 text = context.home.appName;
1544 subText = "app is ready";
1545 }
1546 }
1547 else if (context.currentPage == actionIndex) {
1548 // Action page
1549 icon = context.home.homeAction->icon;
1550 text = PIC(context.home.homeAction->text);
1551 context.stepCallback = context.home.homeAction->callback;
1552 }
1553 else if (context.currentPage == settingsIndex) {
1554 // Settings page
1555 icon = &C_icon_coggle;
1556 text = "App settings";
1557 context.stepCallback = startUseCaseSettings;
1558 }
1559 else if (context.currentPage == infoIndex) {
1560 // About page
1561 icon = &C_Information_circle_14px;
1562 text = "App info";
1563 context.stepCallback = startUseCaseInfo;
1564 }
1565 else {
1566 icon = &C_Quit_14px;
1567 text = "Quit app";
1568 context.stepCallback = context.home.quitCallback;
1569 }
1570
1571 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1572 nbgl_refresh();
1573}
1574
1575// function used to display the current page in choice
1576static void displayChoicePage(nbgl_stepPosition_t pos)
1577{
1578 const char *text = NULL;
1579 const char *subText = NULL;
1580 const nbgl_icon_details_t *icon = NULL;
1581
1582 context.stepCallback = NULL;
1583
1584 // Handle case where there is no icon or subMessage
1585 if (context.currentPage == 1
1586 && (context.choice.icon == NULL || context.choice.subMessage == NULL)) {
1587 if (pos & BACKWARD_DIRECTION) {
1588 context.currentPage -= 1;
1589 }
1590 else {
1591 context.currentPage += 1;
1592 }
1593 }
1594
1595 if (context.currentPage == 0) { // title page
1596 text = context.choice.message;
1597 if (context.choice.icon != NULL) {
1598 icon = context.choice.icon;
1599 }
1600 else {
1601 subText = context.choice.subMessage;
1602 }
1603 }
1604 else if (context.currentPage == 1) { // sub-title page
1605 // displayed only if there is both icon and submessage
1606 text = context.choice.message;
1607 subText = context.choice.subMessage;
1608 }
1609 else if (context.currentPage == 2) { // confirm page
1610 icon = &C_icon_validate_14;
1611 text = context.choice.confirmText;
1612 context.stepCallback = onChoiceAccept;
1613 }
1614 else { // cancel page
1615 icon = &C_icon_crossmark;
1616 text = context.choice.cancelText;
1617 context.stepCallback = onChoiceReject;
1618 }
1619
1620 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1621 nbgl_refresh();
1622}
1623
1624// function used to display the Confirm page
1625static void displayConfirm(nbgl_stepPosition_t pos)
1626{
1627 const char *text = NULL;
1628 const char *subText = NULL;
1629 const nbgl_icon_details_t *icon = NULL;
1630
1631 context.stepCallback = NULL;
1632 switch (context.currentPage) {
1633 case 0:
1634 // title page
1635 text = context.confirm.message;
1636 subText = context.confirm.subMessage;
1637 break;
1638 case 1:
1639 // confirm page
1640 icon = &C_icon_validate_14;
1641 text = context.confirm.confirmText;
1642 context.stepCallback = onConfirmAccept;
1643 break;
1644 case 2:
1645 // cancel page
1646 icon = &C_icon_crossmark;
1647 text = context.confirm.cancelText;
1648 context.stepCallback = onConfirmReject;
1649 break;
1650 }
1651
1652 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1653 nbgl_refresh();
1654}
1655
1656// function used to display the current navigable content
1657static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1658{
1659 PageContent_t contentPage = {0};
1660 ForcedType_t forcedType = NO_FORCED_TYPE;
1661
1662 context.stepCallback = NULL;
1663
1664 if (context.currentPage < (context.nbPages - 1)) {
1665 getContentPage(toogle_state, &contentPage);
1666 if (contentPage.isCenteredInfo) {
1667 forcedType = FORCE_CENTERED_INFO;
1668 }
1669 }
1670 else { // last page is for quit
1671 if (context.content.rejectText) {
1672 contentPage.text = context.content.rejectText;
1673 }
1674 else {
1675 contentPage.text = "Back";
1676 }
1677 if (context.type == GENERIC_REVIEW_USE_CASE) {
1678 contentPage.icon = &C_icon_crossmark;
1679 }
1680 else {
1681 contentPage.icon = &C_icon_back_x;
1682 }
1683 context.stepCallback = context.content.quitCallback;
1684 }
1685
1686 if (contentPage.isSwitch) {
1687 drawSwitchStep(
1688 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1689 }
1690 else {
1691 drawStep(pos,
1692 contentPage.icon,
1693 contentPage.text,
1694 contentPage.subText,
1695 contentCallback,
1696 false,
1697 forcedType);
1698 }
1699
1700 nbgl_refresh();
1701}
1702
1703static void displaySpinner(const char *text)
1704{
1705 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1706 nbgl_refresh();
1707}
1708
1709// function to factorize code for all simple reviews
1710static void useCaseReview(ContextType_t type,
1711 nbgl_operationType_t operationType,
1712 const nbgl_contentTagValueList_t *tagValueList,
1713 const nbgl_icon_details_t *icon,
1714 const char *reviewTitle,
1715 const char *reviewSubTitle,
1716 const char *finishTitle,
1717 nbgl_choiceCallback_t choiceCallback)
1718{
1719 memset(&context, 0, sizeof(UseCaseContext_t));
1720 context.type = type;
1721 context.operationType = operationType;
1722 context.review.tagValueList = tagValueList;
1723 context.review.reviewTitle = reviewTitle;
1724 context.review.reviewSubTitle = reviewSubTitle;
1725 context.review.finishTitle = finishTitle;
1726 context.review.icon = icon;
1727 context.review.onChoice = choiceCallback;
1728 context.currentPage = 0;
1729 // 1 page for title and 2 pages at the end for accept/reject
1730 context.nbPages = tagValueList->nbPairs + 3;
1731 if (reviewSubTitle) {
1732 context.nbPages++; // 1 page for subtitle page
1733 }
1734
1735 displayReviewPage(FORWARD_DIRECTION);
1736}
1737
1738#ifdef NBGL_KEYPAD
1739static void setPinCodeText(void)
1740{
1741 bool enableValidate = false;
1742 bool enableBackspace = true;
1743
1744 // pin can be validated when min digits is entered
1745 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1746 // backspace is disabled when no digit is entered and back vallback is not provided
1747 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1748 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1749 context.keypad.hidden,
1750 context.keypad.pinLen,
1751 (const char *) context.keypad.pinEntry);
1753 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1754 nbgl_layoutDraw(context.keypad.layoutCtx);
1755 nbgl_refresh();
1756}
1757
1758// called when a key is touched on the keypad
1759static void keypadCallback(char touchedKey)
1760{
1761 switch (touchedKey) {
1762 case BACKSPACE_KEY:
1763 if (context.keypad.pinLen > 0) {
1764 context.keypad.pinLen--;
1765 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1766 }
1767 else if (context.keypad.backCallback != NULL) {
1768 context.keypad.backCallback();
1769 break;
1770 }
1771 setPinCodeText();
1772 break;
1773
1774 case VALIDATE_KEY:
1775 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1776 break;
1777
1778 default:
1779 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1780 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1781 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1782 context.keypad.pinLen++;
1783 }
1784 setPinCodeText();
1785 }
1786 break;
1787 }
1788}
1789
1790// called to create a keypad, with either hidden or visible digits
1791static void keypadGenericUseCase(const char *title,
1792 uint8_t minDigits,
1793 uint8_t maxDigits,
1794 bool shuffled,
1795 bool hidden,
1796 nbgl_pinValidCallback_t validatePinCallback,
1797 nbgl_callback_t backCallback)
1798{
1799 nbgl_layoutDescription_t layoutDescription = {0};
1800 int status = -1;
1801
1802 // reset the keypad context
1803 memset(&context, 0, sizeof(KeypadContext_t));
1804 context.type = KEYPAD_USE_CASE;
1805 context.currentPage = 0;
1806 context.nbPages = 1;
1807 context.keypad.validatePin = validatePinCallback;
1808 context.keypad.backCallback = backCallback;
1809 context.keypad.pinMinDigits = minDigits;
1810 context.keypad.pinMaxDigits = maxDigits;
1811 context.keypad.hidden = hidden;
1812 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
1813
1814 // add keypad
1815 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
1816 if (status < 0) {
1817 return;
1818 }
1819 context.keypad.keypadIndex = status;
1820 // add digits
1821 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
1822 if (status < 0) {
1823 return;
1824 }
1825
1826 nbgl_layoutDraw(context.keypad.layoutCtx);
1827 if (context.keypad.backCallback != NULL) {
1828 // force backspace to be visible at first digit, to be used as quit
1829 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
1830 }
1831 nbgl_refresh();
1832}
1833#endif // NBGL_KEYPAD
1834
1835// this is the callback used when navigating in warning pages
1836static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
1837{
1838 UNUSED(stepCtx);
1839
1840 if (event == BUTTON_LEFT_PRESSED) {
1841 // only decrement page if we are not at the first page
1842 if (reviewWithWarnCtx.warningPage > 0) {
1843 reviewWithWarnCtx.warningPage--;
1844 }
1845 }
1846 else if (event == BUTTON_RIGHT_PRESSED) {
1847 // only increment page if not at last page
1848 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
1849 reviewWithWarnCtx.warningPage++;
1850 }
1851 }
1852 else if (event == BUTTON_BOTH_PRESSED) {
1853 // if at first page, double press leads to start of review
1854 if (reviewWithWarnCtx.warningPage == 0) {
1855 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
1856 useCaseReview(reviewWithWarnCtx.type,
1857 reviewWithWarnCtx.operationType,
1858 reviewWithWarnCtx.tagValueList,
1859 reviewWithWarnCtx.icon,
1860 reviewWithWarnCtx.reviewTitle,
1861 reviewWithWarnCtx.reviewSubTitle,
1862 reviewWithWarnCtx.finishTitle,
1863 reviewWithWarnCtx.choiceCallback);
1864 }
1865 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
1866 displayStreamingReviewPage(FORWARD_DIRECTION);
1867 }
1868 }
1869 // if at last page, reject operation
1870 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1871 reviewWithWarnCtx.choiceCallback(false);
1872 }
1873 return;
1874 }
1875 else {
1876 return;
1877 }
1878 displayWarningStep();
1879}
1880
1881// function used to display the initial warning page when starting a "review with warning"
1882static void displayWarningStep(void)
1883{
1884 nbgl_layoutCenteredInfo_t info = {0};
1885 nbgl_stepPosition_t pos = 0;
1886 if (reviewWithWarnCtx.warningPage == 0) {
1887 // draw the main warning page
1888 info.icon = &C_icon_warning;
1889 info.text1 = "Blind signing ahead";
1890 info.text2 = "To accept risk, press both buttons";
1892 }
1893 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1894 getLastPageInfo(false, &info.icon, &info.text1);
1896 }
1897 info.style = BOLD_TEXT1_INFO;
1898 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
1899 nbgl_refresh();
1900}
1901
1902// function used to display the initial warning page when starting a "review with warning"
1903static void displayInitialWarning(void)
1904{
1905 // draw the main warning page
1906 reviewWithWarnCtx.warningPage = 0;
1907 reviewWithWarnCtx.nbWarningPages = 2;
1908 displayWarningStep();
1909}
1910
1911/**********************
1912 * GLOBAL FUNCTIONS
1913 **********************/
1914
1927 const nbgl_contentTagValueList_t *tagValueList,
1928 uint8_t startIndex,
1929 bool *requireSpecificDisplay)
1930{
1931 UNUSED(nbPairs);
1932 UNUSED(tagValueList);
1933 UNUSED(startIndex);
1934 *requireSpecificDisplay = true;
1935 return 1;
1936}
1937
1951 const nbgl_contentTagValueList_t *tagValueList,
1952 uint8_t startIndex,
1953 bool isSkippable,
1954 bool *requireSpecificDisplay)
1955{
1956 UNUSED(nbPairs);
1957 UNUSED(tagValueList);
1958 UNUSED(startIndex);
1959 UNUSED(isSkippable);
1960 *requireSpecificDisplay = true;
1961 return 1;
1962}
1963
1973 const nbgl_contentInfoList_t *infosList,
1974 uint8_t startIndex,
1975 bool withNav)
1976{
1977 UNUSED(nbInfos);
1978 UNUSED(infosList);
1979 UNUSED(startIndex);
1980 UNUSED(withNav);
1981 return 1;
1982}
1983
1993 const nbgl_contentSwitchesList_t *switchesList,
1994 uint8_t startIndex,
1995 bool withNav)
1996{
1997 UNUSED(nbSwitches);
1998 UNUSED(switchesList);
1999 UNUSED(startIndex);
2000 UNUSED(withNav);
2001 return 1;
2002}
2003
2013 const nbgl_contentBarsList_t *barsList,
2014 uint8_t startIndex,
2015 bool withNav)
2016{
2017 UNUSED(nbBars);
2018 UNUSED(barsList);
2019 UNUSED(startIndex);
2020 UNUSED(withNav);
2021 return 1;
2022}
2023
2033 const nbgl_contentRadioChoice_t *choicesList,
2034 uint8_t startIndex,
2035 bool withNav)
2036{
2037 UNUSED(nbChoices);
2038 UNUSED(choicesList);
2039 UNUSED(startIndex);
2040 UNUSED(withNav);
2041 return 1;
2042}
2043
2051{
2052 uint8_t nbPages = 0;
2053 uint8_t nbPairs = tagValueList->nbPairs;
2054 uint8_t nbPairsInPage;
2055 uint8_t i = 0;
2056 bool flag;
2057
2058 while (i < tagValueList->nbPairs) {
2059 // upper margin
2060 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2061 i += nbPairsInPage;
2062 nbPairs -= nbPairsInPage;
2063 nbPages++;
2064 }
2065 return nbPages;
2066}
2067
2081void nbgl_useCaseNavigableContent(const char *title,
2082 uint8_t initPage,
2083 uint8_t nbPages,
2084 nbgl_callback_t quitCallback,
2085 nbgl_navCallback_t navCallback,
2086 nbgl_layoutTouchCallback_t controlsCallback)
2087{
2088 memset(&context, 0, sizeof(UseCaseContext_t));
2089 context.type = CONTENT_USE_CASE;
2090 context.currentPage = initPage;
2091 context.content.title = title;
2092 context.content.quitCallback = quitCallback;
2093 context.content.navCallback = navCallback;
2094 context.content.controlsCallback = controlsCallback;
2095 context.content.genericContents.callbackCallNeeded = true;
2096 context.content.genericContents.nbContents = nbPages;
2097
2098 startUseCaseContent();
2099}
2100
2115void nbgl_useCaseHomeAndSettings(const char *appName,
2116 const nbgl_icon_details_t *appIcon,
2117 const char *tagline,
2118 const uint8_t initSettingPage,
2119 const nbgl_genericContents_t *settingContents,
2120 const nbgl_contentInfoList_t *infosList,
2121 const nbgl_homeAction_t *action,
2122 nbgl_callback_t quitCallback)
2123{
2124 memset(&context, 0, sizeof(UseCaseContext_t));
2125 context.home.appName = appName;
2126 context.home.appIcon = appIcon;
2127 context.home.tagline = tagline;
2128 context.home.settingContents = PIC(settingContents);
2129 context.home.infosList = PIC(infosList);
2130 context.home.homeAction = action;
2131 context.home.quitCallback = quitCallback;
2132
2133 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2134 startUseCaseSettingsAtPage(initSettingPage);
2135 }
2136 else {
2137 startUseCaseHome();
2138 }
2139}
2140
2153void nbgl_useCaseGenericSettings(const char *appName,
2154 uint8_t initPage,
2155 const nbgl_genericContents_t *settingContents,
2156 const nbgl_contentInfoList_t *infosList,
2157 nbgl_callback_t quitCallback)
2158{
2159 memset(&context, 0, sizeof(UseCaseContext_t));
2160 context.type = GENERIC_SETTINGS;
2161 context.home.appName = appName;
2162 context.home.settingContents = PIC(settingContents);
2163 context.home.infosList = PIC(infosList);
2164 context.home.quitCallback = quitCallback;
2165
2166 startUseCaseSettingsAtPage(initPage);
2167}
2168
2180void nbgl_useCaseGenericConfiguration(const char *title,
2181 uint8_t initPage,
2182 const nbgl_genericContents_t *contents,
2183 nbgl_callback_t quitCallback)
2184{
2185 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2186}
2187
2202void nbgl_useCaseReview(nbgl_operationType_t operationType,
2203 const nbgl_contentTagValueList_t *tagValueList,
2204 const nbgl_icon_details_t *icon,
2205 const char *reviewTitle,
2206 const char *reviewSubTitle,
2207 const char *finishTitle,
2208 nbgl_choiceCallback_t choiceCallback)
2209{
2210 useCaseReview(REVIEW_USE_CASE,
2211 operationType,
2212 tagValueList,
2213 icon,
2214 reviewTitle,
2215 reviewSubTitle,
2216 finishTitle,
2217 choiceCallback);
2218}
2219
2243 const nbgl_contentTagValueList_t *tagValueList,
2244 const nbgl_icon_details_t *icon,
2245 const char *reviewTitle,
2246 const char *reviewSubTitle,
2247 const char *finishTitle,
2248 const nbgl_tipBox_t *tipBox,
2249 const nbgl_warning_t *warning,
2250 nbgl_choiceCallback_t choiceCallback)
2251{
2252 UNUSED(tipBox);
2253 ContextType_t type = REVIEW_USE_CASE;
2254
2255 // if no warning at all, it's a simple review
2256 if ((warning == NULL)
2257 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
2258 && (warning->reviewDetails == NULL))) {
2259 useCaseReview(type,
2260 operationType,
2261 tagValueList,
2262 icon,
2263 reviewTitle,
2264 reviewSubTitle,
2265 finishTitle,
2266 choiceCallback);
2267 return;
2268 }
2269 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2270 operationType |= NO_THREAT_OPERATION;
2271 }
2272 else {
2273 operationType |= RISKY_OPERATION;
2274 }
2275
2276 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2277 reviewWithWarnCtx.type = type;
2278 reviewWithWarnCtx.operationType = operationType;
2279 reviewWithWarnCtx.tagValueList = tagValueList;
2280 reviewWithWarnCtx.icon = icon;
2281 reviewWithWarnCtx.reviewTitle = reviewTitle;
2282 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2283 reviewWithWarnCtx.finishTitle = finishTitle;
2284 reviewWithWarnCtx.warning = warning;
2285 reviewWithWarnCtx.choiceCallback = choiceCallback;
2286
2287 // display the initial warning only of a risk/threat or blind signing
2288 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
2289 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
2290 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
2291 useCaseReview(type,
2292 operationType,
2293 tagValueList,
2294 icon,
2295 reviewTitle,
2296 reviewSubTitle,
2297 finishTitle,
2298 choiceCallback);
2299 return;
2300 }
2301 displayInitialWarning();
2302}
2303
2324 const nbgl_contentTagValueList_t *tagValueList,
2325 const nbgl_icon_details_t *icon,
2326 const char *reviewTitle,
2327 const char *reviewSubTitle,
2328 const char *finishTitle,
2329 const nbgl_tipBox_t *dummy,
2330 nbgl_choiceCallback_t choiceCallback)
2331{
2332 nbgl_useCaseAdvancedReview(operationType,
2333 tagValueList,
2334 icon,
2335 reviewTitle,
2336 reviewSubTitle,
2337 finishTitle,
2338 dummy,
2339 &blindSigningWarning,
2340 choiceCallback);
2341}
2342
2358 const nbgl_contentTagValueList_t *tagValueList,
2359 const nbgl_icon_details_t *icon,
2360 const char *reviewTitle,
2361 const char *reviewSubTitle,
2362 const char *finishTitle,
2363 nbgl_choiceCallback_t choiceCallback)
2364{
2365 nbgl_useCaseReview(operationType,
2366 tagValueList,
2367 icon,
2368 reviewTitle,
2369 reviewSubTitle,
2370 finishTitle,
2371 choiceCallback);
2372}
2373
2390void nbgl_useCaseAddressReview(const char *address,
2391 const nbgl_contentTagValueList_t *additionalTagValueList,
2392 const nbgl_icon_details_t *icon,
2393 const char *reviewTitle,
2394 const char *reviewSubTitle,
2395 nbgl_choiceCallback_t choiceCallback)
2396{
2397 memset(&context, 0, sizeof(UseCaseContext_t));
2398 context.type = ADDRESS_REVIEW_USE_CASE;
2399 context.review.address = address;
2400 context.review.reviewTitle = reviewTitle;
2401 context.review.reviewSubTitle = reviewSubTitle;
2402 context.review.icon = icon;
2403 context.review.onChoice = choiceCallback;
2404 context.currentPage = 0;
2405 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2406 context.nbPages = 4;
2407 if (additionalTagValueList) {
2408 context.review.tagValueList = PIC(additionalTagValueList);
2409 context.nbPages += additionalTagValueList->nbPairs;
2410 }
2411
2412 displayReviewPage(FORWARD_DIRECTION);
2413}
2414
2424 const char *rejectText,
2425 nbgl_callback_t rejectCallback)
2426{
2427 memset(&context, 0, sizeof(UseCaseContext_t));
2428 context.type = GENERIC_REVIEW_USE_CASE;
2429 context.content.rejectText = rejectText;
2430 context.content.quitCallback = rejectCallback;
2431 context.content.genericContents.nbContents = contents->nbContents;
2432 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2433 if (contents->callbackCallNeeded) {
2434 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2435 }
2436 else {
2437 context.content.genericContents.contentsList = PIC(contents->contentsList);
2438 }
2439
2440 startUseCaseContent();
2441}
2442
2451void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2452{
2453 UNUSED(isSuccess);
2454 memset(&context, 0, sizeof(UseCaseContext_t));
2455 context.type = STATUS_USE_CASE;
2456 context.stepCallback = quitCallback;
2457 context.currentPage = 0;
2458 context.nbPages = 1;
2459
2461 NULL,
2462 message,
2463 NULL,
2464 statusButtonCallback,
2465 false,
2466 NO_FORCED_TYPE);
2467}
2468
2476 nbgl_callback_t quitCallback)
2477{
2478 const char *msg;
2479 bool isSuccess;
2480 switch (reviewStatusType) {
2482 msg = "Operation signed";
2483 isSuccess = true;
2484 break;
2486 msg = "Operation rejected";
2487 isSuccess = false;
2488 break;
2490 msg = "Transaction signed";
2491 isSuccess = true;
2492 break;
2494 msg = "Transaction rejected";
2495 isSuccess = false;
2496 break;
2498 msg = "Message signed";
2499 isSuccess = true;
2500 break;
2502 msg = "Message rejected";
2503 isSuccess = false;
2504 break;
2506 msg = "Address verified";
2507 isSuccess = true;
2508 break;
2510 msg = "Address verification cancelled";
2511 isSuccess = false;
2512 break;
2513 default:
2514 return;
2515 }
2516 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2517}
2518
2532 const nbgl_icon_details_t *icon,
2533 const char *reviewTitle,
2534 const char *reviewSubTitle,
2535 nbgl_choiceCallback_t choiceCallback)
2536{
2537 // memorize streaming operation type for future API calls
2538 streamingOpType = operationType;
2539
2540 memset(&context, 0, sizeof(UseCaseContext_t));
2541 context.type = STREAMING_START_REVIEW_USE_CASE;
2542 context.operationType = operationType;
2543 context.review.reviewTitle = reviewTitle;
2544 context.review.reviewSubTitle = reviewSubTitle;
2545 context.review.icon = icon;
2546 context.review.onChoice = choiceCallback;
2547 context.currentPage = 0;
2548 context.nbPages = 2; // Start page + trick for review continue
2549
2550 displayStreamingReviewPage(FORWARD_DIRECTION);
2551}
2552
2567 const nbgl_icon_details_t *icon,
2568 const char *reviewTitle,
2569 const char *reviewSubTitle,
2570 nbgl_choiceCallback_t choiceCallback)
2571{
2573 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2574}
2575
2592 const nbgl_icon_details_t *icon,
2593 const char *reviewTitle,
2594 const char *reviewSubTitle,
2595 const nbgl_warning_t *warning,
2596 nbgl_choiceCallback_t choiceCallback)
2597{
2598 memset(&context, 0, sizeof(UseCaseContext_t));
2599 context.type = STREAMING_START_REVIEW_USE_CASE;
2600 context.operationType = operationType;
2601 context.review.reviewTitle = reviewTitle;
2602 context.review.reviewSubTitle = reviewSubTitle;
2603 context.review.icon = icon;
2604 context.review.onChoice = choiceCallback;
2605 context.currentPage = 0;
2606 context.nbPages = 2; // Start page + trick for review continue
2607
2608 // memorize streaming operation type for future API calls
2609 streamingOpType = operationType;
2610
2611 // if no warning at all, it's a simple review
2612 if ((warning == NULL)
2613 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
2614 && (warning->reviewDetails == NULL))) {
2615 displayStreamingReviewPage(FORWARD_DIRECTION);
2616 return;
2617 }
2618 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2619 operationType |= NO_THREAT_OPERATION;
2620 }
2621 else {
2622 operationType |= RISKY_OPERATION;
2623 }
2624 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2625
2626 reviewWithWarnCtx.type = context.type;
2627 reviewWithWarnCtx.operationType = operationType;
2628 reviewWithWarnCtx.icon = icon;
2629 reviewWithWarnCtx.reviewTitle = reviewTitle;
2630 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2631 reviewWithWarnCtx.choiceCallback = choiceCallback;
2632 reviewWithWarnCtx.warning = warning;
2633
2634 // display the initial warning only of a risk/threat or blind signing
2635 if (!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
2636 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
2637 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
2638 displayStreamingReviewPage(FORWARD_DIRECTION);
2639 return;
2640 }
2641 displayInitialWarning();
2642}
2643
2658 nbgl_choiceCallback_t choiceCallback,
2659 nbgl_callback_t skipCallback)
2660{
2661 uint8_t curNbDataSets = context.review.nbDataSets;
2662
2663 memset(&context, 0, sizeof(UseCaseContext_t));
2664 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
2665 context.operationType = streamingOpType;
2666 context.review.tagValueList = tagValueList;
2667 context.review.onChoice = choiceCallback;
2668 context.currentPage = 0;
2669 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
2670 context.review.skipCallback = skipCallback;
2671 context.review.nbDataSets = curNbDataSets + 1;
2672
2673 displayStreamingReviewPage(FORWARD_DIRECTION);
2674}
2675
2687 nbgl_choiceCallback_t choiceCallback)
2688{
2689 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
2690}
2691
2692void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
2693 nbgl_choiceCallback_t choiceCallback)
2694{
2695 memset(&context, 0, sizeof(UseCaseContext_t));
2696 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
2697 context.operationType = streamingOpType;
2698 context.review.onChoice = choiceCallback;
2699 context.review.finishTitle = finishTitle;
2700 context.currentPage = 0;
2701 context.nbPages = 2; // 2 pages at the end for accept/reject
2702
2703 displayStreamingReviewPage(FORWARD_DIRECTION);
2704}
2705
2711void nbgl_useCaseSpinner(const char *text)
2712{
2713 memset(&context, 0, sizeof(UseCaseContext_t));
2714 context.type = SPINNER_USE_CASE;
2715 context.currentPage = 0;
2716 context.nbPages = 1;
2717
2718 displaySpinner(text);
2719}
2720
2722 const char *message,
2723 const char *subMessage,
2724 const char *confirmText,
2725 const char *cancelText,
2726 nbgl_choiceCallback_t callback)
2727{
2728 memset(&context, 0, sizeof(UseCaseContext_t));
2729 context.type = CHOICE_USE_CASE;
2730 context.choice.icon = icon;
2731 context.choice.message = message;
2732 context.choice.subMessage = subMessage;
2733 context.choice.confirmText = confirmText;
2734 context.choice.cancelText = cancelText;
2735 context.choice.onChoice = callback;
2736 context.currentPage = 0;
2737 context.nbPages = 1 + 1 + 2; // 2 pages at the end for confirm/cancel
2738
2739 displayChoicePage(FORWARD_DIRECTION);
2740};
2741
2755void nbgl_useCaseConfirm(const char *message,
2756 const char *subMessage,
2757 const char *confirmText,
2758 const char *cancelText,
2759 nbgl_callback_t callback)
2760{
2761 memset(&context, 0, sizeof(UseCaseContext_t));
2762 context.type = CONFIRM_USE_CASE;
2763 context.confirm.message = message;
2764 context.confirm.subMessage = subMessage;
2765 context.confirm.confirmText = confirmText;
2766 context.confirm.cancelText = cancelText;
2767 context.confirm.onConfirm = callback;
2768 context.currentPage = 0;
2769 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
2770
2771 displayConfirm(FORWARD_DIRECTION);
2772}
2773
2784 const char *message,
2785 const char *actionText,
2786 nbgl_callback_t callback)
2787{
2788 nbgl_layoutCenteredInfo_t centeredInfo = {0};
2789
2790 UNUSED(actionText);
2791
2792 // memorize callback in context
2793 memset(&context, 0, sizeof(UseCaseContext_t));
2794 context.type = ACTION_USE_CASE;
2795 context.action.actionCallback = callback;
2796
2797 centeredInfo.icon = icon;
2798 centeredInfo.text1 = message;
2799 centeredInfo.style = BOLD_TEXT1_INFO;
2800 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
2801}
2802
2803#ifdef NBGL_KEYPAD
2821void nbgl_useCaseKeypadDigits(const char *title,
2822 uint8_t minDigits,
2823 uint8_t maxDigits,
2824 bool shuffled,
2825 nbgl_pinValidCallback_t validatePinCallback,
2826 nbgl_callback_t backCallback)
2827{
2828 keypadGenericUseCase(
2829 title, minDigits, maxDigits, shuffled, false, validatePinCallback, backCallback);
2830}
2831
2850void nbgl_useCaseKeypadPIN(const char *title,
2851 uint8_t minDigits,
2852 uint8_t maxDigits,
2853 bool shuffled,
2854 nbgl_pinValidCallback_t validatePinCallback,
2855 nbgl_callback_t backCallback)
2856{
2857 keypadGenericUseCase(
2858 title, minDigits, maxDigits, shuffled, true, validatePinCallback, backCallback);
2859}
2860#endif // NBGL_KEYPAD
2861
2862#endif // HAVE_SE_TOUCH
2863#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_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
@ 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
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_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
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition nbgl_obj.c:1661
nbgl_buttonEvent_t
Definition nbgl_obj.h:207
@ BUTTON_BOTH_PRESSED
Sent when both buttons are released.
Definition nbgl_obj.h:214
@ BUTTON_LEFT_PRESSED
Sent when Left button is released.
Definition nbgl_obj.h:208
@ BUTTON_RIGHT_PRESSED
Send when Right button is released.
Definition nbgl_obj.h:209
#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)
This function redraws the whole screen on top of stack and its children.
Definition nbgl_screen.c:66
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
@ 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
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
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:181
@ ON_STATE
Definition nbgl_types.h:183
@ OFF_STATE
Definition nbgl_types.h:182
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.
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
void nbgl_useCaseGenericSettings(const char *appName, uint8_t initPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, nbgl_callback_t quitCallback)
void nbgl_useCaseKeypadPIN(const char *title, uint8_t minDigits, uint8_t maxDigits, uint8_t backToken, bool shuffled, tune_index_e tuneId, nbgl_pinValidCallback_t validatePinCallback, nbgl_layoutTouchCallback_t actionCallback)
uint32_t nbgl_operationType_t
This mask is used to describe the type of operation to review with additional options It is a mask of...
void nbgl_useCaseReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
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_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_useCaseKeypadDigits(const char *title, uint8_t minDigits, uint8_t maxDigits, uint8_t backToken, bool shuffled, tune_index_e tuneId, nbgl_pinValidCallback_t validatePinCallback, nbgl_layoutTouchCallback_t actionCallback)
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
@ W3C_THREAT_DETECTED_WARN
Web3 Checks: Threat detected (see reportRisk field)
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_NO_THREAT_WARN
Web3 Checks: No Threat detected.
@ W3C_RISK_DETECTED_WARN
Web3 Checks: Risk detected (see reportRisk field)
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_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_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)
#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
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)
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 nbContents
number of contents
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
nbgl_contentCallback_t contentGetterCallback
function to call to retrieve a given content
Structure describing the action button in Home Screen.
Structure containing all information when creating a layout. This structure must be passed as argumen...
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 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:66
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:64
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:67
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:65
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
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_warningDetails_t * introDetails
const nbgl_warningDetails_t * reviewDetails
uint32_t predefinedSet
nbgl_contentInfoList_t infosList
INFOS_LIST 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
unsigned char uint8_t
Definition usbd_conf.h:53
signed char int8_t
Definition usbd_conf.h:49