Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_use_case_nanos.c
Go to the documentation of this file.
1
6#ifdef NBGL_USE_CASE
7#ifndef HAVE_SE_TOUCH
8/*********************
9 * INCLUDES
10 *********************/
11#include <string.h>
12#include <stdio.h>
13#include "nbgl_debug.h"
14#include "nbgl_use_case.h"
15#include "glyphs.h"
16#include "os_pic.h"
17#include "os_print.h"
18#include "os_helpers.h"
19#include "ux.h"
20
21/*********************
22 * DEFINES
23 *********************/
24#define WITH_HORIZONTAL_CHOICES_LIST
25#define WITH_HORIZONTAL_BARS_LIST
26
27/**********************
28 * TYPEDEFS
29 **********************/
30
31typedef struct ReviewContext_s {
32 nbgl_choiceCallback_t onChoice;
33 const nbgl_contentTagValueList_t *tagValueList;
34 const nbgl_icon_details_t *icon;
35 const char *reviewTitle;
36 const char *reviewSubTitle;
37 const char *finishTitle;
38 const char *address; // for address confirmation review
39 nbgl_callback_t skipCallback; // callback provided by used
40 uint8_t nbDataSets; // number of sets of data received by StreamingContinue
41 bool skipDisplay; // if set to true, means that we are displaying the skip page
42 uint8_t dataDirection; // used to know whether the skip page is reached from back or forward
43 uint8_t currentTagValueIndex;
44 uint8_t currentExtensionPage;
45 uint8_t nbExtensionPages;
46 const nbgl_contentValueExt_t *extension;
47 nbgl_step_t extensionStepCtx;
48
49} ReviewContext_t;
50
51typedef struct ChoiceContext_s {
52 const nbgl_icon_details_t *icon;
53 const char *message;
54 const char *subMessage;
55 const char *confirmText;
56 const char *cancelText;
57 nbgl_choiceCallback_t onChoice;
58 const nbgl_genericDetails_t *details;
59} ChoiceContext_t;
60
61typedef struct ConfirmContext_s {
62 const char *message;
63 const char *subMessage;
64 const char *confirmText;
65 const char *cancelText;
66 nbgl_callback_t onConfirm;
67 nbgl_step_t currentStep;
68} ConfirmContext_t;
69
70typedef struct ContentContext_s {
71 const char *title; // For CHOICES_LIST /BARS_LIST
72 nbgl_genericContents_t genericContents;
73 const char *rejectText;
74 nbgl_layoutTouchCallback_t controlsCallback;
75 nbgl_navCallback_t navCallback;
76 nbgl_callback_t quitCallback;
77} ContentContext_t;
78
79typedef struct HomeContext_s {
80 const char *appName;
81 const nbgl_icon_details_t *appIcon;
82 const char *tagline;
83 const nbgl_genericContents_t *settingContents;
84 const nbgl_contentInfoList_t *infosList;
85 const nbgl_homeAction_t *homeAction;
86 nbgl_callback_t quitCallback;
87} HomeContext_t;
88
89typedef struct ActionContext_s {
90 nbgl_callback_t actionCallback;
91} ActionContext_t;
92
93#ifdef NBGL_KEYPAD
94typedef struct KeypadContext_s {
95 uint8_t pinEntry[8];
96 uint8_t pinLen;
97 uint8_t pinMinDigits;
98 uint8_t pinMaxDigits;
99 nbgl_layout_t *layoutCtx;
100 bool hidden;
101 uint8_t keypadIndex;
102 nbgl_pinValidCallback_t validatePin;
103 nbgl_callback_t backCallback;
104} KeypadContext_t;
105#endif
106
107#ifdef NBGL_KEYBOARD
108typedef struct KeyboardContext_s {
110 char *entryBuffer;
111 uint16_t entryMaxLen;
112 nbgl_layout_t *layoutCtx;
113 uint8_t keyboardIndex;
114 uint8_t textIndex;
115 nbgl_callback_t actionCallback;
116 nbgl_callback_t backCallback;
117 nbgl_keyboardButtonsCallback_t getSuggestButtons;
118 nbgl_layoutTouchCallback_t onButtonCallback;
119} KeyboardContext_t;
120#endif
121
122typedef enum {
123 NONE_USE_CASE,
124 SPINNER_USE_CASE,
125 REVIEW_USE_CASE,
126 GENERIC_REVIEW_USE_CASE,
127 ADDRESS_REVIEW_USE_CASE,
128 STREAMING_START_REVIEW_USE_CASE,
129 STREAMING_CONTINUE_REVIEW_USE_CASE,
130 STREAMING_FINISH_REVIEW_USE_CASE,
131 CHOICE_USE_CASE,
132 STATUS_USE_CASE,
133 CONFIRM_USE_CASE,
134 KEYPAD_USE_CASE,
135 KEYBOARD_USE_CASE,
136 HOME_USE_CASE,
137 INFO_USE_CASE,
138 SETTINGS_USE_CASE,
139 GENERIC_SETTINGS,
140 CONTENT_USE_CASE,
141 ACTION_USE_CASE
142} ContextType_t;
143
144typedef struct UseCaseContext_s {
145 ContextType_t type;
146 nbgl_operationType_t operationType;
147 uint8_t nbPages;
148 uint8_t currentPage;
149 uint8_t firstPairPage;
150 bool forceAction;
152 stepCallback;
153 union {
154 ReviewContext_t review;
155 ChoiceContext_t choice;
156 ConfirmContext_t confirm;
157 HomeContext_t home;
158 ContentContext_t content;
159#ifdef NBGL_KEYPAD
160 KeypadContext_t keypad;
161#endif
162#ifdef NBGL_KEYBOARD
163 KeyboardContext_t keyboard;
164#endif
165 ActionContext_t action;
166 };
167} UseCaseContext_t;
168
169typedef struct PageContent_s {
170 bool isSwitch;
171 const char *text;
172 const char *subText;
173 const nbgl_icon_details_t *icon;
174 const nbgl_contentValueExt_t *extension;
175 nbgl_state_t state;
176 bool isCenteredInfo;
177 bool isAction;
178} PageContent_t;
179
180typedef struct ReviewWithWarningContext_s {
181 ContextType_t type;
182 nbgl_operationType_t operationType;
183 const nbgl_contentTagValueList_t *tagValueList;
184 const nbgl_icon_details_t *icon;
185 const char *reviewTitle;
186 const char *reviewSubTitle;
187 const char *finishTitle;
188 const nbgl_warning_t *warning;
189 nbgl_choiceCallback_t choiceCallback;
190 uint8_t securityReportLevel; // level 1 is the first level of menus
191 bool isIntro; // set to true during intro (before actual review)
192 uint8_t warningPage;
193 uint8_t nbWarningPages;
194 uint8_t firstWarningPage;
195 uint8_t barDetailIdx; // index of the bar whose detail is displayed
196} ReviewWithWarningContext_t;
197
198typedef enum {
199 NO_FORCED_TYPE = 0,
200 FORCE_BUTTON,
201 FORCE_CENTERED_INFO
202} ForcedType_t;
203
204/**********************
205 * STATIC VARIABLES
206 **********************/
207static UseCaseContext_t context;
208
209static ReviewWithWarningContext_t reviewWithWarnCtx;
210// configuration of warning when using @ref nbgl_useCaseReviewBlindSigning()
211static const nbgl_warning_t blindSigningWarning = {.predefinedSet = (1 << BLIND_SIGNING_WARN)};
212
213// Operation type for streaming (because the one in 'context' is reset at each streaming API call)
214nbgl_operationType_t streamingOpType;
215
216/**********************
217 * STATIC FUNCTIONS
218 **********************/
219static void displayReviewPage(nbgl_stepPosition_t pos);
220static void displayStreamingReviewPage(nbgl_stepPosition_t pos);
221static void displayHomePage(nbgl_stepPosition_t pos);
222static void displayInfoPage(nbgl_stepPosition_t pos);
223static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state);
224static void displayChoicePage(nbgl_stepPosition_t pos);
225static void displayConfirm(nbgl_stepPosition_t pos);
226static void displayContent(nbgl_stepPosition_t pos, bool toogle_state);
227static void displaySpinner(const char *text);
228
229static void startUseCaseHome(void);
230static void startUseCaseInfo(void);
231static void startUseCaseSettings(void);
232static void startUseCaseSettingsAtPage(uint8_t initSettingPage);
233static void startUseCaseContent(void);
234
235static void statusTickerCallback(void);
236
237static void displayExtensionStep(nbgl_stepPosition_t pos);
238static void displayWarningStep(void);
239
240// Simple helper to get the number of elements inside a nbgl_content_t
241static uint8_t getContentNbElement(const nbgl_content_t *content)
242{
243 switch (content->type) {
244 case CENTERED_INFO:
245 return 1;
246 case INFO_BUTTON:
247 return 1;
248 case TAG_VALUE_LIST:
249 return content->content.tagValueList.nbPairs;
251 // last element is for Confirm page
252 return content->content.tagValueConfirm.tagValueList.nbPairs + 1;
253 case SWITCHES_LIST:
254 return content->content.switchesList.nbSwitches;
255 case INFOS_LIST:
256 return content->content.infosList.nbInfos;
257 case CHOICES_LIST:
258 return content->content.choicesList.nbChoices;
259 case BARS_LIST:
260 return content->content.barsList.nbBars;
261 default:
262 return 0;
263 }
264}
265
266// Helper to retrieve the content inside a nbgl_genericContents_t using
267// either the contentsList or using the contentGetterCallback
268static const nbgl_content_t *getContentAtIdx(const nbgl_genericContents_t *genericContents,
269 uint8_t contentIdx,
270 nbgl_content_t *content)
271{
272 nbgl_pageContent_t pageContent = {0};
273 if (contentIdx >= genericContents->nbContents) {
274 LOG_DEBUG(USE_CASE_LOGGER, "No content available at %d\n", contentIdx);
275 return NULL;
276 }
277
278 if (genericContents->callbackCallNeeded) {
279 if (content == NULL) {
280 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content variable\n");
281 return NULL;
282 }
283 // Retrieve content through callback, but first memset the content.
284 memset(content, 0, sizeof(nbgl_content_t));
285 if (context.content.navCallback) {
286 if (context.content.navCallback(contentIdx, &pageContent) == true) {
287 // Copy the Page Content to the Content variable
288 content->type = pageContent.type;
289 switch (content->type) {
290 case CENTERED_INFO:
291 content->content.centeredInfo = pageContent.centeredInfo;
292 break;
293 case INFO_BUTTON:
294 content->content.infoButton = pageContent.infoButton;
295 break;
296 case TAG_VALUE_LIST:
297 content->content.tagValueList = pageContent.tagValueList;
298 break;
300 content->content.tagValueConfirm = pageContent.tagValueConfirm;
301 break;
302 case SWITCHES_LIST:
303 content->content.switchesList = pageContent.switchesList;
304 break;
305 case INFOS_LIST:
306 content->content.infosList = pageContent.infosList;
307 break;
308 case CHOICES_LIST:
309 content->content.choicesList = pageContent.choicesList;
310 break;
311 case BARS_LIST:
312 content->content.barsList = pageContent.barsList;
313 break;
314 default:
315 LOG_DEBUG(USE_CASE_LOGGER, "Invalid content type\n");
316 return NULL;
317 }
318 }
319 else {
320 LOG_DEBUG(USE_CASE_LOGGER, "Error getting page content\n");
321 return NULL;
322 }
323 }
324 else {
325 genericContents->contentGetterCallback(contentIdx, content);
326 }
327 return content;
328 }
329 else {
330 // Retrieve content through list
331 return PIC(&genericContents->contentsList[contentIdx]);
332 }
333}
334
335// Helper to retrieve the content inside a nbgl_genericContents_t using
336// either the contentsList or using the contentGetterCallback
337static const nbgl_content_t *getContentElemAtIdx(uint8_t elemIdx,
338 uint8_t *elemContentIdx,
339 nbgl_content_t *content)
340{
341 const nbgl_genericContents_t *genericContents = NULL;
342 const nbgl_content_t *p_content = NULL;
343 uint8_t nbPages = 0;
344 uint8_t elemNbPages = 0;
345
346 switch (context.type) {
347 case SETTINGS_USE_CASE:
348 case HOME_USE_CASE:
349 case GENERIC_SETTINGS:
350 genericContents = context.home.settingContents;
351 break;
352 case CONTENT_USE_CASE:
353 case GENERIC_REVIEW_USE_CASE:
354 genericContents = &context.content.genericContents;
355 break;
356 default:
357 return NULL;
358 }
359 for (uint i = 0; i < genericContents->nbContents; i++) {
360 p_content = getContentAtIdx(genericContents, i, content);
361 elemNbPages = getContentNbElement(p_content);
362 if (nbPages + elemNbPages > elemIdx) {
363 *elemContentIdx = context.currentPage - nbPages;
364 break;
365 }
366 nbPages += elemNbPages;
367 }
368
369 return p_content;
370}
371
372static const char *getChoiceName(uint8_t choiceIndex)
373{
374 uint8_t elemIdx;
375 uint8_t nbValues;
376 const nbgl_content_t *p_content = NULL;
377 nbgl_content_t content = {0};
378 nbgl_contentRadioChoice_t *contentChoices = NULL;
379 nbgl_contentBarsList_t *contentBars = NULL;
380 char **names = NULL;
381
382 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
383 if (p_content == NULL) {
384 return NULL;
385 }
386 switch (p_content->type) {
387 case CHOICES_LIST:
388 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
389 names = (char **) PIC(contentChoices->names);
390 nbValues = contentChoices->nbChoices;
391 break;
392 case BARS_LIST:
393 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
394 names = (char **) PIC(contentBars->barTexts);
395 nbValues = contentBars->nbBars;
396 break;
397 default:
398 // Not supported as vertical MenuList
399 return NULL;
400 }
401 if (choiceIndex >= nbValues) {
402 // Last item is always "Back" button
403 return "Back";
404 }
405 return (const char *) PIC(names[choiceIndex]);
406}
407
408static void onChoiceSelected(uint8_t choiceIndex)
409{
410 uint8_t elemIdx;
411 uint8_t token = 255;
412 const nbgl_content_t *p_content = NULL;
413 nbgl_content_t content = {0};
414 nbgl_contentRadioChoice_t *contentChoices = NULL;
415 nbgl_contentBarsList_t *contentBars = NULL;
416
417 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
418 if (p_content == NULL) {
419 return;
420 }
421 switch (p_content->type) {
422 case CHOICES_LIST:
423 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
424 if (choiceIndex < contentChoices->nbChoices) {
425 token = contentChoices->token;
426 }
427 break;
428 case BARS_LIST:
429 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
430 if (choiceIndex < contentBars->nbBars) {
431 token = contentBars->tokens[choiceIndex];
432 }
433 break;
434 default:
435 // Not supported as vertical MenuList
436 break;
437 }
438 if ((token != 255) && (context.content.controlsCallback != NULL)) {
439 context.content.controlsCallback(token, 0);
440 }
441 else if (context.content.quitCallback != NULL) {
442 context.content.quitCallback();
443 }
444}
445
446static void getPairData(const nbgl_contentTagValueList_t *tagValueList,
447 uint8_t index,
448 const char **item,
449 const char **value,
450 const nbgl_contentValueExt_t **extension,
451 const nbgl_icon_details_t **icon,
452 bool *isCenteredInfo)
453{
454 const nbgl_contentTagValue_t *pair;
455
456 if (tagValueList->pairs != NULL) {
457 pair = PIC(&tagValueList->pairs[index]);
458 }
459 else {
460 pair = PIC(tagValueList->callback(index));
461 }
462 *item = pair->item;
463 *value = pair->value;
464 if (pair->aliasValue) {
465 *extension = pair->extension;
466 }
467 else if (pair->centeredInfo) {
468 *isCenteredInfo = true;
469 *icon = pair->valueIcon;
470 }
471 else {
472 *extension = NULL;
473 }
474}
475
476static void onReviewAccept(void)
477{
478 if (context.review.onChoice) {
479 context.review.onChoice(true);
480 }
481}
482
483static void onReviewReject(void)
484{
485 if (context.review.onChoice) {
486 context.review.onChoice(false);
487 }
488}
489
490static void onChoiceAccept(void)
491{
492 if (context.choice.onChoice) {
493 context.choice.onChoice(true);
494 }
495}
496
497static void onChoiceReject(void)
498{
499 if (context.choice.onChoice) {
500 context.choice.onChoice(false);
501 }
502}
503
504static void onConfirmAccept(void)
505{
506 if (context.confirm.currentStep) {
507 nbgl_stepRelease(context.confirm.currentStep);
508 }
509 if (context.confirm.onConfirm) {
510 context.confirm.onConfirm();
511 }
512}
513
514static void onConfirmReject(void)
515{
516 if (context.confirm.currentStep) {
517 nbgl_stepRelease(context.confirm.currentStep);
519 nbgl_refresh();
520 }
521}
522
523static void onSwitchAction(void)
524{
525 const nbgl_contentSwitch_t *contentSwitch = NULL;
526 const nbgl_content_t *p_content = NULL;
527 nbgl_content_t content = {0};
528 uint8_t elemIdx;
529
530 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
531 if ((p_content == NULL) || (p_content->type != SWITCHES_LIST)) {
532 return;
533 }
534 contentSwitch
535 = &((const nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
536 switch (context.type) {
537 case SETTINGS_USE_CASE:
538 case HOME_USE_CASE:
539 case GENERIC_SETTINGS:
540 displaySettingsPage(FORWARD_DIRECTION, true);
541 break;
542 case CONTENT_USE_CASE:
543 case GENERIC_REVIEW_USE_CASE:
544 displayContent(FORWARD_DIRECTION, true);
545 break;
546 default:
547 break;
548 }
549 if (p_content->contentActionCallback != NULL) {
550 nbgl_contentActionCallback_t actionCallback = PIC(p_content->contentActionCallback);
551 actionCallback(contentSwitch->token,
552 (contentSwitch->initState == ON_STATE) ? OFF_STATE : ON_STATE,
553 context.currentPage);
554 }
555 else if (context.content.controlsCallback != NULL) {
556 context.content.controlsCallback(contentSwitch->token, 0);
557 }
558}
559
560static void drawStep(nbgl_stepPosition_t pos,
561 const nbgl_icon_details_t *icon,
562 const char *txt,
563 const char *subTxt,
564 nbgl_stepButtonCallback_t onActionCallback,
565 bool modal,
566 ForcedType_t forcedType)
567{
568 uint8_t elemIdx;
569 nbgl_step_t newStep = NULL;
570 const nbgl_content_t *p_content = NULL;
571 nbgl_content_t content = {0};
572 nbgl_contentRadioChoice_t *contentChoices = NULL;
573 nbgl_contentBarsList_t *contentBars = NULL;
574 nbgl_screenTickerConfiguration_t *p_ticker = NULL;
575 nbgl_layoutMenuList_t list = {0};
576 nbgl_screenTickerConfiguration_t ticker = {.tickerCallback = PIC(statusTickerCallback),
577 .tickerIntervale = 0, // not periodic
578 .tickerValue = STATUS_SCREEN_DURATION};
579
580 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
581 // if we are in streaming+skip case, enable going backward even for first tag/value of the set
582 // (except the first set) because the set starts with a "skip" page
583 if ((context.type == STREAMING_CONTINUE_REVIEW_USE_CASE)
584 && (context.review.skipCallback != NULL) && (context.review.nbDataSets > 1)) {
585 pos |= LAST_STEP;
586 }
587 if ((context.type == STATUS_USE_CASE) || (context.type == SPINNER_USE_CASE)) {
588 p_ticker = &ticker;
589 }
590 if ((context.type == CONFIRM_USE_CASE) && (context.confirm.currentStep != NULL)) {
591 nbgl_stepRelease(context.confirm.currentStep);
592 }
593
594 if (txt == NULL) {
595 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
596 if (p_content) {
597 switch (p_content->type) {
598 case CHOICES_LIST:
599 contentChoices
600 = ((nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList));
601 list.nbChoices = contentChoices->nbChoices + 1; // For Back button
602 list.selectedChoice = contentChoices->initChoice;
603 list.callback = getChoiceName;
604 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
605 break;
606 case BARS_LIST:
607 contentBars = ((nbgl_contentBarsList_t *) PIC(&p_content->content.barsList));
608 list.nbChoices = contentBars->nbBars + 1; // For Back button
609 list.selectedChoice = 0;
610 list.callback = getChoiceName;
611 newStep = nbgl_stepDrawMenuList(onChoiceSelected, p_ticker, &list, modal);
612 break;
613 default:
614 // Not supported as vertical MenuList
615 break;
616 }
617 }
618 }
619 else if ((icon == NULL) && (forcedType != FORCE_CENTERED_INFO)) {
621 if (subTxt != NULL) {
622 style = (forcedType == FORCE_BUTTON) ? BUTTON_INFO : BOLD_TEXT1_INFO;
623 }
624 else {
625 style = REGULAR_INFO;
626 }
627 newStep = nbgl_stepDrawText(pos, onActionCallback, p_ticker, txt, subTxt, style, modal);
628 }
629 else {
631 info.icon = icon;
632 info.text1 = txt;
633 info.text2 = subTxt;
634 info.onTop = false;
635 if ((subTxt != NULL) || (context.stepCallback != NULL) || context.forceAction) {
636 info.style = BOLD_TEXT1_INFO;
637 }
638 else {
639 info.style = REGULAR_INFO;
640 }
641 newStep = nbgl_stepDrawCenteredInfo(pos, onActionCallback, p_ticker, &info, modal);
642 }
643 if (context.type == CONFIRM_USE_CASE) {
644 context.confirm.currentStep = newStep;
645 }
646}
647
648static void drawSwitchStep(nbgl_stepPosition_t pos,
649 const char *title,
650 const char *description,
651 bool state,
652 nbgl_stepButtonCallback_t onActionCallback,
653 bool modal)
654{
655 nbgl_layoutSwitch_t switchInfo;
656
657 pos |= GET_POS_OF_STEP(context.currentPage, context.nbPages);
658 switchInfo.initState = state;
659 switchInfo.text = title;
660 switchInfo.subText = description;
661 nbgl_stepDrawSwitch(pos, onActionCallback, NULL, &switchInfo, modal);
662}
663
664static bool buttonGenericCallback(nbgl_buttonEvent_t event, nbgl_stepPosition_t *pos)
665{
666 uint8_t elemIdx;
667 uint8_t token = 0;
668 uint8_t index = 0;
669 const nbgl_content_t *p_content = NULL;
670 nbgl_content_t content = {0};
671
672 if (event == BUTTON_LEFT_PRESSED) {
673 if (context.currentPage > 0) {
674 context.currentPage--;
675 }
676 // in streaming+skip case, it is allowed to go backward at the first tag/value, except for
677 // the first set
678 else if ((context.type != STREAMING_CONTINUE_REVIEW_USE_CASE)
679 || (context.review.skipCallback == NULL) || (context.review.nbDataSets == 1)) {
680 // Drop the event
681 return false;
682 }
683 *pos = BACKWARD_DIRECTION;
684 }
685 else if (event == BUTTON_RIGHT_PRESSED) {
686 if (context.currentPage < (int) (context.nbPages - 1)) {
687 context.currentPage++;
688 }
689 else {
690 // Drop the event
691 return false;
692 }
693 *pos = FORWARD_DIRECTION;
694 }
695 else {
696 if (event == BUTTON_BOTH_PRESSED) {
697 if (context.stepCallback != NULL) {
698 context.stepCallback();
699 }
700 else if ((context.type == CONTENT_USE_CASE) || (context.type == SETTINGS_USE_CASE)
701 || (context.type == GENERIC_SETTINGS)
702 || (context.type == GENERIC_REVIEW_USE_CASE)) {
703 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
704 if (p_content != NULL) {
705 switch (p_content->type) {
706 case CENTERED_INFO:
707 // No associated callback
708 return false;
709 case INFO_BUTTON:
710 token = p_content->content.infoButton.buttonToken;
711 break;
712 case SWITCHES_LIST:
713 token = p_content->content.switchesList.switches->token;
714 break;
715 case BARS_LIST:
716 token = p_content->content.barsList.tokens[context.currentPage];
717 break;
718 case CHOICES_LIST:
719 token = p_content->content.choicesList.token;
720 index = context.currentPage;
721 break;
722 case TAG_VALUE_LIST:
723 return false;
725 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
726 return false;
727 }
728 token = p_content->content.tagValueConfirm.confirmationToken;
729 break;
730 default:
731 break;
732 }
733
734 if ((p_content) && (p_content->contentActionCallback != NULL)) {
735 p_content->contentActionCallback(token, 0, context.currentPage);
736 }
737 else if (context.content.controlsCallback != NULL) {
738 context.content.controlsCallback(token, index);
739 }
740 }
741 }
742 }
743 return false;
744 }
745 return true;
746}
747
748static void reviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
749{
750 UNUSED(stepCtx);
752
753 if (!buttonGenericCallback(event, &pos)) {
754 return;
755 }
756 else {
757 // memorize last direction
758 context.review.dataDirection = pos;
759 }
760 displayReviewPage(pos);
761}
762
763// this is the callback used when button action on the "skip" page
764static void buttonSkipCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
765{
766 UNUSED(stepCtx);
768
769 if (event == BUTTON_LEFT_PRESSED) {
770 // only decrement page if we are going backward but coming from forward (back & forth)
771 if ((context.review.dataDirection == FORWARD_DIRECTION)
772 && (context.currentPage > context.firstPairPage)) {
773 context.currentPage--;
774 }
775 pos = BACKWARD_DIRECTION;
776 }
777 else if (event == BUTTON_RIGHT_PRESSED) {
778 // only increment page if we are going forward but coming from backward (back & forth)
779 if ((context.review.dataDirection == BACKWARD_DIRECTION)
780 && (context.currentPage < (int) (context.nbPages - 1))
781 && (context.currentPage > context.firstPairPage)) {
782 context.currentPage++;
783 }
784 pos = FORWARD_DIRECTION;
785 }
786 else if (event == BUTTON_BOTH_PRESSED) {
787 // the first tag/value page is at page 0 only in streaming case
788 if (context.firstPairPage == 0) {
789 // in streaming, we have to call the provided callback
790 context.review.skipCallback();
791 }
792 else {
793 // if not in streaming, go directly to the "approve" page
794 context.currentPage = context.nbPages - 2;
795 displayReviewPage(FORWARD_DIRECTION);
796 }
797 return;
798 }
799 else {
800 return;
801 }
802 // the first tag/value page is at page 0 only in streaming case
803 if (context.firstPairPage == 0) {
804 displayStreamingReviewPage(pos);
805 }
806 else {
807 displayReviewPage(pos);
808 }
809}
810
811// this is the callback used when buttons in "Action" use case are pressed
812static void useCaseActionCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
813{
814 UNUSED(stepCtx);
815
816 if (event == BUTTON_BOTH_PRESSED) {
817 context.action.actionCallback();
818 }
819}
820
821static void streamingReviewCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
822{
824 UNUSED(stepCtx);
825
826 if (!buttonGenericCallback(event, &pos)) {
827 return;
828 }
829 else {
830 // memorize last direction
831 context.review.dataDirection = pos;
832 }
833
834 displayStreamingReviewPage(pos);
835}
836
837static void settingsCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
838{
839 UNUSED(stepCtx);
841
842 if (!buttonGenericCallback(event, &pos)) {
843 return;
844 }
845
846 displaySettingsPage(pos, false);
847}
848
849static void infoCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
850{
851 UNUSED(stepCtx);
853
854 if (!buttonGenericCallback(event, &pos)) {
855 return;
856 }
857
858 displayInfoPage(pos);
859}
860
861static void homeCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
862{
863 UNUSED(stepCtx);
865
866 if (!buttonGenericCallback(event, &pos)) {
867 return;
868 }
869
870 displayHomePage(pos);
871}
872
873static void genericChoiceCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
874{
875 UNUSED(stepCtx);
877
878 if (!buttonGenericCallback(event, &pos)) {
879 return;
880 }
881
882 displayChoicePage(pos);
883}
884
885static void genericConfirmCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
886{
887 UNUSED(stepCtx);
889
890 if (!buttonGenericCallback(event, &pos)) {
891 return;
892 }
893
894 displayConfirm(pos);
895}
896
897static void statusButtonCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
898{
899 UNUSED(stepCtx);
900 // any button press should dismiss the status screen
901 if ((event == BUTTON_BOTH_PRESSED) || (event == BUTTON_LEFT_PRESSED)
902 || (event == BUTTON_RIGHT_PRESSED)) {
903 if (context.stepCallback != NULL) {
904 context.stepCallback();
905 }
906 }
907}
908
909static void contentCallback(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
910{
911 UNUSED(stepCtx);
913
914 if (!buttonGenericCallback(event, &pos)) {
915 return;
916 }
917
918 displayContent(pos, false);
919}
920
921// callback used for timeout
922static void statusTickerCallback(void)
923{
924 if (context.stepCallback != NULL) {
925 context.stepCallback();
926 }
927}
928
929// this is the callback used when navigating in extension pages
930static void extensionNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
931{
933 UNUSED(stepCtx);
934
935 if (event == BUTTON_LEFT_PRESSED) {
936 // only decrement page if we are not at the first page
937 if (context.review.currentExtensionPage > 0) {
938 context.review.currentExtensionPage--;
939 }
940 pos = BACKWARD_DIRECTION;
941 }
942 else if (event == BUTTON_RIGHT_PRESSED) {
943 // only increment page if not at last page
944 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
945 context.review.currentExtensionPage++;
946 }
947 pos = FORWARD_DIRECTION;
948 }
949 else if (event == BUTTON_BOTH_PRESSED) {
950 // if at last page, leave modal context
951 if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
952 nbgl_stepRelease(context.review.extensionStepCtx);
954 nbgl_refresh();
955 }
956 return;
957 }
958 else {
959 return;
960 }
961 displayExtensionStep(pos);
962}
963
964// function used to display the extension pages
965static void displayExtensionStep(nbgl_stepPosition_t pos)
966{
967 nbgl_layoutCenteredInfo_t info = {0};
968 const nbgl_contentTagValueList_t *tagValueList = NULL;
969 const nbgl_contentInfoList_t *infoList = NULL;
970 const char *text = NULL;
971 const char *subText = NULL;
972
973 if (context.review.extensionStepCtx != NULL) {
974 nbgl_stepRelease(context.review.extensionStepCtx);
975 }
976 if (context.review.currentExtensionPage < (context.review.nbExtensionPages - 1)) {
977 if (context.review.currentExtensionPage == 0) {
978 pos |= FIRST_STEP;
979 }
980 else {
982 }
983
984 switch (context.review.extension->aliasType) {
985 case ENS_ALIAS:
986 text = context.review.extension->title;
987 subText = context.review.extension->fullValue;
988 break;
989 case ADDRESS_BOOK_ALIAS: {
990 bool has_scope = (context.review.extension->aliasSubName != NULL);
991 bool has_tn = (context.review.extension->explanation != NULL);
992 uint8_t page = context.review.currentExtensionPage;
993 if (page == 0) {
994 text = context.review.extension->title;
995 subText = has_scope ? context.review.extension->aliasSubName
996 : context.review.extension->fullValue;
997 }
998 else if (has_scope && page == 1) {
999 text = context.review.extension->title;
1000 subText = context.review.extension->fullValue;
1001 }
1002 else if (has_tn) {
1003 text = context.review.extension->explanation;
1004 }
1005 break;
1006 }
1007 case INFO_LIST_ALIAS:
1008 infoList = context.review.extension->infolist;
1009 text = PIC(infoList->infoTypes[context.review.currentExtensionPage]);
1010 subText = PIC(infoList->infoContents[context.review.currentExtensionPage]);
1011 break;
1013 tagValueList = context.review.extension->tagValuelist;
1014 text = PIC(tagValueList->pairs[context.review.currentExtensionPage].item);
1015 subText = PIC(tagValueList->pairs[context.review.currentExtensionPage].value);
1016 break;
1017 default:
1018 break;
1019 }
1020 if (text != NULL) {
1021 context.review.extensionStepCtx = nbgl_stepDrawText(
1022 pos, extensionNavigate, NULL, text, subText, BOLD_TEXT1_INFO, true);
1023 }
1024 }
1025 else if (context.review.currentExtensionPage == (context.review.nbExtensionPages - 1)) {
1026 // draw the back page
1027 info.icon = &C_icon_back_x;
1028 info.text1 = "Back";
1029 info.style = BOLD_TEXT1_INFO;
1030 pos |= LAST_STEP;
1031 context.review.extensionStepCtx
1032 = nbgl_stepDrawCenteredInfo(pos, extensionNavigate, NULL, &info, true);
1033 }
1034 nbgl_refresh();
1035}
1036
1037static void displayAliasFullValue(void)
1038{
1039 const char *text = NULL;
1040 const char *subText = NULL;
1041 const nbgl_icon_details_t *icon;
1042 bool isCenteredInfo;
1043
1044 getPairData(context.review.tagValueList,
1045 context.review.currentTagValueIndex,
1046 &text,
1047 &subText,
1048 &context.review.extension,
1049 &icon,
1050 &isCenteredInfo);
1051 if (context.review.extension == NULL) {
1052 // probably an error
1054 "displayAliasFullValue: extension nor found for pair %d\n",
1055 context.review.currentTagValueIndex);
1056 return;
1057 }
1058 context.review.currentExtensionPage = 0;
1059 context.review.extensionStepCtx = NULL;
1060 // create a modal flow to display this extension
1061 switch (context.review.extension->aliasType) {
1062 case ENS_ALIAS:
1063 context.review.nbExtensionPages = 2;
1064 break;
1065 case ADDRESS_BOOK_ALIAS:
1066 context.review.nbExtensionPages
1067 = 2 + ((context.review.extension->aliasSubName != NULL) ? 1 : 0)
1068 + ((context.review.extension->explanation != NULL) ? 1 : 0);
1069 break;
1070 case INFO_LIST_ALIAS:
1071 context.review.nbExtensionPages = context.review.extension->infolist->nbInfos + 1;
1072 break;
1074 context.review.nbExtensionPages = context.review.extension->tagValuelist->nbPairs + 1;
1075 break;
1076 default:
1078 "displayAliasFullValue: unsupported alias type %d\n",
1079 context.review.extension->aliasType);
1080 return;
1081 }
1082 displayExtensionStep(FORWARD_DIRECTION);
1083}
1084
1085static void getLastPageInfo(bool approve, const nbgl_icon_details_t **icon, const char **text)
1086{
1087 if (approve) {
1088 // Approve page
1089 *icon = &C_icon_validate_14;
1090 if (context.operationType & ADDRESS_BOOK_OPERATION) {
1091 if ((context.type == REVIEW_USE_CASE) && (context.review.finishTitle != NULL)) {
1092 *text = context.review.finishTitle;
1093 }
1094 else {
1095 *text = "Confirm contact details";
1096 }
1097 }
1098 else if (context.type == ADDRESS_REVIEW_USE_CASE) {
1099 *text = "Confirm";
1100 }
1101 else {
1102 // if finish title is provided, use it
1103 if (context.review.finishTitle != NULL) {
1104 *text = context.review.finishTitle;
1105 }
1106 else {
1107 switch (context.operationType & REAL_TYPE_MASK) {
1108 case TYPE_TRANSACTION:
1109 if (context.operationType & RISKY_OPERATION) {
1110 *text = "Accept risk and sign transaction";
1111 }
1112 else {
1113 *text = "Sign transaction";
1114 }
1115 break;
1116 case TYPE_MESSAGE:
1117 if (context.operationType & RISKY_OPERATION) {
1118 *text = "Accept risk and sign message";
1119 }
1120 else {
1121 *text = "Sign message";
1122 }
1123 break;
1124 default:
1125 if (context.operationType & RISKY_OPERATION) {
1126 *text = "Accept risk and sign operation";
1127 }
1128 else {
1129 *text = "Sign operation";
1130 }
1131 break;
1132 }
1133 }
1134 }
1135 context.stepCallback = onReviewAccept;
1136 }
1137 else {
1138 // Reject page
1139 *icon = &C_icon_crossmark;
1140 if ((context.type == ADDRESS_REVIEW_USE_CASE)
1141 || (context.operationType & ADDRESS_BOOK_OPERATION)) {
1142 *text = "Cancel";
1143 }
1144 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_TRANSACTION) {
1145 *text = "Reject transaction";
1146 }
1147 else if ((context.operationType & REAL_TYPE_MASK) == TYPE_MESSAGE) {
1148 *text = "Reject message";
1149 }
1150 else {
1151 *text = "Reject operation";
1152 }
1153 context.stepCallback = onReviewReject;
1154 }
1155}
1156
1157// function used to display the current page in review
1158static void displayReviewPage(nbgl_stepPosition_t pos)
1159{
1160 uint8_t reviewPages = 0;
1161 uint8_t finalPages = 0;
1162 uint8_t pairIndex = 0;
1163 const char *text = NULL;
1164 const char *subText = NULL;
1165 const nbgl_icon_details_t *icon = NULL;
1166 uint8_t currentIndex = 0;
1167 uint8_t titleIndex = 255;
1168 uint8_t subIndex = 255;
1169 uint8_t approveIndex = 255;
1170 uint8_t rejectIndex = 255;
1171 const nbgl_contentValueExt_t *extension = NULL;
1172 ForcedType_t forcedType = NO_FORCED_TYPE;
1173
1174 context.stepCallback = NULL;
1175
1176 // Determine the 1st page to display tag/values
1177 // Title page to display
1178 titleIndex = currentIndex++;
1179 reviewPages++;
1180 if (context.review.reviewSubTitle) {
1181 // subtitle page to display
1182 subIndex = currentIndex++;
1183 reviewPages++;
1184 }
1185 approveIndex = context.nbPages - 2;
1186 rejectIndex = context.nbPages - 1;
1187 finalPages = approveIndex;
1188
1189 // Determine which page to display
1190 if (context.currentPage >= finalPages) {
1191 if (context.currentPage == approveIndex) {
1192 // Approve page
1193 getLastPageInfo(true, &icon, &text);
1194 }
1195 else if (context.currentPage == rejectIndex) {
1196 // Reject page
1197 getLastPageInfo(false, &icon, &text);
1198 }
1199 }
1200 else if (context.currentPage < reviewPages) {
1201 if (context.currentPage == titleIndex) {
1202 // Title page
1203 icon = context.review.icon;
1204 text = context.review.reviewTitle;
1205 }
1206 else if (context.currentPage == subIndex) {
1207 // SubTitle page
1208 text = context.review.reviewSubTitle;
1209 }
1210 }
1211 else if ((context.review.address != NULL) && (context.currentPage == reviewPages)) {
1212 // address confirmation and 2nd page
1213 text = "Address";
1214 subText = context.review.address;
1215 }
1216 else {
1217 // if there is a skip, and we are not already displaying the "skip" page
1218 // and we are not at the first tag/value of the first set of data (except if going
1219 // backward) then display the "skip" page
1220 if ((context.operationType & SKIPPABLE_OPERATION) && (context.review.skipDisplay == false)
1221 && ((context.currentPage > reviewPages)
1222 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1223 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1224 nbgl_layoutCenteredInfo_t info = {0};
1225 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1226 directions |= LAST_STEP;
1227 }
1228 info.icon = &C_Information_circle_14px;
1229 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1230 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1231 nbgl_refresh();
1232 context.review.skipDisplay = true;
1233 context.firstPairPage = reviewPages;
1234 return;
1235 }
1236 context.review.skipDisplay = false;
1237 bool isCenteredInfo = false;
1238 pairIndex = context.currentPage - reviewPages;
1239 if (context.review.address != NULL) {
1240 pairIndex--;
1241 }
1242 getPairData(context.review.tagValueList,
1243 pairIndex,
1244 &text,
1245 &subText,
1246 &extension,
1247 &icon,
1248 &isCenteredInfo);
1249 if (extension != NULL) {
1250 context.stepCallback = displayAliasFullValue;
1251 context.review.currentTagValueIndex = pairIndex;
1252 forcedType = FORCE_BUTTON;
1253 }
1254 else {
1255 if (isCenteredInfo) {
1256 forcedType = FORCE_CENTERED_INFO;
1257 }
1258 }
1259 }
1260
1261 drawStep(pos, icon, text, subText, reviewCallback, false, forcedType);
1262 nbgl_refresh();
1263}
1264
1265// function used to display the current page in review
1266static void displayStreamingReviewPage(nbgl_stepPosition_t pos)
1267{
1268 const char *text = NULL;
1269 const char *subText = NULL;
1270 const nbgl_icon_details_t *icon = NULL;
1271 uint8_t reviewPages = 0;
1272 uint8_t titleIndex = 255;
1273 uint8_t subIndex = 255;
1274 const nbgl_contentValueExt_t *extension = NULL;
1275 ForcedType_t forcedType = NO_FORCED_TYPE;
1276
1277 context.stepCallback = NULL;
1278 switch (context.type) {
1279 case STREAMING_START_REVIEW_USE_CASE:
1280 // Title page to display
1281 titleIndex = reviewPages++;
1282 if (context.review.reviewSubTitle) {
1283 // subtitle page to display
1284 subIndex = reviewPages++;
1285 }
1286 // Determine which page to display
1287 if (context.currentPage >= reviewPages) {
1288 onReviewAccept();
1289 return;
1290 }
1291 // header page(s)
1292 if (context.currentPage == titleIndex) {
1293 // title page
1294 icon = context.review.icon;
1295 text = context.review.reviewTitle;
1296 }
1297 else if (context.currentPage == subIndex) {
1298 // subtitle page
1299 text = context.review.reviewSubTitle;
1300 }
1301 break;
1302
1303 case STREAMING_CONTINUE_REVIEW_USE_CASE:
1304 if (context.currentPage >= context.review.tagValueList->nbPairs) {
1305 onReviewAccept();
1306 return;
1307 }
1308 // if there is a skip, and we are not already displaying the "skip" page
1309 // and we are not at the first tag/value of the first set of data (except if going
1310 // backward) then display the "skip" page
1311 if ((context.review.skipCallback != NULL) && (context.review.skipDisplay == false)
1312 && ((context.review.nbDataSets > 1) || (context.currentPage > 0)
1313 || (context.review.dataDirection == BACKWARD_DIRECTION))) {
1314 nbgl_stepPosition_t directions = (pos & BACKWARD_DIRECTION) | FIRST_STEP;
1315 nbgl_layoutCenteredInfo_t info = {0};
1316 if ((context.review.nbDataSets == 1) || (context.currentPage > 0)) {
1317 directions |= LAST_STEP;
1318 }
1319 info.icon = &C_Information_circle_14px;
1320 info.text1 = "Press right button to continue message or \bpress both to skip\b";
1321 nbgl_stepDrawCenteredInfo(directions, buttonSkipCallback, NULL, &info, false);
1322 nbgl_refresh();
1323 context.review.skipDisplay = true;
1324 return;
1325 }
1326 context.review.skipDisplay = false;
1327 bool isCenteredInfo = false;
1328 getPairData(context.review.tagValueList,
1329 context.currentPage,
1330 &text,
1331 &subText,
1332 &extension,
1333 &icon,
1334 &isCenteredInfo);
1335 if (extension != NULL) {
1336 forcedType = FORCE_BUTTON;
1337 }
1338 else {
1339 if (isCenteredInfo) {
1340 forcedType = FORCE_CENTERED_INFO;
1341 }
1342 }
1343 break;
1344
1345 case STREAMING_FINISH_REVIEW_USE_CASE:
1346 default:
1347 if (context.currentPage == 0) {
1348 // accept page
1349 getLastPageInfo(true, &icon, &text);
1350 }
1351 else {
1352 // reject page
1353 getLastPageInfo(false, &icon, &text);
1354 }
1355 break;
1356 }
1357
1358 drawStep(pos, icon, text, subText, streamingReviewCallback, false, forcedType);
1359 nbgl_refresh();
1360}
1361
1362// function used to display the current page in info
1363static void displayInfoPage(nbgl_stepPosition_t pos)
1364{
1365 const char *text = NULL;
1366 const char *subText = NULL;
1367 const nbgl_icon_details_t *icon = NULL;
1368
1369 context.stepCallback = NULL;
1370
1371 if (context.currentPage < (context.nbPages - 1)) {
1372 text = PIC(
1373 ((const char *const *) PIC(context.home.infosList->infoTypes))[context.currentPage]);
1374 subText = PIC(
1375 ((const char *const *) PIC(context.home.infosList->infoContents))[context.currentPage]);
1376 }
1377 else {
1378 icon = &C_icon_back_x;
1379 text = "Back";
1380 context.stepCallback = startUseCaseHome;
1381 }
1382
1383 drawStep(pos, icon, text, subText, infoCallback, false, FORCE_CENTERED_INFO);
1384 nbgl_refresh();
1385}
1386
1387// function used to get the current page content
1388static void getContentPage(bool toogle_state, PageContent_t *contentPage)
1389{
1390 uint8_t elemIdx = 0;
1391 const nbgl_content_t *p_content = NULL;
1392 nbgl_content_t content = {0};
1393 nbgl_contentSwitch_t *contentSwitch = NULL;
1394#ifdef WITH_HORIZONTAL_CHOICES_LIST
1395 nbgl_contentRadioChoice_t *contentChoices = NULL;
1396 char **names = NULL;
1397#endif
1398#ifdef WITH_HORIZONTAL_BARS_LIST
1399 nbgl_contentBarsList_t *contentBars = NULL;
1400 char **texts = NULL;
1401#endif
1402 p_content = getContentElemAtIdx(context.currentPage, &elemIdx, &content);
1403 if (p_content == NULL) {
1404 return;
1405 }
1406 switch (p_content->type) {
1407 case CENTERED_INFO:
1408 contentPage->text = PIC(p_content->content.centeredInfo.text1);
1409 contentPage->subText = PIC(p_content->content.centeredInfo.text2);
1410 break;
1411 case INFO_BUTTON:
1412 contentPage->icon = PIC(p_content->content.infoButton.icon);
1413 contentPage->text = PIC(p_content->content.infoButton.text);
1414 contentPage->subText = PIC(p_content->content.infoButton.buttonText);
1415 break;
1416 case TAG_VALUE_LIST:
1417 getPairData(&p_content->content.tagValueList,
1418 elemIdx,
1419 &contentPage->text,
1420 &contentPage->subText,
1421 &contentPage->extension,
1422 &contentPage->icon,
1423 &contentPage->isCenteredInfo);
1424 break;
1425 case TAG_VALUE_CONFIRM:
1426 if (elemIdx < p_content->content.tagValueConfirm.tagValueList.nbPairs) {
1427 getPairData(&p_content->content.tagValueConfirm.tagValueList,
1428 elemIdx,
1429 &contentPage->text,
1430 &contentPage->subText,
1431 &contentPage->extension,
1432 &contentPage->icon,
1433 &contentPage->isCenteredInfo);
1434 }
1435 else {
1436 contentPage->text = p_content->content.tagValueConfirm.confirmationText;
1437 contentPage->icon = &C_icon_validate_14;
1438 contentPage->isAction = true;
1439 }
1440 break;
1441 case SWITCHES_LIST:
1442 contentPage->isSwitch = true;
1443 contentSwitch = &(
1444 (nbgl_contentSwitch_t *) PIC(p_content->content.switchesList.switches))[elemIdx];
1445 contentPage->text = contentSwitch->text;
1446 contentPage->state = contentSwitch->initState;
1447 if (toogle_state) {
1448 contentPage->state = (contentPage->state == ON_STATE) ? OFF_STATE : ON_STATE;
1449 }
1450 context.stepCallback = onSwitchAction;
1451 contentPage->subText = contentSwitch->subText;
1452 break;
1453 case INFOS_LIST:
1454 contentPage->text
1455 = ((const char *const *) PIC(p_content->content.infosList.infoTypes))[elemIdx];
1456 contentPage->subText
1457 = ((const char *const *) PIC(p_content->content.infosList.infoContents))[elemIdx];
1458 break;
1459 case CHOICES_LIST:
1460#ifdef WITH_HORIZONTAL_CHOICES_LIST
1461 contentChoices = (nbgl_contentRadioChoice_t *) PIC(&p_content->content.choicesList);
1462 names = (char **) PIC(contentChoices->names);
1463 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1464 contentPage->text = PIC(context.content.title);
1465 contentPage->subText = (const char *) PIC(names[elemIdx]);
1466 }
1467 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1468 contentPage->text = PIC(context.home.appName);
1469 contentPage->subText = (const char *) PIC(names[elemIdx]);
1470 }
1471 else {
1472 contentPage->text = (const char *) PIC(names[elemIdx]);
1473 }
1474#endif
1475 break;
1476 case BARS_LIST:
1477#ifdef WITH_HORIZONTAL_BARS_LIST
1478 contentBars = (nbgl_contentBarsList_t *) PIC(&p_content->content.barsList);
1479 texts = (char **) PIC(contentBars->barTexts);
1480 if ((context.type == CONTENT_USE_CASE) && (context.content.title != NULL)) {
1481 contentPage->text = PIC(context.content.title);
1482 contentPage->subText = PIC(texts[elemIdx]);
1483 }
1484 else if ((context.type == GENERIC_SETTINGS) && (context.home.appName != NULL)) {
1485 contentPage->text = PIC(context.home.appName);
1486 contentPage->subText = PIC(texts[elemIdx]);
1487 }
1488 else {
1489 contentPage->text = PIC(texts[elemIdx]);
1490 }
1491#endif
1492 break;
1493 default:
1494 break;
1495 }
1496}
1497
1498// function used to display the current page in settings
1499static void displaySettingsPage(nbgl_stepPosition_t pos, bool toogle_state)
1500{
1501 PageContent_t contentPage = {0};
1502
1503 context.stepCallback = NULL;
1504
1505 if (context.currentPage < (context.nbPages - 1)) {
1506 getContentPage(toogle_state, &contentPage);
1507 }
1508 else { // last page is for quit
1509 contentPage.icon = &C_icon_back_x;
1510 contentPage.text = "Back";
1511 if (context.type == GENERIC_SETTINGS) {
1512 context.stepCallback = context.home.quitCallback;
1513 }
1514 else {
1515 context.stepCallback = startUseCaseHome;
1516 }
1517 }
1518
1519 if (contentPage.isSwitch) {
1520 drawSwitchStep(
1521 pos, contentPage.text, contentPage.subText, contentPage.state, settingsCallback, false);
1522 }
1523 else {
1524 drawStep(pos,
1525 contentPage.icon,
1526 contentPage.text,
1527 contentPage.subText,
1528 settingsCallback,
1529 false,
1530 NO_FORCED_TYPE);
1531 }
1532
1533 nbgl_refresh();
1534}
1535
1536static void startUseCaseHome(void)
1537{
1538 switch (context.type) {
1539 case SETTINGS_USE_CASE:
1540 // Settings page index
1541 context.currentPage = 1;
1542 if (context.home.homeAction) {
1543 // Action page is before Settings page
1544 context.currentPage++;
1545 }
1546 break;
1547 case INFO_USE_CASE:
1548 // Info page index
1549 context.currentPage = 1;
1550 if (context.home.homeAction) {
1551 // Action page is before Settings and Info pages
1552 context.currentPage++;
1553 }
1554 if (context.home.settingContents) {
1555 // Settings page is before Info pages
1556 context.currentPage++;
1557 }
1558 break;
1559 default:
1560 // Home page index
1561 context.currentPage = 0;
1562 break;
1563 }
1564
1565 context.type = HOME_USE_CASE;
1566 context.nbPages = 2; // Home + Quit
1567 if (context.home.settingContents) {
1568 context.nbPages++;
1569 }
1570 if (context.home.infosList) {
1571 context.nbPages++;
1572 }
1573 if (context.home.homeAction) {
1574 context.nbPages++;
1575 }
1576 displayHomePage(FORWARD_DIRECTION);
1577}
1578
1579static void startUseCaseInfo(void)
1580{
1581 context.type = INFO_USE_CASE;
1582 context.nbPages = context.home.infosList->nbInfos + 1; // For back screen
1583 context.currentPage = 0;
1584
1585 displayInfoPage(FORWARD_DIRECTION);
1586}
1587
1588static void startUseCaseSettingsAtPage(uint8_t initSettingPage)
1589{
1590 nbgl_content_t content = {0};
1591 const nbgl_content_t *p_content = NULL;
1592
1593 // if not coming from GENERIC_SETTINGS, force to SETTINGS_USE_CASE
1594 if (context.type != GENERIC_SETTINGS) {
1595 context.type = SETTINGS_USE_CASE;
1596 }
1597
1598 context.nbPages = 1; // For back screen
1599 for (uint i = 0; i < context.home.settingContents->nbContents; i++) {
1600 p_content = getContentAtIdx(context.home.settingContents, i, &content);
1601 context.nbPages += getContentNbElement(p_content);
1602 }
1603 context.currentPage = initSettingPage;
1604
1605 displaySettingsPage(FORWARD_DIRECTION, false);
1606}
1607
1608static void startUseCaseSettings(void)
1609{
1610 startUseCaseSettingsAtPage(0);
1611}
1612
1613static void startUseCaseContent(void)
1614{
1615 uint8_t contentIdx = 0;
1616 const nbgl_content_t *p_content = NULL;
1617 nbgl_content_t content = {0};
1618
1619 context.nbPages = 1; // Quit
1620
1621 for (contentIdx = 0; contentIdx < context.content.genericContents.nbContents; contentIdx++) {
1622 p_content = getContentAtIdx(&context.content.genericContents, contentIdx, &content);
1623 context.nbPages += getContentNbElement(p_content);
1624 }
1625
1626 // Ensure currentPage is valid
1627 if (context.currentPage >= context.nbPages) {
1628 return;
1629 }
1630
1631 displayContent(FORWARD_DIRECTION, false);
1632}
1633
1634// function used to display the current page in home
1635static void displayHomePage(nbgl_stepPosition_t pos)
1636{
1637 const char *text = NULL;
1638 const char *subText = NULL;
1639 const nbgl_icon_details_t *icon = NULL;
1640 uint8_t currentIndex = 0;
1641 uint8_t homeIndex = 255;
1642 uint8_t actionIndex = 255;
1643 uint8_t settingsIndex = 255;
1644 uint8_t infoIndex = 255;
1645
1646 context.stepCallback = NULL;
1647
1648 // Determine which pages are present
1649 homeIndex = currentIndex++;
1650 if (context.home.homeAction) {
1651 actionIndex = currentIndex++;
1652 }
1653 if (context.home.settingContents) {
1654 settingsIndex = currentIndex++;
1655 }
1656 if (context.home.infosList) {
1657 infoIndex = currentIndex++;
1658 }
1659
1660 if (context.currentPage == homeIndex) {
1661 // Home page
1662 icon = context.home.appIcon;
1663 if (context.home.tagline != NULL) {
1664 text = context.home.tagline;
1665 }
1666 else {
1667 text = context.home.appName;
1668 subText = "app is ready";
1669 }
1670 }
1671 else if (context.currentPage == actionIndex) {
1672 // Action page
1673 icon = context.home.homeAction->icon;
1674 text = PIC(context.home.homeAction->text);
1675 context.stepCallback = context.home.homeAction->callback;
1676 }
1677 else if (context.currentPage == settingsIndex) {
1678 // Settings page
1679 icon = &C_icon_coggle;
1680 text = "App settings";
1681 context.stepCallback = startUseCaseSettings;
1682 }
1683 else if (context.currentPage == infoIndex) {
1684 // About page
1685 icon = &C_Information_circle_14px;
1686 text = "App info";
1687 context.stepCallback = startUseCaseInfo;
1688 }
1689 else {
1690 icon = &C_Quit_14px;
1691 text = "Quit app";
1692 context.stepCallback = context.home.quitCallback;
1693 }
1694
1695 drawStep(pos, icon, text, subText, homeCallback, false, NO_FORCED_TYPE);
1696 nbgl_refresh();
1697}
1698
1699// function used to display the current page in choice
1700static void displayChoicePage(nbgl_stepPosition_t pos)
1701{
1702 const char *text = NULL;
1703 const char *subText = NULL;
1704 const nbgl_icon_details_t *icon = NULL;
1705 // set to 1 if there is only one page for intro (if either icon or subMessage is NULL)
1706 // acceptPage = number of intro pages:
1707 // 0 if no message, 1 if message with icon OR subMessage only, 2 if both icon and subMessage
1708 uint8_t acceptPage = 0;
1709 // nbDetailPages = number of BAR_LIST bar pages shown before confirm/cancel (0 if none)
1710 // Page order: intro(s) | bar pages | confirm | cancel
1711 uint8_t nbDetailPages = 0;
1712
1713 if (context.choice.message != NULL) {
1714 if ((context.choice.icon == NULL) || (context.choice.subMessage == NULL)) {
1715 acceptPage = 1;
1716 }
1717 else {
1718 acceptPage = 2;
1719 }
1720 }
1721 if ((context.choice.details != NULL) && (context.choice.details->type == BAR_LIST_WARNING)) {
1722 nbDetailPages = context.choice.details->barList.nbBars;
1723 }
1724 context.stepCallback = NULL;
1725
1726 if (context.currentPage < acceptPage) {
1727 if (context.currentPage == 0) { // title page
1728 text = context.choice.message;
1729 if (context.choice.icon != NULL) {
1730 icon = context.choice.icon;
1731 }
1732 else {
1733 subText = context.choice.subMessage;
1734 }
1735 }
1736 else if ((acceptPage == 2) && (context.currentPage == 1)) { // sub-title page
1737 // displayed only if there is both icon and subMessage
1738 text = context.choice.message;
1739 subText = context.choice.subMessage;
1740 }
1741 }
1742 else if (context.currentPage < (acceptPage + nbDetailPages)) {
1743 // BAR_LIST detail pages, shown before confirm/cancel (one page per bar)
1744 uint8_t idx = context.currentPage - acceptPage;
1745 text = (context.choice.details->barList.texts != NULL)
1746 ? context.choice.details->barList.texts[idx]
1747 : NULL;
1748 subText = (context.choice.details->barList.subTexts != NULL)
1749 ? context.choice.details->barList.subTexts[idx]
1750 : NULL;
1751 }
1752 else if (context.currentPage == (acceptPage + nbDetailPages)) { // confirm page
1753 icon = &C_icon_validate_14;
1754 text = context.choice.confirmText;
1755 context.stepCallback = onChoiceAccept;
1756 }
1757 else { // cancel page (last page)
1758 icon = &C_icon_crossmark;
1759 text = context.choice.cancelText;
1760 context.stepCallback = onChoiceReject;
1761 }
1762 // other detail types (non-BAR_LIST) are not navigated on Nano
1763
1764 drawStep(pos, icon, text, subText, genericChoiceCallback, false, NO_FORCED_TYPE);
1765 nbgl_refresh();
1766}
1767
1768// function used to display the Confirm page
1769static void displayConfirm(nbgl_stepPosition_t pos)
1770{
1771 const char *text = NULL;
1772 const char *subText = NULL;
1773 const nbgl_icon_details_t *icon = NULL;
1774
1775 context.stepCallback = NULL;
1776 switch (context.currentPage) {
1777 case 0:
1778 // title page
1779 text = context.confirm.message;
1780 subText = context.confirm.subMessage;
1781 break;
1782 case 1:
1783 // confirm page
1784 icon = &C_icon_validate_14;
1785 text = context.confirm.confirmText;
1786 context.stepCallback = onConfirmAccept;
1787 break;
1788 case 2:
1789 // cancel page
1790 icon = &C_icon_crossmark;
1791 text = context.confirm.cancelText;
1792 context.stepCallback = onConfirmReject;
1793 break;
1794 }
1795
1796 drawStep(pos, icon, text, subText, genericConfirmCallback, true, NO_FORCED_TYPE);
1797 nbgl_refresh();
1798}
1799
1800// function used to display the current navigable content
1801static void displayContent(nbgl_stepPosition_t pos, bool toogle_state)
1802{
1803 PageContent_t contentPage = {0};
1804 ForcedType_t forcedType = NO_FORCED_TYPE;
1805
1806 context.stepCallback = NULL;
1807
1808 if (context.currentPage < (context.nbPages - 1)) {
1809 getContentPage(toogle_state, &contentPage);
1810 if (contentPage.isCenteredInfo) {
1811 forcedType = FORCE_CENTERED_INFO;
1812 }
1813 context.forceAction = contentPage.isAction;
1814 }
1815 else { // last page is for quit
1816 if (context.content.rejectText) {
1817 contentPage.text = context.content.rejectText;
1818 }
1819 else {
1820 contentPage.text = "Back";
1821 }
1822 if (context.type == GENERIC_REVIEW_USE_CASE) {
1823 contentPage.icon = &C_icon_crossmark;
1824 }
1825 else {
1826 contentPage.icon = &C_icon_back_x;
1827 }
1828 context.stepCallback = context.content.quitCallback;
1829 }
1830
1831 if (contentPage.isSwitch) {
1832 drawSwitchStep(
1833 pos, contentPage.text, contentPage.subText, contentPage.state, contentCallback, false);
1834 }
1835 else {
1836 drawStep(pos,
1837 contentPage.icon,
1838 contentPage.text,
1839 contentPage.subText,
1840 contentCallback,
1841 false,
1842 forcedType);
1843 }
1844 context.forceAction = false;
1845 nbgl_refresh();
1846}
1847
1848static void displaySpinner(const char *text)
1849{
1850 drawStep(SINGLE_STEP, &C_icon_processing, text, NULL, NULL, false, false);
1851 nbgl_refresh();
1852}
1853
1854// function to factorize code for all simple reviews
1855static void useCaseReview(ContextType_t type,
1856 nbgl_operationType_t operationType,
1857 const nbgl_contentTagValueList_t *tagValueList,
1858 const nbgl_icon_details_t *icon,
1859 const char *reviewTitle,
1860 const char *reviewSubTitle,
1861 const char *finishTitle,
1862 nbgl_choiceCallback_t choiceCallback)
1863{
1864 memset(&context, 0, sizeof(UseCaseContext_t));
1865 context.type = type;
1866 context.operationType = operationType;
1867 context.review.tagValueList = tagValueList;
1868 context.review.reviewTitle = reviewTitle;
1869 context.review.reviewSubTitle = reviewSubTitle;
1870 context.review.finishTitle = finishTitle;
1871 context.review.icon = icon;
1872 context.review.onChoice = choiceCallback;
1873 context.currentPage = 0;
1874 // 1 page for title and 2 pages at the end for accept/reject
1875 context.nbPages = tagValueList->nbPairs + 3;
1876 if (reviewSubTitle) {
1877 context.nbPages++; // 1 page for subtitle page
1878 }
1879
1880 displayReviewPage(FORWARD_DIRECTION);
1881}
1882
1883#ifdef NBGL_KEYPAD
1884static void setPinCodeText(void)
1885{
1886 bool enableValidate = false;
1887 bool enableBackspace = true;
1888
1889 // pin can be validated when min digits is entered
1890 enableValidate = (context.keypad.pinLen >= context.keypad.pinMinDigits);
1891 // backspace is disabled when no digit is entered and back vallback is not provided
1892 enableBackspace = (context.keypad.pinLen > 0) || (context.keypad.backCallback != NULL);
1893 nbgl_layoutUpdateKeypadContent(context.keypad.layoutCtx,
1894 context.keypad.hidden,
1895 context.keypad.pinLen,
1896 (const char *) context.keypad.pinEntry);
1898 context.keypad.layoutCtx, context.keypad.keypadIndex, enableValidate, enableBackspace);
1899 nbgl_layoutDraw(context.keypad.layoutCtx);
1900 nbgl_refresh();
1901}
1902
1903// called when a key is touched on the keypad
1904static void keypadCallback(char touchedKey)
1905{
1906 switch (touchedKey) {
1907 case BACKSPACE_KEY:
1908 if (context.keypad.pinLen > 0) {
1909 context.keypad.pinLen--;
1910 context.keypad.pinEntry[context.keypad.pinLen] = 0;
1911 }
1912 else if (context.keypad.backCallback != NULL) {
1913 context.keypad.backCallback();
1914 break;
1915 }
1916 setPinCodeText();
1917 break;
1918
1919 case VALIDATE_KEY:
1920 context.keypad.validatePin(context.keypad.pinEntry, context.keypad.pinLen);
1921 break;
1922
1923 default:
1924 if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
1925 if (context.keypad.pinLen < context.keypad.pinMaxDigits) {
1926 context.keypad.pinEntry[context.keypad.pinLen] = touchedKey;
1927 context.keypad.pinLen++;
1928 }
1929 setPinCodeText();
1930 }
1931 break;
1932 }
1933}
1934#endif // NBGL_KEYPAD
1935
1936#ifdef NBGL_KEYBOARD
1937// Saved keyboard context for suggestion selection
1938// (needed because switching to CONTENT_USE_CASE overwrites the union context)
1939static struct {
1940 const char **buttons;
1941 int firstButtonToken;
1942 uint8_t nbUsedButtons;
1943 nbgl_layoutTouchCallback_t onButtonCallback;
1944 nbgl_callback_t backCallback;
1945 char title[KEYBOARD_MAX_TITLE];
1946} savedKeyboardContext;
1947
1948// Navigation callback to fill the suggestion choices page
1949static bool suggestionNavCallback(uint8_t page, nbgl_pageContent_t *content)
1950{
1951 UNUSED(page);
1952 content->type = CHOICES_LIST;
1953 content->choicesList.names = savedKeyboardContext.buttons;
1954 content->choicesList.token = savedKeyboardContext.firstButtonToken;
1955 content->choicesList.initChoice = 0;
1956 content->choicesList.nbChoices = savedKeyboardContext.nbUsedButtons;
1957 return true;
1958}
1959
1960// Display suggestion selection page
1961static void displaySuggestionSelection(void)
1962{
1963 // Save keyboard context before it gets overwritten
1964 savedKeyboardContext.buttons = context.keyboard.content.suggestionButtons.buttons;
1965 savedKeyboardContext.firstButtonToken
1966 = context.keyboard.content.suggestionButtons.firstButtonToken;
1967 savedKeyboardContext.nbUsedButtons = context.keyboard.content.suggestionButtons.nbUsedButtons;
1968 savedKeyboardContext.onButtonCallback = context.keyboard.onButtonCallback;
1969 savedKeyboardContext.backCallback = context.keyboard.backCallback;
1970
1971 // Release the keyboard layout
1972 nbgl_layoutRelease(context.keyboard.layoutCtx);
1973 context.keyboard.layoutCtx = NULL;
1974
1975 snprintf(savedKeyboardContext.title,
1976 sizeof(savedKeyboardContext.title),
1977 "Select word #%d",
1978 context.keyboard.content.number);
1979 nbgl_useCaseNavigableContent(savedKeyboardContext.title,
1980 0,
1981 1,
1982 savedKeyboardContext.backCallback,
1983 suggestionNavCallback,
1984 savedKeyboardContext.onButtonCallback);
1985}
1986
1987// called when a key is touched on the keyboard
1988static void keyboardCallback(char touchedKey)
1989{
1990 uint32_t mask = 0;
1991 size_t textLen = strlen(context.keyboard.entryBuffer);
1992 PRINTF("[keyboardCallback] touchedKey: '%c'\n", touchedKey);
1993 if (touchedKey == BACKSPACE_KEY) {
1994 if (textLen == 0) {
1995 // Used to exit the keyboard when backspace is pressed on an empty entry
1996 context.keyboard.backCallback();
1997 return;
1998 }
1999 context.keyboard.entryBuffer[--textLen] = '\0';
2000 }
2001 else if (touchedKey == VALIDATE_KEY) {
2002 context.keyboard.actionCallback();
2003 return;
2004 }
2005 else {
2006 context.keyboard.entryBuffer[textLen] = touchedKey;
2007 context.keyboard.entryBuffer[++textLen] = '\0';
2008 }
2009 // Set the keyMask to disable some keys
2010 if (context.keyboard.content.type == KEYBOARD_WITH_SUGGESTIONS) {
2011 // if suggestions are displayed, we update them at each key press
2012 context.keyboard.getSuggestButtons(&context.keyboard.content, &mask);
2013 if ((context.keyboard.content.suggestionButtons.nbUsedButtons > 0)
2014 && (context.keyboard.content.suggestionButtons.nbUsedButtons
2015 < NB_MAX_SUGGESTION_BUTTONS)) {
2016 // On Nano, when we have few suggestions, display a selection page
2017 // instead of continuing with keyboard entry
2018 displaySuggestionSelection();
2019 return; // Don't update keyboard, we're in suggestion mode
2020 }
2021 }
2022 else if (textLen >= context.keyboard.entryMaxLen) {
2023 // entry length can't be greater, so we mask every characters
2024 mask = -1;
2025 }
2026 nbgl_layoutUpdateKeyboard(context.keyboard.layoutCtx, context.keyboard.keyboardIndex, mask);
2028 context.keyboard.layoutCtx, context.keyboard.textIndex, context.keyboard.entryBuffer);
2029 nbgl_refresh();
2030}
2031#endif
2032
2033// this is the function called to start the actual review, from the initial warning pages
2034static void launchReviewAfterWarning(void)
2035{
2036 if (reviewWithWarnCtx.type == REVIEW_USE_CASE) {
2037 useCaseReview(reviewWithWarnCtx.type,
2038 reviewWithWarnCtx.operationType,
2039 reviewWithWarnCtx.tagValueList,
2040 reviewWithWarnCtx.icon,
2041 reviewWithWarnCtx.reviewTitle,
2042 reviewWithWarnCtx.reviewSubTitle,
2043 reviewWithWarnCtx.finishTitle,
2044 reviewWithWarnCtx.choiceCallback);
2045 }
2046 else if (reviewWithWarnCtx.type == STREAMING_START_REVIEW_USE_CASE) {
2047 displayStreamingReviewPage(FORWARD_DIRECTION);
2048 }
2049}
2050
2051// callback used when navigating in a bar detail sub-page
2052static void barDetailNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
2053{
2054 UNUSED(stepCtx);
2055 if (event == BUTTON_LEFT_PRESSED) {
2056 displayWarningStep();
2057 }
2058}
2059
2060// display a bar detail sub-page (CENTERED_INFO_WARNING type)
2061static void displayBarDetailStep(void)
2062{
2063 const nbgl_genericDetails_t *detail
2064 = &reviewWithWarnCtx.warning->introDetails->barList.details[reviewWithWarnCtx.barDetailIdx];
2065
2066 if (detail->type == CENTERED_INFO_WARNING) {
2067 nbgl_layoutCenteredInfo_t info = {0};
2068 info.icon = detail->centeredInfo.icon;
2069 info.text1 = detail->centeredInfo.title;
2070 info.text2 = detail->centeredInfo.description;
2071 info.style = BOLD_TEXT1_INFO;
2072 // LAST_STEP: only left arrow shown, pressing LEFT goes back via barDetailNavigate
2074 LAST_STEP | BACKWARD_DIRECTION, barDetailNavigate, NULL, &info, false);
2075 nbgl_refresh();
2076 }
2077}
2078
2079// this is the callback used when navigating in warning pages
2080static void warningNavigate(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
2081{
2082 UNUSED(stepCtx);
2083 uint8_t barIdx = 0;
2084
2085 if (event == BUTTON_LEFT_PRESSED) {
2086 // only decrement page if we are not at the first page
2087 if (reviewWithWarnCtx.warningPage > 0) {
2088 reviewWithWarnCtx.warningPage--;
2089 }
2090 }
2091 else if (event == BUTTON_RIGHT_PRESSED) {
2092 // only increment page if not at last page
2093 if (reviewWithWarnCtx.warningPage < (reviewWithWarnCtx.nbWarningPages - 1)) {
2094 reviewWithWarnCtx.warningPage++;
2095 }
2096 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2097 && (reviewWithWarnCtx.warning->info != NULL)) {
2098 launchReviewAfterWarning();
2099 return;
2100 }
2101 }
2102 else if ((event == BUTTON_BOTH_PRESSED)
2103 && (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))) {
2104 // if at first page, double press leads to start of review
2105 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2106 launchReviewAfterWarning();
2107 }
2108 // if at last page, reject operation
2109 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2110 reviewWithWarnCtx.choiceCallback(false);
2111 }
2112 return;
2113 }
2114 else if ((event == BUTTON_BOTH_PRESSED) && (reviewWithWarnCtx.warning->introDetails != NULL)
2115 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)
2116 && (reviewWithWarnCtx.warningPage > reviewWithWarnCtx.firstWarningPage)) {
2117 // enter the detail sub-page for the current bar (if it has one)
2118 barIdx = reviewWithWarnCtx.warningPage - reviewWithWarnCtx.firstWarningPage - 1;
2119 if ((reviewWithWarnCtx.warning->introDetails->barList.details != NULL)
2120 && (reviewWithWarnCtx.warning->introDetails->barList.details[barIdx].type
2121 != NO_TYPE_WARNING)) {
2122 reviewWithWarnCtx.barDetailIdx = barIdx;
2123 displayBarDetailStep();
2124 }
2125 return;
2126 }
2127 else {
2128 return;
2129 }
2130 displayWarningStep();
2131}
2132
2133// function used to display the initial warning pages when starting a "review with warning"
2134static void displayWarningStep(void)
2135{
2136 nbgl_layoutCenteredInfo_t info = {0};
2137 nbgl_stepPosition_t pos = 0;
2138 uint8_t barIdx = 0;
2139 if ((reviewWithWarnCtx.warning->prelude) && (reviewWithWarnCtx.warningPage == 0)) {
2140 // for prelude, only draw text as a single step
2142 warningNavigate,
2143 NULL,
2144 reviewWithWarnCtx.warning->prelude->title,
2145 reviewWithWarnCtx.warning->prelude->description,
2146 REGULAR_INFO,
2147 false);
2148 nbgl_refresh();
2149 return;
2150 }
2151 else if (reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN)) {
2152 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2153 // draw the main warning page
2154 info.icon = &C_icon_warning;
2155 info.text1 = "Blind signing ahead";
2156 info.text2 = "To accept risk, press both buttons";
2157 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2159 pos |= FORWARD_DIRECTION;
2160 }
2161 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2162 getLastPageInfo(false, &info.icon, &info.text1);
2164 }
2165 }
2166 else if ((reviewWithWarnCtx.warning->predefinedSet == 0)
2167 && (reviewWithWarnCtx.warning->info != NULL)) {
2168 if (reviewWithWarnCtx.warningPage == reviewWithWarnCtx.firstWarningPage) {
2169 info.icon = reviewWithWarnCtx.warning->info->icon;
2170 info.text1 = reviewWithWarnCtx.warning->info->title;
2171 info.text2 = reviewWithWarnCtx.warning->info->description;
2172 pos = (reviewWithWarnCtx.firstWarningPage == 0) ? FIRST_STEP
2174 pos |= FORWARD_DIRECTION;
2175 }
2176 else if ((reviewWithWarnCtx.warning->introDetails != NULL)
2177 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)) {
2178 // intermediate or last bar page
2179 barIdx = reviewWithWarnCtx.warningPage - reviewWithWarnCtx.firstWarningPage - 1;
2180 if (reviewWithWarnCtx.warning->introDetails->barList.icons) {
2181 info.icon = reviewWithWarnCtx.warning->introDetails->barList.icons[barIdx];
2182 }
2183 info.text1 = reviewWithWarnCtx.warning->introDetails->barList.texts[barIdx];
2184 if (reviewWithWarnCtx.warning->introDetails->barList.subTexts) {
2185 info.text2 = reviewWithWarnCtx.warning->introDetails->barList.subTexts[barIdx];
2186 }
2188 }
2189 else if (reviewWithWarnCtx.warningPage == (reviewWithWarnCtx.nbWarningPages - 1)) {
2190 if ((reviewWithWarnCtx.warning->introDetails != NULL)
2191 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING)) {
2192 info.icon = reviewWithWarnCtx.warning->introDetails->centeredInfo.icon;
2193 info.text1 = reviewWithWarnCtx.warning->introDetails->centeredInfo.title;
2194 info.text2 = reviewWithWarnCtx.warning->introDetails->centeredInfo.description;
2196 }
2197 else {
2198 // not supported
2199 return;
2200 }
2201 }
2202 }
2203 else {
2204 // not supported
2205 return;
2206 }
2207 info.style = BOLD_TEXT1_INFO;
2208 nbgl_stepDrawCenteredInfo(pos, warningNavigate, NULL, &info, false);
2209 nbgl_refresh();
2210}
2211
2212// function used to display the initial warning page when starting a "review with warning"
2213static void displayInitialWarning(void)
2214{
2215 // draw the main warning page
2216 reviewWithWarnCtx.warningPage = 0;
2217 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2218 || ((reviewWithWarnCtx.warning->introDetails)
2219 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2220 reviewWithWarnCtx.nbWarningPages = 2;
2221 }
2222 else if ((reviewWithWarnCtx.warning->introDetails)
2223 && (reviewWithWarnCtx.warning->introDetails->type == BAR_LIST_WARNING)) {
2224 reviewWithWarnCtx.nbWarningPages
2225 = reviewWithWarnCtx.warning->introDetails->barList.nbBars + 1;
2226 }
2227 else {
2228 // if no intro details and not Blind Signing warning, only one page
2229 reviewWithWarnCtx.nbWarningPages = 1;
2230 }
2231
2232 reviewWithWarnCtx.firstWarningPage = 0;
2233 displayWarningStep();
2234}
2235
2236// function used to display the prelude page when starting a "review with warning"
2237static void displayPrelude(void)
2238{
2239 // draw the main warning page
2240 reviewWithWarnCtx.warningPage = 0;
2241 if ((reviewWithWarnCtx.warning->predefinedSet & (1 << BLIND_SIGNING_WARN))
2242 || ((reviewWithWarnCtx.warning->introDetails)
2243 && (reviewWithWarnCtx.warning->introDetails->type == CENTERED_INFO_WARNING))) {
2244 reviewWithWarnCtx.nbWarningPages = 3;
2245 }
2246 else {
2247 // if no intro details and not Blind Signing warning, only 2 pages
2248 reviewWithWarnCtx.nbWarningPages = 2;
2249 }
2250 reviewWithWarnCtx.firstWarningPage = 1;
2251 displayWarningStep();
2252}
2253
2254/**********************
2255 * GLOBAL FUNCTIONS
2256 **********************/
2257
2269uint8_t nbgl_useCaseGetNbTagValuesInPage(uint8_t nbPairs,
2270 const nbgl_contentTagValueList_t *tagValueList,
2271 uint8_t startIndex,
2272 bool *requireSpecificDisplay)
2273{
2274 UNUSED(nbPairs);
2275 UNUSED(tagValueList);
2276 UNUSED(startIndex);
2277 *requireSpecificDisplay = true;
2278 return 1;
2279}
2280
2293uint8_t nbgl_useCaseGetNbTagValuesInPageExt(uint8_t nbPairs,
2294 const nbgl_contentTagValueList_t *tagValueList,
2295 uint8_t startIndex,
2296 bool isSkippable,
2297 bool *requireSpecificDisplay)
2298{
2299 UNUSED(nbPairs);
2300 UNUSED(tagValueList);
2301 UNUSED(startIndex);
2302 UNUSED(isSkippable);
2303 *requireSpecificDisplay = true;
2304 return 1;
2305}
2306
2315uint8_t nbgl_useCaseGetNbInfosInPage(uint8_t nbInfos,
2316 const nbgl_contentInfoList_t *infosList,
2317 uint8_t startIndex,
2318 bool withNav)
2319{
2320 UNUSED(nbInfos);
2321 UNUSED(infosList);
2322 UNUSED(startIndex);
2323 UNUSED(withNav);
2324 return 1;
2325}
2326
2335uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches,
2336 const nbgl_contentSwitchesList_t *switchesList,
2337 uint8_t startIndex,
2338 bool withNav)
2339{
2340 UNUSED(nbSwitches);
2341 UNUSED(switchesList);
2342 UNUSED(startIndex);
2343 UNUSED(withNav);
2344 return 1;
2345}
2346
2355uint8_t nbgl_useCaseGetNbBarsInPage(uint8_t nbBars,
2356 const nbgl_contentBarsList_t *barsList,
2357 uint8_t startIndex,
2358 bool withNav)
2359{
2360 UNUSED(nbBars);
2361 UNUSED(barsList);
2362 UNUSED(startIndex);
2363 UNUSED(withNav);
2364 return 1;
2365}
2366
2375uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices,
2376 const nbgl_contentRadioChoice_t *choicesList,
2377 uint8_t startIndex,
2378 bool withNav)
2379{
2380 UNUSED(nbChoices);
2381 UNUSED(choicesList);
2382 UNUSED(startIndex);
2383 UNUSED(withNav);
2384 return 1;
2385}
2386
2394{
2395 uint8_t nbPages = 0;
2396 uint8_t nbPairs = tagValueList->nbPairs;
2397 uint8_t nbPairsInPage;
2398 uint8_t i = 0;
2399 bool flag;
2400
2401 while (i < tagValueList->nbPairs) {
2402 // upper margin
2403 nbPairsInPage = nbgl_useCaseGetNbTagValuesInPageExt(nbPairs, tagValueList, i, false, &flag);
2404 i += nbPairsInPage;
2405 nbPairs -= nbPairsInPage;
2406 nbPages++;
2407 }
2408 return nbPages;
2409}
2410
2424void nbgl_useCaseNavigableContent(const char *title,
2425 uint8_t initPage,
2426 uint8_t nbPages,
2427 nbgl_callback_t quitCallback,
2428 nbgl_navCallback_t navCallback,
2429 nbgl_layoutTouchCallback_t controlsCallback)
2430{
2431 memset(&context, 0, sizeof(UseCaseContext_t));
2432 context.type = CONTENT_USE_CASE;
2433 context.currentPage = initPage;
2434 context.content.title = title;
2435 context.content.quitCallback = quitCallback;
2436 context.content.navCallback = navCallback;
2437 context.content.controlsCallback = controlsCallback;
2438 context.content.genericContents.callbackCallNeeded = true;
2439 context.content.genericContents.nbContents = nbPages;
2440
2441 startUseCaseContent();
2442}
2443
2458void nbgl_useCaseHomeAndSettings(const char *appName,
2459 const nbgl_icon_details_t *appIcon,
2460 const char *tagline,
2461 const uint8_t initSettingPage,
2462 const nbgl_genericContents_t *settingContents,
2463 const nbgl_contentInfoList_t *infosList,
2464 const nbgl_homeAction_t *action,
2465 nbgl_callback_t quitCallback)
2466{
2467 memset(&context, 0, sizeof(UseCaseContext_t));
2468 context.home.appName = appName;
2469 context.home.appIcon = appIcon;
2470 context.home.tagline = tagline;
2471 context.home.settingContents = PIC(settingContents);
2472 context.home.infosList = PIC(infosList);
2473 context.home.homeAction = action;
2474 context.home.quitCallback = quitCallback;
2475
2476 if ((initSettingPage != INIT_HOME_PAGE) && (settingContents != NULL)) {
2477 startUseCaseSettingsAtPage(initSettingPage);
2478 }
2479 else {
2480 startUseCaseHome();
2481 }
2482}
2483
2496void nbgl_useCaseGenericSettings(const char *appName,
2497 uint8_t initPage,
2498 const nbgl_genericContents_t *settingContents,
2499 const nbgl_contentInfoList_t *infosList,
2500 nbgl_callback_t quitCallback)
2501{
2502 memset(&context, 0, sizeof(UseCaseContext_t));
2503 context.type = GENERIC_SETTINGS;
2504 context.home.appName = appName;
2505 context.home.settingContents = PIC(settingContents);
2506 context.home.infosList = PIC(infosList);
2507 context.home.quitCallback = quitCallback;
2508
2509 startUseCaseSettingsAtPage(initPage);
2510}
2511
2523void nbgl_useCaseGenericConfiguration(const char *title,
2524 uint8_t initPage,
2525 const nbgl_genericContents_t *contents,
2526 nbgl_callback_t quitCallback)
2527{
2528 nbgl_useCaseGenericSettings(title, initPage, contents, NULL, quitCallback);
2529}
2530
2545void nbgl_useCaseReview(nbgl_operationType_t operationType,
2546 const nbgl_contentTagValueList_t *tagValueList,
2547 const nbgl_icon_details_t *icon,
2548 const char *reviewTitle,
2549 const char *reviewSubTitle,
2550 const char *finishTitle,
2551 nbgl_choiceCallback_t choiceCallback)
2552{
2553 useCaseReview(REVIEW_USE_CASE,
2554 operationType,
2555 tagValueList,
2556 icon,
2557 reviewTitle,
2558 reviewSubTitle,
2559 finishTitle,
2560 choiceCallback);
2561}
2562
2586 const nbgl_contentTagValueList_t *tagValueList,
2587 const nbgl_icon_details_t *icon,
2588 const char *reviewTitle,
2589 const char *reviewSubTitle,
2590 const char *finishTitle,
2591 const nbgl_tipBox_t *tipBox,
2592 const nbgl_warning_t *warning,
2593 nbgl_choiceCallback_t choiceCallback)
2594{
2595 UNUSED(tipBox);
2596 ContextType_t type = REVIEW_USE_CASE;
2597
2598 // if no warning at all, it's a simple review
2599 if ((warning == NULL)
2600 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2601 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2602 useCaseReview(type,
2603 operationType,
2604 tagValueList,
2605 icon,
2606 reviewTitle,
2607 reviewSubTitle,
2608 finishTitle,
2609 choiceCallback);
2610 return;
2611 }
2612 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2613 operationType |= NO_THREAT_OPERATION;
2614 }
2615 else if (warning->predefinedSet != 0) {
2616 operationType |= RISKY_OPERATION;
2617 }
2618
2619 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2620 reviewWithWarnCtx.type = type;
2621 reviewWithWarnCtx.operationType = operationType;
2622 reviewWithWarnCtx.tagValueList = tagValueList;
2623 reviewWithWarnCtx.icon = icon;
2624 reviewWithWarnCtx.reviewTitle = reviewTitle;
2625 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2626 reviewWithWarnCtx.finishTitle = finishTitle;
2627 reviewWithWarnCtx.warning = warning;
2628 reviewWithWarnCtx.choiceCallback = choiceCallback;
2629
2630 // if the warning contains a prelude, display it first
2631 if (reviewWithWarnCtx.warning->prelude) {
2632 displayPrelude();
2633 }
2634 // display the initial warning only of a risk/threat or blind signing or prelude
2635 else if (HAS_INITIAL_WARNING(warning)) {
2636 displayInitialWarning();
2637 }
2638 else {
2639 useCaseReview(type,
2640 operationType,
2641 tagValueList,
2642 icon,
2643 reviewTitle,
2644 reviewSubTitle,
2645 finishTitle,
2646 choiceCallback);
2647 }
2648}
2649
2670 const nbgl_contentTagValueList_t *tagValueList,
2671 const nbgl_icon_details_t *icon,
2672 const char *reviewTitle,
2673 const char *reviewSubTitle,
2674 const char *finishTitle,
2675 const nbgl_tipBox_t *dummy,
2676 nbgl_choiceCallback_t choiceCallback)
2677{
2678 nbgl_useCaseAdvancedReview(operationType,
2679 tagValueList,
2680 icon,
2681 reviewTitle,
2682 reviewSubTitle,
2683 finishTitle,
2684 dummy,
2685 &blindSigningWarning,
2686 choiceCallback);
2687}
2688
2704 const nbgl_contentTagValueList_t *tagValueList,
2705 const nbgl_icon_details_t *icon,
2706 const char *reviewTitle,
2707 const char *reviewSubTitle,
2708 const char *finishTitle,
2709 nbgl_choiceCallback_t choiceCallback)
2710{
2711 nbgl_useCaseReview(operationType,
2712 tagValueList,
2713 icon,
2714 reviewTitle,
2715 reviewSubTitle,
2716 finishTitle,
2717 choiceCallback);
2718}
2719
2736void nbgl_useCaseAddressReview(const char *address,
2737 const nbgl_contentTagValueList_t *additionalTagValueList,
2738 const nbgl_icon_details_t *icon,
2739 const char *reviewTitle,
2740 const char *reviewSubTitle,
2741 nbgl_choiceCallback_t choiceCallback)
2742{
2743 memset(&context, 0, sizeof(UseCaseContext_t));
2744 context.type = ADDRESS_REVIEW_USE_CASE;
2745 context.review.address = address;
2746 context.review.reviewTitle = reviewTitle;
2747 context.review.reviewSubTitle = reviewSubTitle;
2748 context.review.icon = icon;
2749 context.review.onChoice = choiceCallback;
2750 context.currentPage = 0;
2751 // + 4 because 1 page for title, 1 for address and 2 pages at the end for approve/reject
2752 // + 1 if sub Title
2753 context.nbPages = reviewSubTitle ? 5 : 4;
2754 if (additionalTagValueList) {
2755 context.review.tagValueList = PIC(additionalTagValueList);
2756 context.nbPages += additionalTagValueList->nbPairs;
2757 }
2758
2759 displayReviewPage(FORWARD_DIRECTION);
2760}
2761
2771 const char *rejectText,
2772 nbgl_callback_t rejectCallback)
2773{
2774 memset(&context, 0, sizeof(UseCaseContext_t));
2775 context.type = GENERIC_REVIEW_USE_CASE;
2776 context.content.rejectText = rejectText;
2777 context.content.quitCallback = rejectCallback;
2778 context.content.genericContents.nbContents = contents->nbContents;
2779 context.content.genericContents.callbackCallNeeded = contents->callbackCallNeeded;
2780 if (contents->callbackCallNeeded) {
2781 context.content.genericContents.contentGetterCallback = contents->contentGetterCallback;
2782 }
2783 else {
2784 context.content.genericContents.contentsList = PIC(contents->contentsList);
2785 }
2786
2787 startUseCaseContent();
2788}
2789
2798void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
2799{
2800 UNUSED(isSuccess);
2801 memset(&context, 0, sizeof(UseCaseContext_t));
2802 context.type = STATUS_USE_CASE;
2803 context.stepCallback = quitCallback;
2804 context.currentPage = 0;
2805 context.nbPages = 1;
2806
2808 NULL,
2809 message,
2810 NULL,
2811 statusButtonCallback,
2812 false,
2813 NO_FORCED_TYPE);
2814}
2815
2823 nbgl_callback_t quitCallback)
2824{
2825 const char *msg;
2826 bool isSuccess;
2827 switch (reviewStatusType) {
2829 msg = "Operation signed";
2830 isSuccess = true;
2831 break;
2833 msg = "Operation rejected";
2834 isSuccess = false;
2835 break;
2837 msg = "Transaction signed";
2838 isSuccess = true;
2839 break;
2841 msg = "Transaction rejected";
2842 isSuccess = false;
2843 break;
2845 msg = "Message signed";
2846 isSuccess = true;
2847 break;
2849 msg = "Message rejected";
2850 isSuccess = false;
2851 break;
2853 msg = "Address verified";
2854 isSuccess = true;
2855 break;
2857 msg = "Address verification cancelled";
2858 isSuccess = false;
2859 break;
2860 default:
2861 return;
2862 }
2863 nbgl_useCaseStatus(msg, isSuccess, quitCallback);
2864}
2865
2879 const nbgl_icon_details_t *icon,
2880 const char *reviewTitle,
2881 const char *reviewSubTitle,
2882 nbgl_choiceCallback_t choiceCallback)
2883{
2884 // memorize streaming operation type for future API calls
2885 streamingOpType = operationType;
2886
2887 memset(&context, 0, sizeof(UseCaseContext_t));
2888 context.type = STREAMING_START_REVIEW_USE_CASE;
2889 context.operationType = operationType;
2890 context.review.reviewTitle = reviewTitle;
2891 context.review.reviewSubTitle = reviewSubTitle;
2892 context.review.icon = icon;
2893 context.review.onChoice = choiceCallback;
2894 context.currentPage = 0;
2895 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2896
2897 displayStreamingReviewPage(FORWARD_DIRECTION);
2898}
2899
2914 const nbgl_icon_details_t *icon,
2915 const char *reviewTitle,
2916 const char *reviewSubTitle,
2917 nbgl_choiceCallback_t choiceCallback)
2918{
2920 operationType, icon, reviewTitle, reviewSubTitle, &blindSigningWarning, choiceCallback);
2921}
2922
2939 const nbgl_icon_details_t *icon,
2940 const char *reviewTitle,
2941 const char *reviewSubTitle,
2942 const nbgl_warning_t *warning,
2943 nbgl_choiceCallback_t choiceCallback)
2944{
2945 memset(&context, 0, sizeof(UseCaseContext_t));
2946 context.type = STREAMING_START_REVIEW_USE_CASE;
2947 context.operationType = operationType;
2948 context.review.reviewTitle = reviewTitle;
2949 context.review.reviewSubTitle = reviewSubTitle;
2950 context.review.icon = icon;
2951 context.review.onChoice = choiceCallback;
2952 context.currentPage = 0;
2953 context.nbPages = reviewSubTitle ? 3 : 2; // Start page(s) + trick for review continue
2954
2955 // memorize streaming operation type for future API calls
2956 streamingOpType = operationType;
2957
2958 // if no warning at all, it's a simple review
2959 if ((warning == NULL)
2960 || ((warning->predefinedSet == 0) && (warning->info == NULL)
2961 && (warning->reviewDetails == NULL) && (warning->prelude == NULL))) {
2962 displayStreamingReviewPage(FORWARD_DIRECTION);
2963 return;
2964 }
2965 if (warning->predefinedSet == (1 << W3C_NO_THREAT_WARN)) {
2966 operationType |= NO_THREAT_OPERATION;
2967 }
2968 else if (warning->predefinedSet != 0) {
2969 operationType |= RISKY_OPERATION;
2970 }
2971 memset(&reviewWithWarnCtx, 0, sizeof(reviewWithWarnCtx));
2972
2973 reviewWithWarnCtx.type = context.type;
2974 reviewWithWarnCtx.operationType = operationType;
2975 reviewWithWarnCtx.icon = icon;
2976 reviewWithWarnCtx.reviewTitle = reviewTitle;
2977 reviewWithWarnCtx.reviewSubTitle = reviewSubTitle;
2978 reviewWithWarnCtx.choiceCallback = choiceCallback;
2979 reviewWithWarnCtx.warning = warning;
2980
2981 // if the warning contains a prelude, display it first
2982 if (reviewWithWarnCtx.warning->prelude) {
2983 displayPrelude();
2984 }
2985 // display the initial warning only of a risk/threat or blind signing or prelude
2986 else if (HAS_INITIAL_WARNING(warning)) {
2987 displayInitialWarning();
2988 }
2989 else {
2990 displayStreamingReviewPage(FORWARD_DIRECTION);
2991 }
2992}
2993
3008 nbgl_choiceCallback_t choiceCallback,
3009 nbgl_callback_t skipCallback)
3010{
3011 uint8_t curNbDataSets = context.review.nbDataSets;
3012
3013 memset(&context, 0, sizeof(UseCaseContext_t));
3014 context.type = STREAMING_CONTINUE_REVIEW_USE_CASE;
3015 context.operationType = streamingOpType;
3016 context.review.tagValueList = tagValueList;
3017 context.review.onChoice = choiceCallback;
3018 context.currentPage = 0;
3019 context.nbPages = tagValueList->nbPairs + 1; // data + trick for review continue
3020 context.review.skipCallback = skipCallback;
3021 context.review.nbDataSets = curNbDataSets + 1;
3022
3023 displayStreamingReviewPage(FORWARD_DIRECTION);
3024}
3025
3037 nbgl_choiceCallback_t choiceCallback)
3038{
3039 nbgl_useCaseReviewStreamingContinueExt(tagValueList, choiceCallback, NULL);
3040}
3041
3042void nbgl_useCaseReviewStreamingFinish(const char *finishTitle,
3043 nbgl_choiceCallback_t choiceCallback)
3044{
3045 memset(&context, 0, sizeof(UseCaseContext_t));
3046 context.type = STREAMING_FINISH_REVIEW_USE_CASE;
3047 context.operationType = streamingOpType;
3048 context.review.onChoice = choiceCallback;
3049 context.review.finishTitle = finishTitle;
3050 context.currentPage = 0;
3051 context.nbPages = 2; // 2 pages at the end for accept/reject
3052
3053 displayStreamingReviewPage(FORWARD_DIRECTION);
3054}
3055
3061void nbgl_useCaseSpinner(const char *text)
3062{
3063 memset(&context, 0, sizeof(UseCaseContext_t));
3064 context.type = SPINNER_USE_CASE;
3065 context.currentPage = 0;
3066 context.nbPages = 1;
3067
3068 displaySpinner(text);
3069}
3070
3084 const char *message,
3085 const char *subMessage,
3086 const char *confirmText,
3087 const char *cancelText,
3088 nbgl_choiceCallback_t callback)
3089{
3091 icon, message, subMessage, confirmText, cancelText, NULL, callback);
3092};
3093
3109 const char *message,
3110 const char *subMessage,
3111 const char *confirmText,
3112 const char *cancelText,
3113 nbgl_genericDetails_t *details,
3114 nbgl_choiceCallback_t callback)
3115{
3116 memset(&context, 0, sizeof(UseCaseContext_t));
3117 context.type = CHOICE_USE_CASE;
3118 context.choice.icon = icon;
3119 context.choice.message = message;
3120 context.choice.subMessage = subMessage;
3121 context.choice.confirmText = confirmText;
3122 context.choice.cancelText = cancelText;
3123 context.choice.onChoice = callback;
3124 context.choice.details = details;
3125 context.currentPage = 0;
3126 context.nbPages = 2; // 2 pages for confirm/cancel
3127 if (message != NULL) {
3128 context.nbPages++;
3129 // if both icon and subMessage are non NULL, add a page
3130 if ((icon != NULL) && (subMessage != NULL)) {
3131 context.nbPages++;
3132 }
3133 }
3134 if (details != NULL) {
3135 // only the first level of details and BAR_LIST type are supported
3136 if (details->type == BAR_LIST_WARNING) {
3137 context.nbPages += details->barList.nbBars;
3138 }
3139 }
3140
3141 displayChoicePage(FORWARD_DIRECTION);
3142};
3143
3144// On Nano, the advanced variant falls back to the basic one:
3145// - title → message (first text line)
3146// - message → subMessage (second text line; subMessage/gray address is not shown)
3147// - headerIcon is ignored (no top-right button on Nano)
3149 const nbgl_icon_details_t *headerIcon,
3150 const char *title,
3151 const char *message,
3152 const char *subMessage,
3153 const char *confirmText,
3154 const char *cancelText,
3155 nbgl_genericDetails_t *details,
3156 nbgl_choiceCallback_t callback)
3157{
3158 UNUSED(headerIcon);
3159 UNUSED(subMessage);
3161 centerIcon, title, message, confirmText, cancelText, details, callback);
3162}
3163
3177void nbgl_useCaseConfirm(const char *message,
3178 const char *subMessage,
3179 const char *confirmText,
3180 const char *cancelText,
3181 nbgl_callback_t callback)
3182{
3183 memset(&context, 0, sizeof(UseCaseContext_t));
3184 context.type = CONFIRM_USE_CASE;
3185 context.confirm.message = message;
3186 context.confirm.subMessage = subMessage;
3187 context.confirm.confirmText = confirmText;
3188 context.confirm.cancelText = cancelText;
3189 context.confirm.onConfirm = callback;
3190 context.currentPage = 0;
3191 context.nbPages = 1 + 2; // 2 pages at the end for confirm/cancel
3192
3193 displayConfirm(FORWARD_DIRECTION);
3194}
3195
3206 const char *message,
3207 const char *actionText,
3208 nbgl_callback_t callback)
3209{
3210 nbgl_layoutCenteredInfo_t centeredInfo = {0};
3211
3212 UNUSED(actionText);
3213
3214 // memorize callback in context
3215 memset(&context, 0, sizeof(UseCaseContext_t));
3216 context.type = ACTION_USE_CASE;
3217 context.action.actionCallback = callback;
3218
3219 centeredInfo.icon = icon;
3220 centeredInfo.text1 = message;
3221 centeredInfo.style = BOLD_TEXT1_INFO;
3222 nbgl_stepDrawCenteredInfo(0, useCaseActionCallback, NULL, &centeredInfo, false);
3223}
3224
3225#ifdef NBGL_KEYPAD
3244void nbgl_useCaseKeypad(const char *title,
3245 uint8_t minDigits,
3246 uint8_t maxDigits,
3247 bool shuffled,
3248 bool hidden,
3249 nbgl_pinValidCallback_t validatePinCallback,
3250 nbgl_callback_t backCallback)
3251{
3252 nbgl_layoutDescription_t layoutDescription = {0};
3253 int status = -1;
3254
3255 // reset the keypad context
3256 memset(&context, 0, sizeof(KeypadContext_t));
3257 context.type = KEYPAD_USE_CASE;
3258 context.currentPage = 0;
3259 context.nbPages = 1;
3260 context.keypad.validatePin = validatePinCallback;
3261 context.keypad.backCallback = backCallback;
3262 context.keypad.pinMinDigits = minDigits;
3263 context.keypad.pinMaxDigits = maxDigits;
3264 context.keypad.hidden = hidden;
3265 context.keypad.layoutCtx = nbgl_layoutGet(&layoutDescription);
3266
3267 // add keypad
3268 status = nbgl_layoutAddKeypad(context.keypad.layoutCtx, keypadCallback, title, shuffled);
3269 if (status < 0) {
3270 return;
3271 }
3272 context.keypad.keypadIndex = status;
3273 // add digits
3274 status = nbgl_layoutAddKeypadContent(context.keypad.layoutCtx, hidden, maxDigits, "");
3275 if (status < 0) {
3276 return;
3277 }
3278
3279 nbgl_layoutDraw(context.keypad.layoutCtx);
3280 if (context.keypad.backCallback != NULL) {
3281 // force backspace to be visible at first digit, to be used as quit
3282 nbgl_layoutUpdateKeypad(context.keypad.layoutCtx, context.keypad.keypadIndex, false, true);
3283 }
3284 nbgl_refresh();
3285}
3286#endif // NBGL_KEYPAD
3287
3288#ifdef NBGL_KEYBOARD
3312void nbgl_useCaseKeyboard(const nbgl_keyboardParams_t *params, nbgl_callback_t backCallback)
3313{
3314 // clang-format off
3315 nbgl_layoutDescription_t layoutDescription = {0};
3316 nbgl_layoutCenteredInfo_t centeredInfo = {.text1 = params->title,
3317 .onTop = true};
3319 .indication = LEFT_ARROW | RIGHT_ARROW};
3320 nbgl_layoutKbd_t kbdInfo = {.callback = &keyboardCallback,
3321 .mode = params->mode,
3322 .lettersOnly = params->lettersOnly};
3323 int status = -1;
3324 // clang-format on
3325
3326 // reset the keypad context
3327 memset(&context, 0, sizeof(UseCaseContext_t));
3328 context.type = KEYBOARD_USE_CASE;
3329 context.currentPage = 0;
3330 context.nbPages = 1;
3331 context.keyboard.entryBuffer = PIC(params->entryBuffer);
3332 context.keyboard.entryMaxLen = params->entryMaxLen;
3333 context.keyboard.entryBuffer[0] = '\0';
3334
3335 context.keyboard.content.type = params->type;
3336
3337 // memorize context
3338 context.keyboard.backCallback = PIC(backCallback);
3339
3340 switch (params->type) {
3342 context.keyboard.actionCallback = PIC(params->confirmationParams.onButtonCallback);
3343 break;
3345 // No need for 'Validate' or 'Case switch' buttons
3346 kbdInfo.keyMask = (1 << VALIDATE_INDEX) | (1 << SHIFT_KEY_INDEX);
3347 context.keyboard.getSuggestButtons
3349 context.keyboard.onButtonCallback = PIC(params->suggestionParams.onButtonCallback);
3350 context.keyboard.content.suggestionButtons = (nbgl_layoutSuggestionButtons_t){
3351 .buttons = PIC(params->suggestionParams.buttons),
3352 .firstButtonToken = params->suggestionParams.firstButtonToken,
3353 };
3354 break;
3355 default:
3356 return;
3357 }
3358
3359 // get a layout
3360 context.keyboard.layoutCtx = nbgl_layoutGet(&layoutDescription);
3361
3362 // add description
3363 nbgl_layoutAddCenteredInfo(context.keyboard.layoutCtx, &centeredInfo);
3364
3365 // Add keyboard
3366 status = nbgl_layoutAddKeyboard(context.keyboard.layoutCtx, &kbdInfo);
3367 if (status < 0) {
3368 return;
3369 }
3370 context.keyboard.keyboardIndex = status;
3371
3372 // add empty entered text
3373 status = nbgl_layoutAddEnteredText(context.keyboard.layoutCtx, "", true);
3374 if (status < 0) {
3375 return;
3376 }
3377 context.keyboard.textIndex = status;
3378
3379 nbgl_layoutAddNavigation(context.keyboard.layoutCtx, &navInfo);
3380
3381 nbgl_layoutDraw(context.keyboard.layoutCtx);
3382 nbgl_refresh();
3383}
3384#endif // NBGL_KEYBOARD
3385
3386#endif // HAVE_SE_TOUCH
3387#endif // NBGL_USE_CASE
nbgl_contentCenteredInfoStyle_t
possible styles for Centered Info Area
@ CHOICES_LIST
list of choices through radio buttons
@ CENTERED_INFO
a centered info
@ SWITCHES_LIST
list of switches with descriptions
@ INFOS_LIST
list of infos with titles
@ TAG_VALUE_CONFIRM
tag/value pairs and a black button/footer to confirm/cancel.
@ TAG_VALUE_LIST
list of tag/value pairs
@ BARS_LIST
list of touchable bars (with > on the right to go to sub-pages)
@ INFO_BUTTON
a centered info and a simple black button
@ INFO_LIST_ALIAS
alias is list of infos
@ TAG_VALUE_LIST_ALIAS
@ ENS_ALIAS
alias comes from ENS
@ ADDRESS_BOOK_ALIAS
alias comes from Address Book
void(* nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
prototype of function to be called when an action on a content object occurs
debug traces management
#define LOG_WARN(__logger,...)
Definition nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
@ USE_CASE_LOGGER
Definition nbgl_debug.h:35
void(* nbgl_stepCallback_t)(void)
prototype of function to be called when a step is using a callback on "double-key" action
Definition nbgl_flow.h:38
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
DEPRECATED int nbgl_layoutAddEnteredText(nbgl_layout_t *layout, bool numbered, uint8_t number, const char *text, bool grayedOut, int offsetY, int token)
Adds a "text entry" area under the previously entered object. This area can be preceded (beginning of...
int nbgl_layoutUpdateKeyboard(nbgl_layout_t *layout, uint8_t index, uint32_t keyMask, bool updateCasing, keyboardCase_t casing)
Updates an existing keyboard on bottom of the screen, with the given configuration.
int nbgl_layoutAddKeyboard(nbgl_layout_t *layout, const nbgl_layoutKbd_t *kbdInfo)
Creates a keyboard on bottom of the screen, with the given configuration.
@ HORIZONTAL_NAV
'<' and '>' are displayed, to navigate between pages and steps
@ LEFT_ARROW
left arrow is used
@ RIGHT_ARROW
right arrow is used
int nbgl_layoutUpdateKeypad(nbgl_layout_t *layout, uint8_t index, bool enableValidate, bool enableBackspace, bool enableDigits)
Updates an existing keypad on bottom of the screen, with the given configuration.
int nbgl_layoutAddCenteredInfo(nbgl_layout_t *layout, const nbgl_layoutCenteredInfo_t *info)
Creates an area on the center of the main panel, with a possible icon/image, a possible text in black...
int nbgl_layoutDraw(nbgl_layout_t *layout)
Applies given layout. The screen will be redrawn.
void * nbgl_layout_t
type shared externally
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
@ KEYBOARD_WITH_BUTTON
text entry area + confirmation button
@ KEYBOARD_WITH_SUGGESTIONS
text entry area + suggestion buttons
int nbgl_layoutRelease(nbgl_layout_t *layout)
Release the layout obtained with nbgl_layoutGet()
DEPRECATED int nbgl_layoutUpdateEnteredText(nbgl_layout_t *layout, uint8_t index, bool numbered, uint8_t number, const char *text, bool grayedOut)
Updates an existing "text entry" area, created with nbgl_layoutAddEnteredText()
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
#define SHIFT_KEY_INDEX
Definition nbgl_obj.h:41
void nbgl_refresh(void)
nbgl_buttonEvent_t
Definition nbgl_obj.h:313
@ BUTTON_BOTH_PRESSED
Sent when both buttons are released.
Definition nbgl_obj.h:320
@ BUTTON_LEFT_PRESSED
Sent when Left button is released.
Definition nbgl_obj.h:314
@ BUTTON_RIGHT_PRESSED
Send when Right button is released.
Definition nbgl_obj.h:315
#define BACKSPACE_KEY
Definition nbgl_obj.h:26
#define VALIDATE_KEY
Definition nbgl_obj.h:27
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
void(* nbgl_stepButtonCallback_t)(nbgl_step_t stepCtx, nbgl_buttonEvent_t event)
prototype of function to be called when buttons are touched on a screen
Definition nbgl_step.h:57
#define GET_POS_OF_STEP(_step, _nb_steps)
Definition nbgl_step.h:30
nbgl_step_t nbgl_stepDrawSwitch(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutSwitch_t *switchInfo, bool modal)
void * nbgl_step_t
type shared externally
Definition nbgl_step.h:44
uint8_t nbgl_stepPosition_t
this type is a bitfield containing:
Definition nbgl_step.h:88
nbgl_step_t nbgl_stepDrawMenuList(nbgl_stepMenuListCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutMenuList_t *list, bool modal)
#define ACTION_ON_ANY_BUTTON
When action callback applies only on both button press.
Definition nbgl_step.h:75
@ NEITHER_FIRST_NOR_LAST_STEP
neither first nor last in a multiple steps flow
Definition nbgl_step.h:67
@ SINGLE_STEP
single step flow
Definition nbgl_step.h:64
@ LAST_STEP
last in a multiple steps flow
Definition nbgl_step.h:66
@ FIRST_STEP
first in a multiple steps flow
Definition nbgl_step.h:65
int nbgl_stepRelease(nbgl_step_t step)
#define FORWARD_DIRECTION
When the flow is navigated from last to first step.
Definition nbgl_step.h:71
nbgl_step_t nbgl_stepDrawText(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, const char *text, const char *subText, nbgl_contentCenteredInfoStyle_t style, bool modal)
nbgl_step_t nbgl_stepDrawCenteredInfo(nbgl_stepPosition_t pos, nbgl_stepButtonCallback_t onActionCallback, nbgl_screenTickerConfiguration_t *ticker, nbgl_layoutCenteredInfo_t *info, bool modal)
#define BACKWARD_DIRECTION
When action callback applies on any button press.
Definition nbgl_step.h:73
nbgl_state_t
to represent a boolean state.
Definition nbgl_types.h:199
@ ON_STATE
Definition nbgl_types.h:201
@ OFF_STATE
Definition nbgl_types.h:200
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
@ NO_TYPE_WARNING
Invalid type (to use for bars leading to nothing)
@ 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)
#define RISKY_OPERATION
This is to use in nbgl_operationType_t when the operation is concerned by an internal warning....
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)
#define NO_THREAT_OPERATION
This is to use in nbgl_operationType_t when the operation is concerned by an internal information....
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_useCaseAdvancedChoiceWithDetails(const nbgl_icon_details_t *centerIcon, const nbgl_icon_details_t *headerIcon, const char *title, const char *message, const char *subMessage, const char *confirmText, const char *cancelText, nbgl_warningDetails_t *details, nbgl_choiceCallback_t callback)
void nbgl_useCaseSpinner(const char *text)
void nbgl_useCaseConfirm(const char *message, const char *subMessage, const char *confirmText, const char *rejectText, nbgl_callback_t callback)
void nbgl_useCaseStatus(const char *message, bool isSuccess, nbgl_callback_t quitCallback)
@ BLIND_SIGNING_WARN
Blind signing.
@ W3C_NO_THREAT_WARN
Web3 Checks: No Threat detected.
void nbgl_useCaseGenericConfiguration(const char *title, uint8_t initPage, const nbgl_genericContents_t *contents, nbgl_callback_t quitCallback)
#define INIT_HOME_PAGE
Value to pass to nbgl_useCaseHomeAndSettings() initSettingPage parameter to initialize the use case o...
void nbgl_useCaseKeypad(const char *title, uint8_t minDigits, uint8_t maxDigits, bool shuffled, bool hidden, nbgl_pinValidCallback_t validatePinCallback, nbgl_callback_t backCallback)
void nbgl_useCaseReviewStreamingContinueExt(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback, nbgl_callback_t skipCallback)
void nbgl_useCaseReviewBlindSigning(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseChoice(const nbgl_icon_details_t *icon, const char *message, const char *subMessage, const char *confirmText, const char *rejectString, nbgl_choiceCallback_t callback)
void nbgl_useCaseGenericReview(const nbgl_genericContents_t *contents, const char *rejectText, nbgl_callback_t rejectCallback)
#define STATUS_SCREEN_DURATION
void nbgl_useCaseKeyboard(const nbgl_keyboardParams_t *params, nbgl_callback_t backCallback)
void nbgl_useCaseReviewStreamingBlindSigningStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbChoicesInPage(uint8_t nbChoices, const nbgl_contentRadioChoice_t *choicesList, uint8_t startIndex, bool withNav)
void(* nbgl_keyboardButtonsCallback_t)(nbgl_layoutKeyboardContent_t *content, uint32_t *mask)
prototype of keyboard buttons callback function
#define REAL_TYPE_MASK
Mask to apply on nbgl_operationType_t to extract the base nbgl_opType_t. Bits 0-3 are reserved for th...
void nbgl_useCaseReviewStatus(nbgl_reviewStatusType_t reviewStatusType, nbgl_callback_t quitCallback)
#define ADDRESS_BOOK_OPERATION
This is to use in nbgl_operationType_t when the operation is related to "Address Book" to adapt wordi...
nbgl_reviewStatusType_t
The different types of review status.
@ STATUS_TYPE_TRANSACTION_REJECTED
@ STATUS_TYPE_ADDRESS_REJECTED
@ STATUS_TYPE_TRANSACTION_SIGNED
@ STATUS_TYPE_OPERATION_REJECTED
@ STATUS_TYPE_OPERATION_SIGNED
@ STATUS_TYPE_ADDRESS_VERIFIED
@ STATUS_TYPE_MESSAGE_SIGNED
@ STATUS_TYPE_MESSAGE_REJECTED
bool(* nbgl_navCallback_t)(uint8_t page, nbgl_pageContent_t *content)
prototype of navigation callback function
#define HAS_INITIAL_WARNING(_warning)
void nbgl_useCaseAddressReview(const char *address, const nbgl_contentTagValueList_t *additionalTagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, nbgl_choiceCallback_t choiceCallback)
@ TYPE_MESSAGE
@ TYPE_TRANSACTION
For operations transferring a coin or taken from an account to another.
void nbgl_useCaseAdvancedReviewStreamingStart(nbgl_operationType_t operationType, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseAdvancedReview(nbgl_operationType_t operationType, const nbgl_contentTagValueList_t *tagValueList, const nbgl_icon_details_t *icon, const char *reviewTitle, const char *reviewSubTitle, const char *finishTitle, const nbgl_tipBox_t *tipBox, const nbgl_warning_t *warning, nbgl_choiceCallback_t choiceCallback)
uint8_t nbgl_useCaseGetNbSwitchesInPage(uint8_t nbSwitches, const nbgl_contentSwitchesList_t *switchesList, uint8_t startIndex, bool withNav)
This structure contains data to build a BARS_LIST content.
const uint8_t * tokens
array of tokens, one for each bar (nbBars items)
const char *const * barTexts
array of texts for each bar (nbBars items, in black/bold)
uint8_t nbBars
number of elements in barTexts and tokens array
const nbgl_icon_details_t * icon
the icon (can be null)
const char * title
title in black large (can be null)
const char * description
description in black small regular case (can be null)
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
bool onTop
if set to true, align only horizontally
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * buttonText
text of the long press button
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
const char * text
centered text in large case
This structure contains data to build a INFOS_LIST content.
const char *const * infoContents
array of contents of infos (in black)
const char *const * infoTypes
array of types of infos (in black/bold)
uint8_t nbInfos
number of elements in infoTypes and infoContents array
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
uint8_t token
the token that will be used as argument of the callback
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
const char * text
main text for the switch
uint8_t token
the token that will be used as argument of the callback (unused on Nano)
nbgl_state_t initState
initial state of the switch
const char * subText
description under main text (NULL terminated, single line, may be null)
const char * confirmationText
text of the confirmation button, if NULL "It matches" is used
uint8_t confirmationToken
the token used as argument of the onActionCallback
nbgl_contentTagValueList_t tagValueList
list of tag/value pairs
This structure contains a list of [tag,value] pairs.
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair and possible extensions.
const nbgl_contentValueExt_t * extension
if not NULL, gives additional info on value field
const nbgl_icon_details_t * valueIcon
int8_t centeredInfo
if set to 1, the tag will be displayed as a centered info
const char * value
string giving the value name
const char * item
string giving the tag name
This structure contains additions to a tag/value pair, to be able to build a screen to display these ...
This structure contains data to build a content.
nbgl_content_u content
nbgl_contentActionCallback_t contentActionCallback
callback to be called when an action on an object occurs
nbgl_contentType_t type
type of page content in the content union
const struct nbgl_genericDetails_s * details
array of nbBars structures giving what to display when each bar is touched.
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
nbgl_contentCenter_t centeredInfo
centered info, if type == CENTERED_INFO_WARNING
Structure describing the action button in Home Screen.
nbgl_callback_t onButtonCallback
callback to call when the button is pressed
nbgl_keyboardButtonsCallback_t updateButtonsCallback
callback to call when a key is pressed to update suggestions
nbgl_layoutTouchCallback_t onButtonCallback
callback to call when one of the buttons is pressed
const char ** buttons
array of strings for buttons (last ones can be NULL)
int firstButtonToken
first token used for buttons, provided in onButtonCallback
Structure containing all parameters for keyboard use case.
keyboardMode_t mode
keyboard mode to start with
nbgl_kbdSuggestParams_t suggestionParams
nbgl_kbdButtonParams_t confirmationParams
used if type is KEYBOARD_WITH_SUGGESTIONS
bool lettersOnly
if true, only display letter keys and Backspace
uint8_t entryMaxLen
maximum length of text that can be entered
nbgl_layoutKeyboardContentType_t type
type of content
const char * title
centered title explaining the screen
char * entryBuffer
already entered text
Structure containing all information when creating a layout. This structure must be passed as argumen...
This structure contains info to build a keyboard with nbgl_layoutAddKeyboard()
keyboardCallback_t callback
function called when an active key is pressed
This structure contains info to build a keyboard content (controls that are linked to keyboard)
This structure contains a list of names to build a menu list on Nanos, with for each item a descripti...
nbgl_menuListCallback_t callback
function to call to retrieve a menu list item text
uint8_t nbChoices
total number of choices in the menu list
uint8_t selectedChoice
index of the selected choice (centered, in bold)
This structure contains info to build a navigation bar at the bottom of the screen.
nbgl_layoutNavDirection_t direction
vertical or horizontal navigation
This structure contains info to build suggestion buttons.
This structure contains data to build a page in multi-pages mode (nbgl_pageDrawGenericContent)
Definition nbgl_flow.h:58
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type
Definition nbgl_flow.h:67
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition nbgl_flow.h:65
nbgl_contentBarsList_t barsList
BARS_LIST type
Definition nbgl_flow.h:68
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
Definition nbgl_flow.h:62
nbgl_contentInfoList_t infosList
INFOS_LIST type
Definition nbgl_flow.h:66
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
Definition nbgl_flow.h:63
nbgl_contentType_t type
type of page content in the following union
Definition nbgl_flow.h:59
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
Definition nbgl_flow.h:61
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
Definition nbgl_flow.h:64
This structure contains data to build a SWITCHES_LIST content.
uint8_t nbSwitches
number of elements in switches and tokens array
const nbgl_contentSwitch_t * switches
array of switches (nbSwitches items)
The necessary parameters to build a tip-box in first review page and the modal if this tip box is tou...
The necessary parameters to build a warning page preceding a review. One can either use predefinedSet...
const nbgl_preludeDetails_t * prelude
if not null, means that the review can start by a prelude
const nbgl_warningDetails_t * reviewDetails
uint32_t predefinedSet
const nbgl_contentCenter_t * info
parameters to build the intro warning page, if not using pre-defined
nbgl_contentInfoList_t infosList
INFOS_LIST type
nbgl_contentTagValueConfirm_t tagValueConfirm
TAG_VALUE_CONFIRM type
nbgl_contentTagValueList_t tagValueList
TAG_VALUE_LIST type
nbgl_contentCenteredInfo_t centeredInfo
CENTERED_INFO type
nbgl_contentBarsList_t barsList
BARS_LIST type
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
nbgl_contentInfoButton_t infoButton
INFO_BUTTON type
nbgl_contentRadioChoice_t choicesList
CHOICES_LIST type