Skip to content

Commit

Permalink
Add keypad usecase
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed Feb 2, 2024
1 parent ede8dac commit 270a8cb
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib_nbgl/include/nbgl_obj.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ extern "C" {
// for Keypad
#ifdef HAVE_SE_TOUCH
#define KEYPAD_KEY_HEIGHT 104
#define KEYPAD_MAX_DIGITS 12
#else // HAVE_SE_TOUCH
#define KEYPAD_WIDTH 114
#define KEYPAD_HEIGHT 18
Expand Down
18 changes: 18 additions & 0 deletions lib_nbgl/include/nbgl_use_case.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ typedef void (*nbgl_choiceCallback_t)(bool confirm);
*/
typedef void (*nbgl_actionCallback_t)(uint8_t page);

/**
* @brief prototype of pin validation callback function
* @param content pin value
* @param length pin length
*/
typedef void (*nbgl_pinValidCallback_t)(uint8_t *content, uint8_t page);

/**********************
* GLOBAL PROTOTYPES
**********************/
Expand Down Expand Up @@ -194,6 +201,17 @@ void nbgl_useCaseAddressConfirmation(const char *address, nbgl_choiceCallback_t
void nbgl_useCaseAddressConfirmationExt(const char *address,
nbgl_choiceCallback_t callback,
const nbgl_layoutTagValueList_t *tagValueList);
#ifdef NBGL_KEYPAD
void nbgl_useCaseKeypad(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);
void nbgl_useCaseKeypadRelease(void);
#endif
#else // HAVE_SE_TOUCH
void nbgl_useCaseHome(const char *appName,
const nbgl_icon_details_t *appIcon,
Expand Down
2 changes: 1 addition & 1 deletion lib_nbgl/src/nbgl_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -3481,7 +3481,7 @@ int nbgl_layoutAddHiddenDigits(nbgl_layout_t *layout, uint8_t nbDigits)
if (layout == NULL) {
return -1;
}
if (nbDigits > 12) {
if (nbDigits > KEYPAD_MAX_DIGITS) {
return -1;
}
if (nbDigits > 8) {
Expand Down
213 changes: 213 additions & 0 deletions lib_nbgl/src/nbgl_use_case.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ typedef struct AddressConfirmationContext_s {
const nbgl_layoutTagValueList_t *tagValueList;
} AddressConfirmationContext_t;

#ifdef NBGL_KEYPAD
typedef struct KeypadContext_s {
uint8_t pinEntry[KEYPAD_MAX_DIGITS];
uint8_t pinLen;
uint8_t pinMinDigits;
uint8_t pinMaxDigits;
uint32_t keypadIndex;
uint32_t hiddenDigitsIndex;
nbgl_layout_t *layoutCtx;
} KeypadContext_t;
#endif

/**********************
* STATIC VARIABLES
**********************/
Expand All @@ -77,6 +89,9 @@ static nbgl_navCallback_t onNav;
static nbgl_layoutTouchCallback_t onControls;
static nbgl_choiceCallback_t onChoice;
static nbgl_callback_t onModalConfirm;
#ifdef NBGL_KEYPAD
static nbgl_pinValidCallback_t onValidatePin;
#endif

// contexts for background and modal pages
static nbgl_page_t *pageContext;
Expand All @@ -96,6 +111,9 @@ static StaticReviewContext_t staticReviewContext;

static AddressConfirmationContext_t addressConfirmationContext;

#ifdef NBGL_KEYPAD
static KeypadContext_t keypadContext;
#endif
// buffer of bits to store all numbers of tag/value pairs per page in static review
// this number is from 1 to 4, so we can use 2 bits per page
// there are up to 256 pages, so a buffer of 256/4 bytes is enough
Expand Down Expand Up @@ -127,6 +145,9 @@ static void reset_callbacks(void)
onControls = NULL;
onChoice = NULL;
onModalConfirm = NULL;
#ifdef NBGL_KEYPAD
onValidatePin = NULL;
#endif
}

// function called when navigating (or exiting) modal details pages
Expand Down Expand Up @@ -1526,5 +1547,197 @@ void nbgl_useCaseSpinner(const char *text)
pageContext = nbgl_pageDrawSpinner(NULL, (const char *) text);
nbgl_refreshSpecial(FULL_COLOR_PARTIAL_REFRESH);
}

#ifdef NBGL_KEYPAD
/**
* @brief internal function to refresh the keypad and automatically show / hide:
* - backspace key if no digits are present
* - validation key if the min digit is reached
* - keypad if the max number of digit is reached
*
* @param add indicate if a digit has been added or removed
*/
static void nbgl_refreshKeyPad(bool add)
{
bool enableValidate, enableBackspace, enableDigits;
bool redrawKeypad = false;
nbgl_refresh_mode_t mode = BLACK_AND_WHITE_FAST_REFRESH;

enableDigits = (keypadContext.pinLen < keypadContext.pinMaxDigits);
enableValidate = (keypadContext.pinLen >= keypadContext.pinMinDigits);
enableBackspace = (keypadContext.pinLen > 0);
if (add) {
if ((keypadContext.pinLen == keypadContext.pinMinDigits)
|| // activate "validate" button on keypad
(keypadContext.pinLen == keypadContext.pinMaxDigits)
|| // deactivate "digits" on keypad
(keypadContext.pinLen == 1)) { // activate "backspace"
redrawKeypad = true;
}
}
else { // remove
if ((keypadContext.pinLen == 0) || // deactivate "backspace" button on keypad
(keypadContext.pinLen == (keypadContext.pinMinDigits - 1))
|| // deactivate "validate" button on keypad
(keypadContext.pinLen
== (keypadContext.pinMaxDigits - 1))) { // reactivate "digits" on keypad
redrawKeypad = true;
}
}
nbgl_layoutUpdateHiddenDigits(
keypadContext.layoutCtx, keypadContext.hiddenDigitsIndex, keypadContext.pinLen);
if (redrawKeypad) {
nbgl_layoutUpdateKeypad(keypadContext.layoutCtx,
keypadContext.keypadIndex,
enableValidate,
enableBackspace,
enableDigits);
}

if ((!add) && (keypadContext.pinLen == 0)) {
// Full refresh to fully clean the bullets when reaching 0 digits
mode = FULL_COLOR_REFRESH;
}
nbgl_refreshSpecialWithPostRefresh(mode, POST_REFRESH_FORCE_POWER_ON);
}

/**
* @brief keypad callback when a key is touched
* - backspace key
*
* @param touchedKey the key touched
*/
static void nbgl_keypadCallback(char touchedKey)
{
switch (touchedKey) {
case BACKSPACE_KEY:
if (keypadContext.pinLen > 0) {
keypadContext.pinLen--;
keypadContext.pinEntry[keypadContext.pinLen] = 0;
}
nbgl_refreshKeyPad(false);
break;

case VALIDATE_KEY:
// Gray out keyboard / buttons as a first user feedback
nbgl_layoutUpdateKeypad(
keypadContext.layoutCtx, keypadContext.keypadIndex, false, false, true);
nbgl_refreshSpecialWithPostRefresh(BLACK_AND_WHITE_FAST_REFRESH,
POST_REFRESH_FORCE_POWER_ON);

// Free layout
nbgl_layoutRelease(keypadContext.layoutCtx);
keypadContext.layoutCtx = NULL;
onValidatePin(keypadContext.pinEntry, keypadContext.pinLen);
break;

default:
if ((touchedKey >= 0x30) && (touchedKey < 0x40)) {
if (keypadContext.pinLen < keypadContext.pinMaxDigits) {
keypadContext.pinEntry[keypadContext.pinLen] = touchedKey;
keypadContext.pinLen++;
}
nbgl_refreshKeyPad(true);
}
break;
}
}

/**
* @brief draws a standard keypad modal page. The page contains
* - a navigation bar at the top
* - a title for the pin code
* - a hidden digit entry
* - the keypad at the bottom
*
* @note callbacks allow to control the behavior.
* backspace and validation button are shown/hidden automatically
*
* @param title string to set in pin code title
* @param minDigits pin minimum number of digits
* @param maxDigits maximum number of digits to be displayed
* @param backToken token used with actionCallback (0 if unused))
* @param shuffled if set to true, digits are shuffled in keypad
* @param tuneId if not @ref NBGL_NO_TUNE, a tune will be played when back button is pressed
* @param validatePinCallback function calledto validate the pin code
* @param onActionCallback callback called on any action on the layout
*/
void nbgl_useCaseKeypad(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)
{
nbgl_layoutDescription_t layoutDescription = {0};
nbgl_layoutCenteredInfo_t centeredInfo = {0};
int status = -1;

if ((minDigits > KEYPAD_MAX_DIGITS) || (maxDigits > KEYPAD_MAX_DIGITS)) {
return;
}

reset_callbacks();
// reset the keypad context
memset(&keypadContext, 0, sizeof(KeypadContext_t));

// get a layout
layoutDescription.onActionCallback = actionCallback;
layoutDescription.modal = true;
layoutDescription.withLeftBorder = false;
keypadContext.layoutCtx = nbgl_layoutGet(&layoutDescription);

// set navigation bar
nbgl_layoutAddProgressIndicator(
keypadContext.layoutCtx, 0, 0, (backToken != 0), backToken, tuneId);

// add text description
centeredInfo.text1 = title;
centeredInfo.style = LARGE_CASE_INFO;
centeredInfo.onTop = true;
nbgl_layoutAddCenteredInfo(keypadContext.layoutCtx, &centeredInfo);

// add keypad
status = nbgl_layoutAddKeypad(keypadContext.layoutCtx, nbgl_keypadCallback, shuffled);
if (status < 0) {
nbgl_layoutRelease(keypadContext.layoutCtx);
keypadContext.layoutCtx = NULL;
return;
}
keypadContext.keypadIndex = (unsigned int) status;

// add hidden digits
status = nbgl_layoutAddHiddenDigits(keypadContext.layoutCtx, maxDigits);
if (status < 0) {
nbgl_layoutRelease(keypadContext.layoutCtx);
keypadContext.layoutCtx = NULL;
return;
}
keypadContext.hiddenDigitsIndex = (unsigned int) status;

// validation pin callback
onValidatePin = validatePinCallback;
// pin code acceptable lengths
keypadContext.pinMinDigits = minDigits;
keypadContext.pinMaxDigits = maxDigits;

nbgl_layoutDraw(keypadContext.layoutCtx);
nbgl_refreshSpecialWithPostRefresh(FULL_COLOR_CLEAN_REFRESH, POST_REFRESH_FORCE_POWER_ON);
}

/**
* @brief keypad layout release
*/
void nbgl_useCaseKeypadRelease(void)
{
if (keypadContext.layoutCtx != NULL) {
nbgl_layoutRelease(keypadContext.layoutCtx);
memset(&keypadContext, 0, sizeof(KeypadContext_t));
}
}
#endif // NBGL_KEYPAD

#endif // HAVE_SE_TOUCH
#endif // NBGL_USE_CASE

0 comments on commit 270a8cb

Please sign in to comment.