| @@ -7,7 +7,6 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin | |||||
| import io.flutter.embedding.engine.plugins.activity.ActivityAware | import io.flutter.embedding.engine.plugins.activity.ActivityAware | ||||
| import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding | ||||
| import io.flutter.plugin.common.BinaryMessenger | import io.flutter.plugin.common.BinaryMessenger | ||||
| import io.flutter.plugin.common.MethodChannel | |||||
| import io.flutter.plugin.common.PluginRegistry | import io.flutter.plugin.common.PluginRegistry | ||||
| import io.flutter.plugin.platform.PlatformViewRegistry | import io.flutter.plugin.platform.PlatformViewRegistry | ||||
| @@ -3,7 +3,6 @@ package net.touchcapture.qr.flutterqr | |||||
| import android.Manifest | import android.Manifest | ||||
| import android.app.Activity | import android.app.Activity | ||||
| import android.app.Application | import android.app.Application | ||||
| import android.content.Context | |||||
| import android.content.pm.PackageManager | import android.content.pm.PackageManager | ||||
| import android.os.Bundle | import android.os.Bundle | ||||
| import android.view.View | import android.view.View | ||||
| @@ -18,27 +17,12 @@ import io.flutter.plugin.common.BinaryMessenger | |||||
| import io.flutter.plugin.common.MethodCall | import io.flutter.plugin.common.MethodCall | ||||
| import io.flutter.plugin.common.MethodChannel | import io.flutter.plugin.common.MethodChannel | ||||
| import io.flutter.plugin.platform.PlatformView | import io.flutter.plugin.platform.PlatformView | ||||
| class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, private val params: HashMap<String, Any>) : | |||||
| class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap<String, Any>) : | |||||
| PlatformView, MethodChannel.MethodCallHandler { | PlatformView, MethodChannel.MethodCallHandler { | ||||
| private var isTorchOn: Boolean = false | private var isTorchOn: Boolean = false | ||||
| private var barcodeView: BarcodeView? = null | private var barcodeView: BarcodeView? = null | ||||
| private val channel: MethodChannel | private val channel: MethodChannel | ||||
| var allowedBarcodeTypes = mutableListOf<BarcodeFormat>() | |||||
| private val qrCodeTypes = mapOf( | |||||
| 0 to BarcodeFormat.AZTEC, | |||||
| 1 to BarcodeFormat.CODE_128, | |||||
| 2 to BarcodeFormat.CODE_39, | |||||
| 3 to BarcodeFormat.CODE_93, | |||||
| 4 to BarcodeFormat.DATA_MATRIX, | |||||
| 5 to BarcodeFormat.EAN_13, | |||||
| 6 to BarcodeFormat.EAN_8, | |||||
| 7 to BarcodeFormat.ITF, | |||||
| 8 to BarcodeFormat.PDF_417, | |||||
| 9 to BarcodeFormat.QR_CODE, | |||||
| 10 to BarcodeFormat.UPC_E | |||||
| ) | |||||
| init { | init { | ||||
| checkAndRequestPermission(null) | checkAndRequestPermission(null) | ||||
| @@ -81,7 +65,7 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, | |||||
| override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | ||||
| when(call.method) { | when(call.method) { | ||||
| "startScan" -> startScan() | |||||
| "startScan" -> startScan(call.arguments as? List<Int>, result) | |||||
| "stopScan" -> stopScan() | "stopScan" -> stopScan() | ||||
| "flipCamera" -> flipCamera(result) | "flipCamera" -> flipCamera(result) | ||||
| "toggleFlash" -> toggleFlash(result) | "toggleFlash" -> toggleFlash(result) | ||||
| @@ -90,9 +74,7 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, | |||||
| "requestPermissions" -> checkAndRequestPermission(result) | "requestPermissions" -> checkAndRequestPermission(result) | ||||
| "getCameraInfo" -> getCameraInfo(result) | "getCameraInfo" -> getCameraInfo(result) | ||||
| "getFlashInfo" -> getFlashInfo(result) | "getFlashInfo" -> getFlashInfo(result) | ||||
| // "showNativeAlertDialog" -> showNativeAlertDialog(result) | |||||
| "getSystemFeatures" -> getSystemFeatures(result) | "getSystemFeatures" -> getSystemFeatures(result) | ||||
| "setAllowedBarcodeFormats" -> setBarcodeFormats(call.arguments as List<Int>, result) | |||||
| else -> result.notImplemented() | else -> result.notImplemented() | ||||
| } | } | ||||
| } | } | ||||
| @@ -209,7 +191,16 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, | |||||
| return barcodeView | return barcodeView | ||||
| } | } | ||||
| private fun startScan() { | |||||
| private fun startScan(arguments: List<Int>?, result: MethodChannel.Result) { | |||||
| val allowedBarcodeTypes = mutableListOf<BarcodeFormat>() | |||||
| try { | |||||
| arguments?.forEach { | |||||
| allowedBarcodeTypes.add(BarcodeFormat.values()[it]) | |||||
| } | |||||
| } catch (e: java.lang.Exception) { | |||||
| result.error(null, null, null) | |||||
| } | |||||
| barcodeView?.decodeContinuous( | barcodeView?.decodeContinuous( | ||||
| object : BarcodeCallback { | object : BarcodeCallback { | ||||
| override fun barcodeResult(result: BarcodeResult) { | override fun barcodeResult(result: BarcodeResult) { | ||||
| @@ -242,29 +233,6 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context, | |||||
| } | } | ||||
| } | } | ||||
| private fun setBarcodeFormats(arguments: List<Int>, result: MethodChannel.Result) { | |||||
| try { | |||||
| allowedBarcodeTypes.clear() | |||||
| arguments.forEach { | |||||
| allowedBarcodeTypes.add(qrCodeTypes[it]!!) | |||||
| } | |||||
| result.success(true) | |||||
| } catch (e: java.lang.Exception) { | |||||
| result.error(null, null, null) | |||||
| } | |||||
| } | |||||
| // private fun showNativeAlertDialog(result: MethodChannel.Result) { | |||||
| // AlertDialog.Builder(context) | |||||
| // .setTitle("Scanning Unavailable") | |||||
| // .setMessage("This app does not have permission to access the camera") | |||||
| // .setPositiveButton(R.string.ok, null) | |||||
| // .setCancelable(false) | |||||
| // .setIcon(R.drawable.ic_dialog_alert) | |||||
| // .show() | |||||
| // result.success(true) | |||||
| // } | |||||
| private fun hasCameraPermission(): Boolean { | private fun hasCameraPermission(): Boolean { | ||||
| return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || | return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || | ||||
| Shared.activity?.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED | Shared.activity?.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED | ||||
| @@ -12,7 +12,7 @@ class QRViewFactory(private val messenger: BinaryMessenger) : | |||||
| override fun create(context: Context, id: Int, args: Any?): PlatformView { | override fun create(context: Context, id: Int, args: Any?): PlatformView { | ||||
| val params = args as HashMap<String, Any> | val params = args as HashMap<String, Any> | ||||
| return QRView(messenger, id, context, params) | |||||
| return QRView(messenger, id, params) | |||||
| } | } | ||||
| } | } | ||||
| @@ -9,11 +9,11 @@ | |||||
| /* Begin PBXBuildFile section */ | /* Begin PBXBuildFile section */ | ||||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | ||||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | ||||
| 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75B64521967C76C845268E71 /* Pods_Runner.framework */; }; | |||||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | ||||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | ||||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | ||||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | ||||
| F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */; }; | |||||
| /* End PBXBuildFile section */ | /* End PBXBuildFile section */ | ||||
| /* Begin PBXCopyFilesBuildPhase section */ | /* Begin PBXCopyFilesBuildPhase section */ | ||||
| @@ -30,15 +30,14 @@ | |||||
| /* End PBXCopyFilesBuildPhase section */ | /* End PBXCopyFilesBuildPhase section */ | ||||
| /* Begin PBXFileReference section */ | /* Begin PBXFileReference section */ | ||||
| 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||||
| 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | |||||
| 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | ||||
| 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | ||||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | ||||
| 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||||
| 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | ||||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||||
| 75B64521967C76C845268E71 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | ||||
| 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | |||||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | ||||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | ||||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| @@ -46,7 +45,8 @@ | |||||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||||
| 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||||
| 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||||
| EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||||
| /* End PBXFileReference section */ | /* End PBXFileReference section */ | ||||
| /* Begin PBXFrameworksBuildPhase section */ | /* Begin PBXFrameworksBuildPhase section */ | ||||
| @@ -54,21 +54,13 @@ | |||||
| isa = PBXFrameworksBuildPhase; | isa = PBXFrameworksBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */, | |||||
| 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| /* End PBXFrameworksBuildPhase section */ | /* End PBXFrameworksBuildPhase section */ | ||||
| /* Begin PBXGroup section */ | /* Begin PBXGroup section */ | ||||
| 509AC3E235E568B6DDAAEC7D /* Frameworks */ = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */, | |||||
| ); | |||||
| name = Frameworks; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| 9740EEB11CF90186004384FC /* Flutter */ = { | 9740EEB11CF90186004384FC /* Flutter */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -87,7 +79,7 @@ | |||||
| 97C146F01CF9000F007C117D /* Runner */, | 97C146F01CF9000F007C117D /* Runner */, | ||||
| 97C146EF1CF9000F007C117D /* Products */, | 97C146EF1CF9000F007C117D /* Products */, | ||||
| D7C58E57CAF69D9A14CC212F /* Pods */, | D7C58E57CAF69D9A14CC212F /* Pods */, | ||||
| 509AC3E235E568B6DDAAEC7D /* Frameworks */, | |||||
| AF031A634C9742F16BF4F89A /* Frameworks */, | |||||
| ); | ); | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| }; | }; | ||||
| @@ -122,12 +114,20 @@ | |||||
| name = "Supporting Files"; | name = "Supporting Files"; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| }; | }; | ||||
| AF031A634C9742F16BF4F89A /* Frameworks */ = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| 75B64521967C76C845268E71 /* Pods_Runner.framework */, | |||||
| ); | |||||
| name = Frameworks; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| D7C58E57CAF69D9A14CC212F /* Pods */ = { | D7C58E57CAF69D9A14CC212F /* Pods */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */, | |||||
| 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */, | |||||
| 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */, | |||||
| DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */, | |||||
| EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */, | |||||
| 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */, | |||||
| ); | ); | ||||
| path = Pods; | path = Pods; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| @@ -139,14 +139,14 @@ | |||||
| isa = PBXNativeTarget; | isa = PBXNativeTarget; | ||||
| buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | ||||
| buildPhases = ( | buildPhases = ( | ||||
| 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */, | |||||
| 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */, | |||||
| 9740EEB61CF901F6004384FC /* Run Script */, | 9740EEB61CF901F6004384FC /* Run Script */, | ||||
| 97C146EA1CF9000F007C117D /* Sources */, | 97C146EA1CF9000F007C117D /* Sources */, | ||||
| 97C146EB1CF9000F007C117D /* Frameworks */, | 97C146EB1CF9000F007C117D /* Frameworks */, | ||||
| 97C146EC1CF9000F007C117D /* Resources */, | 97C146EC1CF9000F007C117D /* Resources */, | ||||
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | 9705A1C41CF9048500538489 /* Embed Frameworks */, | ||||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||||
| FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */, | |||||
| 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */, | |||||
| ); | ); | ||||
| buildRules = ( | buildRules = ( | ||||
| ); | ); | ||||
| @@ -221,7 +221,7 @@ | |||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | ||||
| }; | }; | ||||
| 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */ = { | |||||
| 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| @@ -243,39 +243,41 @@ | |||||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||||
| showEnvVarsInLog = 0; | showEnvVarsInLog = 0; | ||||
| }; | }; | ||||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||||
| 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| ); | ); | ||||
| inputPaths = ( | inputPaths = ( | ||||
| "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||||
| "${PODS_ROOT}/../Flutter/Flutter.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| name = "Run Script"; | |||||
| name = "[CP] Embed Pods Frameworks"; | |||||
| outputPaths = ( | outputPaths = ( | ||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||||
| showEnvVarsInLog = 0; | |||||
| }; | }; | ||||
| FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */ = { | |||||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| ); | ); | ||||
| inputPaths = ( | inputPaths = ( | ||||
| "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||||
| "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| name = "[CP] Embed Pods Frameworks"; | |||||
| name = "Run Script"; | |||||
| outputPaths = ( | outputPaths = ( | ||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||||
| showEnvVarsInLog = 0; | |||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||||
| }; | }; | ||||
| /* End PBXShellScriptBuildPhase section */ | /* End PBXShellScriptBuildPhase section */ | ||||
| @@ -132,6 +132,7 @@ class _QRViewExampleState extends State<QRViewExample> { | |||||
| key: qrKey, | key: qrKey, | ||||
| cameraFacing: CameraFacing.front, | cameraFacing: CameraFacing.front, | ||||
| onQRViewCreated: _onQRViewCreated, | onQRViewCreated: _onQRViewCreated, | ||||
| formatsAllowed: [BarcodeFormat.qrcode], | |||||
| overlay: QrScannerOverlayShape( | overlay: QrScannerOverlayShape( | ||||
| borderColor: Colors.red, | borderColor: Colors.red, | ||||
| borderRadius: 10, | borderRadius: 10, | ||||
| @@ -15,20 +15,18 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| var channel: FlutterMethodChannel | var channel: FlutterMethodChannel | ||||
| var cameraFacing: MTBCamera | var cameraFacing: MTBCamera | ||||
| var allowedBarcodeTypes: Array<AVMetadataObject.ObjectType> = [] | |||||
| var QRCodeTypes = [ | var QRCodeTypes = [ | ||||
| 0: AVMetadataObject.ObjectType.aztec, | 0: AVMetadataObject.ObjectType.aztec, | ||||
| 1: AVMetadataObject.ObjectType.code128, | |||||
| 2: AVMetadataObject.ObjectType.code39, | 2: AVMetadataObject.ObjectType.code39, | ||||
| 3: AVMetadataObject.ObjectType.code93, | 3: AVMetadataObject.ObjectType.code93, | ||||
| 4: AVMetadataObject.ObjectType.dataMatrix, | |||||
| 5: AVMetadataObject.ObjectType.ean13, | |||||
| 4: AVMetadataObject.ObjectType.code128, | |||||
| 5: AVMetadataObject.ObjectType.dataMatrix, | |||||
| 6: AVMetadataObject.ObjectType.ean8, | 6: AVMetadataObject.ObjectType.ean8, | ||||
| 7: AVMetadataObject.ObjectType.interleaved2of5, | |||||
| 8: AVMetadataObject.ObjectType.pdf417, | |||||
| 9: AVMetadataObject.ObjectType.qr, | |||||
| 10: AVMetadataObject.ObjectType.upce | |||||
| 7: AVMetadataObject.ObjectType.ean13, | |||||
| 8: AVMetadataObject.ObjectType.interleaved2of5, | |||||
| 10: AVMetadataObject.ObjectType.pdf417, | |||||
| 11: AVMetadataObject.ObjectType.qr, | |||||
| 15: AVMetadataObject.ObjectType.upce | |||||
| ] | ] | ||||
| public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary<String, Any>){ | public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary<String, Any>){ | ||||
| @@ -50,7 +48,7 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| let arguments = call.arguments as! Dictionary<String, Double> | let arguments = call.arguments as! Dictionary<String, Double> | ||||
| self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) | self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) | ||||
| case "startScan": | case "startScan": | ||||
| self?.startScan(result) | |||||
| self?.startScan(call.arguments as! Array<Int>, result) | |||||
| case "flipCamera": | case "flipCamera": | ||||
| self?.flipCamera(result) | self?.flipCamera(result) | ||||
| case "toggleFlash": | case "toggleFlash": | ||||
| @@ -63,12 +61,8 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| self?.getCameraInfo(result) | self?.getCameraInfo(result) | ||||
| case "getFlashInfo": | case "getFlashInfo": | ||||
| self?.getFlashInfo(result) | self?.getFlashInfo(result) | ||||
| case "showNativeAlertDialog": | |||||
| self?.showNativeAlertDialog(result) | |||||
| case "getSystemFeatures": | case "getSystemFeatures": | ||||
| self?.getSystemFeatures(result) | self?.getSystemFeatures(result) | ||||
| case "setAllowedBarcodeFormats": | |||||
| self?.setBarcodeFormats(call.arguments as! Array<Int>, result) | |||||
| default: | default: | ||||
| result(FlutterMethodNotImplemented) | result(FlutterMethodNotImplemented) | ||||
| return | return | ||||
| @@ -96,45 +90,51 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| } | } | ||||
| } | } | ||||
| func startScan(_ result: @escaping FlutterResult) -> Void { | |||||
| func startScan(_ arguments: Array<Int>, _ result: @escaping FlutterResult) -> Void { | |||||
| scanner = MTBBarcodeScanner(previewView: previewView) | scanner = MTBBarcodeScanner(previewView: previewView) | ||||
| var allowedBarcodeTypes: Array<AVMetadataObject.ObjectType> = [] | |||||
| arguments.forEach { arg in | |||||
| allowedBarcodeTypes.append( QRCodeTypes[arg]!) | |||||
| } | |||||
| MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in | MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in | ||||
| if permissionGranted { | if permissionGranted { | ||||
| do { | do { | ||||
| try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in | try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in | ||||
| if let codes = codes { | if let codes = codes { | ||||
| for code in codes { | for code in codes { | ||||
| var typeString: String; | |||||
| switch(code.type) { | |||||
| case AVMetadataObject.ObjectType.aztec: | |||||
| typeString = "AZTEC" | |||||
| case AVMetadataObject.ObjectType.code39: | |||||
| typeString = "CODE_39" | |||||
| case AVMetadataObject.ObjectType.code93: | |||||
| typeString = "CODE_93" | |||||
| case AVMetadataObject.ObjectType.code128: | |||||
| typeString = "CODE_128" | |||||
| case AVMetadataObject.ObjectType.dataMatrix: | |||||
| typeString = "DATA_MATRIX" | |||||
| case AVMetadataObject.ObjectType.ean8: | |||||
| typeString = "EAN_8" | |||||
| case AVMetadataObject.ObjectType.ean13: | |||||
| typeString = "EAN_13" | |||||
| case AVMetadataObject.ObjectType.itf14: | |||||
| typeString = "ITF" | |||||
| case AVMetadataObject.ObjectType.pdf417: | |||||
| typeString = "PDF_417" | |||||
| case AVMetadataObject.ObjectType.qr: | |||||
| typeString = "QR_CODE" | |||||
| case AVMetadataObject.ObjectType.upce: | |||||
| typeString = "UPC_E" | |||||
| default: | |||||
| return | |||||
| } | |||||
| // var typeString: String; | |||||
| // switch(code.type) { | |||||
| // case AVMetadataObject.ObjectType.aztec: | |||||
| // typeString = "AZTEC" | |||||
| // case AVMetadataObject.ObjectType.code39: | |||||
| // typeString = "CODE_39" | |||||
| // case AVMetadataObject.ObjectType.code93: | |||||
| // typeString = "CODE_93" | |||||
| // case AVMetadataObject.ObjectType.code128: | |||||
| // typeString = "CODE_128" | |||||
| // case AVMetadataObject.ObjectType.dataMatrix: | |||||
| // typeString = "DATA_MATRIX" | |||||
| // case AVMetadataObject.ObjectType.ean8: | |||||
| // typeString = "EAN_8" | |||||
| // case AVMetadataObject.ObjectType.ean13: | |||||
| // typeString = "EAN_13" | |||||
| // case AVMetadataObject.ObjectType.itf14: | |||||
| // typeString = "ITF" | |||||
| // case AVMetadataObject.ObjectType.pdf417: | |||||
| // typeString = "PDF_417" | |||||
| // case AVMetadataObject.ObjectType.qr: | |||||
| // typeString = "QR_CODE" | |||||
| // case AVMetadataObject.ObjectType.upce: | |||||
| // typeString = "UPC_E" | |||||
| // default: | |||||
| // return | |||||
| // } | |||||
| guard let stringValue = code.stringValue else { continue } | guard let stringValue = code.stringValue else { continue } | ||||
| let typeString = code.type.rawValue | |||||
| let result = ["code": stringValue, "type": typeString] | let result = ["code": stringValue, "type": typeString] | ||||
| if self!.allowedBarcodeTypes.count == 0 || self!.allowedBarcodeTypes.contains(code.type) { | |||||
| if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { | |||||
| self?.channel.invokeMethod("onRecognizeQR", arguments: result) | self?.channel.invokeMethod("onRecognizeQR", arguments: result) | ||||
| } | } | ||||
| @@ -218,11 +218,6 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| } | } | ||||
| return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | ||||
| } | } | ||||
| func showNativeAlertDialog(_ result: @escaping FlutterResult) -> Void { | |||||
| UIAlertView(title: "Scanning Unavailable", message: "This app does not have permission to access the camera", delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Ok").show() | |||||
| return result(true) | |||||
| } | |||||
| func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { | func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { | ||||
| if let sc: MTBBarcodeScanner = scanner { | if let sc: MTBBarcodeScanner = scanner { | ||||
| @@ -251,15 +246,4 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| return result(FlutterError(code: "404", message: nil, details: nil)) | return result(FlutterError(code: "404", message: nil, details: nil)) | ||||
| } | } | ||||
| func setBarcodeFormats(_ arguments: Array<Int>, _ result: @escaping FlutterResult){ | |||||
| do{ | |||||
| allowedBarcodeTypes.removeAll() | |||||
| try arguments.forEach { arg in | |||||
| allowedBarcodeTypes.append(try QRCodeTypes[arg]!) | |||||
| } | |||||
| result(true) | |||||
| }catch{ | |||||
| result(FlutterError(code: "404", message: nil, details: nil)) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -1,5 +1,7 @@ | |||||
| export 'src/qr_code_scanner.dart'; | export 'src/qr_code_scanner.dart'; | ||||
| export 'src/qr_scanner_overlay_shape.dart'; | export 'src/qr_scanner_overlay_shape.dart'; | ||||
| export 'src/types/barcode.dart'; | export 'src/types/barcode.dart'; | ||||
| export 'src/types/barcode_format.dart'; | |||||
| export 'src/types/camera.dart'; | export 'src/types/camera.dart'; | ||||
| export 'src/types/camera_exception.dart'; | |||||
| export 'src/types/features.dart'; | export 'src/types/features.dart'; | ||||
| @@ -24,7 +24,7 @@ class QRView extends StatefulWidget { | |||||
| this.overlayMargin = EdgeInsets.zero, | this.overlayMargin = EdgeInsets.zero, | ||||
| this.cameraFacing = CameraFacing.back, | this.cameraFacing = CameraFacing.back, | ||||
| this.onPermissionSet, | this.onPermissionSet, | ||||
| this.showNativeAlertDialog = false, | |||||
| this.formatsAllowed, | |||||
| }) : assert(key != null), | }) : assert(key != null), | ||||
| assert(onQRViewCreated != null), | assert(onQRViewCreated != null), | ||||
| super(key: key); | super(key: key); | ||||
| @@ -48,8 +48,8 @@ class QRView extends StatefulWidget { | |||||
| /// Calls the provided [onPermissionSet] callback when the permission is set. | /// Calls the provided [onPermissionSet] callback when the permission is set. | ||||
| final PermissionSetCallback onPermissionSet; | final PermissionSetCallback onPermissionSet; | ||||
| /// Gives the possibility to show a dialog. | |||||
| final bool showNativeAlertDialog; | |||||
| /// Use [formatsAllowed] to specify which formats needs to be scanned. | |||||
| final List<BarcodeFormat> formatsAllowed; | |||||
| @override | @override | ||||
| State<StatefulWidget> createState() => _QRViewState(); | State<StatefulWidget> createState() => _QRViewState(); | ||||
| @@ -133,8 +133,8 @@ class _QRViewState extends State<QRView> { | |||||
| // Start scan after creation of the view | // Start scan after creation of the view | ||||
| final controller = QRViewController._(_channel, widget.key, cutOutSize, | final controller = QRViewController._(_channel, widget.key, cutOutSize, | ||||
| widget.onPermissionSet, widget.showNativeAlertDialog) | |||||
| .._startScan(widget.key, cutOutSize); | |||||
| widget.onPermissionSet) | |||||
| .._startScan(widget.key, cutOutSize, widget.formatsAllowed); | |||||
| // Initialize the controller for controlling the QRView | // Initialize the controller for controlling the QRView | ||||
| if (widget.onQRViewCreated != null) { | if (widget.onQRViewCreated != null) { | ||||
| @@ -162,8 +162,7 @@ class QRViewController { | |||||
| MethodChannel channel, | MethodChannel channel, | ||||
| GlobalKey qrKey, | GlobalKey qrKey, | ||||
| double scanArea, | double scanArea, | ||||
| PermissionSetCallback onPermissionSet, | |||||
| bool showNativeAlertDialogOnError, | |||||
| PermissionSetCallback onPermissionSet | |||||
| ) : _channel = channel { | ) : _channel = channel { | ||||
| _channel.setMethodCallHandler((call) async { | _channel.setMethodCallHandler((call) async { | ||||
| switch (call.method) { | switch (call.method) { | ||||
| @@ -190,9 +189,6 @@ class QRViewController { | |||||
| _hasPermissions = true; | _hasPermissions = true; | ||||
| } else { | } else { | ||||
| _hasPermissions = false; | _hasPermissions = false; | ||||
| if (showNativeAlertDialogOnError) { | |||||
| await showNativeAlertDialog(); | |||||
| } | |||||
| } | } | ||||
| if (onPermissionSet != null) { | if (onPermissionSet != null) { | ||||
| onPermissionSet(this, call.arguments as bool); | onPermissionSet(this, call.arguments as bool); | ||||
| @@ -219,12 +215,14 @@ class QRViewController { | |||||
| Future<void> _startScan( | Future<void> _startScan( | ||||
| GlobalKey key, | GlobalKey key, | ||||
| double cutOutSize, | double cutOutSize, | ||||
| ) async { | |||||
| List<BarcodeFormat> barcodeFormats) async { | |||||
| // We need to update the dimension before the scan is started. | // We need to update the dimension before the scan is started. | ||||
| QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); | QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); | ||||
| return _channel.invokeMethod('startScan'); | |||||
| return _channel.invokeMethod('startScan', | |||||
| barcodeFormats?.map((e) => e.asInt())?.toList() ?? []); | |||||
| } | } | ||||
| /// Gets information about which camera is active. | |||||
| Future<CameraFacing> getCameraInfo() async { | Future<CameraFacing> getCameraInfo() async { | ||||
| try { | try { | ||||
| return CameraFacing | return CameraFacing | ||||
| @@ -280,23 +278,7 @@ class QRViewController { | |||||
| } | } | ||||
| } | } | ||||
| Future<void> showNativeAlertDialog() async { | |||||
| try { | |||||
| await _channel.invokeMethod('showNativeAlertDialog'); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| Future<void> setAllowedBarcodeTypes(List<BarcodeFormat> list) async { | |||||
| try { | |||||
| await _channel.invokeMethod('setAllowedBarcodeFormats', | |||||
| list?.map((e) => e.asInt())?.toList() ?? []); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| /// Returns which features are available on device. | |||||
| Future<SystemFeatures> getSystemFeatures() async { | Future<SystemFeatures> getSystemFeatures() async { | ||||
| try { | try { | ||||
| var features = | var features = | ||||