PIN Complexity
HYPR SDK for Android includes new Personal Identification Number (PIN) rules and a new User Interface (UI).
SDK for Android
We use the same layout for enrolling, confirming, and authorizing. If you want just to change the UI and not the behavior, reimplement the .XML
file.
The app now verifies PIN complexity on the fly during input; if you override this class, you must verify PIN complexity in your own code.
If you change the layout, remember to retain the class types and ids.
User Interface (UI)
Enroll (Create PIN) | Confirm | Authorize |
---|---|---|
![]() | ![]() | ![]() |
A Rule Is Broken | Incorrect PIN on Confirmation | Incorrect PIN on Authorization |
---|---|---|
![]() | ![]() | ![]() |

All three fragments are based on one .XML
file. Fields are set in proper fragments to adjust or customize the view.
-
hypr_header_layout_button_cancel
: ImageView in thehypr_pin_layout_header
layout that calls the cancel action. Image source is@drawable/hypr_common_view_exit_selector drawables
:<item android:drawable="@drawable/hypr_common_view_exit_pressed" android:state_pressed="true" /><item android:drawable="@drawable/hypr_common_view_exit_default" />
-
hypr_title_logo
: ImageView with a logo image (in the SDK this is a placeholder). The image source is@drawable/hypr_logo_small_blue
-
hypr_pin_layout_title
: Title of the fragment:-
Enroll:
@string/hypr_enroll_pin_title
-
Confirm:
@string/hypr_confirm_pin_title
-
Authorize:
@string/hypr_authorize_pin_title
-
-
hypr_pin_layout_hint_second
: Hint about the repeating rule@string/hypr_enroll_pin_repeating_error
, which is in the Enroll fragment -
hypr_pin_layout_hint_first
: Hint about the sequential rule@string/hypr_enroll_pin_sequence_error
, which is in the Enroll fragment -
hypr_pin_layout_prompt
: Prompt for PIN input,@string/hypr_authorize_pin_hint
, which is in the Authorize fragment
Hints are instances of com.hypr.hyprandroidpinframework.ui.screens.pinentry.HintTextView
and its action when the rule(s) are violated: change of font color and addition of the X icon; the image source is @drawable/hypr_common_cross_red_small
-
hypr_pin_layout_input
: The input field should be an instance ofcom.hypr.hyprandroidpinframework.ui.screens.pinentry.PinTextEditor
;hypr_pin_layout_input
works withcom.hypr.hyprandroidpinframework.ui.screens.pinentry.SecretWatcher
and masks the input;hypr_pin_layout_input
will auto-submit upon typing the sixth digit unless a rule is broken -
hypr_pin_keyboard_separator
: The line to which the top of the keyboard is anchored
Fragment Layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/hypr_pin_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/hyprColorWhiteBackground"
tools:context="com.hypr.hyprandroidpinframework.ui.activities.HyprPinActivity"
tools:ignore="Overdraw">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/hypr_pin_header_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.12" />
<include
android:id="@+id/hypr_pin_layout_header"
layout="@layout/hypr_common_screen_header"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/hypr_pin_header_separator"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@id/hypr_pin_layout_input"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/hypr_pin_header_separator">
<TextView
android:id="@+id/hypr_pin_layout_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="TITLE"
android:textColor="@color/hyprColorHyprBlueDark"
android:textSize="@dimen/hypr_complex_pin_hint_text_size"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<com.hypr.hyprandroidpinframework.ui.screens.pinentry.HintTextView
android:id="@+id/hypr_pin_layout_hint_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="@dimen/hypr_complex_pin_rule_violation_img_padding"
android:text="@string/hypr_enroll_pin_repeating_error"
android:textColor="@color/hyprColorGrey"
android:textSize="@dimen/hypr_complex_pin_hint_text_size"
app:defaultPromptColor="@color/hyprColorGrey"
app:failPromptColor="@color/hyprColorRedFail"
app:failPromptLeftIcon="@drawable/hypr_common_cross_red_small"
tools:ignore="UseCompatTextViewDrawableXml" />
<com.hypr.hyprandroidpinframework.ui.screens.pinentry.HintTextView
android:id="@+id/hypr_pin_layout_hint_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/hypr_enroll_pin_sequence_error"
android:textColor="@color/hyprColorGrey"
android:textSize="@dimen/hypr_complex_pin_hint_text_size"
app:defaultPromptColor="@color/hyprColorGrey"
app:failPromptColor="@color/hyprColorRedFail"
app:failPromptLeftIcon="@drawable/hypr_common_cross_red_small" />
<com.hypr.hyprandroidpinframework.ui.screens.pinentry.HintTextView
android:id="@+id/hypr_pin_layout_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:labelFor="@+id/hypr_pin_layout_input"
android:text="@string/hypr_authorize_pin_hint"
android:textColor="@color/hyprColorGrey"
android:textSize="@dimen/hypr_complex_pin_hint_text_size"
app:defaultPromptColor="@color/hyprColorGrey"
app:failPromptColor="@color/hyprColorRedFail"
app:failPromptLeftIcon="@drawable/hypr_common_cross_red_small" />
</LinearLayout>
<com.hypr.hyprandroidpinframework.ui.screens.pinentry.PinTextEditor
android:id="@+id/hypr_pin_layout_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginStart="@dimen/hypr_complex_pin_input_center"
android:layout_marginEnd="@dimen/hypr_complex_pin_input_center"
android:backgroundTint="@color/hyprColorLogoBlue"
android:inputType="number"
android:letterSpacing="0.3"
android:maxLength="6"
android:text="******"
android:textColor="@color/hyprColorLogoBlue"
android:textSize="@dimen/hypr_complex_pin_input_text_size"
app:layout_constraintBottom_toBottomOf="@+id/hypr_pin_keyboard_separator"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/hypr_pin_header_separator"
android:textColorHint="@color/hyprColorTrans"
android:importantForAccessibility="yes"
android:accessibilityTraversalAfter="@+id/hypr_pin_layout_title"
tools:ignore="HardcodedText" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/hypr_pin_keyboard_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
<View
android:id="@+id/hypr_pin_view_bottom_anchor"
android:layout_width="match_parent"
android:layout_height="@dimen/hypr_common_keyboard_divider_bar_height"
app:layout_constraintBottom_toBottomOf="parent" />
<include layout="@layout/hypr_numeric_keyboard_view_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>
Rules
-
Default rules: PINs may not be left at the default value of 123456
-
Repeating rules: PINs may have no more than two consecutive numbers. Example: 11632 will pass (two repeating 1s), but 145558 will not (three repeating 5s).
-
Sequential rules: PINs must have no more than two sequential numbers, ascending or descending. Example: 124587 will work, but 123900 and 348765 will not.
-
Pattern rules: Repeating patterns in pairs and triplets will also be detected and rejected. Repeating a separate set of pairs will also fail. Example: 123123 or 121212 will both fail; and 112266 will fail.
-
Blocklist rules: PINs may not be on any blocklist (e.g., YubiKey prohibits 159753)
A message will warn the user if one or more of the rules is broken as they type.
Keyboard
The Android keyboard is implemented in com.hypr.hyprandroidcommon.uiadapter.ui.views.keyboard.HyprNumericKeyboardView
, and uses the hypr_numeric_keyboard_view_layout.xml
layout with labels from hypr_numeric_keyboard_view
.

