anyline-ocr-react-native-module 42.2.0 → 43.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +7 -3
- package/android/src/main/AndroidManifest.xml +9 -8
- package/android/src/main/assets/cow_tag_scanner.ale +46 -0
- package/android/src/main/java/com/anyline/reactnative/AnylinePackage.java +1 -2
- package/android/src/main/java/com/anyline/reactnative/AnylineSDKPlugin.java +25 -71
- package/android/src/main/java/com/anyline/reactnative/RotateButtonConfig.java +12 -2
- package/android/src/main/java/com/anyline/reactnative/ScanActivity.java +206 -0
- package/android/src/main/res/drawable/rotate_screen_background.xml +18 -0
- package/android/src/main/res/drawable/rotate_screen_white.png +0 -0
- package/android/src/main/res/layout/activity_scan.xml +27 -0
- package/android/src/main/res/values/anyline_sdk_plugin_strings.xml +2 -28
- package/ios/ALJsonUIConfiguration.h +29 -36
- package/ios/ALJsonUIConfiguration.m +136 -76
- package/ios/ALNFCScanViewController.h +3 -16
- package/ios/ALNFCScanViewController.m +361 -232
- package/ios/ALPluginHelper.h +19 -62
- package/ios/ALPluginHelper.m +208 -529
- package/ios/ALPluginScanViewController.h +5 -32
- package/ios/ALPluginScanViewController.m +339 -287
- package/ios/AnylineReact.podspec +4 -3
- package/ios/AnylineSDKPlugin.m +39 -82
- package/package.json +8 -8
- package/android/.gradle/6.7/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/6.7/fileChanges/last-build.bin +0 -0
- package/android/.gradle/6.7/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/6.7/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/6.7/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/.gradle/checksums/checksums.lock +0 -0
- package/android/.gradle/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/configuration-cache/gc.properties +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.idea/compiler.xml +0 -6
- package/android/.idea/gradle.xml +0 -18
- package/android/.idea/jarRepositories.xml +0 -35
- package/android/.idea/misc.xml +0 -10
- package/android/.idea/vcs.xml +0 -6
- package/android/.settings/org.eclipse.buildship.core.prefs +0 -13
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/local.properties +0 -8
- package/android/src/main/java/com/anyline/reactnative/Anyline4Activity.java +0 -621
- package/android/src/main/java/com/anyline/reactnative/AnylineBaseActivity.java +0 -267
- package/android/src/main/java/com/anyline/reactnative/AnylinePluginHelper.java +0 -324
- package/android/src/main/java/com/anyline/reactnative/AnylineUIConfig.java +0 -88
- package/android/src/main/java/com/anyline/reactnative/Document4Activity.java +0 -595
- package/android/src/main/res/layout/activity_scan_document.xml +0 -63
- package/android/src/main/res/layout/activity_scan_scanview.xml +0 -52
- package/android/src/main/res/values/com_crashlytics_export_strings.xml +0 -10
|
@@ -1,55 +1,76 @@
|
|
|
1
|
-
//
|
|
2
|
-
// ALNFCScanViewController.m
|
|
3
|
-
// AnylineExamples
|
|
4
|
-
//
|
|
5
|
-
// Created by Angela Brett on 08.10.19.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#import "ALNFCScanViewController.h"
|
|
9
1
|
#import <Anyline/Anyline.h>
|
|
2
|
+
#import "ALNFCScanViewController.h"
|
|
10
3
|
#import "ALPluginHelper.h"
|
|
11
4
|
|
|
12
5
|
|
|
13
6
|
API_AVAILABLE(ios(13.0))
|
|
14
|
-
@interface ALNFCScanViewController () <ALNFCDetectorDelegate,
|
|
7
|
+
@interface ALNFCScanViewController () <ALNFCDetectorDelegate, ALScanPluginDelegate, ALScanViewDelegate>
|
|
8
|
+
|
|
9
|
+
@property (nonatomic, strong) ALPluginCallback callback;
|
|
10
|
+
|
|
11
|
+
@property (nonatomic, strong) NSDictionary *config;
|
|
12
|
+
|
|
13
|
+
@property (nonatomic, copy) NSString *licenseKey;
|
|
14
|
+
|
|
15
|
+
@property (nonatomic, strong) ALScanView *scanView;
|
|
16
|
+
|
|
17
|
+
@property (nonatomic, strong) ALScanViewPlugin *mrzScanViewPlugin;
|
|
15
18
|
|
|
16
|
-
@property (nonatomic, strong) ALIDScanViewPlugin *mrzScanViewPlugin;
|
|
17
|
-
@property (nonatomic, strong) ALIDScanPlugin *mrzScanPlugin;
|
|
18
|
-
@property (nullable, nonatomic, strong) ALScanView *scanView;
|
|
19
19
|
@property (nonatomic, strong) ALNFCDetector *nfcDetector;
|
|
20
|
-
@property (nullable, nonatomic, strong) UIView *hintView;
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
// the result from NFC scanning is retained while NFC reading is initiated, and the results aggregated later
|
|
22
|
+
@property (nonatomic, strong) NSMutableDictionary *resultDict;
|
|
23
|
+
|
|
24
|
+
// TODO: make sure the following use the `options` group in the uiConfig
|
|
25
|
+
// BOOL showingLabel
|
|
26
|
+
// UILabel scannedLabel
|
|
27
|
+
// ALRoundedView
|
|
28
|
+
// Segment
|
|
29
|
+
@property (nonatomic, strong) ALJSONUIConfiguration *uiConfig;
|
|
30
|
+
|
|
31
|
+
@property (nonatomic, strong) UIView *hintView;
|
|
32
|
+
|
|
33
|
+
@property (nonatomic, strong) UILabel *hintViewLabel;
|
|
23
34
|
|
|
35
|
+
@property (nonatomic, strong) NSLayoutConstraint *labelHorizontalOffsetConstraint;
|
|
36
|
+
|
|
37
|
+
@property (nonatomic, strong) NSLayoutConstraint *labelVerticalOffsetConstraint;
|
|
38
|
+
|
|
39
|
+
@property (nonatomic, strong) UIButton *doneButton;
|
|
40
|
+
|
|
41
|
+
// keep the last values we read from the MRZ so we can retry reading NFC
|
|
42
|
+
// if NFC failed for reasons other than getting these details wrong
|
|
43
|
+
@property (nonatomic, copy) NSString *passportNumberForNFC;
|
|
44
|
+
|
|
45
|
+
@property (nonatomic, strong) NSDate *dateOfBirth;
|
|
46
|
+
|
|
47
|
+
@property (nonatomic, strong) NSDate *dateOfExpiry;
|
|
24
48
|
|
|
25
|
-
//keep the last values we read from the MRZ so we can retry reading NFC if NFC failed for reasons other than getting these details wrong
|
|
26
|
-
@property NSString *passportNumberForNFC;
|
|
27
|
-
@property NSDate *dateOfBirth;
|
|
28
|
-
@property NSDate *dateOfExpiry;
|
|
29
|
-
@property (nonatomic, strong) NSDictionary *anylineConfig;
|
|
30
|
-
@property (nonatomic, weak) id<ALPluginScanViewControllerDelegate> delegate;
|
|
31
|
-
@property (nonatomic, strong) NSString *licensekey;
|
|
32
|
-
@property (nonatomic, strong) ALJsonUIConfiguration *uiConfig;
|
|
33
|
-
@property (nonatomic, strong) NSMutableDictionary *anylineResult;
|
|
34
49
|
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *detectedBarcodes;
|
|
35
50
|
|
|
51
|
+
// not used
|
|
52
|
+
@property (nonatomic, strong) NSString *cropAndTransformErrorMessage;
|
|
53
|
+
|
|
54
|
+
// JPEG compression quality 0-100
|
|
55
|
+
@property (nonatomic, assign) NSUInteger quality;
|
|
56
|
+
|
|
57
|
+
|
|
36
58
|
@end
|
|
37
59
|
|
|
60
|
+
|
|
38
61
|
@implementation ALNFCScanViewController
|
|
39
62
|
|
|
40
63
|
- (instancetype)initWithLicensekey:(NSString *)licensekey
|
|
41
64
|
configuration:(NSDictionary *)anylineConfig
|
|
42
|
-
uiConfig:(
|
|
43
|
-
|
|
44
|
-
self = [super init]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
_anylineConfig = anylineConfig;
|
|
65
|
+
uiConfig:(ALJSONUIConfiguration *)uiConfig
|
|
66
|
+
finished:(ALPluginCallback)callback {
|
|
67
|
+
if (self = [super init]) {
|
|
68
|
+
_licenseKey = licensekey;
|
|
69
|
+
_callback = callback;
|
|
70
|
+
_config = anylineConfig;
|
|
49
71
|
_uiConfig = uiConfig;
|
|
50
72
|
|
|
51
|
-
self.quality =
|
|
52
|
-
self.nativeBarcodeEnabled = NO;
|
|
73
|
+
self.quality = 90;
|
|
53
74
|
self.cropAndTransformErrorMessage = @"";
|
|
54
75
|
}
|
|
55
76
|
return self;
|
|
@@ -57,271 +78,379 @@ API_AVAILABLE(ios(13.0))
|
|
|
57
78
|
|
|
58
79
|
- (void)viewDidLoad {
|
|
59
80
|
[super viewDidLoad];
|
|
60
|
-
|
|
61
|
-
self.anylineResult = [[NSMutableDictionary alloc] init];
|
|
62
|
-
self.detectedBarcodes = [NSMutableArray array];
|
|
63
|
-
|
|
64
|
-
self.doneButton = [ALPluginHelper createButtonForViewController:self config:self.uiConfig];
|
|
65
|
-
|
|
66
|
-
self.title = @"Passport NFC";
|
|
67
|
-
CGFloat hintMargin = 7;
|
|
68
|
-
// Set the background color to black to have a nicer transition
|
|
69
|
-
self.view.backgroundColor = [UIColor blackColor];
|
|
70
|
-
|
|
71
|
-
UILabel * hintViewLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
72
|
-
UIView * hintView = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
73
|
-
hintViewLabel.text = @"Scan MRZ";
|
|
74
|
-
[hintViewLabel sizeToFit];
|
|
75
|
-
[hintView addSubview:hintViewLabel];
|
|
76
|
-
hintView.frame = UIEdgeInsetsInsetRect(hintViewLabel.frame, UIEdgeInsetsMake(-hintMargin, -hintMargin, -hintMargin, -hintMargin));
|
|
77
|
-
hintView.center = CGPointMake(self.view.center.x, 0);
|
|
78
|
-
hintViewLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
79
|
-
[hintViewLabel.centerYAnchor constraintEqualToAnchor:hintView.centerYAnchor constant:0].active = YES;
|
|
80
|
-
[hintViewLabel.centerXAnchor constraintEqualToAnchor:hintView.centerXAnchor constant:0].active = YES;
|
|
81
|
-
hintView.layer.cornerRadius = 8;
|
|
82
|
-
hintView.layer.masksToBounds = true;
|
|
83
|
-
hintView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
|
84
|
-
hintViewLabel.textColor = [UIColor whiteColor];
|
|
85
|
-
self.hintView = hintView;
|
|
86
|
-
[self.view addSubview:hintView];
|
|
87
|
-
|
|
88
|
-
// Initializing the scan view. It's a UIView subclass. We set the frame to fill the whole screen
|
|
89
|
-
CGRect frame = [self scanViewFrame];
|
|
90
|
-
|
|
91
|
-
NSDictionary *mrzConfigDict = [self.anylineConfig valueForKeyPath:@"viewPlugin.plugin.nfcPlugin.mrzConfig"];
|
|
92
|
-
|
|
93
|
-
ALMRZConfig *mrzConfig;
|
|
94
|
-
if (mrzConfigDict) {
|
|
95
|
-
mrzConfig = [[ALMRZConfig alloc] initWithJsonDictionary:mrzConfigDict];
|
|
96
|
-
} else {
|
|
97
|
-
mrzConfig = [[ALMRZConfig alloc] init];
|
|
98
|
-
//we want to be quite confident of these fields to ensure we can read the NFC with them
|
|
99
|
-
ALMRZFieldConfidences *fieldConfidences = [[ALMRZFieldConfidences alloc] init];
|
|
100
|
-
fieldConfidences.documentNumber = 90;
|
|
101
|
-
fieldConfidences.dateOfBirth = 90;
|
|
102
|
-
fieldConfidences.dateOfExpiry = 90;
|
|
103
|
-
mrzConfig.idFieldConfidences = fieldConfidences;
|
|
104
|
-
|
|
105
|
-
//Create fieldScanOptions to configure individual scannable fields
|
|
106
|
-
ALMRZFieldScanOptions *scanOptions = [[ALMRZFieldScanOptions alloc] init];
|
|
107
|
-
scanOptions.vizAddress = ALDefault;
|
|
108
|
-
scanOptions.vizDateOfIssue = ALDefault;
|
|
109
|
-
scanOptions.vizSurname = ALDefault;
|
|
110
|
-
scanOptions.vizGivenNames = ALDefault;
|
|
111
|
-
scanOptions.vizDateOfBirth = ALDefault;
|
|
112
|
-
scanOptions.vizDateOfExpiry = ALDefault;
|
|
113
|
-
|
|
114
|
-
//Set scanOptions for MRZConfig
|
|
115
|
-
mrzConfig.idFieldScanOptions = scanOptions;
|
|
116
|
-
}
|
|
117
81
|
|
|
82
|
+
// NOTE: NFCDetector can throw an exception if the AnylineSDK isn't initialized first
|
|
83
|
+
// as it will check the scope value of the license
|
|
118
84
|
NSError *error = nil;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
[AnylineSDK setupWithLicenseKey:self.licensekey error:&error];
|
|
122
|
-
if (error) {
|
|
123
|
-
[self.delegate pluginScanViewController:nil
|
|
124
|
-
didStopScanning:self
|
|
125
|
-
error:error];
|
|
85
|
+
[AnylineSDK setupWithLicenseKey:self.licenseKey error:&error];
|
|
86
|
+
if ([self showErrorAlertIfNeeded:error]) {
|
|
126
87
|
return;
|
|
127
88
|
}
|
|
128
|
-
|
|
129
|
-
// the MRZConfig (which will configure the scan Plugin for MRZ scanning), and an error
|
|
130
|
-
self.mrzScanPlugin = [[ALIDScanPlugin alloc] initWithPluginID:@"ModuleID" delegate:self idConfig:mrzConfig error:&error];
|
|
89
|
+
|
|
131
90
|
if (@available(iOS 13.0, *)) {
|
|
132
|
-
self.nfcDetector=[
|
|
91
|
+
self.nfcDetector = [self.class getNFCDetectorWithDelegate:self error:&error];
|
|
133
92
|
} else {
|
|
134
|
-
|
|
93
|
+
error = [ALPluginHelper errorWithMessage:@"iOS 13.0 or newer is required to scan with MRZ / NFC."];
|
|
135
94
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
self.mrzScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.mrzScanPlugin];
|
|
141
|
-
NSAssert(self.mrzScanViewPlugin, @"Setup Error: %@", error.debugDescription);
|
|
142
|
-
[self.mrzScanViewPlugin addScanViewPluginDelegate:self];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.mrzScanViewPlugin];
|
|
146
|
-
|
|
147
|
-
self.scanView.flashButtonConfig.flashAlignment = ALFlashAlignmentTopLeft;
|
|
148
|
-
|
|
149
|
-
// After setup is complete we add the scanView to the view of this view controller
|
|
95
|
+
if ([self showErrorAlertIfNeeded:error]) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
150
99
|
[self.view addSubview:self.scanView];
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
100
|
+
|
|
101
|
+
self.resultDict = [[NSMutableDictionary alloc] init];
|
|
102
|
+
self.detectedBarcodes = [NSMutableArray array];
|
|
103
|
+
|
|
104
|
+
self.scanView = [ALScanViewFactory withJSONDictionary:self.config delegate:self error:&error];
|
|
105
|
+
self.scanView.delegate = self;
|
|
106
|
+
|
|
107
|
+
[self configureMRZPlugin];
|
|
108
|
+
|
|
109
|
+
if ([self showErrorAlertIfNeeded:error]) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
self.mrzScanViewPlugin = (ALScanViewPlugin *)self.scanView.scanViewPlugin;
|
|
114
|
+
|
|
115
|
+
self.scanView.supportedNativeBarcodeFormats = self.uiConfig.nativeBarcodeFormats;
|
|
116
|
+
self.scanView.delegate = self;
|
|
117
|
+
self.detectedBarcodes = [NSMutableArray array];
|
|
157
118
|
|
|
158
|
-
|
|
119
|
+
[self.view addSubview:self.scanView];
|
|
120
|
+
|
|
121
|
+
self.scanView.translatesAutoresizingMaskIntoConstraints = false;
|
|
122
|
+
[self.scanView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
|
|
123
|
+
[self.scanView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
|
|
124
|
+
[self.scanView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES;
|
|
125
|
+
[self.scanView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
|
|
126
|
+
|
|
159
127
|
[self.scanView startCamera];
|
|
160
|
-
}
|
|
161
128
|
|
|
162
|
-
|
|
163
|
-
This method will be called once the view controller and its subviews have appeared on screen
|
|
164
|
-
*/
|
|
165
|
-
-(void)viewDidAppear:(BOOL)animated {
|
|
166
|
-
[super viewDidAppear:animated];
|
|
129
|
+
[self setupHintView];
|
|
167
130
|
|
|
168
|
-
|
|
169
|
-
// so that we can later use it to restart the scanning process.
|
|
170
|
-
[self startMRZScanning];
|
|
131
|
+
self.doneButton = [ALPluginHelper createButtonForViewController:self config:self.uiConfig];
|
|
171
132
|
}
|
|
172
133
|
|
|
173
|
-
- (void)
|
|
174
|
-
|
|
134
|
+
- (void)viewDidAppear:(BOOL)animated {
|
|
135
|
+
[super viewDidAppear:animated];
|
|
136
|
+
|
|
137
|
+
NSError *error;
|
|
138
|
+
[self startMRZScanning:&error];
|
|
139
|
+
[self showErrorAlertIfNeeded:error];
|
|
175
140
|
}
|
|
176
141
|
|
|
177
|
-
- (void)
|
|
142
|
+
- (void)viewDidDisappear:(BOOL)animated {
|
|
178
143
|
[self stopMRZScanning];
|
|
144
|
+
[super viewDidDisappear:animated];
|
|
179
145
|
}
|
|
180
146
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
This method is used to tell Anyline to start scanning. It gets called in
|
|
184
|
-
viewDidAppear to start scanning the moment the view appears. Once a result
|
|
185
|
-
is found scanning will stop automatically (you can change this behaviour
|
|
186
|
-
with cancelOnResult:). When the user dismisses self.identificationView this
|
|
187
|
-
method will get called again.
|
|
188
|
-
*/
|
|
189
|
-
- (void)startMRZScanning {
|
|
190
|
-
[self startPlugin:self.mrzScanViewPlugin];
|
|
147
|
+
- (void)startMRZScanning:(NSError **)error {
|
|
148
|
+
[self.mrzScanViewPlugin startWithError:error];
|
|
191
149
|
self.hintView.hidden = NO;
|
|
192
150
|
}
|
|
193
151
|
|
|
194
|
-
/*
|
|
195
|
-
Cancel scanning to allow the module to clean up
|
|
196
|
-
*/
|
|
197
152
|
- (void)stopMRZScanning {
|
|
198
|
-
[self.mrzScanViewPlugin
|
|
153
|
+
[self.mrzScanViewPlugin stop];
|
|
199
154
|
self.hintView.hidden = YES;
|
|
200
155
|
}
|
|
201
156
|
|
|
202
|
-
- (void)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
157
|
+
- (void)configureMRZPlugin {
|
|
158
|
+
|
|
159
|
+
ALScanViewPlugin *scanViewPlugin = (ALScanViewPlugin *)self.scanView.scanViewPlugin;
|
|
160
|
+
if (![scanViewPlugin isKindOfClass:ALScanViewPlugin.class]) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
ALCutoutConfig *cutoutConfig = scanViewPlugin.scanViewPluginConfig.cutoutConfig;
|
|
165
|
+
ALScanFeedbackConfig *scanFeedbackConfig = scanViewPlugin.scanViewPluginConfig.scanFeedbackConfig;
|
|
166
|
+
|
|
167
|
+
ALPluginConfig *pluginConfig = scanViewPlugin.scanPlugin.scanPluginConfig.pluginConfig;
|
|
168
|
+
ALMrzConfig *mrzConfig = pluginConfig.mrzConfig;
|
|
169
|
+
|
|
170
|
+
// a bit lengthy but this is how you properly change the config (mrzFieldScanOptions and mrzMinFieldConfidences)
|
|
171
|
+
// taking into account the readonly config fields
|
|
172
|
+
mrzConfig.mrzFieldScanOptions = [[ALMrzFieldScanOptions alloc] init];
|
|
173
|
+
|
|
174
|
+
mrzConfig.mrzFieldScanOptions.vizAddress = ALMrzScanOption.mrzScanOptionDefault;
|
|
175
|
+
mrzConfig.mrzFieldScanOptions.vizDateOfIssue = ALMrzScanOption.mrzScanOptionDefault;
|
|
176
|
+
mrzConfig.mrzFieldScanOptions.vizSurname = ALMrzScanOption.mrzScanOptionDefault;
|
|
177
|
+
mrzConfig.mrzFieldScanOptions.vizGivenNames = ALMrzScanOption.mrzScanOptionDefault;
|
|
178
|
+
mrzConfig.mrzFieldScanOptions.vizDateOfBirth = ALMrzScanOption.mrzScanOptionDefault;
|
|
179
|
+
mrzConfig.mrzFieldScanOptions.vizDateOfExpiry = ALMrzScanOption.mrzScanOptionDefault;
|
|
180
|
+
|
|
181
|
+
mrzConfig.mrzMinFieldConfidences = [[ALMrzMinFieldConfidences alloc] init];
|
|
182
|
+
mrzConfig.mrzMinFieldConfidences.documentNumber = @(90);
|
|
183
|
+
mrzConfig.mrzMinFieldConfidences.dateOfBirth = @(90);
|
|
184
|
+
mrzConfig.mrzMinFieldConfidences.dateOfExpiry = @(90);
|
|
185
|
+
|
|
186
|
+
NSError *error;
|
|
187
|
+
ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig withScanPluginConfig:[ALScanPluginConfig withPluginConfig:pluginConfig]
|
|
188
|
+
cutoutConfig:cutoutConfig
|
|
189
|
+
scanFeedbackConfig:scanFeedbackConfig];
|
|
190
|
+
ALScanViewPlugin *updatedScanViewPlugin = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig error:&error];
|
|
191
|
+
[self.scanView setScanViewPlugin:updatedScanViewPlugin error:&error];
|
|
192
|
+
|
|
193
|
+
// the delegate binding was lost when you recreated the ScanPlugin it so you have to bring it back here
|
|
194
|
+
scanViewPlugin = (ALScanViewPlugin *)self.scanView.scanViewPlugin;
|
|
195
|
+
scanViewPlugin.scanPlugin.delegate = self;
|
|
207
196
|
}
|
|
208
197
|
|
|
209
|
-
|
|
198
|
+
// MARK: - ALIDPluginDelegate
|
|
199
|
+
|
|
200
|
+
- (void)scanPlugin:(ALScanPlugin *)scanPlugin resultReceived:(ALScanResult *)scanResult {
|
|
201
|
+
|
|
202
|
+
CGFloat compressionQuality = self.quality / 100.0f;
|
|
203
|
+
|
|
204
|
+
// ACO just a failsafe for when cancelOnResult is not true
|
|
205
|
+
[self stopMRZScanning];
|
|
206
|
+
|
|
207
|
+
self.resultDict = [NSMutableDictionary dictionaryWithDictionary:scanResult.resultDictionary];
|
|
208
|
+
|
|
209
|
+
NSString *imagePath = [ALPluginHelper saveImageToFileSystem:scanResult.croppedImage
|
|
210
|
+
compressionQuality:compressionQuality];
|
|
211
|
+
self.resultDict[@"imagePath"] = imagePath;
|
|
212
|
+
|
|
213
|
+
imagePath = [ALPluginHelper saveImageToFileSystem:scanResult.fullSizeImage
|
|
214
|
+
compressionQuality:compressionQuality];
|
|
215
|
+
|
|
216
|
+
self.resultDict[@"fullImagePath"] = imagePath;
|
|
217
|
+
|
|
218
|
+
ALMrzResult *MRZResult = scanResult.pluginResult.mrzResult;
|
|
219
|
+
NSString *passportNumber = [MRZResult.documentNumber stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
220
|
+
|
|
221
|
+
self.dateOfBirth = [ALPluginHelper formattedStringToDate:MRZResult.dateOfBirthObject];
|
|
222
|
+
|
|
223
|
+
self.dateOfExpiry = [ALPluginHelper formattedStringToDate:MRZResult.dateOfExpiryObject];
|
|
210
224
|
|
|
211
|
-
/*
|
|
212
|
-
This is the main delegate method Anyline uses to report its results from an ID plugin such as MRZ
|
|
213
|
-
*/
|
|
214
|
-
- (void)anylineIDScanPlugin:(ALIDScanPlugin *)anylineIDScanPlugin didFindResult:(ALIDResult *)scanResult {
|
|
215
|
-
ALMRZIdentification *identification = (ALMRZIdentification*)scanResult.result;
|
|
216
|
-
|
|
217
|
-
self.anylineResult = [[ALPluginHelper dictionaryForIDResult:scanResult detectedBarcodes:self.detectedBarcodes outline:self.scanView.scanViewPlugin.outline quality:_quality] mutableCopy];
|
|
218
|
-
|
|
219
|
-
NSString *passportNumber = [[identification documentNumber] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
220
|
-
NSDate *dateOfBirth = [identification dateOfBirthObject];
|
|
221
|
-
NSDate *dateOfExpiry = [identification dateOfExpiryObject];
|
|
222
|
-
//The passport number passed to the NFC chip must have a trailing < if there is one in the MRZ string.
|
|
223
|
-
NSString *mrzString = [identification mrzString];
|
|
224
225
|
NSMutableString *passportNumberForNFC = [passportNumber mutableCopy];
|
|
225
|
-
NSRange passportNumberRange = [mrzString rangeOfString:passportNumber];
|
|
226
|
+
NSRange passportNumberRange = [MRZResult.mrzString rangeOfString:passportNumber];
|
|
226
227
|
if (passportNumberRange.location != NSNotFound) {
|
|
227
|
-
if ([mrzString characterAtIndex:NSMaxRange(passportNumberRange)] == '<') {
|
|
228
|
+
if ([MRZResult.mrzString characterAtIndex:NSMaxRange(passportNumberRange)] == '<') {
|
|
228
229
|
[passportNumberForNFC appendString:@"<"];
|
|
229
230
|
}
|
|
230
231
|
}
|
|
231
|
-
|
|
232
|
+
|
|
232
233
|
self.passportNumberForNFC = passportNumberForNFC;
|
|
233
|
-
|
|
234
|
-
self.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
234
|
+
|
|
235
|
+
[self.nfcDetector startNfcDetectionWithPassportNumber:self.passportNumberForNFC
|
|
236
|
+
dateOfBirth:self.dateOfBirth
|
|
237
|
+
expirationDate:self.dateOfExpiry];
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
- (void)handleResult:(id _Nullable)resultObj {
|
|
241
|
+
|
|
242
|
+
NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionaryWithDictionary:resultObj];
|
|
243
|
+
if (self.detectedBarcodes.count) {
|
|
244
|
+
resultDictionary[@"nativeBarcodesDetected"] = self.detectedBarcodes;
|
|
245
|
+
}
|
|
241
246
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
NSObject<ALScanViewPluginBase> *scanViewPluginBase = self.scanView.scanViewPlugin;
|
|
248
|
+
// TODO: handle this for composites: cancelOnResult = true? dismiss
|
|
249
|
+
if ([scanViewPluginBase isKindOfClass:ALScanViewPlugin.class]) {
|
|
250
|
+
ALScanViewPlugin *scanViewPlugin = (ALScanViewPlugin *)scanViewPluginBase;
|
|
251
|
+
BOOL cancelOnResult = scanViewPlugin.scanPlugin.scanPluginConfig.cancelOnResult;
|
|
252
|
+
if (cancelOnResult) {
|
|
253
|
+
[self dismissViewControllerAnimated:YES completion:nil];
|
|
254
|
+
}
|
|
255
|
+
} else if ([scanViewPluginBase isKindOfClass:ALViewPluginComposite.class]) {
|
|
256
|
+
// for composites, the cancelOnResult values for each child don't matter
|
|
257
|
+
[self dismissViewControllerAnimated:YES completion:nil];
|
|
258
|
+
}
|
|
259
|
+
self.callback(resultDictionary, nil);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
- (void)doneButtonPressed:(id)sender {
|
|
263
|
+
[self stopMRZScanning];
|
|
264
|
+
|
|
265
|
+
__weak __block typeof(self) weakSelf = self;
|
|
266
|
+
[self dismissViewControllerAnimated:YES completion:^{
|
|
267
|
+
weakSelf.callback(nil, @"Canceled");
|
|
268
|
+
}];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// MARK: - ALNFCDetectorDelegate
|
|
272
|
+
|
|
273
|
+
- (void)nfcSucceededWithResult:(ALNFCResult * _Nonnull)nfcResult API_AVAILABLE(ios(13.0)) {
|
|
274
|
+
|
|
275
|
+
// DataGroup1
|
|
276
|
+
NSMutableDictionary *dictResultDataGroup1 = [[NSMutableDictionary alloc] init];
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
[dictResultDataGroup1 setValue:[ALPluginHelper stringForDate:nfcResult.dataGroup1.dateOfBirth]
|
|
280
|
+
forKey:@"dateOfBirth"];
|
|
281
|
+
[dictResultDataGroup1 setValue:[ALPluginHelper stringForDate:nfcResult.dataGroup1.dateOfExpiry]
|
|
282
|
+
forKey:@"dateOfExpiry"];
|
|
283
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.documentNumber forKey:@"documentNumber"];
|
|
284
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.documentType forKey:@"documentType"];
|
|
285
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.firstName forKey:@"firstName"];
|
|
286
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.gender forKey:@"gender"];
|
|
287
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.issuingStateCode forKey:@"issuingStateCode"];
|
|
288
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.lastName forKey:@"lastName"];
|
|
289
|
+
[dictResultDataGroup1 setValue:nfcResult.dataGroup1.nationality forKey:@"nationality"];
|
|
290
|
+
|
|
291
|
+
[self.resultDict setObject:dictResultDataGroup1 forKey:@"dataGroup1"];
|
|
292
|
+
|
|
293
|
+
// DataGroup2
|
|
294
|
+
// ACO: we don't put the path into a separate 'dataGroup' category for the wrapper
|
|
295
|
+
NSString *imagePath = [ALPluginHelper saveImageToFileSystem:nfcResult.dataGroup2.faceImage
|
|
296
|
+
compressionQuality:self.quality / (CGFloat)100.0f];
|
|
297
|
+
if (imagePath) {
|
|
298
|
+
[self.resultDict setValue:imagePath forKey:@"imagePath"];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// SOD (Passport metadata)
|
|
302
|
+
NSMutableDictionary *dictResultSOD = [[NSMutableDictionary alloc] init];
|
|
303
|
+
|
|
304
|
+
[dictResultSOD setValue:nfcResult.sod.issuerCertificationAuthority forKey:@"issuerCertificationAuthority"];
|
|
305
|
+
[dictResultSOD setValue:nfcResult.sod.issuerCountry forKey:@"issuerCountry"];
|
|
306
|
+
[dictResultSOD setValue:nfcResult.sod.issuerOrganization forKey:@"issuerOrganization"];
|
|
307
|
+
[dictResultSOD setValue:nfcResult.sod.issuerOrganizationalUnit forKey:@"issuerOrganizationalUnit"];
|
|
308
|
+
[dictResultSOD setValue:nfcResult.sod.ldsHashAlgorithm forKey:@"ldsHashAlgorithm"];
|
|
309
|
+
|
|
310
|
+
// ACO need to disable this because AppStoreConnect produces a warning with this enabled:
|
|
311
|
+
// https://anyline.atlassian.net/browse/SDKY-1509?focusedCommentId=71554
|
|
312
|
+
// [dictResultSOB setValue:nfcResult.sod.signatureAlgorithm forKey:@"signatureAlgorithm"];
|
|
313
|
+
|
|
314
|
+
[dictResultSOD setValue:nfcResult.sod.validFromString forKey:@"validFromString"];
|
|
315
|
+
[dictResultSOD setValue:nfcResult.sod.validUntilString forKey:@"validUntilString"];
|
|
316
|
+
|
|
317
|
+
[self.resultDict setObject:dictResultSOD forKey:@"sod"];
|
|
318
|
+
|
|
319
|
+
__weak __block typeof(self) weakSelf = self;
|
|
245
320
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
246
|
-
[
|
|
247
|
-
[self handleResult:self.anylineResult result:nil];
|
|
321
|
+
[weakSelf handleResult:weakSelf.resultDict];
|
|
248
322
|
});
|
|
249
323
|
}
|
|
250
324
|
|
|
251
325
|
- (void)nfcFailedWithError:(NSError * _Nonnull)error {
|
|
252
|
-
//In most cases we don't really need to do anything special here since the NFC UI already shows that it failed. We shouldn't get ALNFCTagErrorNFCNotSupported either because we check +readingAvailable before even showing NFC, so this is just an example of how else that situation could be handled.
|
|
253
326
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
254
327
|
if (error.code == ALNFCTagErrorNFCNotSupported) {
|
|
255
|
-
[self showAlertWithTitle:@"NFC Not Supported"
|
|
328
|
+
[self showAlertWithTitle:@"NFC Not Supported"
|
|
329
|
+
message:@"NFC passport reading is not supported on this device."];
|
|
256
330
|
}
|
|
257
|
-
if (error.code == ALNFCTagErrorResponseError || //
|
|
258
|
-
error.code == ALNFCTagErrorUnexpectedError) {
|
|
259
|
-
|
|
331
|
+
if (error.code == ALNFCTagErrorResponseError || // MRZ key was likely wrong
|
|
332
|
+
error.code == ALNFCTagErrorUnexpectedError) {
|
|
333
|
+
// can mean the user pressed the 'Cancel' button while scanning, or the phone lost the
|
|
334
|
+
// connection with the NFC chip because it was moved
|
|
335
|
+
[self startMRZScanning:nil]; //run the MRZ scanner so we can try again.
|
|
260
336
|
} else {
|
|
261
|
-
//the MRZ details are correct, but something else went wrong. We can try reading
|
|
262
|
-
|
|
263
|
-
|
|
337
|
+
// the MRZ details are correct, but something else went wrong. We can try reading
|
|
338
|
+
// the NFC chip again without rescanning the MRZ.
|
|
339
|
+
__weak __block typeof(self) weakSelf = self;
|
|
340
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
341
|
+
[weakSelf.nfcDetector startNfcDetectionWithPassportNumber:weakSelf.passportNumberForNFC
|
|
342
|
+
dateOfBirth:weakSelf.dateOfBirth
|
|
343
|
+
expirationDate:weakSelf.dateOfExpiry];
|
|
344
|
+
});
|
|
264
345
|
}
|
|
265
346
|
});
|
|
266
347
|
}
|
|
267
348
|
|
|
349
|
+
// MARK: - ALScanViewDelegate
|
|
268
350
|
|
|
269
|
-
- (
|
|
270
|
-
|
|
271
|
-
|
|
351
|
+
- (void)scanView:(ALScanView *)scanView updatedCutoutWithPluginID:(NSString *)pluginID
|
|
352
|
+
frame:(CGRect)frame {
|
|
353
|
+
|
|
354
|
+
if (CGRectIsEmpty(frame)) {
|
|
355
|
+
return;
|
|
272
356
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
357
|
+
|
|
358
|
+
self.hintView.hidden = NO;
|
|
359
|
+
|
|
360
|
+
CGFloat xOffset = self.uiConfig.labelXPositionOffset;
|
|
361
|
+
CGFloat yOffset = self.uiConfig.labelYPositionOffset;
|
|
362
|
+
|
|
363
|
+
// takes into account that frame reported for a cutout is in relation to
|
|
364
|
+
// its scan view's coordinate system
|
|
365
|
+
yOffset += [self.scanView convertRect:frame toView:self.scanView.superview].origin.y;
|
|
366
|
+
|
|
367
|
+
self.labelHorizontalOffsetConstraint.constant = xOffset;
|
|
368
|
+
self.labelVerticalOffsetConstraint.constant = yOffset;
|
|
280
369
|
}
|
|
281
370
|
|
|
282
|
-
- (void)
|
|
283
|
-
|
|
371
|
+
- (void)scanView:(ALScanView *)scanView didReceiveNativeBarcodeResult:(ALScanResult *)scanResult {
|
|
372
|
+
// for this implementation we just take the last detected (we can show a list of it)
|
|
373
|
+
[self.detectedBarcodes removeAllObjects];
|
|
374
|
+
[self.detectedBarcodes addObject:scanResult.resultDictionary];
|
|
284
375
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
// MARK: - ALNFCDetector
|
|
379
|
+
|
|
380
|
+
+ (ALNFCDetector * _Nullable)getNFCDetectorWithDelegate:(id<ALNFCDetectorDelegate> _Nonnull)delegate
|
|
381
|
+
error:(NSError * _Nullable * _Nullable)error API_AVAILABLE(ios(13.0)) {
|
|
382
|
+
ALNFCDetector *NFCDetector;
|
|
383
|
+
if (![ALNFCDetector readingAvailable]) {
|
|
384
|
+
if (error) {
|
|
385
|
+
*error = [ALPluginHelper errorWithMessage:@"NFC is not available for this device."];
|
|
386
|
+
}
|
|
387
|
+
return nil;
|
|
388
|
+
}
|
|
389
|
+
NFCDetector = [[ALNFCDetector alloc] initWithDelegate:delegate error:error];
|
|
390
|
+
if (!NFCDetector) {
|
|
391
|
+
return nil;
|
|
291
392
|
}
|
|
393
|
+
return NFCDetector;
|
|
292
394
|
}
|
|
395
|
+
|
|
396
|
+
// MARK: - Alerts
|
|
397
|
+
|
|
293
398
|
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
preferredStyle:UIAlertControllerStyleAlert];
|
|
297
|
-
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil];
|
|
298
|
-
[alertController addAction:dismissAction];
|
|
299
|
-
[self.navigationController presentViewController:alertController animated:YES completion:nil];
|
|
399
|
+
[ALPluginHelper showErrorAlertWithTitle:title message:message
|
|
400
|
+
presentingViewController:self.navigationController];
|
|
300
401
|
}
|
|
301
402
|
|
|
302
|
-
- (
|
|
303
|
-
|
|
304
|
-
frame = CGRectMake(frame.origin.x, frame.origin.y + CGRectGetMaxY(self.navigationController.navigationBar.frame), frame.size.width, frame.size.height - CGRectGetMaxY(self.navigationController.navigationBar.frame));
|
|
305
|
-
return frame;
|
|
403
|
+
- (BOOL)showErrorAlertIfNeeded:(NSError *)error {
|
|
404
|
+
return [ALPluginHelper showErrorAlertIfNeeded:error pluginCallback:self.callback];
|
|
306
405
|
}
|
|
307
406
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
407
|
+
// MARK: - User Interface
|
|
408
|
+
|
|
409
|
+
- (void)setupHintView {
|
|
410
|
+
|
|
411
|
+
UIView *hintView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
412
|
+
hintView.layer.cornerRadius = 8;
|
|
413
|
+
hintView.layer.masksToBounds = true;
|
|
414
|
+
hintView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
|
415
|
+
|
|
416
|
+
hintView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
417
|
+
[self.view addSubview:hintView];
|
|
418
|
+
|
|
419
|
+
self.labelHorizontalOffsetConstraint = [hintView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant:0];
|
|
420
|
+
|
|
421
|
+
// at 0, the bottom of the label (container) should be touching the cutout top when the updatedCutoutRect report
|
|
422
|
+
// is made
|
|
423
|
+
self.labelVerticalOffsetConstraint = [hintView.bottomAnchor constraintEqualToAnchor:self.view.topAnchor constant:0];
|
|
424
|
+
|
|
425
|
+
self.labelHorizontalOffsetConstraint.active = YES;
|
|
426
|
+
self.labelVerticalOffsetConstraint.active = YES;
|
|
427
|
+
|
|
428
|
+
UILabel *hintViewLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
429
|
+
|
|
430
|
+
hintViewLabel.text = @"Scan MRZ";
|
|
431
|
+
if (self.uiConfig.labelText.length) {
|
|
432
|
+
hintViewLabel.text = self.uiConfig.labelText;
|
|
316
433
|
}
|
|
317
|
-
self.detectedBarcodes = [NSMutableArray array];
|
|
318
|
-
}
|
|
319
434
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
435
|
+
hintViewLabel.font = [UIFont fontWithName:@"HelveticaNeue"
|
|
436
|
+
size:self.uiConfig.labelSize];
|
|
437
|
+
hintViewLabel.textColor = self.uiConfig.labelColor;
|
|
438
|
+
hintViewLabel.textAlignment = NSTextAlignmentCenter;
|
|
439
|
+
hintViewLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
440
|
+
[hintViewLabel sizeToFit];
|
|
441
|
+
[hintView addSubview:hintViewLabel];
|
|
442
|
+
|
|
443
|
+
[hintViewLabel.centerXAnchor constraintEqualToAnchor:hintView.centerXAnchor].active = YES;
|
|
444
|
+
[hintViewLabel.centerYAnchor constraintEqualToAnchor:hintView.centerYAnchor].active = YES;
|
|
445
|
+
|
|
446
|
+
NSLayoutConstraint *cnst = [hintView.widthAnchor constraintGreaterThanOrEqualToAnchor:hintViewLabel.widthAnchor
|
|
447
|
+
constant:20];
|
|
448
|
+
cnst.active = YES;
|
|
449
|
+
cnst = [hintView.heightAnchor constraintEqualToAnchor:hintViewLabel.heightAnchor constant:20];
|
|
450
|
+
cnst.active = YES;
|
|
451
|
+
|
|
452
|
+
self.hintView = hintView;
|
|
453
|
+
self.hintViewLabel = hintViewLabel;
|
|
325
454
|
}
|
|
326
455
|
|
|
327
456
|
@end
|