From 3bef0ec62245cbee9772b2d62bc7e05bf76e3719 Mon Sep 17 00:00:00 2001 From: Nicolas Roggeman Date: Wed, 22 May 2024 10:05:33 +0200 Subject: [PATCH] Add possible skipping when using nbgl_useCaseReviewStreamingXXX() functions --- lib_nbgl/include/nbgl_use_case.h | 40 ++++++++++++++++++++++++------ lib_nbgl/src/nbgl_layout.c | 14 +---------- lib_nbgl/src/nbgl_page.c | 2 +- lib_nbgl/src/nbgl_use_case.c | 40 +++++++++++++++++++++++++----- lib_nbgl/src/nbgl_use_case_nanos.c | 16 +++++++++++- lib_ux_sync/src/ux_sync.c | 3 ++- 6 files changed, 85 insertions(+), 30 deletions(-) diff --git a/lib_nbgl/include/nbgl_use_case.h b/lib_nbgl/include/nbgl_use_case.h index 6751d9938..19d641ef5 100644 --- a/lib_nbgl/include/nbgl_use_case.h +++ b/lib_nbgl/include/nbgl_use_case.h @@ -145,9 +145,9 @@ typedef struct { } nbgl_genericContents_t; typedef struct { - const char *text; - const nbgl_icon_details_t *icon; - nbgl_callback_t callback; + const char *text; ///< text to use in action button in Home page + const nbgl_icon_details_t *icon; ///< icon to use in action button in Home page + nbgl_callback_t callback; ///< function to call when action button is touched in Home page } nbgl_homeAction_t; /** @@ -155,10 +155,33 @@ typedef struct { * */ typedef enum { - TYPE_TRANSACTION = 0, // For operations transferring a coin or taken from an account to another - TYPE_MESSAGE, // For operations signing a message that will not be broadcast on the blockchain - TYPE_OPERATION, // For other types of operation (generic type) -} nbgl_operationType_t; + TYPE_TRANSACTION + = 0, ///< For operations transferring a coin or taken from an account to another + TYPE_MESSAGE, ///< For operations signing a message that will not be broadcast on the + ///< blockchain + TYPE_OPERATION ///< For other types of operation (generic type) +} nbgl_opType_t; + +/** + * @brief This is to use in @ref nbgl_operationType_t when the operation is skippable. + * This is used + * + */ +#define SKIPPABLE_OPERATION (1 << 4) + +/** + * @brief This is to use in @ref nbgl_operationType_t when the operation is "blind" + * This is used to indicate a warning with a top-right button in review first & last page + * + */ +#define BLIND_OPERATION (1 << 5) + +/** + * @brief This mask is used to describe the type of operation to review with additional options + * It is a mask of @ref nbgl_opType_t [| @ref SKIPPABLE_OPERATION] [| @ref BLIND_OPERATION] + * + */ +typedef uint32_t nbgl_operationType_t; /** * @brief The different types of review status @@ -221,7 +244,8 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, nbgl_choiceCallback_t choiceCallback); void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, - nbgl_choiceCallback_t choiceCallback); + nbgl_choiceCallback_t choiceCallback, + nbgl_callback_t skipCallback); void nbgl_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback); diff --git a/lib_nbgl/src/nbgl_layout.c b/lib_nbgl/src/nbgl_layout.c index 235af5efc..76d9d92b6 100644 --- a/lib_nbgl/src/nbgl_layout.c +++ b/lib_nbgl/src/nbgl_layout.c @@ -2378,18 +2378,6 @@ int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *heade = (nbgl_obj_t *) textArea; layoutInt->headerContainer->nbChildren++; layoutInt->headerContainer->obj.area.height = textArea->obj.area.height; - - // create vertical line separating texts - separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer); - separationLine->lineColor = LIGHT_GRAY; - separationLine->obj.area.width = 1; - separationLine->obj.area.height = layoutInt->headerContainer->obj.area.height; - separationLine->direction = VERTICAL; - separationLine->thickness = 1; - separationLine->obj.alignment = MID_LEFT; - separationLine->obj.alignTo = (nbgl_obj_t *) textArea; - separationLine->obj.alignmentMarginX = -1; - layoutInt->headerContainer->obj.area.height = textArea->obj.area.height; break; } default: @@ -2550,7 +2538,7 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_ separationLine->thickness = 1; separationLine->obj.alignment = MID_LEFT; separationLine->obj.alignTo = (nbgl_obj_t *) textArea; - separationLine->obj.alignmentMarginY = -1; + separationLine->obj.alignmentMarginX = -1; break; } case FOOTER_TEXT_AND_NAV: { diff --git a/lib_nbgl/src/nbgl_page.c b/lib_nbgl/src/nbgl_page.c index 4db978475..7d4953a18 100644 --- a/lib_nbgl/src/nbgl_page.c +++ b/lib_nbgl/src/nbgl_page.c @@ -533,7 +533,7 @@ nbgl_page_t *nbgl_pageDrawGenericContentExt(nbgl_layoutTouchCallback_t onA if (nav->skipText != NULL) { nbgl_layoutHeader_t headerDesc = {.type = HEADER_RIGHT_TEXT, - .separationLine = true, + .separationLine = false, .rightText.text = nav->skipText, .rightText.token = nav->skipToken, .rightText.tuneId = nav->tuneId}; diff --git a/lib_nbgl/src/nbgl_use_case.c b/lib_nbgl/src/nbgl_use_case.c index 11281b897..518ea201b 100644 --- a/lib_nbgl/src/nbgl_use_case.c +++ b/lib_nbgl/src/nbgl_use_case.c @@ -128,6 +128,7 @@ typedef struct { typedef struct { nbgl_operationType_t operationType; nbgl_choiceCallback_t choiceCallback; + nbgl_callback_t skipCallback; const nbgl_icon_details_t *icon; uint8_t stepPageNb; } nbgl_reviewStreamingContext_t; @@ -393,6 +394,8 @@ static void prepareReviewLightLastPage(nbgl_contentInfoButton_t *infoButton, static const char *getRejectReviewText(nbgl_operationType_t operationType) { #ifdef TARGET_STAX + // clear skip and blind bits) + operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION); if (operationType == TYPE_TRANSACTION) { return "Reject transaction"; } @@ -427,7 +430,7 @@ static void pageModalCallback(int token, uint8_t index) else if (token == SKIP_TOKEN) { if (index == 0) { // display the last forward only review page, whatever it is - displayReviewPage(LAST_PAGE_FOR_REVIEW, true); + displayGenericContextPage(LAST_PAGE_FOR_REVIEW, true); } else { // display background, which should be the page where skip has been touched @@ -907,9 +910,17 @@ static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh) bool flag; const nbgl_content_t *p_content = NULL; - if ((navType == STREAMING_NAV) && (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb)) { - bundleNavReviewStreamingChoice(true); - return; + if (navType == STREAMING_NAV) { + if (pageIdx == LAST_PAGE_FOR_REVIEW) { + if (bundleNavContext.reviewStreaming.skipCallback != NULL) { + bundleNavContext.reviewStreaming.skipCallback(); + } + return; + } + else if (pageIdx >= bundleNavContext.reviewStreaming.stepPageNb) { + bundleNavReviewStreamingChoice(true); + return; + } } if (navInfo.activePage == pageIdx) { @@ -1441,7 +1452,8 @@ static void bundleNavReviewAskRejectionConfirmation(nbgl_operationType_t operati { const char *title; const char *confirmText; - + // clear skip and blind bits) + operationType &= ~(SKIPPABLE_OPERATION | BLIND_OPERATION); if (operationType == TYPE_TRANSACTION) { title = "Reject transaction?"; confirmText = "Go back to transaction"; @@ -2605,14 +2617,19 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, * @param tagValueList list of tag/value pairs * @param choiceCallback callback called when more operation data are needed (param is true) or * operation is rejected (param is false) + * @param skipCallback callback called when skip button is pressed (if operationType has the @ref + * SKIPPABLE_OPERATION in @ref nbgl_useCaseReviewStreamingStart) + * @ref nbgl_useCaseReviewStreamingFinish shall then be called. */ void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, - nbgl_choiceCallback_t choiceCallback) + nbgl_choiceCallback_t choiceCallback, + nbgl_callback_t skipCallback) { // Should follow a call to nbgl_useCaseReviewStreamingStart memset(&genericContext, 0, sizeof(genericContext)); bundleNavContext.reviewStreaming.choiceCallback = choiceCallback; + bundleNavContext.reviewStreaming.skipCallback = skipCallback; // memorize context onChoice = bundleNavReviewStreamingChoice; @@ -2635,6 +2652,17 @@ void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagVa prepareNavInfo(true, NBGL_NO_PROGRESS_INDICATOR, getRejectReviewText(bundleNavContext.reviewStreaming.operationType)); + // if the operation is skippable + if (bundleNavContext.reviewStreaming.operationType & SKIPPABLE_OPERATION) { +#ifdef TARGET_STAX + navInfo.skipText = "Skip >>"; + navInfo.navWithTap.quitText = "Reject"; +#else + navInfo.progressIndicator = false; + navInfo.skipText = "Skip"; +#endif + navInfo.skipToken = SKIP_TOKEN; + } displayGenericContextPage(0, true); } diff --git a/lib_nbgl/src/nbgl_use_case_nanos.c b/lib_nbgl/src/nbgl_use_case_nanos.c index e62d348cc..dc36449b2 100644 --- a/lib_nbgl/src/nbgl_use_case_nanos.c +++ b/lib_nbgl/src/nbgl_use_case_nanos.c @@ -972,9 +972,23 @@ void nbgl_useCaseReviewStreamingStart(nbgl_operationType_t operationType, displayStreamingReviewPage(FORWARD_DIRECTION); } +/** + * @brief Continue drawing the flow of pages of a review. + * @note This should be called after a call to nbgl_useCaseReviewStreamingStart and can be followed + * by others calls to nbgl_useCaseReviewStreamingContinue and finally to + * nbgl_useCaseReviewStreamingFinish. + * + * @param tagValueList list of tag/value pairs + * @param choiceCallback callback called when more operation data are needed (param is true) or + * operation is rejected (param is false) + * @param skipCallback callback unused on Nano. + */ void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, - nbgl_choiceCallback_t choiceCallback) + nbgl_choiceCallback_t choiceCallback, + nbgl_callback_t skipCallback) { + UNUSED(skipCallback); + memset(&context, 0, sizeof(UseCaseContext_t)); context.type = STREAMING_CONTINUE_REVIEW_USE_CASE; context.review.tagValueList = tagValueList; diff --git a/lib_ux_sync/src/ux_sync.c b/lib_ux_sync/src/ux_sync.c index ffc977e8f..99a91e01f 100644 --- a/lib_ux_sync/src/ux_sync.c +++ b/lib_ux_sync/src/ux_sync.c @@ -262,7 +262,8 @@ ux_sync_ret_t ux_sync_reviewStreamingContinue(const nbgl_contentTagValueList_t * { ux_sync_init(); - nbgl_useCaseReviewStreamingContinue(tagValueList, choice_callback); + // no skipping + nbgl_useCaseReviewStreamingContinue(tagValueList, choice_callback, NULL); return ux_sync_wait(false); }