You can change separate elements as follows:
-
Background color:
<color name="hyprNumericKeyboardBackground">#E9EAEE</color>
-
Default key background color:
<color name="hyprNumericKeyboardBackgroundKey">#@color/hyprColorWhite</color>
-
Backspace key background color:
<color name="hyprNumericBackgroundKeyBackspace">#FFCBCED5</color>
-
Ack key background:
<color name="hyprNumericKeyboardBackgroundKeyAck">#FF1A73E9</color>
-
Button shadow color:
<color name="hyprNumericKey_shadow">#09000000</color>
Shadow is defined in the hypr_numeric_key_shadow.xml
file.
Mapping for keyLabels
, which extends labels
, is hardcoded:
private val labels = mapOf(
"2" to "ABC",
"3" to "DEF",
"4" to "GHI",
"5" to "JKL",
"6" to "MNO",
"7" to "PQRS",
"8" to "TUV",
"9" to "WXYZ",
"0" to " + "
)
For other buttons we use images:
private val icons = mapOf(
-5 to R.drawable.hypr_common_backspace,
-6 to R.drawable.hypr_common_success_vector
)
Colors
-
@color/hyprPinInput
: The input field text color (hypr_pin_layout_input
) -
@color/hyprPinTitle
: The title color (hypr_pin_layout_title
) -
@color/hyprPinHint
: The default hint color -
@color/hyprPinHintViolation
: The color of a hint when it is related to the violated rule. The X icon displays in this color -
@color/hyprHeaderExitDefault
: The default color of the exit X (hypr_header_layout_button_cancel
) -
@color/hyprHeaderExitPressed
: The pressed color of exit X (hypr_header_layout_button_cancel
) -
@color/hyprComplexPinDialogLabel
: Error dialog label color
Drawables
-
@drawable/hypr_logo_header_placeholder
: A placeholder for the logo on the screen header, currently with transparent paths -
@drawable/hypr_common_view_exit_selector
: A source set in the screen header. It includes the following:-
@drawable/hypr_common_view_exit_default
: The default image selector -
@drawable/hypr_common_view_exit_pressed
: The image for the pressed state
-
-
@drawable/hypr_cross_red_small
: A small red X shown when the rule is violated -
@drawable/hypr_numeric_key_ack
: The keyboard success button background (without shadow) -
@drawable/hypr_numeric_key_backspace
-
@drawable/hypr_numeric_key_background
Strings
Error Dialog Strings
<string name="hypr_complex_pin_not_confirmed">PINs did not match. Try creating a PIN again.</string>
<string name="hypr_complex_pin_not_match_title">Incorrect PIN</string>
<string name="hypr_complex_pin_not_match">Please check your PIN and try again!</string>
-
@string/hypr_complex_pin_not_confirmed
: A message shown in the Confirm fragment when the PIN does not match what was previously set. -
@string/hypr_complex_pin_not_match_title
: The title on a dialog shown when the authorization PIN does not pass. -
@string/hypr_complex_pin_not_match
: A message shown when the authorization PIN does not pass.
Other Strings
<string name="hypr_enroll_pin_title">Create a 6 digit PIN</string>
<string name="hypr_confirm_pin_title">Confirm PIN</string>
<string name="hypr_authorize_pin_title">Pin Authentication</string>
<string name="hypr_authorize_pin_hint">Enter your 6 digit PIN</string>
<string name="hypr_enroll_pin_repeating_error">No more than 2 repeating numbers</string>
<string name="hypr_enroll_pin_sequence_error">No more than 2 consecutive numbers</string>
<string name="hypr_content_description_enroll_pin_input">Input for create your 6 digit PIN.</string>
<string name="hypr_content_description_confirm_pin_input">Input for confirm 6 digit PIN.</string>
<string name="hypr_content_description_auth_pin_input">Input for enter 6 digit authorization PIN.</string>
-
@string/hypr_enroll_pin_title
-
@string/hypr_confirm_pin_title
: The fragment title -
@string/hypr_authorize_pin_title
-
@string/hypr_authorize_pin_hint
: The Enroll prompt -
@string/hypr_enroll_pin_repeating_error
: The Enroll hint and error message when the repeating rule is violated (color changed to@color/hyprPinHintViolation
) -
@string/hypr_enroll_pin_sequence_error
: An Enroll hint and error message when the sequential rule is violated (color changed to@color/hyprPinHintViolation
)
If you want to override the rules, you must override the entire fragment.
-
@string/hypr_content_description_enroll_pin_input
-
@string/hypr_content_description_confirm_pin_input
: A content description set into the proper fragment for the accessibility feature -
@string/hypr_content_description_auth_pin_input
SDK for iOS
Add PIN Complexity customization to the iOS Reference app; bring custom fonts, colors, and images to the layout.
You must have the Mobile PIN Complexity feature enabled for this to work. If it is not enabled and you would like to have it turned on, contact HYPR Support.
Instructions
You must import HYPRPIN
to access the PIN Complexity feature.
Below the import, we created a struct
that encompasses some basic colors; edit these to fit your needs.
import HYPRPIN
struct CustomColors {
static let red = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
static let green = #colorLiteral(red: 0, green: 1, blue: 0, alpha: 1)
static let blue = #colorLiteral(red: 0.1382772923, green: 0.1466971338, blue: 0.6042590737, alpha: 1)
static let yellow = #colorLiteral(red: 1, green: 1, blue: 0, alpha: 1)
static let black = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
static let white = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
static let grey = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)
}
In the function application(didFinishLaunchingWithOptions)
, call the function we created ealier, customizePinComplexityScreen()
.
customizePinComplexityScreen()
Below is the function for implementation of the customizable options in our SDK; change the UI to customize the items listed.
func customizePinComplexityScreen() {
//Color of the button close button in the top left corner
HYPRPINAuthenticatorViewConfiguration.sharedInstance().closeButtonColorPCE = CustomColors.black
//Logo in the top center
HYPRPINAuthenticatorViewConfiguration.sharedInstance().customerLogoPCE = UIImage(named: "fakeCompanyImage.png")
//The top (largest) label on the first pin entry screen. (“Create a 6 digit PIN” as an example)
HYPRPINAuthenticatorViewConfiguration.sharedInstance().titleLabelRegistrationCreatePINTextPCE = "First PIN Entry Screen"
//The top (largest) label on the second PIN entry/confirmation screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().titleLabelRegistrationConfirmPINTextPCE = "Confirmation PIN Text"
//The top (largest) label on the authentication screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().titleLabelAuthenticationTextPCE = "Authentication Text"
//The top (largest) label on all screens
HYPRPINAuthenticatorViewConfiguration.sharedInstance().titleLabelColorPCE = CustomColors.black
HYPRPINAuthenticatorViewConfiguration.sharedInstance().titleLabelFontPCE = UIFont.systemFont(ofSize: 15)
//Labels after the title on the first pin entry screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().ruleLabelsTextsPCE = ["Please enter what you would like to say here", "Hello"]
//Same as the first label from the previous item, but for the authentication screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().subtitleLabelAuthenticationTextPCE = "Subtitle Text for Authentication Screen"
//Colors, font for the rule labels and authentication subtitle label from the items above
HYPRPINAuthenticatorViewConfiguration.sharedInstance().ruleLabelsNormalColorPCE = CustomColors.black
HYPRPINAuthenticatorViewConfiguration.sharedInstance().ruleLabelsHihglightedColorPCE = CustomColors.black
HYPRPINAuthenticatorViewConfiguration.sharedInstance().ruleLabelsFontPCE = UIFont.systemFont(ofSize: 15)
//Checkmark in the example, to the left of the rule label
if #available(iOS 13.0, *) {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().passedRuleImagePCE = UIImage(systemName: "checkmark")
} else {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().passedRuleImagePCE = UIImage(named: "checkmark")
}
//The left of the rule label
if #available(iOS 13.0, *) {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().failedRuleImagePCE = UIImage(systemName: "wrongwaysign")
} else {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().failedRuleImagePCE = UIImage(named: "wrongwaysign")
}
//Image covering the entered digits
if #available(iOS 13.0, *) {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().asteriskImagePCE = UIImage(systemName: "asterisk")
} else {
HYPRPINAuthenticatorViewConfiguration.sharedInstance().asteriskImagePCE = UIImage(named: "asterisk")
}
//Normal color for the textfield underscore line
HYPRPINAuthenticatorViewConfiguration.sharedInstance().textFieldUnderscoreNormalColorPCE = CustomColors.grey
//Highlighted color for the textfield underscore line
HYPRPINAuthenticatorViewConfiguration.sharedInstance().textFieldUnderscoreHighlightedColorPCE = CustomColors.grey
//Text for the submit button on the first PIN entry screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().submitButtonRegistrationCreatePINTextPCE = "Submit"
//Text for the submit button on the second pin entry/confirmation screen
HYPRPINAuthenticatorViewConfiguration.sharedInstance().submitButtonRegistrationConfirmPINTextPCE = "Submit"
//Submit button text color
HYPRPINAuthenticatorViewConfiguration.sharedInstance().submitButtonTextColorPCE = CustomColors.white
//Submit button background color
HYPRPINAuthenticatorViewConfiguration.sharedInstance().submitButtonBackgroundColorPCE = CustomColors.blue
//Submit button font
HYPRPINAuthenticatorViewConfiguration.sharedInstance().submitButtonFontPCE = UIFont.systemFont(ofSize: 15)
}