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.
Requirements
- Development environment: Android Studio 4.0+, Windows, Mac or Linux, JDK 8.. JDK12.
- Android device/emulator with Android 5.0 (API 21) or greater.
- Knowledge of Kotlin programming language.
- Internet connection.
Step-by-Step Guide
-
First of all fetch sample apps from Artifactory. There is separate project per SDK feature (document, face, finger capture). There are also dedicated apps for remote use cases.
-
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 Experience Portal in Access section: My dashboards -> My Identity Proofing -> Access. Under Environments section you can find backend API keys and under SDK artifactory and licenses, Artifactory and LKMS credentials are stored. Please find required properties below.
Properties1#Artifactory2artifactoryUserMI=artifactory_user3artifactoryPasswordMI=artifactory_credentials4repositoryUrlMI=https://mi-artifactory.otlabs.fr/artifactory/smartsdk-android-local56#LKMS7lkmsProfileId="profile_id"8lkmsApiKey="lkms_api_key"910#Backend API KEYS11WbsApiKey="bio_server_api_key"12gipsApiKey="gips_api_key"
More about gradle properties can be found here.
- 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.
You can find application ids associated with your LKMS profile in Experience Portal. Go: My dashboards -> My Identity Proofing -> Access -> SDK artifactory and licenses. You can find ids in row: Associated App(s) and add new one.
-
When the license is active, you can guide the user through the registration/authentication process described in the User Management section.
-
Review the specific application by studying the
FaceSampleApp
orDocumentSampleApp
modules. -
When you run the face scanning application ( menu Run → Run 'FaceSampleApp'), you will be presented with the following screen:
- When you run the document scanning application (menu Run → Run 'DocumentSampleApp'), you will be presented with the following screen:
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
Document 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
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
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:
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
.
Java1//Fragment2override fun onStart() { //triggered by Android System3 super.onStart()4 scopeIO.launch {5 presenter.onStart()6 }7}89//Presenter10suspend fun onStart() { //triggered by Fragment11 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.
XML1<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:
Java1fragment.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.
Java1suspend 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.
Java1suspend 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.
Java1companion object {2 private const val PERMISSION_REQUEST_CODE = 113 private val PERMISSIONS_TO_REQUEST: Array<String> = arrayOf(Manifest.permission.CAMERA)45 operator fun invoke(f:Fragment):DocumentAppPermissionProvider = DocumentAppPermissionProvider(6 @RequiresApi(Build.VERSION_CODES.M) { (f.activity as7Activity).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
Tutorial page for a passport
Tutorial page for a driver's license
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.
Navigation Flow
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.
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
Java1// Returns true if given document has tutorial animation file2private fun hasTutorialAnimation(documentType: DocumentType): Boolean = getTutorialAnimationFileId(documentType) != TutorialContentProvider.DOCUMENT_OPTION_WITHOUT_TUTORIAL34// Each document has given animation file id as an argument with default5value equal 06private 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.
CR2D
This challenge mode requires multiple dots to connect on the screen.
The logic responsible for this challenge mode can be found in the class: CR2DChallengeFragment
which delegates operations to CR2DChallengePresenter.
Java1builder.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 }1415if (appSettings.useIllumination) {16 builder.illumination(this\@CR2DChallengePresenter)17}18cR2DPreview = builder.build()
Layout
Layout is defined in the file fragment_challenge_cr2d.xml
.
XML1<!--Custom component provided by UI-EXTENSIONS library. Is responsible for capturing biometric data -->2 <com.idemia.biometricsdkuiextensions.ui.scene.scene.SceneView3 ...4 />56 <ImageView7 android:id="@+id/faceOverlay"8 .../>910 <TextView11 android:id="@+id/faceOverlayText"12 ...13 />1415<!--General text field which will be used to display information **for**16 user-->17 <TextView18 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.
Java1builder.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 }1415 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:
-
Presenter:
prepareFaceCapture
- builds the
CR2DPreview
object
- builds the
-
Presenter:
startFaceCapture
- start
CR2DPreview
- draws face outline
- starts
CR2DCapture
- starts
SceneController
- start
-
Presenter:
onPause
- stops
SceneController
- stops
CR2DPreview
- stops
CR2DCapture
- stops
-
Presenter:
release
- destroys
SceneController
- destroys
CR2DCapture
- destroys
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.
Choose which challenge method to use (CR2D - for joining points on the screen and SLAM for face scan).
Settings are backed by SettingsFragment.kt
and SettingsPresenter.kt
. The last one saves the chosen values by using SettingStorage.
Java1interface SettingsStorage {2 fun load(): FaceAppSettings3 fun save(settings: FaceAppSettings)4}
Face Sample App 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:
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.
Java1//fragment2override fun onViewCreated(view: View, savedInstanceState: Bundle?) {3 super.onViewCreated(view, savedInstanceState)4 presenter = providePresenter()5 presenter.onCreate()6 }789//presenter10 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.
XML1<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.
Java1fragment.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.
Java1class AppPermissionProvider(2 @RequiresApi(Build.VERSION_CODES.M) private val checkPermission: PermissionChecker,3 private val triggerPermissionRequest: PermissionRequester,4 private val versionInfoProvider: VersionInfoProvider = StaticVersionInfo()5) : PermissionProvider {67//In companion object8operator fun invoke(f: Fragment): AppPermissionProvider =9 AppPermissionProvider(10 @RequiresApi(Build.VERSION_CODES.M) {11 (f.activity as Activity).checkSelfPermission( //permission checker1213 it14 )15 },16 { f.requestPermissions(PERMISSIONS_TO_REQUEST, PERMISSION_REQUEST_CODE) } //permission requester17 )
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.
Java1fun 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
Screen 2
Screen 3
Tutorial
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.
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.
Java1//This part handles tutorial settings choice2override fun configureTutorial() {3 if (!appSettings.showTutorial) {4 tutorialView.setStartingDestination(R.id.settingsActivity)5 } else {6 chooseChallengeMode()7 }8}910//if you checked tutorial field in settings then the second condition11handles challenge mode.1213private fun chooseChallengeMode() {14 when (appSettings.mode) {15 ChallengeMode.CR2D -> {16 tutorialView.setStartingDestination(17 R.id.firstStepFragment,18 bundleOf(19 TutorialActivity.USE_ILLUMINATION to appSettings.useIllumination20 )21 )22 }23 ChallengeMode.SLAM ->24 tutorialView.setStartingDestination(R.id.SLAMStepFragment)25 }26}
In the activity, notice the use of the standard navigation API.
Java1override fun setStartingDestination(destinationResId: Int, arguments: Bundle) {2 val graph = navController.navInflater.inflate(R.navigation.navigation_tutorial)3 graph.startDestination = destinationResId4 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
SampleApps
notice the usage of this module inSplashPresenter
where the license service is used during theonCreate
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 only one dependency which you need to use in order to configure the fetching license procedure:
Java1private val licenseManager: LkmsLicenseManagerWrapper,
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 theLicenseManager
LicenseService Usage
When all dependencies are provided, you can use an implementation of LicenseService
, which is called LkmsLicenseService
.
Java1override suspend fun prepareLicence(): LicenseActivationResult {2 return licenseManager.activate()3}
You can see that licenseManager trying to fetch new license and activate or if license was fetched before it will activate license without fetching new one.
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 return error after fail to activate licence - calling
prepareLicense
onLicenseService
trigger on licenseManager activation but ifLicenseManager
can not fetch or activate license it return LicenseActivationError -
should return LicenseActivationSuccess after successful license activation - calling
prepareLicense
trigger on licenseManager activation. If can fetch and activate license with success it return LicenseActivationSuccess
For more information about LicenseManger
see integration guide
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
andid
- Templates - Biometric Data captured during enrollment
- Image - it is an image captured during enrollment
- Biometric Capture SDK User data - just
UserService Configuration
This component has three dependencies:
-
UserStore implemented by BiosUserStore - responsible for storing
IUser
information. Is dependent only onMorphoBioStoreDBWrapper
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 onFileStore
. This store is implemented byAndroidInternalFileStore
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 theBioUSerStore
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:
Java1private fun removeUsers() {2 runBlocking {3 userManager.removeAll()4 }5}