Embedded SDK
Embedded SDK
Predefined Application Use-cases API

Introduction

This chapter describes the Application Use-cases API of Advanced BOLOS Graphic Library.

This layer offers a simplified view of some typical use-cases of display in an Application running on Stax. For example, a use-case can be:

  • Reviewing a transaction/message
  • Reviewing details on a given data of a transaction/message
  • Displaying pages of settings

A full description of each predefined use-case can be found in this document

Concepts

This layer uses the high-level API described in Predefined pages API, but offers to developer more than a single page.

The goal is to simplify the usage of NBGL, but also to offer a better homogeneity across applications, by pushing developers to use common API for common use-cases.

So that not only the look of the pages but also their transitions look the same. Which should be a real help for end-users, getting more and more familiar with the user experience of applications.

Example 1: transaction review

In this example, a transaction review consists in 3 successive pages, and can be seen as a use-case

Example 2: home & settings pages

In this other example, displaying home page, then the settings and info consists in 3 pages, and can be seen as another use-case.

Use Cases

A few APIs are available to draw typical Use-Cases, such as:

Some APIs have also been kept for backward compatibility, and for some rare cases:

Home & Settings screen Use Case

Ledger would like all application to have the same layout for home screen and settings/info, so the nbgl_useCaseHomeAndSettings() function enables to create such a set of page, the configurable parameters being:

  • the application name (appName)
  • the application icon (appIcon)
  • the tagline, a text under app name (if NULL, it will be "This app enables signing transactions on the <appName> network.")
  • the callback when touching quit application button
extern const nbgl_icon_details_t *eth_icon;
enum {
SWITCH1_TOKEN = FIRST_USER_TOKEN,
SWITCH2_TOKEN
};
static const nbgl_layoutSwitch_t switches[] = {
{.initState = false,
.text = "ENS addresses",
.subText = "Displays the resolved address of ENS domains.",
.token = SWITCH1_TOKEN,
.tuneId = TUNE_TAP_CASUAL},
{.initState = true,
.text = "Raw messages",
.subText = "Displays raw content from EIP712 messages.",
.token = SWITCH2_TOKEN,
.tuneId = TUNE_TAP_CASUAL}
};
static const char *infoTypes[] = {"Version", "Developer"};
static const char *infoContents[] = {"1.9.18", "Ledger"};
// function called in case of action on switches
static void controlsCallback(int token, uint8_t index, int page) {
if (token == SWITCH1_TOKEN) {
if (index == 0) {
// deactivate something related with ENS
}
else {
// activate something related with ENS
}
}
else if (token == SWITCH2_TOKEN) {
if (index == 0) {
// deactivate something related with EIP712
}
else {
// activate something related with EIP712
}
}
}
static const nbgl_content_t contentsList = {
.content.switchesList.switches = switches,
.type = SWITCHES_LIST,
.contentActionCallback = controlsCallback
};
static const nbgl_genericContents_t eth_settingContents
= {.contentsList = &contentsList, .nbContents = 1};
static const nbgl_contentInfoList_t eth_infosList
= {.nbInfos = 2, .infoTypes = infoTypes, .infoContents = infoContents};
void onQuit(void) {
// exit app here
}
void appMain(void) {
eth_icon,
NULL, // use default tag line
INIT_HOME_PAGE, // start at home page
&eth_settingContents, // description of settings
&eth_infosList, // description of app info
NULL, // no action button on home screen
onQuit); // when quitting
}
@ SWITCHES_LIST
list of switches with descriptions
Definition: nbgl_content.h:337
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
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 INIT_HOME_PAGE
Value to pass to nbgl_useCaseHomeAndSettings() initSettingPage parameter to initialize the use case o...
Definition: nbgl_use_case.h:97
#define FIRST_USER_TOKEN
when using controls in page content (nbgl_pageContent_t), this is the first token value usable for th...
Definition: nbgl_use_case.h:32
This structure contains data to build a INFOS_LIST content.
Definition: nbgl_content.h:264
uint8_t nbInfos
number of elements in infoTypes and infoContents array
Definition: nbgl_content.h:267
This structure contains info to build a switch (on the right) with a description (on the left),...
Definition: nbgl_content.h:242
nbgl_state_t initState
initial state of the switch
Definition: nbgl_content.h:246
This structure contains data to build a content.
Definition: nbgl_content.h:363
nbgl_content_u content
Definition: nbgl_content.h:365
const nbgl_content_t * contentsList
array of nbgl_content_t (nbContents items).
uint8_t nbSwitches
number of elements in switches and tokens array
Definition: nbgl_content.h:258
nbgl_contentSwitchesList_t switchesList
SWITCHES_LIST type
Definition: nbgl_content.h:354
unsigned char uint8_t
Definition: usbd_conf.h:53

Home & Settings screen with action button Use Case

For some rare applications, one may need an action button in the Home screen, to perform either:

  • The main action of the Application
  • Or an side-action, as to display an address

The action argument of nbgl_useCaseHomeAndSettings() can be used for that. This structure (nbgl_homeAction_t) enables to specify:

  • A text & an icon for the button
  • A function to be called when the button is touched
  • The type of button (either STRONG_HOME_ACTION for main action, in black, or SOFT_HOME_ACTION for side action, in white)

Confirmation Use Case

A confirmation use-case consists in a single modal page containing a fixed icon, a configurable message, a black button and a footer to make the choice, with configuration texts. The nbgl_useCaseConfirm() function enables to create such a page.

The callback argument is called when the button is touched.

When the footer is touched, this modal screen is simply dismissed, revealing the previous page on background.

Here is the code to display the example picture (and a status page for confirmation)

static void confirmationCallback(void) {
// draw a status screen which continues by returning to appMain
nbgl_useCaseStatus("Transaction rejected",false,appMain);
}
void onRejectTransaction(void) {
nbgl_useCaseConfirm("Reject transaction?","Yes, Reject","Go back to transaction",confirmationCallback);
}
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)

Choice Use Case

A choice use-case consists in a single page containing a fixed icon, a configurable icon, a configurable message, a black button and a footer to make the choice, with configuration texts. The nbgl_useCaseChoice() function enables to create such a page.

The callback argument is called when the button or the footer is touched. Its argument is a boolean which is true when button is touched, false when footer is touched.

Here is the code to display the example picture

static void onChoice(bool confirm) {
if (confirm) {
// do something on button touch
}
else {
// do something on footer touch
}
}
void onRejectTransaction(void) {
nbgl_useCaseChoice(&C_key_64px,
"Delete all credentials?",
"This includes all saved\ncredentials from all websites\nand services.",
"Yes, erase","No, don't erase",
onChoice);
}
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)

Status Use Case

A status is a transient page, without control, to display during a short time, for example when a transaction is successfully signed. The nbgl_useCaseStatus() function enables to create such a page, with the following arguments:

  • a message string to set in middle of page
  • a boolean to indicate if true, that the message is drawn in a Ledger style (with corners)
  • a quit callback, called when quit timer times out (or the page is "tapped")

If it's a success status, a "success" tune will be automatically played.

Pre-defined status Use Case

Similar as Status Use Case, this is used to display transient page, without control, during a short time, for example when a transaction is successfully signed. The nbgl_useCaseReviewStatus() function enables to create such a page, with the following arguments:

  • a type of status (with predefined message)
  • a quit callback, called when quit timer times out (or the page is "tapped")

If it's a success status, a "success" tune will be automatically played.

Review Use Case

In most cases, the developer may know all tag/value pairs of a transaction when it is submitted.

In this case, what we call a "static" review can be used. The number of pages is computed automatically and pages can be navigated forward and backward.

In case of a tag/value pair too long to be fully displayed, the "more" button will be automatically drawn and its handling automatically performed by NBGL by building a detailed modal view.

When the user taps on "Reject" in any page, a confirmation page is automatically drawned to let user confirm that he rejects the transaction. In this case, the given callback is called and it's up to app's developer to call nbgl_useCaseReviewStatus(), as in case of long-press.

The API to initiate the display of the series of pages is nbgl_useCaseReview(), providing:

  • the type of operation to review (transaction, message or generic operation)
  • the list of tag/value pairs (or a callback to get them one by one)
  • the texts/icon to use in presentation page and in last page
  • a callback called when the long press button on last page or reject confirmation is used. The callback's param is true for confirmation, false for rejection.

Here is the code to display something similar to example picture:

// 4 pairs of tag/value to display
static nbgl_layoutTagValue_t pairs[4];
static const nbgl_contentTagValueList_t pairList = {
.nbPairs = 4,
.pairs = (nbgl_layoutTagValue_t*)pairs
};
// called when long press button on 3rd page is long-touched or when reject footer is touched
static void onReviewResult(bool confirm) {
// display a status page and go back to main
if (confirm) {
}
else {
}
}
void staticReview(void) {
// static review, providing the whole list of pairs
nbgl_useCaseReview(TYPE_TRANSACTION, // type of operation
&pairList, // list of tag/value pairs
coinIcon, // icon of the coin
"Review transaction\nto send coin", // title of the first page
NULL, // sub-title of the first page
"Sign transaction to\nsend coin?", // title of the last page
onReviewResult); // callback on result of the review
}
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_useCaseReviewStatus(nbgl_reviewStatusType_t reviewStatusType, nbgl_callback_t quitCallback)
@ STATUS_TYPE_TRANSACTION_REJECTED
@ STATUS_TYPE_TRANSACTION_SIGNED
@ TYPE_TRANSACTION
For operations transferring a coin or taken from an account to another.
This structure contains a list of [tag,value] pairs.
Definition: nbgl_content.h:181
This structure contains a [tag,value] pair.
Definition: nbgl_content.h:144

Here is another version of the example code, using a callback mechanism to get tag/value pairs:

// common tag/value pair to return
static nbgl_layoutTagValue_t* getPair(uint8_t index);
static const nbgl_contentTagValueList_t pairList = {
.nbPairs = 4,
.pairs = NULL, // to indicate that callback should be used
.callback = getPair,
.startIndex = 0
};
// called when long press button on 3rd page is long-touched or when reject footer is touched
static void onReviewResult(bool confirm) {
// display a status page and go back to main
if (confirm) {
}
else {
}
}
// function called by NBGL to get the pair indexed by "index"
static nbgl_layoutTagValue_t* getPair(uint8_t index) {
switch (index) {
case 0:
pair.item = "To";
pair.value = "0x123456";
break;
case 1:
pair.item = "From";
pair.value = "0x654321";
break;
case 2:
pair.item = "Amount";
pair.value = "1.2345 BTC";
break;
case 3:
pair.item = "Fees";
pair.value = "0.1 BTC";
break;
}
return &pair;
}
void staticReview(void) {
// static review, providing the whole list of pairs
nbgl_useCaseReview(TYPE_TRANSACTION, // type of operation
&pairList, // list of tag/value pairs
coinIcon, // icon of the coin
"Review transaction\nto send coin", // title of the first page
NULL, // sub-title of the first page
"Sign transaction to\nsend coin?", // title of the last page
onReviewResult); // callback on result of the review
}
const char * value
string giving the value name
Definition: nbgl_content.h:146
const char * item
string giving the tag name
Definition: nbgl_content.h:145

Light review Use Case

In some cases, the developer may want to display a review but with a less intense confirmation than a long-press button. A simple button is used in this case.

The API to initiate the display of the series of pages is nbgl_useCaseReviewLight(), providing:

  • the list of tag/value pairs (or a callback to get them one by one)
  • the texts/icon to use in presentation page and in last page
  • a callback called when the long press button on last page or reject confirmation is used. The callback's param is true for confirmation, false for rejection.

Streaming review Use Case

In some cases, the application cannot know all tag/value pairs of a transaction when the review is started.

In this case, what we call a "streaming" review can be used. The pages to display for each "stream are computed automatically and pages can be navigated forward and backward (only within a "stream" for backward).

In case of a tag/value pair too long to be fully displayed, the "more" button will be automatically drawn and its handling automatically performed by NBGL by building a detailed modal view.

When the user taps on "Reject" in any page, a confirmation page is automatically drawned to let user confirm that he rejects the transaction. In this case, the given callback is called and it's up to app's developer to call nbgl_useCaseReviewStatus(), as in case of long-press.

The API to initiate the display of the series of pages is nbgl_useCaseReviewStreamingStart(), providing:

  • the type of operation to review (transaction, message or generic operation)
  • the texts/icon to use in presentation page
  • a callback with one boolean parameter.

As long as there are new tag/value pairs to send, the API to call is either nbgl_useCaseReviewStreamingContinueExt() (if skip is possible) or nbgl_useCaseReviewStreamingContinue(), providing:

When there is no more data to senf, the API to call is either nbgl_useCaseReviewStreamingFinish(), providing:

  • the title to use for last page (with long-press button)
  • a callback called when the long press button on last page or reject confirmation is used. The callback's param is true for confirmation, false for rejection.

Here is the code to display something similar to example picture:

// called when long press button on last page is long-touched or when reject footer is touched
static void onReviewResult(bool confirm) {
// display a status page and go back to main
if (confirm) {
}
else {
}
}
static void onTransactionContinue(bool askMore)
{
if (askMore) {
// try to get more data
if (moreDataToSend(&pairsToSend)) {
nbgl_useCaseReviewStreamingContinue(&pairsToSend, onTransactionContinue);
}
else {
// all data sent, so finish
nbgl_useCaseReviewStreamingFinish("Sign transaction to transfer Cardano?", // title on last page
onReviewResult); // callback to handle reject/accept
}
}
else {
onReviewResult(false);
}
}
void staticReview(void) {
// initiate the streaming review
&C_ic_asset_cardano_64, // icon on first and last page
"Review transaction", // title of the first page
NULL, // sub-title of the first page
onTransactionContinue); // callback to reject or ask more data
}
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_useCaseReviewStreamingFinish(const char *finishTitle, nbgl_choiceCallback_t choiceCallback)
void nbgl_useCaseReviewStreamingContinue(const nbgl_contentTagValueList_t *tagValueList, nbgl_choiceCallback_t choiceCallback)

Address Review Use Case

When an address needs to be confirmed, it can be displayed in a Address Review Use Case. After a title page, a second page is displayed with the raw address (as text). An extra button under the raw address enables to open a modal page to see the address as a QR code. Moreover, if extra information need to be displayed, for example a derivation path, it is provided in a second page, also containing a black button/Footer pair to choose to confirm or reject the address.

The nbgl_useCaseAddressReview() function enables to create such a set of pages, with the following parameters:

  • the address to confirm (NULL terminated string)
  • a callback called when button or footer is touched (if true, confirm, if false reject)
  • the list of extra tag/value pairs

Here is the code to display something similar to example picture:

// 2 pairs of tag/value to display in second page
static nbgl_layoutTagValue_t pairs[2];
static const nbgl_contentTagValueList_t pairList = {
.nbPairs = 2,
.pairs = (nbgl_layoutTagValue_t*)pairs
};
// called when either confirm button or reject token is called
static void displayAddressCallback(bool confirm) {
if (confirm) {
nbgl_useCaseStatus("ADDRESS\nVERIFIED",true,app_fullEthereum);
}
else {
nbgl_useCaseStatus("Address rejected",false,app_fullEthereum);
}
}
void app_ethereumVerifyAddress(void) {
nbgl_useCaseAddressReview("bc1pkdcufjh6dxjaaa05hudvxqg5fhspfmwmp8g92gq8cv4gwwnmgrfqfd4jlg",
&pairList
myAppIcon,
"Verify MyCoin\naddress",
NULL,"Cancel",
appMain);
}
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)

Spinner Use Case

When an address needs to be confirmed, it can be displayed in a Address Confirmation Use Case, at first as simple page with the raw address (as text) and a black button/Footer pair to choose to confirm or reject the address.

An extra button under the raw address enables to open a modal page to see the address as a QR code.

The nbgl_useCaseSpinner() function enables to create such a page, with the following parameters:

Keypad Use Case

We have here 2 different variants, allowing to show or hide the entered digits.

When a pincode is requested, a default keypad can be displayed, with hidden digits. As shown on the image above, it consists of:

  • a navigation bar at the top
  • a title area, specifying the type of pin code or operation requested
  • a hidden Digits area (the max nb of supported digits is 12)
  • the keypad at the bottom

The nbgl_useCaseKeypadPIN() function enables to create such page, with the following parameters:

  • the title
  • min and max pin code lengths
  • a token for the navigation callback (if not provided, no navigation bar will appear)
  • a boolean to request a shuffled keypad
  • a tune value
  • callbacks for navigation and pin validation

The other variant, where digits don't need to be hidden is nbgl_useCaseKeypadDigits(); it takes the same parameters.

Note
The backspace and validate buttons will be shown or hidden automatically.

Here is the code to display something similar to example picture:

static void validate_pin(const uint8_t *pinentry, uint8_t length) {
// Code to validate the entered pin code
}
static void pinentry_cb(int token, uint8_t index) {
UNUSED(index);
// Callback for the key navigation (back key mainly)
if (token == TOKEN_PIN_ENTRY_BACK) {
ui_init();
}
}
void ui_menu_pinentry_display(unsigned int value) {
// Draw the keypad
nbgl_useCaseKeypadPIN("Enter User PIN",
6,
12,
TOKEN_PIN_ENTRY_BACK,
false,
TUNE_TAP_CASUAL,
validate_pin,
pinentry_cb);
}
void nbgl_useCaseKeypadPIN(const char *title, uint8_t minDigits, uint8_t maxDigits, uint8_t backToken, bool shuffled, tune_index_e tuneId, nbgl_pinValidCallback_t validatePinCallback, nbgl_layoutTouchCallback_t actionCallback)

Refreshing screen

After having drawn graphic objects in framebuffer, all functions of this API automatically refresh the screen. So no need to call nbgl_refresh().