Skip to content

Commit 88af5e3

Browse files
authored
Merge pull request #8 from blinkcard/release/v2.9.1
Release/v2.9.1
2 parents 6adb56f + 12f6b9b commit 88af5e3

14 files changed

Lines changed: 744 additions & 225 deletions

File tree

BlinkCard/blinkcard-react-native.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ Pod::Spec.new do |s|
1313
s.source = { :git => "https://github.com/BlinkCard/blinkcard-react-native.git", :tag => "v#{s.version}" }
1414
s.source_files = "src/ios", "src/ios/**/*.{h,m}"
1515
s.dependency 'React'
16-
s.dependency 'MBBlinkCard', '~> 2.9.0'
16+
s.dependency 'MBBlinkCard', '~> 2.9.1'
1717
s.frameworks = 'UIKit'
1818
end

BlinkCard/index.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,33 @@ const BlinkCardNative = Platform.select({
3434
* licensee: String,
3535
* showTrialLicenseKeyWarning: Boolean
3636
* }
37+
*
38+
* -> 'scanWithDirectApi' takes the following parameters:
39+
* 1. RecognizerCollection recognizerCollection: object containing recognizers to use for scanning
40+
* 2. String frontImage: a Base64 format string that represents the image of the card where the card number is located that will be used for processing with DirectAPI
41+
* 3. String backImage: a Base64 format string that represents the second side of the card that will be used for processing with DirectAPI
42+
* - backImage parameter is optional for cards that have all of the information located on one side, or if the BlinkCardRecognizer information extraction
43+
* settings, that are enabled, are located only on one side of the card
44+
* - Pass 'null' or an empty string "" for this parameter in this case
45+
* 4. String license: BlinkCard base64 license key bound to application ID for Android or iOS. To obtain
46+
* valid license key, please visit http://microblink.com/login or
47+
* contact us at http://help.microblink.com
48+
*
49+
* OR
50+
*
51+
* Object license: containing:
52+
* - mandatory parameter 'licenseKey': base64 license key bound to application ID
53+
* for Android or iOS. To obtain valid license key, please visit
54+
* http://microblink.com/login or contact us at http://help.microblink.com
55+
* - optioanl parameter 'licensee' when license for multiple apps is used
56+
* - optional flag 'showTrialLicenseKeyWarning' which indicates
57+
* whether warning for trial license key will be shown
58+
* in format
59+
* {
60+
* licenseKey: '<base64iOSLicense or base64AndroidLicense>',
61+
* licensee: String,
62+
* showTrialLicenseKeyWarning: Boolean
63+
* }
3764
*/
3865
class BlinkCardWrapper {
3966
async scanWithCamera(overlaySettings, recognizerCollection, license) {
@@ -66,6 +93,48 @@ class BlinkCardWrapper {
6693
return [];
6794
}
6895
}
96+
97+
async scanWithDirectApi(recognizerCollection, frontImage, backImage, license) {
98+
try {
99+
var licenseObject = license;
100+
if (typeof license === 'string' || license instanceof String) {
101+
licenseObject = { licenseKey: license };
102+
}
103+
104+
var frontImageObject = frontImage;
105+
if (typeof frontImage === 'string' || frontImage instanceof String) {
106+
frontImageObject = { frontImage: frontImage };
107+
}
108+
109+
var backImageObject = backImage;
110+
if (typeof backImage === 'string' || backImage instanceof String) {
111+
backImageObject = { backImage: backImage };
112+
}
113+
114+
const nativeResults = await BlinkCardNative.scanWithDirectApi(recognizerCollection, frontImageObject, backImageObject, licenseObject);
115+
if (nativeResults.length != recognizerCollection.recognizerArray.length) {
116+
console.log("INTERNAL ERROR: native plugin returned wrong number of results!");
117+
return [];
118+
} else {
119+
let results = [];
120+
for (let i = 0; i < nativeResults.length; ++i) {
121+
// native plugin must ensure types match
122+
// recognizerCollection.recognizerArray[i].result = recognizerCollection.recognizerArray[i].createResultFromNative(nativeResults[i]);
123+
124+
// unlike Cordova, ReactNative does not allow mutation of user-provided recognizers, so we need to
125+
// return results and let user handle them manually.
126+
let result = recognizerCollection.recognizerArray[i].createResultFromNative(nativeResults[i]);
127+
if (result.resultState != RecognizerResultState.empty) {
128+
results.push(result);
129+
}
130+
}
131+
return results;
132+
}
133+
} catch (error) {
134+
console.log(error);
135+
return [];
136+
}
137+
}
69138
}
70139

71140
export var BlinkCard = new BlinkCardWrapper();

BlinkCard/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

BlinkCard/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microblink/blinkcard-react-native",
3-
"version": "2.9.0",
3+
"version": "2.9.1",
44
"description": "AI-driven credit card scanning for cross-platform apps built with ReactNative.",
55
"main": "index.js",
66
"repository": {

BlinkCard/src/android/build.gradle

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ rootProject.allprojects {
99
apply plugin: 'com.android.library'
1010

1111
android {
12-
compileSdkVersion 33
13-
buildToolsVersion "28.0.3"
12+
compileSdkVersion 34
1413

1514
defaultConfig {
1615
minSdkVersion 21
17-
targetSdkVersion 33
16+
targetSdkVersion 34
1817
}
1918
buildTypes {
2019
release {
@@ -26,7 +25,7 @@ android {
2625

2726
dependencies {
2827
implementation 'com.facebook.react:react-native:+'
29-
implementation('com.microblink:blinkcard:2.9.0@aar') {
28+
implementation('com.microblink:blinkcard:2.9.3@aar') {
3029
transitive = true
3130
}
3231
}

BlinkCard/src/android/src/main/java/com/microblink/blinkcard/reactnative/MicroblinkModule.java

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import android.app.Activity;
44
import android.content.Intent;
5+
import androidx.annotation.NonNull;
6+
import android.graphics.Bitmap;
7+
import android.graphics.BitmapFactory;
8+
import android.util.Base64;
59

610
import com.facebook.react.bridge.ActivityEventListener;
711
import com.facebook.react.bridge.BaseActivityEventListener;
@@ -20,6 +24,13 @@
2024
import com.microblink.blinkcard.reactnative.overlays.OverlaySettingsSerializers;
2125
import com.microblink.blinkcard.uisettings.ActivityRunner;
2226
import com.microblink.blinkcard.locale.LanguageUtils;
27+
import com.microblink.blinkcard.directApi.DirectApiErrorListener;
28+
import com.microblink.blinkcard.directApi.RecognizerRunner;
29+
import com.microblink.blinkcard.hardware.orientation.Orientation;
30+
import com.microblink.blinkcard.metadata.MetadataCallbacks;
31+
import com.microblink.blinkcard.metadata.recognition.FirstSideRecognitionCallback;
32+
import com.microblink.blinkcard.recognition.RecognitionSuccessType;
33+
import com.microblink.blinkcard.view.recognition.ScanResultListener;
2334

2435
import com.microblink.blinkcard.uisettings.UISettings;
2536
import com.microblink.blinkcard.reactnative.recognizers.RecognizerSerializers;
@@ -37,6 +48,8 @@ public class MicroblinkModule extends ReactContextBaseJavaModule {
3748
private static final String PARAM_LICENSE_KEY = "licenseKey";
3849
private static final String PARAM_LICENSEE = "licensee";
3950
private static final String PARAM_SHOW_TRIAL_LICENSE_WARNING = "showTrialLicenseKeyWarning";
51+
private static final String PARAM_FRONT_IMAGE = "frontImage";
52+
private static final String PARAM_BACK_IMAGE = "backImage";
4053

4154
/**
4255
* Request code for scan activity
@@ -45,6 +58,8 @@ public class MicroblinkModule extends ReactContextBaseJavaModule {
4558

4659
private Promise mScanPromise;
4760
private RecognizerBundle mRecognizerBundle;
61+
private RecognizerRunner mRecognizerRunner;
62+
private boolean mFirstSideScanned = false;
4863

4964
public MicroblinkModule(ReactApplicationContext reactContext) {
5065
super(reactContext);
@@ -71,6 +86,70 @@ public void scanWithCamera(ReadableMap jsonOverlaySettings, ReadableMap jsonReco
7186
ActivityRunner.startActivityForResult(getCurrentActivity(), REQUEST_CODE, overlaySettings);
7287
}
7388

89+
@ReactMethod
90+
private void scanWithDirectApi(ReadableMap jsonRecognizerCollection, ReadableMap frontImage, ReadableMap backImage, ReadableMap license, Promise promise) {
91+
//DirectAPI processing
92+
mScanPromise = promise;
93+
prepareScanning(license, promise);
94+
95+
ScanResultListener mScanResultListenerBackSide = new ScanResultListener() {
96+
@Override
97+
public void onScanningDone(@NonNull RecognitionSuccessType recognitionSuccessType) {
98+
mFirstSideScanned = false;
99+
handleDirectApiResult(recognitionSuccessType);
100+
}
101+
@Override
102+
public void onUnrecoverableError(@NonNull Throwable throwable) {
103+
promise.reject(throwable);
104+
}
105+
};
106+
107+
FirstSideRecognitionCallback mFirstSideRecognitionCallback = new FirstSideRecognitionCallback() {
108+
@Override
109+
public void onFirstSideRecognitionFinished() {
110+
mFirstSideScanned = true;
111+
}
112+
};
113+
114+
ScanResultListener mScanResultListenerFrontSide = new ScanResultListener() {
115+
@Override
116+
public void onScanningDone(@NonNull RecognitionSuccessType recognitionSuccessType) {
117+
if (mFirstSideScanned == true) {
118+
//multiside recognizer used
119+
try {
120+
if (backImage != null) {
121+
processImage(backImage.getString(PARAM_BACK_IMAGE), mScanResultListenerBackSide);
122+
} else if (recognitionSuccessType != RecognitionSuccessType.UNSUCCESSFUL) {
123+
handleDirectApiResult(recognitionSuccessType);
124+
} else {
125+
handleDirectApiError("Could not extract the information from the front side and back side is empty!", promise);
126+
}
127+
} catch (Exception e) {
128+
throw new RuntimeException(e);
129+
}
130+
} else if (mFirstSideScanned == false && recognitionSuccessType != RecognitionSuccessType.UNSUCCESSFUL){
131+
//singleside recognizer used
132+
handleDirectApiResult(recognitionSuccessType);
133+
} else {
134+
mFirstSideScanned = false;
135+
handleDirectApiError("Could not extract the information with DirectAPI!", promise);
136+
}
137+
}
138+
@Override
139+
public void onUnrecoverableError(@NonNull Throwable throwable) {
140+
promise.reject(throwable);
141+
}
142+
};
143+
144+
setupRecognizerRunner(jsonRecognizerCollection, mFirstSideRecognitionCallback, promise);
145+
146+
if (frontImage != null) {
147+
processImage(frontImage.getString(PARAM_FRONT_IMAGE), mScanResultListenerFrontSide);
148+
} else {
149+
handleDirectApiError("The provided image for the 'frontImage' parameter is empty!", promise);
150+
}
151+
}
152+
74153
private void prepareScanning(ReadableMap license, Promise promise) {
75154
Activity currentActivity = getCurrentActivity();
76155
if (currentActivity == null) {
@@ -96,6 +175,64 @@ private void prepareScanning(ReadableMap license, Promise promise) {
96175
setLicense(licenseKey, licensee, showTrialLicenseKeyWarning);
97176
}
98177

178+
private void setupRecognizerRunner(ReadableMap jsonRecognizerCollection, FirstSideRecognitionCallback mFirstSideRecognitionCallback, Promise promise) {
179+
if (mRecognizerRunner != null) {
180+
mRecognizerRunner.terminate();
181+
}
182+
183+
mRecognizerBundle = RecognizerSerializers.INSTANCE.deserializeRecognizerCollection(jsonRecognizerCollection);
184+
185+
try {
186+
mRecognizerRunner = RecognizerRunner.getSingletonInstance();
187+
} catch (Exception e) {
188+
handleDirectApiError("DirectAPI not support: " + e.getMessage(), promise);
189+
}
190+
191+
MetadataCallbacks metadataCallbacks = new MetadataCallbacks();
192+
metadataCallbacks.setFirstSideRecognitionCallback(mFirstSideRecognitionCallback);
193+
mRecognizerRunner.setMetadataCallbacks(metadataCallbacks);
194+
mRecognizerRunner.initialize(getCurrentActivity(), mRecognizerBundle, new DirectApiErrorListener() {
195+
@Override
196+
public void onRecognizerError(@NonNull Throwable throwable) {
197+
handleDirectApiError("Failed to initialize recognizer with DirectAPI: " + throwable.getMessage(), promise);
198+
}
199+
});
200+
}
201+
202+
private void processImage(String base64Image, ScanResultListener scanResultListener) {
203+
Bitmap image = base64ToBitmap(base64Image);
204+
if (image != null) {
205+
mRecognizerRunner.recognizeBitmap(
206+
base64ToBitmap(base64Image),
207+
Orientation.ORIENTATION_LANDSCAPE_RIGHT,
208+
scanResultListener
209+
);
210+
} else {
211+
handleDirectApiError("Could not decode the Base64 image!", mScanPromise);
212+
}
213+
}
214+
215+
private void handleDirectApiResult(RecognitionSuccessType recognitionSuccessType) {
216+
if (recognitionSuccessType != RecognitionSuccessType.UNSUCCESSFUL) {
217+
WritableArray resultList = RecognizerSerializers.INSTANCE.serializeRecognizerResults(mRecognizerBundle.getRecognizers());
218+
mScanPromise.resolve(resultList);
219+
} else {
220+
handleDirectApiError("Unexpected error with DirectAPI scanning", mScanPromise);
221+
}
222+
}
223+
224+
private void handleDirectApiError(String errorMessage, Promise promise) {
225+
promise.reject(errorMessage);
226+
if (mRecognizerRunner != null) {
227+
mRecognizerRunner.resetRecognitionState(true);
228+
}
229+
}
230+
231+
private Bitmap base64ToBitmap(String base64String) {
232+
byte[] decodedBytes = Base64.decode(base64String, Base64.DEFAULT);
233+
return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
234+
}
235+
99236
private void setLicense( String licenseKey, String licensee, Boolean showTrialLicenseKeyWarning ) {
100237
if (showTrialLicenseKeyWarning != null) {
101238
MicroblinkSDK.setShowTrialLicenseWarning(showTrialLicenseKeyWarning);

BlinkCard/src/android/src/main/java/com/microblink/blinkcard/reactnative/recognizers/serialization/BlinkCardSerializationUtils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,4 @@ public static WritableMap serializeDocumentLivenessCheckResult(DocumentLivenessC
5959
public static MatchLevel deserializeMatchLevel(int value) {
6060
return MatchLevel.values()[value];
6161
}
62-
6362
}

0 commit comments

Comments
 (0)