diff --git a/CHANGELOG.md b/CHANGELOG.md index 7249efa..d3be39e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -# 53.0.0 +# 54.0.0 Please find the complete and updated release notes at https://documentation.anyline.com/flutter-plugin-component/latest/release-notes.html. diff --git a/android/build.gradle b/android/build.gradle index 6da9f00..410a1fd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,7 +4,7 @@ version '1.0' buildscript { ext { - anyline_sdk_version = "53.3.0" + anyline_sdk_version = "54.0.0" kotlin_version = "1.8.0" } repositories { diff --git a/android/src/main/java/io/anyline/flutter/AnylinePlugin.java b/android/src/main/java/io/anyline/flutter/AnylinePlugin.java index b2d17ca..4ba9e14 100644 --- a/android/src/main/java/io/anyline/flutter/AnylinePlugin.java +++ b/android/src/main/java/io/anyline/flutter/AnylinePlugin.java @@ -39,9 +39,6 @@ public class AnylinePlugin implements private MethodChannel channel; - private String licenseKey; - private String pluginVersion = ""; - private boolean enableOfflineCache = false; private String customModelsPath = "flutter_assets"; private String viewConfigsPath = "flutter_assets"; @@ -79,9 +76,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { } else if (call.method.equals(Constants.METHOD_SET_VIEW_CONFIGS_PATH)) { viewConfigsPath = call.argument(Constants.EXTRA_VIEW_CONFIGS_PATH); } else if (call.method.equals(Constants.METHOD_SET_LICENSE_KEY)) { - licenseKey = call.argument(Constants.EXTRA_LICENSE_KEY); - pluginVersion = call.argument(Constants.EXTRA_PLUGIN_VERSION); - enableOfflineCache = Boolean.TRUE.equals(call.argument(Constants.EXTRA_ENABLE_OFFLINE_CACHE)); + String licenseKey = call.argument(Constants.EXTRA_LICENSE_KEY); + String pluginVersion = call.argument(Constants.EXTRA_PLUGIN_VERSION); + boolean enableOfflineCache = Boolean.TRUE.equals(call.argument(Constants.EXTRA_ENABLE_OFFLINE_CACHE)); try { initSdk(licenseKey, customModelsPath, pluginVersion, enableOfflineCache); result.success(true); diff --git a/android/src/main/java/io/anyline/flutter/AnylineUIConfig.java b/android/src/main/java/io/anyline/flutter/AnylineUIConfig.java index fbfd987..9022a19 100644 --- a/android/src/main/java/io/anyline/flutter/AnylineUIConfig.java +++ b/android/src/main/java/io/anyline/flutter/AnylineUIConfig.java @@ -31,14 +31,13 @@ public class AnylineUIConfig { /** * Create config from the given json object. * - * @param context the context * @param jsonObject the json object with the settings */ - public AnylineUIConfig(Context context, JSONObject jsonObject) { - initFromJsonObject(context, jsonObject); + public AnylineUIConfig(JSONObject jsonObject) { + initFromJsonObject(jsonObject); } - private void initFromJsonObject(Context context, JSONObject json) { + private void initFromJsonObject(JSONObject json) { JSONObject segment = json.optJSONObject(SEGMENT); if (segment != null) { @@ -47,8 +46,8 @@ private void initFromJsonObject(Context context, JSONObject json) { JSONArray titlesJson = segment.getJSONArray(SEGMENT_TITLES); JSONArray viewConfigsJson = segment.getJSONArray(SEGMENT_VIEWCONFIGS); - titles = new ArrayList(); - viewConfigs = new ArrayList(); + titles = new ArrayList<>(); + viewConfigs = new ArrayList<>(); for (int i = 0; i < titlesJson.length(); i++) { titles.add(titlesJson.get(i).toString()); viewConfigs.add(viewConfigsJson.get(i).toString()); diff --git a/android/src/main/java/io/anyline/flutter/ScanActivity.java b/android/src/main/java/io/anyline/flutter/ScanActivity.java index 6cd127a..37abc35 100644 --- a/android/src/main/java/io/anyline/flutter/ScanActivity.java +++ b/android/src/main/java/io/anyline/flutter/ScanActivity.java @@ -61,7 +61,6 @@ public class ScanActivity extends Activity implements CameraOpenListener, private boolean defaultOrientationApplied; private static final String KEY_DEFAULT_ORIENTATION_APPLIED = "default_orientation_applied"; - private JSONObject configJson; private JSONObject optionsJson = null; private Map nativeBarcodeMap = null; @@ -176,15 +175,9 @@ protected void onPause() { private void setDebugListener() { ViewPluginBase scanViewPlugin = anylineScanView.getScanViewPlugin(); if (scanViewPlugin != null) { - scanViewPlugin.scanInfoReceived = jsonObject -> { - Log.d(TAG, "info received: " + jsonObject.toString()); - }; - scanViewPlugin.runSkippedReceived = jsonObject -> { - Log.d(TAG, "run skipped: " + jsonObject.toString()); - }; - scanViewPlugin.errorReceived = jsonObject -> { - Log.w(TAG, "error received: " + jsonObject.toString()); - }; + scanViewPlugin.scanInfoReceived = jsonObject -> Log.d(TAG, "info received: " + jsonObject.toString()); + scanViewPlugin.runSkippedReceived = jsonObject -> Log.d(TAG, "run skipped: " + jsonObject.toString()); + scanViewPlugin.errorReceived = jsonObject -> Log.w(TAG, "error received: " + jsonObject.toString()); } } @@ -230,8 +223,7 @@ private void setScanConfig(String viewConfigAssetFileName) { private void setScanConfig(JSONObject scanConfigJson, String viewConfigAssetFileName) { try { - configJson = scanConfigJson; - optionsJson = configJson.optJSONObject("options"); + optionsJson = scanConfigJson.optJSONObject("options"); anylineScanView.getCameraView().removeNativeBarcodeReceivedEventListener(this); nativeBarcodeMap = null; @@ -249,9 +241,7 @@ private void setScanConfig(JSONObject scanConfigJson, String viewConfigAssetFile ScanViewPlugin scanViewPlugin = viewPluginBase.getFirstActiveScanViewPlugin(); - viewPluginBase.resultReceived = scanResult -> { - setResult(scanViewPlugin, AnylinePluginHelper.jsonHelper(scanResult, nativeBarcodeMap).toString()); - }; + viewPluginBase.resultReceived = scanResult -> setResult(scanViewPlugin, AnylinePluginHelper.jsonHelper(scanResult, nativeBarcodeMap).toString()); viewPluginBase.resultsReceived = scanResults -> { JSONObject jsonResult = new JSONObject(); @@ -337,7 +327,7 @@ && getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { } private void addSegmentRadioButtonUI(JSONObject optionsJson, String currentSegment) { - anylineUIConfig = new AnylineUIConfig(this, optionsJson); + anylineUIConfig = new AnylineUIConfig(optionsJson); setupRadioGroup(anylineUIConfig, currentSegment); } @@ -345,7 +335,7 @@ private void setupRadioGroup(AnylineUIConfig anylineUIConfig, String scanModeStr ArrayList titles = anylineUIConfig.getTitles(); final ArrayList viewConfigs = anylineUIConfig.getViewConfigs(); - if (titles != null && titles.size() > 0) { + if (titles != null && !titles.isEmpty()) { if (titles.size() != viewConfigs.size()) { finishWithError(getString(getResources().getIdentifier("error_invalid_segment_config", "string", @@ -395,7 +385,7 @@ private void configRotateButtonInView(RotateButtonConfig rotateButtonConfig) { buttonLayoutParams.gravity = Gravity.TOP | Gravity.RIGHT; String alignment = rotateButtonConfig.getAlignment(); - if (alignment.length() > 0) { + if (!alignment.isEmpty()) { if (alignment.equals("top_left")) { buttonLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 273504e..23ed8a4 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + compileSdkVersion 34 ndkVersion '24.0.8215888' // Replace this with the version you're using @@ -38,7 +38,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.anyline.flutter.examples" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml index f2e59ee..8750d21 100644 --- a/example/android/app/src/main/res/drawable/launch_background.xml +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -1,13 +1,4 @@ - - - - - diff --git a/example/config/NFCAndMRZConfig.json b/example/config/NFCAndMRZConfig.json deleted file mode 100644 index 7ece80e..0000000 --- a/example/config/NFCAndMRZConfig.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "options": { - "enableNFCWithMRZ": true, - "labelConfig": { - "text": "Scan Passport", - "size": 22, - "offset.x": 0, - "offset.y": -10 - }, - "doneButtonConfig": { - "offset.y": -88 - } - }, - "cameraConfig": { - "captureResolution": "1080p" - }, - "flashConfig": { - "mode": "manual", - "alignment": "bottom_left" - }, - "viewPluginConfig": { - "pluginConfig": { - "id": "mrz_nfc", - "mrzConfig": { - "strictMode": false, - "cropAndTransformID": false - }, - "cancelOnResult": true - }, - "cutoutConfig": { - "animation": "none", - "maxWidthPercent": "90%", - "maxHeightPercent": "90%", - "alignment": "center", - "ratioFromSize": { - "width": 161, - "height": 100 - }, - "offset": { - "x": 0, - "y": 0 - }, - "cropPadding": { - "x": -30, - "y": -90 - }, - "cropOffset": { - "x": 0, - "y": 0 - }, - "outerColor": "000000", - "outerAlpha": 0.3, - "strokeWidth": 2, - "strokeColor": "0099FF", - "cornerRadius": 4, - "feedbackStrokeColor": "0099FF" - }, - "scanFeedbackConfig": { - "style": "rect", - "strokeWidth": 2, - "strokeColor": "0099FF", - "fillColor": "220099FF", - "beepOnResult": true, - "vibrateOnResult": true, - "blinkAnimationOnResult": false - } - } -} \ No newline at end of file diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b5abf52..8c880c3 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,13 +1,13 @@ PODS: - - Anyline (53.3.0) - - anyline_plugin (53.0.0): - - Anyline (~> 53) + - Anyline (54.0.0) + - anyline_plugin (54.0.0): + - Anyline (= 54.0.0) - Flutter - Flutter (1.0.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.3.0): + - permission_handler_apple (9.1.1): - Flutter - shared_preferences_foundation (0.0.1): - Flutter @@ -37,12 +37,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" SPEC CHECKSUMS: - Anyline: d076ffac04ccf43a281ac6a6dd5230630c7e260c - anyline_plugin: 1f62863478c6660d2f40144e31e1b74b215ac721 + Anyline: 2a4d542643e9bbf0ddfef674a34012d8f866dc2e + anyline_plugin: 5d8b3468299874434c132a1f17bcaf6650151ea9 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - permission_handler_apple: 036b856153a2b1f61f21030ff725f3e6fece2b78 - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 PODFILE CHECKSUM: 1ba8e470e8138e872af9577a0e261e59d83eaf57 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1f37c45..160de49 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -407,7 +407,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 53.3.0; + MARKETING_VERSION = 53.0.0; PRODUCT_BUNDLE_IDENTIFIER = io.anyline.flutter.examples; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -545,7 +545,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 53.3.0; + MARKETING_VERSION = 53.0.0; PRODUCT_BUNDLE_IDENTIFIER = io.anyline.flutter.examples; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -578,7 +578,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 53.3.0; + MARKETING_VERSION = 53.0.0; PRODUCT_BUNDLE_IDENTIFIER = io.anyline.flutter.examples; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 98876d6..7b30569 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -28,8 +28,6 @@ LSRequiresIPhoneOS - NFCReaderUsageDescription - NFC Usage NSCameraUsageDescription Allow Camera Access to scan with Anyline UIApplicationSupportsIndirectInputEvents @@ -51,9 +49,5 @@ UIViewControllerBasedStatusBarAppearance - com.apple.developer.nfc.readersession.iso7816.select-identifiers - - A0000002471001 - diff --git a/example/lib/anyline_service.dart b/example/lib/anyline_service.dart index 0bf3d00..cf71d59 100644 --- a/example/lib/anyline_service.dart +++ b/example/lib/anyline_service.dart @@ -5,6 +5,7 @@ import 'package:anyline_plugin/constants.dart'; import 'package:anyline_plugin_example/license_state.dart'; import 'package:anyline_plugin_example/result.dart'; import 'package:anyline_plugin_example/scan_modes.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:path/path.dart' as path; @@ -20,7 +21,12 @@ abstract class AnylineService { } class AnylineServiceImpl implements AnylineService { - static const MethodChannel _channel = const MethodChannel('anyline_plugin'); + AnylineServiceImpl() { + _initAnylinePlugin(); + _initResultListFromSharedPreferences(); + } + + static const MethodChannel _channel = MethodChannel('anyline_plugin'); late String? _cachePath; @@ -30,18 +36,13 @@ class AnylineServiceImpl implements AnylineService { String? _sdkVersion = 'Unknown'; String? _pluginVersion = 'Unknown'; - AnylineServiceImpl() { - _initAnylinePlugin(); - _initResultListFromSharedPreferences(); - } - Future _initSdk(String licenseKey) async { try { await anylinePlugin.initSdk(licenseKey); - licenseState = new LicenseState(true, ""); + licenseState = new LicenseState(true, ''); } catch (anylineException) { licenseState = new LicenseState(false, anylineException.toString()); - throw anylineException; + rethrow; } return licenseState; } @@ -67,7 +68,7 @@ class AnylineServiceImpl implements AnylineService { return _pluginVersion; } - _initAnylinePlugin() async { + void _initAnylinePlugin() async { String? sdkVersion; try { sdkVersion = await AnylinePlugin.sdkVersion; @@ -78,11 +79,11 @@ class AnylineServiceImpl implements AnylineService { _pluginVersion = await AnylinePlugin.pluginVersion; anylinePlugin = AnylinePlugin(); - anylinePlugin.setCustomModelsPath("flutter_assets/custom_scripts"); - anylinePlugin.setViewConfigsPath("flutter_assets/config"); + anylinePlugin.setCustomModelsPath('flutter_assets/custom_scripts'); + anylinePlugin.setViewConfigsPath('flutter_assets/config'); } - _initResultListFromSharedPreferences() async { + void _initResultListFromSharedPreferences() async { SharedPreferences prefs = await SharedPreferences.getInstance(); List list = prefs.getStringList('results') ?? []; @@ -90,7 +91,7 @@ class AnylineServiceImpl implements AnylineService { .invokeMethod(Constants.METHOD_GET_APPLICATION_CACHE_PATH); List modifiedResults = []; - for (String result in list) { + for (final String result in list) { Result? res = resultFromJSONString(result); if (res != null) { modifiedResults.add(res); @@ -102,7 +103,7 @@ class AnylineServiceImpl implements AnylineService { // ACO: get the 2 image file paths for each result. If their directories are // different from the current cache directory, fix their paths. Result? resultFromJSONString(String result) { - Result res = Result.fromJson(json.decode(result)); + Result res = Result.fromJson(json.decode(result) as Map); if (_cachePath == null) { return res; } @@ -116,10 +117,11 @@ class AnylineServiceImpl implements AnylineService { return null; } - var savedImageDirectory = path.dirname(res.jsonMap!['fullImagePath']); + var savedImageDirectory = + path.dirname(res.jsonMap!['fullImagePath'] as String); - var fullImageName = path.basename(res.jsonMap!['fullImagePath']); - var croppedImageName = path.basename(res.jsonMap!['imagePath']); + var fullImageName = path.basename(res.jsonMap!['fullImagePath'] as String); + var croppedImageName = path.basename(res.jsonMap!['imagePath'] as String); if (savedImageDirectory != _cachePath) { res.jsonMap!['fullImagePath'] = path.join(_cachePath!, fullImageName); @@ -138,33 +140,39 @@ class AnylineServiceImpl implements AnylineService { String? stringResult = await anylinePlugin.startScanning(configJson); - print(stringResult); + if (kDebugMode) { + print(stringResult); + } if (stringResult == 'Canceled') { return null; } - Map? jsonResult = jsonDecode(stringResult!); + Map? jsonResult = + jsonDecode(stringResult!) as Map; return Result(jsonResult, mode, DateTime.now()); } Future _loadJsonConfigFromFile(String config) async { - return await rootBundle.loadString("config/${config}Config.json"); + return rootBundle.loadString('config/${config}Config.json'); } /// Returns the licenseKey stored in associated file from the config folder. Future _getExternalLicenseKey() async { Map? licenseKeyMap; - String externalLicenseKeyJson = ""; + String externalLicenseKeyJson = ''; try { externalLicenseKeyJson = - await rootBundle.loadString("config/license.json"); - licenseKeyMap = jsonDecode(externalLicenseKeyJson); + await rootBundle.loadString('config/license.json'); + licenseKeyMap = + jsonDecode(externalLicenseKeyJson) as Map; } catch (e) { - print("exception: $e"); + if (kDebugMode) { + print('exception: $e'); + } } - return licenseKeyMap?["licenseKey"] ?? ""; + return licenseKeyMap?['licenseKey'] as String; } /// Returns the config string for a given scan mode in JSON format, reading the @@ -174,14 +182,14 @@ class AnylineServiceImpl implements AnylineService { return configJson; } - _saveResultToResultList(Result result) { + void _saveResultToResultList(Result result) { _results.insert(0, result); _saveResultListToSharedPreferences(_results); } - _saveResultListToSharedPreferences(List results) async { + void _saveResultListToSharedPreferences(List results) async { List results = [ - for (Result result in _results) json.encode(result.toJson()) + for (final Result result in _results) json.encode(result.toJson()) ]; SharedPreferences prefs = await SharedPreferences.getInstance(); diff --git a/example/lib/date_helpers.dart b/example/lib/date_helpers.dart index f404dd8..ea827b4 100644 --- a/example/lib/date_helpers.dart +++ b/example/lib/date_helpers.dart @@ -1,15 +1,13 @@ extension DateHelpers on DateTime { bool isToday() { final now = DateTime.now(); - return now.day == this.day && - now.month == this.month && - now.year == this.year; + return now.day == day && now.month == month && now.year == year; } bool isYesterday() { final yesterday = DateTime.now().subtract(Duration(days: 1)); - return yesterday.day == this.day && - yesterday.month == this.month && - yesterday.year == this.year; + return yesterday.day == day && + yesterday.month == month && + yesterday.year == year; } } diff --git a/example/lib/home.dart b/example/lib/home.dart index 2da2a95..7116ec5 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -4,15 +4,18 @@ import 'package:anyline_plugin/exceptions.dart'; import 'package:anyline_plugin_example/anyline_service.dart'; import 'package:anyline_plugin_example/result.dart'; import 'package:anyline_plugin_example/styles.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'result_display.dart'; -import 'result_list.dart'; -import 'scan_modes.dart'; -import 'license_state.dart'; +import 'package:anyline_plugin_example/result_display.dart'; +import 'package:anyline_plugin_example/result_list.dart'; +import 'package:anyline_plugin_example/scan_modes.dart'; +import 'package:anyline_plugin_example/license_state.dart'; class AnylineDemoApp extends StatelessWidget { + const AnylineDemoApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -34,6 +37,8 @@ class AnylineDemoApp extends StatelessWidget { } class Home extends StatefulWidget { + const Home({Key? key}) : super(key: key); + @override _HomeState createState() => _HomeState(); } @@ -66,37 +71,39 @@ class _HomeState extends State { if (e is AnylineLicenseException) { message = LicenseState.LicenseKeyEmptyErrorMessage; } - print(message); + if (kDebugMode) { + print(message); + } - showDialog( + showDialog( context: context, builder: (_) => AlertDialog( - elevation: 0, - title: const Text( - 'Error', - style: TextStyle( - fontFamily: "Roboto", fontWeight: FontWeight.bold), - ), - content: Text( - message, - style: TextStyle(fontFamily: "Roboto"), - textAlign: TextAlign.start, - ), - actions: [ - TextButton( - child: Text("OK", - style: TextStyle( - fontFamily: "Roboto", fontWeight: FontWeight.bold)), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ], - )); + elevation: 0, + title: const Text( + 'Error', + style: TextStyle( + fontFamily: 'Roboto', fontWeight: FontWeight.bold), + ), + content: Text( + message, + style: TextStyle(fontFamily: 'Roboto'), + textAlign: TextAlign.start, + ), + actions: [ + TextButton( + child: Text('OK', + style: TextStyle( + fontFamily: 'Roboto', fontWeight: FontWeight.bold)), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + )); } } - _openResultDisplay(Result result) { + void _openResultDisplay(Result result) { Navigator.pushNamed( context, result.scanMode.isCompositeScan() @@ -185,24 +192,23 @@ class _HomeState extends State { color: Colors.white, ), onPressed: () { - showDialog( + showDialog( context: context, builder: (_) => AlertDialog( - elevation: 0, - title: FittedBox( - fit: BoxFit.fitWidth, - child: Text( - 'Anyline Flutter Demo App', - style: TextStyle(fontWeight: FontWeight.bold), - )), - content: FittedBox( - fit: BoxFit.fitWidth, - child: Text( - 'SDK Version ${_anylineService.getSdkVersion()}' + - '\n' + - 'Plugin Version ${_anylineService.getPluginVersion()}' - )), - )); + elevation: 0, + title: FittedBox( + fit: BoxFit.fitWidth, + child: Text( + 'Anyline Flutter Demo App', + style: TextStyle(fontWeight: FontWeight.bold), + )), + content: FittedBox( + fit: BoxFit.fitWidth, + child: Text( + 'SDK Version ${_anylineService.getSdkVersion()}' + + '\n' + + 'Plugin Version ${_anylineService.getPluginVersion()}')), + )); }, ), ) @@ -360,12 +366,6 @@ class _HomeState extends State { scan(ScanMode.MRZ); }, ), - ScanButton( - text: 'NFC', - onPressed: () { - scan(ScanMode.NFCAndMRZ); - }, - ), ScanButton( text: 'PDF 417 (AAMVA)', onPressed: () { @@ -512,7 +512,7 @@ class _HomeState extends State { } class ScanButton extends StatelessWidget { - ScanButton({required this.text, this.onPressed}); + ScanButton({Key? key, required this.text, this.onPressed}) : super(key: key); final String text; final Function? onPressed; @@ -530,9 +530,8 @@ class ScanButton extends StatelessWidget { padding: EdgeInsets.all(10), child: TextButton( style: flatButtonStyle, - child: Container( - height: double.infinity, - width: double.infinity, + onPressed: onPressed as void Function()?, + child: SizedBox.expand( child: Stack( clipBehavior: Clip.hardEdge, alignment: Alignment.bottomLeft, @@ -542,7 +541,7 @@ class ScanButton extends StatelessWidget { left: 10, child: Text(text, style: - TextStyle(fontWeight: FontWeight.w800, fontSize: 17)), + TextStyle(fontWeight: FontWeight.w800, fontSize: 17)), ), Positioned( bottom: -15, @@ -557,7 +556,6 @@ class ScanButton extends StatelessWidget { ], ), ), - onPressed: onPressed as void Function()?, ), ), ); @@ -565,7 +563,7 @@ class ScanButton extends StatelessWidget { } class UseCaseButton extends StatelessWidget { - UseCaseButton({this.image, required this.text, this.onPressed}); + UseCaseButton({Key? key, this.image, required this.text, this.onPressed}) : super(key: key); final ImageProvider? image; final String text; @@ -584,9 +582,8 @@ class UseCaseButton extends StatelessWidget { padding: EdgeInsets.all(10), child: TextButton( style: flatButtonStyle, - child: Container( - height: double.infinity, - width: double.infinity, + onPressed: onPressed as void Function()?, + child: SizedBox.expand( child: Stack( alignment: Alignment.bottomLeft, children: [ @@ -602,7 +599,7 @@ class UseCaseButton extends StatelessWidget { left: 10, child: Text(text, style: - TextStyle(fontWeight: FontWeight.w800, fontSize: 17)), + TextStyle(fontWeight: FontWeight.w800, fontSize: 17)), ), Positioned( bottom: -15, @@ -617,7 +614,6 @@ class UseCaseButton extends StatelessWidget { ], ), ), - onPressed: onPressed as void Function()?, ), ), ); diff --git a/example/lib/license_state.dart b/example/lib/license_state.dart index 4fb3b15..2aa1826 100644 --- a/example/lib/license_state.dart +++ b/example/lib/license_state.dart @@ -1,6 +1,6 @@ class LicenseState { static const LicenseKeyEmptyErrorMessage = - "Please ensure that your license key is valid and set correctly in config/license.json.\n\nFor more information, please check https://documentation.anyline.com/main-component/license-key-generation.html"; + 'Please ensure that your license key is valid and set correctly in config/license.json.\n\nFor more information, please check https://documentation.anyline.com/main-component/license-key-generation.html'; late bool initialized; late String reason; diff --git a/example/lib/main.dart b/example/lib/main.dart index 38b3ea1..7e29edd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,10 +2,11 @@ import 'dart:async'; import 'dart:convert'; import 'package:anyline_plugin/anyline_plugin.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'home.dart'; +import 'package:anyline_plugin_example/home.dart'; void main() { runApp(AnylineDemoApp()); @@ -20,7 +21,7 @@ void scanWithAnyline() async { /// Load the config file which also includes the license key (for more info /// visit documentation.anyline.com). - var config = await rootBundle.loadString("config/AnalogMeterConfig.json"); + var config = await rootBundle.loadString('config/AnalogMeterConfig.json'); /// Start the scanning process. var stringResult = @@ -29,7 +30,10 @@ void scanWithAnyline() async { /// Convert the stringResult to a Map to access the result fields. It is /// recommended to create result classes that fit your use case. For more /// information on that, visit the Flutter Guide on documentation.anyline.com. - Map? result = jsonDecode(stringResult); + Map? result = + jsonDecode(stringResult) as Map; - print(result); + if (kDebugMode) { + print(result); + } } diff --git a/example/lib/result.dart b/example/lib/result.dart index 427b728..b925f29 100644 --- a/example/lib/result.dart +++ b/example/lib/result.dart @@ -1,21 +1,27 @@ import 'package:anyline_plugin_example/scan_modes.dart'; class Result { + + Result.fromJson(Map json) + : timestamp = DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int), + scanMode = ScanMode.values + .firstWhere((element) => element.key == json['scanMode']), + jsonMap = json['jsonMap'] as Map; + + Result(this.jsonMap, this.scanMode, this.timestamp); DateTime timestamp; ScanMode scanMode; Map? jsonMap; - Result(this.jsonMap, this.scanMode, this.timestamp); - int get length { return jsonMap!.length; } - get values { + List get values { return jsonMap!.values.toList(); } - get keys { + List get keys { return jsonMap!.keys.toList(); } @@ -24,10 +30,4 @@ class Result { 'scanMode': scanMode.key, 'jsonMap': jsonMap, }; - - Result.fromJson(Map json) - : timestamp = DateTime.fromMillisecondsSinceEpoch(json['timestamp']), - scanMode = ScanMode.values - .firstWhere((element) => element.key == json['scanMode']), - jsonMap = json['jsonMap']; } diff --git a/example/lib/result_display.dart b/example/lib/result_display.dart index ce6c39b..23eb52d 100644 --- a/example/lib/result_display.dart +++ b/example/lib/result_display.dart @@ -7,9 +7,11 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:photo_view/photo_view.dart'; -import 'scan_modes.dart'; +import 'package:anyline_plugin_example/scan_modes.dart'; class ResultDisplay extends StatelessWidget { + const ResultDisplay({Key? key}) : super(key: key); + static const routeName = '/resultDisplay'; @override @@ -22,7 +24,7 @@ class ResultDisplay extends StatelessWidget { backgroundColor: Styles.backgroundBlack, centerTitle: true, title: Text( - "${result.scanMode.label} Result", + '${result.scanMode.label} Result', style: GoogleFonts.montserrat(fontWeight: FontWeight.w400), ), elevation: 0, @@ -39,6 +41,8 @@ class ResultDisplay extends StatelessWidget { } class CompositeResultDisplay extends StatelessWidget { + const CompositeResultDisplay({Key? key}) : super(key: key); + static const routeName = '/compositeResultDisplay'; @override @@ -47,7 +51,8 @@ class CompositeResultDisplay extends StatelessWidget { var subResults = result.jsonMap!.values.take(3); - List> results = [for (var j in subResults) j]; + List> results = [ + for (final j in subResults) j as Map]; return DefaultTabController( length: results.length, @@ -65,7 +70,7 @@ class CompositeResultDisplay extends StatelessWidget { title: FittedBox( fit: BoxFit.fitWidth, child: Text( - "${result.scanMode.label}", + '${result.scanMode.label}', style: GoogleFonts.montserrat(fontWeight: FontWeight.w400), )), ), @@ -99,22 +104,20 @@ class CompositeResultDisplay extends StatelessWidget { } class ResultDetails extends StatelessWidget { - final Map? json; - late final Map? imageMap; - late final List>? orderedJson; - late final List? nativeBarcodesDetected; - ResultDetails(Map? json) : this.json = json { - this.orderedJson = []; - this.imageMap = Map(); - this.nativeBarcodesDetected = []; + ResultDetails(Map? json, {Key? key}) + : json = json, + super(key: key) { + orderedJson = []; + imageMap = Map(); + nativeBarcodesDetected = []; var actualResultMap = Map(); // NOTE: keep xxxResult on top, nativeBarcodesDetected, imagePath and fullImagePath at the bottom json?.forEach((key, value) { if (key.toLowerCase().endsWith('imagepath')) { - this.imageMap![key] = value; + imageMap![key] = value; return; } if (key.toLowerCase().endsWith('result')) { @@ -123,38 +126,41 @@ class ResultDetails extends StatelessWidget { return; } if (key.toLowerCase() == 'nativebarcodesdetected') { - this.nativeBarcodesDetected?.add(value); + nativeBarcodesDetected?.add(value); return; } - this.orderedJson!.add({key: value}); + orderedJson!.add({key: value}); }); actualResultMap.forEach((key, value) { var encoder = new JsonEncoder.withIndent(' ' * 2); var prettyJSON = encoder.convert(value); - this.orderedJson!.insert(0, {key: prettyJSON}); + orderedJson!.insert(0, {key: prettyJSON}); }); - if (this.nativeBarcodesDetected != null && - this.nativeBarcodesDetected!.length > 0) { - this - .orderedJson! - .add({'nativeBarcodesDetected': this.nativeBarcodesDetected}); + if (nativeBarcodesDetected != null && + nativeBarcodesDetected!.length > 0) { + orderedJson! + .add({'nativeBarcodesDetected': nativeBarcodesDetected}); } dynamic imagePath; imagePath = imageMap?['imagePath']; if (imagePath != null && imagePath.toString().isNotEmpty) { - this.orderedJson!.add({'imagePath': imagePath}); + orderedJson!.add({'imagePath': imagePath}); } imagePath = imageMap?['fullImagePath']; if (imagePath != null && imagePath.toString().isNotEmpty) { - this.orderedJson!.add({'fullImagePath': imagePath}); + orderedJson!.add({'fullImagePath': imagePath}); } } + final Map? json; + late final Map? imageMap; + late final List>? orderedJson; + late final List? nativeBarcodesDetected; @override Widget build(BuildContext context) { @@ -163,13 +169,13 @@ class ResultDetails extends StatelessWidget { child: ListView( children: [ Container( + color: Colors.black87, child: Image.file( - File(imageMap!['imagePath']), + File(imageMap!['imagePath'] as String), fit: BoxFit.scaleDown, height: 240, // prevents weird display of tall images (e.g. vertical shipping containers) - ), - color: Colors.black87), + )), ListView.builder( shrinkWrap: true, physics: ScrollPhysics(), @@ -189,7 +195,7 @@ class ResultDetails extends StatelessWidget { // will try in order // "American Typewriter", // "Avenir Book", - "Roboto Mono" + 'Roboto Mono' ]), ), contentPadding: EdgeInsets.all(4), @@ -212,6 +218,8 @@ class ResultDetails extends StatelessWidget { } class FullScreenImage extends StatelessWidget { + const FullScreenImage({Key? key}) : super(key: key); + static const routeName = '/resultDisplay/fullImage'; @override @@ -220,10 +228,8 @@ class FullScreenImage extends StatelessWidget { ModalRoute.of(context)!.settings.arguments as String; return GestureDetector( - child: Container( - child: PhotoView( - imageProvider: FileImage(File(fullImagePath)), - ), + child: PhotoView( + imageProvider: FileImage(File(fullImagePath)), ), onTap: () { Navigator.pop(context); diff --git a/example/lib/result_list.dart b/example/lib/result_list.dart index ffc0ccc..a32e62f 100644 --- a/example/lib/result_list.dart +++ b/example/lib/result_list.dart @@ -5,19 +5,18 @@ import 'package:flutter/material.dart'; import 'dart:io'; import 'package:intl/intl.dart'; -import 'date_helpers.dart'; +import 'package:anyline_plugin_example/date_helpers.dart'; -import 'result_display.dart'; +import 'package:anyline_plugin_example/result_display.dart'; class ResultList extends StatelessWidget { + ResultList(this.results, {Key? key}) : super(key: key); static const routeName = '/resultList'; final fullDate = DateFormat('d/M/y, HH:mm'); final time = DateFormat('HH:mm'); final List results; - ResultList(this.results); - @override Widget build(BuildContext context) { return Container( @@ -52,11 +51,11 @@ class ResultList extends StatelessWidget { } class CompositeResultListItem extends StatelessWidget { + CompositeResultListItem(this.result, this.timestamp, {Key? key}) + : super(key: key); final Result result; final String timestamp; - CompositeResultListItem(this.result, this.timestamp); - final ButtonStyle flatButtonStyle = TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0), @@ -118,11 +117,10 @@ class CompositeResultListItem extends StatelessWidget { } class ResultListItem extends StatelessWidget { + ResultListItem(this.result, this.timestamp, {Key? key}) : super(key: key); final Result result; final String timestamp; - ResultListItem(this.result, this.timestamp); - final ButtonStyle flatButtonStyle = TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), @@ -161,7 +159,7 @@ class ResultListItem extends StatelessWidget { SizedBox( height: 5, ), - Image.file(File(result.jsonMap!['imagePath'])), + Image.file(File(result.jsonMap!['imagePath'] as String)), ListTile( dense: true, title: Text( diff --git a/example/lib/scan_modes.dart b/example/lib/scan_modes.dart index 1c661e5..cf81126 100644 --- a/example/lib/scan_modes.dart +++ b/example/lib/scan_modes.dart @@ -9,7 +9,6 @@ enum ScanMode { LicensePlate, JapaneseLandingPermission, MRZ, - NFCAndMRZ, Odometer, ParallelScanning, ParallelFirstScanning, @@ -73,8 +72,6 @@ extension ScanModeInfo on ScanMode { return 'Parallel First Scanning (VIN/Barcode)'; case ScanMode.SerialScanning: return 'Serial Scanning (LP>DL>VIN)'; - case ScanMode.NFCAndMRZ: - return 'MRZ and NFC'; case ScanMode.VRC: return 'Vehicle Registration Certificate'; case ScanMode.CowTag: @@ -83,7 +80,7 @@ extension ScanModeInfo on ScanMode { } String get key { - return this.toString().split('.').last; + return toString().split('.').last; } bool isCompositeScan() { diff --git a/example/pubspec.lock b/example/pubspec.lock index 3a3c512..ae7ea4d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: path: ".." relative: true source: path - version: "53.3.0" + version: "54.0.0" async: dependency: transitive description: @@ -60,18 +60,18 @@ packages: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" fake_async: dependency: transitive description: @@ -84,10 +84,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.3" file: dependency: transitive description: @@ -123,10 +123,10 @@ packages: dependency: transitive description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.2" http_parser: dependency: transitive description: @@ -147,10 +147,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -211,26 +211,26 @@ packages: dependency: transitive description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.10" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -251,58 +251,50 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" permission_handler: dependency: transitive description: name: permission_handler - sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" + sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "10.4.5" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" + sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" url: "https://pub.dev" source: hosted - version: "12.0.5" + version: "10.3.6" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "9.4.0" - permission_handler_html: - dependency: transitive - description: - name: permission_handler_html - sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" - url: "https://pub.dev" - source: hosted - version: "0.1.1" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "3.12.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.1.3" photo_view: dependency: "direct main" description: @@ -315,10 +307,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -339,66 +331,66 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.2" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.5.2" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -480,18 +472,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" - win32: - dependency: transitive - description: - name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "1.0.0" xdg_directories: dependency: transitive description: @@ -509,5 +493,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 5d65b9d..0c73299 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: anyline_plugin_example description: Demonstrates how to use the anyline_plugin plugin. -version: 53.3.0 +version: 54.0.0 # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. diff --git a/ios/Classes/ALNFCScanViewController.h b/ios/Classes/ALNFCScanViewController.h deleted file mode 100644 index 22938f1..0000000 --- a/ios/Classes/ALNFCScanViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -#import "ALPluginScanViewController.h" -#import "ALJSONUIConfiguration.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface ALNFCScanViewController : UIViewController - -- (instancetype)initWithLicensekey:(NSString *)licensekey - configuration:(NSDictionary *)anylineConfig - uiConfig:(ALJSONUIConfiguration *)uiConfig - initializationParamsStr:(NSString *)initializationParamsStr - finished:(ALPluginCallback)callback; -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Classes/ALNFCScanViewController.m b/ios/Classes/ALNFCScanViewController.m deleted file mode 100644 index f30cc35..0000000 --- a/ios/Classes/ALNFCScanViewController.m +++ /dev/null @@ -1,445 +0,0 @@ -#import -#import "ALNFCScanViewController.h" -#import "ALPluginHelper.h" - - -API_AVAILABLE(ios(13.0)) -@interface ALNFCScanViewController () - -@property (nonatomic, strong) ALPluginCallback callback; - -@property (nonatomic, strong) NSDictionary *config; - -@property (nonatomic, copy) NSString *licenseKey; - -@property (nonatomic, strong) ALScanView *scanView; - -@property (nonatomic, strong) ALScanViewPlugin *mrzScanViewPlugin; - -@property (nonatomic, strong) ALNFCDetector *nfcDetector; - -// the result from NFC scanning is retained while NFC reading is initiated, and the results aggregated later -@property (nonatomic, strong) NSMutableDictionary *resultDict; - -// TODO: make sure the following use the `options` group in the uiConfig -// BOOL showingLabel -// UILabel scannedLabel -// ALRoundedView -// Segment -@property (nonatomic, strong) ALJSONUIConfiguration *uiConfig; - -@property (nonatomic, strong) UIView *hintView; - -@property (nonatomic, strong) UILabel *hintViewLabel; - -@property (nonatomic, strong) NSLayoutConstraint *labelHorizontalOffsetConstraint; - -@property (nonatomic, strong) NSLayoutConstraint *labelVerticalOffsetConstraint; - -@property (nonatomic, strong) UIButton *doneButton; - -// 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 -@property (nonatomic, copy) NSString *passportNumberForNFC; - -@property (nonatomic, strong) NSDate *dateOfBirth; - -@property (nonatomic, strong) NSDate *dateOfExpiry; - -@property (nonatomic, strong) NSMutableArray *detectedBarcodes; - -// not used -@property (nonatomic, strong) NSString *cropAndTransformErrorMessage; - -// JPEG compression quality 0-100 -@property (nonatomic, assign) NSUInteger quality; - -@property (nonatomic, nullable) NSString *initializationParamsStr; - -@end - - -@implementation ALNFCScanViewController - -- (instancetype)initWithLicensekey:(NSString *)licensekey - configuration:(NSDictionary *)anylineConfig - uiConfig:(ALJSONUIConfiguration *)uiConfig - initializationParamsStr:(NSString *)initializationParamsStr - finished:(ALPluginCallback)callback { - if (self = [super init]) { - _licenseKey = licensekey; - _callback = callback; - _config = anylineConfig; - _uiConfig = uiConfig; - _initializationParamsStr = initializationParamsStr; - - self.quality = 90; - self.cropAndTransformErrorMessage = @""; - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - // NOTE: NFCDetector can throw an exception if the AnylineSDK isn't initialized first - // as it will check the scope value of the license - NSError *error = nil; - if (@available(iOS 13.0, *)) { - self.nfcDetector = [self.class getNFCDetectorWithDelegate:self error:&error]; - } else { - error = [ALPluginHelper errorWithMessage:@"iOS 13.0 or newer is required to scan with MRZ / NFC."]; - } - if ([self showErrorAlertIfNeeded:error]) { - return; - } - - ALScanViewInitializationParameters *initializationParams = nil; - if(![self isStringEmpty:_initializationParamsStr]){ - initializationParams = [ALScanViewInitializationParameters withJSONString: _initializationParamsStr error:&error]; - } - - [self.view addSubview:self.scanView]; - - self.resultDict = [[NSMutableDictionary alloc] init]; - self.detectedBarcodes = [NSMutableArray array]; - - self.scanView = [ALScanViewFactory withJSONDictionary:self.config - initializationParams:initializationParams - delegate:self - error:&error]; - self.scanView.delegate = self; - - [self configureMRZPlugin]; - - if ([self showErrorAlertIfNeeded:error]) { - return; - } - - self.mrzScanViewPlugin = (ALScanViewPlugin *)self.scanView.viewPlugin; - - self.scanView.supportedNativeBarcodeFormats = self.uiConfig.nativeBarcodeFormats; - self.scanView.delegate = self; - self.detectedBarcodes = [NSMutableArray array]; - - [self.view addSubview:self.scanView]; - - self.scanView.translatesAutoresizingMaskIntoConstraints = false; - [self.scanView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES; - [self.scanView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES; - [self.scanView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES; - [self.scanView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES; - - [self.scanView startCamera]; - - [self setupHintView]; - - self.doneButton = [ALPluginHelper createButtonForViewController:self config:self.uiConfig]; -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - NSError *error; - [self startMRZScanning:&error]; - [self showErrorAlertIfNeeded:error]; -} - -- (void)viewDidDisappear:(BOOL)animated { - [self stopMRZScanning]; - [super viewDidDisappear:animated]; -} - -- (void)startMRZScanning:(NSError **)error { - [self.mrzScanViewPlugin startWithError:error]; - self.hintView.hidden = NO; -} - -- (void)stopMRZScanning { - [self.mrzScanViewPlugin stop]; - self.hintView.hidden = YES; -} - -- (void)configureMRZPlugin { - - ALScanViewPlugin *scanViewPlugin = (ALScanViewPlugin *)self.scanView.viewPlugin; - if (![scanViewPlugin isKindOfClass:ALScanViewPlugin.class]) { - return; - } - - ALViewPluginConfig *scanViewPluginConfig = scanViewPlugin.scanViewPluginConfig; - NSError *error; - [self.scanView setViewPluginConfig:scanViewPluginConfig error:&error]; - - // the delegate binding was lost when you recreated the ScanPlugin it so you have to bring it back here - scanViewPlugin = (ALScanViewPlugin *)self.scanView.viewPlugin; - scanViewPlugin.scanPlugin.delegate = self; -} - -// MARK: - ALIDPluginDelegate - -- (void)scanPlugin:(ALScanPlugin *)scanPlugin resultReceived:(ALScanResult *)scanResult { - - CGFloat compressionQuality = self.quality / 100.0f; - - // ACO just a failsafe for when cancelOnResult is not true - [self stopMRZScanning]; - - self.resultDict = [NSMutableDictionary dictionaryWithDictionary:scanResult.resultDictionary]; - - NSString *imagePath = [ALPluginHelper saveImageToFileSystem:scanResult.croppedImage - compressionQuality:compressionQuality]; - self.resultDict[@"imagePath"] = imagePath; - - imagePath = [ALPluginHelper saveImageToFileSystem:scanResult.fullSizeImage - compressionQuality:compressionQuality]; - - self.resultDict[@"fullImagePath"] = imagePath; - - ALMrzResult *MRZResult = scanResult.pluginResult.mrzResult; - NSString *passportNumber = [MRZResult.documentNumber stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - self.dateOfBirth = [ALPluginHelper formattedStringToDate:MRZResult.dateOfBirthObject]; - - self.dateOfExpiry = [ALPluginHelper formattedStringToDate:MRZResult.dateOfExpiryObject]; - - NSMutableString *passportNumberForNFC = [passportNumber mutableCopy]; - NSRange passportNumberRange = [MRZResult.mrzString rangeOfString:passportNumber]; - if (passportNumberRange.location != NSNotFound) { - if ([MRZResult.mrzString characterAtIndex:NSMaxRange(passportNumberRange)] == '<') { - [passportNumberForNFC appendString:@"<"]; - } - } - - self.passportNumberForNFC = passportNumberForNFC; - - [self.nfcDetector startNfcDetectionWithPassportNumber:self.passportNumberForNFC - dateOfBirth:self.dateOfBirth - expirationDate:self.dateOfExpiry]; -} - -- (void)handleResult:(id _Nullable)resultObj { - - NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionaryWithDictionary:resultObj]; - if (self.detectedBarcodes.count) { - resultDictionary[@"nativeBarcodesDetected"] = self.detectedBarcodes; - } - - NSObject *scanViewPluginBase = self.scanView.viewPlugin; - // TODO: handle this for composites: cancelOnResult = true? dismiss - if ([scanViewPluginBase isKindOfClass:ALScanViewPlugin.class]) { - ALScanViewPlugin *scanViewPlugin = (ALScanViewPlugin *)scanViewPluginBase; - BOOL cancelOnResult = scanViewPlugin.scanPlugin.pluginConfig.cancelOnResult; - if (cancelOnResult) { - [self dismissViewControllerAnimated:YES completion:nil]; - } - } else if ([scanViewPluginBase isKindOfClass:ALViewPluginComposite.class]) { - // for composites, the cancelOnResult values for each child don't matter - [self dismissViewControllerAnimated:YES completion:nil]; - } - self.callback(resultDictionary, nil); -} - -- (void)doneButtonPressed:(id)sender { - [self stopMRZScanning]; - - __weak __block typeof(self) weakSelf = self; - [self dismissViewControllerAnimated:YES completion:^{ - weakSelf.callback(nil, [NSError errorWithDomain:@"ALFlutterDomain" code:-1 userInfo:@{@"Error reason": @"Canceled"}]); - }]; -} - -// MARK: - ALNFCDetectorDelegate - -- (void)nfcSucceededWithResult:(ALNFCResult * _Nonnull)nfcResult API_AVAILABLE(ios(13.0)) { - - // DataGroup1 - NSMutableDictionary *dictResultDataGroup1 = [[NSMutableDictionary alloc] init]; - - - [dictResultDataGroup1 setValue:[ALPluginHelper stringForDate:nfcResult.dataGroup1.dateOfBirth] - forKey:@"dateOfBirth"]; - [dictResultDataGroup1 setValue:[ALPluginHelper stringForDate:nfcResult.dataGroup1.dateOfExpiry] - forKey:@"dateOfExpiry"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.documentNumber forKey:@"documentNumber"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.documentType forKey:@"documentType"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.firstName forKey:@"firstName"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.gender forKey:@"gender"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.issuingStateCode forKey:@"issuingStateCode"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.lastName forKey:@"lastName"]; - [dictResultDataGroup1 setValue:nfcResult.dataGroup1.nationality forKey:@"nationality"]; - - [self.resultDict setObject:dictResultDataGroup1 forKey:@"dataGroup1"]; - - // DataGroup2 - // ACO: we don't put the path into a separate 'dataGroup' category for the wrapper - NSString *imagePath = [ALPluginHelper saveImageToFileSystem:nfcResult.dataGroup2.faceImage - compressionQuality:self.quality / (CGFloat)100.0f]; - if (imagePath) { - [self.resultDict setValue:imagePath forKey:@"imagePath"]; - } - - // SOD (Passport metadata) - NSMutableDictionary *dictResultSOD = [[NSMutableDictionary alloc] init]; - - [dictResultSOD setValue:nfcResult.sod.issuerCertificationAuthority forKey:@"issuerCertificationAuthority"]; - [dictResultSOD setValue:nfcResult.sod.issuerCountry forKey:@"issuerCountry"]; - [dictResultSOD setValue:nfcResult.sod.issuerOrganization forKey:@"issuerOrganization"]; - [dictResultSOD setValue:nfcResult.sod.issuerOrganizationalUnit forKey:@"issuerOrganizationalUnit"]; - [dictResultSOD setValue:nfcResult.sod.ldsHashAlgorithm forKey:@"ldsHashAlgorithm"]; - - // ACO need to disable this because AppStoreConnect produces a warning with this enabled: - // https://anyline.atlassian.net/browse/SDKY-1509?focusedCommentId=71554 - // [dictResultSOB setValue:nfcResult.sod.signatureAlgorithm forKey:@"signatureAlgorithm"]; - - [dictResultSOD setValue:nfcResult.sod.validFromString forKey:@"validFromString"]; - [dictResultSOD setValue:nfcResult.sod.validUntilString forKey:@"validUntilString"]; - - [self.resultDict setObject:dictResultSOD forKey:@"sod"]; - - __weak __block typeof(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf handleResult:weakSelf.resultDict]; - }); -} - -- (void)nfcFailedWithError:(NSError * _Nonnull)error { - dispatch_async(dispatch_get_main_queue(), ^{ - if (error.code == ALNFCTagErrorNFCNotSupported) { - [self showAlertWithTitle:@"NFC Not Supported" - message:@"NFC passport reading is not supported on this device."]; - } - if (error.code == ALNFCTagErrorResponseError || // MRZ key was likely wrong - error.code == ALNFCTagErrorUnexpectedError) { - // can mean the user pressed the 'Cancel' button while scanning, or the phone lost the - // connection with the NFC chip because it was moved - [self startMRZScanning:nil]; //run the MRZ scanner so we can try again. - } else { - // the MRZ details are correct, but something else went wrong. We can try reading - // the NFC chip again without rescanning the MRZ. - __weak __block typeof(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf.nfcDetector startNfcDetectionWithPassportNumber:weakSelf.passportNumberForNFC - dateOfBirth:weakSelf.dateOfBirth - expirationDate:weakSelf.dateOfExpiry]; - }); - } - }); -} - -// MARK: - ALScanViewDelegate - -- (void)scanView:(ALScanView *)scanView updatedCutoutWithPluginID:(NSString *)pluginID - frame:(CGRect)frame { - - if (CGRectIsEmpty(frame)) { - return; - } - - self.hintView.hidden = NO; - - CGFloat xOffset = self.uiConfig.labelXPositionOffset; - CGFloat yOffset = self.uiConfig.labelYPositionOffset; - - // takes into account that frame reported for a cutout is in relation to - // its scan view's coordinate system - yOffset += [self.scanView convertRect:frame toView:self.scanView.superview].origin.y; - - self.labelHorizontalOffsetConstraint.constant = xOffset; - self.labelVerticalOffsetConstraint.constant = yOffset; -} - -- (void)scanView:(ALScanView *)scanView didReceiveNativeBarcodeResult:(ALScanResult *)scanResult { - // for this implementation we just take the last detected (we can show a list of it) - [self.detectedBarcodes removeAllObjects]; - [self.detectedBarcodes addObject:scanResult.resultDictionary]; -} - - -// MARK: - ALNFCDetector - -+ (ALNFCDetector * _Nullable)getNFCDetectorWithDelegate:(id _Nonnull)delegate - error:(NSError * _Nullable * _Nullable)error API_AVAILABLE(ios(13.0)) { - ALNFCDetector *NFCDetector; - if (![ALNFCDetector readingAvailable]) { - if (error) { - *error = [ALPluginHelper errorWithMessage:@"NFC is not available for this device."]; - } - return nil; - } - NFCDetector = [[ALNFCDetector alloc] initWithDelegate:delegate error:error]; - if (!NFCDetector) { - return nil; - } - return NFCDetector; -} - -// MARK: - Alerts - -- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { - [ALPluginHelper showErrorAlertWithTitle:title message:message - presentingViewController:self.navigationController]; -} - -- (BOOL)showErrorAlertIfNeeded:(NSError *)error { - return [ALPluginHelper showErrorAlertIfNeeded:error pluginCallback:self.callback]; -} - -// MARK: - User Interface - -- (void)setupHintView { - - UIView *hintView = [[UIView alloc] initWithFrame:CGRectZero]; - hintView.layer.cornerRadius = 8; - hintView.layer.masksToBounds = true; - hintView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; - - hintView.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:hintView]; - - self.labelHorizontalOffsetConstraint = [hintView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant:0]; - - // at 0, the bottom of the label (container) should be touching the cutout top when the updatedCutoutRect report - // is made - self.labelVerticalOffsetConstraint = [hintView.bottomAnchor constraintEqualToAnchor:self.view.topAnchor constant:0]; - - self.labelHorizontalOffsetConstraint.active = YES; - self.labelVerticalOffsetConstraint.active = YES; - - UILabel *hintViewLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - - hintViewLabel.text = @"Scan MRZ"; - if (self.uiConfig.labelText.length) { - hintViewLabel.text = self.uiConfig.labelText; - } - - hintViewLabel.font = [UIFont fontWithName:@"HelveticaNeue" - size:self.uiConfig.labelSize]; - hintViewLabel.textColor = self.uiConfig.labelColor; - hintViewLabel.textAlignment = NSTextAlignmentCenter; - hintViewLabel.translatesAutoresizingMaskIntoConstraints = NO; - [hintViewLabel sizeToFit]; - [hintView addSubview:hintViewLabel]; - - [hintViewLabel.centerXAnchor constraintEqualToAnchor:hintView.centerXAnchor].active = YES; - [hintViewLabel.centerYAnchor constraintEqualToAnchor:hintView.centerYAnchor].active = YES; - - NSLayoutConstraint *cnst = [hintView.widthAnchor constraintGreaterThanOrEqualToAnchor:hintViewLabel.widthAnchor - constant:20]; - cnst.active = YES; - cnst = [hintView.heightAnchor constraintEqualToAnchor:hintViewLabel.heightAnchor constant:20]; - cnst.active = YES; - - self.hintView = hintView; - self.hintViewLabel = hintViewLabel; -} - --(BOOL)isStringEmpty:(NSString *)str { - if(str == nil || [str isKindOfClass:[NSNull class]] || str.length==0) { - return YES; - } - return NO; - } - - -@end diff --git a/ios/Classes/ALPluginHelper.h b/ios/Classes/ALPluginHelper.h index 7a5dc9f..7806806 100644 --- a/ios/Classes/ALPluginHelper.h +++ b/ios/Classes/ALPluginHelper.h @@ -39,7 +39,7 @@ typedef void (^ALPluginCallback)(NSDictionary * _Nullable callbackObj, NSError * + (NSError *)errorWithMessage:(NSString *)message; -+ (NSDate *)formattedStringToDate:(NSString *)formattedStr; ++ (NSDate * _Nullable)formattedStringToDate:(NSString *)formattedStr; + (NSString *)stringForDate:(NSDate *)date; diff --git a/ios/Classes/ALPluginHelper.m b/ios/Classes/ALPluginHelper.m index 6202105..533a77c 100644 --- a/ios/Classes/ALPluginHelper.m +++ b/ios/Classes/ALPluginHelper.m @@ -1,5 +1,5 @@ #import "ALPluginHelper.h" -#import "ALNFCScanViewController.h" // because NFC-specific code is there +#import "ALPluginScanViewController.h" #import #import @@ -21,55 +21,28 @@ + (void)startScan:(NSDictionary *)config initializationParamsStr:(NSString *)ini NSDictionary *optionsDict = [config objectForKey:@"options"]; ALJSONUIConfiguration *jsonUIConf = [[ALJSONUIConfiguration alloc] initWithDictionary:optionsDict]; - - BOOL isNFC = [optionsDict[@"enableNFCWithMRZ"] boolValue]; - - if (isNFC) { - if (@available(iOS 13.0, *)) { - - if (![ALNFCDetector readingAvailable]) { - callback(nil, [NSError errorWithDomain:ALFlutterDomain code:100 userInfo:@{@"Error reason": @"NFC passport reading is not supported on this device or app."}]); - return; - } - - ALNFCScanViewController *nfcScanViewController = [[ALNFCScanViewController alloc] initWithLicensekey:licenseKey - configuration:pluginConf - uiConfig:jsonUIConf - initializationParamsStr:initializationParamsStr - finished:callback]; - if (nfcScanViewController != nil) { - [nfcScanViewController setModalPresentationStyle:UIModalPresentationFullScreen]; - [presentingViewController presentViewController:nfcScanViewController - animated:YES - completion:nil]; - } - } else { - callback(nil, [NSError errorWithDomain:ALFlutterDomain code:100 userInfo:@{@"Error reason": @"NFC passport reading is only supported on iOS 13 and later."}]); - return; - } - } else { - ALPluginScanViewController *pluginScanViewController = [[ALPluginScanViewController alloc] initWithLicensekey:licenseKey - configuration:pluginConf - uiConfiguration:jsonUIConf - initializationParamsStr:initializationParamsStr - finished:callback]; - - // TODO: should remove these extras - if ([pluginConf valueForKey:@"quality"]){ - pluginScanViewController.quality = [[pluginConf valueForKey:@"quality"] integerValue]; - } - - if ([pluginConf valueForKey:@"cropAndTransformErrorMessage"]) { - NSString *str = [pluginConf objectForKey:@"cropAndTransformErrorMessage"]; - pluginScanViewController.cropAndTransformErrorMessage = str; - } - - if (pluginScanViewController) { - [pluginScanViewController setModalPresentationStyle:UIModalPresentationFullScreen]; - [presentingViewController presentViewController:pluginScanViewController - animated:YES - completion:nil]; - } + + ALPluginScanViewController *pluginScanViewController = [[ALPluginScanViewController alloc] initWithLicensekey:licenseKey + configuration:pluginConf + uiConfiguration:jsonUIConf + initializationParamsStr:initializationParamsStr + finished:callback]; + + // TODO: should remove these extras + if ([pluginConf valueForKey:@"quality"]){ + pluginScanViewController.quality = [[pluginConf valueForKey:@"quality"] integerValue]; + } + + if ([pluginConf valueForKey:@"cropAndTransformErrorMessage"]) { + NSString *str = [pluginConf objectForKey:@"cropAndTransformErrorMessage"]; + pluginScanViewController.cropAndTransformErrorMessage = str; + } + + if (pluginScanViewController) { + [pluginScanViewController setModalPresentationStyle:UIModalPresentationFullScreen]; + [presentingViewController presentViewController:pluginScanViewController + animated:YES + completion:nil]; } } @@ -316,7 +289,7 @@ + (UIViewController *)topMostViewController { return vc; } -+ (NSDate *)formattedStringToDate:(NSString *)formattedStr { ++ (NSDate * _Nullable)formattedStringToDate:(NSString *)formattedStr { // From this: "Sun Apr 12 00:00:00 UTC 1977" to this: "04/12/1977" NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT+0:00"]]; diff --git a/ios/Classes/ALRoundedView.m b/ios/Classes/ALRoundedView.m index 2e37d84..73b0d47 100644 --- a/ios/Classes/ALRoundedView.m +++ b/ios/Classes/ALRoundedView.m @@ -48,11 +48,7 @@ - (void)setText:(NSString*)text; { - (void)drawRect:(CGRect)rect { [super drawRect:rect]; - - //CGContextRef context = UIGraphicsGetCurrentContext(); - //CGContextSaveGState(context); - //CGContextSetBlendMode(context, kCGBlendModeDestinationOut); - + CGRect frame = CGRectInset(self.bounds, 0, 0); CGRect mainRect = frame; @@ -78,8 +74,6 @@ - (void)drawRect:(CGRect)rect { mainRect = CGRectInset(frame, _borderWidth, _borderWidth); } - - //CGContextRestoreGState(context); } - (void)setBorderColor:(UIColor *)borderColor { diff --git a/ios/anyline_plugin.podspec b/ios/anyline_plugin.podspec index 42b3453..df83bf7 100644 --- a/ios/anyline_plugin.podspec +++ b/ios/anyline_plugin.podspec @@ -4,19 +4,19 @@ # Pod::Spec.new do |s| s.name = 'anyline_plugin' - s.version = '53.0.0' + s.version = '54.0.0' s.summary = 'Anyline SDK' s.description = <<-DESC Anyline OCR Module DESC s.homepage = 'http://example.com' s.license = { :file => '../LICENSE' } - s.author = { 'Anyline GmbH' => 'daniel@anyline.com' } + s.author = { 'Anyline GmbH' => 'capture-team@anyline.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'Anyline', '~> 53' + s.dependency 'Anyline', '54.0.0' s.static_framework = true s.platform = :ios, '12.0' s.ios.deployment_target = '12.0' diff --git a/lib/anyline_plugin.dart b/lib/anyline_plugin.dart index fe5a610..8ab94e5 100644 --- a/lib/anyline_plugin.dart +++ b/lib/anyline_plugin.dart @@ -3,15 +3,16 @@ import 'dart:convert'; import 'package:anyline_plugin/constants.dart'; import 'package:anyline_plugin/exceptions.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; -/// Entrypoint for perfoming any scans using the Anyline OCR library. +/// Entrypoint for performing any scans using the Anyline OCR library. class AnylinePlugin { - static const MethodChannel _channel = const MethodChannel('anyline_plugin'); AnylinePlugin(); + static const MethodChannel _channel = MethodChannel('anyline_plugin'); /// Returns the Anyline SDK version the plugin currently is powered by. static Future get sdkVersion async { @@ -25,7 +26,7 @@ class AnylinePlugin { final fileContent = await rootBundle.loadString('packages/anyline_plugin/pubspec.yaml'); final pubspec = Pubspec.parse(fileContent); - return pubspec.version?.canonicalizedVersion ?? ""; + return pubspec.version?.canonicalizedVersion ?? ''; } void setCustomModelsPath(String customModelsPath) { @@ -56,7 +57,9 @@ class AnylinePlugin { await _channel.invokeMethod(Constants.METHOD_SET_LICENSE_KEY, params); return result; } on PlatformException catch (e) { - print("${e.message}"); + if (kDebugMode) { + print('${e.message}'); + } throw AnylineException.parse(e); } } @@ -82,7 +85,9 @@ class AnylinePlugin { await _channel.invokeMethod(Constants.METHOD_START_ANYLINE, config); return result; } on PlatformException catch (e) { - print("${e.message}"); + if (kDebugMode) { + print('${e.message}'); + } throw AnylineException.parse(e); } } else { @@ -97,7 +102,7 @@ class AnylinePlugin { static String? getLicenseExpiryDate(String base64License) { Map licenseMap = _decodeBase64LicenseToJsonMap(base64License)!; - return licenseMap['valid']; + return licenseMap['valid'] as String?; } // Export all cached events and return the created zip file path. @@ -111,7 +116,9 @@ class AnylinePlugin { Constants.METHOD_EXPORT_CACHED_EVENTS, null); return result; } on PlatformException catch (e) { - print("${e.message}"); + if (kDebugMode) { + print('${e.message}'); + } throw AnylineException.parse(e); } } @@ -121,7 +128,7 @@ class AnylinePlugin { Codec base64ToString = ascii.fuse(base64); String licenseString = base64ToString.decode(base64License); String licenseJson = _extractJsonFromLicenseString(licenseString); - return jsonDecode(licenseJson); + return jsonDecode(licenseJson) as Map?; } static String _extractJsonFromLicenseString(String licenseJson) { diff --git a/lib/constants.dart b/lib/constants.dart index 26d35f2..5d97b0d 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,29 +1,29 @@ /// Needed for communication with the native SDK. abstract class Constants { - static const String METHOD_GET_SDK_VERSION = "METHOD_GET_SDK_VERSION"; + static const String METHOD_GET_SDK_VERSION = 'METHOD_GET_SDK_VERSION'; static const String METHOD_SET_CUSTOM_MODELS_PATH = - "METHOD_SET_CUSTOM_MODELS_PATH"; + 'METHOD_SET_CUSTOM_MODELS_PATH'; static const String METHOD_SET_VIEW_CONFIGS_PATH = - "METHOD_SET_VIEW_CONFIGS_PATH"; - static const String METHOD_SET_LICENSE_KEY = "METHOD_SET_LICENSE_KEY"; - static const String METHOD_START_ANYLINE = "METHOD_START_ANYLINE"; + 'METHOD_SET_VIEW_CONFIGS_PATH'; + static const String METHOD_SET_LICENSE_KEY = 'METHOD_SET_LICENSE_KEY'; + static const String METHOD_START_ANYLINE = 'METHOD_START_ANYLINE'; static const String METHOD_GET_APPLICATION_CACHE_PATH = - "METHOD_GET_APPLICATION_CACHE_PATH"; + 'METHOD_GET_APPLICATION_CACHE_PATH'; static const String METHOD_EXPORT_CACHED_EVENTS = - "METHOD_EXPORT_CACHED_EVENTS"; + 'METHOD_EXPORT_CACHED_EVENTS'; - static const String EXTRA_CONFIG_JSON = "EXTRA_CONFIG_JSON"; - static const String EXTRA_INITIALIZATION_PARAMETERS = "EXTRA_INITIALIZATION_PARAMETERS"; - static const String EXTRA_LICENSE_KEY = "EXTRA_LICENSE_KEY"; - static const String EXTRA_ENABLE_OFFLINE_CACHE = "EXTRA_ENABLE_OFFLINE_CACHE"; - static const String EXTRA_PLUGIN_VERSION = "EXTRA_PLUGIN_VERSION"; - static const String EXTRA_CUSTOM_MODELS_PATH = "EXTRA_CUSTOM_MODELS_PATH"; - static const String EXTRA_VIEW_CONFIGS_PATH = "EXTRA_VIEW_CONFIGS_PATH"; + static const String EXTRA_CONFIG_JSON = 'EXTRA_CONFIG_JSON'; + static const String EXTRA_INITIALIZATION_PARAMETERS = 'EXTRA_INITIALIZATION_PARAMETERS'; + static const String EXTRA_LICENSE_KEY = 'EXTRA_LICENSE_KEY'; + static const String EXTRA_ENABLE_OFFLINE_CACHE = 'EXTRA_ENABLE_OFFLINE_CACHE'; + static const String EXTRA_PLUGIN_VERSION = 'EXTRA_PLUGIN_VERSION'; + static const String EXTRA_CUSTOM_MODELS_PATH = 'EXTRA_CUSTOM_MODELS_PATH'; + static const String EXTRA_VIEW_CONFIGS_PATH = 'EXTRA_VIEW_CONFIGS_PATH'; - static const String EXCEPTION_DEFAULT = "AnylineException"; - static const String EXCEPTION_LICENSE = "AnylineLicenseException"; - static const String EXCEPTION_CONFIG = "AnylineConfigException"; + static const String EXCEPTION_DEFAULT = 'AnylineException'; + static const String EXCEPTION_LICENSE = 'AnylineLicenseException'; + static const String EXCEPTION_CONFIG = 'AnylineConfigException'; static const String EXCEPTION_NO_CAMERA_PERMISSION = - "AnylineCameraPermissionException"; - static const String EXCEPTION_CORE = "AnylineCoreException"; + 'AnylineCameraPermissionException'; + static const String EXCEPTION_CORE = 'AnylineCoreException'; } diff --git a/lib/exceptions.dart b/lib/exceptions.dart index e9e6a76..c576fa8 100644 --- a/lib/exceptions.dart +++ b/lib/exceptions.dart @@ -4,9 +4,9 @@ import 'package:flutter/services.dart'; /// Custom exception including parser to correctly interpret and propagate errors /// coming from the native SDK. class AnylineException implements Exception { - final String? message; const AnylineException(this.message); + final String? message; static AnylineException parse(Exception e) { if (e is PlatformException) { diff --git a/pubspec.yaml b/pubspec.yaml index 7c859bc..22f90e1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: anyline_plugin description: Flutter Plugin for Anyline OCR, which allows you to scan all kinds of numbers, characters, text and codes. -version: 53.3.0 +version: 54.0.0 homepage: https://anyline.com repository: https://github.com/Anyline/anyline-ocr-flutter-module documentation: https://documentation.anyline.com @@ -12,13 +12,15 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^11.0.1 + permission_handler: ^10.3.6 pubspec_parse: ^1.2.0 dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^3.0.0 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec