Skip to content

Commit

Permalink
Merge pull request #613 from LedgerHQ/xch/nbgl_use_case_sync
Browse files Browse the repository at this point in the history
lib_ux_sync: First version
  • Loading branch information
xchapron-ledger authored Apr 15, 2024
2 parents 2123272 + cbe3ba5 commit 732ab1e
Show file tree
Hide file tree
Showing 4 changed files with 377 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile.rules
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
ifeq ($(USE_NBGL),0)
SDK_SOURCE_PATH += lib_bagl lib_ux
else
SDK_SOURCE_PATH += lib_nbgl lib_ux_nbgl
SDK_SOURCE_PATH += lib_nbgl lib_ux_nbgl lib_ux_sync
endif

define uniq =
Expand Down
17 changes: 17 additions & 0 deletions lib_standard_app/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,20 @@ WEAK int io_send_response_buffers(const buffer_t *rdatalist, size_t count, uint1

return ret;
}

#ifdef STANDARD_APP_SYNC_RAPDU
WEAK bool io_recv_and_process_event(void)
{
int apdu_state = G_io_app.apdu_state;

os_io_seph_recv_and_process(0);

// If an APDU was received in previous os_io_seph_recv_and_process call and
// is waiting to be processed, return true
if (apdu_state == APDU_IDLE && G_io_app.apdu_state != APDU_IDLE) {
return true;
}

return false;
}
#endif
64 changes: 64 additions & 0 deletions lib_ux_sync/include/ux_sync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifdef HAVE_NBGL

#include "nbgl_use_case.h"

typedef enum {
UX_SYNC_RET_APPROVED,
UX_SYNC_RET_REJECTED,
UX_SYNC_RET_QUITTED,
UX_SYNC_RET_APDU_RECEIVED,
UX_SYNC_RET_ERROR
} ux_sync_ret_t;

ux_sync_ret_t ux_sync_homeAndSettings(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);

ux_sync_ret_t ux_sync_review(nbgl_operationType_t operationType,
const nbgl_layoutTagValueList_t *tagValueList,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle,
const char *finishTitle);

ux_sync_ret_t ux_sync_addressReview(const char *address,
const nbgl_layoutTagValueList_t *additionalTagValueList,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle);

ux_sync_ret_t ux_sync_reviewStatus(nbgl_reviewStatusType_t reviewStatusType);

ux_sync_ret_t ux_sync_status(const char *message, bool isSuccess);

ux_sync_ret_t ux_sync_reviewStreamingStart(nbgl_operationType_t operationType,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle);

ux_sync_ret_t ux_sync_reviewStreamingContinue(const nbgl_layoutTagValueList_t *tagValueList);

ux_sync_ret_t ux_sync_reviewStreamingFinish(const char *finishTitle);

ux_sync_ret_t ux_sync_genericReview(const nbgl_genericContents_t *contents, const char *rejectText);

ux_sync_ret_t ux_sync_genericConfiguration(const char *title,
uint8_t initPage,
const nbgl_genericContents_t *contents);

/*
* This function must be implemented by the caller.
* It must wait for the next seph event and process it except for APDU events.
* It must return:
* - true when an APDU has been received in the processed event
* - false otherwise
*
* Note on C apps using SDK lib_standard_app, this is already provided in io.c by the lib.
*/
extern bool io_recv_and_process_event(void);

#endif
295 changes: 295 additions & 0 deletions lib_ux_sync/src/ux_sync.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
#ifdef HAVE_NBGL

#include "ux_sync.h"

static ux_sync_ret_t g_ret;
static bool g_ended;

static void choice_callback(bool confirm)
{
if (confirm) {
g_ret = UX_SYNC_RET_APPROVED;
}
else {
g_ret = UX_SYNC_RET_REJECTED;
}

g_ended = true;
}

static void quit_callback(void)
{
g_ret = UX_SYNC_RET_QUITTED;
g_ended = true;
}

static void rejected_callback(void)
{
g_ret = UX_SYNC_RET_REJECTED;
g_ended = true;
}

static void ux_sync_init(void)
{
g_ended = false;
g_ret = UX_SYNC_RET_ERROR;
}

static ux_sync_ret_t ux_sync_wait(bool exitOnApdu)
{
bool apduReceived;

while (!g_ended) {
apduReceived = io_recv_and_process_event();
if (exitOnApdu && apduReceived) {
return UX_SYNC_RET_APDU_RECEIVED;
}
}

return g_ret;
}

/**
* @brief Draws the extended version of home page of an app (page on which we land when launching it
* from dashboard) with automatic support of setting display.
* @note it enables to use an action button (black on Stax, white on Flex)
*
* @param appName app name
* @param appIcon app icon
* @param tagline text under app name (if NULL, it will be "This app enables signing transactions on
* the <appName> network.")
* @param initSettingPage if not INIT_HOME_PAGE, start directly the corresponding setting page
* @param settingContents setting contents to be displayed
* @param infosList infos to be displayed (version, license, developer, ...)
* @param action if not NULL, info used for an action button (on top of "Quit
* App" button/footer)
*
* @return ret code:
* - UX_SYNC_RET_QUITTED
* - UX_SYNC_RET_APDU_RECEIVED
*/
ux_sync_ret_t ux_sync_homeAndSettings(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)
{
ux_sync_init();
nbgl_useCaseHomeAndSettings(appName,
appIcon,
tagline,
initSettingPage,
settingContents,
infosList,
action,
quit_callback);
return ux_sync_wait(true);
}

