Commit 25857e7f authored by Javokhir's avatar Javokhir
Browse files

Updated documentation

parent 8b061fd6
......@@ -196,7 +196,7 @@ All notable changes to this project will be documented in this file.
- 🔥 Added new `withOrganizationDetails(value: OrganizationDetails)` method. You can customize the
SDK, for it to match your organization's brand
book. [Usage](CUSTOMIZATION.md)
book. [Usage](docs/CUSTOMIZATION.md)
- 🌟 Added `comparison` value to the `MyIdResult` object on successful identification.
### Fixes
......
# MyID Android SDK
## Table of contents
This repository contains **two supported SDK flows**.
- [Changelog](CHANGELOG.md)
- [Getting started](#getting-started)
- [Before you begin](#11-before-you-begin)
- [Setup MyID Android SDK](#12-setup-myid-android-sdk)
- [Permissions](#13-permissions)
- [Usage](#usage)
- [Methods](#11-methods)
- [Handling callbacks](#12-handling-callbacks)
- [SDK error codes](#sdk-error-codes)
- [UI customization](CUSTOMIZATION.md)
- [Huawei configuration](HUAWEI-CONFIGURATION.md)
## Getting started
### 1.1 Before you begin
Install or update Android Studio to its latest version.
The SDK supports API level 23 and above
Make sure that your app meets the following requirements:
- `minSdkVersion = 23`
- `targetSdkVersion = 36`
- `Kotlin = 1.8.22+`
- `android.useAndroidX = true`
``` gradle
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
```
### 1.2 Setup MyID Android SDK
#### Step 1: Add the Repository
First, add the Artifactory repository to the repositories block of your module's **_build.gradle_** file:
```gradle
repositories {
maven { url "https://artifactory.aigroup.uz:443/artifactory/myid" }
}
```
Here’s a clean and concise documentation section based on your instructions:
Choose the flow that matches your integration scenario.
---
#### Step 2: Add the Dependency
Add the required SDK dependency to your `build.gradle`:
```gradle
dependencies {
implementation("uz.myid.sdk.capture:myid-capture-sdk:2.4.9")
}
```
Due to the advanced validation support (in C++ code), we recommend that the integrator app performs [multi-APK split](#multi-apk-split) to optimize the app size for individual architectures.
Average size (with Proguard enabled):
| ABI | Size |
|-------------|:--------:|
| armeabi-v7a | 10.0 Mb |
| arm64-v8a | 10.5 Mb |
| universal | 20.4 Mb |
#
If you are using **VideoIdentification** entry mode, also include:
```gradle
dependencies {
implementation("uz.myid.sdk.capture:myid-video-capture-sdk:2.4.9")
}
```
Average size (with Proguard enabled):
| ABI | Size |
|-------------|:--------:|
| armeabi-v7a | 15.6 Mb |
| arm64-v8a | 16.7 Mb |
| universal | 38.2 Mb |
**Note**: The average sizes were measured by building the minimum possible wrappers around our SDK.
Different versions of the dependencies, such as Gradle or NDK, may result in slightly different values.
#### Multi-APK split
C++ code needs to be compiled for each of the CPU architectures (known as "ABIs") present on the Android environment. Currently, the SDK supports the following ABIs:
* `armeabi-v7a`: Version 7 or higher of the ARM processor. Most recent Android phones use this
* `arm64-v8a`: 64-bit ARM processors. Found on new generation devices
* `x86`: Used by most tablets and emulators
* `x86_64`: Used by 64-bit tablets
The SDK binary contains a copy of the native `.so` file for each of these four platforms.
You can considerably reduce the size of your `.apk` by applying APK split by ABI, editing your `build.gradle` to the following:
```gradle
android {
splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a'
universalApk false
}
}
}
```
Read the [Android documentation](https://developer.android.com/build/configure-apk-splits) for more information.
### 1.3 Permissions
Add following lines to the **_AndroidManifest.xml_**:
``` xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
```
## New Flow (Default) — SDK 3.1.4
## Usage
Use this flow if you are integrating MyID **for the first time**
or starting a **new backend session-based flow**.
### With Activity Result API
- Docs: [New Flow documentation](docs/new/README.md)
- Customization: [Customization](docs/CUSTOMIZATION.md)
- Huawei: [Huawei configuration (New Flow)](docs/new/HUAWEI-CONFIGURATION.md)
``` kotlin
class ExampleActivity : AppCompatActivity(), MyIdResultListener {
private val client = MyIdClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startMyId()
}
private fun startMyId() {
val config = MyIdConfig.Builder(clientId = /* Your client id */)
.withClientHash(/* Your clientHash */, /* Your clientHashId */)
.withPassportData(passportData)
.withBirthDate(dateOfBirth)
.withEnvironment(MyIdEnvironment.Production)
.build()
val intent = client.createIntent(activity = this, config)
result.launch(intent)
}
private val result = takeMyIdResult(listener = this)
}
```
### With `onActivityResult` method
``` kotlin
class ExampleActivity : AppCompatActivity(), MyIdResultListener {
private val client = MyIdClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startMyId()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
client.handleActivityResult(resultCode, this)
}
private fun startMyId() {
val config = MyIdConfig.Builder(clientId = /* Your client id */)
.withClientHash(/* Your clientHash */, /* Your clientHashId */)
.withPassportData(passportData)
.withBirthDate(dateOfBirth)
.withEnvironment(MyIdEnvironment.Production)
.build()
/*
Start the flow. 1 should be your request code (customize as needed).
Must be an Activity or Fragment (support library).
This request code will be important for you on onActivityResult() to identify the MyIdResultListener.
*/
client.startActivityForResult(this, 1, config)
}
}
```
### 1.1 Methods
Method | Notes | Default
--- | --- | ---
`withClientHash(clientHash: String, clientHashId: String)` | Provided by MyID sales team | Mandatory, if using withEntryType(MyIdEntryType.Identification)
`withPassportData(passportData: String)` | Passport serial number or PINFL data | Optional
`withBirthDate(dateOfBirth: String)` | Date of birth. Format: `dd.MM.yyyy` | Optional
`withSdkHash(sdkHash: String)` | 32 characters long string (Note 1.2) | Optional
`withMinAge(age: Int)` | To set a specific minimum age to use MyID service | 16
`withExternalId(externalId: String)` | 36 characters long. Should match with UUID4 regex (Note 1.3) | Optional
`withThreshold(threshold: Float)` | The value can be in the range of `0.55` - `0.99` | 0.55
`withResidency(residency: MyIdResidency)` | To set a specific residency type | MyIdResidency.Resident
`withEnvironment(environment: MyIdEnvironment)` | Environment mode (Note 1.4) | MyIdEnvironment.Production
`withEntryType(type: MyIdEntryType)` | Customizing the SDK Entry types (Note 1.5) | MyIdEntryType.Identification
`withLocale(locale: MyIdLocale)` | To set a specific locale | MyIdLocale.Uzbek
`withCameraShape(shape: MyIdCameraShape)` | To set a specific camera shape (Note 1.6) | MyIdCameraShape.Circle
`withCameraResolution(resolution: MyIdCameraResolution)` | To set a specific camera resolution | MyIdCameraResolution.Low
`withScreenOrientation(orientation: MyIdScreenOrientation)` | To set a specific sdk orientation | MyIdScreenOrientation.Portrait
`withImageFormat(format: MyIdImageFormat)` | To set a specific image format | MyIdImageFormat.PNG
`withOrganizationDetails(details: MyIdOrganizationDetails)` | Custom Organization Details | Optional
`withSoundGuides(enable: Boolean)` | To set sound guides | true
`withErrorScreen(showErrorScreen: Boolean)` | Customizing the SDK error screens | true
`withHuaweiAppId(appId: String)` | To set a huawei app id | Required for HMS
**Note 1.1.** You can customize the screen for entering passport data and date of birth in your
application, in which case you can pass these parameters during initialization to the SDK, otherwise
the SDK requires the input of passport data and date of birth for user identification.
**Note 1.2.** If the `sdk_hash` is empty, blank or string with length other than 32 has been provided, we will continue showing the credentials screen.
**Note 1.3.** If the `externalId` is not empty, has a length of 36 characters and corresponds to the regular expression UUID4, we will not display a [recommendation](images/screen01.jpg) screen. If a certain number of unsuccessful identification attempts (currently set to 5) occur in MyID within one hour and the `externalId` is not empty, SDK returns to the parent app error message as `message` in `MyIdException`.
**Note 1.4.** `MyIdEnvironment` contains **Debug** and **Production** modes.
- **Debug** is used to sandbox.
- **Production** is used to production.
**Note 1.5.** `MyIdEntryType` contains **Identification**, **VideoIdentification** and **FaceDetection** types.
- **Identification** is used to identify the user through the MyID services.
- **VideoIdentification** is used to identify the user through the MyID services. Requires the `myid-video-capture-sdk` dependency.
- **FaceDetection** is used to detect a face and returns a picture (bitmap).
**Note 1.6.** `MyIdCameraShape` contains **[Circle](images/screen03.jpg)**
and **[Ellipse](images/screen04.jpg)** types.
**Note 1.7.** If the user sends a **passport data** to the SDK, the **residency** must be handled by the **client**. If `residency = MyIdResidency.UserDefined` is sent, the SDK will treat the user as **Non-Resident**.
**Note 1.8.** If the SDK **does not receive a passport data** and receives `residency = MyIdResidency.UserDefined`, the SDK displays the **MyID passport input screen**. If the user enters a **PINFL**, the screen will show a **checkbox** allowing the user to select **Resident** or **Non-Resident**.
### 1.2 Handling callbacks
```kotlin
val resultListener: MyIdResultListener = object : MyIdResultListener {
override fun onSuccess(result: MyIdResult) {
// Get face bitmap and result code
val bitmap = result.getGraphicFieldImageByType(MyIdGraphicFieldType.FacePortrait)
val code = result.code
val comparison = result.comparison
}
override fun onUserExited() {
// User left the SDK
}
override fun onError(e: MyIdException) {
// Get error message and code:
val message = e.message
val code = e.code
}
override fun onEvent(event: MyIdEvent) {
}
}
```
---
| Attribute | Notes |
| -----|-------|
| `onSuccess` | `MyIdResult` contains information about the face captures made during the flow, result code and comparison value. |
| `onUserExited` | User left the SDK flow without completing it. |
| `onError` | Some error happened. `MyIdException` contains information about the error message and code |
| `onEvent` | `MyIdEvent` contains information about the event type |
## Old Flow — SDK 2.4.9
## SDK error codes
Use this flow if you already have an existing
**clientId-based integration** in production.
You can view the full list of SDK error codes at:
- Docs: [Old Flow documentation](docs/old/README.md)
- Customization: [Customization](docs/CUSTOMIZATION.md)
- Huawei: [Huawei configuration (Old Flow)](docs/old/HUAWEI-CONFIGURATION.md)
👉 [Error Codes Documentation](https://docs.myid.uz/#/ru/embedded?id=javob-kodlar-uz-result_code)
---
The error code in the following list may appear during the call of SDK. The list below is for your
reference.
## Changelog
| Code | Error message
|:----------:|:-------------
| 102 | Camera access denied
| 103 | Error while retrieving data from server or SDK
| 122 | User banned
- [Changelog](CHANGELOG.md)
/build
\ No newline at end of file
......@@ -4,18 +4,27 @@ plugins {
}
android {
namespace = "uz.uzinfocom.myid"
compileSdk = 35
namespace = "uz.myid.sample"
compileSdk = 36
defaultConfig {
applicationId = "uz.uzinfocom.myid"
applicationId = "uz.myid.sample"
targetSdk = 36
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "1.0"
versionName = "1.0.$versionCode"
}
buildTypes {
debug {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
release {
isMinifyEnabled = false
......@@ -41,11 +50,9 @@ android {
}
dependencies {
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity)
implementation(libs.google.material)
implementation(libs.myid)
implementation(libs.myid.video)
debugImplementation("uz.myid.sdk.capture:myid-capture-sdk-debug:3.1.4")
releaseImplementation("uz.myid.sdk.capture:myid-capture-sdk:3.1.4")
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
-dontwarn com.google.mlkit.vision.facemesh.**
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uz.myid.sample">
<uses-permission android:name="android.permission.INTERNET" />
......@@ -8,17 +9,14 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyId">
android:theme="@style/Theme.MyIdAndroid">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package uz.myid.sample
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.widget.EditText
import android.widget.Toast
inline val Context.clipboardManager: ClipboardManager?
get() = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
fun Context.copyToClipboard(text: String) {
val clipboard = clipboardManager
val clip = ClipData.newPlainText("Copied", text)
clipboard?.setPrimaryClip(clip)
Toast.makeText(this, "Copied", Toast.LENGTH_SHORT).show()
}
inline val EditText.value: String get() = text.toString().trim()
package uz.uzinfocom.myid
package uz.myid.sample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import uz.myid.android.sdk.capture.MyIdClient
import uz.myid.android.sdk.capture.MyIdConfig
import uz.myid.android.sdk.capture.MyIdException
......@@ -12,19 +15,21 @@ import uz.myid.android.sdk.capture.model.MyIdCameraSelector
import uz.myid.android.sdk.capture.model.MyIdCameraShape
import uz.myid.android.sdk.capture.model.MyIdEntryType
import uz.myid.android.sdk.capture.model.MyIdEnvironment
import uz.myid.android.sdk.capture.model.MyIdEvent
import uz.myid.android.sdk.capture.model.MyIdGraphicFieldType
import uz.myid.android.sdk.capture.model.MyIdImageFormat
import uz.myid.android.sdk.capture.model.MyIdLocale
import uz.myid.android.sdk.capture.model.MyIdResidency
import uz.myid.android.sdk.capture.model.MyIdScreenOrientation
import uz.myid.android.sdk.capture.takeMyIdResult
import uz.uzinfocom.myid.databinding.ActivityMainBinding
import uz.myid.sample.data.SessionRepository
import uz.myid.sample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity(), MyIdResultListener {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val client = MyIdClient()
private val myidClient by lazy { MyIdClient() }
private var residency = MyIdResidency.Resident
private var environment = MyIdEnvironment.Debug
......@@ -40,13 +45,6 @@ class MainActivity : AppCompatActivity(), MyIdResultListener {
setContentView(binding.root)
with(binding) {
radioGroupResidency.setOnCheckedChangeListener { _, checkedId ->
residency = when (checkedId) {
R.id.radioNonResident -> MyIdResidency.NonResident
R.id.radioUserDefined -> MyIdResidency.UserDefined
else -> MyIdResidency.Resident
}
}
radioGroupEnvironment.setOnCheckedChangeListener { _, checkedId ->
environment = if (checkedId == R.id.radioProd) {
MyIdEnvironment.Production
......@@ -102,25 +100,42 @@ class MainActivity : AppCompatActivity(), MyIdResultListener {
}
override fun onSuccess(result: MyIdResult) {
val bitmap = result.getGraphicFieldImageByType(MyIdGraphicFieldType.FacePortrait)
val bitmap = result.getGraphicFieldImageByType(
MyIdGraphicFieldType.FacePortrait
)
binding.imageResult.setImageBitmap(bitmap)
with(binding) {
"""
Result code: ${result.code}
""".trimIndent().also { textResult.text = it }
Result code: ${result.code},
Image width: ${bitmap?.width},
Image height: ${bitmap?.height},
""".trimIndent().also {
textResult.text = it
textResult.setOnClickListener {
copyToClipboard(result.code)
}
}
}
}
override fun onError(exception: MyIdException) {
val bitmap = myidClient.getGraphicFieldImageByType(
MyIdGraphicFieldType.FacePortrait
)
with(binding) {
imageResult.setImageBitmap(null)
imageResult.setImageBitmap(bitmap)
"""
Result error: ${exception.message}
Result error code: ${exception.code}
""".trimIndent().also { textResult.text = it }
""".trimIndent().also {
textResult.text = it
textResult.setOnClickListener {
}
}
}
}
......@@ -132,28 +147,27 @@ class MainActivity : AppCompatActivity(), MyIdResultListener {
}
}
override fun onEvent(event: MyIdEvent) {
}
private fun startMyId() {
val config = MyIdConfig.Builder("client_id")
val config = MyIdConfig.Builder(
sessionId = binding.inputSessionId.value
)
.withClientHash(
clientHash = "client_hash",
clientHashId = "client_hash_id",
)
.withDocumentData(
passportData = binding.inputPassportData.value,
dateOfBirth = binding.inputDate.value,
clientHash = binding.inputHash.value,
clientHashId = binding.inputHashId.value
)
.withResidency(residency)
.withEnvironment(environment)
.withEntryType(entryType)
.withCameraSelector(cameraSelector)
.withCameraResolution(cameraResolution)
.withCameraShape(cameraShape)
.withImageFormat(imageFormat)
.withScreenOrientation(MyIdScreenOrientation.Portrait)
.withLocale(locale)
.build()
val intent = client.createIntent(this, config)
val intent = myidClient.createIntent(this, config)
result.launch(intent)
}
......
package uz.myid.sample.data
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.request.header
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.request.url
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.contentType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import org.json.JSONObject
import uz.myid.sample.BuildConfig
import uz.myid.sample.MainActivity
internal class SessionRepository {
private val httpClient by lazy {
HttpClient(OkHttp) {
install(HttpTimeout) {
requestTimeoutMillis = 50 * 1_000
connectTimeoutMillis = 10 * 1_000
}
followRedirects = false
}
}
fun getAccessToken(): Flow<String> = flow {
val httpResponse = httpClient.post {
url(
if (BuildConfig.PRODUCTION) {
"https://api.myid.uz/api/v1/auth/clients/access-token"
} else {
"https://api.devmyid.uz/api/v1/auth/clients/access-token"
}
)
contentType(ContentType.Application.Json)
setBody(
"""
{
"client_id": "${
if (BuildConfig.PRODUCTION) {
MainActivity.prodClientId
} else {
MainActivity.devClientId
}
}",
"client_secret": "${
if (BuildConfig.PRODUCTION) {
MainActivity.prodClientSecret
} else {
MainActivity.devClientSecret
}
}"
}
""".trimIndent()
)
}
val json = httpResponse.bodyAsText()
val jsonObject = JSONObject(json)
val accessToken = jsonObject.optString("access_token", "")
emit(accessToken)
}.catch { cause ->
emit(cause.message.orEmpty())
}.flowOn(Dispatchers.IO)
fun getSessionId(accessToken: String): Flow<String> = flow {
val httpResponse = httpClient.post {
url(
if (BuildConfig.PRODUCTION) {
"https://api.myid.uz/api/v2/sdk/sessions"
} else {
"https://api.devmyid.uz/api/v2/sdk/sessions"
}
)
contentType(ContentType.Application.Json)
header(
HttpHeaders.Authorization,
"Bearer $accessToken"
)
setBody("{}")
}
val json = httpResponse.bodyAsText()
val jsonObject = JSONObject(json)
val sessionId = jsonObject.optString("session_id", "")
emit(sessionId)
}.catch { cause ->
emit(cause.message.orEmpty())
}.flowOn(Dispatchers.IO)
}
\ No newline at end of file
package uz.uzinfocom.myid
import android.widget.EditText
inline val EditText.value: String get() = text.toString().trim()
This diff is collapsed.
......@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
......@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources />
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<resources>
<string name="app_name">MyID SDK</string>
<string name="client_id">Client ID</string>
<string name="session_id">Session ID</string>
<string name="client_hash">Client Hash</string>
<string name="client_hash_id">Client Hash ID</string>
<string name="passport">Passport data</string>
<string name="birth_date">Date of birth</string>
<string name="prod">Production</string>
<string name="dev">Debug</string>
<string name="auth">Photo Identification</string>
......@@ -22,9 +20,5 @@
<string name="uzbek">Uzbek</string>
<string name="english">English</string>
<string name="russian">Russian</string>
<string name="resident">Resident</string>
<string name="non_resident">Non-resident</string>
<string name="user_defined">User defined</string>
<string name="scan_face_data">Scan face data</string>
<string name="save_to_gallery">Save to gallery</string>
</resources>
<resources>
<style name="Base.Theme.MyId" parent="Theme.Material3.DayNight.NoActionBar" />
<style name="Theme.MyId" parent="Base.Theme.MyId" />
<style name="Theme.MyIdAndroid" parent="Theme.Material3.Light.NoActionBar" />
</resources>
\ No newline at end of file
......@@ -26,7 +26,7 @@ customize following fields:
- *phoneNumber* - by default 712022202, which is MyID's call center. If you would like the customer
to call your own call center, you can display your own phone number on the error screen, by
providing it in this field ([sample](images/screen01.jpg)).
providing it in this field ([sample](../images/screen01.jpg)).
- *logo* - the drawable asset, that will be displayed on the input screen. If you would like to
display your own logo on the top of the screen, this is the place to provide it. Make sure it fits
the imageView element, which has the *240x60* size.
......@@ -66,7 +66,7 @@ You can customize the appearance of some widgets in your `dimens.xml` file by ov
#
![Screenshot](images/frame_1.jpg)
![Screenshot](images/frame_2.jpg)
![Screenshot](images/frame_3.jpg)
![Screenshot](images/frame_4.jpg)
![Screenshot](../images/frame_1.jpg)
![Screenshot](../images/frame_2.jpg)
![Screenshot](../images/frame_3.jpg)
![Screenshot](../images/frame_4.jpg)
# Huawei Configuration
## Overview
MyID SDK supports Huawei devices that use **Huawei Mobile Services (HMS)** instead of **Google Play Services (GMS)**.
If your app runs on Huawei devices (for example, Mate or P series) **without GMS**,
you must add the **MyID Integrity SDK** and configure your Huawei **App ID**.
If this dependency is **missing** and the device **supports HMS**,
the MyID SDK will **crash during initialization**.
---
## Table of contents
* [1. Add the Huawei Integrity SDK](#1-add-the-huawei-integrity-sdk)
* [2. Configure the App ID](#2-configure-the-app-id)
* [3. Pass Huawei App ID to the SDK](#3-pass-huawei-app-id-to-the-sdk)
* [4. Example setup](#4-example-setup)
* [5. Troubleshooting](#5-troubleshooting)
---
## 1. Add the Huawei Integrity SDK
Add the following dependency to your **module-level** `build.gradle` file:
```gradle
dependencies {
implementation("uz.myid.sdk.capture:myid-integrity-sdk:1.0.6")
}
```
This library enables the SDK to handle Huawei Mobile Services (HMS)
and prevents runtime crashes on Huawei devices.
---
## 2. Configure the App ID
You need a valid **Huawei App ID** from **AppGallery Connect**.
1. Open [AppGallery Connect](https://developer.huawei.com/consumer/en/service/josp/agc/index.html).
2. Go to your project → **General information**.
3. Copy your **App ID**.
4. Add it to your `AndroidManifest.xml` inside the `<application>` tag:
```xml
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=YOUR_HUAWEI_APP_ID" />
```
Replace `YOUR_HUAWEI_APP_ID` with your actual App ID.
---
## 3. Pass Huawei App ID to the SDK
When initializing `MyIdConfig`, include your Huawei App ID using the `withHuaweiAppId()` method:
```kotlin
val config = MyIdConfig.Builder(clientId = "YOUR_CLIENT_ID")
.withClientHash("YOUR_CLIENT_HASH", "YOUR_CLIENT_HASH_ID")
.withEnvironment(MyIdEnvironment.Production)
.withHuaweiAppId("YOUR_HUAWEI_APP_ID")
.build()
```
This ensures that the SDK recognizes and properly initializes HMS support.
---
## 4. Example setup
**Gradle dependencies:**
```gradle
dependencies {
implementation("uz.myid.sdk.capture:myid-capture-sdk:3.1.4")
implementation("uz.myid.sdk.capture:myid-integrity-sdk:1.0.6")
}
```
**Initialization:**
```kotlin
val config = MyIdConfig.Builder(sessionId = "SESSION_ID")
.withClientHash("CLIENT_HASH", "CLIENT_HASH_ID")
.withEnvironment(MyIdEnvironment.Production)
.withHuaweiAppId("YOUR_HUAWEI_APP_ID")
.build()
val intent = MyIdClient().createIntent(this, config)
startActivity(intent)
```
---
## 5. Troubleshooting
| Issue | Cause | Solution |
| -------------------------------------- | ------------------------------- | -------------------------------------------------------------------- |
| SDK crashes on Huawei devices | `myid-integrity-sdk` not added | Add `implementation("uz.myid.sdk.capture:myid-integrity-sdk:1.0.6")` |
| SDK fails to initialize | Missing App ID | Add `.withHuaweiAppId("YOUR_HUAWEI_APP_ID")` |
| Invalid App ID error | Wrong App ID in manifest or SDK | Recheck AppGallery Connect for correct ID |
| Works on Google devices but not Huawei | HMS dependency missing | Make sure Integrity SDK is included |
# MyID Android SDK
## Table of contents
- [Getting started](#getting-started)
- [Before you begin](#11-before-you-begin)
- [Setup MyID Android SDK](#12-setup-myid-android-sdk)
- [Permissions](#13-permissions)
- [Usage](#usage)
- [Methods](#11-methods)
- [Handling callbacks](#12-handling-callbacks)
- [SDK error codes](#sdk-error-codes)
## Getting started
### 1.1 Before you begin
Install or update Android Studio to its latest version.
The SDK supports API level 21 and above
Make sure that your app meets the following requirements:
- `minSdkVersion = 21`
- `targetSdkVersion = 36`
- `Kotlin = 1.8.22+`
- `android.useAndroidX = true`
``` gradle
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
```
### 1.2 Setup MyID Android SDK
#### Step 1: Add the Repository
First, add the Artifactory repository to the repositories block of your module's **_build.gradle_** file:
```gradle
repositories {
maven { url "https://artifactory.aigroup.uz:443/artifactory/myid" }
}
```
Here’s a clean and concise documentation section based on your instructions:
---
#### Step 2: Add the Dependency
Add the required SDK dependency to your `build.gradle`:
```gradle
dependencies {
debugImplementation("uz.myid.sdk.capture:myid-capture-sdk-debug:3.1.4")
releaseImplementation("uz.myid.sdk.capture:myid-capture-sdk:3.1.4")
}
```
Due to the advanced validation support (in C++ code), we recommend that the integrator app performs [multi-APK split](#multi-apk-split) to optimize the app size for individual architectures.
Average size (with Proguard enabled):
| ABI | Size |
|-------------|:--------:|
| armeabi-v7a | 28.7 Mb |
| arm64-v8a | 23.3 Mb |
| universal | 37.2 Mb |
#
If you are using **VideoIdentification** entry mode, also include:
```gradle
dependencies {
implementation("uz.myid.sdk.capture:myid-video-capture-sdk:3.1.4")
}
```
Average size (with Proguard enabled):
| ABI | Size |
|-------------|:--------:|
| armeabi-v7a | 39.5 Mb |
| arm64-v8a | 35.6 Mb |
| universal | 57.8 Mb |
**Note**: The average sizes were measured by building the minimum possible wrappers around our SDK.
Different versions of the dependencies, such as Gradle or NDK, may result in slightly different values.
#### Multi-APK split
C++ code needs to be compiled for each of the CPU architectures (known as "ABIs") present on the Android environment. Currently, the SDK supports the following ABIs:
* `armeabi-v7a`: Version 7 or higher of the ARM processor. Most recent Android phones use this
* `arm64-v8a`: 64-bit ARM processors. Found on new generation devices
* `x86`: Used by most tablets and emulators
* `x86_64`: Used by 64-bit tablets
The SDK binary contains a copy of the native `.so` file for each of these four platforms.
You can considerably reduce the size of your `.apk` by applying APK split by ABI, editing your `build.gradle` to the following:
```gradle
android {
splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a'
universalApk false
}
}
}
```
Read the [Android documentation](https://developer.android.com/build/configure-apk-splits) for more information.
### 1.3 Permissions
Add following lines to the **_AndroidManifest.xml_**:
``` xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
```
## Usage
### With Activity Result API
``` kotlin
class ExampleActivity : AppCompatActivity(), MyIdResultListener {
private val client by lazy { MyIdClient() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startMyId()
}
private fun startMyId() {
val config = MyIdConfig.Builder(sessionId = /* Your session id */)
.withClientHash(/* Your clientHash */, /* Your clientHashId */)
.withEnvironment(MyIdEnvironment.Production)
.build()
val intent = client.createIntent(activity = this, config)
result.launch(intent)
}
private val result = takeMyIdResult(listener = this)
}
```
### With `onActivityResult` method
``` kotlin
class ExampleActivity : AppCompatActivity(), MyIdResultListener {
private val client by lazy { MyIdClient() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startMyId()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
client.handleActivityResult(resultCode, this)
}
private fun startMyId() {
val config = MyIdConfig.Builder(sessionId = /* Your session id */)
.withClientHash(/* Your clientHash */, /* Your clientHashId */)
.withEnvironment(MyIdEnvironment.Production)
.build()
/*
Start the flow. 1 should be your request code (customize as needed).
Must be an Activity or Fragment (support library).
This request code will be important for you on onActivityResult() to identify the MyIdResultListener.
*/
client.startActivityForResult(this, 1, config)
}
}
```
### 1.1 Methods
Method | Notes | Default
--- | --- | ---
`withClientHash(clientHash: String, clientHashId: String)` | Provided by MyID sales team | Mandatory, if using withEntryType(MyIdEntryType.Identification)
`withResidency(residency: MyIdResidency)` | To set a specific residency type | MyIdResidency.Resident
`withMinAge(age: Int)` | To set a specific minimum age to use MyID service | 16
`withDistance(distance: Float)` | To set a specific distance for taking photo | 0.65
`withEnvironment(environment: MyIdEnvironment)` | Environment mode (Note 1.4) | MyIdEnvironment.Production
`withEntryType(type: MyIdEntryType)` | Customizing the SDK Entry types (Note 1.5) | MyIdEntryType.Identification
`withLocale(locale: MyIdLocale)` | To set a specific locale | MyIdLocale.Uzbek
`withCameraShape(shape: MyIdCameraShape)` | To set a specific camera shape (Note 1.6) | MyIdCameraShape.Circle
`withCameraResolution(resolution: MyIdCameraResolution)` | To set a specific camera resolution | MyIdCameraResolution.Low
`withImageFormat(format: MyIdImageFormat)` | To set a specific image format | MyIdImageFormat.JPEG
`withScreenOrientation(orientation: MyIdScreenOrientation)` | To set a specific screen orientation | MyIdScreenOrientation.Portrait
`withOrganizationDetails(details: MyIdOrganizationDetails)` | Custom Organization Details | Optional
`withSoundGuides(enable: Boolean)` | To enable or disable sound guides | true
`withErrorScreen(showErrorScreen: Boolean)` | To enable or disable error screen | true
`withHuaweiAppId(appId: String)` | To set a huawei app id | Required for HMS
**Note 1.1.** `MyIdEnvironment` contains **Debug** and **Production** modes.
- **Debug** is used to sandbox.
- **Production** is used to production.
**Note 1.2.** `MyIdEntryType` contains **Identification**, **VideoIdentification** and **FaceDetection** types.
- **Identification** is used to identify the user through the MyID services.
- **VideoIdentification** is used to identify the user through the MyID services. Requires the `myid-video-capture-sdk` dependency.
- **FaceDetection** is used to detect a face and returns a picture (bitmap).
**Note 1.3.** `MyIdCameraShape` contains **[Circle](images/screen03.jpg)**
and **[Ellipse](images/screen04.jpg)** types.
**Note 1.4.** If the SDK **does not receive a passport data** and receives `residency = MyIdResidency.UserDefined`, the SDK displays the **MyID passport input screen**. If the user enters a **PINFL**, the screen will show a **checkbox** allowing the user to select **Resident** or **Non-Resident**.
### 1.2 Handling callbacks
```kotlin
val resultListener: MyIdResultListener = object : MyIdResultListener {
override fun onSuccess(result: MyIdResult) {
// Get face bitmap and result code
val bitmap = result.getGraphicFieldImageByType(MyIdGraphicFieldType.FacePortrait)
val code = result.code
}
override fun onUserExited() {
// User left the SDK
}
override fun onError(e: MyIdException) {
// Get error message and code:
val message = e.message
val code = e.code
}
override fun onEvent(event: MyIdEvent) {
// Get event type
}
}
```
| Attribute | Notes |
| -----|-------|
| `onSuccess` | `MyIdResult` contains information about the face captures made during the flow, result code and comparison value. |
| `onUserExited` | User left the SDK flow without completing it. |
| `onError` | Some error happened. `MyIdException` contains information about the error message and code |
| `onEvent` | `MyIdEvent` contains information about the event type |
## SDK error codes
You can view the full list of SDK error codes at:
👉 [Error Codes Documentation](https://docs.myid.uz/#/ru/embedded?id=javob-kodlar-uz-result_code)
The error code in the following list may appear during the call of SDK. The list below is for your
reference.
| Code | Error message
|:----------:|:-------------
| 102 | Camera access denied
| 103 | Error while retrieving data from server or SDK
| 122 | User banned
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment