Skip to content

Commit

Permalink
Merge pull request semlette#7 from smlu/patch-trnx
Browse files Browse the repository at this point in the history
Add optional parameter timeout to transcieve
  • Loading branch information
Harry-Chen authored May 3, 2020
2 parents b6974c0 + d76a815 commit 74b3909
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package im.nfc.flutter_nfc_kit

import java.util.*
import java.io.IOException
import java.lang.reflect.InvocationTargetException
import org.json.JSONObject
import kotlin.concurrent.schedule

import android.app.Activity
import android.nfc.NfcAdapter
import android.nfc.NfcAdapter.*
import android.nfc.Tag
import android.nfc.tech.*
import android.os.Handler
import android.os.Looper
Expand All @@ -33,6 +33,16 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
private var activity: Activity? = null
private var pollingTimeoutTask: TimerTask? = null
private var tagTechnology: TagTechnology? = null
private fun TagTechnology.transcieve(data: ByteArray, timeout: Int?) : ByteArray {
if(timeout != null) {
try {
val timeoutMethod = this.javaClass.getMethod("setTimeout", Int::class.java)
timeoutMethod.invoke(this, timeout)
} catch (ex: Throwable){}
}
val transceiveMethod = this.javaClass.getMethod("transceive", ByteArray::class.java)
return transceiveMethod.invoke(this, data) as ByteArray
}
}

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
Expand Down Expand Up @@ -76,11 +86,10 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
result.success("")
}


"transceive" -> {
val tagTech = tagTechnology
val req = call.arguments as? String
if (req == null) {
val req = call.argument<Any>("data")
if (req == null || (req !is String && req !is ByteArray)) {
result.error("400", "Bad argument", null)
return
}
Expand All @@ -94,33 +103,33 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
} catch (ex: IOException) {
Log.e(TAG, "Transceive Error: $req", ex)
result.error("500", "Communication error", ex.localizedMessage)
return
}
}
try {
val sendingBytes = req!!.hexToBytes()
val recvingBytes: ByteArray
when (tagTech) {
is IsoDep -> recvingBytes = tagTech.transceive(sendingBytes)
is NfcA -> recvingBytes = tagTech.transceive(sendingBytes)
is NfcB -> recvingBytes = tagTech.transceive(sendingBytes)
is NfcF -> recvingBytes = tagTech.transceive(sendingBytes)
is NfcV -> recvingBytes = tagTech.transceive(sendingBytes)
is MifareClassic -> recvingBytes = tagTech.transceive(sendingBytes)
is MifareUltralight -> recvingBytes = tagTech.transceive(sendingBytes)
else -> {
result.error("405", "Transceive not supported on this type of card", null)
return
}
val sendingBytes = when(req) {
is String -> (req !!as String).hexToBytes()
else -> req !!as ByteArray
}

val timeout = call.argument<Int>("timeout")
val resp = tagTech.transcieve(sendingBytes, timeout)
when(req) {
is String -> result.success(resp.toHexString())
else -> result.success(resp)
}
val resp = recvingBytes.toHexString()
Log.d(TAG, "Transceive: $req, $resp")
result.success(resp)
} catch (ex: IOException) {
Log.e(TAG, "Transceive Error: $req", ex)
result.error("500", "Communication error", ex.localizedMessage)
} catch (ex: IllegalArgumentException) {
} catch(ex: InvocationTargetException) {
Log.e(TAG, "Transceive Error: $req", ex.cause ?: ex)
result.error("500", "Communication error", ex.cause?.localizedMessage)
}
catch (ex: IllegalArgumentException) {
Log.e(TAG, "APDU Error: $req", ex)
result.error("400", "APDU format error", ex.localizedMessage)
} catch(ex: NoSuchMethodException) {
result.error("405", "Transceive not supported for this type of card", null)
}
}

Expand Down
33 changes: 23 additions & 10 deletions ios/Classes/SwiftFlutterNfcKitPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ func dataWithHexString(hex: String) -> Data {
let subIndex = hex.index(hex.startIndex, offsetBy: 2)
let c = String(hex[..<subIndex])
hex = String(hex[subIndex...])
var ch: UInt32 = 0
Scanner(string: c).scanHexInt32(&ch)
var ch: UInt64 = 0
Scanner(string: c).scanHexInt64(&ch)
var char = UInt8(ch)
data.append(&char, count: 1)
}
Expand Down Expand Up @@ -63,23 +63,36 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
} else if call.method == "transceive" {
if tag != nil {
if let input = call.arguments as? String {
let data = dataWithHexString(hex: input)
let req = (call.arguments as? [String:Any?])?["data"]
if req != nil && (req is String || req is FlutterStandardTypedData) {
switch tag {
case let .iso7816(tag):
if let apdu = NFCISO7816APDU(data: data) {
tag.sendCommand(apdu: apdu, completionHandler: { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in
let apdu: NFCISO7816APDU? = {
switch req {
case let hexReq as String:
return NFCISO7816APDU(data: dataWithHexString(hex: hexReq))
case let binReq as FlutterStandardTypedData:
return NFCISO7816APDU(data: binReq.data)
default: return nil
}
}()
if apdu != nil {
tag.sendCommand(apdu: apdu!, completionHandler: { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in
if let error = error {
result(FlutterError(code: "500", message: "Communication error", details: error.localizedDescription))
} else {
let sw = String(format: "%02X%02X", sw1, sw2)
result("\(response.hexEncodedString())\(sw)")
var response = response
response.append(contentsOf: [sw1, sw2])
if req is String {
result(response.hexEncodedString())
} else {
result(response)
}
}
})
} else {
result(FlutterError(code: "400", message: "APDU format error", details: nil))
}

default:
result(FlutterError(code: "405", message: "Transceive not supported on this type of card", details: nil))
}
Expand Down Expand Up @@ -108,7 +121,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
self.session = nil
}

tag = nil
result(nil)
} else if call.method == "setIosAlertMessage" {
Expand Down
15 changes: 12 additions & 3 deletions lib/flutter_nfc_kit.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/services.dart';
import 'package:json_annotation/json_annotation.dart';
Expand Down Expand Up @@ -112,11 +113,19 @@ class FlutterNfcKit {
}

/// Transceive data with the card / tag in the format of APDU (iso7816) or raw commands (other technologies).
/// The [capdu] can be either of type Uint8List or hex string.
///
/// Note that iOS only supports APDU.
/// There must be a valid session when invoking.
static Future<String> transceive(String capdu) async {
return await _channel.invokeMethod('transceive', capdu);
///
/// On Android, [timeout] parameter will set transceive execution timeout.
/// Timeout is reset to default value when finish() is called.
static Future<T> transceive<T>(T capdu, {Duration timeout}) async {
assert(capdu is String || capdu is Uint8List);
return await _channel.invokeMethod('transceive', {
'data' : capdu,
'timeout' : timeout?.inMilliseconds
});
}

/// Finish current session.
Expand All @@ -135,7 +144,7 @@ class FlutterNfcKit {

/// iOS only, change currently displayed NFC reader session alert message with [message].
/// There must be a valid session when invoking.
/// On android, call to this function does nothing.
/// On Android, call to this function does nothing.
static Future<void> setIosAlertMessage(String message) async {
if (Platform.isIOS) {
return await _channel.invokeMethod('setIosAlertMessage', message);
Expand Down

0 comments on commit 74b3909

Please sign in to comment.