diff --git a/assets/json/junit_report_example.xml b/assets/json/junit_report_example.xml
new file mode 100644
index 0000000..c6415f7
--- /dev/null
+++ b/assets/json/junit_report_example.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ WARNING: Use a program name that matches the source file name
+ Category: COBOL Code Review – Naming Conventions
+ File: /project/PROGRAM.cbl
+ Line: 2
+
+
+
+
\ No newline at end of file
diff --git a/assets/json/test_json_3.json b/assets/json/test_json_3.json
new file mode 100644
index 0000000..3f67c8d
--- /dev/null
+++ b/assets/json/test_json_3.json
@@ -0,0 +1,237 @@
+{
+ "setup": {
+ "name": "Experiment",
+ "description": "It shows what we can do with a card",
+ "personalizationConfig": {
+ "config": {
+ "issuerName": "TANGEM SDK",
+ "acquirerName": "Smart Cash",
+ "series": "BB",
+ "startNumber": 300000000000,
+ "count": 0,
+ "pin": "000000",
+ "pin2": "000",
+ "pin3": "",
+ "hexCrExKey": "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF",
+ "cvc": "000",
+ "pauseBeforePin2": 5000,
+ "smartSecurityDelay": true,
+ "curveID": "Secp256k1",
+ "signingMethods": [
+ "SignHash"
+ ],
+ "maxSignatures": 999999,
+ "isReusable": true,
+ "allowSetPIN1": true,
+ "allowSetPIN2": true,
+ "useActivation": false,
+ "useCvc": false,
+ "useNDEF": true,
+ "useDynamicNDEF": true,
+ "useOneCommandAtTime": false,
+ "useBlock": false,
+ "allowSelectBlockchain": true,
+ "prohibitPurgeWallet": false,
+ "allowUnencrypted": true,
+ "allowFastEncryption": true,
+ "protectIssuerDataAgainstReplay": false,
+ "prohibitDefaultPIN1": false,
+ "disablePrecomputedNDEF": false,
+ "skipSecurityDelayIfValidatedByIssuer": true,
+ "skipCheckPIN2CVCIfValidatedByIssuer": true,
+ "skipSecurityDelayIfValidatedByLinkedTerminal": true,
+ "restrictOverwriteIssuerExtraData": false,
+ "requireTerminalTxSignature": false,
+ "requireTerminalCertSignature": false,
+ "checkPIN3OnCard": false,
+ "createWallet": false,
+ "walletsCount": 1,
+ "cardData": {
+ "batchId": "FFFF",
+ "blockchainName": "ETH",
+ "issuerName": "TANGEM SDK",
+ "manufacturerSignature": null,
+ "manufactureDateTime": "2021-06-22",
+ "productMask": [
+ "Note"
+ ]
+ },
+ "ndefRecords": [
+ {
+ "type": "URI",
+ "value": "https://tangem.com"
+ }
+ ]
+ },
+ "issuer": {
+ "name": "TANGEM SDK",
+ "id": "TANGEM SDK",
+ "dataKeyPair": {
+ "publicKey": "045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26",
+ "privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
+ },
+ "transactionKeyPair": {
+ "publicKey": "0484c5192e9bfa6c528a344f442137a92b89ea835bfef1d04cb4362eb906b508c5889846cfea71ba6dc7b3120c2208df9c46127d3d85cb5cfbd1479e97133a39d8",
+ "privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312"
+ }
+ },
+ "acquirer": {
+ "name": "Smart Cash",
+ "id": "Smart Cash",
+ "keyPair": {
+ "publicKey": "0456ad1a82b22bcb40c38fd08939f87e6b80e40dec5b3bdb351c55fcd709e47f9fb2ed00c2304d3a986f79c5ae0ac3c84e88da46dc8f513b7542c716af8c9a2daf",
+ "privateKey": "21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
+ }
+ },
+ "manufacturer": {
+ "name": "TANGEM",
+ "keyPair": {
+ "publicKey": "04bab86d56298c996f564a84fc88e28aed38184b12f07e519113bef48c76f3df3adc303599b08ac05b55ec3df98d9338573a6242f76f5d28f4f0f364e87e8fca2f",
+ "privateKey": "1b48cfd24bbb5b394771ed81f2bacf57479e4735eb1405083927372d40da9e92"
+ }
+ }
+ },
+ "sdkConfig": {},
+ "minimalFirmware": null,
+ "platform": null,
+ "iterations": 2,
+ "creationDateMs": 1626326543961
+ },
+ "steps": [
+ {
+ "name": "0_scan",
+ "method": "SCAN_TASK",
+ "parameters": {},
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "manufacturerName": "TANGEM",
+ "status": "Empty"
+ },
+ "asserts": [
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.actualResult.status}",
+ "Empty"
+ ]
+ },
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#setup.personalizationConfig.manufacturer.name}",
+ "{#parent.actualResult.manufacturerName}"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ },
+ {
+ "name": "1_createWallet",
+ "method": "CREATE_WALLET_TASK",
+ "parameters": {
+ "config": {
+ "isReusable": true,
+ "prohibitPurgeWallet": null,
+ "curveId": "Secp256k1",
+ "signingMethods": "SignHash"
+ }
+ },
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "status": "Loaded",
+ "walletPublicKey": "049AD86A7F7F7696369A39D53CFA619FD1D917608AEF6CAF9F85BC9E7577D28B1C76EFF56384DDB867EFD1A38DE0479240258649A03CB76DF7EB8F8CAE9A8775C0"
+ },
+ "asserts": [
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.actualResult.status}",
+ "Loaded"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ },
+ {
+ "name": "2_sign",
+ "method": "SIGN_COMMAND",
+ "parameters": {
+ "hashes": [
+ "44617461207573656420666f722068617368696e67"
+ ],
+ "walletPublicKey": "{#1_createWallet.actualResult.walletPublicKey}"
+ },
+ "expectedResult": {
+ "signedHashes": [
+ "2EF25F7E70D4332E4C915A50BA56C3143950DD51130B4723F998AC05A2F5679F03462CCA69E2A3B37372A108CD2BB411FBD00575C9FC30DF4EFB82BB4BF93276"
+ ]
+ },
+ "asserts": [
+ {
+ "type": "IS_NOT_EMPTY",
+ "fields": [
+ "{#parent.actualResult.signatures}"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 2
+ },
+ {
+ "name": "scan_after_sign",
+ "method": "SCAN_TASK",
+ "parameters": {},
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "manufacturerName": "TANGEM",
+ "status": "Empty",
+ "wallets": [
+ {
+ "index": 0,
+ "status": "Empty",
+ "curve": "Secp256k1",
+ "signedHashes": 1
+ }
+ ]
+ },
+ "asserts": [
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.actualResult.wallets.0.signedHashes}",
+ 0
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ },
+ {
+ "name": "4_purgeWallet",
+ "method": "PURGE_WALLET_COMMAND",
+ "parameters": {
+ "walletIndex": 0
+ },
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "status": "Empty"
+ },
+ "asserts": [
+ {
+ "type": "SUCCESS"
+ },
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.expectedResult.status}",
+ "Empty"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/assets/json/test_json_fail_3.json b/assets/json/test_json_fail_3.json
new file mode 100644
index 0000000..acba8a8
--- /dev/null
+++ b/assets/json/test_json_fail_3.json
@@ -0,0 +1,183 @@
+{
+ "setup": {
+ "name": "Experiment with fails",
+ "description": "It shows what we can do with a card",
+ "personalizationConfig": {
+ "config": {
+ "issuerName": "TANGEM SDK",
+ "acquirerName": "Smart Cash",
+ "series": "BB",
+ "startNumber": 300000000000,
+ "count": 0,
+ "pin": "000000",
+ "pin2": "000",
+ "pin3": "",
+ "hexCrExKey": "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF",
+ "cvc": "000",
+ "pauseBeforePin2": 5000,
+ "smartSecurityDelay": true,
+ "curveID": "Secp256k1",
+ "signingMethods": [
+ "SignHash"
+ ],
+ "maxSignatures": 999999,
+ "isReusable": true,
+ "allowSetPIN1": true,
+ "allowSetPIN2": true,
+ "useActivation": false,
+ "useCvc": false,
+ "useNDEF": true,
+ "useDynamicNDEF": true,
+ "useOneCommandAtTime": false,
+ "useBlock": false,
+ "allowSelectBlockchain": true,
+ "prohibitPurgeWallet": false,
+ "allowUnencrypted": true,
+ "allowFastEncryption": true,
+ "protectIssuerDataAgainstReplay": false,
+ "prohibitDefaultPIN1": false,
+ "disablePrecomputedNDEF": false,
+ "skipSecurityDelayIfValidatedByIssuer": true,
+ "skipCheckPIN2CVCIfValidatedByIssuer": true,
+ "skipSecurityDelayIfValidatedByLinkedTerminal": true,
+ "restrictOverwriteIssuerExtraData": false,
+ "requireTerminalTxSignature": false,
+ "requireTerminalCertSignature": false,
+ "checkPIN3OnCard": false,
+ "createWallet": false,
+ "walletsCount": 1,
+ "cardData": {
+ "batchId": "FFFF",
+ "blockchainName": "ETH",
+ "issuerName": "TANGEM SDK",
+ "manufacturerSignature": null,
+ "manufactureDateTime": "2021-06-22",
+ "productMask": [
+ "Note"
+ ]
+ },
+ "ndefRecords": [
+ {
+ "type": "URI",
+ "value": "https://tangem.com"
+ }
+ ]
+ },
+ "issuer": {
+ "name": "TANGEM SDK",
+ "id": "TANGEM SDK",
+ "dataKeyPair": {
+ "publicKey": "045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26",
+ "privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
+ },
+ "transactionKeyPair": {
+ "publicKey": "0484c5192e9bfa6c528a344f442137a92b89ea835bfef1d04cb4362eb906b508c5889846cfea71ba6dc7b3120c2208df9c46127d3d85cb5cfbd1479e97133a39d8",
+ "privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312"
+ }
+ },
+ "acquirer": {
+ "name": "Smart Cash",
+ "id": "Smart Cash",
+ "keyPair": {
+ "publicKey": "0456ad1a82b22bcb40c38fd08939f87e6b80e40dec5b3bdb351c55fcd709e47f9fb2ed00c2304d3a986f79c5ae0ac3c84e88da46dc8f513b7542c716af8c9a2daf",
+ "privateKey": "21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
+ }
+ },
+ "manufacturer": {
+ "name": "TANGEM",
+ "keyPair": {
+ "publicKey": "04bab86d56298c996f564a84fc88e28aed38184b12f07e519113bef48c76f3df3adc303599b08ac05b55ec3df98d9338573a6242f76f5d28f4f0f364e87e8fca2f",
+ "privateKey": "1b48cfd24bbb5b394771ed81f2bacf57479e4735eb1405083927372d40da9e92"
+ }
+ }
+ },
+ "sdkConfig": {},
+ "minimalFirmware": null,
+ "platform": null,
+ "iterations": 1,
+ "creationDateMs": 1626326543961
+ },
+ "steps": [
+ {
+ "name": "0_scan",
+ "method": "SCAN_TASK",
+ "parameters": {},
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "manufacturerName": "TANGEM",
+ "status": "Empty"
+ },
+ "asserts": [
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.actualResult.status}",
+ "Empty"
+ ]
+ },
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#setup.personalizationConfig.manufacturer.name}",
+ "{#parent.actualResult.manufacturerName}"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ },
+ {
+ "name": "1_createWallet",
+ "method": "CREATE_WALLET_TASK",
+ "parameters": {
+ "config": {
+ "isReusable": true,
+ "prohibitPurgeWallet": null,
+ "curveId": "Secp256k1",
+ "signingMethods": "SignHash"
+ }
+ },
+ "expectedResult": {
+ "cardId": "BB03000000000004",
+ "status": "Loaded",
+ "walletPublicKey": "049AD86A7F7F7696369A39D53CFA619FD1D917608AEF6CAF9F85BC9E7577D28B1C76EFF56384DDB867EFD1A38DE0479240258649A03CB76DF7EB8F8CAE9A8775C0"
+ },
+ "asserts": [
+ {
+ "type": "EQUALS",
+ "fields": [
+ "{#parent.actualResult.status}",
+ "Loaded"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 1
+ },
+ {
+ "name": "2_sign",
+ "method": "SIGN_COMMAND",
+ "parameters": {
+ "hashes": [
+ "44617461207573656420666f722068617368696e67"
+ ],
+ "walletPublicKey": "{#1_createWallet.actualResult.walletPublicKey}"
+ },
+ "expectedResult": {
+ "signedHashes": [
+ "2EF25F7E70D4332E4C915A50BA56C3143950DD51130B4723F998AC05A2F5679F03462CCA69E2A3B37372A108CD2BB411FBD00575C9FC30DF4EFB82BB4BF93276"
+ ]
+ },
+ "asserts": [
+ {
+ "type": "IS_NOT_EMPTY",
+ "fields": [
+ "{#parent.actualResult.walletPublicKey}"
+ ]
+ }
+ ],
+ "actionType": "NFC_SESSION_RUNNABLE",
+ "iterations": 2
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ios/.gitignore b/ios/.gitignore
index e96ef60..151026b 100644
--- a/ios/.gitignore
+++ b/ios/.gitignore
@@ -18,6 +18,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
+Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id
deleted file mode 100644
index acbe8b3..0000000
--- a/ios/Flutter/.last_build_id
+++ /dev/null
@@ -1 +0,0 @@
-28141c607f8a167c562e88ee9f9fc15d
\ No newline at end of file
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index be08867..ec97fc6 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit.debug.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index 29cbe91..c4855bf 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit.release.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
index 57274ff..4cc8643 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,94 +1,43 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '13.0'
+# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
-project 'Tangem DevKit', {
+project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
-def parse_KV_file(file, separator='=')
- file_abs_path = File.expand_path(file)
- if !File.exists? file_abs_path
- return [];
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
- generated_key_values = {}
- skip_line_start_symbols = ["#", "/"]
- File.foreach(file_abs_path) do |line|
- next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
- plugin = line.split(pattern=separator)
- if plugin.length == 2
- podname = plugin[0].strip()
- path = plugin[1].strip()
- podpath = File.expand_path("#{path}", file_abs_path)
- generated_key_values[podname] = podpath
- else
- puts "Invalid plugin specification: #{line}"
- end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
end
- generated_key_values
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
-target 'Tangem DevKit' do
- use_frameworks!
- use_modular_headers!
-
- # Flutter Pod
-
- copied_flutter_dir = File.join(__dir__, 'Flutter')
- copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
- copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
- unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
- # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
- # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
- # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
-
- generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
- unless File.exist?(generated_xcode_build_settings_path)
- raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
- end
- generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
- cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
- unless File.exist?(copied_framework_path)
- FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
- end
- unless File.exist?(copied_podspec_path)
- FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
- end
- end
-
- # Keep pod path relative so it can be checked into Podfile.lock.
- pod 'Flutter', :path => 'Flutter'
+flutter_ios_podfile_setup
- # Plugin Pods
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
- # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
- # referring to absolute paths on developers' machines.
- system('rm -rf .symlinks')
- system('mkdir -p .symlinks/plugins')
- plugin_pods = parse_KV_file('../.flutter-plugins')
- plugin_pods.each do |name, path|
- symlink = File.join('.symlinks', 'plugins', name)
- File.symlink(path, symlink)
- pod name, :path => File.join(symlink, 'ios')
- end
+ pod 'TangemSdk', :path => '../../tangem-sdk-ios'
- pod 'TangemSdk'
- #pod 'TangemSdk', :path => '../../card-sdk-swift'
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
-
post_install do |installer|
installer.pods_project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['ENABLE_BITCODE'] = 'NO'
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
- end
+ flutter_additional_ios_build_settings(target)
end
end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 65d466e..0f2122d 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -8,6 +8,7 @@ PODS:
- SDWebImageWebPCoder
- fluttertoast (0.0.2):
- Flutter
+ - Toast
- libwebp (1.2.0):
- libwebp/demux (= 1.2.0)
- libwebp/mux (= 1.2.0)
@@ -20,64 +21,32 @@ PODS:
- Mantle (2.1.6):
- Mantle/extobjc (= 2.1.6)
- Mantle/extobjc (2.1.6)
- - native_device_orientation (0.0.1):
- - Flutter
- path_provider (0.0.1):
- Flutter
- - path_provider_linux (0.0.1):
- - Flutter
- - path_provider_macos (0.0.1):
- - Flutter
- - path_provider_windows (0.0.1):
- - Flutter
- - SDWebImage/Core (5.10.4)
- - SDWebImageWebPCoder (0.8.3):
+ - SDWebImage/Core (5.11.1)
+ - SDWebImageWebPCoder (0.8.4):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.10)
- share (0.0.1):
- Flutter
- shared_preferences (0.0.1):
- Flutter
- - shared_preferences_linux (0.0.1):
- - Flutter
- - shared_preferences_macos (0.0.1):
- - Flutter
- - shared_preferences_web (0.0.1):
- - Flutter
- - shared_preferences_windows (0.0.1):
- - Flutter
- tangem_sdk (0.0.3):
- Flutter
- - TangemSdk (~> 2.4.2)
- - TangemSdk (2.4.2)
- - video_player (0.0.1):
- - Flutter
- - video_player_web (0.0.1):
- - Flutter
- - wakelock (0.0.1):
- - Flutter
+ - TangemSdk (~> 3.0.2)
+ - TangemSdk (3.0.2)
+ - Toast (4.0.0)
DEPENDENCIES:
- camera (from `.symlinks/plugins/camera/ios`)
- Flutter (from `Flutter`)
- flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`)
- - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
- - path_provider_windows (from `.symlinks/plugins/path_provider_windows/ios`)
- share (from `.symlinks/plugins/share/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- - shared_preferences_linux (from `.symlinks/plugins/shared_preferences_linux/ios`)
- - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`)
- - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`)
- - shared_preferences_windows (from `.symlinks/plugins/shared_preferences_windows/ios`)
- tangem_sdk (from `.symlinks/plugins/tangem_sdk/ios`)
- - TangemSdk
- - video_player (from `.symlinks/plugins/video_player/ios`)
- - video_player_web (from `.symlinks/plugins/video_player_web/ios`)
- - wakelock (from `.symlinks/plugins/wakelock/ios`)
+ - TangemSdk (from `../../tangem-sdk-ios`)
SPEC REPOS:
trunk:
@@ -85,7 +54,7 @@ SPEC REPOS:
- Mantle
- SDWebImage
- SDWebImageWebPCoder
- - TangemSdk
+ - Toast
EXTERNAL SOURCES:
camera:
@@ -96,63 +65,33 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_image_compress/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
- native_device_orientation:
- :path: ".symlinks/plugins/native_device_orientation/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
- path_provider_linux:
- :path: ".symlinks/plugins/path_provider_linux/ios"
- path_provider_macos:
- :path: ".symlinks/plugins/path_provider_macos/ios"
- path_provider_windows:
- :path: ".symlinks/plugins/path_provider_windows/ios"
share:
:path: ".symlinks/plugins/share/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
- shared_preferences_linux:
- :path: ".symlinks/plugins/shared_preferences_linux/ios"
- shared_preferences_macos:
- :path: ".symlinks/plugins/shared_preferences_macos/ios"
- shared_preferences_web:
- :path: ".symlinks/plugins/shared_preferences_web/ios"
- shared_preferences_windows:
- :path: ".symlinks/plugins/shared_preferences_windows/ios"
tangem_sdk:
:path: ".symlinks/plugins/tangem_sdk/ios"
- video_player:
- :path: ".symlinks/plugins/video_player/ios"
- video_player_web:
- :path: ".symlinks/plugins/video_player_web/ios"
- wakelock:
- :path: ".symlinks/plugins/wakelock/ios"
+ TangemSdk:
+ :path: "../../tangem-sdk-ios"
SPEC CHECKSUMS:
- camera: a0ca5080336f7af47b88436e5e26da3dee5568f0
- Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
+ camera: 3164201dc344383e62282964016528c4f5a9ad50
+ Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae
- fluttertoast: b644586ef3b16f67fae9a1f8754cef6b2d6b634b
+ fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58
libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
Mantle: 4c0ed6ce47c96eccc4dc3bb071deb3def0e2c3be
- native_device_orientation: e24d00be281de72996640885d80e706142707660
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
- path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4
- path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
- path_provider_windows: a2b81600c677ac1959367280991971cb9a1edb3b
- SDWebImage: c666b97e1fa9c64b4909816a903322018f0a9c84
- SDWebImageWebPCoder: bbf46e29fb8d1980a78ad3d5e9b4123c77f10ebc
+ SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
+ SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
- shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78
- shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087
- shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9
- shared_preferences_windows: 36b76d6f54e76ead957e60b49e2f124b4cd3e6ae
- tangem_sdk: e7063c3a57970265a09d435fc3f533286efddb6d
- TangemSdk: 468a619db3a2105b2f93524893bc7aa5de0647c3
- video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
- video_player_web: da8cadb8274ed4f8dbee8d7171b420dedd437ce7
- wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4
+ tangem_sdk: 37d2c964859b56040342eb01cb3d2c35e4e8f2fb
+ TangemSdk: 26b8d99c941d307ce44c2eb0bb73d5337ced5bed
+ Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
-PODFILE CHECKSUM: c158dd0976c7bc7fa3d9cfd2d0fb213ba376cb3e
+PODFILE CHECKSUM: b3ccbb53d4ae4e225daec72aebe6f42015aaa139
COCOAPODS: 1.10.1
diff --git a/ios/Tangem DevKit.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
similarity index 79%
rename from ios/Tangem DevKit.xcodeproj/project.pbxproj
rename to ios/Runner.xcodeproj/project.pbxproj
index 41d5657..63b255b 100644
--- a/ios/Tangem DevKit.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,18 +3,17 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 5D3F3CFC25CAA8BC00BA3BC8 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D3F3CFB25CAA8BC00BA3BC8 /* App.framework */; };
+ 4E7BA296778EAA717D7B3614 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C61C09B78255B9DC5704B4C5 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- FE8E94330493F527E1283A8C /* Pods_Tangem_DevKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A54215D1256FD8320E9A7CE2 /* Pods_Tangem_DevKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -33,26 +32,22 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
- 211F0E5BAD43410CA55ECEE2 /* 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 = ""; };
+ 274A521632525F3F9DB03A17 /* 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 = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 5D3F3CFB25CAA8BC00BA3BC8 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
- 5DE841A3246D272300AE6931 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
- 66D8E5CB1DF2E9A73602813A /* 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 = ""; };
+ 3FA1D002D52E3F5FDF1BC105 /* 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 = ""; };
+ 5DE3952D268B3373007B0BCE /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
- 97C146EE1CF9000F007C117D /* Tangem DevKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Tangem DevKit.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- A54215D1256FD8320E9A7CE2 /* Pods_Tangem_DevKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tangem_DevKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- BD993B71A6F4C4E0CD17F7BD /* Pods-Tangem DevKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tangem DevKit.debug.xcconfig"; path = "Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit.debug.xcconfig"; sourceTree = ""; };
- C9A49CB04432C2F706E9F7EB /* Pods-Tangem DevKit.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tangem DevKit.profile.xcconfig"; path = "Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit.profile.xcconfig"; sourceTree = ""; };
- CF7C2B205B5D73E75615718A /* Pods-Tangem DevKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tangem DevKit.release.xcconfig"; path = "Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit.release.xcconfig"; sourceTree = ""; };
- DDFD9136A4E1F70D8F2BB736 /* 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 = ""; };
+ C61C09B78255B9DC5704B4C5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DD859713615359523B05CDDE /* 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 = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -60,14 +55,21 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 5D3F3CFC25CAA8BC00BA3BC8 /* App.framework in Frameworks */,
- FE8E94330493F527E1283A8C /* Pods_Tangem_DevKit.framework in Frameworks */,
+ 4E7BA296778EAA717D7B3614 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 81FDCC4795E568773F142C14 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ C61C09B78255B9DC5704B4C5 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -85,15 +87,15 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
- BC08B59DEF1C545A67596105 /* Pods */,
- F75C03F14861BE7370BEB05F /* Frameworks */,
+ F7D6F278003A8834356819F3 /* Pods */,
+ 81FDCC4795E568773F142C14 /* Frameworks */,
);
sourceTree = "";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
- 97C146EE1CF9000F007C117D /* Tangem DevKit.app */,
+ 97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "";
@@ -101,12 +103,11 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
- 5DE841A3246D272300AE6931 /* Runner.entitlements */,
+ 5DE3952D268B3373007B0BCE /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
- 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@@ -115,58 +116,40 @@
path = Runner;
sourceTree = "";
};
- 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ F7D6F278003A8834356819F3 /* Pods */ = {
isa = PBXGroup;
children = (
+ DD859713615359523B05CDDE /* Pods-Runner.debug.xcconfig */,
+ 274A521632525F3F9DB03A17 /* Pods-Runner.release.xcconfig */,
+ 3FA1D002D52E3F5FDF1BC105 /* Pods-Runner.profile.xcconfig */,
);
- name = "Supporting Files";
- sourceTree = "";
- };
- BC08B59DEF1C545A67596105 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 211F0E5BAD43410CA55ECEE2 /* Pods-Runner.debug.xcconfig */,
- DDFD9136A4E1F70D8F2BB736 /* Pods-Runner.release.xcconfig */,
- 66D8E5CB1DF2E9A73602813A /* Pods-Runner.profile.xcconfig */,
- BD993B71A6F4C4E0CD17F7BD /* Pods-Tangem DevKit.debug.xcconfig */,
- CF7C2B205B5D73E75615718A /* Pods-Tangem DevKit.release.xcconfig */,
- C9A49CB04432C2F706E9F7EB /* Pods-Tangem DevKit.profile.xcconfig */,
- );
+ name = Pods;
path = Pods;
sourceTree = "";
};
- F75C03F14861BE7370BEB05F /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 5D3F3CFB25CAA8BC00BA3BC8 /* App.framework */,
- A54215D1256FD8320E9A7CE2 /* Pods_Tangem_DevKit.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
- 97C146ED1CF9000F007C117D /* Tangem DevKit */ = {
+ 97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
- buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Tangem DevKit" */;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- C63BF11100DAB782052ACD7B /* [CP] Check Pods Manifest.lock */,
+ 1C8047A9A95B48009C92A486 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 21A9DB3CE5A2D07C4AA6AE68 /* [CP] Embed Pods Frameworks */,
+ 0301E404DD96C2B851A9E10E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
- name = "Tangem DevKit";
+ name = Runner;
productName = Runner;
- productReference = 97C146EE1CF9000F007C117D /* Tangem DevKit.app */;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -176,17 +159,16 @@
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
- ORGANIZATIONNAME = "The Chromium Authors";
+ ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
- DevelopmentTeam = 4897UJ6D8C;
LastSwiftMigration = 1100;
};
};
};
- buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Tangem DevKit" */;
- compatibilityVersion = "Xcode 3.2";
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -198,7 +180,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
- 97C146ED1CF9000F007C117D /* Tangem DevKit */,
+ 97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
@@ -218,70 +200,72 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 21A9DB3CE5A2D07C4AA6AE68 /* [CP] Embed Pods Frameworks */ = {
+ 0301E404DD96C2B851A9E10E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
- inputPaths = (
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
- outputPaths = (
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tangem DevKit/Pods-Tangem DevKit-frameworks.sh\"\n";
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ 1C8047A9A95B48009C92A486 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Thin Binary";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ 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;
};
- 9740EEB61CF901F6004384FC /* Run Script */ = {
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Run Script";
+ name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- C63BF11100DAB782052ACD7B /* [CP] Check Pods Manifest.lock */ = {
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
- inputFileListPaths = (
- );
inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
);
+ name = "Run Script";
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Tangem DevKit-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- 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;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
@@ -374,21 +358,15 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 8;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 4897UJ6D8C;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tangem.devkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -498,7 +476,8 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -511,21 +490,15 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 8;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 4897UJ6D8C;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tangem.devkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -543,21 +516,15 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 8;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 4897UJ6D8C;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tangem.devkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -570,7 +537,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Tangem DevKit" */ = {
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
@@ -580,7 +547,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Tangem DevKit" */ = {
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
diff --git a/ios/Tangem DevKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
similarity index 71%
rename from ios/Tangem DevKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a1..919434a 100644
--- a/ios/Tangem DevKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/ios/Tangem DevKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
similarity index 100%
rename from ios/Tangem DevKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
diff --git a/ios/Tangem DevKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
similarity index 78%
rename from ios/Tangem DevKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
index 18d9810..f9b0d7c 100644
--- a/ios/Tangem DevKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -2,7 +2,7 @@
- IDEDidComputeMac32BitWarning
-
+ PreviewsEnabled
+
diff --git a/ios/Tangem DevKit.xcodeproj/xcshareddata/xcschemes/Tangem DevKit.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
similarity index 78%
rename from ios/Tangem DevKit.xcodeproj/xcshareddata/xcschemes/Tangem DevKit.xcscheme
rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index ae6a7b7..a28140c 100644
--- a/ios/Tangem DevKit.xcodeproj/xcshareddata/xcschemes/Tangem DevKit.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
@@ -27,17 +27,19 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
-
-
+
+
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+
+
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
index 9bb7689..21a3cc1 100644
--- a/ios/Runner.xcworkspace/contents.xcworkspacedata
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -2,7 +2,7 @@
+ location = "group:Runner.xcodeproj">
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
index 7335fdf..308a2a5 100644
--- a/ios/Runner/Runner-Bridging-Header.h
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -1 +1 @@
-#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
+#import "GeneratedPluginRegistrant.h"
diff --git a/ios/Tangem DevKit.xcworkspace/contents.xcworkspacedata b/ios/Tangem DevKit.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index b83c8b5..0000000
--- a/ios/Tangem DevKit.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
diff --git a/lib/app/domain/actions_bloc/abstracts.dart b/lib/app/domain/actions_bloc/abstracts.dart
index f6b3a05..9041b0e 100644
--- a/lib/app/domain/actions_bloc/abstracts.dart
+++ b/lib/app/domain/actions_bloc/abstracts.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:devkit/app/domain/model/command_data_models.dart';
import 'package:devkit/commons/common_abstracts.dart';
+import 'package:devkit/main.dart';
import 'package:rxdart/rxdart.dart';
import 'package:tangem_sdk/tangem_sdk.dart';
@@ -13,7 +14,7 @@ abstract class ActionBloc extends BaseBloc {
final PublishSubject _commandDataIsReady = PublishSubject();
final PublishSubject _successResponse = PublishSubject();
- final PublishSubject _errorResponse = PublishSubject();
+ final PublishSubject _errorResponse = PublishSubject();
ActionBloc() {
addSubscription(bsCid.stream.listen((event) => _cid = event));
@@ -23,7 +24,7 @@ abstract class ActionBloc extends BaseBloc {
Stream get successResponseStream => _successResponse.stream;
- Stream get errorResponseStream => _errorResponse.stream;
+ Stream get errorResponseStream => _errorResponse.stream;
Callback get callback => Callback((success) => sendSuccess(success), (error) => sendError(error));
@@ -31,7 +32,7 @@ abstract class ActionBloc extends BaseBloc {
_successResponse.add(success);
}
- sendError(TangemSdkBaseError? error) {
+ sendError(TangemSdkPluginError? error) {
_errorResponse.add(error);
}
@@ -47,7 +48,20 @@ abstract class ActionBloc extends BaseBloc {
}
invokeAction() async {
- createCommandData((commandData) => _prepareCommandAndRun(commandData, callback), sendSnackbarMessage);
+ createCommandData((commandData) {
+ if (commandData.type == TangemSdk.cCreateWallet) {
+ _prepareCommandAndRun(
+ commandData,
+ Callback((result) {
+ if (result is CreateWalletResponse) {
+ gWalletPublicKey = result.walletPublicKey;
+ }
+ callback.onSuccess(result);
+ }, callback.onError));
+ } else {
+ _prepareCommandAndRun(commandData, callback);
+ }
+ }, sendSnackbarMessage);
}
_prepareCommandAndRun(CommandDataModel commandData, Callback callback) async {
diff --git a/lib/app/domain/actions_bloc/actions_blocs.dart b/lib/app/domain/actions_bloc/actions_blocs.dart
index 1d1fe97..530ba5a 100644
--- a/lib/app/domain/actions_bloc/actions_blocs.dart
+++ b/lib/app/domain/actions_bloc/actions_blocs.dart
@@ -5,6 +5,7 @@ import 'package:devkit/app/domain/actions_bloc/personalize/personalization_value
import 'package:devkit/app/domain/model/command_data_models.dart';
import 'package:devkit/app/domain/model/personalization/utils.dart';
import 'package:devkit/commons/common_abstracts.dart';
+import 'package:devkit/main.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
@@ -368,8 +369,7 @@ class SignBloc extends ActionBloc {
addSubscription(bsWalletPublicKey.stream.listen((event) => _walletPublicKey = event));
bsDataForHashing.add("Data used for hashing");
//TODO: before trying to sign, must read the card and fetch walletPubKey (v.<4) - by 0 index, (v.>=4) - by using slider
- bsWalletPublicKey.add(
- "04B2A74E1E502A3E5C4B03B53412A5891F270752543D77B5FE685F3125196610E43C880E29ADA29B2D9641FEAB37A699355863F920DE98937B426B1F303A4752C5");
+ bsWalletPublicKey.add(gWalletPublicKey);
}
@override
diff --git a/lib/app/domain/actions_bloc/test_bloc.dart b/lib/app/domain/actions_bloc/test_bloc.dart
index a96dff5..685a36b 100644
--- a/lib/app/domain/actions_bloc/test_bloc.dart
+++ b/lib/app/domain/actions_bloc/test_bloc.dart
@@ -22,7 +22,7 @@ class TestBlock extends ActionBloc {
invokeAction() async {
_clearFields();
if (_inputedCommand.isNullOrEmpty()) {
- sendError(TangemSdkError("Input the command json first"));
+ sendError(PluginFlutterError("Input the command json first"));
return;
}
@@ -31,11 +31,11 @@ class TestBlock extends ActionBloc {
_commandType = _command![TangemSdk.commandType];
if (_commandType == null) {
- sendError(TangemSdkError("Missing the commandType attribute"));
+ sendError(PluginFlutterError("Missing the commandType attribute"));
return;
}
} catch (e) {
- sendError(TangemSdkError("Json conversion error: $e"));
+ sendError(PluginFlutterError("Json conversion error: $e"));
return;
}
@@ -43,10 +43,10 @@ class TestBlock extends ActionBloc {
createCommandData((commandData) {
TangemSdk.runCommand(callback, commandData);
}, (errorMessage) {
- sendError(TangemSdkError("Command data signature not created. Cause: $errorMessage"));
+ sendError(PluginFlutterError("Command data signature not created. Cause: $errorMessage"));
});
} catch (e) {
- sendError(TangemSdkError("Can't create the command data: $e"));
+ sendError(PluginFlutterError("Can't create the command data: $e"));
}
}
@@ -116,8 +116,8 @@ class TestBlock extends ActionBloc {
}
@override
- sendError(TangemSdkBaseError? error) {
- if (error is UserCancelledError) return;
+ sendError(TangemSdkPluginError? error) {
+ if (error?.isUserCancelledError() == true) return;
super.sendError(error);
}
diff --git a/lib/app/domain/model/command_data_models.dart b/lib/app/domain/model/command_data_models.dart
index 29abf42..4323fcb 100644
--- a/lib/app/domain/model/command_data_models.dart
+++ b/lib/app/domain/model/command_data_models.dart
@@ -36,13 +36,22 @@ class SignModel extends CommandDataModel {
class PersonalizationModel extends CommandDataModel {
final PersonalizationConfig config;
final Issuer issuer;
+ final Manufacturer manufacturer;
+ final Acquirer? acquirer;
- PersonalizationModel(this.config, this.issuer) : super(TangemSdk.cPersonalize);
+ PersonalizationModel(
+ this.config,
+ this.issuer,
+ this.manufacturer, [
+ this.acquirer,
+ ]) : super(TangemSdk.cPersonalize);
factory PersonalizationModel.fromJson(Map json) {
final model = PersonalizationModel(
PersonalizationConfig.fromJson(json["config"]),
Issuer.fromJson(json["issuer"]),
+ Manufacturer.fromJson(json["manufacturer"]),
+ Acquirer.fromJson(json["acquirer"]),
);
return CommandDataModel.attachBaseData(model, json);
}
@@ -110,7 +119,7 @@ class WriteIssuerDataModel extends CommandDataModel {
factory WriteIssuerDataModel.fromJson(Map json) {
final model = WriteIssuerDataModel(
- json[TangemSdk.cid],
+ json[TangemSdk.cardId],
json[TangemSdk.issuerData],
json[TangemSdk.privateKey],
json[TangemSdk.issuerDataCounter],
@@ -175,7 +184,7 @@ class WriteIssuerExDataModel extends CommandDataModel {
factory WriteIssuerExDataModel.fromJson(Map json) {
final model = WriteIssuerExDataModel(
- json[TangemSdk.cid],
+ json[TangemSdk.cardId],
json[TangemSdk.issuerData],
json[TangemSdk.privateKey],
json[TangemSdk.issuerDataCounter],
diff --git a/lib/app/resources/keys.dart b/lib/app/resources/keys.dart
index b3f0063..3725278 100644
--- a/lib/app/resources/keys.dart
+++ b/lib/app/resources/keys.dart
@@ -112,6 +112,7 @@ class ItemName {
//hidden
static final navigateToTestScreen = "navigateToTestScreen";
static final navigateToJsonTestAssembler = "navigateToJsonTestAssembler";
+ static final navigateToJsonTestLauncher = "navigateToJsonTestLauncher";
static final commandJson = "commandJson";
static final responseSuccessJson = "responseSuccessJson";
static final responseErrorJson = "responseErrorJson";
diff --git a/lib/app/ui/screen/card_action/test_screen.dart b/lib/app/ui/screen/card_action/test_screen.dart
index 7871171..5deee6a 100644
--- a/lib/app/ui/screen/card_action/test_screen.dart
+++ b/lib/app/ui/screen/card_action/test_screen.dart
@@ -113,7 +113,7 @@ class _TestBodyState extends State {
child: TextWidget.center("Error Response"),
),
HorizontalDelimiter(),
- StreamBuilder(
+ StreamBuilder(
stream: _bloc.errorResponseStream,
initialData: null,
builder: (context, snapshot) {
@@ -121,7 +121,7 @@ class _TestBodyState extends State {
if (snapshot.data == null) return stub;
final data = snapshot.data!;
- if (data is TangemSdkError) {
+ if (data is TangemSdkPluginError) {
Fluttertoast.showToast(msg: data.message, toastLength: Toast.LENGTH_LONG);
return stub;
} else {
diff --git a/lib/app/ui/widgets/menu/menu.dart b/lib/app/ui/widgets/menu/menu.dart
index 9dba103..5542b3e 100644
--- a/lib/app/ui/widgets/menu/menu.dart
+++ b/lib/app/ui/widgets/menu/menu.dart
@@ -15,6 +15,7 @@ enum MenuItem {
personalizationExport,
navigateToTestScreen,
navigateToJsonTestAssembler,
+ navigateToJsonTestLauncher,
}
class Menu {
@@ -31,7 +32,10 @@ class Menu {
Navigator.of(temporaryContext).pushNamed(Routes.TEST);
break;
case MenuItem.navigateToJsonTestAssembler:
- Navigator.of(temporaryContext).pushNamed(Routes.JSON_TEST_LIST);
+ Navigator.of(temporaryContext).pushNamed(Routes.JSON_TEST_ASSEMBLER);
+ break;
+ case MenuItem.navigateToJsonTestLauncher:
+ Navigator.of(temporaryContext).pushNamed(Routes.JSON_TEST_LAUNCHER);
break;
}
},
@@ -52,7 +56,13 @@ class Menu {
value: MenuItem.navigateToJsonTestAssembler,
child: TextWidget("Json tests assembler"),
);
+ final cardTesterScreenItem = PopupMenuItem(
+ key: ItemId.btnFrom(ItemName.navigateToJsonTestLauncher),
+ value: MenuItem.navigateToJsonTestLauncher,
+ child: TextWidget("Json tests launcher"),
+ );
menuItemList.add(jsonTestAssemblerScreenItem);
+ menuItemList.add(cardTesterScreenItem);
return menuItemList;
},
);
diff --git a/lib/app_test_assembler/domain/bloc/json_test_list_bloc.dart b/lib/app_test_assembler/domain/bloc/json_test_assembler_bloc.dart
similarity index 96%
rename from lib/app_test_assembler/domain/bloc/json_test_list_bloc.dart
rename to lib/app_test_assembler/domain/bloc/json_test_assembler_bloc.dart
index 7a839c5..26d9d3e 100644
--- a/lib/app_test_assembler/domain/bloc/json_test_list_bloc.dart
+++ b/lib/app_test_assembler/domain/bloc/json_test_assembler_bloc.dart
@@ -8,14 +8,14 @@ import 'package:devkit/commons/common_abstracts.dart';
import 'package:rxdart/rxdart.dart';
import 'package:share/share.dart';
-class JsonTestListBloc extends BaseBloc {
+class JsonTestAssemblerBloc extends BaseBloc {
final bsRecords = BehaviorSubject>();
final StorageRepository _storageRepo;
late final JsonTestsStorage _jsonTestsStorage;
final _storedJsonTests = [];
- JsonTestListBloc(this._storageRepo) {
+ JsonTestAssemblerBloc(this._storageRepo) {
this._jsonTestsStorage = _storageRepo.testsStorage;
addSubject(bsRecords);
addSubscription(_jsonTestsStorage.isReadyToUseStream.listen(_listenStorageReady));
diff --git a/lib/app_test_assembler/domain/bloc/test_recorder_bloc.dart b/lib/app_test_assembler/domain/bloc/test_recorder_bloc.dart
index b46bbc7..80cd209 100644
--- a/lib/app_test_assembler/domain/bloc/test_recorder_bloc.dart
+++ b/lib/app_test_assembler/domain/bloc/test_recorder_bloc.dart
@@ -3,7 +3,6 @@ import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
import 'package:devkit/app_test_assembler/domain/test_storages.dart';
import 'package:devkit/commons/common_abstracts.dart';
import 'package:tangem_sdk/model/command_data.dart';
-import 'package:tangem_sdk/sdk_plugin.dart';
import 'package:tangem_sdk/tangem_sdk.dart';
class TestRecorderBlock extends BaseBloc {
@@ -65,7 +64,7 @@ class TestRecorderBlock extends BaseBloc {
_currentRecord = null;
}
- handleCommandError(TangemSdkBaseError? error) {
+ handleCommandError(TangemSdkPluginError? error) {
_currentRecord = null;
}
}
@@ -104,7 +103,7 @@ class TestAssembler {
}
}
- TestStep? _createStep(StepRecord record, int index, TestStep stepConfig) {
+ StepModel? _createStep(StepRecord record, int index, StepModel stepConfig) {
final errorMessage = "Can't create a test step for the command: ${record.commandData.type}";
if (!record.commandData.isPrepared()) {
onErrorListener?.call("$errorMessage. Command isn't prepared.");
@@ -125,10 +124,11 @@ class TestAssembler {
final jsonRpc = JSONRPCRequest.fromCommandDataJson(jsonData);
final expectedResult = (record.response as TangemSdkResponse).toJson();
- return TestStep(
- "$index.${stepConfig.name}.${jsonRpc.method}",
+ final modelName = ["$index", jsonRpc.method].join("_");
+ return StepModel(
+ modelName,
jsonRpc.method,
- jsonRpc.parameters,
+ jsonRpc.params,
expectedResult,
stepConfig.asserts,
stepConfig.actionType,
diff --git a/lib/app_test_assembler/domain/model/json_test_model.dart b/lib/app_test_assembler/domain/model/json_test_model.dart
index 83561f2..e5381b2 100644
--- a/lib/app_test_assembler/domain/model/json_test_model.dart
+++ b/lib/app_test_assembler/domain/model/json_test_model.dart
@@ -8,7 +8,7 @@ part 'json_test_model.g.dart';
@JsonSerializable()
class JsonTest {
final TestSetup setup;
- final List steps;
+ final List steps;
JsonTest(this.setup, this.steps);
@@ -16,10 +16,10 @@ class JsonTest {
Map toJson() => _$JsonTestToJson(this);
- JsonTest copyWith({TestSetup? setup, List? steps}) => JsonTest(
- setup ?? this.setup,
- steps ?? this.steps,
- );
+ JsonTest copyWith({TestSetup? setup, List? steps}) => JsonTest(
+ setup ?? this.setup,
+ steps ?? this.steps,
+ );
}
@JsonSerializable()
@@ -27,7 +27,7 @@ class TestSetup {
final String name;
final String description;
final Map personalizationConfig;
- final ConfigSdk? sdkConfig;
+ final Map sdkConfig;
final FirmwareVersion? minimalFirmware;
final String? platform;
final int? iterations;
@@ -36,8 +36,8 @@ class TestSetup {
TestSetup(
this.name,
this.description,
- this.personalizationConfig, [
- this.sdkConfig,
+ this.personalizationConfig,
+ this.sdkConfig, [
this.minimalFirmware,
this.platform,
this.iterations,
@@ -51,7 +51,7 @@ class TestSetup {
"Simple test",
"It shows what we can do with a card",
persCommandConfig,
- null,
+ {},
null,
null,
1,
@@ -66,7 +66,7 @@ class TestSetup {
String? name,
String? description,
Map? personalizationConfig,
- ConfigSdk? sdkConfig,
+ Map? sdkConfig,
FirmwareVersion? minimalFirmware,
String? platform,
int? iterations,
@@ -83,55 +83,70 @@ class TestSetup {
}
@JsonSerializable()
-class ConfigSdk {
- ConfigSdk();
-
- factory ConfigSdk.fromJson(Map json) => ConfigSdk();
-
- Map toJson() => {};
-}
-
-@JsonSerializable()
-class TestStep {
+class StepModel {
final String name;
final String method;
- final Map parameters;
+ final Map params = {};
final Map expectedResult;
- final List asserts;
+ final List asserts;
final String actionType;
final int? iterations;
- TestStep(
+ Map _rawParams = {};
+
+ Map get rawParams => {}..addAll(_rawParams);
+
+ StepModel(
this.name,
this.method,
- this.parameters,
+ Map params,
this.expectedResult,
this.asserts,
this.actionType,
this.iterations,
- );
+ ) : this._rawParams = params;
- factory TestStep.getDefault() {
- return TestStep("stepName", "methodName", {}, {}, [], "NFC_SESSION_RUNNABLE", 1);
+ factory StepModel.getDefault() {
+ return StepModel("stepName", "methodName", {}, {}, [], "NFC_SESSION_RUNNABLE", 1);
}
- factory TestStep.empty(String name, String method) {
- return TestStep(name, method, {}, {}, [], "NFC_SESSION_RUNNABLE", 1);
+ factory StepModel.empty(String name, String method) {
+ return StepModel(name, method, {}, {}, [], "NFC_SESSION_RUNNABLE", 1);
}
- factory TestStep.fromJson(Map json) => _$TestStepFromJson(json);
+ factory StepModel.fromJson(Map json) => _$TestStepFromJson(json);
Map toJson() => _$TestStepToJson(this);
+
+ StepModel copyWith({
+ String? name,
+ String? method,
+ Map? params,
+ Map? expectedResult,
+ List? asserts,
+ String? actionType,
+ int? iterations,
+ }) {
+ return StepModel(
+ name ?? this.name,
+ method ?? this.method,
+ params ?? this._rawParams,
+ expectedResult ?? this.expectedResult,
+ asserts ?? this.asserts,
+ actionType ?? this.actionType,
+ iterations ?? this.iterations,
+ );
+ }
}
@JsonSerializable()
-class TestAssert {
+class AssertModel {
final String type;
- final List fields;
+ final List? fields;
- TestAssert(this.type, this.fields);
+ AssertModel(this.type, this.fields);
- factory TestAssert.fromJson(Map json) => _$TestAssertFromJson(json);
+ factory AssertModel.fromJson(Map json) => _$TestAssertFromJson(json);
Map toJson() => _$TestAssertToJson(this);
}
diff --git a/lib/app_test_assembler/domain/model/json_test_model.g.dart b/lib/app_test_assembler/domain/model/json_test_model.g.dart
index 34ddab9..7d1e08b 100644
--- a/lib/app_test_assembler/domain/model/json_test_model.g.dart
+++ b/lib/app_test_assembler/domain/model/json_test_model.g.dart
@@ -9,7 +9,7 @@ part of 'json_test_model.dart';
JsonTest _$JsonTestFromJson(Map json) {
return JsonTest(
TestSetup.fromJson(json['setup'] as Map),
- (json['steps'] as List).map((e) => TestStep.fromJson(e as Map)).toList(),
+ (json['steps'] as List).map((e) => StepModel.fromJson(e as Map)).toList(),
);
}
@@ -23,11 +23,11 @@ TestSetup _$TestSetupFromJson(Map json) {
json['name'] as String,
json['description'] as String,
json['personalizationConfig'] as Map,
- json['sdkConfig'] == null ? null : ConfigSdk.fromJson(json['sdkConfig'] as Map),
- json['minimalFirmware'] == null ? null : FirmwareVersion.fromJson(json['minimalFirmware']),
+ json['sdkConfig'] as Map,
+ json['minimalFirmware'] == null ? null : FirmwareVersion.fromJson(json['minimalFirmware'] as String),
json['platform'] as String?,
json['iterations'] as int?,
- json['creationDateMs'] as int,
+ json['creationDateMs'] as int?,
);
}
@@ -42,42 +42,36 @@ Map _$TestSetupToJson(TestSetup instance) => {
'creationDateMs': instance.creationDateMs,
};
-ConfigSdk _$ConfigSdkFromJson(Map json) {
- return ConfigSdk();
-}
-
-Map _$ConfigSdkToJson(ConfigSdk instance) => {};
-
-TestStep _$TestStepFromJson(Map json) {
- return TestStep(
+StepModel _$TestStepFromJson(Map json) {
+ return StepModel(
json['name'] as String,
json['method'] as String,
json['parameters'] as Map,
json['expectedResult'] as Map,
- (json['asserts'] as List).map((e) => TestAssert.fromJson(e as Map)).toList(),
+ (json['asserts'] as List).map((e) => AssertModel.fromJson(e as Map)).toList(),
json['actionType'] as String,
json['iterations'] as int?,
);
}
-Map _$TestStepToJson(TestStep instance) => {
+Map _$TestStepToJson(StepModel instance) => {
'name': instance.name,
'method': instance.method,
- 'parameters': instance.parameters,
+ 'parameters': instance.params,
'expectedResult': instance.expectedResult,
'asserts': instance.asserts,
'actionType': instance.actionType,
'iterations': instance.iterations,
};
-TestAssert _$TestAssertFromJson(Map json) {
- return TestAssert(
+AssertModel _$TestAssertFromJson(Map json) {
+ return AssertModel(
json['type'] as String,
- (json['fields'] as List).map((e) => e as String).toList(),
+ (json['fields'] == null ? null : (json['fields'] as List).map((e) => e).toList()),
);
}
-Map _$TestAssertToJson(TestAssert instance) => {
+Map _$TestAssertToJson(AssertModel instance) => {
'type': instance.type,
'fields': instance.fields,
};
diff --git a/lib/app_test_assembler/domain/test_storages.dart b/lib/app_test_assembler/domain/test_storages.dart
index 07be9b5..4deb0b8 100644
--- a/lib/app_test_assembler/domain/test_storages.dart
+++ b/lib/app_test_assembler/domain/test_storages.dart
@@ -16,12 +16,12 @@ class TestSetupConfigStorage extends ConfigSharedPrefsStorage {
TestSetup convertFrom(Map json) => TestSetup.fromJson(json);
}
-class TestStepConfigStorage extends ConfigSharedPrefsStorage {
+class TestStepConfigStorage extends ConfigSharedPrefsStorage {
TestStepConfigStorage() : super("testStepConfigStorage");
@override
- TestStep getDefaultValue() => TestStep.getDefault();
+ StepModel getDefaultValue() => StepModel.getDefault();
@override
- TestStep convertFrom(Map json) => TestStep.fromJson(json);
+ StepModel convertFrom(Map json) => StepModel.fromJson(json);
}
diff --git a/lib/app_test_assembler/ui/screen/json_test_list_screen.dart b/lib/app_test_assembler/ui/screen/json_test_assembler_screen.dart
similarity index 71%
rename from lib/app_test_assembler/ui/screen/json_test_list_screen.dart
rename to lib/app_test_assembler/ui/screen/json_test_assembler_screen.dart
index eed4f33..79bd334 100644
--- a/lib/app_test_assembler/ui/screen/json_test_list_screen.dart
+++ b/lib/app_test_assembler/ui/screen/json_test_assembler_screen.dart
@@ -1,6 +1,6 @@
import 'package:devkit/app/ui/screen/card_action/helpers.dart';
import 'package:devkit/app/ui/widgets/app_widgets.dart';
-import 'package:devkit/app_test_assembler/domain/bloc/json_test_list_bloc.dart';
+import 'package:devkit/app_test_assembler/domain/bloc/json_test_assembler_bloc.dart';
import 'package:devkit/app_test_assembler/domain/bloc/test_recorder_bloc.dart';
import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
import 'package:devkit/app_test_assembler/ui/screen/json_test_detail_screen.dart';
@@ -11,22 +11,24 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tangem_sdk/extensions/exp_extensions.dart';
-class JsonTestListScreen extends StatefulWidget {
- const JsonTestListScreen({Key? key}) : super(key: key);
+class JsonTestAssemblerScreen extends StatefulWidget {
+ const JsonTestAssemblerScreen({Key? key}) : super(key: key);
@override
- _JsonTestListScreenState createState() => _JsonTestListScreenState();
+ _JsonTestAssemblerScreenState createState() => _JsonTestAssemblerScreenState();
}
-class _JsonTestListScreenState extends State {
- late JsonTestListBloc _bloc;
+class _JsonTestAssemblerScreenState extends State {
+ late JsonTestAssemblerBloc _bloc;
@override
Widget build(BuildContext context) {
final storageRepo = context.read().storageRepo;
return MultiRepositoryProvider(
- providers: [RepositoryProvider(create: (context) => JsonTestListBloc(storageRepo).apply((it) => _bloc = it))],
- child: JsonTestListFrame(),
+ providers: [
+ RepositoryProvider(create: (context) => JsonTestAssemblerBloc(storageRepo).apply((it) => _bloc = it))
+ ],
+ child: JsonTestAssemblerFrame(),
);
}
@@ -37,7 +39,7 @@ class _JsonTestListScreenState extends State {
}
}
-class JsonTestListFrame extends StatelessWidget {
+class JsonTestAssemblerFrame extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -45,25 +47,25 @@ class JsonTestListFrame extends StatelessWidget {
title: Text("Test assembler"),
actions: [StartStopRecordWidget()],
),
- body: JsonTestListBody(),
+ body: JsonTestAssemblerBody(),
);
}
}
-class JsonTestListBody extends StatefulWidget {
+class JsonTestAssemblerBody extends StatefulWidget {
@override
- _JsonTestListBodyState createState() => _JsonTestListBodyState();
+ _JsonTestAssemblerBodyState createState() => _JsonTestAssemblerBodyState();
}
-class _JsonTestListBodyState extends State {
- late JsonTestListBloc _jsonTestListBloc;
+class _JsonTestAssemblerBodyState extends State {
+ late JsonTestAssemblerBloc _jsonTestAssemblerBloc;
late TestRecorderBlock _testRecorderBlock;
@override
void initState() {
super.initState();
- _jsonTestListBloc = context.read();
+ _jsonTestAssemblerBloc = context.read();
_testRecorderBlock = context.read().testRecorderBloc;
}
@@ -71,9 +73,9 @@ class _JsonTestListBodyState extends State {
Widget build(BuildContext context) {
return Stack(
children: [
- HiddenSnackBarHandlerWidget([_jsonTestListBloc, _testRecorderBlock]),
+ HiddenSnackBarHandlerWidget([_jsonTestAssemblerBloc, _testRecorderBlock]),
StreamBuilder>(
- stream: _jsonTestListBloc.bsRecords.stream,
+ stream: _jsonTestAssemblerBloc.bsRecords.stream,
builder: (context, snapshot) {
if (snapshot.data == null) return CenterLoadingText();
if (snapshot.data!.isEmpty) return CenterText("No tests have been created yet");
@@ -91,9 +93,10 @@ class _JsonTestListBodyState extends State {
children: [
IconButton(
icon: Icon(Icons.delete_outline),
- onPressed: _testRecorderBlock.recordIsActive() ? null : () => _jsonTestListBloc.delete(index),
+ onPressed:
+ _testRecorderBlock.recordIsActive() ? null : () => _jsonTestAssemblerBloc.delete(index),
),
- IconButton(icon: Icon(Icons.ios_share), onPressed: () => _jsonTestListBloc.share(index)),
+ IconButton(icon: Icon(Icons.ios_share), onPressed: () => _jsonTestAssemblerBloc.share(index)),
],
),
);
diff --git a/lib/app_test_launcher/domain/common/test_result.dart b/lib/app_test_launcher/domain/common/test_result.dart
new file mode 100644
index 0000000..0f39090
--- /dev/null
+++ b/lib/app_test_launcher/domain/common/test_result.dart
@@ -0,0 +1,40 @@
+import 'package:devkit/app_test_launcher/domain/error/error.dart';
+
+class TestResult {}
+
+class Success implements TestResult {
+ final String? name;
+
+ Success([this.name]);
+}
+
+class StepSuccess extends Success {
+ StepSuccess(String name) : super(name);
+}
+
+class AssertSuccess extends Success {
+ AssertSuccess(String name) : super(name);
+}
+
+class Failure implements TestResult {
+ final TestFrameworkError error;
+
+ Failure(this.error);
+}
+
+class StepFailure extends Failure {
+ final String stepName;
+ final String? assertName;
+
+ StepFailure(this.stepName, TestFrameworkError error, [this.assertName]) : super(error);
+
+ factory StepFailure.fromAssert(String stepName, AssertFailure failure) {
+ return StepFailure(stepName, failure.error, failure.assertName);
+ }
+}
+
+class AssertFailure extends Failure {
+ final String assertName;
+
+ AssertFailure(this.assertName, TestFrameworkError error) : super(error);
+}
diff --git a/lib/app_test_launcher/domain/common/typedefs.dart b/lib/app_test_launcher/domain/common/typedefs.dart
new file mode 100644
index 0000000..93d9d2d
--- /dev/null
+++ b/lib/app_test_launcher/domain/common/typedefs.dart
@@ -0,0 +1,7 @@
+import 'package:devkit/app_test_launcher/domain/common/test_result.dart';
+import 'package:devkit/app_test_launcher/domain/error/error.dart';
+
+typedef SourceMap = Map;
+typedef OnComplete = void Function(TestResult result);
+typedef OnTestSequenceComplete = void Function(TestFrameworkError? error);
+typedef OnStepSequenceComplete = void Function(TestResult result);
diff --git a/lib/app_test_launcher/domain/error/error.dart b/lib/app_test_launcher/domain/error/error.dart
new file mode 100644
index 0000000..db67248
--- /dev/null
+++ b/lib/app_test_launcher/domain/error/error.dart
@@ -0,0 +1,31 @@
+import 'package:tangem_sdk/model/json_rpc.dart';
+import 'package:tangem_sdk/plugin_error.dart';
+
+abstract class TestFrameworkError {
+ String get errorMessage;
+}
+
+class TangemSdkPluginWrappedError extends TestFrameworkError {
+ final TangemSdkPluginError error;
+
+ TangemSdkPluginWrappedError(this.error);
+
+ @override
+ String get errorMessage => "TangemSdkPluginError: ${error.toString()}";
+}
+
+extension OnJSONRPCError on JSONRPCError {
+ bool isInterruptTest() {
+ switch (code) {
+ case -32000:
+ return data != null && data!.contains("code: 50002");
+ case 1000:
+ return true;
+ case 50002:
+ return true;
+ case 50003:
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/lib/app_test_launcher/domain/error/test_assert_error.dart b/lib/app_test_launcher/domain/error/test_assert_error.dart
new file mode 100644
index 0000000..5e467db
--- /dev/null
+++ b/lib/app_test_launcher/domain/error/test_assert_error.dart
@@ -0,0 +1,30 @@
+import 'error.dart';
+
+abstract class TestAssertError extends TestFrameworkError {}
+
+class ExpectedAndActualResultError extends TestAssertError {
+ final String? message;
+
+ ExpectedAndActualResultError([this.message]);
+
+ @override
+ String get errorMessage => "Expected and actual results doesn't match. $message";
+}
+
+class EqualsError extends TestAssertError {
+ final dynamic firstValue;
+ final dynamic secondValue;
+
+ EqualsError(this.firstValue, this.secondValue);
+
+ @override
+ String get errorMessage => "Fields doesn't match. f1: $firstValue, f2: $secondValue";
+}
+
+class IsNotEmptyError extends TestAssertError {
+ final String fieldName;
+
+ IsNotEmptyError(this.fieldName);
+ @override
+ String get errorMessage => "Field $fieldName is empty";
+}
diff --git a/lib/app_test_launcher/domain/error/test_step_error.dart b/lib/app_test_launcher/domain/error/test_step_error.dart
new file mode 100644
index 0000000..6b0568f
--- /dev/null
+++ b/lib/app_test_launcher/domain/error/test_step_error.dart
@@ -0,0 +1,12 @@
+import 'error.dart';
+
+abstract class TestStepError extends TestFrameworkError {}
+
+class AssertNotRegisteredError extends TestStepError {
+ final String name;
+
+ AssertNotRegisteredError(this.name);
+
+ @override
+ String get errorMessage => "Executable is not found for the name: $name";
+}
diff --git a/lib/app_test_launcher/domain/executable/assert/assert.dart b/lib/app_test_launcher/domain/executable/assert/assert.dart
new file mode 100644
index 0000000..758522b
--- /dev/null
+++ b/lib/app_test_launcher/domain/executable/assert/assert.dart
@@ -0,0 +1,83 @@
+import 'dart:core';
+
+import 'package:devkit/app_test_launcher/domain/common/test_result.dart';
+import 'package:devkit/app_test_launcher/domain/common/typedefs.dart';
+import 'package:devkit/app_test_launcher/domain/error/test_assert_error.dart';
+import 'package:devkit/app_test_launcher/domain/executable/executable.dart';
+import 'package:devkit/app_test_launcher/domain/variable_service.dart';
+
+class AssertsFactory {
+ Map _assertsBuilders = {};
+
+ registerAssert(String type, TestAssert Function() assertBuilder) {
+ _assertsBuilders[type] = assertBuilder;
+ }
+
+ TestAssert? getAssert(String type) {
+ return _assertsBuilders[type]?.call();
+ }
+}
+
+abstract class TestAssert implements Executable {
+ final String _type;
+
+ late String parentName;
+ late List fields;
+
+ TestAssert(this._type);
+
+ void init(String parentName, List? fields) {
+ this.parentName = parentName;
+ this.fields = fields == null ? [] : fields;
+ }
+
+ @override
+ void run(OnComplete callback);
+
+ dynamic _getFieldValue(dynamic pointer) {
+ return pointer is String ? VariableService.getValue(parentName, pointer) : pointer;
+ }
+
+ static String equals = "EQUALS";
+ static String isNotEmpty = "IS_NOT_EMPTY";
+ static String success = "SUCCESS";
+}
+
+class EqualsAssert extends TestAssert {
+ EqualsAssert() : super(TestAssert.equals);
+
+ @override
+ run(OnComplete callback) {
+ final firstValue = _getFieldValue(fields[0]);
+ final secondValue = _getFieldValue(fields[1]);
+ if (firstValue == secondValue) {
+ callback(AssertSuccess(_type));
+ } else {
+ callback(AssertFailure(_type, EqualsError(firstValue, secondValue)));
+ }
+ }
+}
+
+class IsNotEmptyAssert extends TestAssert {
+ IsNotEmptyAssert() : super(TestAssert.isNotEmpty);
+
+ @override
+ void run(OnComplete callback) {
+ final value = _getFieldValue(fields[0]);
+ if (value != null) {
+ callback(AssertSuccess(_type));
+ } else {
+ _getFieldValue(fields[0]);
+ callback(AssertFailure(_type, IsNotEmptyError("${fields[0]}")));
+ }
+ }
+}
+
+class SuccessAssert extends TestAssert {
+ SuccessAssert() : super(TestAssert.success);
+
+ @override
+ void run(OnComplete callback) {
+ callback(AssertSuccess(_type));
+ }
+}
diff --git a/lib/app_test_launcher/domain/executable/assert/assert_launcher.dart b/lib/app_test_launcher/domain/executable/assert/assert_launcher.dart
new file mode 100644
index 0000000..af077af
--- /dev/null
+++ b/lib/app_test_launcher/domain/executable/assert/assert_launcher.dart
@@ -0,0 +1,33 @@
+import 'dart:collection';
+
+import 'package:devkit/app_test_launcher/domain/common/test_result.dart';
+import 'package:devkit/app_test_launcher/domain/common/typedefs.dart';
+import 'package:devkit/app_test_launcher/domain/executable/executable.dart';
+import 'package:tangem_sdk/extensions/exp_extensions.dart';
+
+import 'assert.dart';
+
+class AssertsLauncher implements Executable {
+ final Queue _assertsQueue;
+
+ AssertsLauncher(this._assertsQueue);
+
+ @override
+ void run(OnComplete callback) {
+ _executeAssert(_assertsQueue.poll(), callback);
+ }
+
+ void _executeAssert(TestAssert? testAssert, OnComplete callback) {
+ if (testAssert == null) {
+ callback(Success());
+ } else {
+ testAssert.run((result) {
+ if (result is AssertSuccess) {
+ _executeAssert(_assertsQueue.poll(), callback);
+ } else {
+ callback(result);
+ }
+ });
+ }
+ }
+}
diff --git a/lib/app_test_launcher/domain/executable/executable.dart b/lib/app_test_launcher/domain/executable/executable.dart
new file mode 100644
index 0000000..1e03f7b
--- /dev/null
+++ b/lib/app_test_launcher/domain/executable/executable.dart
@@ -0,0 +1,5 @@
+import 'package:devkit/app_test_launcher/domain/common/typedefs.dart';
+
+abstract class Executable {
+ void run(OnComplete callback);
+}
diff --git a/lib/app_test_launcher/domain/executable/step/step_launcher.dart b/lib/app_test_launcher/domain/executable/step/step_launcher.dart
new file mode 100644
index 0000000..0b45b30
--- /dev/null
+++ b/lib/app_test_launcher/domain/executable/step/step_launcher.dart
@@ -0,0 +1,109 @@
+import 'dart:collection';
+
+import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
+import 'package:devkit/app_test_launcher/domain/common/test_result.dart';
+import 'package:devkit/app_test_launcher/domain/common/typedefs.dart';
+import 'package:devkit/app_test_launcher/domain/error/error.dart';
+import 'package:devkit/app_test_launcher/domain/error/test_assert_error.dart';
+import 'package:devkit/app_test_launcher/domain/error/test_step_error.dart';
+import 'package:devkit/app_test_launcher/domain/executable/assert/assert.dart';
+import 'package:devkit/app_test_launcher/domain/executable/assert/assert_launcher.dart';
+import 'package:devkit/app_test_launcher/domain/executable/executable.dart';
+import 'package:devkit/app_test_launcher/domain/variable_service.dart';
+import 'package:tangem_sdk/tangem_sdk.dart';
+
+class StepLauncher implements Executable {
+ final StepModel _model;
+ final AssertsFactory _assertsFactory;
+
+ StepLauncher(this._model, this._assertsFactory) {
+ VariableService.registerStep(_model.name, _model.toJson());
+ }
+
+ @override
+ void run(OnComplete callback) {
+ _fetchVariables();
+
+ final jsonRpcRequestCallback = Callback((result) {
+ final jsonRpcResponse = result as JSONRPCResponse;
+ if (jsonRpcResponse.result == null && jsonRpcResponse.error != null) {
+ if (jsonRpcResponse.error?.isInterruptTest() == true) {
+ // TODO: приудмать что отправить
+ // callback(Failure());
+ return;
+ } else {
+ VariableService.registerError(_model.name, jsonRpcResponse.error);
+ }
+ } else {
+ final error = _checkExpectedWithActualResult(_model.expectedResult, jsonRpcResponse);
+ if (error != null) {
+ callback(StepFailure(_model.name, error));
+ return;
+ }
+ VariableService.registerActualResult(_model.name, jsonRpcResponse.result);
+ }
+ _executeAsserts(callback);
+ }, (error) {
+ callback(StepFailure(_model.name, TangemSdkPluginWrappedError(error)));
+ });
+
+ TangemSdk.runJSONRPCRequest(jsonRpcRequestCallback, JSONRPCRequest(_model.method, _model.params));
+ }
+
+ void _fetchVariables() {
+ _model.params.clear();
+ _model.rawParams.forEach((key, value) {
+ //TODO: добавить раскрытие переменных во вложенных структурах
+ final extractedValue = VariableService.getValue(_model.name, value);
+ _model.params[key] = extractedValue;
+ });
+ }
+
+ void _executeAsserts(OnComplete callback) {
+ if (_model.asserts.isEmpty) {
+ callback(StepSuccess(_model.name));
+ return;
+ }
+
+ final assertsQueue = Queue();
+ for (final element in _model.asserts){
+ final testAssert = _assertsFactory.getAssert(element.type);
+ if (testAssert == null) {
+ callback(StepFailure(_model.name, AssertNotRegisteredError(element.type)));
+ return;
+ }
+
+ testAssert.init(_model.name, element.fields);
+ assertsQueue.add(testAssert);
+ }
+
+ AssertsLauncher(assertsQueue).run((result) {
+ if (result is Success) {
+ callback(StepSuccess(_model.name));
+ } else {
+ callback(StepFailure.fromAssert(_model.name, result as AssertFailure));
+ }
+ });
+ }
+
+ TestAssertError? _checkExpectedWithActualResult(Map expResult, JSONRPCResponse jsonRpcResponse) {
+ if (jsonRpcResponse.result is! Map)
+ return ExpectedAndActualResultError("JsonRpc result is not a map");
+
+ return null;
+
+ final actualResult = jsonRpcResponse.result as Map;
+ if (expResult.length != actualResult.length) return ExpectedAndActualResultError("Results length doesn't match");
+
+ final missedKeys = expResult.keys.toList()..removeWhere((element) => actualResult.containsKey(element));
+ final unexpectedKeys = actualResult.keys.toList()..removeWhere((element) => expResult.containsKey(element));
+
+ if (missedKeys.isNotEmpty || unexpectedKeys.isNotEmpty) {
+ final missed = "Missed keys in the actual result: [${missedKeys.join(", ")}]";
+ final unexpected = "Unexpected keys in the actual result: [${unexpectedKeys.join(", ")}]";
+ return ExpectedAndActualResultError("$missed. $unexpected");
+ }
+
+ return null;
+ }
+}
diff --git a/lib/app_test_launcher/domain/json_value_finder.dart b/lib/app_test_launcher/domain/json_value_finder.dart
new file mode 100644
index 0000000..7b61c27
--- /dev/null
+++ b/lib/app_test_launcher/domain/json_value_finder.dart
@@ -0,0 +1,83 @@
+import 'package:tangem_sdk/extensions/exp_extensions.dart';
+
+class JsonValueFinder {
+ final _variablePattern = RegExp("\\{[^\\{\\}]*\\}");
+ final _bracketLeft = "{";
+ final _bracketRight = "}";
+
+ final _variables = >{};
+
+ final String pathDelimiter;
+
+ JsonValueFinder([this.pathDelimiter = "."]);
+
+ void reset() {
+ _variables.clear();
+ }
+
+ void setValue(String name, dynamic value) {
+ _variables[name] = value;
+ }
+
+ dynamic removeValue(String name) {
+ return _variables.remove(name);
+ }
+
+ dynamic getValue(dynamic pointer) {
+ return canBeInterpret(pointer) ? getValueFrom(pointer, _variables) : pointer;
+ }
+
+ dynamic getValueFrom(String pointer, dynamic from) {
+ if (from == null) return null;
+
+ return _getValueByPattern(removeBrackets(pointer).split(pathDelimiter), 0, from);
+ }
+
+ dynamic _getValueByPattern(List? pathList, int position, dynamic result) {
+ if (result == null) return null;
+ if (pathList == null || position >= pathList.length) return result;
+
+ final key = pathList[position];
+ if (result is Map) {
+ return _getValueByPattern(pathList, ++position, result[key]);
+ }
+
+ if (result is List) {
+ if (!key.isNumber()) return null;
+
+ final index = int.parse(key.toString());
+ if (index >= result.length) {
+ return null;
+ } else {
+ return _getValueByPattern(pathList, ++position, result[index]);
+ }
+ } else {
+ return result;
+ }
+ }
+
+ bool canBeInterpret(dynamic pointer) {
+ if (pointer == null) {
+ return false;
+ } else if (pointer is! String) {
+ return false;
+ } else if (!containsVariable(pointer)) {
+ return false;
+ } else
+ return true;
+ }
+
+ bool containsVariable(String? pointer) {
+ if (pointer == null || !pointer.contains(_bracketLeft)) return false;
+
+ return _variablePattern.hasMatch(pointer);
+ }
+
+ String removeBrackets(String text) {
+ if (text.startsWith(_bracketLeft) && text.endsWith(_bracketRight)) {
+ return text.substring(1, text.length - 1);
+ } else {
+ return text;
+ }
+ }
+}
diff --git a/lib/app_test_launcher/domain/repository/repositories.dart b/lib/app_test_launcher/domain/repository/repositories.dart
new file mode 100644
index 0000000..c5397a9
--- /dev/null
+++ b/lib/app_test_launcher/domain/repository/repositories.dart
@@ -0,0 +1,82 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
+import 'package:devkit/app_test_assembler/domain/test_storages.dart';
+import 'package:flutter/services.dart' show rootBundle;
+import 'package:tangem_sdk/extensions/exp_extensions.dart';
+
+abstract class JsonTestsRepository {
+ Future init();
+
+ List getList();
+
+ JsonTest? get(String name);
+}
+
+class JsonTestStorageRepository implements JsonTestsRepository {
+ final JsonTestsStorage storage = JsonTestsStorage();
+
+ @override
+ Future init() {
+ final completer = Completer();
+ storage.isReadyToUseStream.listen((event) {
+ if (event) completer.complete();
+ });
+ return completer.future;
+ }
+
+ @override
+ JsonTest? get(String name) {
+ return storage.get(name);
+ }
+
+ @override
+ List getList() {
+ return storage.names().map((e) => storage.get(e)).toList().toNullSafe();
+ }
+}
+
+class JsonTestAssetsRepository implements JsonTestsRepository {
+ List _jsonTests = [];
+
+ @override
+ Future init() async {
+ final completer = Completer();
+
+ final manifestContent = await rootBundle.loadString('AssetManifest.json');
+
+ final Map manifestMap = json.decode(manifestContent);
+ // >> To get paths you need these 2 lines
+
+ final testPaths = manifestMap.keys
+ .where((String key) => key.contains('json/'))
+ .where((String key) => key.contains('.json'))
+ .toList();
+
+ int inconvertibleCount = 0;
+ testPaths.forEach((element) {
+ rootBundle.loadString(element).then((value) {
+ try {
+ final map = jsonDecode(value);
+ _jsonTests.add(JsonTest.fromJson(map));
+ } catch (ex) {
+ inconvertibleCount++;
+ }
+ if (testPaths.length - inconvertibleCount == _jsonTests.length) completer.complete();
+ });
+ });
+
+ return completer.future;
+ }
+
+ @override
+ JsonTest? get(String name) {
+ return _jsonTests.firstWhereOrNull((e) => e.setup.name == name);
+ }
+
+ @override
+ List getList() {
+ return _jsonTests;
+ }
+}
diff --git a/lib/app_test_launcher/domain/test_launcher.dart b/lib/app_test_launcher/domain/test_launcher.dart
new file mode 100644
index 0000000..026113f
--- /dev/null
+++ b/lib/app_test_launcher/domain/test_launcher.dart
@@ -0,0 +1,202 @@
+import 'dart:collection';
+import 'dart:convert';
+
+import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
+import 'package:devkit/app_test_launcher/domain/error/error.dart';
+import 'package:devkit/app_test_launcher/domain/error/test_step_error.dart';
+import 'package:tangem_sdk/extensions/exp_extensions.dart';
+import 'package:tangem_sdk/tangem_sdk.dart';
+
+import 'common/test_result.dart';
+import 'common/typedefs.dart';
+import 'error/test_assert_error.dart';
+import 'executable/assert/assert.dart';
+import 'executable/step/step_launcher.dart';
+import 'variable_service.dart';
+
+class TestLauncher {
+ static final int defaultIterationsCount = 1;
+
+ final JsonTest _jsonTest;
+ final AssertsFactory _assertsFactory;
+ OnComplete? onTestComplete;
+
+ Queue _stepQueue = Queue();
+
+ late JsonTest _testTemplate;
+ late StepModel _stepTemplate;
+
+ int _testIterationsLeft = defaultIterationsCount;
+ int _stepIterationsLeft = defaultIterationsCount;
+
+ List _successSteps = [];
+ StepFailure? _stepFailure;
+
+ TestLauncher(this._jsonTest, this._assertsFactory);
+
+ void launch() async {
+ if (_jsonTest.steps.isEmpty) {
+ onTestComplete?.call(Success(_jsonTest.setup.name));
+ return;
+ }
+
+ _testTemplate = _jsonTest.copyWith();
+ _testIterationsLeft = _jsonTest.setup.iterations ?? defaultIterationsCount;
+ _startNewTest(_jsonTest);
+ }
+
+ void _startNewTest(JsonTest test) async {
+ await Future.delayed(Duration(milliseconds: 1000));
+ if (_testIterationsLeft == 0) {
+ onTestComplete?.call(Success(_jsonTest.setup.name));
+ return;
+ }
+
+ VariableService.reset();
+ VariableService.registerSetup(test.setup.toJson());
+
+ print("");
+ print("Test: ${test.setup.name}: Start");
+ _prepare(() async {
+ print("Test: Prepare: Complete");
+ await Future.delayed(Duration(milliseconds: 1000));
+ _startSession(() {
+ _runTest(test);
+ });
+ });
+ }
+
+ void _runTest(JsonTest test) {
+ print("Test: ${test.setup.name}: Run: iterations left: $_testIterationsLeft");
+ _testIterationsLeft--;
+ _stepQueue = Queue.from(test.steps);
+ final nextStep = _stepQueue.poll();
+ if (nextStep == null) {
+ _onStepSequenceComplete(Success(test.setup.name));
+ } else {
+ _runStep(nextStep);
+ }
+ }
+
+ void _runStep(StepModel step, [bool isNewIteration = true]) async {
+ await Future.delayed(Duration(milliseconds: 500));
+ if (isNewIteration) {
+ _stepTemplate = step.copyWith();
+ _stepIterationsLeft = step.iterations ?? defaultIterationsCount;
+ }
+ print("Test: ${_testTemplate.setup.name}: Step: ${step.name}: Run: iterations left: $_stepIterationsLeft");
+ _stepIterationsLeft--;
+ StepLauncher(step, _assertsFactory).run(_onStepComplete);
+ }
+
+ void _onStepComplete(TestResult result) {
+ if (result is Success) {
+ final stepName = result.name ?? "Undefined";
+ _successSteps.add(stepName);
+ print("Test: ${_testTemplate.setup.name}: Step: $stepName: Complete");
+ if (_stepIterationsLeft == 0) {
+ final nextStep = _stepQueue.poll();
+ if (nextStep == null) {
+ _onStepSequenceComplete(result);
+ } else {
+ _runStep(nextStep);
+ }
+ } else {
+ _runStep(_stepTemplate, false);
+ }
+ } else {
+ _onStepSequenceComplete(result);
+ }
+ }
+
+ void _onStepSequenceComplete(TestResult result) {
+ print("Test: ${_testTemplate.setup.name}: Complete");
+ if (result is Success) {
+ _stopSession(() {
+ _startNewTest(_testTemplate);
+ });
+ } else {
+ final failure = result as StepFailure;
+ _stopSession(() {
+ _onStepError(failure);
+ }, _extractErrorMessage(failure));
+ }
+ }
+
+ void _prepare(Function onSuccess) {
+ print("Test: Prepare");
+ _stepFailure = null;
+ _rePersonalize(onSuccess);
+ }
+
+ void _rePersonalize(Function onSuccess) {
+ final config = _jsonTest.setup.personalizationConfig;
+ final personalizationRequest = JSONRPCRequest(TangemSdk.getJsonRpcMethod(TangemSdk.cPersonalize)!, config);
+ final onPersonalizeCallback = Callback((result) {
+ print("Test: Prepare: PERSONALIZE complete");
+ _stopSession(onSuccess);
+ }, _onSdkError);
+
+ final depersonalizeRequest = JSONRPCRequest(TangemSdk.getJsonRpcMethod(TangemSdk.cDepersonalize)!, {});
+ final onDepersonalizeCallback = Callback((result) {
+ print("Test: Prepare: DEPERSONALIZE complete");
+ TangemSdk.runJSONRPCRequest(onPersonalizeCallback, personalizationRequest);
+ }, _onSdkError);
+
+ _startSession(() {
+ print("Test: Prepare: Run: DE/PERSONALIZE");
+ TangemSdk.runJSONRPCRequest(onDepersonalizeCallback, depersonalizeRequest);
+ });
+ }
+
+ // все ошибки TangemSdkPluginError должны прерывать выполнение теста, т.к. они формируется
+ // не в результате работы jsonRPC
+
+ _onSdkError(dynamic error) {
+ print("Error type: ${error.runtimeType.toString()}");
+ print(jsonEncode(error));
+ }
+
+ _onStepError(StepFailure failure) {
+ _stepFailure = failure;
+ final message = _extractErrorMessage(failure);
+ print(message);
+ print(jsonEncode(failure.error));
+ }
+
+ String _extractErrorMessage(StepFailure failure) {
+ if (failure.error is TangemSdkPluginWrappedError) {
+ return "TangemSdkPluginWrappedError: ${failure.error.errorMessage}";
+ } else if (failure.error is TestAssertError) {
+ return "${failure.error.runtimeType.toString()}. ${failure.error.errorMessage}";
+ } else if (failure.error is TestStepError) {
+ return "Step failure: ${failure.error.runtimeType.toString()}";
+ } else {
+ return "Some undefined failure: ${failure.error.runtimeType.toString()}";
+ }
+ }
+
+ void _startSession(Function onSuccess) {
+ // print("Starting session...");
+ TangemSdk.startSession(
+ Callback((success) {
+ print("Session started +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ onSuccess();
+ }, _onSdkError),
+ {
+ TangemSdk.initialMessage: _jsonTest.setup.sdkConfig[TangemSdk.initialMessage],
+ TangemSdk.cardId: _jsonTest.setup.sdkConfig[TangemSdk.cardId],
+ });
+ }
+
+ //TODO: останавливать сессию только при ошибке ассертов
+ void _stopSession([Function? onSuccess, String? error]) {
+ // print("Stopping session...");
+ TangemSdk.stopSession(
+ Callback((success) {
+ print("Session stopped ---------------------------------------------------------------------");
+ onSuccess?.call();
+ }, _onSdkError),
+ error != null ? {"error": error} : {});
+ }
+}
diff --git a/lib/app_test_launcher/domain/variable_service.dart b/lib/app_test_launcher/domain/variable_service.dart
new file mode 100644
index 0000000..9bda294
--- /dev/null
+++ b/lib/app_test_launcher/domain/variable_service.dart
@@ -0,0 +1,74 @@
+import 'package:devkit/app_test_launcher/domain/common/typedefs.dart';
+
+import 'json_value_finder.dart';
+
+class VariableService {
+ static final _valueFinder = JsonValueFinder();
+
+ static final _targetKey = "#";
+ static final _parentTargetKey = "#parent";
+
+ static final _actualResult = "actualResult";
+ static final _error = "error";
+
+ static void registerSetup(SourceMap source) {
+ _valueFinder.setValue("setup", source);
+ }
+
+ static void registerStep(String name, SourceMap source) {
+ _valueFinder.setValue(name, source);
+ }
+
+ static void registerActualResult(String name, dynamic result) {
+ final stepMap = _valueFinder.getValue("{$name}");
+ if (stepMap == null) {
+ // Step is not registered
+ return;
+ }
+
+ stepMap[_actualResult] = result;
+ }
+
+ static void registerError(String name, dynamic result) {
+ final stepMap = _valueFinder.getValue("{$name}");
+ if (stepMap == null) {
+ // Step is not registered
+ return;
+ }
+
+ stepMap[_error] = result;
+ }
+
+ static dynamic getValue(String name, dynamic pointer) {
+ if (!_valueFinder.canBeInterpret(pointer)) return pointer;
+
+ if (_containsTarget(pointer)) {
+ final targetPointer = _extractPointer(pointer);
+ if (targetPointer == null) return null;
+
+ final targetName = targetPointer == _parentTargetKey ? name : _extractTargetName(targetPointer);
+ final valuePointer = pointer.replaceAll("$targetPointer.", "");
+ final target = _valueFinder.getValue("{$targetName}");
+ return target == null ? null : _valueFinder.getValueFrom(valuePointer, target);
+ } else {
+ return _valueFinder.getValueFrom(pointer, _valueFinder.getValue("{$name}"));
+ }
+ }
+
+ static String _extractTargetName(String stepPointer) => stepPointer.replaceAll(_targetKey, "");
+
+ static String? _extractPointer(String pointer) => _getPrefix(_valueFinder.removeBrackets(pointer));
+
+ static String? _getPrefix(String value) {
+ final suffixIdx = value.indexOf(".");
+ return suffixIdx < 0 ? null : value.substring(0, suffixIdx);
+ }
+
+ static bool _containsTarget(String? pointer) {
+ return pointer == null ? false : pointer.indexOf(_targetKey) == 1;
+ }
+
+ static void reset() {
+ _valueFinder.reset();
+ }
+}
diff --git a/lib/app_test_launcher/ui/screen/json_test_launcher_screen.dart b/lib/app_test_launcher/ui/screen/json_test_launcher_screen.dart
new file mode 100644
index 0000000..8ea34a1
--- /dev/null
+++ b/lib/app_test_launcher/ui/screen/json_test_launcher_screen.dart
@@ -0,0 +1,137 @@
+import 'package:devkit/app/ui/screen/card_action/helpers.dart';
+import 'package:devkit/app/ui/widgets/app_widgets.dart';
+import 'package:devkit/app_test_assembler/domain/model/json_test_model.dart';
+import 'package:devkit/app_test_launcher/domain/common/test_result.dart';
+import 'package:devkit/app_test_launcher/domain/executable/assert/assert.dart';
+import 'package:devkit/app_test_launcher/domain/repository/repositories.dart';
+import 'package:devkit/app_test_launcher/domain/test_launcher.dart';
+import 'package:devkit/commons/common_abstracts.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:rxdart/rxdart.dart';
+import 'package:tangem_sdk/extensions/exp_extensions.dart';
+
+class JsonTestLauncherScreen extends StatefulWidget {
+ const JsonTestLauncherScreen({Key? key}) : super(key: key);
+
+ @override
+ _JsonTestLauncherScreenState createState() => _JsonTestLauncherScreenState();
+}
+
+class _JsonTestLauncherScreenState extends State {
+ late JsonTestLauncherBloc _bloc;
+
+ @override
+ Widget build(BuildContext context) {
+ return MultiRepositoryProvider(
+ providers: [
+ RepositoryProvider(
+ create: (context) => JsonTestLauncherBloc(JsonTestAssetsRepository()).apply((it) => _bloc = it)),
+ ],
+ child: JsonTestLauncherFrame(),
+ );
+ }
+
+ @override
+ void dispose() {
+ _bloc.dispose();
+ super.dispose();
+ }
+}
+
+class JsonTestLauncherFrame extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text("Tests")),
+ body: JsonTestLauncherBody(),
+ );
+ }
+}
+
+class JsonTestLauncherBody extends StatefulWidget {
+ final bool attachSnackBarHandler;
+
+ const JsonTestLauncherBody({Key? key, this.attachSnackBarHandler = true}) : super(key: key);
+
+ @override
+ _JsonTestLauncherBodyState createState() => _JsonTestLauncherBodyState();
+}
+
+class _JsonTestLauncherBodyState extends State {
+ late JsonTestLauncherBloc _bloc;
+
+ @override
+ void initState() {
+ super.initState();
+ _bloc = context.read();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ children: [
+ HiddenSnackBarHandlerWidget([_bloc]),
+ StubStreamBuilder>(
+ _bloc.bsJsonTests.stream,
+ (context, data) {
+ return ListView.separated(
+ itemCount: _bloc.repo.getList().length,
+ separatorBuilder: (context, index) => HorizontalDelimiter(),
+ itemBuilder: (context, index) {
+ final jsonTest = data[index];
+ return ListTile(
+ title: Text(jsonTest.setup.name),
+ subtitle: Text(jsonTest.setup.description),
+ trailing: IconButton(
+ icon: Icon(Icons.play_arrow),
+ onPressed: () {
+ _bloc.launch(jsonTest);
+ },
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ],
+ );
+ }
+}
+
+class JsonTestLauncherBloc extends BaseBloc {
+ final JsonTestsRepository repo;
+ final bsJsonTests = BehaviorSubject>();
+
+ JsonTestLauncherBloc(this.repo) {
+ addSubject(bsJsonTests);
+ initRepository();
+ }
+
+ void initRepository() async {
+ await repo.init();
+ bsJsonTests.add(repo.getList());
+ }
+
+ void launch(JsonTest jsonTest) {
+ final assertsFactory = AssertsFactory().apply((it) {
+ it.registerAssert(TestAssert.equals, () => EqualsAssert());
+ it.registerAssert(TestAssert.isNotEmpty, () => IsNotEmptyAssert());
+ it.registerAssert(TestAssert.success, () => SuccessAssert());
+ });
+ final launcher = TestLauncher(jsonTest, assertsFactory);
+ launcher.onTestComplete = (result) {
+ if (result is Success) {
+ final message = result.name ?? "Unknown test";
+ sendSnackbarMessage("$message is completed");
+ } else {
+ sendSnackbarMessage(result);
+ }
+ };
+ launcher.launch();
+ }
+
+ void _handleError(dynamic error) {
+ sendSnackbarMessage(error);
+ }
+}
diff --git a/lib/commons/common_abstracts.dart b/lib/commons/common_abstracts.dart
index c1e733d..85424ef 100644
--- a/lib/commons/common_abstracts.dart
+++ b/lib/commons/common_abstracts.dart
@@ -59,9 +59,9 @@ abstract class Disposable {
dispose();
}
-abstract class DisposableBloc extends Disposable {}
+abstract class DisposableBloc implements Disposable {}
-abstract class BaseBloc extends DisposableBloc with SnackBarStreamHolder {
+abstract class AsyncBloc implements DisposableBloc {
final _subscriptions = [];
final _subjects = [];
@@ -78,8 +78,8 @@ abstract class BaseBloc extends DisposableBloc with SnackBarStreamHolder {
_subscriptions.forEach((element) => element.cancel());
_subjects.forEach((element) => element.close());
}
-
}
+abstract class BaseBloc with AsyncBloc, SnackBarStreamHolder {}
abstract class SnackBarStreamHolder {
final PublishSubject _snackbarMessage = PublishSubject();
diff --git a/lib/main.dart b/lib/main.dart
index e71ad2f..b5564d8 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -8,3 +8,5 @@ void main() async {
final launcher = App.isDebug ? DebugLauncher() : ReleaseLauncher();
await launcher.launch();
}
+
+String gWalletPublicKey = "";
\ No newline at end of file
diff --git a/lib/navigation/routes.dart b/lib/navigation/routes.dart
index 86d747e..88e5988 100644
--- a/lib/navigation/routes.dart
+++ b/lib/navigation/routes.dart
@@ -19,10 +19,11 @@ import 'package:devkit/app/ui/screen/card_action/wallet_create_screen.dart';
import 'package:devkit/app/ui/screen/card_action/wallet_purge_screen.dart';
import 'package:devkit/app/ui/screen/main_screen.dart';
import 'package:devkit/app/ui/screen/response/response_screen.dart';
+import 'package:devkit/app_test_assembler/ui/screen/json_test_assembler_screen.dart';
import 'package:devkit/app_test_assembler/ui/screen/json_test_detail_screen.dart';
-import 'package:devkit/app_test_assembler/ui/screen/json_test_list_screen.dart';
import 'package:devkit/app_test_assembler/ui/screen/test_setup_detail_screen.dart';
import 'package:devkit/app_test_assembler/ui/screen/test_step_detail_screen.dart';
+import 'package:devkit/app_test_launcher/ui/screen/json_test_launcher_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tangem_sdk/model/sdk.dart';
@@ -30,10 +31,11 @@ import 'package:tangem_sdk/model/sdk.dart';
class Routes {
static const MAIN = "/";
static const TEST = "/test";
- static const JSON_TEST_LIST = "/json_test_list";
+ static const JSON_TEST_ASSEMBLER = "/json_test_assembler";
static const JSON_TEST_DETAIL = "/json_test_detail";
static const TEST_SETUP_DETAIL = "/test_setup_detail";
static const TEST_STEP_DETAIL = "/test_step_detail";
+ static const JSON_TEST_LAUNCHER = "/json_test_launcher";
static const SCAN = "/scan";
static const SIGN = "/sign";
static const PERSONALIZE = "/personalize";
@@ -101,8 +103,8 @@ class Routes {
return PageRouteBuilder(pageBuilder: (_, __, ___) => ResponseScreen(arguments: settings.arguments));
case TEST:
return MaterialPageRoute(builder: (_) => TestScreen());
- case JSON_TEST_LIST:
- return MaterialPageRoute(builder: (_) => JsonTestListScreen());
+ case JSON_TEST_ASSEMBLER:
+ return MaterialPageRoute(builder: (_) => JsonTestAssemblerScreen());
case JSON_TEST_DETAIL:
return CupertinoPageRoute(
builder: (_) => JsonTestDetailScreen(),
@@ -118,6 +120,8 @@ class Routes {
builder: (_) => TestStepDetailScreen(),
settings: settings,
);
+ case JSON_TEST_LAUNCHER:
+ return MaterialPageRoute(builder: (_) => JsonTestLauncherScreen());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
diff --git a/plugin/tangem-sdk-flutter/android/build.gradle b/plugin/tangem-sdk-flutter/android/build.gradle
index c93048f..2c003c7 100644
--- a/plugin/tangem-sdk-flutter/android/build.gradle
+++ b/plugin/tangem-sdk-flutter/android/build.gradle
@@ -18,6 +18,7 @@ rootProject.allprojects {
repositories {
google()
jcenter()
+ mavenLocal()
maven {url "https://nexus.tangem-tech.com/repository/maven-releases/"}
maven {url 'https://jitpack.io'}
}
@@ -52,8 +53,12 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'com.tangem:core:develop-37'
- implementation 'com.tangem:sdk:develop-37'
+ implementation 'com.tangem.tangem-sdk-kotlin:core:develop-46'
+ implementation 'com.tangem.tangem-sdk-kotlin:android:develop-46'
+
+ // local dependencies
+// implementation 'com.tangem.tangem-sdk-kotlin:core:0.0.1'
+// implementation 'com.tangem.tangem-sdk-kotlin:android:0.0.1'
implementation "com.squareup.sqldelight:android-driver:1.4.0"
diff --git a/plugin/tangem-sdk-flutter/android/src/main/kotlin/com/tangem/flutter/plugin/tangem_sdk/TangemSdkPlugin.kt b/plugin/tangem-sdk-flutter/android/src/main/kotlin/com/tangem/flutter/plugin/tangem_sdk/TangemSdkPlugin.kt
index 1db19e2..fef8d5f 100644
--- a/plugin/tangem-sdk-flutter/android/src/main/kotlin/com/tangem/flutter/plugin/tangem_sdk/TangemSdkPlugin.kt
+++ b/plugin/tangem-sdk-flutter/android/src/main/kotlin/com/tangem/flutter/plugin/tangem_sdk/TangemSdkPlugin.kt
@@ -10,7 +10,7 @@ import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.tangem.*
import com.tangem.commands.common.card.FirmwareType
import com.tangem.commands.common.jsonConverter.MoshiJsonConverter
-import com.tangem.commands.file.FileData
+import com.tangem.commands.file.DataToWrite
import com.tangem.commands.file.FileSettingsChange
import com.tangem.common.CardValuesDbStorage
import com.tangem.common.CardValuesStorage
@@ -39,6 +39,8 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var wActivity: WeakReference
private lateinit var sdk: TangemSdk
+ private var cardSession: CardSession? = null
+
private var replyAlreadySubmit = false
override fun onAttachedToActivity(pluginBinding: ActivityPluginBinding) {
@@ -82,8 +84,9 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
- val channel = MethodChannel(flutterPluginBinding.flutterEngine.dartExecutor, "tangemSdk")
- channel.setMethodCallHandler(this)
+ val messenger = flutterPluginBinding.binaryMessenger
+ MethodChannel(messenger, "tangemSdk").setMethodCallHandler(this)
+ MethodChannel(messenger, "tangemSdk_JSONRPC").setMethodCallHandler(this)
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
@@ -92,6 +95,9 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
replyAlreadySubmit = false
when (call.method) {
+ "startSession" -> startSession(call, result)
+ "stopSession" -> stopSession(call, result)
+ "runJSONRPCRequest" -> runJSONRPCRequest(call, result)
"allowsOnlyDebugCards" -> allowsOnlyDebugCards(call, result)
"scanCard" -> scanCard(call, result)
"sign" -> sign(call, result)
@@ -118,6 +124,103 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
}
}
+ private fun startSession(call: MethodCall, result: Result) {
+ try {
+ if (cardSession != null && cardSession !!.state == CardSessionState.Active)
+ throw PluginException("The CardSession has already started")
+
+ sdk.startSession(
+ call.extractOptional("cardId"),
+ call.extractOptional("initialMessage"),
+ ) { session, error ->
+ if (error == null) {
+ cardSession = session
+ handleResult(result, CompletionResult.Success(true))
+ } else {
+ cardSession = null
+ handleResult(result, CompletionResult.Failure(error))
+ }
+ }
+ } catch (ex: Exception) {
+ handleException(result, ex)
+ }
+ }
+
+ private fun stopSession(call: MethodCall, result: Result) {
+ try {
+ val session = cardSession ?: throw PluginException("Session not started")
+ session.stop()
+ cardSession = null
+ handleResult(result, CompletionResult.Success(true))
+ } catch (ex: Exception) {
+ handleException(result, ex)
+ }
+ }
+
+ private fun runJSONRPCRequest(call: MethodCall, result: Result) {
+ try {
+ replyAlreadySubmit = false
+ val stringOfJSONRPCRequest = call.extract("JSONRPCRequest")
+
+ val callback = callbackWithResult@{ response: String ->
+ if (! replyAlreadySubmit) {
+ replyAlreadySubmit = true
+ handler.post { result.success(response) }
+ }
+ }
+
+ if (cardSession == null) {
+ sdk.startSessionWithJsonRequest(
+ stringOfJSONRPCRequest,
+ call.extractOptional("cardId"),
+ call.extractOptional("initialMessage"),
+ callback
+ )
+ } else {
+ cardSession !!.run(stringOfJSONRPCRequest, callback)
+ }
+ } catch (ex: Exception) {
+ handleException(result, ex)
+ }
+ }
+
+ private fun handleResult(result: Result, completionResult: CompletionResult<*>) {
+ if (replyAlreadySubmit) return
+ replyAlreadySubmit = true
+
+ when (completionResult) {
+ is CompletionResult.Success -> {
+ handler.post { result.success(converter.toJson(completionResult.data)) }
+ }
+ is CompletionResult.Failure -> {
+ val error = completionResult.error
+ val errorMessage = if (error is TangemSdkError) {
+ val activity = wActivity.get()
+ if (activity == null) error.customMessage else error.localizedDescription(activity)
+ } else {
+ error.customMessage
+ }
+ val pluginError = PluginError(error.code, errorMessage)
+ handler.post {
+ result.error("${error.code}", errorMessage, converter.toJson(pluginError))
+ }
+ }
+ }
+ }
+
+ private fun handleException(result: Result, ex: Exception) {
+ if (replyAlreadySubmit) return
+ replyAlreadySubmit = true
+
+ val exception = ex as? PluginException ?: TangemSdkException(ex)
+ handler.post {
+ val code = 1000
+ val localizedDescription: String = exception.toString()
+ result.error("$code", localizedDescription,
+ converter.toJson(PluginError(code, localizedDescription)))
+ }
+ }
+
private fun scanCard(call: MethodCall, result: Result) {
try {
sdk.scanCard(
@@ -144,10 +247,10 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private fun personalize(call: MethodCall, result: Result) {
try {
sdk.personalize(
- call.extract("cardConfig"),
+ call.extract("config"),
call.extract("issuer"),
call.extract("manufacturer"),
- call.extract("acquirer"),
+ call.extractOptional("acquirer"),
call.extractOptional("initialMessage")
) { handleResult(result, it) }
} catch (ex: Exception) {
@@ -384,45 +487,13 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
}
}
- private fun handleResult(result: Result, completionResult: CompletionResult<*>) {
- if (replyAlreadySubmit) return
- replyAlreadySubmit = true
-
- when (completionResult) {
- is CompletionResult.Success -> {
- handler.post { result.success(converter.toJson(completionResult.data)) }
- }
- is CompletionResult.Failure -> {
- val error = completionResult.error
- val errorMessage = if (error is TangemSdkError) {
- val activity = wActivity.get()
- if (activity == null) error.customMessage else error.localizedDescription(activity)
- } else {
- error.customMessage
- }
- val pluginError = PluginError(error.code, errorMessage)
- handler.post {
- result.error("${error.code}", errorMessage, converter.toJson(pluginError))
- }
- }
- }
- }
-
- private fun handleException(result: Result, ex: Exception) {
- if (replyAlreadySubmit) return
- replyAlreadySubmit = true
-
- handler.post {
- val code = 9999
- val localizedDescription: String = ex.toString()
- result.error("$code".capitalize(), localizedDescription,
- converter.toJson(PluginError(code, localizedDescription)))
- }
- }
-
- @Throws(Exception::class)
+ @Throws(PluginException::class)
inline fun MethodCall.extract(name: String): T {
- return this.extractOptional(name) ?: throw NoSuchFieldException(name)
+ return try {
+ this.extractOptional(name) ?: throw PluginException("MethodCall.extract: no such field: $name, or field is NULL")
+ } catch (ex: Exception) {
+ throw ex as? PluginException ?: PluginException("MethodCall.extractOptional", ex)
+ }
}
inline fun MethodCall.extractOptional(name: String): T? {
@@ -457,7 +528,7 @@ class TangemSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
sealed class FileCommand {
data class Read(val readPrivateFiles: Boolean = false, val indices: List? = null)
- data class Write(val files: List)
+ data class Write(val files: List)
data class Delete(val indices: List?)
data class ChangeSettings(val changes: List)
}
@@ -467,15 +538,15 @@ class MoshiAdapters {
class DataToWriteAdapter {
@ToJson
- fun toJson(src: FileData): String {
+ fun toJson(src: DataToWrite): String {
return when (src) {
- is FileData.DataProtectedBySignature -> DataProtectedBySignatureAdapter().toJson(src)
- is FileData.DataProtectedByPasscode -> DataProtectedByPasscodeAdapter().toJson(src)
+ is DataToWrite.DataProtectedBySignature -> DataProtectedBySignatureAdapter().toJson(src)
+ is DataToWrite.DataProtectedByPasscode -> DataProtectedByPasscodeAdapter().toJson(src)
}
}
@FromJson
- fun fromJson(map: MutableMap): FileData {
+ fun fromJson(map: MutableMap): DataToWrite {
return if (map.containsKey("signature")) {
DataProtectedBySignatureAdapter().fromJson(map)
} else {
@@ -486,10 +557,10 @@ class MoshiAdapters {
class DataProtectedBySignatureAdapter {
@ToJson
- fun toJson(src: FileData.DataProtectedBySignature): String = MoshiJsonConverter.default().toJson(src)
+ fun toJson(src: DataToWrite.DataProtectedBySignature): String = MoshiJsonConverter.default().toJson(src)
@FromJson
- fun fromJson(map: MutableMap): FileData.DataProtectedBySignature {
+ fun fromJson(map: MutableMap): DataToWrite.DataProtectedBySignature {
val converter = MoshiJsonConverter.default()
return converter.fromJson(converter.toJson(map)) !!
}
@@ -497,14 +568,25 @@ class MoshiAdapters {
class DataProtectedByPasscodeAdapter {
@ToJson
- fun toJson(src: FileData.DataProtectedByPasscode): String = MoshiJsonConverter.default().toJson(src)
+ fun toJson(src: DataToWrite.DataProtectedByPasscode): String = MoshiJsonConverter.default().toJson(src)
@FromJson
- fun fromJson(map: MutableMap): FileData.DataProtectedByPasscode {
+ fun fromJson(map: MutableMap): DataToWrite.DataProtectedByPasscode {
val converter = MoshiJsonConverter.default()
return converter.fromJson(converter.toJson(map)) !!
}
}
}
-data class PluginError(val code: Int, val localizedDescription: String)
\ No newline at end of file
+data class PluginError(
+ // code = 1000 - it's the plugin or it's the tangemSdk internal exception
+ // any other value in code greater than 10000 - it's the tangemSdk internal error
+ val code: Int,
+ val localizedDescription: String
+)
+
+class PluginException(
+ message: String, cause: Throwable? = null
+): Exception("TangemSdkPlugin exception. Message: $message", cause)
+
+class TangemSdkException(cause: Throwable): Exception("TangemSdk internal exception", cause)
diff --git a/plugin/tangem-sdk-flutter/example/lib/main.dart b/plugin/tangem-sdk-flutter/example/lib/main.dart
index f35faae..3081c36 100644
--- a/plugin/tangem-sdk-flutter/example/lib/main.dart
+++ b/plugin/tangem-sdk-flutter/example/lib/main.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
+import 'package:tangem_sdk/plugin_error.dart';
import 'package:tangem_sdk/tangem_sdk.dart';
import 'app_widgets.dart';
@@ -46,7 +47,7 @@ class _CommandListWidgetState extends State {
final prettyJson = _jsonEncoder.convert(success.toJson());
prettyJson.split("\n").forEach((element) => print(element));
}, (error) {
- if (error is SdkPluginError) {
+ if (error is TangemSdkPluginError) {
print(error.message);
} else {
print(error);
@@ -121,11 +122,11 @@ class _CommandListWidgetState extends State {
final listOfData = List.generate(_utils.randomInt(1, 10), (index) => _utils.randomString(20));
final hashes = listOfData.map((e) => e.toHexString()).toList();
- TangemSdk.sign(_callback, hashes, {TangemSdk.cid: _cardId});
+ TangemSdk.sign(_callback, hashes, {TangemSdk.cardId: _cardId});
}
handleReadIssuerData() {
- TangemSdk.readIssuerData(_callback, {TangemSdk.cid: _cardId});
+ TangemSdk.readIssuerData(_callback, {TangemSdk.cardId: _cardId});
}
handleWriteIssuerData() {
@@ -139,13 +140,13 @@ class _CommandListWidgetState extends State {
final issuerDataCounter = 1;
TangemSdk.writeIssuerData(_callback, issuerData.toHexString(), issuerDataSignature.toHexString(), {
- TangemSdk.cid: _cardId,
+ TangemSdk.cardId: _cardId,
TangemSdk.issuerDataCounter: issuerDataCounter,
});
}
handleReadIssuerExtraData() {
- TangemSdk.readIssuerExtraData(_callback, {TangemSdk.cid: _cardId});
+ TangemSdk.readIssuerExtraData(_callback, {TangemSdk.cardId: _cardId});
}
handleWriteIssuerExtraData() {
@@ -162,13 +163,13 @@ class _CommandListWidgetState extends State {
TangemSdk.writeIssuerExtraData(
_callback, issuerData.toHexString(), startingSignature.toHexString(), finalizingSignature.toHexString(), {
- TangemSdk.cid: _cardId,
+ TangemSdk.cardId: _cardId,
TangemSdk.issuerDataCounter: counter,
});
}
handleReadUserData() {
- TangemSdk.readUserData(_callback, {TangemSdk.cid: _cardId});
+ TangemSdk.readUserData(_callback, {TangemSdk.cardId: _cardId});
}
handleWriteUserData() {
@@ -176,7 +177,7 @@ class _CommandListWidgetState extends State {
final userCounter = 1;
TangemSdk.writeUserData(_callback, userData.toHexString(), {
- TangemSdk.cid: _cardId,
+ TangemSdk.cardId: _cardId,
TangemSdk.userCounter: userCounter,
});
}
@@ -186,25 +187,25 @@ class _CommandListWidgetState extends State {
final protectedCounter = 1;
TangemSdk.writeUserProtectedData(_callback, userProtectedData.toHexString(), {
- TangemSdk.cid: _cardId,
+ TangemSdk.cardId: _cardId,
TangemSdk.userProtectedCounter: protectedCounter,
});
}
handleCreateWallet() {
- TangemSdk.createWallet(_callback, {TangemSdk.cid: _cardId});
+ TangemSdk.createWallet(_callback, {TangemSdk.cardId: _cardId});
}
handlePurgeWallet() {
- TangemSdk.purgeWallet(_callback, {TangemSdk.cid: _cardId});
+ TangemSdk.purgeWallet(_callback, {TangemSdk.cardId: _cardId});
}
handleSetPin1() {
- TangemSdk.setPinCode(_callback, PinType.PIN1, {TangemSdk.cid: _cardId});
+ TangemSdk.setPinCode(_callback, PinType.PIN1, {TangemSdk.cardId: _cardId});
}
handleSetPin2() {
- TangemSdk.setPinCode(_callback, PinType.PIN2, {TangemSdk.cid: _cardId});
+ TangemSdk.setPinCode(_callback, PinType.PIN2, {TangemSdk.cardId: _cardId});
}
_showToast(String message) {
diff --git a/plugin/tangem-sdk-flutter/ios/Classes/SwiftTangemSdkPlugin.swift b/plugin/tangem-sdk-flutter/ios/Classes/SwiftTangemSdkPlugin.swift
index b5868f5..2e8650d 100644
--- a/plugin/tangem-sdk-flutter/ios/Classes/SwiftTangemSdkPlugin.swift
+++ b/plugin/tangem-sdk-flutter/ios/Classes/SwiftTangemSdkPlugin.swift
@@ -12,53 +12,61 @@ public class SwiftTangemSdkPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "tangemSdk", binaryMessenger: registrar.messenger())
+ let channelJS = FlutterMethodChannel(name: "tangemSdk_JSONRPC", binaryMessenger: registrar.messenger())
let instance = SwiftTangemSdkPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
+ registrar.addMethodCallDelegate(instance, channel: channelJS)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
do {
switch call.method {
- case "scanCard":
- try scanCard(call.arguments, result)
- case "sign":
- try sign(call.arguments, result)
- case "personalize":
- try personalize(call.arguments, result)
- case "depersonalize":
- try depersonalize(call.arguments, result)
- case "createWallet":
- try createWallet(call.arguments, result)
- case "purgeWallet":
- try purgeWallet(call.arguments, result)
- case "readIssuerData":
- try readIssuerData(call.arguments, result)
- case "writeIssuerData":
- try writeIssuerData(call.arguments, result)
- case "readIssuerExData":
- try readIssuerExData(call.arguments, result)
- case "writeIssuerExData":
- try writeIssuerExData(call.arguments, result)
- case "readUserData":
- try readUserData(call.arguments, result)
- case "writeUserData":
- try writeUserData(call.arguments, result)
- case "writeUserProtectedData":
- try writeUserProtectedData(call.arguments, result)
- case "setPin1":
- try setPin1(call.arguments, result)
- case "setPin2":
- try setPin2(call.arguments, result)
- case "allowsOnlyDebugCards":
- allowsOnlyDebugCards(call.arguments, result)
- case "prepareHashes":
- try prepareHashes(call.arguments, result)
+ case "runJSONRPCRequest":
+ try runJSONRPCRequest(call.arguments, result)
+ case "startSession":
+ result("null")
+ case "stopSession":
+ result("null")
+// case "scanCard":
+// try scanCard(call.arguments, result)
+// case "sign":
+// try sign(call.arguments, result)
+// case "personalize":
+// try personalize(call.arguments, result)
+// case "depersonalize":
+// try depersonalize(call.arguments, result)
+// case "createWallet":
+// try createWallet(call.arguments, result)
+// case "purgeWallet":
+// try purgeWallet(call.arguments, result)
+// case "readIssuerData":
+// try readIssuerData(call.arguments, result)
+// case "writeIssuerData":
+// try writeIssuerData(call.arguments, result)
+// case "readIssuerExData":
+// try readIssuerExData(call.arguments, result)
+// case "writeIssuerExData":
+// try writeIssuerExData(call.arguments, result)
+// case "readUserData":
+// try readUserData(call.arguments, result)
+// case "writeUserData":
+// try writeUserData(call.arguments, result)
+// case "writeUserProtectedData":
+// try writeUserProtectedData(call.arguments, result)
+// case "setPin1":
+// try setPin1(call.arguments, result)
+// case "setPin2":
+// try setPin2(call.arguments, result)
+// case "allowsOnlyDebugCards":
+// allowsOnlyDebugCards(call.arguments, result)
+// case "prepareHashes":
+// try prepareHashes(call.arguments, result)
// case "writeFiles":
// try writeFiles(call.arguments, result)
- case "readFiles":
- try readFiles(call.arguments, result)
- case "deleteFiles":
- try deleteFiles(call.arguments, result)
+// case "readFiles":
+// try readFiles(call.arguments, result)
+// case "deleteFiles":
+// try deleteFiles(call.arguments, result)
// case "changeFilesSettings":
// try changeFilesSettings(call.arguments, result)
default:
@@ -67,180 +75,196 @@ public class SwiftTangemSdkPlugin: NSObject, FlutterPlugin {
} catch {
print(error)
print(error.localizedDescription)
- handleError(error, result)
+ let flutterError = error as? FlutterError ?? .underlyingError(error)
+ result(flutterError)
}
}
- private func scanCard(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.scanCard(onlineVerification: false,
- walletIndex: nil,
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
+ private func runJSONRPCRequest(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+ guard let request: String = getArg(for: .request, from: args) else {
+ throw FlutterError.missingRequest
}
- }
-
- private func sign(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- let hexHashes: [String] = try getArg(.hashes, from: args)
- let hashes = hexHashes.compactMap({Data(hexString: $0)})
- sdk.sign(hashes: hashes,
- cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args),
- pin2: try getArgOptional(.pin2, from: args)
- ) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func personalize(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.personalize(config: try getArg(.cardConfig, from: args),
- issuer: try getArg(.issuer, from: args),
- manufacturer: try getArg(.manufacturer, from: args),
- acquirer: try getArgOptional(.acquirer, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func depersonalize(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.depersonalize(initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func createWallet(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.createWallet(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args),
- pin2: try getArgOptional(.pin2, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func purgeWallet(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.purgeWallet(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args),
- pin2: try getArgOptional(.pin2, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func readIssuerData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.readIssuerData(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func writeIssuerData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.writeIssuerData(cardId: try getArg(.cid, from: args),
- issuerData: try getArg(.issuerData, from: args),
- issuerDataSignature: try getArg(.issuerDataSignature, from: args),
- issuerDataCounter: try getArgOptional(.issuerDataCounter, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func readIssuerExData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.readIssuerExtraData(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func writeIssuerExData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.writeIssuerExtraData(cardId: try getArg(.cid, from: args),
- issuerData: try getArg(.issuerData, from: args),
- startingSignature: try getArg(.startingSignature, from: args),
- finalizingSignature: try getArg(.finalizingSignature, from: args),
- issuerDataCounter: try getArgOptional(.issuerDataCounter, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)){ [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func readUserData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.readUserData(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func writeUserData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.writeUserData(cardId: try getArgOptional(.cid, from: args),
- userData: try getArg(.userData, from: args),
- userCounter: try getArgOptional(.userCounter, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)){ [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func writeUserProtectedData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.writeUserProtectedData(cardId: try getArgOptional(.cid, from: args),
- userProtectedData: try getArg(.userProtectedData, from: args),
- userProtectedCounter: try getArgOptional(.userProtectedCounter, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func setPin1(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- let pin: String? = try getArgOptional(.pinCode, from: args)
+ let cardId: String? = getArg(for: .cardId, from: args)
+ let initialMessage: String? = getArg(for: .initialMessage, from: args)
- sdk.changePin1(cardId: try getArgOptional(.cid, from: args),
- pin: pin?.sha256(),
- initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
+ sdk.startSession(with: request,
+ cardId: cardId,
+ initialMessage: initialMessage) { result in
+ completion(result)
}
}
- private func setPin2(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- let pin: String? = try getArgOptional(.pinCode, from: args)
-
- sdk.changePin2(cardId: try getArgOptional(.cid, from: args),
- pin: pin?.sha256(),
- initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
-
- private func allowsOnlyDebugCards(_ args: Any?, _ completion: @escaping FlutterResult) {
- if let allowedOnlyDebug = (args as? [String: Any])?["isAllowedOnlyDebugCards"] as? Bool {
- sdk.config.allowedCardTypes = allowedOnlyDebug ? [.sdk] : FirmwareType.allCases
- }
- }
-
- private func prepareHashes(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- let hashes = sdk.prepareHashes(cardId: try getArg(.cid, from: args),
- fileData: try getArg(.fileData, from: args),
- fileCounter: try getArg(.fileCounter, from: args),
- privateKey: try getArgOptional(.privateKey, from: args))
- completion(hashes.description)
- }
-
- private func readFiles(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- let readPrivateFiles: Bool = try getArgOptional(.readPrivateFiles, from: args) ?? false
- //let indices: [Int] = try getArg(.indices, from: args)
-
- sdk.readFiles(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args),
- pin2: try getArgOptional(.pin2, from: args),
- readSettings: ReadFilesTaskSettings(readPrivateFiles: readPrivateFiles)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
+// private func scanCard(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.scanCard(onlineVerification: false,
+// walletIndex: nil,
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func sign(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// let hexHashes: [String] = try getArg(.hashes, from: args)
+// let hashes = hexHashes.compactMap({Data(hexString: $0)})
+//
+// sdk.sign(hashes: hashes,
+// cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args),
+// pin2: try getArgOptional(.pin2, from: args)
+// ) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func personalize(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.personalize(config: try getArg(.cardConfig, from: args),
+// issuer: try getArg(.issuer, from: args),
+// manufacturer: try getArg(.manufacturer, from: args),
+// acquirer: try getArgOptional(.acquirer, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func depersonalize(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.depersonalize(initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func createWallet(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.createWallet(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args),
+// pin2: try getArgOptional(.pin2, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func purgeWallet(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.purgeWallet(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args),
+// pin2: try getArgOptional(.pin2, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func readIssuerData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.readIssuerData(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func writeIssuerData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.writeIssuerData(cardId: try getArg(.cid, from: args),
+// issuerData: try getArg(.issuerData, from: args),
+// issuerDataSignature: try getArg(.issuerDataSignature, from: args),
+// issuerDataCounter: try getArgOptional(.issuerDataCounter, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func readIssuerExData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.readIssuerExtraData(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func writeIssuerExData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.writeIssuerExtraData(cardId: try getArg(.cid, from: args),
+// issuerData: try getArg(.issuerData, from: args),
+// startingSignature: try getArg(.startingSignature, from: args),
+// finalizingSignature: try getArg(.finalizingSignature, from: args),
+// issuerDataCounter: try getArgOptional(.issuerDataCounter, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)){ [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func readUserData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.readUserData(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func writeUserData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.writeUserData(cardId: try getArgOptional(.cid, from: args),
+// userData: try getArg(.userData, from: args),
+// userCounter: try getArgOptional(.userCounter, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)){ [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func writeUserProtectedData(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.writeUserProtectedData(cardId: try getArgOptional(.cid, from: args),
+// userProtectedData: try getArg(.userProtectedData, from: args),
+// userProtectedCounter: try getArgOptional(.userProtectedCounter, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func setPin1(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// let pin: String? = try getArgOptional(.pinCode, from: args)
+//
+// sdk.changePin1(cardId: try getArgOptional(.cid, from: args),
+// pin: pin?.sha256(),
+// initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func setPin2(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// let pin: String? = try getArgOptional(.pinCode, from: args)
+//
+// sdk.changePin2(cardId: try getArgOptional(.cid, from: args),
+// pin: pin?.sha256(),
+// initialMessage: try getArgOptional(.initialMessage, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
+//
+// private func allowsOnlyDebugCards(_ args: Any?, _ completion: @escaping FlutterResult) {
+// if let allowedOnlyDebug = (args as? [String: Any])?["isAllowedOnlyDebugCards"] as? Bool {
+// sdk.config.allowedCardTypes = allowedOnlyDebug ? [.sdk] : FirmwareType.allCases
+// }
+// }
+////
+// private func prepareHashes(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// let hashes = sdk.prepareHashes(cardId: try getArg(.cid, from: args),
+// fileData: try getArg(.fileData, from: args),
+// fileCounter: try getArg(.fileCounter, from: args),
+// privateKey: try getArgOptional(.privateKey, from: args))
+// completion(hashes.description)
+// }
+//
+// private func readFiles(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// let readPrivateFiles: Bool = try getArgOptional(.readPrivateFiles, from: args) ?? false
+// //let indices: [Int] = try getArg(.indices, from: args)
+//
+// sdk.readFiles(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args),
+// pin2: try getArgOptional(.pin2, from: args),
+// readSettings: ReadFilesTaskSettings(readPrivateFiles: readPrivateFiles)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
// private func writeFiles(_ args: Any?, _ completion: @escaping FlutterResult) throws {
// sdk.writeFiles(cardId: try getArgOptional(.cid, from: args),
@@ -263,150 +287,149 @@ public class SwiftTangemSdkPlugin: NSObject, FlutterPlugin {
// }
// }
- private func deleteFiles(_ args: Any?, _ completion: @escaping FlutterResult) throws {
- sdk.deleteFiles(cardId: try getArgOptional(.cid, from: args),
- initialMessage: try getArgOptional(.initialMessage, from: args),
- pin1: try getArgOptional(.pin1, from: args),
- pin2: try getArgOptional(.pin2, from: args),
- indicesToDelete: try getArgOptional(.indices, from: args)) { [weak self] result in
- self?.handleCompletion(result, completion)
- }
- }
+// private func deleteFiles(_ args: Any?, _ completion: @escaping FlutterResult) throws {
+// sdk.deleteFiles(cardId: try getArgOptional(.cid, from: args),
+// initialMessage: try getArgOptional(.initialMessage, from: args),
+// pin1: try getArgOptional(.pin1, from: args),
+// pin2: try getArgOptional(.pin2, from: args),
+// indicesToDelete: try getArgOptional(.indices, from: args)) { [weak self] result in
+// self?.handleCompletion(result, completion)
+// }
+// }
- private func handleCompletion(_ sdkResult: Result, _ completion: @escaping FlutterResult) {
- switch sdkResult {
- case .success(let response):
- completion(response.description)
- case .failure(let error):
- handleError(error, completion)
- }
- }
+// private func handleCompletion(_ sdkResult: Result, _ completion: @escaping FlutterResult) {
+// switch sdkResult {
+// case .success(let response):
+// completion(response.description)
+// case .failure(let error):
+// handleError(error, completion)
+// }
+// }
+//
+// private func handleError(_ error: Error, _ completion: @escaping FlutterResult) {
+// var pluginError: PluginError
+//
+// if let sdkError = error as? TangemSdkError {
+// pluginError = sdkError.toPluginError()
+// } else if let pluginInternalError = error as? PluginInternalError {
+// pluginError = pluginInternalError.toPluginError()
+// } else {
+// pluginError = PluginError(code: 9998, localizedDescription: "\(error)")
+// }
+//
+// completion(pluginError.flutterError)
+// }
- private func handleError(_ error: Error, _ completion: @escaping FlutterResult) {
- var pluginError: PluginError
-
- if let sdkError = error as? TangemSdkError {
- pluginError = sdkError.toPluginError()
- } else if let pluginInternalError = error as? PluginInternalError {
- pluginError = pluginInternalError.toPluginError()
- } else {
- pluginError = PluginError(code: 9998, localizedDescription: "\(error)")
+// private func getArgOptional(_ key: ArgKey, from arguments: Any?) throws -> T? {
+// do {
+// return try getArg(key, from: arguments)
+// } catch PluginInternalError.missingArgument {
+// return nil
+// }
+// }
+//
+ private func getArg(for key: ArgKey, from arguments: Any?) -> T? {
+ if let value = (arguments as? NSDictionary)?[key.rawValue] {
+ return value as? T
}
- completion(pluginError.flutterError)
- }
-
- private func getArgOptional(_ key: ArgKey, from arguments: Any?) throws -> T? {
- do {
- return try getArg(key, from: arguments)
- } catch PluginInternalError.missingArgument {
- return nil
- }
- }
-
- private func getArg(_ key: ArgKey, from arguments: Any?) throws -> T {
- if let value = (arguments as? [String: Any])?[key.rawValue] {
- if String(describing: value) == "" {
- throw PluginInternalError.missingArgument(key)
- }
-
- if T.self == Data.self || T.self == Data?.self {
- if let hex = value as? String {
- return (Data(hexString: hex) as! T)
- } else {
- throw PluginInternalError.failedToParseDataFromHex(key)
- }
- } else {
- do {
- return try decodeObject(value)
- } catch {
- if let converted = value as? T {
- return converted
- } else {
- throw error
- }
- }
- }
- } else {
- throw PluginInternalError.missingArgument(key)
- }
- }
-
- private func decodeObject(_ value: Any) throws -> T {
- if let json = value as? String, let jsonData = json.data(using: .utf8) {
- return try JSONDecoder.tangemSdkDecoder.decode(T.self, from: jsonData)
- } else {
- throw PluginInternalError.failedToParseJson
- }
+ return nil
}
+//
+// private func decodeObject(_ value: Any) throws -> T {
+// if let json = value as? String, let jsonData = json.data(using: .utf8) {
+// return try JSONDecoder.tangemSdkDecoder.decode(T.self, from: jsonData)
+// } else {
+// throw PluginInternalError.failedToParseJson
+// }
+// }
}
-fileprivate struct PluginError: Encodable {
- let code: Int
- let localizedDescription: String
-
- var jsonDescription: String {
- let encoder = JSONEncoder()
- encoder.outputFormatting = [.sortedKeys, .prettyPrinted]
- let data = (try? encoder.encode(self)) ?? Data()
- return String(data: data, encoding: .utf8)!
- }
-
- var flutterError: FlutterError {
- FlutterError(code: "\(code)", message: localizedDescription, details: jsonDescription)
- }
-}
+//fileprivate struct PluginError: Encodable {
+// let code: Int
+// let localizedDescription: String
+//
+// var jsonDescription: String {
+// let encoder = JSONEncoder()
+// encoder.outputFormatting = [.sortedKeys, .prettyPrinted]
+// let data = (try? encoder.encode(self)) ?? Data()
+// return String(data: data, encoding: .utf8)!
+// }
+//
+// var flutterError: FlutterError {
+// FlutterError(code: "\(code)", message: localizedDescription, details: jsonDescription)
+// }
+//}
+//
+//fileprivate extension TangemSdkError {
+// func toPluginError() -> PluginError {
+// return PluginError(code: code, localizedDescription: localizedDescription)
+// }
+//}
-fileprivate extension TangemSdkError {
- func toPluginError() -> PluginError {
- return PluginError(code: code, localizedDescription: localizedDescription)
- }
+//fileprivate enum PluginInternalError: Error, LocalizedError {
+// case failedToParseJson
+// case missingArgument(_ value: ArgKey)
+// case failedToParseDataFromHex(_ value: ArgKey)
+//
+// var errorDescription: String? {
+// switch self {
+// case .failedToParseJson:
+// return "Failed to parse JSON object"
+// case .missingArgument(let value):
+// return "Missing required argument: \(value.rawValue)"
+// case .failedToParseDataFromHex(let value):
+// return "Failed to parse Data from HEX for: \(value.rawValue)"
+// }
+// }
+//
+// func toPluginError() -> PluginError {
+// return PluginError(code: 9999 , localizedDescription: localizedDescription)
+// }
+//}
+
+fileprivate enum ArgKey: String {
+// case pin1
+// case pin2
+ case cardId
+// case hashes
+// case userCounter
+// case userProtectedCounter
+// case userData
+// case issuerDataCounter
+// case userProtectedData
+// case issuerData
+// case issuerDataSignature
+// case startingSignature
+// case finalizingSignature
+// case issuer
+// case manufacturer
+// case acquirer
+// case cardConfig
+// case pinCode
+ case initialMessage
+// case fileData
+// case fileCounter
+// case privateKey
+// case readPrivateFiles
+// case indices
+ case request = "JSONRPCRequest"
}
-fileprivate enum PluginInternalError: Error, LocalizedError {
- case failedToParseJson
- case missingArgument(_ value: ArgKey)
- case failedToParseDataFromHex(_ value: ArgKey)
+extension FlutterError: Error {}
+
+fileprivate extension FlutterError {
+ static let genericCode = "9999"
- var errorDescription: String? {
- switch self {
- case .failedToParseJson:
- return "Failed to parse JSON object"
- case .missingArgument(let value):
- return "Missing required argument: \(value.rawValue)"
- case .failedToParseDataFromHex(let value):
- return "Failed to parse Data from HEX for: \(value.rawValue)"
- }
+ static var parseArgumentsError: FlutterError {
+ FlutterError(code: genericCode, message: "Faield to parse arguments", details: nil)
}
- func toPluginError() -> PluginError {
- return PluginError(code: 9999 , localizedDescription: localizedDescription)
+ static var missingRequest: FlutterError {
+ FlutterError(code: genericCode, message: "Missing JSON RPC request", details: nil)
+ }
+
+ static func underlyingError(_ error: Error) -> FlutterError {
+ FlutterError(code: genericCode, message: "Some error occured", details: error)
}
-}
-
-fileprivate enum ArgKey: String {
- case pin1
- case pin2
- case cid
- case hashes
- case userCounter
- case userProtectedCounter
- case userData
- case issuerDataCounter
- case userProtectedData
- case issuerData
- case issuerDataSignature
- case startingSignature
- case finalizingSignature
- case issuer
- case manufacturer
- case acquirer
- case cardConfig
- case pinCode
- case initialMessage
- case fileData
- case fileCounter
- case privateKey
- case readPrivateFiles
- case indices
}
diff --git a/plugin/tangem-sdk-flutter/ios/tangem_sdk.podspec b/plugin/tangem-sdk-flutter/ios/tangem_sdk.podspec
index d55c88f..1350091 100644
--- a/plugin/tangem-sdk-flutter/ios/tangem_sdk.podspec
+++ b/plugin/tangem-sdk-flutter/ios/tangem_sdk.podspec
@@ -18,7 +18,7 @@ TangemSdk plugin for integration into flutter projects
s.ios.deployment_target = '13.0'
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
- s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
- s.dependency 'TangemSdk', "~> 2.4.2"
+ s.dependency 'TangemSdk', "~> 3.0.2"
end
diff --git a/plugin/tangem-sdk-flutter/lib/card_responses/card_response.g.dart b/plugin/tangem-sdk-flutter/lib/card_responses/card_response.g.dart
index ea23ef4..e09401d 100644
--- a/plugin/tangem-sdk-flutter/lib/card_responses/card_response.g.dart
+++ b/plugin/tangem-sdk-flutter/lib/card_responses/card_response.g.dart
@@ -16,7 +16,9 @@ CardResponse _$CardResponseFromJson(Map json) {
json['defaultCurve'] as String?,
(json['settingsMask'] as List?)?.map((e) => e as String).toList(),
json['issuerPublicKey'] as String?,
- (json['signingMethods'] as List?)?.map((e) => e as String).toList(),
+ (json['signingMethods'] as List?)
+ ?.map((e) => e as String)
+ .toList(),
json['pauseBeforePin2'] as int?,
json['walletsCount'] as int?,
json['walletIndex'] as int?,
@@ -27,14 +29,19 @@ CardResponse _$CardResponseFromJson(Map json) {
json['userCounter'] as int?,
json['userProtectedCounter'] as int?,
json['terminalIsLinked'] as bool,
- json['cardData'] == null ? null : CardData.fromJson(json['cardData'] as Map),
+ json['cardData'] == null
+ ? null
+ : CardData.fromJson(json['cardData'] as Map),
json['isPin1Default'] as bool?,
json['isPin2Default'] as bool?,
- (json['wallets'] as List?)?.map((e) => CardWallet.fromJson(e as Map)).toList(),
+ (json['wallets'] as List?)
+ ?.map((e) => CardWallet.fromJson(e as Map))
+ .toList(),
);
}
-Map _$CardResponseToJson(CardResponse instance) => {
+Map _$CardResponseToJson(CardResponse instance) =>
+ {
'cardId': instance.cardId,
'manufacturerName': instance.manufacturerName,
'status': instance.status,
@@ -66,17 +73,21 @@ SignResponse _$SignResponseFromJson(Map json) {
);
}
-Map _$SignResponseToJson(SignResponse instance) => {
+Map _$SignResponseToJson(SignResponse instance) =>
+ {
'signedHashes': instance.signedHashes,
};
-DepersonalizeResponse _$DepersonalizeResponseFromJson(Map json) {
+DepersonalizeResponse _$DepersonalizeResponseFromJson(
+ Map json) {
return DepersonalizeResponse(
json['success'] as bool,
);
}
-Map _$DepersonalizeResponseToJson(DepersonalizeResponse instance) => {
+Map _$DepersonalizeResponseToJson(
+ DepersonalizeResponse instance) =>
+ {
'success': instance.success,
};
@@ -88,7 +99,9 @@ CreateWalletResponse _$CreateWalletResponseFromJson(Map json) {
);
}
-Map _$CreateWalletResponseToJson(CreateWalletResponse instance) => {
+Map _$CreateWalletResponseToJson(
+ CreateWalletResponse instance) =>
+ {
'cardId': instance.cardId,
'status': instance.status,
'walletPublicKey': instance.walletPublicKey,
@@ -101,12 +114,15 @@ PurgeWalletResponse _$PurgeWalletResponseFromJson(Map json) {
);
}
-Map _$PurgeWalletResponseToJson(PurgeWalletResponse instance) => {
+Map _$PurgeWalletResponseToJson(
+ PurgeWalletResponse instance) =>
+ {
'cardId': instance.cardId,
'status': instance.status,
};
-ReadIssuerDataResponse _$ReadIssuerDataResponseFromJson(Map json) {
+ReadIssuerDataResponse _$ReadIssuerDataResponseFromJson(
+ Map json) {
return ReadIssuerDataResponse(
json['cardId'] as String,
json['issuerData'] as String,
@@ -115,24 +131,30 @@ ReadIssuerDataResponse _$ReadIssuerDataResponseFromJson(Map jso
);
}
-Map _$ReadIssuerDataResponseToJson(ReadIssuerDataResponse instance) => {
+Map _$ReadIssuerDataResponseToJson(
+ ReadIssuerDataResponse instance) =>
+ {
'cardId': instance.cardId,
'issuerData': instance.issuerData,
'issuerDataSignature': instance.issuerDataSignature,
'issuerDataCounter': instance.issuerDataCounter,
};
-WriteIssuerDataResponse _$WriteIssuerDataResponseFromJson(Map json) {
+WriteIssuerDataResponse _$WriteIssuerDataResponseFromJson(
+ Map