Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_use_case_nanos.c
Go to the documentation of this file.
1
6#ifdef NBGL_USE_CASE
7#ifndef HAVE_SE_TOUCH
8/*********************
9 * INCLUDES
10 *********************/
11#include <string.h>
12#include <stdio.h>
13#include "nbgl_debug.h"
14#include "nbgl_use_case.h"
15#include "glyphs.h"
16#include "os_pic.h"
17#include "os_helpers.h"
18#include "ux.h"
19
20/*********************
21 * DEFINES
22 *********************/
23#define WITH_HORIZONTAL_CHOICES_LIST
24#define WITH_HORIZONTAL_BARS_LIST
25
31#define RISKY_OPERATION (1 << 6)
32
38#define NO_THREAT_OPERATION (1 << 7)
39
40/**********************
41 * TYPEDEFS
42 **********************/
43
44typedef struct ReviewContext_s {
45 nbgl_choiceCallback_t onChoice;
46 const nbgl_contentTagValueList_t *tagValueList;
47 const nbgl_icon_details_t *icon;
48 const char *reviewTitle;
49 const char *reviewSubTitle;
50 const char *finishTitle;
51 const char *address; // for address confirmation review
52 nbgl_callback_t skipCallback; // callback provided by used
53 uint8_t nbDataSets; // number of sets of data received by StreamingContinue
54 bool skipDisplay; // if set to true, means that we are displaying the skip page
55 uint8_t dataDirection; // used to know whether the skip page is reached from back or forward
56 uint8_t currentTagValueIndex;
57 uint8_t currentExtensionPage;
58 uint8_t nbExtensionPages;
59 const nbgl_contentValueExt_t *extension;
60 nbgl_step_t extensionStepCtx;
61
62} ReviewContext_t;
63
64typedef struct ChoiceContext_s {
65 const nbgl_icon_details_t *icon;
66 const char *message;
67 const char *subMessage;
68 const char *confirmText;
69 const char *cancelText;
70 nbgl_choiceCallback_t onChoice;
71 const nbgl_genericDetails_t *details;
72} ChoiceContext_t;
73
74typedef struct ConfirmContext_s {
75 const char *message;
76 const char *subMessage;
77 const char *confirmText;
78 const char *cancelText;
79 nbgl_callback_t onConfirm;
80 nbgl_step_t currentStep;
81} ConfirmContext_t;
82
83typedef struct ContentContext_s {
84 const char *title; // For CHOICES_LIST /BARS_LIST
85 nbgl_genericContents_t genericContents;
86 const char *rejectText;
87 nbgl_layoutTouchCallback_t controlsCallback;
88 nbgl_navCallback_t navCallback;
89 nbgl_callback_t quitCallback;
90} ContentContext_t;
91
92typedef struct HomeContext_s {
93 const char *appName;
94 const nbgl_icon_details_t *appIcon;
95 const char *tagline;
96 const nbgl_genericContents_t *settingContents;
97 const nbgl_contentInfoList_t *infosList;
98 const nbgl_homeAction_t *homeAction;
99 nbgl_callback_t quitCallback;
100} HomeContext_t;
101
102typedef struct ActionContext_s {
103 nbgl_callback_t actionCallback;
104} ActionContext_t;
105
106#ifdef NBGL_KEYPAD
107typedef struct KeypadContext_s {
108 uint8_t pinEntry[8];
109 uint8_t pinLen;
110 uint8_t pinMinDigits;
111 uint8_t pinMaxDigits;
112 nbgl_layout_t *layoutCtx;
113 bool hidden;
114 uint8_t keypadIndex;
115 nbgl_pinValidCallback_t validatePin;
116 nbgl_callback_t backCallback;
117} KeypadContext_t;
118#endif
119
120typedef enum {
121 NONE_USE_CASE,
122 SPINNER_USE_CASE,
123 REVIEW_USE_CASE,
124 GENERIC_REVIEW_USE_CASE,
125 ADDRESS_REVIEW_USE_CASE,
126 STREAMING_START_REVIEW_USE_CASE,
127 STREAMING_CONTINUE_REVIEW_USE_CASE,
128 STREAMING_FINISH_REVIEW_USE_CASE,
129 CHOICE_USE_CASE,
130 STATUS_USE_CASE,
131 CONFIRM_USE_CASE,
132 KEYPAD_USE_CASE,
133 HOME_USE_CASE,
134 INFO_USE_CASE,
135 SETTINGS_USE_CASE,
136 GENERIC_SETTINGS,
137 CONTENT_USE_CASE,
138 ACTION_USE_CASE
139} ContextType_t;
140
141typedef struct UseCaseContext_s {
142 ContextType_t type;
143 nbgl_operationType_t operationType;
144 uint8_t nbPages;
145 int8_t currentPage;
146 int8_t firstPairPage;
148 stepCallback;
149 union {
150 ReviewContext_t review;
151 ChoiceContext_t choice;
152 ConfirmContext_t confirm;
153 HomeContext_t home;
154 ContentContext_t content;
155#ifdef NBGL_KEYPAD
156 KeypadContext_t keypad;
157#endif
158 ActionContext_t action;
159 };
160} UseCaseContext_t;
161
162typedef struct PageContent_s {
163 bool isSwitch;
164 const char *text;
165 const char *subText;
166 const nbgl_icon_details_t *icon;
167 const nbgl_contentValueExt_t *extension;
168 nbgl_state_t state;
169 bool isCenteredInfo;
170} PageContent_t;
171
172typedef struct ReviewWithWarningContext_s {
173 ContextType_t type;
174 nbgl_operationType_t operationType;
175 const nbgl_contentTagValueList_t *tagValueList;
176 const nbgl_icon_details_t *icon;
177 const char *reviewTitle;
178 const char *reviewSubTitle;
179 const char *finishTitle;
180 const nbgl_warning_t *warning;
181 nbgl_choiceCallback_t choiceCallback;
182 uint8_t securityReportLevel; // level 1 is the first level of menus
183 bool isIntro; // set to true during intro (before actual review)
184 uint8_t warningPage;
185 uint8_t nbWarningPages;
186} ReviewWithWarningContext_t;
187
188typedef enum {
189 NO_FORCED_TYPE = 0,
190 FORCE_BUTTON,
191 FORCE_CENTERED_INFO
192} ForcedType_t;
193
194/**********************
195 * STATIC VARIABLES
196 **********************/
197static UseCaseContext_t context;
198
199static ReviewWithWarningContext_t reviewWithWarnCtx;
200// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
201static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
202
203// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
204nbgl_operationType_t streamingOpType;
205
206/**********************
207 * STATIC FUNCTIONS
208 **********************/
209static void displayReviewPage(nbgl_stepPosition_t pos);
210static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
211static void displayHomePage(nbgl_stepPosition_t pos);
212static void displayInfoPage(nbgl_stepPosition_t pos);
213static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
214static void displayChoicePage(nbgl_stepPosition_t pos);
215static void displayConfirm(nbgl_stepPosition_t pos);
216static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
217static void displaySpinner(const char *text);
218
219static void startUseCaseHome(void);
220static void startUseCaseInfo(void);
221static void startUseCaseSettings(void);
222static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
223static void startUseCaseContent(void);
224
225static void statusTickerCallback(void);
226
227static void displayExtensionStep(nbgl_stepPosition_t pos);
228static void displayWarningStep(void);
229
230// Simple helper to get the number of elements inside a nbgl_content_t
231static uint8_t getContentNbElement(const nbgl_content_t *content)
232{
233 switch (content->type) {
234 case CENTERED_INFO:
235 return 1;
236 case INFO_BUTTON:
237 return 1;
238 case TAG_VALUE_LIST:
239 return content->content.tagValueList.nbPairs;
240 case SWITCHES_LIST:
241 return content->content.switchesList.nbSwitches;
242 case INFOS_LIST:
243 return content->content.infosList.nbInfos;
244 case CHOICES_LIST:
245 return content->content.choicesList.nbChoices;
246 case BARS_LIST:
247 return content->content.barsList.nbBars;
248 default:
249 return 0;
250 }
251}
252
253// Helper to retrieve the content inside a nbgl_genericContents_t using
254// either the contentsList or using the contentGetterCallback
255static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
256 int8_t contentIdx,
257 nbgl_content_t *content)
258{
259 nbgl_pageContent_t pageContent = {0};
260 if (contentIdx < 0 || contentIdx >= genericContents->nbContents) {
261 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
262 return NULL;
263 }
264
265 if (genericContents->callbackCallNeeded) {
266 if (content == NULL) {
267 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
268 return NULL;
269 }
270 // Retrieve content through callback, but first memset the content.
271 memset(content, 0, sizeof(nbgl_content_t));
272 if (context.content.navCallback) {
273 if (context.content.navCallback(contentIdx, &pageContent) == true) {
274 // Copy the Page Content to the Content variable
275 content->type = pageContent.type;
276 switch (content->type) {
277 case CENTERED_INFO:
278 content->content.centeredInfo = pageContent.centeredInfo;
279 break;
280 case INFO_BUTTON:
281 content->content.infoButton = pageContent.infoButton;
282 break;
283 case TAG_VALUE_LIST:
284 content->content.tagValueList = pageContent.tagValueList;
285 break;
286 case SWITCHES_LIST:
287 content->content.switchesList = pageContent.switchesList;
288 break;
289 case INFOS_LIST:
290 content->content.infosList = pageContent.infosList;
291 break;
292 case CHOICES_LIST:
293 content->content.choicesList = pageContent.choicesList;
294 break;
295 case BARS_LIST:
296 content->content.barsList = pageContent.barsList;
297 break;
298 default:
299 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
300 return NULL;
301 }
302 }
303 else {
304 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
305 return NULL;
306 }
307 }
308 else {
309 genericContents->contentGetterCallback(contentIdx, content);
310 }
311 return content;
312 }
313 else {
314 // Retrieve content through list
315 return PIC(&genericContents->contentsList[contentIdx]);
316 }
317}
318
319// Helper to retrieve the content inside a nbgl_genericContents_t using
320// either the contentsList or using the contentGetterCallback
321static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
322 uint8_t *elemContentIdx,
323 nbgl_content_t *content)
324{
325 const nbgl_genericContents_t *genericContents = NULL;
326 const nbgl_content_t *p_content = NULL;
327 uint8_t nbPages = 0;
328 uint8_t elemNbPages = 0;
329
330 switch (context.type) {
331 case SETTINGS_USE_CASE:
332 case HOME_USE_CASE:
333 case GENERIC_SETTINGS:
334 genericContents = context.home.settingContents;
335 break;
336 case CONTENT_USE_CASE:
337 case GENERIC_REVIEW_USE_CASE:
338 genericContents = &context.content.genericContents;
339 break;
340 default:
341 return NULL;
342 }
343 for (int i = 0; i < genericContents->nbContents; i++) {
344 p_content = getContentAtIdx(genericContents, i, content);
345 elemNbPages = getContentNbElement(p_content);
346 if (nbPages + elemNbPages > elemIdx) {
347 *elemContentIdx = context.currentPage - nbPages;
348 break;
349 }
350 nbPages += elemNbPages;
351 }
352
353 return p_content;
354}
355
356static const char *getChoiceName(uint8_t choiceIndex)
357{
358 uint8_t elemIdx;
359 uint8_t nbValues;
360 const nbgl_content_t *p_content = NULL;
361 nbgl_content_t content = {0};
362 nbgl_contentRadioChoice_t *contentChoices = NULL;
363 nbgl_contentBarsList_t *contentBars = NULL;
364 char **names = NULL;
365
366 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
367 if (p_content == NULL) {
368 return NULL;
369 }
370 switch (p_content->type) {
371 case CHOICES_LIST:
372 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
373 names = (char **) PIC(contentChoices->names);
374 nbValues = contentChoices->nbChoices;
375 break;
376 case BARS_LIST:
377 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
378 names = (char **) PIC(contentBars->barTexts);
379 nbValues = contentBars->nbBars;
380 break;
381 default:
382 // Not supported as vertical MenuList
383 return NULL;
384 }
385 if (choiceIndex >= nbValues) {
386 // Last item is always "Back" button
387 return "Back";
388 }
389 return (const char *) PIC(names[choiceIndex]);
390}
391
392static void onChoiceSelected(uint8_t choiceIndex)
393{
394 uint8_t elemIdx;
395 uint8_t token = 255;
396 const nbgl_content_t *p_content = NULL;
397 nbgl_content_t content = {0};
398 nbgl_contentRadioChoice_t *contentChoices = NULL;
399 nbgl_contentBarsList_t *contentBars = NULL;
400
401 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
402 if (p_content == NULL) {
403 return;
404 }
405 switch (p_content->type) {
406 case CHOICES_LIST:
407 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
408 if (choiceIndex < contentChoices->nbChoices) {
409 token = contentChoices->token;
410 }
411 break;
412 case BARS_LIST:
413 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
414 if (choiceIndex < contentBars->nbBars) {
415 token = contentBars->tokens[choiceIndex];
416 }
417 break;
418 default:
419 // Not supported as vertical MenuList
420 break;
421 }
422 if ((token != 255) && (context.content.controlsCallback != NULL)) {
423 context.content.controlsCallback(token, 0);
424 }
425 else if (context.content.quitCallback != NULL) {
426 context.content.quitCallback();
427 }
428}
429
430static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
431 uint8_t index,
432 const char **item,
433 const char **value,
434 const nbgl_contentValueExt_t **extension,
435 const nbgl_icon_details_t **icon,
436 bool *isCenteredInfo)
437{
438 const nbgl_contentTagValue_t *pair;
439
440 if (tagValueList->pairs != NULL) {
441 pair = PIC(&tagValueList->pairs[index]);
442 }
443 else {
444 pair = PIC(tagValueList->callback(index));
445 }
446 *item = pair->item;
447 *value = pair->value;
448 if (pair->aliasValue) {
449 *extension = pair->extension;
450 }
451 else if (pair->centeredInfo) {
452 *isCenteredInfo = true;
453 *icon = pair->valueIcon;
454 }
455 else {
456 *extension = NULL;
457 }
458}
459
460static void onReviewAccept(void)
461{
462 if (context.review.onChoice) {
463 context.review.onChoice(true);
464 }
465}
466
467static void onReviewReject(void)
468{
469 if (context.review.onChoice) {
470 context.review.onChoice(false);
471 }
472}
473
474static void onChoiceAccept(void)
475{
476 if (context.choice.onChoice) {
477 context.choice.onChoice(true);
478 }
479}
480
481static void onChoiceReject(void)
482{
483 if (context.choice.onChoice) {
484 context.choice.onChoice(false);
485 }
486}
487
488static void onConfirmAccept(void)
489{
490 if (context.confirm.currentStep) {
491 nbgl_stepRelease(context.confirm.currentStep);
492 }
493 if (context.confirm.onConfirm) {
494 context.confirm.onConfirm();
495 }
496}
497
498static void onConfirmReject(void)
499{
500 if (context.confirm.currentStep) {
501 nbgl_stepRelease(context.confirm.currentStep);
503 nbgl_refresh();
504 }
505}
506
507static void onSwitchAction(void)
508{
509 const nbgl_contentSwitch_t *contentSwitch = NULL;
510 const nbgl_content_t *p_content = NULL;
511 nbgl_content_t content = {0};
512 uint8_t elemIdx;
513
514 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
515 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
516 return;
517 }
518 contentSwitch
519 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
520 switch (context.type) {
521 case SETTINGS_USE_CASE:
522 case HOME_USE_CASE:
523 case GENERIC_SETTINGS:
524 displaySettingsPage(FORWARD_DIRECTION, true);
525 break;
526 case CONTENT_USE_CASE:
527 case GENERIC_REVIEW_USE_CASE:
528 displayContent(FORWARD_DIRECTION, true);
529 break;
530 default:
531 break;
532 }
533 if (p_content->contentActionCallback != NULL) {
534 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
535 actionCallback(contentSwitch->token,
536 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
537 context.currentPage);
538 }
539 else if (context.content.controlsCallback != NULL) {
540 context.content.controlsCallback(contentSwitch->token, 0);
541 }
542}
543
544static void drawStep(nbgl_stepPosition_t pos,
545 const nbgl_icon_details_t *icon,
546 const char *txt,
547 const char *subTxt,
548 nbgl_stepButtonCallback_t onActionCallback,
549 bool modal,
550 ForcedType_t forcedType)
551{
552 uint8_t elemIdx;
553 nbgl_step_t newStep = NULL;
554 const nbgl_content_t *p_content = NULL;
555 nbgl_content_t content = {0};
556 nbgl_contentRadioChoice_t *contentChoices = NULL;
557 nbgl_contentBarsList_t *contentBars = NULL;
558 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
559 nbgl_layoutMenuList_t list = {0};
560 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
561 .tickerIntervale = 0, // not periodic
562 .tickerValue = STATUS_SCREEN_DURATION};
563
564 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
565 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
566 // (except the first set) because the set starts with a "skip" page
567 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
568 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
569 pos |= LAST_STEP;
570 }
571 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
572 p_ticker = &ticker;
573 }
574 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
575 nbgl_stepRelease(context.confirm.currentStep);
576 }
577
578 if (txt == NULL) {
579 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
580 if (p_content) {
581 switch (p_content->type) {
582 case CHOICES_LIST:
583 contentChoices
584 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
585 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
586 list.selectedChoice = contentChoices->initChoice;
587 list.callback = getChoiceName;
588 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
589 break;
590 case BARS_LIST:
591 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
592 list.nbChoices = contentBars->nbBars + 1; // For Back button
593 list.selectedChoice = 0;
594 list.callback = getChoiceName;
595 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
596 break;
597 default:
598 // Not supported as vertical MenuList
599 break;
600 }
601 }
602 }
603 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
605 if (subTxt != NULL) {
606 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
607 }
608 else {
609 style = REGULAR_INFO;
610 }
611 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
612 }
613 else {
615 info.icon = icon;
616 info.text1 = txt;
617 info.text2 = subTxt;
618 info.onTop = false;
619 if ((subTxt != NULL) || (context.stepCallback != NULL)) {
620 info.style = BOLD_TEXT1_INFO;
621 }
622 else {
623 info.style = REGULAR_INFO;
624 }
625 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
626 }
627 if (context.type == CONFIRM_USE_CASE) {
628 context.confirm.currentStep = newStep;
629 }
630}
631
632static void drawSwitchStep(nbgl_stepPosition_t pos,
633 const char *title,
634 const char *description,
635 bool state,
636 nbgl_stepButtonCallback_t onActionCallback,
637 bool modal)
638{
639 nbgl_layoutSwitch_t switchInfo;
640
641 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
642 switchInfo.initState = state;
643 switchInfo.text = title;
644 switchInfo.subText = description;
645 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
646}
647
648static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
649{
650 uint8_t elemIdx;
651 uint8_t token = 0;
652 uint8_t index = 0;
653 const nbgl_content_t *p_content = NULL;
654 nbgl_content_t content = {0};
655
656 if (event == BUTTON_LEFT_PRESSED) {
657 if (context.currentPage > 0) {
658 context.currentPage--;
659 }
660 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
661 // the first set
662 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
663 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
664 // Drop the event
665 return false;
666 }
667 *pos = BACKWARD_DIRECTION;
668 }
669 else if (event == BUTTON_RIGHT_PRESSED) {
670 if (context.currentPage < (int) (context.nbPages - 1)) {
671 context.currentPage++;
672 }
673 else {
674 // Drop the event
675 return false;
676 }
677 *pos = FORWARD_DIRECTION;
678 }
679 else {
680 if (event == BUTTON_BOTH_PRESSED) {
681 if (context.stepCallback != NULL) {
682 context.stepCallback();
683 }
684 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
685 || (context.type == GENERIC_SETTINGS)
686 || (context.type == GENERIC_REVIEW_USE_CASE)) {
687 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
688 if (p_content != NULL) {
689 switch (p_content->type) {
690 case CENTERED_INFO:
691 // No associated callback
692 return false;
693 case INFO_BUTTON:
694 token = p_content->content.infoButton.buttonToken;
695 break;
696 case SWITCHES_LIST:
697 token = p_content->content.switchesList.switches->token;
698 break;
699 case BARS_LIST:
700 token = p_content->content.barsList.tokens[context.currentPage];
701 break;
702 case CHOICES_LIST:
703 token = p_content->content.choicesList.token;
704 index = context.currentPage;
705 break;
706 default:
707 break;
708 }
709
710 if ((p_content) && (p_content->contentActionCallback != NULL)) {
711 p_content->contentActionCallback(token, 0, context.currentPage);
712 }
713 else if (context.content.controlsCallback != NULL) {
714 context.content.controlsCallback(token, index);
715 }
716 }
717 }
718 }
719 return false;
720 }
721 return true;
722}
723
724static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
725{
726 UNUSED(stepCtx);
728
729 if (!buttonGenericCallback(event, &pos)) {
730 return;
731 }
732 else {
733 // memorize last direction
734 context.review.dataDirection = pos;
735 }
736 displayReviewPage(pos);
737}
738
739// this is the callback used when button action on the "skip" page
740static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
741{
742 UNUSED(stepCtx);
744
745 if (event == BUTTON_LEFT_PRESSED) {
746 // only decrement page if we are going backward but coming from forward (back & forth)
747 if ((context.review.dataDirection == FORWARD_DIRECTION)
748 && (context.currentPage > context.firstPairPage)) {
749 context.currentPage--;
750 }
751 pos = BACKWARD_DIRECTION;
752 }
753 else if (event == BUTTON_RIGHT_PRESSED) {
754 // only increment page if we are going forward but coming from backward (back & forth)
755 if ((context.review.dataDirection == BACKWARD_DIRECTION)
756 && (context.currentPage < (int) (context.nbPages - 1))
757 && (context.currentPage > context.firstPairPage)) {
758 context.currentPage++;
759 }
760 pos = FORWARD_DIRECTION;
761 }
762 else if (event == BUTTON_BOTH_PRESSED) {
763 // the first tag/value page is at page 0 only in streaming case
764 if (context.firstPairPage == 0) {
765 // in streaming, we have to call the provided callback
766 context.review.skipCallback();
767 }
768 else {
769 // if not in streaming, go directly to the "approve" page
770 context.currentPage = context.nbPages - 2;
771 displayReviewPage(FORWARD_DIRECTION);
772 }
773 return;
774 }
775 else {
776 return;
777 }
778 // the first tag/value page is at page 0 only in streaming case
779 if (context.firstPairPage == 0) {
780 displayStreamingReviewPage(pos);
781 }
782 else {
783 displayReviewPage(pos);
784 }
785}
786
787// this is the callback used when buttons in "Action" use case are pressed
788static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
789{
790 UNUSED(stepCtx);
791
792 if (event == BUTTON_BOTH_PRESSED) {
793 context.action.actionCallback();
794 }
795}
796
797static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
798{
800 UNUSED(stepCtx);
801
802 if (!buttonGenericCallback(event, &pos)) {
803 return;
804 }
805 else {
806 // memorize last direction
807 context.review.dataDirection = pos;
808 }
809
810 displayStreamingReviewPage(pos);
811}
812
813static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
814{
815 UNUSED(stepCtx);
817
818 if (!buttonGenericCallback(event, &pos)) {
819 return;
820 }
821
822 displaySettingsPage(pos, false);
823}
824
825static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
826{
827 UNUSED(stepCtx);
829
830 if (!buttonGenericCallback(event, &pos)) {
831 return;
832 }
833
834 displayInfoPage(pos);
835}
836
837static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
838{
839 UNUSED(stepCtx);
841
842 if (!buttonGenericCallback(event, &pos)) {
843 return;
844 }
845
846 displayHomePage(pos);
847}
848
849static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
850{
851 UNUSED(stepCtx);
853
854 if (!buttonGenericCallback(event, &pos)) {
855 return;
856 }
857
858 displayChoicePage(pos);
859}
860
861static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
862{
863 UNUSED(stepCtx);
865
866 if (!buttonGenericCallback(event, &pos)) {
867 return;
868 }
869
870 displayConfirm(pos);
871}
872
873static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
874{
875 UNUSED(stepCtx);
876 // any button press should dismiss the status screen
877 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
878 || (event == BUTTON_RIGHT_PRESSED)) {
879 if (context.stepCallback != NULL) {
880 context.stepCallback();
881 }
882 }
883}
884
885static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
886{
887 UNUSED(stepCtx);
889
890 if (!buttonGenericCallback(event, &pos)) {
891 return;
892 }
893
894 displayContent(pos, false);
895}
896
897// callback used for timeout
898static void statusTickerCallback(void)
899{
900 if (context.stepCallback != NULL) {
901 context.stepCallback();
902 }
903}
904
905// this is the callback used when navigating in extension pages
906static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
907{
909 UNUSED(stepCtx);
910
911 if (event == BUTTON_LEFT_PRESSED) {
912 // only decrement page if we are not at the first page
913 if (context.review.currentExtensionPage > 0) {
914 context.review.currentExtensionPage--;
915 }
916 pos = BACKWARD_DIRECTION;
917 }
918 else if (event == BUTTON_RIGHT_PRESSED) {
919 // only increment page if not at last page
920 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
921 context.review.currentExtensionPage++;
922 }
923 pos = FORWARD_DIRECTION;
924 }
925 else if (event == BUTTON_BOTH_PRESSED) {
926 // if at last page, leave modal context
927 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
928 nbgl_stepRelease(context.review.extensionStepCtx);
930 nbgl_refresh();
931 }
932 return;
933 }
934 else {
935 return;
936 }
937 displayExtensionStep(pos);
938}
939
940// function used to display the extension pages
941static void displayExtensionStep(nbgl_stepPosition_t pos)
942{
943 nbgl_layoutCenteredInfo_t info = {0};
944 const nbgl_contentTagValueList_t *tagValueList = NULL;
945 const nbgl_contentInfoList_t *infoList = NULL;
946 const char *text = NULL;
947 const char *subText = NULL;
948
949 if (context.review.extensionStepCtx != NULL) {
950 nbgl_stepRelease(context.review.extensionStepCtx);
951 }
952 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
953 if (context.review.currentExtensionPage == 0) {
954 pos |= FIRST_STEP;
955 }
956 else {
958 }
959
960 switch (context.review.extension->aliasType) {
961 case ENS_ALIAS:
962 text = context.review.extension->title;
963 subText = context.review.extension->fullValue;
964 break;
965 case INFO_LIST_ALIAS:
966 infoList = context.review.extension->infolist;
967 text = PIC(infoList->infoTypes[context.review.currentExtensionPage]);
968 subText = PIC(infoList->infoContents[context.review.currentExtensionPage]);
969 break;
971 tagValueList = context.review.extension->tagValuelist;
972 text = PIC(tagValueList->pairs[context.review.currentExtensionPage].item);
973 subText = PIC(tagValueList->pairs[context.review.currentExtensionPage].value);
974 break;
975 default:
976 break;
977 }
978 if (text != NULL) {
979 context.review.extensionStepCtx = nbgl_stepDrawText(
980 pos, extensionNavigate, NULL, text, subText, BOLD_TEXT1_INFO, true);
981 }
982 }
983 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
984 // draw the back page
985 info.icon = &C_icon_back_x;
986 info.text1 = "Back";
987 info.style = BOLD_TEXT1_INFO;
988 pos |= LAST_STEP;
989 context.review.extensionStepCtx
990 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
991 }
992 nbgl_refresh();
993}
994
995static void displayAliasFullValue(void)
996{
997 const char *text = NULL;
998 const char *subText = NULL;
999 const nbgl_icon_details_t *icon;
1000 bool isCenteredInfo;
1001
1002 getPairData(context.review.tagValueList,
1003 context.review.currentTagValueIndex,
1004 &text,
1005 &subText,
1006 &context.review.extension,
1007 &icon,
1008 &isCenteredInfo);
1009 if (context.review.extension == NULL) {
1010 // probably an error
1012 "displayAliasFullValue: extension nor found for pair %d\n",
1013 context.review.currentTagValueIndex);
1014 return;
1015 }
1016 context.review.currentExtensionPage = 0;
1017 context.review.extensionStepCtx = NULL;
1018 // create a modal flow to display this extension
1019 switch (context.review.extension->aliasType) {
1020 case ENS_ALIAS:
1021 context.review.nbExtensionPages = 2;
1022 break;
1023 case INFO_LIST_ALIAS:
1024 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
1025 break;
1027 context.review.nbExtensionPages = context.review.extension->tagValuelist->nbPairs + 1;
1028 break;
1029 default:
1031 "displayAliasFullValue: unsupported alias type %d\n",
1032 context.review.extension->aliasType);
1033 return;
1034 }
1035 displayExtensionStep(FORWARD_DIRECTION);
1036}
1037
1038static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1039{
1040 if (approve) {
1041 // Approve page
1042 *icon = &C_icon_validate_14;
1043 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1044 *text = "Confirm";
1045 }
1046 else {
1047 // if finish title is provided, use it
1048 if (context.review.finishTitle != NULL) {
1049 *text = context.review.finishTitle;
1050 }
1051 else {
1052 switch (context.operationType & REAL_TYPE_MASK) {
1053 case TYPE_TRANSACTION:
1054 if (context.operationType & RISKY_OPERATION) {
1055 *text = "Accept risk and sign transaction";
1056 }
1057 else {
1058 *text = "Sign transaction";
1059 }
1060 break;
1061 case TYPE_MESSAGE:
1062 if (context.operationType & RISKY_OPERATION) {
1063 *text = "Accept risk and sign message";
1064 }
1065 else {
1066 *text = "Sign message";
1067 }
1068 break;
1069 default:
1070 if (context.operationType & RISKY_OPERATION) {
1071 *text = "Accept risk and sign operation";
1072 }
1073 else {
1074 *text = "Sign operation";
1075 }
1076 break;
1077 }
1078 }
1079 }
1080 context.stepCallback = onReviewAccept;
1081 }
1082 else {
1083 // Reject page
1084 *icon = &C_icon_crossmark;
1085 if (context.type == ADDRESS_REVIEW_USE_CASE) {
1086 *text = "Cancel";
1087 }
1088 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1089 *text = "Reject transaction";
1090 }
1091 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1092 *text = "Reject message";
1093 }
1094 else {
1095 *text = "Reject operation";
1096 }
1097 context.stepCallback = onReviewReject;
1098 }
1099}
1100
1101// function used to display the current page in review
1102static void displayReviewPage(nbgl_stepPosition_t pos)
1103{
1104 uint8_t reviewPages = 0;
1105 uint8_t finalPages = 0;
1106 uint8_t pairIndex = 0;
1107 const char *text = NULL;
1108 const char *subText = NULL;
1109 const nbgl_icon_details_t *icon = NULL;
1110 uint8_t currentIndex = 0;
1111 uint8_t titleIndex = 255;
1112 uint8_t subIndex = 255;
1113 uint8_t approveIndex = 255;
1114 uint8_t rejectIndex = 255;
1115 const nbgl_contentValueExt_t *extension = NULL;
1116 ForcedType_t forcedType = NO_FORCED_TYPE;
1117
1118 context.stepCallback = NULL;
1119
1120 // Determine the 1st page to display tag/values
1121 // Title page to display
1122 titleIndex = currentIndex++;
1123 reviewPages++;
1124 if (context.review.reviewSubTitle) {
1125 // subtitle page to display
1126 subIndex = currentIndex++;
1127 reviewPages++;
1128 }
1129 approveIndex = context.nbPages - 2;
1130 rejectIndex = context.nbPages - 1;
1131 finalPages = approveIndex;
1132
1133 // Determine which page to display
1134 if (context.currentPage >= finalPages) {
1135 if (context.currentPage == approveIndex) {
1136 // Approve page
1137 getLastPageInfo(true, &icon, &text);
1138 }
1139 else if (context.currentPage == rejectIndex) {
1140 // Reject page
1141 getLastPageInfo(false, &icon, &text);
1142 }
1143 }
1144 else if (context.currentPage < reviewPages) {
1145 if (context.currentPage == titleIndex) {
1146 // Title page
1147 icon = context.review.icon;
1148 text = context.review.reviewTitle;
1149 }
1150 else if (context.currentPage == subIndex) {
1151 // SubTitle page
1152 text = context.review.reviewSubTitle;
1153 }
1154 }
1155 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1156 // address confirmation and 2nd page
1157 text = "Address";
1158 subText = context.review.address;
1159 }
1160 else {
1161 // if there is a skip, and we are not already displaying the "skip" page
1162 // and we are not at the first tag/value of the first set of data (except if going
1163 // backward) then display the "skip" page
1164 if ((context.operationType & SKIPPABLE_OPERATION) && (context.review.skipDisplay == false)
1165 && ((context.currentPage > reviewPages)
1166 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1167 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1168 nbgl_layoutCenteredInfo_t info = {0};
1169 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1170 directions |= LAST_STEP;
1171 }
1172 info.icon = &C_Information_circle_14px;
1173 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1174 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1175 nbgl_refresh();
1176 context.review.skipDisplay = true;
1177 context.firstPairPage = reviewPages;
1178 return;
1179 }
1180 context.review.skipDisplay = false;
1181 bool isCenteredInfo = false;
1182 pairIndex = context.currentPage - reviewPages;
1183 if (context.review.address != NULL) {
1184 pairIndex--;
1185 }
1186 getPairData(context.review.tagValueList,
1187 pairIndex,
1188 &text,
1189 &subText,
1190 &extension,
1191 &icon,
1192 &isCenteredInfo);
1193 if (extension != NULL) {
1194 context.stepCallback = displayAliasFullValue;
1195 context.review.currentTagValueIndex = pairIndex;
1196 forcedType = FORCE_BUTTON;
1197 }
1198 else {
1199 if (isCenteredInfo) {
1200 forcedType = FORCE_CENTERED_INFO;
1201 }
1202 }
1203 }
1204
1205 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1206 nbgl_refresh();
1207}
1208
1209// function used to display the current page in review
1210static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1211{
1212 const char *text = NULL;
1213 const char *subText = NULL;
1214 const nbgl_icon_details_t *icon = NULL;
1215 uint8_t reviewPages = 0;
1216 uint8_t titleIndex = 255;
1217 uint8_t subIndex = 255;
1218 const nbgl_contentValueExt_t *extension = NULL;
1219 ForcedType_t forcedType = NO_FORCED_TYPE;
1220
1221 context.stepCallback = NULL;
1222 switch (context.type) {
1223 case STREAMING_START_REVIEW_USE_CASE:
1224 // Title page to display
1225 titleIndex = reviewPages++;
1226 if (context.review.reviewSubTitle) {
1227 // subtitle page to display
1228 subIndex = reviewPages++;
1229 }
1230 // Determine which page to display
1231 if (context.currentPage >= reviewPages) {
1232 onReviewAccept();
1233 return;
1234 }
1235 // header page(s)
1236 if (context.currentPage == titleIndex) {
1237 // title page
1238 icon = context.review.icon;
1239 text = context.review.reviewTitle;
1240 }
1241 else if (context.currentPage == subIndex) {
1242 // subtitle page
1243 text = context.review.reviewSubTitle;
1244 }
1245 break;
1246
1247 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1248 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1249 onReviewAccept();
1250 return;
1251 }
1252 // if there is a skip, and we are not already displaying the "skip" page
1253 // and we are not at the first tag/value of the first set of data (except if going
1254 // backward) then display the "skip" page
1255 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1256 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1257 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1258 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1259 nbgl_layoutCenteredInfo_t info = {0};
1260 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1261 directions |= LAST_STEP;
1262 }
1263 info.icon = &C_Information_circle_14px;
1264 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1265 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1266 nbgl_refresh();
1267 context.review.skipDisplay = true;
1268 return;
1269 }
1270 context.review.skipDisplay = false;
1271 bool isCenteredInfo = false;
1272 getPairData(context.review.tagValueList,
1273 context.currentPage,
1274 &text,
1275 &subText,
1276 &extension,
1277 &icon,
1278 &isCenteredInfo);
1279 if (extension != NULL) {
1280 forcedType = FORCE_BUTTON;
1281 }
1282 else {
1283 if (isCenteredInfo) {
1284 forcedType = FORCE_CENTERED_INFO;
1285 }
1286 }
1287 break;
1288
1289 case STREAMING_FINISH_REVIEW_USE_CASE:
1290 default:
1291 if (context.currentPage == 0) {
1292 // accept page
1293 getLastPageInfo(true, &icon, &text);
1294 }
1295 else {
1296 // reject page
1297 getLastPageInfo(false, &icon, &text);
1298 }
1299 break;
1300 }
1301
1302 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1303 nbgl_refresh();
1304}
1305
1306// function used to display the current page in info
1307static void displayInfoPage(nbgl_stepPosition_t pos)
1308{
1309 const char *text = NULL;
1310 const char *subText = NULL;
1311 const nbgl_icon_details_t *icon = NULL;
1312
1313 context.stepCallback = NULL;
1314
1315 if (context.currentPage < (context.nbPages - 1)) {
1316 text = PIC(
1317 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1318 subText = PIC(
1319 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1320 }
1321 else {
1322 icon = &C_icon_back_x;
1323 text = "Back";
1324 context.stepCallback = startUseCaseHome;
1325 }
1326
1327 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1328 nbgl_refresh();
1329}
1330
1331// function used to get the current page content
1332static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1333{
1334 uint8_t elemIdx = 0;
1335 const nbgl_content_t *p_content = NULL;
1336 nbgl_content_t content = {0};
1337 nbgl_contentSwitch_t *contentSwitch = NULL;
1338#ifdef WITH_HORIZONTAL_CHOICES_LIST
1339 nbgl_contentRadioChoice_t *contentChoices = NULL;
1340 char **names = NULL;
1341#endif
1342#ifdef WITH_HORIZONTAL_BARS_LIST
1343 nbgl_contentBarsList_t *contentBars = NULL;
1344 char **texts = NULL;
1345#endif
1346 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1347 if (p_content == NULL) {
1348 return;
1349 }
1350 switch (p_content->type) {
1351 case CENTERED_INFO:
1352 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1353 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1354 break;
1355 case INFO_BUTTON:
1356 contentPage->icon = PIC(p_content->content.infoButton.icon);
1357 contentPage->text = PIC(p_content->content.infoButton.text);
1358 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1359 break;
1360 case TAG_VALUE_LIST:
1361 getPairData(&p_content->content.tagValueList,
1362 elemIdx,
1363 &contentPage->text,
1364 &contentPage->subText,
1365 &contentPage->extension,
1366 &contentPage->icon,
1367 &contentPage->isCenteredInfo);
1368 break;
1369 case SWITCHES_LIST:
1370 contentPage->isSwitch = true;
1371 contentSwitch = &(
1372 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1373 contentPage->text = contentSwitch->text;
1374 contentPage->state = contentSwitch->initState;
1375 if (toogle_state) {
1376 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1377 }
1378 context.stepCallback = onSwitchAction;
1379 contentPage->subText = contentSwitch->subText;
1380 break;
1381 case INFOS_LIST:
1382 contentPage->text
1383 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1384 contentPage->subText
1385 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1386 break;
1387 case CHOICES_LIST:
1388#ifdef WITH_HORIZONTAL_CHOICES_LIST
1389 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1390 names = (char **) PIC(contentChoices->names);
1391 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1392 contentPage->text = PIC(context.content.title);
1393 contentPage->subText = (const char *) PIC(names[elemIdx]);
1394 }
1395 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1396 contentPage->text = PIC(context.home.appName);
1397 contentPage->subText = (const char *) PIC(names[elemIdx]);
1398 }
1399 else {
1400 contentPage->text = (const char *) PIC(names[elemIdx]);
1401 }
1402#endif
1403 break;
1404 case BARS_LIST:
1405#ifdef WITH_HORIZONTAL_BARS_LIST
1406 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1407 texts = (char **) PIC(contentBars->barTexts);
1408 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1409 contentPage->text = PIC(context.content.title);
1410 contentPage->subText = PIC(texts[elemIdx]);
1411 }
1412 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1413 contentPage->text = PIC(context.home.appName);
1414 contentPage->subText = PIC(texts[elemIdx]);
1415 }
1416 else {
1417 contentPage->text = PIC(texts[elemIdx]);
1418 }
1419#endif
1420 break;
1421 default:
1422 break;
1423 }
1424}
1425
1426// function used to display the current page in settings
1427static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1428{
1429 PageContent_t contentPage = {0};
1430
1431 context.stepCallback = NULL;
1432
1433 if (context.currentPage < (context.nbPages - 1)) {
1434 getContentPage(toogle_state, &contentPage);
1435 }
1436 else { // last page is for quit
1437 contentPage.icon = &C_icon_back_x;
1438 contentPage.text = "Back";
1439 if (context.type == GENERIC_SETTINGS) {
1440 context.stepCallback = context.home.quitCallback;
1441 }
1442 else {
1443 context.stepCallback = startUseCaseHome;
1444 }
1445 }
1446
1447 if (contentPage.isSwitch) {
1448 drawSwitchStep(
1449 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1450 }
1451 else {
1452 drawStep(pos,
1453 contentPage.icon,
1454 contentPage.text,
1455 contentPage.subText,
1456 settingsCallback,
1457 false,
1458 NO_FORCED_TYPE);
1459 }
1460
1461 nbgl_refresh();
1462}
1463
1464static void startUseCaseHome(void)
1465{
1466 switch (context.type) {
1467 case SETTINGS_USE_CASE:
1468 // Settings page index
1469 context.currentPage = 1;
1470 if (context.home.homeAction) {
1471 // Action page is before Settings page
1472 context.currentPage++;
1473 }
1474 break;
1475 case INFO_USE_CASE:
1476 // Info page index
1477 context.currentPage = 1;
1478 if (context.home.homeAction) {
1479 // Action page is before Settings and Info pages
1480 context.currentPage++;
1481 }
1482 if (context.home.settingContents) {
1483 // Settings page is before Info pages
1484 context.currentPage++;
1485 }
1486 break;
1487 default:
1488 // Home page index
1489 context.currentPage = 0;
1490 break;
1491 }
1492
1493 context.type = HOME_USE_CASE;
1494 context.nbPages = 2; // Home + Quit
1495 if (context.home.settingContents) {
1496 context.nbPages++;
1497 }
1498 if (context.home.infosList) {
1499 context.nbPages++;
1500 }
1501 if (context.home.homeAction) {
1502 context.nbPages++;
1503 }
1504 displayHomePage(FORWARD_DIRECTION);
1505}
1506
1507static void startUseCaseInfo(void)
1508{
1509 context.type = INFO_USE_CASE;
1510 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1511 context.currentPage = 0;
1512
1513 displayInfoPage(FORWARD_DIRECTION);
1514}
1515
1516static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1517{
1518 nbgl_content_t content = {0};
1519 const nbgl_content_t *p_content = NULL;
1520
1521 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1522 if (context.type != GENERIC_SETTINGS) {
1523 context.type = SETTINGS_USE_CASE;
1524 }
1525
1526 context.nbPages = 1; // For back screen
1527 for (int i = 0; i < context.home.settingContents->nbContents; i++) {
1528 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1529 context.nbPages += getContentNbElement(p_content);
1530 }
1531 context.currentPage = initSettingPage;
1532
1533 displaySettingsPage(FORWARD_DIRECTION, false);
1534}
1535
1536static void startUseCaseSettings(void)
1537{
1538 startUseCaseSettingsAtPage(0);
1539}
1540
1541static void startUseCaseContent(void)
1542{
1543 uint8_t contentIdx = 0;
1544 const nbgl_content_t *p_content = NULL;
1545 nbgl_content_t content = {0};
1546
1547 context.nbPages = 1; // Quit
1548
1549 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1550 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1551 context.nbPages += getContentNbElement(p_content);
1552 }
1553
1554 // Ensure currentPage is valid
1555 if (context.currentPage >= context.nbPages) {
1556 return;
1557 }
1558
1559 displayContent(FORWARD_DIRECTION, false);
1560}
1561
1562// function used to display the current page in home
1563static void displayHomePage(nbgl_stepPosition_t pos)
1564{
1565 const char *text = NULL;
1566 const char *subText = NULL;
1567 const nbgl_icon_details_t *icon = NULL;
1568 uint8_t currentIndex = 0;
1569 uint8_t homeIndex = 255;
1570 uint8_t actionIndex = 255;
1571 uint8_t settingsIndex = 255;
1572 uint8_t infoIndex = 255;
1573
1574 context.stepCallback = NULL;
1575
1576 // Determine which pages are present
1577 homeIndex = currentIndex++;
1578 if (context.home.homeAction) {
1579 actionIndex = currentIndex++;
1580 }
1581 if (context.home.settingContents) {
1582 settingsIndex = currentIndex++;
1583 }
1584 if (context.home.infosList) {
1585 infoIndex = currentIndex++;
1586 }
1587
1588 if (context.currentPage == homeIndex) {
1589 // Home page
1590 icon = context.home.appIcon;
1591 if (context.home.tagline != NULL) {
1592 text = context.home.tagline;
1593 }
1594 else {
1595 text = context.home.appName;
1596 subText = "app is ready";
1597 }
1598 }
1599 else if (context.currentPage == actionIndex) {
1600 // Action page
1601 icon = context.home.homeAction->icon;
1602 text = PIC(context.home.homeAction->text);
1603 context.stepCallback = context.home.homeAction->callback;
1604 }
1605 else if (context.currentPage == settingsIndex) {
1606 // Settings page
1607 icon = &C_icon_coggle;
1608 text = "App settings";
1609 context.stepCallback = startUseCaseSettings;
1610 }
1611 else if (context.currentPage == infoIndex) {
1612 // About page
1613 icon = &C_Information_circle_14px;
1614 text = "App info";
1615 context.stepCallback = startUseCaseInfo;
1616 }
1617 else {
1618 icon = &C_Quit_14px;
1619 text = "Quit app";
1620 context.stepCallback = context.home.quitCallback;
1621 }
1622
1623 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1624 nbgl_refresh();
1625}
1626
1627// function used to display the current page in choice
1628static void displayChoicePage(nbgl_stepPosition_t pos)
1629{
1630 const char *text = NULL;
1631 const char *subText = NULL;
1632 const nbgl_icon_details_t *icon = NULL;
1633 // set to 1 if there is only one page for intro (if either icon or subMessage is NULL)
1634 uint8_t acceptPage = 0;
1635
1636 if (context.choice.message != NULL) {
1637 if ((context.choice.icon == NULL) || (context.choice.subMessage == NULL)) {
1638 acceptPage = 1;
1639 }
1640 else {
1641 acceptPage = 2;
1642 }
1643 }
1644 context.stepCallback = NULL;
1645
1646 if (context.currentPage < acceptPage) {
1647 if (context.currentPage == 0) { // title page
1648 text = context.choice.message;
1649 if (context.choice.icon != NULL) {
1650 icon = context.choice.icon;
1651 }
1652 else {
1653 subText = context.choice.subMessage;
1654 }
1655 }
1656 else if ((acceptPage == 2) && (context.currentPage == 1)) { // sub-title page
1657 // displayed only if there is both icon and submessage
1658 text = context.choice.message;
1659 subText = context.choice.subMessage;
1660 }
1661 }
1662 else if (context.currentPage == acceptPage) { // confirm page
1663 icon = &C_icon_validate_14;
1664 text = context.choice.confirmText;
1665 context.stepCallback = onChoiceAccept;
1666 }
1667 else if (context.currentPage == (acceptPage + 1)) { // cancel page
1668 icon = &C_icon_crossmark;
1669 text = context.choice.cancelText;
1670 context.stepCallback = onChoiceReject;
1671 }
1672 else if (context.choice.details != NULL) {
1673 // only the first level of details and BAR_LIST type are supported
1674 if (context.choice.details->type == BAR_LIST_WARNING) {
1675 text = context.choice.details->barList.texts[context.currentPage - (acceptPage + 2)];
1676 subText
1677 = context.choice.details->barList.subTexts[context.currentPage - (acceptPage + 2)];
1678 }
1679 }
1680
1681 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1682 nbgl_refresh();
1683}
1684
1685// function used to display the Confirm page
1686static void displayConfirm(nbgl_stepPosition_t pos)
1687{
1688 const char *text = NULL;
1689 const char *subText = NULL;
1690 const nbgl_icon_details_t *icon = NULL;
1691
1692 context.stepCallback = NULL;
1693 switch (context.currentPage) {
1694 case 0:
1695 // title page
1696 text = context.confirm.message;
1697 subText = context.confirm.subMessage;
1698 break;
1699 case 1:
1700 // confirm page
1701 icon = &C_icon_validate_14;
1702 text = context.confirm.confirmText;
1703 context.stepCallback = onConfirmAccept;
1704 break;
1705 case 2:
1706 // cancel page
1707 icon = &C_icon_crossmark;
1708 text = context.confirm.cancelText;
1709 context.stepCallback = onConfirmReject;
1710 break;
1711 }
1712
1713 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1714 nbgl_refresh();
1715}
1716
1717// function used to display the current navigable content
1718static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1719{
1720 PageContent_t contentPage = {0};
1721 ForcedType_t forcedType = NO_FORCED_TYPE;
1722
1723 context.stepCallback = NULL;
1724
1725 if (context.currentPage < (context.nbPages - 1)) {
1726 getContentPage(toogle_state, &contentPage);
1727 if (contentPage.isCenteredInfo) {
1728 forcedType = FORCE_CENTERED_INFO;
1729 }
1730 }
1731 else { // last page is for quit
1732 if (context.content.rejectText) {
1733 contentPage.text = context.content.rejectText;
1734 }
1735 else {
1736 contentPage.text = "Back";
1737 }
1738 if (context.type == GENERIC_REVIEW_USE_CASE) {
1739 contentPage.icon = &C_icon_crossmark;
1740 }
1741 else {
1742 contentPage.icon = &C_icon_back_x;
1743 }
1744 context.stepCallback = context.content.quitCallback;
1745 }
1746
1747 if (contentPage.isSwitch) {
1748 drawSwitchStep(
1749 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1750 }
1751 else {
1752 drawStep(pos,
1753 contentPage.icon,
1754 contentPage.text,
1755 contentPage.subText,
1756 contentCallback,
1757 false,
1758 forcedType);
1759 }
1760
1761 nbgl_refresh();
1762}
1763
1764static void displaySpinner(const char *text)
1765{
1766 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1767 nbgl_refresh();
1768}
1769
1770// function to factorize code for all simple reviews
1771static void useCaseReview(ContextType_t type,
1772 nbgl_operationType_t operationType,
1773 const nbgl_contentTagValueList_t *tagValueList,
1774 const nbgl_icon_details_t *icon,
1775 const char *reviewTitle,
1776 const char *reviewSubTitle,
1777 const char *finishTitle,
1778 nbgl_choiceCallback_t choiceCallback)
1779{
1780 memset(&context, 0, sizeof(UseCaseContext_t));
1781 context.type = type;
1782 context.operationType = operationType;
1783 context.review.tagValueList = tagValueList;
1784 context.review.reviewTitle = reviewTitle;
1785 context.review.reviewSubTitle = reviewSubTitle;
1786 context.review.finishTitle = finishTitle;
1787 context.review.icon = icon;
1788 context.review.onChoice = choiceCallback;
1789 context.currentPage = 0;
1790 // 1 page for title and 2 pages at the end for accept/reject
1791 context.nbPages = tagValueList->nbPairs + 3;
1792 if (reviewSubTitle) {
1793 context.nbPages++; // 1 page for subtitle page
1794 }
1795
1796 displayReviewPage(FORWARD_DIRECTION);
1797}
1798
1799#ifdef NBGL_KEYPAD
1800static void setPinCodeText(void)
1801{
1802 bool enableValidate = false;
1803 bool enableBackspace = true;
1804
1805 // pin can be validated when min digits is entered
1806 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1807 // backspace is disabled when no digit is entered and back vallback is not provided
1808 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1809 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1810 context.keypad.hidden,
1811 context.keypad.pinLen,
1812 (const char *) context.keypad.pinEntry);
1814 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1815 nbgl_layoutDraw(context.keypad.layoutCtx);
1816 nbgl_refresh();
1817}
1818
1819// called when a key is touched on the keypad
1820static void keypadCallback(char touchedKey)
1821{
1822 switch (touchedKey) {
1823 case BACKSPACE_KEY:
1824 if (context.keypad.pinLen > 0) {
1825 context.keypad.pinLen--;
1826 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1827 }
1828 else if (context.keypad.backCallback != NULL) {
1829 context.keypad.backCallback();
1830 break;
1831 }
1832 setPinCodeText();
1833 break;
1834
1835 case VALIDATE_KEY:
1836 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1837 break;
1838
1839 default:
1840 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1841 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1842 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1843 context.keypad.pinLen++;
1844 }
1845 setPinCodeText();
1846 }
1847 break;
1848 }
1849}
1850#endif // NBGL_KEYPAD
1851
1852// this is the function called to start the actual review, from the initial warning pages
1853static void launchReviewAfterWarning(void)
1854{
1855 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
1856 useCaseReview(reviewWithWarnCtx.type,
1857 reviewWithWarnCtx.operationType,
1858 reviewWithWarnCtx.tagValueList,
1859 reviewWithWarnCtx.icon,
1860 reviewWithWarnCtx.reviewTitle,
1861 reviewWithWarnCtx.reviewSubTitle,
1862 reviewWithWarnCtx.finishTitle,
1863 reviewWithWarnCtx.choiceCallback);
1864 }
1865 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
1866 displayStreamingReviewPage(FORWARD_DIRECTION);
1867 }
1868}
1869
1870// this is the callback used when navigating in warning pages
1871static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
1872{
1873 UNUSED(stepCtx);
1874
1875 if (event == BUTTON_LEFT_PRESSED) {
1876 // only decrement page if we are not at the first page
1877 if (reviewWithWarnCtx.warningPage > 0) {
1878 reviewWithWarnCtx.warningPage--;
1879 }
1880 }
1881 else if (event == BUTTON_RIGHT_PRESSED) {
1882 // only increment page if not at last page
1883 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
1884 reviewWithWarnCtx.warningPage++;
1885 }
1886 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
1887 && (reviewWithWarnCtx.warning->info != NULL)) {
1888 launchReviewAfterWarning();
1889 return;
1890 }
1891 }
1892 else if ((event == BUTTON_BOTH_PRESSED)
1893 && (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
1894 // if at first page, double press leads to start of review
1895 if (reviewWithWarnCtx.warningPage == 0) {
1896 launchReviewAfterWarning();
1897 }
1898 // if at last page, reject operation
1899 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1900 reviewWithWarnCtx.choiceCallback(false);
1901 }
1902 return;
1903 }
1904 else {
1905 return;
1906 }
1907 displayWarningStep();
1908}
1909
1910// function used to display the initial warning pages when starting a "review with warning"
1911static void displayWarningStep(void)
1912{
1913 nbgl_layoutCenteredInfo_t info = {0};
1914 nbgl_stepPosition_t pos = 0;
1915 if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
1916 if (reviewWithWarnCtx.warningPage == 0) {
1917 // draw the main warning page
1918 info.icon = &C_icon_warning;
1919 info.text1 = "Blind signing ahead";
1920 info.text2 = "To accept risk, press both buttons";
1922 }
1923 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1924 getLastPageInfo(false, &info.icon, &info.text1);
1926 }
1927 }
1928 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
1929 && (reviewWithWarnCtx.warning->info != NULL)) {
1930 if (reviewWithWarnCtx.warningPage == 0) {
1931 info.icon = reviewWithWarnCtx.warning->info->icon;
1932 info.text1 = reviewWithWarnCtx.warning->info->title;
1933 info.text2 = reviewWithWarnCtx.warning->info->description;
1935 }
1936 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
1937 if (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING) {
1938 info.icon = reviewWithWarnCtx.warning->introDetails->centeredInfo.icon;
1939 info.text1 = reviewWithWarnCtx.warning->introDetails->centeredInfo.title;
1940 info.text2 = reviewWithWarnCtx.warning->introDetails->centeredInfo.description;
1942 }
1943 else {
1944 // not supported
1945 return;
1946 }
1947 }
1948 }
1949 else {
1950 // not supported
1951 return;
1952 }
1953 info.style = BOLD_TEXT1_INFO;
1954 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
1955 nbgl_refresh();
1956}
1957
1958// function used to display the initial warning page when starting a "review with warning"
1959static void displayInitialWarning(void)
1960{
1961 // draw the main warning page
1962 reviewWithWarnCtx.warningPage = 0;
1963 reviewWithWarnCtx.nbWarningPages = 2;
1964 displayWarningStep();
1965}
1966
1967/**********************
1968 * GLOBAL FUNCTIONS
1969 **********************/
1970
1982uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs,
1983 const nbgl_contentTagValueList_t *tagValueList,
1984 uint8_t startIndex,
1985 bool *requireSpecificDisplay)
1986{
1987 UNUSED(nbPairs);
1988 UNUSED(tagValueList);
1989 UNUSED(startIndex);
1990 *requireSpecificDisplay = true;
1991 return 1;
1992}
1993
2006uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs,
2007 const nbgl_contentTagValueList_t *tagValueList,
2008 uint8_t startIndex,
2009 bool isSkippable,
2010 bool *requireSpecificDisplay)
2011{
2012 UNUSED(nbPairs);
2013 UNUSED(tagValueList);
2014 UNUSED(startIndex);
2015 UNUSED(isSkippable);
2016 *requireSpecificDisplay = true;
2017 return 1;
2018}
2019
2028uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos,
2029 const nbgl_contentInfoList_t *infosList,
2030 uint8_t startIndex,
2031 bool withNav)
2032{
2033 UNUSED(nbInfos);
2034 UNUSED(infosList);
2035 UNUSED(startIndex);
2036 UNUSED(withNav);
2037 return 1;
2038}
2039
2048uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches,
2049 const nbgl_contentSwitchesList_t *switchesList,
2050 uint8_t startIndex,
2051 bool withNav)
2052{
2053 UNUSED(nbSwitches);
2054 UNUSED(switchesList);
2055 UNUSED(startIndex);
2056 UNUSED(withNav);
2057 return 1;
2058}
2059
2068uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars,
2069 const nbgl_contentBarsList_t *barsList,
2070 uint8_t startIndex,
2071 bool withNav)
2072{
2073 UNUSED(nbBars);
2074 UNUSED(barsList);
2075 UNUSED(startIndex);
2076 UNUSED(withNav);
2077 return 1;
2078}
2079
2088uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices,
2089 const nbgl_contentRadioChoice_t *choicesList,
2090 uint8_t startIndex,
2091 bool withNav)
2092{
2093 UNUSED(nbChoices);
2094 UNUSED(choicesList);
2095 UNUSED(startIndex);
2096 UNUSED(withNav);
2097 return 1;
2098}
2099
2107{
2108 uint8_t nbPages = 0;
2109 uint8_t nbPairs = tagValueList->nbPairs;
2110 uint8_t nbPairsInPage;
2111 uint8_t i = 0;
2112 bool flag;
2113
2114 while (i < tagValueList->nbPairs) {
2115 // upper margin
2116 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2117 i += nbPairsInPage;
2118 nbPairs -= nbPairsInPage;
2119 nbPages++;
2120 }
2121 return nbPages;
2122}
2123
2137void nbgl_useCaseNavigableContent(const char *title,
2138 uint8_t initPage,
2139 uint8_t nbPages,
2140 nbgl_callback_t quitCallback,
2141 nbgl_navCallback_t navCallback,
2142 nbgl_layoutTouchCallback_t controlsCallback)
2143{
2144 memset(&context, 0, sizeof(UseCaseContext_t));
2145 context.type = CONTENT_USE_CASE;
2146 context.currentPage = initPage;
2147 context.content.title = title;
2148 context.content.quitCallback = quitCallback;
2149 context.content.navCallback = navCallback;
2150 context.content.controlsCallback = controlsCallback;
2151 context.content.genericContents.callbackCallNeeded = true;
2152 context.content.genericContents.nbContents = nbPages;
2153
2154 startUseCaseContent();
2155}
2156
2171void nbgl_useCaseHomeAndSettings(const char *appName,
2172 const nbgl_icon_details_t *appIcon,
2173 const char *tagline,
2174 const uint8_t initSettingPage,
2175 const nbgl_genericContents_t *settingContents,
2176 const nbgl_contentInfoList_t *infosList,
2177 const nbgl_homeAction_t *action,
2178 nbgl_callback_t quitCallback)
2179{
2180 memset(&context, 0, sizeof(UseCaseContext_t));
2181 context.home.appName = appName;
2182 context.home.appIcon = appIcon;
2183 context.home.tagline = tagline;
2184 context.home.settingContents = PIC(settingContents);
2185 context.home.infosList = PIC(infosList);
2186 context.home.homeAction = action;
2187 context.home.quitCallback = quitCallback;
2188
2189 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2190 startUseCaseSettingsAtPage(initSettingPage);
2191 }
2192 else {
2193 startUseCaseHome();
2194 }
2195}
2196
2209void nbgl_useCaseGenericSettings(const char *appName,
2210 uint8_t initPage,
2211 const nbgl_genericContents_t *settingContents,
2212 const nbgl_contentInfoList_t *infosList,
2213 nbgl_callback_t quitCallback)
2214{
2215 memset(&context, 0, sizeof(UseCaseContext_t));
2216 context.type = GENERIC_SETTINGS;
2217 context.home.appName = appName;
2218 context.home.settingContents = PIC(settingContents);
2219 context.home.infosList = PIC(infosList);
2220 context.home.quitCallback = quitCallback;
2221
2222 startUseCaseSettingsAtPage(initPage);
2223}
2224
2236void nbgl_useCaseGenericConfiguration(const char *title,
2237 uint8_t initPage,
2238 const nbgl_genericContents_t *contents,
2239 nbgl_callback_t quitCallback)
2240{
2241 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2242}
2243
2258void nbgl_useCaseReview(nbgl_operationType_t operationType,
2259 const nbgl_contentTagValueList_t *tagValueList,
2260 const nbgl_icon_details_t *icon,
2261 const char *reviewTitle,
2262 const char *reviewSubTitle,
2263 const char *finishTitle,
2264 nbgl_choiceCallback_t choiceCallback)
2265{
2266 useCaseReview(REVIEW_USE_CASE,
2267 operationType,
2268 tagValueList,
2269 icon,
2270 reviewTitle,
2271 reviewSubTitle,
2272 finishTitle,
2273 choiceCallback);
2274}
2275
2299 const nbgl_contentTagValueList_t *tagValueList,
2300 const nbgl_icon_details_t *icon,
2301 const char *reviewTitle,
2302 const char *reviewSubTitle,
2303 const char *finishTitle,
2304 const nbgl_tipBox_t *tipBox,
2305 const nbgl_warning_t *warning,
2306 nbgl_choiceCallback_t choiceCallback)
2307{
2308 UNUSED(tipBox);
2309 ContextType_t type = REVIEW_USE_CASE;
2310
2311 // if no warning at all, it's a simple review
2312 if ((warning == NULL)
2313 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
2314 && (warning->reviewDetails == NULL))) {
2315 useCaseReview(type,
2316 operationType,
2317 tagValueList,
2318 icon,
2319 reviewTitle,
2320 reviewSubTitle,
2321 finishTitle,
2322 choiceCallback);
2323 return;
2324 }
2325 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2326 operationType |= NO_THREAT_OPERATION;
2327 }
2328 else {
2329 operationType |= RISKY_OPERATION;
2330 }
2331
2332 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2333 reviewWithWarnCtx.type = type;
2334 reviewWithWarnCtx.operationType = operationType;
2335 reviewWithWarnCtx.tagValueList = tagValueList;
2336 reviewWithWarnCtx.icon = icon;
2337 reviewWithWarnCtx.reviewTitle = reviewTitle;
2338 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2339 reviewWithWarnCtx.finishTitle = finishTitle;
2340 reviewWithWarnCtx.warning = warning;
2341 reviewWithWarnCtx.choiceCallback = choiceCallback;
2342
2343 // display the initial warning only of a risk/threat or blind signing
2344 if ((!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
2345 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
2346 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)))
2347 && (warning->introDetails == NULL)) {
2348 useCaseReview(type,
2349 operationType,
2350 tagValueList,
2351 icon,
2352 reviewTitle,
2353 reviewSubTitle,
2354 finishTitle,
2355 choiceCallback);
2356 return;
2357 }
2358 displayInitialWarning();
2359}
2360
2381 const nbgl_contentTagValueList_t *tagValueList,
2382 const nbgl_icon_details_t *icon,
2383 const char *reviewTitle,
2384 const char *reviewSubTitle,
2385 const char *finishTitle,
2386 const nbgl_tipBox_t *dummy,
2387 nbgl_choiceCallback_t choiceCallback)
2388{
2389 nbgl_useCaseAdvancedReview(operationType,
2390 tagValueList,
2391 icon,
2392 reviewTitle,
2393 reviewSubTitle,
2394 finishTitle,
2395 dummy,
2396 &blindSigningWarning,
2397 choiceCallback);
2398}
2399
2415 const nbgl_contentTagValueList_t *tagValueList,
2416 const nbgl_icon_details_t *icon,
2417 const char *reviewTitle,
2418 const char *reviewSubTitle,
2419 const char *finishTitle,
2420 nbgl_choiceCallback_t choiceCallback)
2421{
2422 nbgl_useCaseReview(operationType,
2423 tagValueList,
2424 icon,
2425 reviewTitle,
2426 reviewSubTitle,
2427 finishTitle,
2428 choiceCallback);
2429}
2430
2447void nbgl_useCaseAddressReview(const char *address,
2448 const nbgl_contentTagValueList_t *additionalTagValueList,
2449 const nbgl_icon_details_t *icon,
2450 const char *reviewTitle,
2451 const char *reviewSubTitle,
2452 nbgl_choiceCallback_t choiceCallback)
2453{
2454 memset(&context, 0, sizeof(UseCaseContext_t));
2455 context.type = ADDRESS_REVIEW_USE_CASE;
2456 context.review.address = address;
2457 context.review.reviewTitle = reviewTitle;
2458 context.review.reviewSubTitle = reviewSubTitle;
2459 context.review.icon = icon;
2460 context.review.onChoice = choiceCallback;
2461 context.currentPage = 0;
2462 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2463 // + 1 if sub Title
2464 context.nbPages = reviewSubTitle ? 5 : 4;
2465 if (additionalTagValueList) {
2466 context.review.tagValueList = PIC(additionalTagValueList);
2467 context.nbPages += additionalTagValueList->nbPairs;
2468 }
2469
2470 displayReviewPage(FORWARD_DIRECTION);
2471}
2472
2482 const char *rejectText,
2483 nbgl_callback_t rejectCallback)
2484{
2485 memset(&context, 0, sizeof(UseCaseContext_t));
2486 context.type = GENERIC_REVIEW_USE_CASE;
2487 context.content.rejectText = rejectText;
2488 context.content.quitCallback = rejectCallback;
2489 context.content.genericContents.nbContents = contents->nbContents;
2490 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2491 if (contents->callbackCallNeeded) {
2492 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2493 }
2494 else {
2495 context.content.genericContents.contentsList = PIC(contents->contentsList);
2496 }
2497
2498 startUseCaseContent();
2499}
2500
2509void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2510{
2511 UNUSED(isSuccess);
2512 memset(&context, 0, sizeof(UseCaseContext_t));
2513 context.type = STATUS_USE_CASE;
2514 context.stepCallback = quitCallback;
2515 context.currentPage = 0;
2516 context.nbPages = 1;
2517
2519 NULL,
2520 message,
2521 NULL,
2522 statusButtonCallback,
2523 false,
2524 NO_FORCED_TYPE);
2525}
2526
2534 nbgl_callback_t quitCallback)
2535{
2536 const char *msg;
2537 bool isSuccess;
2538 switch (reviewStatusType) {
2540 msg = "Operation signed";
2541 isSuccess = true;
2542 break;
2544 msg = "Operation rejected";
2545 isSuccess = false;
2546 break;
2548 msg = "Transaction signed";
2549 isSuccess = true;
2550 break;
2552 msg = "Transaction rejected";
2553 isSuccess = false;
2554 break;
2556 msg = "Message signed";
2557 isSuccess = true;
2558 break;
2560 msg = "Message rejected";
2561 isSuccess = false;
2562 break;
2564 msg = "Address verified";
2565 isSuccess = true;
2566 break;
2568 msg = "Address verification cancelled";
2569 isSuccess = false;
2570 break;
2571 default:
2572 return;
2573 }
2574 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2575}
2576
2590 const nbgl_icon_details_t *icon,
2591 const char *reviewTitle,
2592 const char *reviewSubTitle,
2593 nbgl_choiceCallback_t choiceCallback)
2594{
2595 // memorize streaming operation type for future API calls
2596 streamingOpType = operationType;
2597
2598 memset(&context, 0, sizeof(UseCaseContext_t));
2599 context.type = STREAMING_START_REVIEW_USE_CASE;
2600 context.operationType = operationType;
2601 context.review.reviewTitle = reviewTitle;
2602 context.review.reviewSubTitle = reviewSubTitle;
2603 context.review.icon = icon;
2604 context.review.onChoice = choiceCallback;
2605 context.currentPage = 0;
2606 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2607
2608 displayStreamingReviewPage(FORWARD_DIRECTION);
2609}
2610
2625 const nbgl_icon_details_t *icon,
2626 const char *reviewTitle,
2627 const char *reviewSubTitle,
2628 nbgl_choiceCallback_t choiceCallback)
2629{
2631 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2632}
2633
2650 const nbgl_icon_details_t *icon,
2651 const char *reviewTitle,
2652 const char *reviewSubTitle,
2653 const nbgl_warning_t *warning,
2654 nbgl_choiceCallback_t choiceCallback)
2655{
2656 memset(&context, 0, sizeof(UseCaseContext_t));
2657 context.type = STREAMING_START_REVIEW_USE_CASE;
2658 context.operationType = operationType;
2659 context.review.reviewTitle = reviewTitle;
2660 context.review.reviewSubTitle = reviewSubTitle;
2661 context.review.icon = icon;
2662 context.review.onChoice = choiceCallback;
2663 context.currentPage = 0;
2664 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2665
2666 // memorize streaming operation type for future API calls
2667 streamingOpType = operationType;
2668
2669 // if no warning at all, it's a simple review
2670 if ((warning == NULL)
2671 || ((warning->predefinedSet == 0) && (warning->introDetails == NULL)
2672 && (warning->reviewDetails == NULL))) {
2673 displayStreamingReviewPage(FORWARD_DIRECTION);
2674 return;
2675 }
2676 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2677 operationType |= NO_THREAT_OPERATION;
2678 }
2679 else {
2680 operationType |= RISKY_OPERATION;
2681 }
2682 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2683
2684 reviewWithWarnCtx.type = context.type;
2685 reviewWithWarnCtx.operationType = operationType;
2686 reviewWithWarnCtx.icon = icon;
2687 reviewWithWarnCtx.reviewTitle = reviewTitle;
2688 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2689 reviewWithWarnCtx.choiceCallback = choiceCallback;
2690 reviewWithWarnCtx.warning = warning;
2691
2692 // display the initial warning only of a risk/threat or blind signing
2693 if ((!(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_THREAT_DETECTED_WARN))
2694 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << W3C_RISK_DETECTED_WARN))
2695 && !(reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)))
2696 && (warning->introDetails == NULL)) {
2697 displayStreamingReviewPage(FORWARD_DIRECTION);
2698 return;
2699 }
2700 displayInitialWarning();
2701}
2702
2717 nbgl_choiceCallback_t choiceCallback,
2718 nbgl_callback_t skipCallback)
2719{
2720 uint8_t curNbDataSets = context.review.nbDataSets;
2721
2722 memset(&context, 0, sizeof(UseCaseContext_t));
2723 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
2724 context.operationType = streamingOpType;
2725 context.review.tagValueList = tagValueList;
2726 context.review.onChoice = choiceCallback;
2727 context.currentPage = 0;
2728 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
2729 context.review.skipCallback = skipCallback;
2730 context.review.nbDataSets = curNbDataSets + 1;
2731
2732 displayStreamingReviewPage(FORWARD_DIRECTION);
2733}
2734
2746 nbgl_choiceCallback_t choiceCallback)
2747{
2748 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
2749}
2750
2751void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
2752 nbgl_choiceCallback_t choiceCallback)
2753{
2754 memset(&context, 0, sizeof(UseCaseContext_t));
2755 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
2756 context.operationType = streamingOpType;
2757 context.review.onChoice = choiceCallback;
2758 context.review.finishTitle = finishTitle;
2759 context.currentPage = 0;
2760 context.nbPages = 2; // 2 pages at the end for accept/reject
2761
2762 displayStreamingReviewPage(FORWARD_DIRECTION);
2763}
2764
2770void nbgl_useCaseSpinner(const char *text)
2771{
2772 memset(&context, 0, sizeof(UseCaseContext_t));
2773 context.type = SPINNER_USE_CASE;
2774 context.currentPage = 0;
2775 context.nbPages = 1;
2776
2777 displaySpinner(text);
2778}
2779
2793 const char *message,
2794 const char *subMessage,
2795 const char *confirmText,
2796 const char *cancelText,
2797 nbgl_choiceCallback_t callback)
2798{
2800 icon, message, subMessage, confirmText, cancelText, NULL, callback);
2801};
2802
2818 const char *message,
2819 const char *subMessage,
2820 const char *confirmText,
2821 const char *cancelText,
2822 nbgl_genericDetails_t *details,
2823 nbgl_choiceCallback_t callback)
2824{
2825 memset(&context, 0, sizeof(UseCaseContext_t));
2826 context.type = CHOICE_USE_CASE;
2827 context.choice.icon = icon;
2828 context.choice.message = message;
2829 context.choice.subMessage = subMessage;
2830 context.choice.confirmText = confirmText;
2831 context.choice.cancelText = cancelText;
2832 context.choice.onChoice = callback;
2833 context.choice.details = details;
2834 context.currentPage = 0;
2835 context.nbPages = 2; // 2 pages for confirm/cancel
2836 if (message != NULL) {
2837 context.nbPages++;
2838 // if both icon and subMessage are non NULL, add a page
2839 if ((icon != NULL) && (subMessage != NULL)) {
2840 context.nbPages++;
2841 }
2842 }
2843 if (details != NULL) {
2844 // only the first level of details and BAR_LIST type are supported
2845 if (details->type == BAR_LIST_WARNING) {
2846 context.nbPages += details->barList.nbBars;
2847 }
2848 }
2849
2850 displayChoicePage(FORWARD_DIRECTION);
2851};
2852
2866void nbgl_useCaseConfirm(const char *message,
2867 const char *subMessage,
2868 const char *confirmText,
2869 const char *cancelText,
2870 nbgl_callback_t callback)
2871{
2872 memset(&context, 0, sizeof(UseCaseContext_t));
2873 context.type = CONFIRM_USE_CASE;
2874 context.confirm.message = message;
2875 context.confirm.subMessage = subMessage;
2876 context.confirm.confirmText = confirmText;
2877 context.confirm.cancelText = cancelText;
2878 context.confirm.onConfirm = callback;
2879 context.currentPage = 0;
2880 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
2881
2882 displayConfirm(FORWARD_DIRECTION);
2883}
2884
2895 const char *message,
2896 const char *actionText,
2897 nbgl_callback_t callback)
2898{
2899 nbgl_layoutCenteredInfo_t centeredInfo = {0};
2900
2901 UNUSED(actionText);
2902
2903 // memorize callback in context
2904 memset(&context, 0, sizeof(UseCaseContext_t));
2905 context.type = ACTION_USE_CASE;
2906 context.action.actionCallback = callback;
2907
2908 centeredInfo.icon = icon;
2909 centeredInfo.text1 = message;
2910 centeredInfo.style = BOLD_TEXT1_INFO;
2911 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
2912}
2913
2914#ifdef NBGL_KEYPAD
2933void nbgl_useCaseKeypad(const char *title,
2934 uint8_t minDigits,
2935 uint8_t maxDigits,
2936 bool shuffled,
2937 bool hidden,
2938 nbgl_pinValidCallback_t validatePinCallback,
2939 nbgl_callback_t backCallback)
2940{
2941 nbgl_layoutDescription_t layoutDescription = {0};
2942 int status = -1;
2943
2944 // reset the keypad context
2945 memset(&context, 0, sizeof(KeypadContext_t));
2946 context.type = KEYPAD_USE_CASE;
2947 context.currentPage = 0;
2948 context.nbPages = 1;
2949 context.keypad.validatePin = validatePinCallback;
2950 context.keypad.backCallback = backCallback;
2951 context.keypad.pinMinDigits = minDigits;
2952 context.keypad.pinMaxDigits = maxDigits;
2953 context.keypad.hidden = hidden;
2954 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
2955
2956 // add keypad
2957 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
2958 if (status < 0) {
2959 return;
2960 }
2961 context.keypad.keypadIndex = status;
2962 // add digits
2963 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
2964 if (status < 0) {
2965 return;
2966 }
2967
2968 nbgl_layoutDraw(context.keypad.layoutCtx);
2969 if (context.keypad.backCallback != NULL) {
2970 // force backspace to be visible at first digit, to be used as quit
2971 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
2972 }
2973 nbgl_refresh();
2974}
2975#endif // NBGL_KEYPAD
2976
2977#endif // HAVE_SE_TOUCH
2978#endif // NBGL_USE_CASE
nbgl_contentCenteredInfoStyle_t
possible styles for Centered Info Area
@ CHOICES_LIST
list of choices through radio buttons
@ CENTERED_INFO
a centered info
@ SWITCHES_LIST
list of switches with descriptions
@ INFOS_LIST
list of infos with titles
@ TAG_VALUE_LIST
list of tag/value pairs
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
@ INFO_BUTTON
a centered info and a simple black button
@ INFO_LIST_ALIAS
alias is list of infos
@ TAG_VALUE_LIST_ALIAS
@ ENS_ALIAS
alias comes from ENS
void(* nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
prototype of function to be called when an action on a content object occurs
debug traces management
#define LOG_WARN(__logger,...)
Definition nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition nbgl_debug.h:35
void(* nbgl_stepCallback_t)(void)
prototype of function to be called when a step is using a callback on "double-key" action
Definition nbgl_flow.h:38
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
int nbgl_layoutUpdateKeypad(nbgl_layout_t *layout, uint8_t index, bool enableValidate, bool enableBackspace, bool enableDigits)
Updates an existing keypad on bottom of the screen, with the given configuration.
int nbgl_layoutDraw(nbgl_layout_t *layout)
Applies given layout. The screen will be redrawn.
void * nbgl_layout_t
type shared externally
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
void nbgl_refresh(void)
This functions refreshes the actual screen on display with what has changed since the last refresh.
Definition nbgl_obj.c:1720
nbgl_buttonEvent_t
Definition nbgl_obj.h:307
@ BUTTON_BOTH_PRESSED
Sent when both buttons are released.
Definition nbgl_obj.h:314
@ BUTTON_LEFT_PRESSED
Sent when Left button is released.
Definition nbgl_obj.h:308
@ BUTTON_RIGHT_PRESSED
Send when Right button is released.
Definition nbgl_obj.h:309
#define BACKSPACE_KEY
Definition nbgl_obj.h:26
#define VALIDATE_KEY
Definition nbgl_obj.h:27
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition nbgl_screen.c:66
void(* nbgl_stepButtonCallback_t)(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
prototype of function to be called when buttons are touched on a screen
Definition nbgl_step.h:57
#define GET_POS_OF_STEP(_step, _nb_steps)
Definition nbgl_step.h:30
nbgl_step_t nbgl_stepDrawSwitch(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutSwitch_t *switchInfo, bool modal)
void * nbgl_step_t
type shared externally
Definition nbgl_step.h:44
uint8_t nbgl_stepPosition_t
this type is a bitfield containing:
Definition nbgl_step.h:88
nbgl_step_t nbgl_stepDrawMenuList(nbgl_stepMenuListCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutMenuList_t *list, bool modal)
#define ACTION_ON_ANY_BUTTON
When action callback applies only on both button press.
Definition nbgl_step.h:75
@ NEITHER_FIRST_NOR_LAST_STEP
neither first nor last in a multiple steps flow
Definition nbgl_step.h:67
@ SINGLE_STEP
single step flow
Definition nbgl_step.h:64
@ LAST_STEP
last in a multiple steps flow
Definition nbgl_step.h:66
@ FIRST_STEP
first in a multiple steps flow
Definition nbgl_step.h:65
int nbgl_stepRelease(nbgl_step_t step)
#define FORWARD_DIRECTION
When the flow is navigated from last to first step.
Definition nbgl_step.h:71
nbgl_step_t nbgl_stepDrawText(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, const char *text, const char *subText, nbgl_contentCenteredInfoStyle_t style, bool modal)
nbgl_step_t nbgl_stepDrawCenteredInfo(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutCenteredInfo_t *info, bool modal)
#define BACKWARD_DIRECTION
When action callback applies on any button press.
Definition nbgl_step.h:73
nbgl_state_t
to represent a boolean state.
Definition nbgl_types.h:199
@ ON_STATE
Definition nbgl_types.h:201
@ OFF_STATE
Definition nbgl_types.h:200
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
@ CENTERED_INFO_WARNING
Centered info.
@ BAR_LIST_WARNING
list of touchable bars, to display sub-pages
uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool isSkippable, bool *requireSpecificDisplay)
void(* nbgl_callback_t)(void)
prototype of generic callback function
#define SKIPPABLE_OPERATION
This is to use in nbgl_operationType_t when the operation is skippable. This is used.
void nbgl_useCaseGenericSettings(const char *appName, uint8_t initPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, nbgl_callback_t quitCallback)
uint32_t nbgl_operationType_t
This mask is used to describe the type of operation to review with additional options It is a mask of...
void nbgl_useCaseReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAction(const nbgl_icon_details_t *icon, const char *message, const char *actionText, nbgl_callback_t callback)
uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs, const nbgl_contentTagValueList_t *tagValueList, uint8_t startIndex, bool *requireSpecificDisplay)
uint8_t nbgl_useCaseGetNbPagesForTagValueList(const nbgl_contentTagValueList_t *tagValueList)
void(* nbgl_pinValidCallback_t)(const uint8_t *pin, uint8_t pinLen)
prototype of pin validation callback function
void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseHomeAndSettings(const char *appName, const nbgl_icon_details_t *appIcon, const char *tagline, const uint8_t initSettingPage, const nbgl_genericContents_t *settingContents, const nbgl_contentInfoList_t *infosList, const nbgl_homeAction_t *action, nbgl_callback_t quitCallback)
uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos, const nbgl_contentInfoList_t *infosList, uint8_t startIndex, bool withNav)
void(* nbgl_choiceCallback_t)(bool confirm)
prototype of choice callback function
uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars, const nbgl_contentBarsList_t *barsList, uint8_t startIndex, bool withNav)
void nbgl_useCaseNavigableContent(const char *title, uint8_t initPage, uint8_t nbPages, nbgl_callback_t quitCallback, nbgl_navCallback_t navCallback, nbgl_layoutTouchCallback_t controlsCallback)
void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseChoiceWithDetails(const nbgl_icon_details_t *icon, const char *message, const char *subMessage, const char *confirmText, const char *cancelText, nbgl_warningDetails_t *details, nbgl_choiceCallback_t callback)
void nbgl_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
@ W3C_THREAT_DETECTED_WARN
Web3 Checks: Threat detected (see reportRisk field)
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_NO_THREAT_WARN
Web3 Checks: No Threat detected.
@ W3C_RISK_DETECTED_WARN
Web3 Checks: Risk detected (see reportRisk field)
void nbgl_useCaseGenericConfiguration(const char *title, uint8_t initPage, const nbgl_genericContents_t *contents, nbgl_callback_t quitCallback)
#define INIT_HOME_PAGE
Value to pass to nbgl_useCaseHomeAndSettings() initSettingPage parameter to initialize the use case o...
void nbgl_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
void nbgl_useCaseAddressReview(const char *address, const nbgl_contentTagValueList_t *additionalTagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
@ TYPE_MESSAGE
@ TYPE_TRANSACTION
For operations transferring a coin or taken from an account to another.
void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches, const nbgl_contentSwitchesList_t *switchesList, uint8_t startIndex, bool withNav)
This structure contains data to build a BARS_LIST content.
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
uint8_t nbBars
number of elements in barTexts and tokens array
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
bool onTop
if set to true, align only horizontally
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * buttonText
text of the long press button
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * text
centered text in large case
This structure contains data to build a INFOS_LIST content.
const char *const * infoContents
array of contents of infos (in black)
const char *const * infoTypes
array of types of infos (in black/bold)
uint8_t nbInfos
number of elements in infoTypes and infoContents array
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
uint8_t token
the token that will be used as argument of the callback
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
const char * text
main text for the switch
uint8_t token
the token that will be used as argument of the callback (unused on Nano)
nbgl_state_t initState
initial state of the switch
const char * subText
description under main text (NULL terminated, single line, may be null)
This structure contains a list of [tag,value] pairs.
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair and possible extensions.
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
const nbgl_icon_details_t * valueIcon
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
const char * value
string giving the value name
const char * item
string giving the tag name
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
This structure contains data to build a content.
nbgl_content_u content
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
nbgl_contentType_t type
type of page content in the content union
uint8_t nbBars
number of touchable bars
uint8_t nbContents
number of contents
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
nbgl_contentCallback_t contentGetterCallback
function to call to retrieve a given content
The necessary parameters to build the page(s) displayed when the top-right button is touched in intro...
nbgl_genericBarList_t barList
touchable bars list, if type == BAR_LIST_WARNING
nbgl_genericDetailsType_t type
type of content in the page, determining what to use in the following union
Structure describing the action button in Home Screen.
Structure containing all information when creating a layout. This structure must be passed as argumen...
This structure contains a list of names to build a menu list on Nanos, with for each item a descripti...
nbgl_menuListCallback_t callback
function to call to retrieve a menu list item text
uint8_t nbChoices
total number of choices in the menu list
uint8_t selectedChoice
index of the selected choice (centered, in bold)
This structure contains data to build a page in multi-pages mode (nbgl_pageDrawGenericContent)
Definition nbgl_flow.h:58
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition nbgl_flow.h:66
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:64
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:67
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:65
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition nbgl_flow.h:63
nbgl_contentType_t type
type of page content in the following union
Definition nbgl_flow.h:59
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition nbgl_flow.h:61
This structure contains data to build a SWITCHES_LIST content.
uint8_t nbSwitches
number of elements in switches and tokens array
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
The necessary parameters to build a warning page preceding a review. One can either use predefinedSet...
const nbgl_warningDetails_t * introDetails
const nbgl_warningDetails_t * reviewDetails
uint32_t predefinedSet
nbgl_contentInfoList_t infosList
INFOS_LIST type
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
nbgl_contentBarsList_t barsList
BARS_LIST type
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type