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.
Files changed (51) hide show
  1. package/android/build.gradle +7 -3
  2. package/android/src/main/AndroidManifest.xml +9 -8
  3. package/android/src/main/assets/cow_tag_scanner.ale +46 -0
  4. package/android/src/main/java/com/anyline/reactnative/AnylinePackage.java +1 -2
  5. package/android/src/main/java/com/anyline/reactnative/AnylineSDKPlugin.java +25 -71
  6. package/android/src/main/java/com/anyline/reactnative/RotateButtonConfig.java +12 -2
  7. package/android/src/main/java/com/anyline/reactnative/ScanActivity.java +206 -0
  8. package/android/src/main/res/drawable/rotate_screen_background.xml +18 -0
  9. package/android/src/main/res/drawable/rotate_screen_white.png +0 -0
  10. package/android/src/main/res/layout/activity_scan.xml +27 -0
  11. package/android/src/main/res/values/anyline_sdk_plugin_strings.xml +2 -28
  12. package/ios/ALJsonUIConfiguration.h +29 -36
  13. package/ios/ALJsonUIConfiguration.m +136 -76
  14. package/ios/ALNFCScanViewController.h +3 -16
  15. package/ios/ALNFCScanViewController.m +361 -232
  16. package/ios/ALPluginHelper.h +19 -62
  17. package/ios/ALPluginHelper.m +208 -529
  18. package/ios/ALPluginScanViewController.h +5 -32
  19. package/ios/ALPluginScanViewController.m +339 -287
  20. package/ios/AnylineReact.podspec +4 -3
  21. package/ios/AnylineSDKPlugin.m +39 -82
  22. package/package.json +8 -8
  23. package/android/.gradle/6.7/executionHistory/executionHistory.lock +0 -0
  24. package/android/.gradle/6.7/fileChanges/last-build.bin +0 -0
  25. package/android/.gradle/6.7/fileHashes/fileHashes.bin +0 -0
  26. package/android/.gradle/6.7/fileHashes/fileHashes.lock +0 -0
  27. package/android/.gradle/6.7/gc.properties +0 -0
  28. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  29. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  30. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  31. package/android/.gradle/checksums/checksums.lock +0 -0
  32. package/android/.gradle/checksums/md5-checksums.bin +0 -0
  33. package/android/.gradle/checksums/sha1-checksums.bin +0 -0
  34. package/android/.gradle/configuration-cache/gc.properties +0 -0
  35. package/android/.gradle/vcs-1/gc.properties +0 -0
  36. package/android/.idea/compiler.xml +0 -6
  37. package/android/.idea/gradle.xml +0 -18
  38. package/android/.idea/jarRepositories.xml +0 -35
  39. package/android/.idea/misc.xml +0 -10
  40. package/android/.idea/vcs.xml +0 -6
  41. package/android/.settings/org.eclipse.buildship.core.prefs +0 -13
  42. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  43. package/android/local.properties +0 -8
  44. package/android/src/main/java/com/anyline/reactnative/Anyline4Activity.java +0 -621
  45. package/android/src/main/java/com/anyline/reactnative/AnylineBaseActivity.java +0 -267
  46. package/android/src/main/java/com/anyline/reactnative/AnylinePluginHelper.java +0 -324
  47. package/android/src/main/java/com/anyline/reactnative/AnylineUIConfig.java +0 -88
  48. package/android/src/main/java/com/anyline/reactnative/Document4Activity.java +0 -595
  49. package/android/src/main/res/layout/activity_scan_document.xml +0 -63
  50. package/android/src/main/res/layout/activity_scan_scanview.xml +0 -52
  51. 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, ALIDPluginDelegate,ALScanViewPluginDelegate,ALInfoDelegate>
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
- @property (nonatomic,strong) UIButton *doneButton;
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:(ALJsonUIConfiguration *)uiConfig
43
- delegate:(id<ALPluginScanViewControllerDelegate>)delegate {
44
- self = [super init];
45
- if(self) {
46
- _licensekey = licensekey;
47
- _delegate = delegate;
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 = 100;
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
- //Init the anyline sdk with your license key
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
- //Init the anyline ID ScanPlugin with an ID, Licensekey, the delegate,
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=[[ALNFCDetector alloc] initWithDelegate:self];
91
+ self.nfcDetector = [self.class getNFCDetectorWithDelegate:self error:&error];
133
92
  } else {
134
- // Fallback on earlier versions
93
+ error = [ALPluginHelper errorWithMessage:@"iOS 13.0 or newer is required to scan with MRZ / NFC."];
135
94
  }
136
- NSAssert(self.mrzScanPlugin, @"Setup Error: %@", error.debugDescription);
137
- [self.mrzScanPlugin addInfoDelegate:self];
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
- [self.view sendSubviewToBack:self.scanView];
152
-
153
- [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
154
-
155
- id topGuide = self.topLayoutGuide;
156
- [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];
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
- //Start Camera:
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
- // We use this subroutine to start Anyline. The reason it has its own subroutine is
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)updateHintPosition:(CGFloat)newPosition {
174
- self.hintView.center = CGPointMake(self.hintView.center.x, newPosition);
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)viewWillDisappear:(BOOL)animated {
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 stopAndReturnError:nil];
153
+ [self.mrzScanViewPlugin stop];
199
154
  self.hintView.hidden = YES;
200
155
  }
