Skip to main content

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.

Verified on the Fly

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)ConfirmAuthorize
A Rule Is BrokenIncorrect PIN on ConfirmationIncorrect 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 the hypr_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 of com.hypr.hyprandroidpinframework.ui.screens.pinentry.PinTextEditor; hypr_pin_layout_input works with com.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)

Reminder

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)
}