Beginner's Guide 

SampleApp is an app provided in the source code that already includes integration of Biometric Capture and Document Capture SDKs. The SDK integration developer can copy and paste the SampleApp code and adapt it for their context (such as personalizing the splash screen), allowing them to speed up implementation and to quickly arrive at a workable app with well-designed UX.

This guide provides information on how to obtain the application code and other artifacts needed to run it on your environment. It also explains the application code functionality step-by-step.

Note: This guide includes only the most useful methods for integrating the SDKs. For more detailed information about SDK integration, please see the Android SDK Integration Guide.

Step-by-Step Guide 

  1. In order to build samples, proper credentials need to be provided. Sample projects use gradle properties for loading credentials. The recommended way to store these properties is local gradle.properties file located in .gradle/ directory on machine. All values ​​should be accessible through the portal. Please find required properties below.
Properties
1#Artifactory
2artifactoryUserMI=artifactory_user
3artifactoryPasswordMI=artifactory_credentials
4repositoryUrlMI=https://mi-artifactory.otlabs.fr/artifactory/smartsdk-android-local
5
6#LKMS
7lkmsProfileId="profile_id"
8lkmsApiKey="lkms_api_key"
9
10#Backend API KEYS
11WbsApiKey="bio_server_api_key"
12gipsApiKey="gips_api_key"

More about gradle properties can be found here.

  1. Before using the Biometric functionality, you need to activate the license. For a detailed description of this step, please go to License Activation. Keep in mind that license is associated with application id. In order to properly run samples there is need to change application id of sample to your id that has been put within LKMS profile. Application id can be changed in build.gradle file that is placed in root directory of application module.
Application Id
  1. When the license is active, you can guide the user through the registration/authentication process described in the User Management section.

  2. Review the specific application by studying the FaceSampleApp or DocumentSampleApp modules.

  3. When you run the face scanning application ( menu Run → Run 'FaceSampleApp'), you will be presented with the following screen:

SampleAppIntGuide FaceScanning
  1. When you run the document scanning application (menu Run → Run 'DocumentSampleApp'), you will be presented with the following screen:
SampleAppIntGuide DocumentSampleApp

Document Sample Application 

This is the top-tier application which uses both the License Manager and UserManager components which depend on Biometric Capture SDK.

The Document Sample Application provides functionality for document scanning, and can easily be used as a template for your own solution.

The following describes each application screen and how it was implemented. There is an assumption that a reader has sufficient technical knowledge about Android development to understand code samples.

Content 

  1. SampleAppIntGuide splash screen
  2. SampleAppIntGuide Tutorial scanPassport

Document Sample App Settings 

SampleAppIntGuide Doc sample app settings

To access the application's settings, click on the round button with the wrench icon located in the bottom right corner of the screen.

This button is accessible on each screen except the scanning page.

Settings options

SampleAppIntGuide Doc settings

There are four options that an end-user can change:

  • Document overlay - decides whether the display overlay with the document boundaries is to be scanned

  • Static overlay - decides which display colored overlay will be placed within the document overlay

  • Time in seconds before the capture timeout - chooses time in seconds for which capture will stand before timeout error

  • Skip tutorial - decides whether a tutorial will be shown before each scanning operation

Document Sample App Splash Screen 

Splash Screen

SampleAppIntGuide splash screen

This screen is displayed when the application is loaded. It can be easily customized it with brand colors.

All graphical settings can be found in the layout file: fragment_splash.xml.

Note: This is also a convenient place to put any initial checks and settings like requesting permissions or activating a license.

The sample app logic behind this screen takes care of requesting the license and requesting the proper permissions. If those operations are successful then it forwards the end-user to the tutorial page.

The flow is illustrated below:

SampleAppIntGuide sample app logic

SampleApp uses the MVP approach which simplifies code testing by decoupling decision logic from the UI view.

In the splash screen fragment, the presenter is responsible for requesting permissions and fetching a license. It is triggered from lifecycle methods of the SlashFragment.

Java
1//Fragment
2override fun onStart() { //triggered by Android System
3 super.onStart()
4 scopeIO.launch {
5 presenter.onStart()
6 }
7}
8
9//Presenter
10suspend fun onStart() { //triggered by Fragment
11 if (permission.permissionsGranted()) {
12 fetchLicence()
13 } else {
14 permission.requestPermissions()
15 }
16}

