Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib_ux_sync: First version #613

Merged
merged 1 commit into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
xchapron-ledger marked this conversation as resolved.
Show resolved Hide resolved
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
Loading