/**
* @brief Draws a flow of pages of a review. A back key is available on top-left of the screen,
* except in first page It is possible to go to next page thanks to "tap to continue".
* @note All tag/value pairs are provided in the API and the number of pages is automatically
* computed, the last page being a long press one
*
* @param operationType type of operation (Operation, Transaction, Message)
* @param tagValueList list of tag/value pairs
* @param icon icon used on first and last review page
* @param reviewTitle string used in the first review page
* @param reviewSubTitle string to set under reviewTitle (can be NULL)
* @param finishTitle string used in the last review page
*
* @return ret code:
* - UX_SYNC_RET_APPROVED
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_review(nbgl_operationType_t operationType,
const nbgl_layoutTagValueList_t *tagValueList,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle,
const char *finishTitle)
{
ux_sync_init();
nbgl_useCaseReview(operationType,
tagValueList,
icon,
reviewTitle,
reviewSubTitle,
finishTitle,
choice_callback);
return ux_sync_wait(false);
}

/**
* @brief Draws a flow of pages of an extended address verification page.
* A back key is available on top-left of the screen,
* except in first page It is possible to go to next page thanks to "tap to continue".
* @note All tag/value pairs are provided in the API and the number of pages is automatically
* computed, the last page being a long press one
*
* @param address address to confirm (NULL terminated string)
* @param additionalTagValueList list of tag/value pairs (can be NULL) (must fit in a single page,
* and be persistent because no copy)
* @param callback callback called when button or footer is touched (if true, button, if false
* footer)
* @param icon icon used on the first review page
* @param reviewTitle string used in the first review page
* @param reviewSubTitle string to set under reviewTitle (can be NULL)
*
* @return ret code:
* - UX_SYNC_RET_APPROVED
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_addressReview(const char *address,
const nbgl_layoutTagValueList_t *additionalTagValueList,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle)
{
ux_sync_init();
nbgl_useCaseAddressReview(
address, additionalTagValueList, icon, reviewTitle, reviewSubTitle, choice_callback);
return ux_sync_wait(false);
}

/**
* @brief Draws a transient (3s) status page for the reviewStatusType
*
* @param reviewStatusType type of status to display
*
* @return ret code:
* - UX_SYNC_RET_QUITTED
*/
ux_sync_ret_t ux_sync_reviewStatus(nbgl_reviewStatusType_t reviewStatusType)
{
ux_sync_init();
nbgl_useCaseReviewStatus(reviewStatusType, quit_callback);
return ux_sync_wait(false);
}

/**
* @brief Draws a transient (3s) status page, either of success or failure, with the given message
*
* @param message string to set in middle of page (Upper case for success)
* @param isSuccess if true, message is drawn in a Ledger style (with corners)
*
* @return ret code:
* - UX_SYNC_RET_QUITTED
*/
ux_sync_ret_t ux_sync_status(const char *message, bool isSuccess)
{
ux_sync_init();
nbgl_useCaseStatus(message, isSuccess, quit_callback);
return ux_sync_wait(false);
}

/**
* @brief Start drawing the flow of pages of a review.
* @note This should be followed by calls to nbgl_useCaseReviewStreamingContinue and finally to
* nbgl_useCaseReviewStreamingFinish.
*
* @param operationType type of operation (Operation, Transaction, Message)
* @param icon icon used on first and last review page
* @param reviewTitle string used in the first review page
* @param reviewSubTitle string to set under reviewTitle (can be NULL)
*
* @return ret code:
* - UX_SYNC_RET_APPROVED
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_reviewStreamingStart(nbgl_operationType_t operationType,
const nbgl_icon_details_t *icon,
const char *reviewTitle,
const char *reviewSubTitle)

{
ux_sync_init();
nbgl_useCaseReviewStreamingStart(
operationType, icon, reviewTitle, reviewSubTitle, choice_callback);
return ux_sync_wait(false);
}

/**
* @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
*
* @return ret code:
* - UX_SYNC_RET_APPROVED
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_reviewStreamingContinue(const nbgl_layoutTagValueList_t *tagValueList)

{
ux_sync_init();
nbgl_useCaseReviewStreamingContinue(tagValueList, choice_callback);
return ux_sync_wait(false);
}

/**
* @brief finish drawing the flow of pages of a review.
* @note This should be called after a call to nbgl_useCaseReviewStreamingContinue.
*
* @param finishTitle string used in the last review page
*
* @return ret code:
* - UX_SYNC_RET_APPROVED
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_reviewStreamingFinish(const char *finishTitle)

{
ux_sync_init();
nbgl_useCaseReviewStreamingFinish(finishTitle, choice_callback);
return ux_sync_wait(false);
}

/**
* @brief Draws a flow of pages of a review with automatic pagination depending on content
* to be displayed that is passed through contents.
*
* @param contents contents to be displayed
* @param rejectText text to use in footer
*
* @return ret code:
* - UX_SYNC_RET_REJECTED
*/
ux_sync_ret_t ux_sync_genericReview(const nbgl_genericContents_t *contents, const char *rejectText)

{
ux_sync_init();
nbgl_useCaseGenericReview(contents, rejectText, rejected_callback);
return ux_sync_wait(false);
}

/**
* @brief Draws a set of pages with automatic pagination depending on content
* to be displayed that is passed through contents.
*
* @param title string to use as title
* @param initPage page on which to start, can be != 0 if you want to display a specific page
* after a confirmation change or something. Then the value should be taken from the
* nbgl_contentActionCallback_t callback call.
* @param contents contents to be displayed
*
* @return ret code:
* - UX_SYNC_RET_QUITTED
* - UX_SYNC_RET_APDU_RECEIVED
*/
ux_sync_ret_t ux_sync_genericConfiguration(const char *title,
uint8_t initPage,
const nbgl_genericContents_t *contents)

{
ux_sync_init();
nbgl_useCaseGenericConfiguration(title, initPage, contents, quit_callback);
return ux_sync_wait(true);
}

#endif

0 comments on commit 732ab1e

Please sign in to comment.