For now, ignore the scopeIO.launch instruction. It's part of the official Kotlin framework called Coroutines. It was designed to simplify the execution of concurrent operations.

You can learn more about it on the official support page.

When the onStart method is called, and proper permissions are granted, the license activation is started. The license fetching procedure is described in License Activation.

Requesting Permissions 

To request permissions, a component com.idemia.smartsdk.sample.common.permission.DocumentAppPermissionProvider is used. If the end-user's Android system is below M-version then all permissions will be required statically, according to the declaration in the manifest file.

XML
1<uses-permission android:name="android.permission.INTERNET" />
2<uses-permission android:name="android.permission.CAMERA"/>

From M-version and above, Android supports dynamic permissions requests:

Java
1fragment.requestPermissions(PERMISSIONS_TO_REQUEST, PERMISSION_REQUEST_CODE)

DocumentAppPermissionProvider expects two functions during initialization. One, which will be called when the request for permission has to be triggered, and the second one, for checking if proper permissions were granted.

You can check usage of this component in SplashPresenter.

The first presenter checks if proper permissions are granted and if not, then the request procedure is triggered.

Java
1suspend fun onStart() {
2 if (permission.permissionsGranted()) {
3 fetchLicence()
4 } else {
5 permission.requestPermissions()
6 }
7 }

Then we try to repeat an operation but, if permissions are not yet given, then the application can not proceed and exits.

Java
1suspend fun onRequestPermissions() {
2 if (permission.permissionsGranted()) {
3 fetchLicence()
4 } else {
5 view.exitApp()
6 }
7 }

Constructions of the two mentioned functions can be found in the companion object of the class.

Java
1companion object {
2 private const val PERMISSION_REQUEST_CODE = 11
3 private val PERMISSIONS_TO_REQUEST: Array<String> = arrayOf(Manifest.permission.CAMERA)
4
5 operator fun invoke(f:Fragment):DocumentAppPermissionProvider = DocumentAppPermissionProvider(
6 @RequiresApi(Build.VERSION_CODES.M) { (f.activity as
7Activity).checkSelfPermission(it) },
8 { f.requestPermissions(PERMISSIONS_TO_REQUEST, PERMISSION_REQUEST_CODE) }
9 )
10 }

Some samples are also in the DocumentAppPermissionProviderTest unit test.

Document Sample App Tutorial 

Tutorial

These are a collection of screens that present the tutorial for scanning specific document types.

Tutorial page for an ID

SampleAppIntGuide Tutorial scanID

Tutorial page for a passport

SampleAppIntGuide Tutorial scanPassport

Tutorial page for a driver's license

SampleAppIntGuide Tutorial scanDL

These are simple animations that show the end-user how to properly scan a chosen document or barcode.

The application's architecture is MVP with a single activity where each view is presented by a separate fragment.

This approach simplifies inter-screen navigation which is composed with Navigation Component.

The core code for navigation is placed under the navigation package.

A representative navigation graph can be found in the file navigation_graph.xml while in design mode.

SampleAppIntGuide navigation graph

Each fragment that has any buttons that initialize a navigation are handled in the same way. Fragments have internal classes that map button IDs to the proper navigation action.

Tutorial Configuration

There are two cases when a tutorial is not displayed and an end-user is navigated directly to the scanner page.

  • In the first case, the tutorial page is skipped when the given document type was not provided with a tutorial animation file.

    Files with tutorial animations are stored in resources under the raw package.

  • An end-user may also manually disable a tutorial in the settings page. Whole tutorial content (i.e., title or animation file) is stored inside the TutorialContent class.

As it is shown in code below, the first mentioned case is the check on the menu page just after the end-user has chosen one of the document options:

Code Block 1 TutorialNavigationFlow.kt

Java
1// Returns true if given document has tutorial animation file
2private fun hasTutorialAnimation(documentType: DocumentType): Boolean = getTutorialAnimationFileId(documentType) != TutorialContentProvider.DOCUMENT_OPTION_WITHOUT_TUTORIAL
3
4// Each document has given animation file id as an argument with default
5value equal 0
6private fun getTutorialAnimationFileId(documentType: DocumentType): Int = TutorialContentProvider.tutorialContent(documentType).animationFileId

