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