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;
147 bool forceAction;
149 stepCallback;
150 union {
151 ReviewContext_t review;
152 ChoiceContext_t choice;
153 ConfirmContext_t confirm;
154 HomeContext_t home;
155 ContentContext_t content;
156#ifdef NBGL_KEYPAD
157 KeypadContext_t keypad;
158#endif
159 ActionContext_t action;
160 };
161} UseCaseContext_t;
162
163typedef struct PageContent_s {
164 bool isSwitch;
165 const char *text;
166 const char *subText;
167 const nbgl_icon_details_t *icon;
168 const nbgl_contentValueExt_t *extension;
169 nbgl_state_t state;
170 bool isCenteredInfo;
171 bool isAction;
172} PageContent_t;
173
174typedef struct ReviewWithWarningContext_s {
175 ContextType_t type;
176 nbgl_operationType_t operationType;
177 const nbgl_contentTagValueList_t *tagValueList;
178 const nbgl_icon_details_t *icon;
179 const char *reviewTitle;
180 const char *reviewSubTitle;
181 const char *finishTitle;
182 const nbgl_warning_t *warning;
183 nbgl_choiceCallback_t choiceCallback;
184 uint8_t securityReportLevel; // level 1 is the first level of menus
185 bool isIntro; // set to true during intro (before actual review)
186 uint8_t warningPage;
187 uint8_t nbWarningPages;
188 uint8_t firstWarningPage;
189} ReviewWithWarningContext_t;
190
191typedef enum {
192 NO_FORCED_TYPE = 0,
193 FORCE_BUTTON,
194 FORCE_CENTERED_INFO
195} ForcedType_t;
196
197/**********************
198 * STATIC VARIABLES
199 **********************/
200static UseCaseContext_t context;
201
202static ReviewWithWarningContext_t reviewWithWarnCtx;
203// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
204static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
205
206// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
207nbgl_operationType_t streamingOpType;
208
209/**********************
210 * STATIC FUNCTIONS
211 **********************/
212static void displayReviewPage(nbgl_stepPosition_t pos);
213static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
214static void displayHomePage(nbgl_stepPosition_t pos);
215static void displayInfoPage(nbgl_stepPosition_t pos);
216static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
217static void displayChoicePage(nbgl_stepPosition_t pos);
218static void displayConfirm(nbgl_stepPosition_t pos);
219static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
220static void displaySpinner(const char *text);
221
222static void startUseCaseHome(void);
223static void startUseCaseInfo(void);
224static void startUseCaseSettings(void);
225static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
226static void startUseCaseContent(void);
227
228static void statusTickerCallback(void);
229
230static void displayExtensionStep(nbgl_stepPosition_t pos);
231static void displayWarningStep(void);
232
233// Simple helper to get the number of elements inside a nbgl_content_t
234static uint8_t getContentNbElement(const nbgl_content_t *content)
235{
236 switch (content->type) {
237 case CENTERED_INFO:
238 return 1;
239 case INFO_BUTTON:
240 return 1;
241 case TAG_VALUE_LIST:
242 return content->content.tagValueList.nbPairs;
244 // last element is for Confirm page
245 return content->content.tagValueConfirm.tagValueList.nbPairs + 1;
246 case SWITCHES_LIST:
247 return content->content.switchesList.nbSwitches;
248 case INFOS_LIST:
249 return content->content.infosList.nbInfos;
250 case CHOICES_LIST:
251 return content->content.choicesList.nbChoices;
252 case BARS_LIST:
253 return content->content.barsList.nbBars;
254 default:
255 return 0;
256 }
257}
258
259// Helper to retrieve the content inside a nbgl_genericContents_t using
260// either the contentsList or using the contentGetterCallback
261static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
262 int8_t contentIdx,
263 nbgl_content_t *content)
264{
265 nbgl_pageContent_t pageContent = {0};
266 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
267 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
268 return NULL;
269 }
270
271 if (genericContents->callbackCallNeeded) {
272 if (content == NULL) {
273 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
274 return NULL;
275 }
276 // Retrieve content through callback, but first memset the content.
277 memset(content, 0, sizeof(nbgl_content_t));
278 if (context.content.navCallback) {
279 if (context.content.navCallback(contentIdx, &pageContent) == true) {
280 // Copy the Page Content to the Content variable
281 content->type = pageContent.type;
282 switch (content->type) {
283 case CENTERED_INFO:
284 content->content.centeredInfo = pageContent.centeredInfo;
285 break;
286 case INFO_BUTTON:
287 content->content.infoButton = pageContent.infoButton;
288 break;
289 case TAG_VALUE_LIST:
290 content->content.tagValueList = pageContent.tagValueList;
291 break;
293 content->content.tagValueConfirm = pageContent.tagValueConfirm;
294 break;
295 case SWITCHES_LIST:
296 content->content.switchesList = pageContent.switchesList;
297 break;
298 case INFOS_LIST:
299 content->content.infosList = pageContent.infosList;
300 break;
301 case CHOICES_LIST:
302 content->content.choicesList = pageContent.choicesList;
303 break;
304 case BARS_LIST:
305 content->content.barsList = pageContent.barsList;
306 break;
307 default:
308 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
309 return NULL;
310 }
311 }
312 else {
313 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
314 return NULL;
315 }
316 }
317 else {
318 genericContents->contentGetterCallback(contentIdx, content);
319 }
320 return content;
321 }
322 else {
323 // Retrieve content through list
324 return PIC(&genericContents->contentsList[contentIdx]);
325 }
326}
327
328// Helper to retrieve the content inside a nbgl_genericContents_t using
329// either the contentsList or using the contentGetterCallback
330static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
331 uint8_t *elemContentIdx,
332 nbgl_content_t *content)
333{
334 const nbgl_genericContents_t *genericContents = NULL;
335 const nbgl_content_t *p_content = NULL;
336 uint8_t nbPages = 0;
337 uint8_t elemNbPages = 0;
338
339 switch (context.type) {
340 case SETTINGS_USE_CASE:
341 case HOME_USE_CASE:
342 case GENERIC_SETTINGS:
343 genericContents = context.home.settingContents;
344 break;
345 case CONTENT_USE_CASE:
346 case GENERIC_REVIEW_USE_CASE:
347 genericContents = &context.content.genericContents;
348 break;
349 default:
350 return NULL;
351 }
352 for (int i = 0; i < genericContents->nbContents; i++) {
353 p_content = getContentAtIdx(genericContents, i, content);
354 elemNbPages = getContentNbElement(p_content);
355 if (nbPages + elemNbPages > elemIdx) {
356 *elemContentIdx = context.currentPage - nbPages;
357 break;
358 }
359 nbPages += elemNbPages;
360 }
361
362 return p_content;
363}
364
365static const char *getChoiceName(uint8_t choiceIndex)
366{
367 uint8_t elemIdx;
368 uint8_t nbValues;
369 const nbgl_content_t *p_content = NULL;
370 nbgl_content_t content = {0};
371 nbgl_contentRadioChoice_t *contentChoices = NULL;
372 nbgl_contentBarsList_t *contentBars = NULL;
373 char **names = NULL;
374
375 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
376 if (p_content == NULL) {
377 return NULL;
378 }
379 switch (p_content->type) {
380 case CHOICES_LIST:
381 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
382 names = (char **) PIC(contentChoices->names);
383 nbValues = contentChoices->nbChoices;
384 break;
385 case BARS_LIST:
386 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
387 names = (char **) PIC(contentBars->barTexts);
388 nbValues = contentBars->nbBars;
389 break;
390 default:
391 // Not supported as vertical MenuList
392 return NULL;
393 }
394 if (choiceIndex >= nbValues) {
395 // Last item is always "Back" button
396 return "Back";
397 }
398 return (const char *) PIC(names[choiceIndex]);
399}
400
401static void onChoiceSelected(uint8_t choiceIndex)
402{
403 uint8_t elemIdx;
404 uint8_t token = 255;
405 const nbgl_content_t *p_content = NULL;
406 nbgl_content_t content = {0};
407 nbgl_contentRadioChoice_t *contentChoices = NULL;
408 nbgl_contentBarsList_t *contentBars = NULL;
409
410 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
411 if (p_content == NULL) {
412 return;
413 }
414 switch (p_content->type) {
415 case CHOICES_LIST:
416 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
417 if (choiceIndex < contentChoices->nbChoices) {
418 token = contentChoices->token;
419 }
420 break;
421 case BARS_LIST:
422 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
423 if (choiceIndex < contentBars->nbBars) {
424 token = contentBars->tokens[choiceIndex];
425 }
426 break;
427 default:
428 // Not supported as vertical MenuList
429 break;
430 }
431 if ((token != 255) && (context.content.controlsCallback != NULL)) {
432 context.content.controlsCallback(token, 0);
433 }
434 else if (context.content.quitCallback != NULL) {
435 context.content.quitCallback();
436 }
437}
438
439static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
440 uint8_t index,
441 const char **item,
442 const char **value,
443 const nbgl_contentValueExt_t **extension,
444 const nbgl_icon_details_t **icon,
445 bool *isCenteredInfo)
446{
447 const nbgl_contentTagValue_t *pair;
448
449 if (tagValueList->pairs != NULL) {
450 pair = PIC(&tagValueList->pairs[index]);
451 }
452 else {
453 pair = PIC(tagValueList->callback(index));
454 }
455 *item = pair->item;
456 *value = pair->value;
457 if (pair->aliasValue) {
458 *extension = pair->extension;
459 }
460 else if (pair->centeredInfo) {
461 *isCenteredInfo = true;
462 *icon = pair->valueIcon;
463 }
464 else {
465 *extension = NULL;
466 }
467}
468
469static void onReviewAccept(void)
470{
471 if (context.review.onChoice) {
472 context.review.onChoice(true);
473 }
474}
475
476static void onReviewReject(void)
477{
478 if (context.review.onChoice) {
479 context.review.onChoice(false);
480 }
481}
482
483static void onChoiceAccept(void)
484{
485 if (context.choice.onChoice) {
486 context.choice.onChoice(true);
487 }
488}
489
490static void onChoiceReject(void)
491{
492 if (context.choice.onChoice) {
493 context.choice.onChoice(false);
494 }
495}
496
497static void onConfirmAccept(void)
498{
499 if (context.confirm.currentStep) {
500 nbgl_stepRelease(context.confirm.currentStep);
501 }
502 if (context.confirm.onConfirm) {
503 context.confirm.onConfirm();
504 }
505}
506
507static void onConfirmReject(void)
508{
509 if (context.confirm.currentStep) {
510 nbgl_stepRelease(context.confirm.currentStep);
512 nbgl_refresh();
513 }
514}
515
516static void onSwitchAction(void)
517{
518 const nbgl_contentSwitch_t *contentSwitch = NULL;
519 const nbgl_content_t *p_content = NULL;
520 nbgl_content_t content = {0};
521 uint8_t elemIdx;
522
523 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
524 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
525 return;
526 }
527 contentSwitch
528 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
529 switch (context.type) {
530 case SETTINGS_USE_CASE:
531 case HOME_USE_CASE:
532 case GENERIC_SETTINGS:
533 displaySettingsPage(FORWARD_DIRECTION, true);
534 break;
535 case CONTENT_USE_CASE:
536 case GENERIC_REVIEW_USE_CASE:
537 displayContent(FORWARD_DIRECTION, true);
538 break;
539 default:
540 break;
541 }
542 if (p_content->contentActionCallback != NULL) {
543 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
544 actionCallback(contentSwitch->token,
545 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
546 context.currentPage);
547 }
548 else if (context.content.controlsCallback != NULL) {
549 context.content.controlsCallback(contentSwitch->token, 0);
550 }
551}
552
553static void drawStep(nbgl_stepPosition_t pos,
554 const nbgl_icon_details_t *icon,
555 const char *txt,
556 const char *subTxt,
557 nbgl_stepButtonCallback_t onActionCallback,
558 bool modal,
559 ForcedType_t forcedType)
560{
561 uint8_t elemIdx;
562 nbgl_step_t newStep = NULL;
563 const nbgl_content_t *p_content = NULL;
564 nbgl_content_t content = {0};
565 nbgl_contentRadioChoice_t *contentChoices = NULL;
566 nbgl_contentBarsList_t *contentBars = NULL;
567 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
568 nbgl_layoutMenuList_t list = {0};
569 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
570 .tickerIntervale = 0, // not periodic
571 .tickerValue = STATUS_SCREEN_DURATION};
572
573 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
574 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
575 // (except the first set) because the set starts with a "skip" page
576 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
577 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
578 pos |= LAST_STEP;
579 }
580 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
581 p_ticker = &ticker;
582 }
583 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
584 nbgl_stepRelease(context.confirm.currentStep);
585 }
586
587 if (txt == NULL) {
588 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
589 if (p_content) {
590 switch (p_content->type) {
591 case CHOICES_LIST:
592 contentChoices
593 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
594 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
595 list.selectedChoice = contentChoices->initChoice;
596 list.callback = getChoiceName;
597 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
598 break;
599 case BARS_LIST:
600 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
601 list.nbChoices = contentBars->nbBars + 1; // For Back button
602 list.selectedChoice = 0;
603 list.callback = getChoiceName;
604 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
605 break;
606 default:
607 // Not supported as vertical MenuList
608 break;
609 }
610 }
611 }
612 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
614 if (subTxt != NULL) {
615 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
616 }
617 else {
618 style = REGULAR_INFO;
619 }
620 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
621 }
622 else {
624 info.icon = icon;
625 info.text1 = txt;
626 info.text2 = subTxt;
627 info.onTop = false;
628 if ((subTxt != NULL) || (context.stepCallback != NULL) || context.forceAction) {
629 info.style = BOLD_TEXT1_INFO;
630 }
631 else {
632 info.style = REGULAR_INFO;
633 }
634 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
635 }
636 if (context.type == CONFIRM_USE_CASE) {
637 context.confirm.currentStep = newStep;
638 }
639}
640
641static void drawSwitchStep(nbgl_stepPosition_t pos,
642 const char *title,
643 const char *description,
644 bool state,
645 nbgl_stepButtonCallback_t onActionCallback,
646 bool modal)
647{
648 nbgl_layoutSwitch_t switchInfo;
649
650 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
651 switchInfo.initState = state;
652 switchInfo.text = title;
653 switchInfo.subText = description;
654 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
655}
656
657static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
658{
659 uint8_t elemIdx;
660 uint8_t token = 0;
661 uint8_t index = 0;
662 const nbgl_content_t *p_content = NULL;
663 nbgl_content_t content = {0};
664
665 if (event == BUTTON_LEFT_PRESSED) {
666 if (context.currentPage > 0) {
667 context.currentPage--;
668 }
669 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
670 // the first set
671 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
672 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
673 // Drop the event
674 return false;
675 }
676 *pos = BACKWARD_DIRECTION;
677 }
678 else if (event == BUTTON_RIGHT_PRESSED) {
679 if (context.currentPage < (int) (context.nbPages - 1)) {
680 context.currentPage++;
681 }
682 else {
683 // Drop the event
684 return false;
685 }
686 *pos = FORWARD_DIRECTION;
687 }
688 else {
689 if (event == BUTTON_BOTH_PRESSED) {
690 if (context.stepCallback != NULL) {
691 context.stepCallback();
692 }
693 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
694 || (context.type == GENERIC_SETTINGS)
695 || (context.type == GENERIC_REVIEW_USE_CASE)) {
696 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
697 if (p_content != NULL) {
698 switch (p_content->type) {
699 case CENTERED_INFO:
700 // No associated callback
701 return false;
702 case INFO_BUTTON:
703 token = p_content->content.infoButton.buttonToken;
704 break;
705 case SWITCHES_LIST:
706 token = p_content->content.switchesList.switches->token;
707 break;
708 case BARS_LIST:
709 token = p_content->content.barsList.tokens[context.currentPage];
710 break;
711 case CHOICES_LIST:
712 token = p_content->content.choicesList.token;
713 index = context.currentPage;
714 break;
715 case TAG_VALUE_LIST:
716 return false;
718 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
719 return false;
720 }
721 token = p_content->content.tagValueConfirm.confirmationToken;
722 break;
723 default:
724 break;
725 }
726
727 if ((p_content) && (p_content->contentActionCallback != NULL)) {
728 p_content->contentActionCallback(token, 0, context.currentPage);
729 }
730 else if (context.content.controlsCallback != NULL) {
731 context.content.controlsCallback(token, index);
732 }
733 }
734 }
735 }
736 return false;
737 }
738 return true;
739}
740
741static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
742{
743 UNUSED(stepCtx);
745
746 if (!buttonGenericCallback(event, &pos)) {
747 return;
748 }
749 else {
750 // memorize last direction
751 context.review.dataDirection = pos;
752 }
753 displayReviewPage(pos);
754}
755
756// this is the callback used when button action on the "skip" page
757static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
758{
759 UNUSED(stepCtx);
761
762 if (event == BUTTON_LEFT_PRESSED) {
763 // only decrement page if we are going backward but coming from forward (back & forth)
764 if ((context.review.dataDirection == FORWARD_DIRECTION)
765 && (context.currentPage > context.firstPairPage)) {
766 context.currentPage--;
767 }
768 pos = BACKWARD_DIRECTION;
769 }
770 else if (event == BUTTON_RIGHT_PRESSED) {
771 // only increment page if we are going forward but coming from backward (back & forth)
772 if ((context.review.dataDirection == BACKWARD_DIRECTION)
773 && (context.currentPage < (int) (context.nbPages - 1))
774 && (context.currentPage > context.firstPairPage)) {
775 context.currentPage++;
776 }
777 pos = FORWARD_DIRECTION;
778 }
779 else if (event == BUTTON_BOTH_PRESSED) {
780 // the first tag/value page is at page 0 only in streaming case
781 if (context.firstPairPage == 0) {
782 // in streaming, we have to call the provided callback
783 context.review.skipCallback();
784 }
785 else {
786 // if not in streaming, go directly to the "approve" page
787 context.currentPage = context.nbPages - 2;
788 displayReviewPage(FORWARD_DIRECTION);
789 }
790 return;
791 }
792 else {
793 return;
794 }
795 // the first tag/value page is at page 0 only in streaming case
796 if (context.firstPairPage == 0) {
797 displayStreamingReviewPage(pos);
798 }
799 else {
800 displayReviewPage(pos);
801 }
802}
803
804// this is the callback used when buttons in "Action" use case are pressed
805static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
806{
807 UNUSED(stepCtx);
808
809 if (event == BUTTON_BOTH_PRESSED) {
810 context.action.actionCallback();
811 }
812}
813
814static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
815{
817 UNUSED(stepCtx);
818
819 if (!buttonGenericCallback(event, &pos)) {
820 return;
821 }
822 else {
823 // memorize last direction
824 context.review.dataDirection = pos;
825 }
826
827 displayStreamingReviewPage(pos);
828}
829
830static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
831{
832 UNUSED(stepCtx);
834
835 if (!buttonGenericCallback(event, &pos)) {
836 return;
837 }
838
839 displaySettingsPage(pos, false);
840}
841
842static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
843{
844 UNUSED(stepCtx);
846
847 if (!buttonGenericCallback(event, &pos)) {
848 return;
849 }
850
851 displayInfoPage(pos);
852}
853
854static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
855{
856 UNUSED(stepCtx);
858
859 if (!buttonGenericCallback(event, &pos)) {
860 return;
861 }
862
863 displayHomePage(pos);
864}
865
866static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
867{
868 UNUSED(stepCtx);
870
871 if (!buttonGenericCallback(event, &pos)) {
872 return;
873 }
874
875 displayChoicePage(pos);
876}
877
878static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
879{
880 UNUSED(stepCtx);
882
883 if (!buttonGenericCallback(event, &pos)) {
884 return;
885 }
886
887 displayConfirm(pos);
888}
889
890static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
891{
892 UNUSED(stepCtx);
893 // any button press should dismiss the status screen
894 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
895 || (event == BUTTON_RIGHT_PRESSED)) {
896 if (context.stepCallback != NULL) {
897 context.stepCallback();
898 }
899 }
900}
901
902static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
903{
904 UNUSED(stepCtx);
906
907 if (!buttonGenericCallback(event, &pos)) {
908 return;
909 }
910
911 displayContent(pos, false);
912}
913
914// callback used for timeout
915static void statusTickerCallback(void)
916{
917 if (context.stepCallback != NULL) {
918 context.stepCallback();
919 }
920}
921
922// this is the callback used when navigating in extension pages
923static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
924{
926 UNUSED(stepCtx);
927
928 if (event == BUTTON_LEFT_PRESSED) {
929 // only decrement page if we are not at the first page
930 if (context.review.currentExtensionPage > 0) {
931 context.review.currentExtensionPage--;
932 }
933 pos = BACKWARD_DIRECTION;
934 }
935 else if (event == BUTTON_RIGHT_PRESSED) {
936 // only increment page if not at last page
937 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
938 context.review.currentExtensionPage++;
939 }
940 pos = FORWARD_DIRECTION;
941 }
942 else if (event == BUTTON_BOTH_PRESSED) {
943 // if at last page, leave modal context
944 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
945 nbgl_stepRelease(context.review.extensionStepCtx);
947 nbgl_refresh();
948 }
949 return;
950 }
951 else {
952 return;
953 }
954 displayExtensionStep(pos);
955}
956
957// function used to display the extension pages
958static void displayExtensionStep(nbgl_stepPosition_t pos)
959{
960 nbgl_layoutCenteredInfo_t info = {0};
961 const nbgl_contentTagValueList_t *tagValueList = NULL;
962 const nbgl_contentInfoList_t *infoList = NULL;
963 const char *text = NULL;
964 const char *subText = NULL;
965
966 if (context.review.extensionStepCtx != NULL) {
967 nbgl_stepRelease(context.review.extensionStepCtx);
968 }
969 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
970 if (context.review.currentExtensionPage == 0) {
971 pos |= FIRST_STEP;
972 }
973 else {
975 }
976
977 switch (context.review.extension->aliasType) {
978 case ENS_ALIAS:
979 text = context.review.extension->title;
980 subText = context.review.extension->fullValue;
981 break;
982 case INFO_LIST_ALIAS:
983 infoList = context.review.extension->infolist;
984 text = PIC(infoList->infoTypes[context.review.currentExtensionPage]);
985 subText = PIC(infoList->infoContents[context.review.currentExtensionPage]);
986 break;
988 tagValueList = context.review.extension->tagValuelist;
989 text = PIC(tagValueList->pairs[context.review.currentExtensionPage].item);
990 subText = PIC(tagValueList->pairs[context.review.currentExtensionPage].value);
991 break;
992 default:
993 break;
994 }
995 if (text != NULL) {
996 context.review.extensionStepCtx = nbgl_stepDrawText(
997 pos, extensionNavigate, NULL, text, subText, BOLD_TEXT1_INFO, true);
998 }
999 }
1000 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
1001 // draw the back page
1002 info.icon = &C_icon_back_x;
1003 info.text1 = "Back";
1004 info.style = BOLD_TEXT1_INFO;
1005 pos |= LAST_STEP;
1006 context.review.extensionStepCtx
1007 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
1008 }
1009 nbgl_refresh();
1010}
1011
1012static void displayAliasFullValue(void)
1013{
1014 const char *text = NULL;
1015 const char *subText = NULL;
1016 const nbgl_icon_details_t *icon;
1017 bool isCenteredInfo;
1018
1019 getPairData(context.review.tagValueList,
1020 context.review.currentTagValueIndex,
1021 &text,
1022 &subText,
1023 &context.review.extension,
1024 &icon,
1025 &isCenteredInfo);
1026 if (context.review.extension == NULL) {
1027 // probably an error
1029 "displayAliasFullValue: extension nor found for pair %d\n",
1030 context.review.currentTagValueIndex);
1031 return;
1032 }
1033 context.review.currentExtensionPage = 0;
1034 context.review.extensionStepCtx = NULL;
1035 // create a modal flow to display this extension
1036 switch (context.review.extension->aliasType) {
1037 case ENS_ALIAS:
1038 context.review.nbExtensionPages = 2;
1039 break;
1040 case INFO_LIST_ALIAS:
1041 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
1042 break;
1044 context.review.nbExtensionPages = context.review.extension->tagValuelist->nbPairs + 1;
1045 break;
1046 default:
1048 "displayAliasFullValue: unsupported alias type %d\n",
1049 context.review.extension->aliasType);
1050 return;
1051 }
1052 displayExtensionStep(FORWARD_DIRECTION);
1053}
1054
1055static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1056{
1057 if (approve) {
1058 // Approve page
1059 *icon = &C_icon_validate_14;
1060 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1061 *text = "Confirm";
1062 }
1063 else {
1064 // if finish title is provided, use it
1065 if (context.review.finishTitle != NULL) {
1066 *text = context.review.finishTitle;
1067 }
1068 else {
1069 switch (context.operationType & REAL_TYPE_MASK) {
1070 case TYPE_TRANSACTION:
1071 if (context.operationType & RISKY_OPERATION) {
1072 *text = "Accept risk and sign transaction";
1073 }
1074 else {
1075 *text = "Sign transaction";
1076 }
1077 break;
1078 case TYPE_MESSAGE:
1079 if (context.operationType & RISKY_OPERATION) {
1080 *text = "Accept risk and sign message";
1081 }
1082 else {
1083 *text = "Sign message";
1084 }
1085 break;
1086 default:
1087 if (context.operationType & RISKY_OPERATION) {
1088 *text = "Accept risk and sign operation";
1089 }
1090 else {
1091 *text = "Sign operation";
1092 }
1093 break;
1094 }
1095 }
1096 }
1097 context.stepCallback = onReviewAccept;
1098 }
1099 else {
1100 // Reject page
1101 *icon = &C_icon_crossmark;
1102 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1103 *text = "Cancel";
1104 }
1105 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1106 *text = "Reject transaction";
1107 }
1108 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1109 *text = "Reject message";
1110 }
1111 else {
1112 *text = "Reject operation";
1113 }
1114 context.stepCallback = onReviewReject;
1115 }
1116}
1117
1118// function used to display the current page in review
1119static void displayReviewPage(nbgl_stepPosition_t pos)
1120{
1121 uint8_t reviewPages = 0;
1122 uint8_t finalPages = 0;
1123 uint8_t pairIndex = 0;
1124 const char *text = NULL;
1125 const char *subText = NULL;
1126 const nbgl_icon_details_t *icon = NULL;
1127 uint8_t currentIndex = 0;
1128 uint8_t titleIndex = 255;
1129 uint8_t subIndex = 255;
1130 uint8_t approveIndex = 255;
1131 uint8_t rejectIndex = 255;
1132 const nbgl_contentValueExt_t *extension = NULL;
1133 ForcedType_t forcedType = NO_FORCED_TYPE;
1134
1135 context.stepCallback = NULL;
1136
1137 // Determine the 1st page to display tag/values
1138 // Title page to display
1139 titleIndex = currentIndex++;
1140 reviewPages++;
1141 if (context.review.reviewSubTitle) {
1142 // subtitle page to display
1143 subIndex = currentIndex++;
1144 reviewPages++;
1145 }
1146 approveIndex = context.nbPages - 2;
1147 rejectIndex = context.nbPages - 1;
1148 finalPages = approveIndex;
1149
1150 // Determine which page to display
1151 if (context.currentPage >= finalPages) {
1152 if (context.currentPage == approveIndex) {
1153 // Approve page
1154 getLastPageInfo(true, &icon, &text);
1155 }
1156 else if (context.currentPage == rejectIndex) {
1157 // Reject page
1158 getLastPageInfo(false, &icon, &text);
1159 }
1160 }
1161 else if (context.currentPage < reviewPages) {
1162 if (context.currentPage == titleIndex) {
1163 // Title page
1164 icon = context.review.icon;
1165 text = context.review.reviewTitle;
1166 }
1167 else if (context.currentPage == subIndex) {
1168 // SubTitle page
1169 text = context.review.reviewSubTitle;
1170 }
1171 }
1172 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1173 // address confirmation and 2nd page
1174 text = "Address";
1175 subText = context.review.address;
1176 }
1177 else {
1178 // if there is a skip, and we are not already displaying the "skip" page
1179 // and we are not at the first tag/value of the first set of data (except if going
1180 // backward) then display the "skip" page
1181 if ((context.operationType & SKIPPABLE_OPERATION) && (context.review.skipDisplay == false)
1182 && ((context.currentPage > reviewPages)
1183 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1184 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1185 nbgl_layoutCenteredInfo_t info = {0};
1186 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1187 directions |= LAST_STEP;
1188 }
1189 info.icon = &C_Information_circle_14px;
1190 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1191 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1192 nbgl_refresh();
1193 context.review.skipDisplay = true;
1194 context.firstPairPage = reviewPages;
1195 return;
1196 }
1197 context.review.skipDisplay = false;
1198 bool isCenteredInfo = false;
1199 pairIndex = context.currentPage - reviewPages;
1200 if (context.review.address != NULL) {
1201 pairIndex--;
1202 }
1203 getPairData(context.review.tagValueList,
1204 pairIndex,
1205 &text,
1206 &subText,
1207 &extension,
1208 &icon,
1209 &isCenteredInfo);
1210 if (extension != NULL) {
1211 context.stepCallback = displayAliasFullValue;
1212 context.review.currentTagValueIndex = pairIndex;
1213 forcedType = FORCE_BUTTON;
1214 }
1215 else {
1216 if (isCenteredInfo) {
1217 forcedType = FORCE_CENTERED_INFO;
1218 }
1219 }
1220 }
1221
1222 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1223 nbgl_refresh();
1224}
1225
1226// function used to display the current page in review
1227static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1228{
1229 const char *text = NULL;
1230 const char *subText = NULL;
1231 const nbgl_icon_details_t *icon = NULL;
1232 uint8_t reviewPages = 0;
1233 uint8_t titleIndex = 255;
1234 uint8_t subIndex = 255;
1235 const nbgl_contentValueExt_t *extension = NULL;
1236 ForcedType_t forcedType = NO_FORCED_TYPE;
1237
1238 context.stepCallback = NULL;
1239 switch (context.type) {
1240 case STREAMING_START_REVIEW_USE_CASE:
1241 // Title page to display
1242 titleIndex = reviewPages++;
1243 if (context.review.reviewSubTitle) {
1244 // subtitle page to display
1245 subIndex = reviewPages++;
1246 }
1247 // Determine which page to display
1248 if (context.currentPage >= reviewPages) {
1249 onReviewAccept();
1250 return;
1251 }
1252 // header page(s)
1253 if (context.currentPage == titleIndex) {
1254 // title page
1255 icon = context.review.icon;
1256 text = context.review.reviewTitle;
1257 }
1258 else if (context.currentPage == subIndex) {
1259 // subtitle page
1260 text = context.review.reviewSubTitle;
1261 }
1262 break;
1263
1264 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1265 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1266 onReviewAccept();
1267 return;
1268 }
1269 // if there is a skip, and we are not already displaying the "skip" page
1270 // and we are not at the first tag/value of the first set of data (except if going
1271 // backward) then display the "skip" page
1272 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1273 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1274 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1275 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1276 nbgl_layoutCenteredInfo_t info = {0};
1277 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1278 directions |= LAST_STEP;
1279 }
1280 info.icon = &C_Information_circle_14px;
1281 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1282 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1283 nbgl_refresh();
1284 context.review.skipDisplay = true;
1285 return;
1286 }
1287 context.review.skipDisplay = false;
1288 bool isCenteredInfo = false;
1289 getPairData(context.review.tagValueList,
1290 context.currentPage,
1291 &text,
1292 &subText,
1293 &extension,
1294 &icon,
1295 &isCenteredInfo);
1296 if (extension != NULL) {
1297 forcedType = FORCE_BUTTON;
1298 }
1299 else {
1300 if (isCenteredInfo) {
1301 forcedType = FORCE_CENTERED_INFO;
1302 }
1303 }
1304 break;
1305
1306 case STREAMING_FINISH_REVIEW_USE_CASE:
1307 default:
1308 if (context.currentPage == 0) {
1309 // accept page
1310 getLastPageInfo(true, &icon, &text);
1311 }
1312 else {
1313 // reject page
1314 getLastPageInfo(false, &icon, &text);
1315 }
1316 break;
1317 }
1318
1319 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1320 nbgl_refresh();
1321}
1322
1323// function used to display the current page in info
1324static void displayInfoPage(nbgl_stepPosition_t pos)
1325{
1326 const char *text = NULL;
1327 const char *subText = NULL;
1328 const nbgl_icon_details_t *icon = NULL;
1329
1330 context.stepCallback = NULL;
1331
1332 if (context.currentPage < (context.nbPages - 1)) {
1333 text = PIC(
1334 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1335 subText = PIC(
1336 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1337 }
1338 else {
1339 icon = &C_icon_back_x;
1340 text = "Back";
1341 context.stepCallback = startUseCaseHome;
1342 }
1343
1344 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1345 nbgl_refresh();
1346}
1347
1348// function used to get the current page content
1349static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1350{
1351 uint8_t elemIdx = 0;
1352 const nbgl_content_t *p_content = NULL;
1353 nbgl_content_t content = {0};
1354 nbgl_contentSwitch_t *contentSwitch = NULL;
1355#ifdef WITH_HORIZONTAL_CHOICES_LIST
1356 nbgl_contentRadioChoice_t *contentChoices = NULL;
1357 char **names = NULL;
1358#endif
1359#ifdef WITH_HORIZONTAL_BARS_LIST
1360 nbgl_contentBarsList_t *contentBars = NULL;
1361 char **texts = NULL;
1362#endif
1363 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1364 if (p_content == NULL) {
1365 return;
1366 }
1367 switch (p_content->type) {
1368 case CENTERED_INFO:
1369 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1370 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1371 break;
1372 case INFO_BUTTON:
1373 contentPage->icon = PIC(p_content->content.infoButton.icon);
1374 contentPage->text = PIC(p_content->content.infoButton.text);
1375 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1376 break;
1377 case TAG_VALUE_LIST:
1378 getPairData(&p_content->content.tagValueList,
1379 elemIdx,
1380 &contentPage->text,
1381 &contentPage->subText,
1382 &contentPage->extension,
1383 &contentPage->icon,
1384 &contentPage->isCenteredInfo);
1385 break;
1386 case TAG_VALUE_CONFIRM:
1387 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
1388 getPairData(&p_content->content.tagValueConfirm.tagValueList,
1389 elemIdx,
1390 &contentPage->text,
1391 &contentPage->subText,
1392 &contentPage->extension,
1393 &contentPage->icon,
1394 &contentPage->isCenteredInfo);
1395 }
1396 else {
1397 contentPage->text = p_content->content.tagValueConfirm.confirmationText;
1398 contentPage->icon = &C_icon_validate_14;
1399 contentPage->isAction = true;
1400 }
1401 break;
1402 case SWITCHES_LIST:
1403 contentPage->isSwitch = true;
1404 contentSwitch = &(
1405 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1406 contentPage->text = contentSwitch->text;
1407 contentPage->state = contentSwitch->initState;
1408 if (toogle_state) {
1409 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1410 }
1411 context.stepCallback = onSwitchAction;
1412 contentPage->subText = contentSwitch->subText;
1413 break;
1414 case INFOS_LIST:
1415 contentPage->text
1416 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1417 contentPage->subText
1418 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1419 break;
1420 case CHOICES_LIST:
1421#ifdef WITH_HORIZONTAL_CHOICES_LIST
1422 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1423 names = (char **) PIC(contentChoices->names);
1424 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1425 contentPage->text = PIC(context.content.title);
1426 contentPage->subText = (const char *) PIC(names[elemIdx]);
1427 }
1428 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1429 contentPage->text = PIC(context.home.appName);
1430 contentPage->subText = (const char *) PIC(names[elemIdx]);
1431 }
1432 else {
1433 contentPage->text = (const char *) PIC(names[elemIdx]);
1434 }
1435#endif
1436 break;
1437 case BARS_LIST:
1438#ifdef WITH_HORIZONTAL_BARS_LIST
1439 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1440 texts = (char **) PIC(contentBars->barTexts);
1441 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1442 contentPage->text = PIC(context.content.title);
1443 contentPage->subText = PIC(texts[elemIdx]);
1444 }
1445 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1446 contentPage->text = PIC(context.home.appName);
1447 contentPage->subText = PIC(texts[elemIdx]);
1448 }
1449 else {
1450 contentPage->text = PIC(texts[elemIdx]);
1451 }
1452#endif
1453 break;
1454 default:
1455 break;
1456 }
1457}
1458
1459// function used to display the current page in settings
1460static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1461{
1462 PageContent_t contentPage = {0};
1463
1464 context.stepCallback = NULL;
1465
1466 if (context.currentPage < (context.nbPages - 1)) {
1467 getContentPage(toogle_state, &contentPage);
1468 }
1469 else { // last page is for quit
1470 contentPage.icon = &C_icon_back_x;
1471 contentPage.text = "Back";
1472 if (context.type == GENERIC_SETTINGS) {
1473 context.stepCallback = context.home.quitCallback;
1474 }
1475 else {
1476 context.stepCallback = startUseCaseHome;
1477 }
1478 }
1479
1480 if (contentPage.isSwitch) {
1481 drawSwitchStep(
1482 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1483 }
1484 else {
1485 drawStep(pos,
1486 contentPage.icon,
1487 contentPage.text,
1488 contentPage.subText,
1489 settingsCallback,
1490 false,
1491 NO_FORCED_TYPE);
1492 }
1493
1494 nbgl_refresh();
1495}
1496
1497static void startUseCaseHome(void)
1498{
1499 switch (context.type) {
1500 case SETTINGS_USE_CASE:
1501 // Settings page index
1502 context.currentPage = 1;
1503 if (context.home.homeAction) {
1504 // Action page is before Settings page
1505 context.currentPage++;
1506 }
1507 break;
1508 case INFO_USE_CASE:
1509 // Info page index
1510 context.currentPage = 1;
1511 if (context.home.homeAction) {
1512 // Action page is before Settings and Info pages
1513 context.currentPage++;
1514 }
1515 if (context.home.settingContents) {
1516 // Settings page is before Info pages
1517 context.currentPage++;
1518 }
1519 break;
1520 default:
1521 // Home page index
1522 context.currentPage = 0;
1523 break;
1524 }
1525
1526 context.type = HOME_USE_CASE;
1527 context.nbPages = 2; // Home + Quit
1528 if (context.home.settingContents) {
1529 context.nbPages++;
1530 }
1531 if (context.home.infosList) {
1532 context.nbPages++;
1533 }
1534 if (context.home.homeAction) {
1535 context.nbPages++;
1536 }
1537 displayHomePage(FORWARD_DIRECTION);
1538}
1539
1540static void startUseCaseInfo(void)
1541{
1542 context.type = INFO_USE_CASE;
1543 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1544 context.currentPage = 0;
1545
1546 displayInfoPage(FORWARD_DIRECTION);
1547}
1548
1549static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1550{
1551 nbgl_content_t content = {0};
1552 const nbgl_content_t *p_content = NULL;
1553
1554 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1555 if (context.type != GENERIC_SETTINGS) {
1556 context.type = SETTINGS_USE_CASE;
1557 }
1558
1559 context.nbPages = 1; // For back screen
1560 for (int i = 0; i < context.home.settingContents->nbContents; i++) {
1561 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1562 context.nbPages += getContentNbElement(p_content);
1563 }
1564 context.currentPage = initSettingPage;
1565
1566 displaySettingsPage(FORWARD_DIRECTION, false);
1567}
1568
1569static void startUseCaseSettings(void)
1570{
1571 startUseCaseSettingsAtPage(0);
1572}
1573
1574static void startUseCaseContent(void)
1575{
1576 uint8_t contentIdx = 0;
1577 const nbgl_content_t *p_content = NULL;
1578 nbgl_content_t content = {0};
1579
1580 context.nbPages = 1; // Quit
1581
1582 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1583 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1584 context.nbPages += getContentNbElement(p_content);
1585 }
1586
1587 // Ensure currentPage is valid
1588 if (context.currentPage >= context.nbPages) {
1589 return;
1590 }
1591
1592 displayContent(FORWARD_DIRECTION, false);
1593}
1594
1595// function used to display the current page in home
1596static void displayHomePage(nbgl_stepPosition_t pos)
1597{
1598 const char *text = NULL;
1599 const char *subText = NULL;
1600 const nbgl_icon_details_t *icon = NULL;
1601 uint8_t currentIndex = 0;
1602 uint8_t homeIndex = 255;
1603 uint8_t actionIndex = 255;
1604 uint8_t settingsIndex = 255;
1605 uint8_t infoIndex = 255;
1606
1607 context.stepCallback = NULL;
1608
1609 // Determine which pages are present
1610 homeIndex = currentIndex++;
1611 if (context.home.homeAction) {
1612 actionIndex = currentIndex++;
1613 }
1614 if (context.home.settingContents) {
1615 settingsIndex = currentIndex++;
1616 }
1617 if (context.home.infosList) {
1618 infoIndex = currentIndex++;
1619 }
1620
1621 if (context.currentPage == homeIndex) {
1622 // Home page
1623 icon = context.home.appIcon;
1624 if (context.home.tagline != NULL) {
1625 text = context.home.tagline;
1626 }
1627 else {
1628 text = context.home.appName;
1629 subText = "app is ready";
1630 }
1631 }
1632 else if (context.currentPage == actionIndex) {
1633 // Action page
1634 icon = context.home.homeAction->icon;
1635 text = PIC(context.home.homeAction->text);
1636 context.stepCallback = context.home.homeAction->callback;
1637 }
1638 else if (context.currentPage == settingsIndex) {
1639 // Settings page
1640 icon = &C_icon_coggle;
1641 text = "App settings";
1642 context.stepCallback = startUseCaseSettings;
1643 }
1644 else if (context.currentPage == infoIndex) {
1645 // About page
1646 icon = &C_Information_circle_14px;
1647 text = "App info";
1648 context.stepCallback = startUseCaseInfo;
1649 }
1650 else {
1651 icon = &C_Quit_14px;
1652 text = "Quit app";
1653 context.stepCallback = context.home.quitCallback;
1654 }
1655
1656 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1657 nbgl_refresh();
1658}
1659
1660// function used to display the current page in choice
1661static void displayChoicePage(nbgl_stepPosition_t pos)
1662{
1663 const char *text = NULL;
1664 const char *subText = NULL;
1665 const nbgl_icon_details_t *icon = NULL;
1666 // set to 1 if there is only one page for intro (if either icon or subMessage is NULL)
1667 uint8_t acceptPage = 0;
1668
1669 if (context.choice.message != NULL) {
1670 if ((context.choice.icon == NULL) || (context.choice.subMessage == NULL)) {
1671 acceptPage = 1;
1672 }
1673 else {
1674 acceptPage = 2;
1675 }
1676 }
1677 context.stepCallback = NULL;
1678
1679 if (context.currentPage < acceptPage) {
1680 if (context.currentPage == 0) { // title page
1681 text = context.choice.message;
1682 if (context.choice.icon != NULL) {
1683 icon = context.choice.icon;
1684 }
1685 else {
1686 subText = context.choice.subMessage;
1687 }
1688 }
1689 else if ((acceptPage == 2) && (context.currentPage == 1)) { // sub-title page
1690 // displayed only if there is both icon and submessage
1691 text = context.choice.message;
1692 subText = context.choice.subMessage;
1693 }
1694 }
1695 else if (context.currentPage == acceptPage) { // confirm page
1696 icon = &C_icon_validate_14;
1697 text = context.choice.confirmText;
1698 context.stepCallback = onChoiceAccept;
1699 }
1700 else if (context.currentPage == (acceptPage + 1)) { // cancel page
1701 icon = &C_icon_crossmark;
1702 text = context.choice.cancelText;
1703 context.stepCallback = onChoiceReject;
1704 }
1705 else if (context.choice.details != NULL) {
1706 // only the first level of details and BAR_LIST type are supported
1707 if (context.choice.details->type == BAR_LIST_WARNING) {
1708 text = context.choice.details->barList.texts[context.currentPage - (acceptPage + 2)];
1709 subText
1710 = context.choice.details->barList.subTexts[context.currentPage - (acceptPage + 2)];
1711 }
1712 }
1713
1714 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1715 nbgl_refresh();
1716}
1717
1718// function used to display the Confirm page
1719static void displayConfirm(nbgl_stepPosition_t pos)
1720{
1721 const char *text = NULL;
1722 const char *subText = NULL;
1723 const nbgl_icon_details_t *icon = NULL;
1724
1725 context.stepCallback = NULL;
1726 switch (context.currentPage) {
1727 case 0:
1728 // title page
1729 text = context.confirm.message;
1730 subText = context.confirm.subMessage;
1731 break;
1732 case 1:
1733 // confirm page
1734 icon = &C_icon_validate_14;
1735 text = context.confirm.confirmText;
1736 context.stepCallback = onConfirmAccept;
1737 break;
1738 case 2:
1739 // cancel page
1740 icon = &C_icon_crossmark;
1741 text = context.confirm.cancelText;
1742 context.stepCallback = onConfirmReject;
1743 break;
1744 }
1745
1746 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1747 nbgl_refresh();
1748}
1749
1750// function used to display the current navigable content
1751static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1752{
1753 PageContent_t contentPage = {0};
1754 ForcedType_t forcedType = NO_FORCED_TYPE;
1755
1756 context.stepCallback = NULL;
1757
1758 if (context.currentPage < (context.nbPages - 1)) {
1759 getContentPage(toogle_state, &contentPage);
1760 if (contentPage.isCenteredInfo) {
1761 forcedType = FORCE_CENTERED_INFO;
1762 }
1763 context.forceAction = contentPage.isAction;
1764 }
1765 else { // last page is for quit
1766 if (context.content.rejectText) {
1767 contentPage.text = context.content.rejectText;
1768 }
1769 else {
1770 contentPage.text = "Back";
1771 }
1772 if (context.type == GENERIC_REVIEW_USE_CASE) {
1773 contentPage.icon = &C_icon_crossmark;
1774 }
1775 else {
1776 contentPage.icon = &C_icon_back_x;
1777 }
1778 context.stepCallback = context.content.quitCallback;
1779 }
1780
1781 if (contentPage.isSwitch) {
1782 drawSwitchStep(
1783 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1784 }
1785 else {
1786 drawStep(pos,
1787 contentPage.icon,
1788 contentPage.text,
1789 contentPage.subText,
1790 contentCallback,
1791 false,
1792 forcedType);
1793 }
1794 context.forceAction = false;
1795 nbgl_refresh();
1796}
1797
1798static void displaySpinner(const char *text)
1799{
1800 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1801 nbgl_refresh();
1802}
1803
1804// function to factorize code for all simple reviews
1805static void useCaseReview(ContextType_t type,
1806 nbgl_operationType_t operationType,
1807 const nbgl_contentTagValueList_t *tagValueList,
1808 const nbgl_icon_details_t *icon,
1809 const char *reviewTitle,
1810 const char *reviewSubTitle,
1811 const char *finishTitle,
1812 nbgl_choiceCallback_t choiceCallback)
1813{
1814 memset(&context, 0, sizeof(UseCaseContext_t));
1815 context.type = type;
1816 context.operationType = operationType;
1817 context.review.tagValueList = tagValueList;
1818 context.review.reviewTitle = reviewTitle;
1819 context.review.reviewSubTitle = reviewSubTitle;
1820 context.review.finishTitle = finishTitle;
1821 context.review.icon = icon;
1822 context.review.onChoice = choiceCallback;
1823 context.currentPage = 0;
1824 // 1 page for title and 2 pages at the end for accept/reject
1825 context.nbPages = tagValueList->nbPairs + 3;
1826 if (reviewSubTitle) {
1827 context.nbPages++; // 1 page for subtitle page
1828 }
1829
1830 displayReviewPage(FORWARD_DIRECTION);
1831}
1832
1833#ifdef NBGL_KEYPAD
1834static void setPinCodeText(void)
1835{
1836 bool enableValidate = false;
1837 bool enableBackspace = true;
1838
1839 // pin can be validated when min digits is entered
1840 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1841 // backspace is disabled when no digit is entered and back vallback is not provided
1842 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1843 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1844 context.keypad.hidden,
1845 context.keypad.pinLen,
1846 (const char *) context.keypad.pinEntry);
1848 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1849 nbgl_layoutDraw(context.keypad.layoutCtx);
1850 nbgl_refresh();
1851}
1852
1853// called when a key is touched on the keypad
1854static void keypadCallback(char touchedKey)
1855{
1856 switch (touchedKey) {
1857 case BACKSPACE_KEY:
1858 if (context.keypad.pinLen > 0) {
1859 context.keypad.pinLen--;
1860 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1861 }
1862 else if (context.keypad.backCallback != NULL) {
1863 context.keypad.backCallback();
1864 break;
1865 }
1866 setPinCodeText();
1867 break;
1868
1869 case VALIDATE_KEY:
1870 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1871 break;
1872
1873 default:
1874 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1875 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1876 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1877 context.keypad.pinLen++;
1878 }
1879 setPinCodeText();
1880 }
1881 break;
1882 }
1883}
1884#endif // NBGL_KEYPAD
1885
1886// this is the function called to start the actual review, from the initial warning pages
1887static void launchReviewAfterWarning(void)
1888{
1889 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
1890 useCaseReview(reviewWithWarnCtx.type,
1891 reviewWithWarnCtx.operationType,
1892 reviewWithWarnCtx.tagValueList,
1893 reviewWithWarnCtx.icon,
1894 reviewWithWarnCtx.reviewTitle,
1895 reviewWithWarnCtx.reviewSubTitle,
1896 reviewWithWarnCtx.finishTitle,
1897 reviewWithWarnCtx.choiceCallback);
1898 }
1899 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
1900 displayStreamingReviewPage(FORWARD_DIRECTION);
1901 }
1902}
1903
1904// this is the callback used when navigating in warning pages
1905static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
1906{
1907 UNUSED(stepCtx);
1908
1909 if (event == BUTTON_LEFT_PRESSED) {
1910 // only decrement page if we are not at the first page
1911 if (reviewWithWarnCtx.warningPage > 0) {
1912 reviewWithWarnCtx.warningPage--;
1913 }
1914 }
1915 else if (event == BUTTON_RIGHT_PRESSED) {
1916 // only increment page if not at last page
1917 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
1918 reviewWithWarnCtx.warningPage++;
1919 }
1920 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
1921 && (reviewWithWarnCtx.warning->info != NULL)) {
1922 launchReviewAfterWarning();
1923 return;
1924 }
1925 }
1926 else if ((event == BUTTON_BOTH_PRESSED)
1927 && (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
1928 // if at first page, double press leads to start of review
1929 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
1930 launchReviewAfterWarning();
1931 }
1932 // if at last page, reject operation
1933 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1934 reviewWithWarnCtx.choiceCallback(false);
1935 }
1936 return;
1937 }
1938 else {
1939 return;
1940 }
1941 displayWarningStep();
1942}
1943
1944// function used to display the initial warning pages when starting a "review with warning"
1945static void displayWarningStep(void)
1946{
1947 nbgl_layoutCenteredInfo_t info = {0};
1948 nbgl_stepPosition_t pos = 0;
1949 if ((reviewWithWarnCtx.warning->prelude) && (reviewWithWarnCtx.warningPage == 0)) {
1950 // for prelude, only draw text as a single step
1952 warningNavigate,
1953 NULL,
1954 reviewWithWarnCtx.warning->prelude->title,
1955 reviewWithWarnCtx.warning->prelude->description,
1956 REGULAR_INFO,
1957 false);
1958 nbgl_refresh();
1959 return;
1960 }
1961 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
1962 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
1963 // draw the main warning page
1964 info.icon = &C_icon_warning;
1965 info.text1 = "Blind signing ahead";
1966 info.text2 = "To accept risk, press both buttons";
1967 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
1969 pos |= FORWARD_DIRECTION;
1970 }
1971 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1972 getLastPageInfo(false, &info.icon, &info.text1);
1974 }
1975 }
1976 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
1977 && (reviewWithWarnCtx.warning->info != NULL)) {
1978 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
1979 info.icon = reviewWithWarnCtx.warning->info->icon;
1980 info.text1 = reviewWithWarnCtx.warning->info->title;
1981 info.text2 = reviewWithWarnCtx.warning->info->description;
1982 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
1984 pos |= FORWARD_DIRECTION;
1985 }
1986 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1987 if (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING) {
1988 info.icon = reviewWithWarnCtx.warning->introDetails->centeredInfo.icon;
1989 info.text1 = reviewWithWarnCtx.warning->introDetails->centeredInfo.title;
1990 info.text2 = reviewWithWarnCtx.warning->introDetails->centeredInfo.description;
1992 }
1993 else {
1994 // not supported
1995 return;
1996 }
1997 }
1998 }
1999 else {
2000 // not supported
2001 return;
2002 }
2003 info.style = BOLD_TEXT1_INFO;
2004 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
2005 nbgl_refresh();
2006}
2007
2008// function used to display the initial warning page when starting a "review with warning"
2009static void displayInitialWarning(void)
2010{
2011 // draw the main warning page
2012 reviewWithWarnCtx.warningPage = 0;
2013 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2014 || ((reviewWithWarnCtx.warning->introDetails)
2015 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2016 reviewWithWarnCtx.nbWarningPages = 2;
2017 }
2018 else {
2019 // if no intro details and not Blind Signing warning, only one page
2020 reviewWithWarnCtx.nbWarningPages = 1;
2021 }
2022
2023 reviewWithWarnCtx.firstWarningPage = 0;
2024 displayWarningStep();
2025}
2026
2027// function used to display the prelude page when starting a "review with warning"
2028static void displayPrelude(void)
2029{
2030 // draw the main warning page
2031 reviewWithWarnCtx.warningPage = 0;
2032 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2033 || ((reviewWithWarnCtx.warning->introDetails)
2034 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2035 reviewWithWarnCtx.nbWarningPages = 3;
2036 }
2037 else {
2038 // if no intro details and not Blind Signing warning, only 2 pages
2039 reviewWithWarnCtx.nbWarningPages = 2;
2040 }
2041 reviewWithWarnCtx.firstWarningPage = 1;
2042 displayWarningStep();
2043}
2044
2045/**********************
2046 * GLOBAL FUNCTIONS
2047 **********************/
2048
2060uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs,
2061 const nbgl_contentTagValueList_t *tagValueList,
2062 uint8_t startIndex,
2063 bool *requireSpecificDisplay)
2064{
2065 UNUSED(nbPairs);
2066 UNUSED(tagValueList);
2067 UNUSED(startIndex);
2068 *requireSpecificDisplay = true;
2069 return 1;
2070}
2071
2084uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs,
2085 const nbgl_contentTagValueList_t *tagValueList,
2086 uint8_t startIndex,
2087 bool isSkippable,
2088 bool *requireSpecificDisplay)
2089{
2090 UNUSED(nbPairs);
2091 UNUSED(tagValueList);
2092 UNUSED(startIndex);
2093 UNUSED(isSkippable);
2094 *requireSpecificDisplay = true;
2095 return 1;
2096}
2097
2106uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos,
2107 const nbgl_contentInfoList_t *infosList,
2108 uint8_t startIndex,
2109 bool withNav)
2110{
2111 UNUSED(nbInfos);
2112 UNUSED(infosList);
2113 UNUSED(startIndex);
2114 UNUSED(withNav);
2115 return 1;
2116}
2117
2126uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches,
2127 const nbgl_contentSwitchesList_t *switchesList,
2128 uint8_t startIndex,
2129 bool withNav)
2130{
2131 UNUSED(nbSwitches);
2132 UNUSED(switchesList);
2133 UNUSED(startIndex);
2134 UNUSED(withNav);
2135 return 1;
2136}
2137
2146uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars,
2147 const nbgl_contentBarsList_t *barsList,
2148 uint8_t startIndex,
2149 bool withNav)
2150{
2151 UNUSED(nbBars);
2152 UNUSED(barsList);
2153 UNUSED(startIndex);
2154 UNUSED(withNav);
2155 return 1;
2156}
2157
2166uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices,
2167 const nbgl_contentRadioChoice_t *choicesList,
2168 uint8_t startIndex,
2169 bool withNav)
2170{
2171 UNUSED(nbChoices);
2172 UNUSED(choicesList);
2173 UNUSED(startIndex);
2174 UNUSED(withNav);
2175 return 1;
2176}
2177
2185{
2186 uint8_t nbPages = 0;
2187 uint8_t nbPairs = tagValueList->nbPairs;
2188 uint8_t nbPairsInPage;
2189 uint8_t i = 0;
2190 bool flag;
2191
2192 while (i < tagValueList->nbPairs) {
2193 // upper margin
2194 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2195 i += nbPairsInPage;
2196 nbPairs -= nbPairsInPage;
2197 nbPages++;
2198 }
2199 return nbPages;
2200}
2201
2215void nbgl_useCaseNavigableContent(const char *title,
2216 uint8_t initPage,
2217 uint8_t nbPages,
2218 nbgl_callback_t quitCallback,
2219 nbgl_navCallback_t navCallback,
2220 nbgl_layoutTouchCallback_t controlsCallback)
2221{
2222 memset(&context, 0, sizeof(UseCaseContext_t));
2223 context.type = CONTENT_USE_CASE;
2224 context.currentPage = initPage;
2225 context.content.title = title;
2226 context.content.quitCallback = quitCallback;
2227 context.content.navCallback = navCallback;
2228 context.content.controlsCallback = controlsCallback;
2229 context.content.genericContents.callbackCallNeeded = true;
2230 context.content.genericContents.nbContents = nbPages;
2231
2232 startUseCaseContent();
2233}
2234
2249void nbgl_useCaseHomeAndSettings(const char *appName,
2250 const nbgl_icon_details_t *appIcon,
2251 const char *tagline,
2252 const uint8_t initSettingPage,
2253 const nbgl_genericContents_t *settingContents,
2254 const nbgl_contentInfoList_t *infosList,
2255 const nbgl_homeAction_t *action,
2256 nbgl_callback_t quitCallback)
2257{
2258 memset(&context, 0, sizeof(UseCaseContext_t));
2259 context.home.appName = appName;
2260 context.home.appIcon = appIcon;
2261 context.home.tagline = tagline;
2262 context.home.settingContents = PIC(settingContents);
2263 context.home.infosList = PIC(infosList);
2264 context.home.homeAction = action;
2265 context.home.quitCallback = quitCallback;
2266
2267 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2268 startUseCaseSettingsAtPage(initSettingPage);
2269 }
2270 else {
2271 startUseCaseHome();
2272 }
2273}
2274
2287void nbgl_useCaseGenericSettings(const char *appName,
2288 uint8_t initPage,
2289 const nbgl_genericContents_t *settingContents,
2290 const nbgl_contentInfoList_t *infosList,
2291 nbgl_callback_t quitCallback)
2292{
2293 memset(&context, 0, sizeof(UseCaseContext_t));
2294 context.type = GENERIC_SETTINGS;
2295 context.home.appName = appName;
2296 context.home.settingContents = PIC(settingContents);
2297 context.home.infosList = PIC(infosList);
2298 context.home.quitCallback = quitCallback;
2299
2300 startUseCaseSettingsAtPage(initPage);
2301}
2302
2314void nbgl_useCaseGenericConfiguration(const char *title,
2315 uint8_t initPage,
2316 const nbgl_genericContents_t *contents,
2317 nbgl_callback_t quitCallback)
2318{
2319 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2320}
2321
2336void nbgl_useCaseReview(nbgl_operationType_t operationType,
2337 const nbgl_contentTagValueList_t *tagValueList,
2338 const nbgl_icon_details_t *icon,
2339 const char *reviewTitle,
2340 const char *reviewSubTitle,
2341 const char *finishTitle,
2342 nbgl_choiceCallback_t choiceCallback)
2343{
2344 useCaseReview(REVIEW_USE_CASE,
2345 operationType,
2346 tagValueList,
2347 icon,
2348 reviewTitle,
2349 reviewSubTitle,
2350 finishTitle,
2351 choiceCallback);
2352}
2353
2377 const nbgl_contentTagValueList_t *tagValueList,
2378 const nbgl_icon_details_t *icon,
2379 const char *reviewTitle,
2380 const char *reviewSubTitle,
2381 const char *finishTitle,
2382 const nbgl_tipBox_t *tipBox,
2383 const nbgl_warning_t *warning,
2384 nbgl_choiceCallback_t choiceCallback)
2385{
2386 UNUSED(tipBox);
2387 ContextType_t type = REVIEW_USE_CASE;
2388
2389 // if no warning at all, it's a simple review
2390 if ((warning == NULL)
2391 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2392 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2393 useCaseReview(type,
2394 operationType,
2395 tagValueList,
2396 icon,
2397 reviewTitle,
2398 reviewSubTitle,
2399 finishTitle,
2400 choiceCallback);
2401 return;
2402 }
2403 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2404 operationType |= NO_THREAT_OPERATION;
2405 }
2406 else if (warning->predefinedSet != 0) {
2407 operationType |= RISKY_OPERATION;
2408 }
2409
2410 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2411 reviewWithWarnCtx.type = type;
2412 reviewWithWarnCtx.operationType = operationType;
2413 reviewWithWarnCtx.tagValueList = tagValueList;
2414 reviewWithWarnCtx.icon = icon;
2415 reviewWithWarnCtx.reviewTitle = reviewTitle;
2416 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2417 reviewWithWarnCtx.finishTitle = finishTitle;
2418 reviewWithWarnCtx.warning = warning;
2419 reviewWithWarnCtx.choiceCallback = choiceCallback;
2420
2421 // if the warning contains a prelude, display it first
2422 if (reviewWithWarnCtx.warning->prelude) {
2423 displayPrelude();
2424 }
2425 // display the initial warning only of a risk/threat or blind signing or prelude
2426 else if (HAS_INITIAL_WARNING(warning)) {
2427 displayInitialWarning();
2428 }
2429 else {
2430 useCaseReview(type,
2431 operationType,
2432 tagValueList,
2433 icon,
2434 reviewTitle,
2435 reviewSubTitle,
2436 finishTitle,
2437 choiceCallback);
2438 }
2439}
2440
2461 const nbgl_contentTagValueList_t *tagValueList,
2462 const nbgl_icon_details_t *icon,
2463 const char *reviewTitle,
2464 const char *reviewSubTitle,
2465 const char *finishTitle,
2466 const nbgl_tipBox_t *dummy,
2467 nbgl_choiceCallback_t choiceCallback)
2468{
2469 nbgl_useCaseAdvancedReview(operationType,
2470 tagValueList,
2471 icon,
2472 reviewTitle,
2473 reviewSubTitle,
2474 finishTitle,
2475 dummy,
2476 &blindSigningWarning,
2477 choiceCallback);
2478}
2479
2495 const nbgl_contentTagValueList_t *tagValueList,
2496 const nbgl_icon_details_t *icon,
2497 const char *reviewTitle,
2498 const char *reviewSubTitle,
2499 const char *finishTitle,
2500 nbgl_choiceCallback_t choiceCallback)
2501{
2502 nbgl_useCaseReview(operationType,
2503 tagValueList,
2504 icon,
2505 reviewTitle,
2506 reviewSubTitle,
2507 finishTitle,
2508 choiceCallback);
2509}
2510
2527void nbgl_useCaseAddressReview(const char *address,
2528 const nbgl_contentTagValueList_t *additionalTagValueList,
2529 const nbgl_icon_details_t *icon,
2530 const char *reviewTitle,
2531 const char *reviewSubTitle,
2532 nbgl_choiceCallback_t choiceCallback)
2533{
2534 memset(&context, 0, sizeof(UseCaseContext_t));
2535 context.type = ADDRESS_REVIEW_USE_CASE;
2536 context.review.address = address;
2537 context.review.reviewTitle = reviewTitle;
2538 context.review.reviewSubTitle = reviewSubTitle;
2539 context.review.icon = icon;
2540 context.review.onChoice = choiceCallback;
2541 context.currentPage = 0;
2542 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2543 // + 1 if sub Title
2544 context.nbPages = reviewSubTitle ? 5 : 4;
2545 if (additionalTagValueList) {
2546 context.review.tagValueList = PIC(additionalTagValueList);
2547 context.nbPages += additionalTagValueList->nbPairs;
2548 }
2549
2550 displayReviewPage(FORWARD_DIRECTION);
2551}
2552
2562 const char *rejectText,
2563 nbgl_callback_t rejectCallback)
2564{
2565 memset(&context, 0, sizeof(UseCaseContext_t));
2566 context.type = GENERIC_REVIEW_USE_CASE;
2567 context.content.rejectText = rejectText;
2568 context.content.quitCallback = rejectCallback;
2569 context.content.genericContents.nbContents = contents->nbContents;
2570 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2571 if (contents->callbackCallNeeded) {
2572 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2573 }
2574 else {
2575 context.content.genericContents.contentsList = PIC(contents->contentsList);
2576 }
2577
2578 startUseCaseContent();
2579}
2580
2589void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2590{
2591 UNUSED(isSuccess);
2592 memset(&context, 0, sizeof(UseCaseContext_t));
2593 context.type = STATUS_USE_CASE;
2594 context.stepCallback = quitCallback;
2595 context.currentPage = 0;
2596 context.nbPages = 1;
2597
2599 NULL,
2600 message,
2601 NULL,
2602 statusButtonCallback,
2603 false,
2604 NO_FORCED_TYPE);
2605}
2606
2614 nbgl_callback_t quitCallback)
2615{
2616 const char *msg;
2617 bool isSuccess;
2618 switch (reviewStatusType) {
2620 msg = "Operation signed";
2621 isSuccess = true;
2622 break;
2624 msg = "Operation rejected";
2625 isSuccess = false;
2626 break;
2628 msg = "Transaction signed";
2629 isSuccess = true;
2630 break;
2632 msg = "Transaction rejected";
2633 isSuccess = false;
2634 break;
2636 msg = "Message signed";
2637 isSuccess = true;
2638 break;
2640 msg = "Message rejected";
2641 isSuccess = false;
2642 break;
2644 msg = "Address verified";
2645 isSuccess = true;
2646 break;
2648 msg = "Address verification cancelled";
2649 isSuccess = false;
2650 break;
2651 default:
2652 return;
2653 }
2654 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2655}
2656
2670 const nbgl_icon_details_t *icon,
2671 const char *reviewTitle,
2672 const char *reviewSubTitle,
2673 nbgl_choiceCallback_t choiceCallback)
2674{
2675 // memorize streaming operation type for future API calls
2676 streamingOpType = operationType;
2677
2678 memset(&context, 0, sizeof(UseCaseContext_t));
2679 context.type = STREAMING_START_REVIEW_USE_CASE;
2680 context.operationType = operationType;
2681 context.review.reviewTitle = reviewTitle;
2682 context.review.reviewSubTitle = reviewSubTitle;
2683 context.review.icon = icon;
2684 context.review.onChoice = choiceCallback;
2685 context.currentPage = 0;
2686 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2687
2688 displayStreamingReviewPage(FORWARD_DIRECTION);
2689}
2690
2705 const nbgl_icon_details_t *icon,
2706 const char *reviewTitle,
2707 const char *reviewSubTitle,
2708 nbgl_choiceCallback_t choiceCallback)
2709{
2711 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2712}
2713
2730 const nbgl_icon_details_t *icon,
2731 const char *reviewTitle,
2732 const char *reviewSubTitle,
2733 const nbgl_warning_t *warning,
2734 nbgl_choiceCallback_t choiceCallback)
2735{
2736 memset(&context, 0, sizeof(UseCaseContext_t));
2737 context.type = STREAMING_START_REVIEW_USE_CASE;
2738 context.operationType = operationType;
2739 context.review.reviewTitle = reviewTitle;
2740 context.review.reviewSubTitle = reviewSubTitle;
2741 context.review.icon = icon;
2742 context.review.onChoice = choiceCallback;
2743 context.currentPage = 0;
2744 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2745
2746 // memorize streaming operation type for future API calls
2747 streamingOpType = operationType;
2748
2749 // if no warning at all, it's a simple review
2750 if ((warning == NULL)
2751 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2752 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2753 displayStreamingReviewPage(FORWARD_DIRECTION);
2754 return;
2755 }
2756 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2757 operationType |= NO_THREAT_OPERATION;
2758 }
2759 else if (warning->predefinedSet != 0) {
2760 operationType |= RISKY_OPERATION;
2761 }
2762 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2763
2764 reviewWithWarnCtx.type = context.type;
2765 reviewWithWarnCtx.operationType = operationType;
2766 reviewWithWarnCtx.icon = icon;
2767 reviewWithWarnCtx.reviewTitle = reviewTitle;
2768 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2769 reviewWithWarnCtx.choiceCallback = choiceCallback;
2770 reviewWithWarnCtx.warning = warning;
2771
2772 // if the warning contains a prelude, display it first
2773 if (reviewWithWarnCtx.warning->prelude) {
2774 displayPrelude();
2775 }
2776 // display the initial warning only of a risk/threat or blind signing or prelude
2777 else if (HAS_INITIAL_WARNING(warning)) {
2778 displayInitialWarning();
2779 }
2780 else {
2781 displayStreamingReviewPage(FORWARD_DIRECTION);
2782 }
2783}
2784
2799 nbgl_choiceCallback_t choiceCallback,
2800 nbgl_callback_t skipCallback)
2801{
2802 uint8_t curNbDataSets = context.review.nbDataSets;
2803
2804 memset(&context, 0, sizeof(UseCaseContext_t));
2805 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
2806 context.operationType = streamingOpType;
2807 context.review.tagValueList = tagValueList;
2808 context.review.onChoice = choiceCallback;
2809 context.currentPage = 0;
2810 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
2811 context.review.skipCallback = skipCallback;
2812 context.review.nbDataSets = curNbDataSets + 1;
2813
2814 displayStreamingReviewPage(FORWARD_DIRECTION);
2815}
2816
2828 nbgl_choiceCallback_t choiceCallback)
2829{
2830 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
2831}
2832
2833void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
2834 nbgl_choiceCallback_t choiceCallback)
2835{
2836 memset(&context, 0, sizeof(UseCaseContext_t));
2837 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
2838 context.operationType = streamingOpType;
2839 context.review.onChoice = choiceCallback;
2840 context.review.finishTitle = finishTitle;
2841 context.currentPage = 0;
2842 context.nbPages = 2; // 2 pages at the end for accept/reject
2843
2844 displayStreamingReviewPage(FORWARD_DIRECTION);
2845}
2846
2852void nbgl_useCaseSpinner(const char *text)
2853{
2854 memset(&context, 0, sizeof(UseCaseContext_t));
2855 context.type = SPINNER_USE_CASE;
2856 context.currentPage = 0;
2857 context.nbPages = 1;
2858
2859 displaySpinner(text);
2860}
2861
2875 const char *message,
2876 const char *subMessage,
2877 const char *confirmText,
2878 const char *cancelText,
2879 nbgl_choiceCallback_t callback)
2880{
2882 icon, message, subMessage, confirmText, cancelText, NULL, callback);
2883};
2884
2900 const char *message,
2901 const char *subMessage,
2902 const char *confirmText,
2903 const char *cancelText,
2904 nbgl_genericDetails_t *details,
2905 nbgl_choiceCallback_t callback)
2906{
2907 memset(&context, 0, sizeof(UseCaseContext_t));
2908 context.type = CHOICE_USE_CASE;
2909 context.choice.icon = icon;
2910 context.choice.message = message;
2911 context.choice.subMessage = subMessage;
2912 context.choice.confirmText = confirmText;
2913 context.choice.cancelText = cancelText;
2914 context.choice.onChoice = callback;
2915 context.choice.details = details;
2916 context.currentPage = 0;
2917 context.nbPages = 2; // 2 pages for confirm/cancel
2918 if (message != NULL) {
2919 context.nbPages++;
2920 // if both icon and subMessage are non NULL, add a page
2921 if ((icon != NULL) && (subMessage != NULL)) {
2922 context.nbPages++;
2923 }
2924 }
2925 if (details != NULL) {
2926 // only the first level of details and BAR_LIST type are supported
2927 if (details->type == BAR_LIST_WARNING) {
2928 context.nbPages += details->barList.nbBars;
2929 }
2930 }
2931
2932 displayChoicePage(FORWARD_DIRECTION);
2933};
2934
2948void nbgl_useCaseConfirm(const char *message,
2949 const char *subMessage,
2950 const char *confirmText,
2951 const char *cancelText,
2952 nbgl_callback_t callback)
2953{
2954 memset(&context, 0, sizeof(UseCaseContext_t));
2955 context.type = CONFIRM_USE_CASE;
2956 context.confirm.message = message;
2957 context.confirm.subMessage = subMessage;
2958 context.confirm.confirmText = confirmText;
2959 context.confirm.cancelText = cancelText;
2960 context.confirm.onConfirm = callback;
2961 context.currentPage = 0;
2962 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
2963
2964 displayConfirm(FORWARD_DIRECTION);
2965}
2966
2977 const char *message,
2978 const char *actionText,
2979 nbgl_callback_t callback)
2980{
2981 nbgl_layoutCenteredInfo_t centeredInfo = {0};
2982
2983 UNUSED(actionText);
2984
2985 // memorize callback in context
2986 memset(&context, 0, sizeof(UseCaseContext_t));
2987 context.type = ACTION_USE_CASE;
2988 context.action.actionCallback = callback;
2989
2990 centeredInfo.icon = icon;
2991 centeredInfo.text1 = message;
2992 centeredInfo.style = BOLD_TEXT1_INFO;
2993 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
2994}
2995
2996#ifdef NBGL_KEYPAD
3015void nbgl_useCaseKeypad(const char *title,
3016 uint8_t minDigits,
3017 uint8_t maxDigits,
3018 bool shuffled,
3019 bool hidden,
3020 nbgl_pinValidCallback_t validatePinCallback,
3021 nbgl_callback_t backCallback)
3022{
3023 nbgl_layoutDescription_t layoutDescription = {0};
3024 int status = -1;
3025
3026 // reset the keypad context
3027 memset(&context, 0, sizeof(KeypadContext_t));
3028 context.type = KEYPAD_USE_CASE;
3029 context.currentPage = 0;
3030 context.nbPages = 1;
3031 context.keypad.validatePin = validatePinCallback;
3032 context.keypad.backCallback = backCallback;
3033 context.keypad.pinMinDigits = minDigits;
3034 context.keypad.pinMaxDigits = maxDigits;
3035 context.keypad.hidden = hidden;
3036 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
3037
3038 // add keypad
3039 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
3040 if (status < 0) {
3041 return;
3042 }
3043 context.keypad.keypadIndex = status;
3044 // add digits
3045 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
3046 if (status < 0) {
3047 return;
3048 }
3049
3050 nbgl_layoutDraw(context.keypad.layoutCtx);
3051 if (context.keypad.backCallback != NULL) {
3052 // force backspace to be visible at first digit, to be used as quit
3053 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
3054 }
3055 nbgl_refresh();
3056}
3057#endif // NBGL_KEYPAD
3058
3059#endif // HAVE_SE_TOUCH
3060#endif // NBGL_USE_CASE
nbgl_contentCenteredInfoStyle_t
possible styles for Centered Info Area
@ CHOICES_LIST
list of choices through radio buttons
@ CENTERED_INFO
a centered info
@ SWITCHES_LIST
list of switches with descriptions
@ INFOS_LIST
list of infos with titles
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
@ TAG_VALUE_LIST
list of tag/value pairs
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
@ INFO_BUTTON
a centered info and a simple black button
@ INFO_LIST_ALIAS
alias is list of infos
@ TAG_VALUE_LIST_ALIAS
@ ENS_ALIAS
alias comes from ENS
void(* nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
prototype of function to be called when an action on a content object occurs
debug traces management
#define LOG_WARN(__logger,...)
Definition nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition nbgl_debug.h:35
void(* nbgl_stepCallback_t)(void)
prototype of function to be called when a step is using a callback on "double-key" action
Definition nbgl_flow.h:38
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
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)
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)
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)
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
uint8_t confirmationToken
the token used as argument of the onActionCallback
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
This structure contains a list of [tag,value] pairs.
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair and possible extensions.
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
const nbgl_icon_details_t * valueIcon
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
const char * value
string giving the value name
const char * item
string giving the tag name
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
This structure contains data to build a content.
nbgl_content_u content
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
nbgl_contentType_t type
type of page content in the content union
uint8_t nbBars
number of touchable bars
uint8_t nbContents
number of contents
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
nbgl_contentCallback_t contentGetterCallback
function to call to retrieve a given content
The necessary parameters to build the page(s) displayed when the top-right button is touched in intro...
nbgl_genericBarList_t barList
touchable bars list, if type == BAR_LIST_WARNING
nbgl_genericDetailsType_t type
type of content in the page, determining what to use in the following union
Structure describing the action button in Home Screen.
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:67
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:65
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:68
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:66
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition nbgl_flow.h:63
nbgl_contentType_t type
type of page content in the following union
Definition nbgl_flow.h:59
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition nbgl_flow.h:61
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition nbgl_flow.h:64
This structure contains data to build a SWITCHES_LIST content.
uint8_t nbSwitches
number of elements in switches and tokens array
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
The necessary parameters to build a warning page preceding a review. One can either use predefinedSet...
const nbgl_preludeDetails_t * prelude
if not null, means that the review can start by a prelude
const nbgl_warningDetails_t * reviewDetails
uint32_t predefinedSet
const nbgl_contentCenter_t * info
parameters to build the intro warning page, if not using pre-defined
nbgl_contentInfoList_t infosList
INFOS_LIST type
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
nbgl_contentBarsList_t barsList
BARS_LIST type
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type