Face Sample Application 

This is the top-tier application which uses both License Manager and UserManager components which depends on Biometric Capture SDK.

Face Sample Application can easily be used as a template for an integrator's own biometric application.

The following describes each application screen and explains how it was implemented. It is assumed that the reader has sufficient technical knowledge about Android development to understand code samples.

Face Sample App Liveness Challenge 

This functionality can be entered directly from the splash screen or after watching the tutorial.

SampleAppIntGuide liveness challenge

CR2D 

This challenge mode requires multiple dots to connect on the screen.

SampleAppIntGuide liveness dots

The logic responsible for this challenge mode can be found in the class: CR2DChallengeFragment which delegates operations to CR2DChallengePresenter.

Java
1builder.captureMode(BioCaptureMode.TRACK_FACE_CR2D_MEDIUM)
2 .timeout(appSettings.timeout.toLong())
3 .cr2dConfiguration(Cr2dConfigurationPath(CR2D_POINTS))
4 .onCurrentUpdated { sceneController.update(it) }
5 .onTargetUpdated { sceneController.update(it) }
6 .onFailure(::onCaptureFailure)
7 .onSuccess(::onCaptureSuccess)
8 .onTracking(BioCaptureTrackingListener { sceneController.onTracking(it) })
9 .onFeedback(BioCaptureFeedbackListener(::onFeedbackReceived))
10 .onTargetsConditionUpdated { targetCount, targetStability ->
11 challengeView.hideFaceOutline()
12 sceneController.update(targetCount, targetStability)
13 }
14
15if (appSettings.useIllumination) {
16 builder.illumination(this\@CR2DChallengePresenter)
17}
18cR2DPreview = builder.build()

Layout 

Layout is defined in the file fragment_challenge_cr2d.xml.

XML
1<!--Custom component provided by UI-EXTENSIONS library. Is responsible for capturing biometric data -->
2 <com.idemia.biometricsdkuiextensions.ui.scene.scene.SceneView
3 ...
4 />
5
6 <ImageView
7 android:id="@+id/faceOverlay"
8 .../>
9
10 <TextView
11 android:id="@+id/faceOverlayText"
12 ...
13 />
14
15<!--General text field which will be used to display information **for**
16 user-->
17 <TextView
18 android:id="@+id/captureFeedback"
19 ...
20 />

Preparation 

Before performing the challenge, initialize the capture screen. In the sample app it's the responsibility of the CR2DCaptureProcess.ProcessBuilder component which describes the meaning of each parameter.

Java
1builder.captureMode(BioCaptureMode.TRACK_FACE_CR2D_MEDIUM)
2 .timeout(appSettings.timeout.toLong())
3 .cr2dConfiguration(Cr2dConfigurationPath(CR2D_POINTS))
4 .onCurrentUpdated { sceneController.update(it) }
5 .onTargetUpdated { sceneController.update(it) }
6 .onFailure(::onCaptureFailure)
7 .onSuccess(::onCaptureSuccess)
8 .onTracking(BioCaptureTrackingListener { sceneController.onTracking(it) })
9 .onFeedback(BioCaptureFeedbackListener(::onFeedbackReceived))
10 .onTargetsConditionUpdated { targetCount, targetStability ->
11 challengeView.hideFaceOutline()
12 sceneController.update(targetCount, targetStability)
13 }
14
15 if (appSettings.useIllumination) {
16 builder.illumination(this@CR2DChallengePresenter)
17 }
18 cR2DPreview = builder.build()

Challenge Resource Management 

During the challenge procedure, multiple resources are used. Warning: Be very careful to remember to release each of them.

As in the rest of the sample application, this functionality is implemented with the view/presenter approach and the presenter is of the type CR2DChallengePresenter. It's a great example to understand the proper order of instructions in the activity which uses the presenter.

Here is the order of all the operations which have to be performed:

  1. Presenter: prepareFaceCapture

    • builds the CR2DPreview object
  2. Presenter: startFaceCapture

    • start CR2DPreview
    • draws face outline
    • starts CR2DCapture
    • starts SceneController
  3. Presenter: onPause

    • stops SceneController
    • stops CR2DPreview
    • stops CR2DCapture
  4. Presenter: release

    • destroys SceneController
    • destroys CR2DCapture

Prepare Face Capture in Details 

Presenter uses JoinThePointsSceneController from UIExtensions. Configure callbacks to this controller through CR2DCaptureProcess.ProcessBuilder.

Property modifying SceneController
Description
onCurrentUpdated { sceneController.update(it) }
onTargetUpdated { sceneController.update(it) }
.onTargetsConditionUpdated { targetCount, targetStability -> challengeView.hideFaceOutline() sceneController.update(targetCount, targetStability) }d
General Property
Description
captureMode(BioCaptureMode.TRACK_FACE_CR2D_MEDIUM)You can choose from among three different: - TRACK_FACE_CR2D_*, TRACK_FACE_LIVENESS_* and INGERPRINT_*

Face Sample App Settings 

The exact flow of the tutorial screens depends on the settings you can choose. To enter the settings screen choose menu (the three little dots) in the right top corner of the screen.

SampleAppIntGuide face sample app

Choose which challenge method to use (CR2D - for joining points on the screen and SLAM for face scan).

SampleAppIntGuide face sample settings

Settings are backed by SettingsFragment.kt and SettingsPresenter.kt. The last one saves the chosen values by using SettingStorage.

Java
1interface SettingsStorage {
2 fun load(): FaceAppSettings
3 fun save(settings: FaceAppSettings)
4}

Face Sample App Splash Screen 

SampleAppIntGuide splash screen

This screen is displayed when the application is loaded and it can easily be customized with brand colors.

All graphical settings can be found in the layout file: fragment_splash.xml.

Note: It's also a convenient place to put any initial checks and settings like: requesting permissions or activating a license.

The sample app presenter logic behind this screen takes care of requesting a license and requesting proper permissions. If those operations are successful, then it forwards the end-user to the tutorial page.

The flow is illustrated in the following picture:

SampleAppIntGuide presenter logic

Splash Screen Presenter 

The Sample App uses the MVP approach which simplifies code testing by decoupling decision logic from the UI view.

For example, when the onViewCreated method in a fragment is called by the framework, then the fragment will inform the presenter that it is started, which triggers the license activation procedure which is described in License Activation.

Java
1//fragment
2override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
3 super.onViewCreated(view, savedInstanceState)
4 presenter = providePresenter()
5 presenter.onCreate()
6 }
7
8
9//presenter
10 override fun onCreate() {
11 Log.d(TAG, "onStart")
12 if (permissionProvider.permissionsGranted()) {
13 startFetchLicense()
14 } else {
15 requestPermissions()
16 }
17}

The main advantage of this approach is that presenter is completely decoupled from the Android framework, which simplifies testing.

Requesting Permissions 

To request permission, a component com.idemia.smartsdk.sample.common.permission.AppPermissionProvider from the Commons module is used.

If the Android system version is below M then all permissions will be required statically according to the declaration in the manifest file.

XML
1<uses-permission android:name="android.permission.INTERNET" /\>
2<uses-permission android:name="android.permission.CAMERA"/\>

From M-version and above, Android supports dynamic permissions requests.

Java
1fragment.requestPermissions(PERMISSIONS_TO_REQUEST, PERMISSION_REQUEST_CODE)

AppPermissionProvider has three dependencies but all three have default implementations provided by a companion object. The purpose of this construction is to make testing of this possible by injecting mock dependencies during tests.

Java
1class AppPermissionProvider(
2 @RequiresApi(Build.VERSION_CODES.M) private val checkPermission: PermissionChecker,
3 private val triggerPermissionRequest: PermissionRequester,
4 private val versionInfoProvider: VersionInfoProvider = StaticVersionInfo()
5) : PermissionProvider {
6
7//In companion object
8operator fun invoke(f: Fragment): AppPermissionProvider =
9 AppPermissionProvider(
10 @RequiresApi(Build.VERSION_CODES.M) {
11 (f.activity as Activity).checkSelfPermission( //permission checker
12
13 it
14 )
15 },
16 { f.requestPermissions(PERMISSIONS_TO_REQUEST, PERMISSION_REQUEST_CODE) } //permission requester
17 )

When permissions are provided, the license activation is launched.

Fetching License with Coroutines 

The component responsible for triggering and fetching the license procedure is SplashPresenter, which is defined in the Commons module. It uses navigator abstraction, which is defined separately by each specific application.

In the SplashPresenter is a method which uses the License Activation Module. Because one of the scenario's application has to connect with the remote server, this operation needs to start in a separate thread to not freeze the UI. This problem was solved with the usage of Coroutines framework.

In the sample below, first trigger licenseService in IO context which provides a thread for input/output operation. Afterwards, switch back to UI context.

Java
1fun fetchLicence() {
2 licenseJob = scopeIO.launch {
3 try {
4 licenseService.prepareLicence()
5 navigator.navigateTo()
6 } catch (exception: LkmsNetworkException) {
7 handleException(exception)
8 }
9 }
10}

When the license is restored and all necessary permissions granted, the end-user will be presenter with a series of tutorials.

Tutorial 

The following is a collection of simple screens which explain the application's functionality:

Screen 1

SampleAppIntGuide UserHelp step1

Screen 2

SampleAppIntGuide UserHelp centerHead

Screen 3

SampleAppIntGuide UserHelp light

Tutorial

SampleAppIntGuide UserHelp dotTrack

Navigation between those screens is backed by a set of fragments placed in the com.idemia.smartsdk.sample.tutorial.fragments package. Transitions between the tutorial sub pages are implemented with usage of the official Navigation Component from Google.

For a nice graphical overview, open the navigation_cr2d_tutorial.xml file or navigation_slam_tutorial.xml while in design mode.

Note: The picture below is used just for presentation purposes. Current navigation in the app can be different.

SampleAppIntGuide navigation slam

Tutorial Configuration 

Tutorial navigation is controlled by settings which are described in in the next section. Seeing the tutorial is optional.

Optionally, challenge mode can be chosen, which will be used in registration and authentication.

Having those choices as an input, it's delegated to TutorialPresenter to handle navigation properly.

Java
1//This part handles tutorial settings choice
2override fun configureTutorial() {
3 if (!appSettings.showTutorial) {
4 tutorialView.setStartingDestination(R.id.settingsActivity)
5 } else {
6 chooseChallengeMode()
7 }
8}
9
10//if you checked tutorial field in settings then the second condition
11handles challenge mode.
12
13private fun chooseChallengeMode() {
14 when (appSettings.mode) {
15 ChallengeMode.CR2D -> {
16 tutorialView.setStartingDestination(
17 R.id.firstStepFragment,
18 bundleOf(
19 TutorialActivity.USE_ILLUMINATION to appSettings.useIllumination
20 )
21 )
22 }
23 ChallengeMode.SLAM ->
24 tutorialView.setStartingDestination(R.id.SLAMStepFragment)
25 }
26}

In the activity, notice the use of the standard navigation API.

Java
1override fun setStartingDestination(destinationResId: Int, arguments: Bundle) {
2 val graph = navController.navInflater.inflate(R.navigation.navigation_tutorial)
3 graph.startDestination = destinationResId
4 navController.setGraph(graph, arguments)
5}

It's possible to use a different navigational approach within an integrator's own application.

License Activation 

To use Biometric Capture SDK, provide the following pre-configuration parameters:

LicenseServerUrl, ApiKey and ProfileID.

Values of those parameters will be determined during external business processes. Please contact your Business Unit Manager for more information.

Pass the aforementioned parameters to the LicenseManager module which is the main component responsible for retrieving a license.

  • In FaceSampleApp notice the usage of this module in SplashScreenPresenter where the license service is used during the onCreate phase.

  • In DocumentSampleApp you can find notice the usage of this module in the SplashScreenPresenter where the license service is used during the onStart phase.

The module entry point is the LkmsLicenseService class which tries to restore the license. In the case this step fails, then it tries to recover by fetching a new license.

LkmsLicenseService has three dependencies which you need to use in order to configure the fetching license procedure:

Java
1private val licenseManager: LkmsLicenseManagerWrapper,
2private val licenseProvider: LicenseProvider,
3private val licenseStore: LicenseStore

LicenseManagerWrapper Configuration 

This component has two initial parameters:

  • Android Context can be obtained directly from the Android framework

  • License Manager can be obtained by LkmsLicenseManagerProvider which just acts as a proxy for a call using the static method in the Biometric Capture SDK

LicenseProvider Configuration 

This component has three initial parameters:

  • LicenseManagerWrapper - configured in the previous point

  • Timeout - it is just an int which represents the max number of seconds the network connection to the license server should be open.

To provide the three configuration parameters, you will use the following data class which gathers all parameters into one structure:

Java
1data class LkmsProfileData (val apiKey: String,
2 val profileId: String,
3 val lkmsServerUrl: String)

When you pass a specified instance of this class to the LkmsLicenseProvider constructor, then it will be passed further to the LkmsLicenseManagerWrapper, which uses the SDK LicenseManager directly.

SampleAppIntGuide LkmsLicenseProvider

LicenseStore Configuration 

This component depends only on LicenseManagerWrapper, which was configured in the LicenseManagerWrapper Configuration section.

LicenseService Usage 

When all dependencies are provided, you can use an implementation of LicenseService, which is called LkmsLicenseService.

Java
1override suspend fun prepareLicence() {
2
3 runBlocking {
4 licenseStore.restoreLicence().onFailure {
5 licenseProvider.fetchLicence().onFailure {
6 throw it
7 }
8 }
9
10 activateLicence()
11
12 }
13
14}

You can see that first it tries to restore the license if it is available on the device. If it is not available, then it tries to recover by fetching the license from server.

At the end it triggers license activation.

Understand API through Tests 

In the sample app you can find very useful unit tests which maybe helpful in API exploration.

For license activation please go to the file LkmsLicenseServiceTest. Most of the test cases should be self descriptive. Below, you will find short descriptions to particular test scenarios.

  • should reset licenses successfully - calling resetLicense on LicenseService triggers the SDK for complete license removal.

  • should return license from store - calling prepareLicense triggers license restoration by the SDK.

  • should fetch new license - when a license is missing on the local device, then it will be fetched remotely.

  • should throw exception when fail to restore and fetch license - when the application fails to retrieve the license through restoration or remote fetch, an exception will be thrown. You need to translate it into a user-friendly form.

  • should fetch and activate license successfully when license is not stored and available - shows the full scenario of

    • restoring
    • fetching
    • activating the license

User Management 

This module is responsible for the general management of users of the sample app.

It allows for a user account to be stored, retrieved, or deleted.

Application User and SDK User 

When reading code you'll come across the following classes and interfaces: User, IUser, and AppUser.

  • IUser - Interface provided to the app by Biometric Capture SDK

  • User - Default implementation of IUser. Maintained by Biometric Capture SDK.

  • AppUser - Describes user data inside the application. It consists of:

    • Biometric Capture SDK User data - just name and id
    • Templates - Biometric Data captured during enrollment
    • Image - it is an image captured during enrollment
SampleAppIntGuide app v sdk

UserService Configuration 

This component has three dependencies:

  • UserStore implemented by BiosUserStore - responsible for storing IUser information. Is dependent only on MorphoBioStoreDBWrapper which only wraps static calls to Biometric Capture SDK.

  • UserTemplateStore implemented by BioUserTemplateStore - it also is only dependent on MorphoBioStoreDBWrapper. Easy to initialize.

  • ImageStore implemented by UserImageStore - this component saves images made by the user during enrollment. They are saved directly on a hard drive. That's why ImageStore depends on FileStore. This store is implemented by AndroidInternalFileStore which just depends on an Android context.

UserService Usage 

The component has a simple interface:

  • saveUser(appUser) - stores userData, templates, and pictures created during enrollment.

  • remove/removeAll - removes a user from the system. In case of any error you will see an error in the logs.

  • listUsers - loads all data from every "store" for each user.

Understand API through Tests 

Because "store" components in this module mainly forward calls to Biometric Capture SDK, the tests defined below explain which internal methods needs to be called in order to obtain the expected behavior.

  • BioUserStoreTest - shows which methods on BioStoreDBWrapper will be called as an effect of the BioUSerStore method calls.

  • BioUserTemplateStoreTest - similar to above but focuses on templates.

  • UserImageStoreTest - this one is for images.

Usages 

Below are some specific usages to help you to better understand the API module.

SettingsPresenter - here you can remove all users to start enrollment all over again:

Java
1private fun removeUsers() {
2 runBlocking {
3 userManager.removeAll()
4 }
5}