201
156
 
202
- - (void)readNFCChip {
203
- /*
204
- This is where we start reading the NFC chip of the passport. We use data from the MRZ to authenticate with the chip.
205
- */
206
- [self.nfcDetector startNfcDetectionWithPassportNumber:self.passportNumberForNFC dateOfBirth:self.dateOfBirth expirationDate:self.dateOfExpiry];
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
- #pragma mark -- ALIDPluginDelegate
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
- [self stopMRZScanning];
232
+
232
233
  self.passportNumberForNFC = passportNumberForNFC;
233
- self.dateOfBirth = dateOfBirth;
234
- self.dateOfExpiry = dateOfExpiry;
235
- dispatch_async(dispatch_get_main_queue(), ^{
236
- [self readNFCChip];
237
- });
234
+
235
+ [self.nfcDetector startNfcDetectionWithPassportNumber:self.passportNumberForNFC
236
+ dateOfBirth:self.dateOfBirth
237
+ expirationDate:self.dateOfExpiry];
238
238
  }
239
239
 
240
- #pragma mark -- ALNFCDetectorDelegate
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
- /* This method is called after all the data has been read from the NFC chip.
243
- To display data as it is read instead of waiting until everything has been read, we could also implement nfcSucceededWithDataGroup1: or nfcSucceededWithDataGroup2: */
244
- - (void)nfcSucceededWithResult:(ALNFCResult * _Nonnull)nfcResult API_AVAILABLE(ios(13.0)){
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
- [self.anylineResult setObject:[ALPluginHelper dictionaryForNFCResult:nfcResult quality:self.quality] forKey:@"nfcResult"];
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" message:@"NFC passport reading is not supported on this device."];
328
+ [self showAlertWithTitle:@"NFC Not Supported"
329
+ message:@"NFC passport reading is not supported on this device."];
256
330
  }
257
- if (error.code == ALNFCTagErrorResponseError || //error ALNFCTagErrorResponseError can mean the MRZ key was wrong
258
- error.code == ALNFCTagErrorUnexpectedError) { //error ALNFCTagErrorUnexpectedError can mean the user pressed the 'Cancel' button while scanning. It could also mean the phone lost the connection with the NFC chip because it was moved.
259
- [self startMRZScanning]; //run the MRZ scanner so we can try again.
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 the NFC chip again without rescanning the MRZ.
262
- [self readNFCChip];
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
- - (NSString *)stringForDate:(NSDate *)date {
270
- if (!date) {
271
- return @"Date not valid";
351
+ - (void)scanView:(ALScanView *)scanView updatedCutoutWithPluginID:(NSString *)pluginID
352
+ frame:(CGRect)frame {
353
+
354
+ if (CGRectIsEmpty(frame)) {
355
+ return;
272
356
  }
273
-
274
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
275
- [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT+0:00"]];
276
- dateFormatter.dateStyle = NSDateFormatterMediumStyle;
277
- dateFormatter.timeStyle = NSDateFormatterNoStyle;
278
-
279
- return [dateFormatter stringFromDate:date];
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)anylineScanViewPlugin:(ALAbstractScanViewPlugin *)anylineScanViewPlugin updatedCutout:(CGRect)cutoutRect {
283
- [self updateHintPosition:cutoutRect.origin.y + self.scanView.frame.origin.y - 50];
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
- - (void)startPlugin:(ALAbstractScanViewPlugin *)plugin {
286
- NSError *error;
287
- BOOL success = [plugin startAndReturnError:&error];
288
- if( !success ) {
289
- //sometimes this alert will show, sometimes the slightly-less-friendly one from the SDK will show instead.
290
- [self showAlertWithTitle:@"Could not start scanning" message:error.localizedDescription];
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
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
295
- message:message
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
- - (CGRect)scanViewFrame {
303
- CGRect frame = [[UIScreen mainScreen] bounds];
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
- - (void)handleResult:(NSDictionary *)dictResult result:(ALScanResult *)scanResult {
309
-
310
- [self.delegate pluginScanViewController:nil
311
- didScan:dictResult
312
- continueScanning:!self.scanView.scanViewPlugin.scanViewPluginConfig.cancelOnResult];
313
-
314
- if (self.scanView.scanViewPlugin.scanViewPluginConfig.cancelOnResult) {
315
- [self dismissViewControllerAnimated:YES completion:NULL];
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
- - (void)doneButtonPressed:(id)sender {
321
- [self.scanView.scanViewPlugin stopAndReturnError:nil];
322
- [self dismissViewControllerAnimated:YES completion:^{
323
- [self.delegate pluginScanViewController:nil didStopScanning:sender];
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