diff --git a/lib_nbgl/glyphs/32px/Mini_Push_32px.png b/lib_nbgl/glyphs/32px/Mini_Push_32px.png new file mode 100755 index 000000000..9d3fc747e Binary files /dev/null and b/lib_nbgl/glyphs/32px/Mini_Push_32px.png differ diff --git a/lib_nbgl/glyphs/40px/Mini_Push_40px.png b/lib_nbgl/glyphs/40px/Mini_Push_40px.png new file mode 100755 index 000000000..6d105ff42 Binary files /dev/null and b/lib_nbgl/glyphs/40px/Mini_Push_40px.png differ diff --git a/lib_nbgl/include/nbgl_content.h b/lib_nbgl/include/nbgl_content.h index bf091d534..88a577d29 100644 --- a/lib_nbgl/include/nbgl_content.h +++ b/lib_nbgl/include/nbgl_content.h @@ -98,6 +98,26 @@ typedef struct { #endif } nbgl_contentInfoButton_t; +/** + * @brief possible types of value alias + * + */ +typedef enum { + ENS_ALIAS = 0, ///< alias comes from ENS + ADDRESS_BOOK_ALIAS ///< alias comes from Address Book +} nbgl_contentValueAliasType_t; + +/** + * @brief This structure contains additions to a tag/value pair, + * to be able to build a screen to display these additions (for alias) + */ +typedef struct { + const char *fullValue; ///< full string of the value when used as an alias + const char *explanation; ///< string displayed in gray, explaing where the alias comes from + ///< if NULL, a default explanation is provided, depending of the type + nbgl_contentValueAliasType_t aliasType; ///< type of alias +} nbgl_contentValueExt_t; + /** * @brief This structure contains a [tag,value] pair */ @@ -105,11 +125,17 @@ typedef struct { const char *item; ///< string giving the tag name const char *value; ///< string giving the value name #ifdef SCREEN_SIZE_WALLET - const nbgl_icon_details_t *valueIcon; ///< a buffer containing the 32px 1BPP icon for icon on - ///< right of value (can be NULL) + union { + const nbgl_icon_details_t *valueIcon; ///< a buffer containing the 32px 1BPP icon for icon + ///< on right of value (can be NULL) + const nbgl_contentValueExt_t + *extension; ///< if not NULL, gives additional info on value field + }; int8_t forcePageStart : 1; ///< if set to 1, the tag will be displayed at the top of a new ///< review page int8_t centeredInfo : 1; ///< if set to 1, the tag will be displayed as a centered info + int8_t aliasValue : 1; ///< if set to 1, the value represents an alias and an icon enables to + ///< view the full value #endif } nbgl_contentTagValue_t; @@ -120,6 +146,15 @@ typedef struct { */ typedef nbgl_contentTagValue_t *(*nbgl_contentTagValueCallback_t)(uint8_t pairIndex); +/** + * @brief prototype of function to be called when an action on a content object occurs + * @param token integer passed at content object initialization + * @param index when the object touched is a list of radio buttons, gives the index of the activated + * @param page index of the current page, can be used to restart the use_case directly at the right + * page button + */ +typedef void (*nbgl_contentActionCallback_t)(int token, uint8_t index, int page); + /** * @brief This structure contains a list of [tag,value] pairs */ @@ -137,6 +172,8 @@ typedef struct { bool smallCaseForValue; ///< if set to true, a 24px font is used for value text, otherwise a ///< 32px font is used bool wrapping; ///< if set to true, value text will be wrapped on ' ' to avoid cutting words + nbgl_contentActionCallback_t + actionCallback; ///< called when a valueIcon is touched on a given pair } nbgl_contentTagValueList_t; /** @@ -277,15 +314,6 @@ typedef union { nbgl_contentBarsList_t barsList; ///< @ref BARS_LIST type } nbgl_content_u; -/** - * @brief prototype of function to be called when an action on a content object occurs - * @param token integer passed at content object initialization - * @param index when the object touched is a list of radio buttons, gives the index of the activated - * @param page index of the current page, can be used to restart the use_case directly at the right - * page button - */ -typedef void (*nbgl_contentActionCallback_t)(int token, uint8_t index, int page); - /** * @brief This structure contains data to build a content */ diff --git a/lib_nbgl/include/nbgl_layout.h b/lib_nbgl/include/nbgl_layout.h index 402bbf472..f7adda34b 100644 --- a/lib_nbgl/include/nbgl_layout.h +++ b/lib_nbgl/include/nbgl_layout.h @@ -545,6 +545,10 @@ int nbgl_layoutAddHorizontalButtons(nbgl_layout_t *layout const nbgl_layoutHorizontalButtons_t *info); int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueList_t *list); int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text); +int nbgl_layoutAddTextContent(nbgl_layout_t *layout, + const char *title, + const char *description, + const char *info); int nbgl_layoutAddSeparationLine(nbgl_layout_t *layout); int nbgl_layoutAddButton(nbgl_layout_t *layout, const nbgl_layoutButton_t *buttonInfo); diff --git a/lib_nbgl/include/nbgl_obj.h b/lib_nbgl/include/nbgl_obj.h index a8c95eafa..ff205568d 100644 --- a/lib_nbgl/include/nbgl_obj.h +++ b/lib_nbgl/include/nbgl_obj.h @@ -123,6 +123,8 @@ extern "C" { #define WHEEL_ICON C_Settings_32px #define INFO_I_ICON C_Info_32px #define QRCODE_ICON C_QRCode_32px +#define MINI_PUSH_ICON C_Mini_Push_32px +#define WARNING_ICON C_Warning_32px #else // TARGET_STAX #define SPACE_ICON C_Space_40px #define BACKSPACE_ICON C_Erase_40px @@ -138,6 +140,8 @@ extern "C" { #define WHEEL_ICON C_Settings_40px #define INFO_I_ICON C_Info_40px #define QRCODE_ICON C_QRCode_40px +#define MINI_PUSH_ICON C_Mini_Push_40px +#define WARNING_ICON C_Warning_40px #endif // TARGET_STAX // For backward compatibility, to be remove later @@ -545,6 +549,9 @@ enum { KEYPAD_ID, KEYBOARD_ID, ENTERED_TEXT_ID, + VALUE_BUTTON_1_ID, + VALUE_BUTTON_2_ID, + VALUE_BUTTON_3_ID, LONG_PRESS_BUTTON_ID, CONTROLS_ID, // when multiple controls in the same pages (buttons, switches, radios) NB_CONTROL_IDS diff --git a/lib_nbgl/src/nbgl_layout.c b/lib_nbgl/src/nbgl_layout.c index 76d9d92b6..f51f7d8de 100644 --- a/lib_nbgl/src/nbgl_layout.c +++ b/lib_nbgl/src/nbgl_layout.c @@ -1156,6 +1156,85 @@ int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text) return 0; } +/** + * @brief Creates in the main container three text areas: + * - a first one in black large case, with title param + * - a second one under it, in black small case, with description param + * - a last one at the bottom of the container, in gray, with info param + * + * @param layout the current layout + * @param title main text (in large bold font) + * @param description description under main text (in small regular font) + * @param info description at bottom (in small gray) + * @return height of the control if OK + */ +int nbgl_layoutAddTextContent(nbgl_layout_t *layout, + const char *title, + const char *description, + const char *info) +{ + nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout; + nbgl_text_area_t *textArea; + + LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n"); + if (layout == NULL) { + return -1; + } + + // create title + textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer); + textArea->textColor = BLACK; + textArea->text = PIC(title); + textArea->textAlignment = MID_LEFT; + textArea->fontId = LARGE_MEDIUM_FONT; + textArea->style = NO_STYLE; + textArea->wrapping = true; + textArea->obj.alignment = NO_ALIGNMENT; + textArea->obj.alignmentMarginX = BORDER_MARGIN; + textArea->obj.alignmentMarginY = 16; + textArea->obj.area.width = AVAILABLE_WIDTH; + textArea->obj.area.height = nbgl_getTextHeightInWidth( + textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping); + // set this new obj as child of main container + layoutAddObject(layoutInt, (nbgl_obj_t *) textArea); + + // create description + textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer); + textArea->textColor = BLACK; + textArea->text = PIC(description); + textArea->fontId = SMALL_REGULAR_FONT; + textArea->style = NO_STYLE; + textArea->wrapping = true; + textArea->obj.area.width = AVAILABLE_WIDTH; + textArea->obj.area.height = nbgl_getTextHeightInWidth( + textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping); + textArea->textAlignment = MID_LEFT; + textArea->obj.alignment = NO_ALIGNMENT; + textArea->obj.alignmentMarginX = BORDER_MARGIN; + textArea->obj.alignmentMarginY = 24; + // set this new obj as child of main container + layoutAddObject(layoutInt, (nbgl_obj_t *) textArea); + + // create info on the bottom + textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer); + textArea->textColor = DARK_GRAY; + textArea->text = PIC(info); + textArea->fontId = SMALL_REGULAR_FONT; + textArea->style = NO_STYLE; + textArea->wrapping = true; + textArea->obj.area.width = AVAILABLE_WIDTH; + textArea->obj.area.height = nbgl_getTextHeightInWidth( + textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping); + textArea->textAlignment = MID_LEFT; + textArea->obj.alignment = BOTTOM_LEFT; + textArea->obj.alignmentMarginX = BORDER_MARGIN; + textArea->obj.alignmentMarginY = 40; + // set this new obj as child of main container + layoutAddObject(layoutInt, (nbgl_obj_t *) textArea); + + return layoutInt->container->obj.area.height; +} + /** * @brief Creates a list of radio buttons (on the right) * @@ -1699,7 +1778,8 @@ int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueL for (i = 0; i < list->nbPairs; i++) { const nbgl_layoutTagValue_t *pair; - uint16_t fullHeight = 0, usableWidth; + uint16_t fullHeight = 0; + const nbgl_icon_details_t *valueIcon = NULL; if (list->pairs != NULL) { pair = &list->pairs[i]; @@ -1707,8 +1787,6 @@ int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueL else { pair = list->callback(list->startIndex + i); } - // width that can be used for item and text - usableWidth = AVAILABLE_WIDTH; container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer); @@ -1725,9 +1803,9 @@ int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueL itemTextArea->textAlignment = MID_LEFT; itemTextArea->fontId = SMALL_REGULAR_FONT; itemTextArea->wrapping = true; - itemTextArea->obj.area.width = usableWidth; + itemTextArea->obj.area.width = AVAILABLE_WIDTH; itemTextArea->obj.area.height = nbgl_getTextHeightInWidth( - itemTextArea->fontId, itemTextArea->text, usableWidth, itemTextArea->wrapping); + itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping); itemTextArea->style = NO_STYLE; itemTextArea->obj.alignment = NO_ALIGNMENT; itemTextArea->obj.alignmentMarginX = 0; @@ -1748,19 +1826,28 @@ int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueL else { valueTextArea->fontId = LARGE_MEDIUM_FONT; } - if (pair->valueIcon == NULL) { - valueTextArea->obj.area.width = usableWidth; + if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) { + valueTextArea->obj.area.width = AVAILABLE_WIDTH; } else { - // we assume that value is single line - valueTextArea->obj.area.width - = nbgl_getTextWidth(valueTextArea->fontId, valueTextArea->text); + if (pair->aliasValue) { + // if the value is an alias, we automatically display a (>) icon + valueIcon = &MINI_PUSH_ICON; + } + else { + // otherwise use the provided icon + valueIcon = PIC(pair->valueIcon); + } + // decrease the available width for value text + valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12; } // handle the nbMaxLinesForValue parameter, used to automatically keep only // nbMaxLinesForValue lines - uint16_t nbLines = nbgl_getTextNbLinesInWidth( - valueTextArea->fontId, valueTextArea->text, usableWidth, list->wrapping); + uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId, + valueTextArea->text, + valueTextArea->obj.area.width, + list->wrapping); // use this nbMaxLinesForValue parameter only if >0 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) { nbLines = list->nbMaxLinesForValue; @@ -1777,23 +1864,24 @@ int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueL container->nbChildren++; fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY; - if (pair->valueIcon != NULL) { + if (valueIcon != NULL) { nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer); layoutObj_t *obj = layoutAddCallbackObj( layoutInt, (nbgl_obj_t *) image, list->token, TUNE_TAP_CASUAL); obj->index = i; image->foregroundColor = BLACK; - image->buffer = PIC(pair->valueIcon); - image->obj.alignment = MID_RIGHT; - image->obj.alignmentMarginX = 4; + image->buffer = valueIcon; + image->obj.alignment = RIGHT_TOP; + image->obj.alignmentMarginX = 12; image->obj.alignTo = (nbgl_obj_t *) valueTextArea; image->obj.touchMask = (1 << TOUCHED); + image->obj.touchId = VALUE_BUTTON_1_ID + i; container->children[container->nbChildren] = (nbgl_obj_t *) image; container->nbChildren++; } - container->obj.area.width = usableWidth; + container->obj.area.width = AVAILABLE_WIDTH; container->obj.area.height = fullHeight; container->layout = VERTICAL; container->obj.alignmentMarginX = BORDER_MARGIN; @@ -2372,7 +2460,7 @@ int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *heade textArea->fontId = SMALL_BOLD_FONT; textArea->textAlignment = CENTER; textArea->obj.touchMask = (1 << TOUCHED); - textArea->obj.touchId = RIGHT_BUTTON_ID; + textArea->obj.touchId = TOP_RIGHT_BUTTON_ID; // add to bottom container layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren] = (nbgl_obj_t *) textArea; diff --git a/lib_nbgl/src/nbgl_use_case.c b/lib_nbgl/src/nbgl_use_case.c index e239f0358..121d4c7fd 100644 --- a/lib_nbgl/src/nbgl_use_case.c +++ b/lib_nbgl/src/nbgl_use_case.c @@ -62,7 +62,8 @@ enum { CHOICE_TOKEN, DETAILS_BUTTON_TOKEN, CONFIRM_TOKEN, - REJECT_TOKEN + REJECT_TOKEN, + VALUE_ALIAS_TOKEN }; typedef enum { @@ -107,6 +108,12 @@ typedef struct { const char *detailsItem; const char *detailsvalue; bool detailsWrapping; + const nbgl_contentTagValue_t + *currentPairs; // to be used to retrieve the pairs with value alias + nbgl_contentTagValueCallback_t + currentCallback; // to be used to retrieve the pairs with value alias + + nbgl_layout_t modalLayout; } GenericContext_t; typedef struct { @@ -226,6 +233,7 @@ static char reducedAddress[QRCODE_REDUCED_ADDR_LEN]; **********************/ static void displayReviewPage(uint8_t page, bool forceFullRefresh); static void displayDetailsPage(uint8_t page, bool forceFullRefresh); +static void displayFullValuePage(const nbgl_contentTagValue_t *pair); static void displaySettingsPage(uint8_t page, bool forceFullRefresh); static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh); static void pageCallback(int token, uint8_t index); @@ -233,6 +241,7 @@ static void pageCallback(int token, uint8_t index); static void displayAddressQRCode(void); static void addressLayoutTouchCallbackQR(int token, uint8_t index); #endif // NBGL_QRCODE +static void modalLayoutTouchCallback(int token, uint8_t index); static void displaySkipWarning(void); static void bundleNavStartHome(void); @@ -543,6 +552,17 @@ static void pageCallback(int token, uint8_t index) // display a modal warning to confirm skip displaySkipWarning(); } + else if (token == VALUE_ALIAS_TOKEN) { + // the icon next to value alias has been touched + const nbgl_contentTagValue_t *pair; + if (genericContext.currentPairs != NULL) { + pair = &genericContext.currentPairs[genericContext.currentElementIdx + index]; + } + else { + pair = genericContext.currentCallback(genericContext.currentElementIdx + index); + } + displayFullValuePage(pair); + } else { // probably a control provided by caller if (onContentAction != NULL) { onContentAction(token, index, navInfo.activePage); @@ -797,6 +817,10 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content, case TAG_VALUE_LIST: { nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList; + // memorize pairs (or callback) for usage when alias is used + genericContext.currentPairs = p_content->content.tagValueList.pairs; + genericContext.currentCallback = p_content->content.tagValueList.callback; + if (flag) { // Flag can be set if the pair is too long to fit or because it needs // to be displayed as centered info. @@ -805,12 +829,10 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content, const nbgl_layoutTagValue_t *pair; if (p_content->content.tagValueList.pairs != NULL) { - pair = PIC( - &p_content->content.tagValueList.pairs[genericContext.currentElementIdx]); + pair = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]); } else { - pair = PIC( - p_content->content.tagValueList.callback(genericContext.currentElementIdx)); + pair = PIC(p_content->content.tagValueList.callback(nextElementIdx)); } if (pair->centeredInfo) { @@ -843,14 +865,31 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content, if (p_tagValueList != NULL) { p_tagValueList->nbPairs = nbElementsInPage; + p_tagValueList->token = p_content->content.tagValueList.token; if (p_content->content.tagValueList.pairs != NULL) { p_tagValueList->pairs = PIC(&p_content->content.tagValueList.pairs[nextElementIdx]); + // parse pairs to check if any contains an alias for value + for (uint8_t i = 0; i < nbElementsInPage; i++) { + if (p_tagValueList->pairs[i].aliasValue) { + p_tagValueList->token = VALUE_ALIAS_TOKEN; + break; + } + } } else { p_tagValueList->pairs = NULL; p_tagValueList->callback = p_content->content.tagValueList.callback; p_tagValueList->startIndex = nextElementIdx; + // parse pairs to check if any contains an alias for value + for (uint8_t i = 0; i < nbElementsInPage; i++) { + const nbgl_layoutTagValue_t *pair + = PIC(p_content->content.tagValueList.callback(nextElementIdx + i)); + if (pair->aliasValue) { + p_tagValueList->token = VALUE_ALIAS_TOKEN; + break; + } + } } p_tagValueList->smallCaseForValue = false; p_tagValueList->nbMaxLinesForValue = NB_MAX_LINES_IN_REVIEW; @@ -1080,6 +1119,45 @@ static void displayDetailsPage(uint8_t detailsPage, bool forceFullRefresh) } } +// function used to display the content of a full value, when touching an alias of a tag/value pair +static void displayFullValuePage(const nbgl_contentTagValue_t *pair) +{ + nbgl_layoutDescription_t layoutDescription = {.modal = true, + .withLeftBorder = true, + .onActionCallback = &modalLayoutTouchCallback, + .tapActionText = NULL}; + nbgl_layoutHeader_t headerDesc = {.type = HEADER_BACK_AND_TEXT, + .separationLine = false, + .backAndText.token = 0, + .backAndText.tuneId = TUNE_TAP_CASUAL, + .backAndText.text = PIC(pair->item)}; + const char *info; + genericContext.modalLayout = nbgl_layoutGet(&layoutDescription); + // add header with the tag part of the pair, to go back + nbgl_layoutAddHeader(genericContext.modalLayout, &headerDesc); + // add full value text + if (pair->extension->explanation == NULL) { + if (pair->extension->aliasType == ENS_ALIAS) { + info = "ENS names are resolved by Ledger backend."; + } + else if (pair->extension->aliasType == ADDRESS_BOOK_ALIAS) { + info = "This account label comes from your Address Book in Ledger Live."; + } + else { + info = ""; + } + } + else { + info = pair->extension->explanation; + } + nbgl_layoutAddTextContent( + genericContext.modalLayout, pair->value, pair->extension->fullValue, info); + + // draw & refresh + nbgl_layoutDraw(genericContext.modalLayout); + nbgl_refresh(); +} + #ifdef NBGL_QRCODE static void displayAddressQRCode(void) { @@ -1141,6 +1219,18 @@ static void addressLayoutTouchCallbackQR(int token, uint8_t index) } #endif // NBGL_QRCODE +// called when header is touched on modal page, to dismiss it +static void modalLayoutTouchCallback(int token, uint8_t index) +{ + UNUSED(token); + UNUSED(index); + + // dismiss modal + nbgl_layoutRelease(genericContext.modalLayout); + nbgl_screenRedraw(); + nbgl_refresh(); +} + // called when skip button is touched in footer, during forward only review static void displaySkipWarning(void) { @@ -1503,7 +1593,7 @@ static void bundleNavReviewStreamingChoice(bool confirm) { if (confirm) { // Display a spinner if it wasn't the finish step - if (localContentsList[0].type != INFO_LONG_PRESS) { + if (STARTING_CONTENT.type != INFO_LONG_PRESS) { nbgl_useCaseSpinner("Processing"); } bundleNavContext.reviewStreaming.choiceCallback(true); @@ -2446,15 +2536,16 @@ void nbgl_useCaseReview(nbgl_operationType_t operationType, memset(localContentsList, 0, 3 * sizeof(nbgl_content_t)); // First a centered info - localContentsList[0].type = CENTERED_INFO; + STARTING_CONTENT.type = CENTERED_INFO; prepareReviewFirstPage( - &localContentsList[0].content.centeredInfo, icon, reviewTitle, reviewSubTitle); + &STARTING_CONTENT.content.centeredInfo, icon, reviewTitle, reviewSubTitle); // Then the tag/value pairs localContentsList[1].type = TAG_VALUE_LIST; memcpy(&localContentsList[1].content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t)); + localContentsList[1].contentActionCallback = tagValueList->actionCallback; // Eventually the long press page localContentsList[2].type = INFO_LONG_PRESS; @@ -2508,9 +2599,9 @@ void nbgl_useCaseReviewLight(nbgl_operationType_t operationType, memset(localContentsList, 0, 3 * sizeof(nbgl_content_t)); // First a centered info - localContentsList[0].type = CENTERED_INFO; + STARTING_CONTENT.type = CENTERED_INFO; prepareReviewFirstPage( - &localContentsList[0].content.centeredInfo, icon, reviewTitle, reviewSubTitle); + &STARTING_CONTENT.content.centeredInfo, icon, reviewTitle, reviewSubTitle); // Then the tag/value pairs localContentsList[1].type = TAG_VALUE_LIST; @@ -2599,9 +2690,9 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, memset(localContentsList, 0, 1 * sizeof(nbgl_content_t)); // First a centered info - localContentsList[0].type = CENTERED_INFO; + STARTING_CONTENT.type = CENTERED_INFO; prepareReviewFirstPage( - &localContentsList[0].content.centeredInfo, icon, reviewTitle, reviewSubTitle); + &STARTING_CONTENT.content.centeredInfo, icon, reviewTitle, reviewSubTitle); // compute number of pages & fill navigation structure bundleNavContext.reviewStreaming.stepPageNb @@ -2653,10 +2744,9 @@ void nbgl_useCaseReviewStreamingContinueExt(const nbgl_contentTagValueList_t *ta memset(localContentsList, 0, 1 * sizeof(nbgl_content_t)); // Then the tag/value pairs - localContentsList[0].type = TAG_VALUE_LIST; - memcpy(&localContentsList[0].content.tagValueList, - tagValueList, - sizeof(nbgl_contentTagValueList_t)); + STARTING_CONTENT.type = TAG_VALUE_LIST; + memcpy( + &STARTING_CONTENT.content.tagValueList, tagValueList, sizeof(nbgl_contentTagValueList_t)); // compute number of pages & fill navigation structure bundleNavContext.reviewStreaming.stepPageNb @@ -2721,8 +2811,8 @@ void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, memset(localContentsList, 0, 1 * sizeof(nbgl_content_t)); // Eventually the long press page - localContentsList[0].type = INFO_LONG_PRESS; - prepareReviewLastPage(&localContentsList[0].content.infoLongPress, + STARTING_CONTENT.type = INFO_LONG_PRESS; + prepareReviewLastPage(&STARTING_CONTENT.content.infoLongPress, bundleNavContext.reviewStreaming.icon, finishTitle); @@ -2816,7 +2906,7 @@ void nbgl_useCaseAddressConfirmationExt(const char *addres genericContext.genericContents.nbContents = (tagValueList == NULL) ? 1 : 2; memset(localContentsList, 0, 2 * sizeof(nbgl_content_t)); prepareAddressConfirmationPages( - address, tagValueList, &localContentsList[0], &localContentsList[1]); + address, tagValueList, &STARTING_CONTENT, &localContentsList[1]); // fill navigation structure, common to all pages uint8_t nbPages = nbgl_useCaseGetNbPagesForGenericContents(&genericContext.genericContents, 0); @@ -2867,9 +2957,9 @@ void nbgl_useCaseAddressReview(const char *address, memset(localContentsList, 0, 3 * sizeof(nbgl_content_t)); // First a centered info - localContentsList[0].type = CENTERED_INFO; + STARTING_CONTENT.type = CENTERED_INFO; prepareReviewFirstPage( - &localContentsList[0].content.centeredInfo, icon, reviewTitle, reviewSubTitle); + &STARTING_CONTENT.content.centeredInfo, icon, reviewTitle, reviewSubTitle); // Then the address confirmation pages prepareAddressConfirmationPages(