Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AGP and JAVA Version to make it compatible with Gradle 8.7 and Add support for Streaming #179

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$KotlinVersion"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.core:core-ktx:1.13.1'
}
2 changes: 1 addition & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
AGPVersion=7.4.2
AGPVersion=8.5.1
KotlinVersion=1.9.23
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri Sep 08 22:01:14 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.flutter.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
Expand All @@ -36,15 +37,17 @@ import kotlin.concurrent.schedule
class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

companion object {
private val TAG_EVENT_CHANNEL = "tagEventChannel"
private val TAG = FlutterNfcKitPlugin::class.java.name
private var activity: WeakReference<Activity> = WeakReference(null)
private var pollingTimeoutTask: TimerTask? = null
private var tagTechnology: TagTechnology? = null
private var ndefTechnology: Ndef? = null
private var mifareInfo: MifareInfo? = null
var tagTechnology: TagTechnology? = null
var ndefTechnology: Ndef? = null
var mifareInfo: MifareInfo? = null

private lateinit var nfcHandlerThread: HandlerThread
private lateinit var nfcHandler: Handler
private lateinit var tagEventHandler: EventChannel

private fun TagTechnology.transceive(data: ByteArray, timeout: Int?): ByteArray {
if (timeout != null) {
Expand Down Expand Up @@ -88,6 +91,8 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_nfc_kit")
channel.setMethodCallHandler(this)

tagEventHandler = EventChannel(flutterPluginBinding.binaryMessenger, TAG_EVENT_CHANNEL)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
Expand Down Expand Up @@ -385,6 +390,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = WeakReference(binding.activity)
tagEventHandler.setStreamHandler(TagEventHandler(activity))
}

override fun onDetachedFromActivity() {
Expand Down
199 changes: 199 additions & 0 deletions android/src/main/kotlin/im/nfc/flutter_nfc_kit/TagEventHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package im.nfc.flutter_nfc_kit

import android.app.Activity
import android.nfc.NfcAdapter
import android.nfc.NfcAdapter.getDefaultAdapter
import android.nfc.tech.IsoDep
import android.nfc.tech.MifareClassic
import android.nfc.tech.MifareUltralight
import android.nfc.tech.Ndef
import android.nfc.tech.NfcA
import android.nfc.tech.NfcB
import android.nfc.tech.NfcF
import android.nfc.tech.NfcV
import android.os.Handler
import android.os.Looper
import im.nfc.flutter_nfc_kit.ByteUtils.toHexString
import im.nfc.flutter_nfc_kit.FlutterNfcKitPlugin.Companion.mifareInfo
import im.nfc.flutter_nfc_kit.FlutterNfcKitPlugin.Companion.ndefTechnology
import im.nfc.flutter_nfc_kit.FlutterNfcKitPlugin.Companion.tagTechnology
import io.flutter.plugin.common.EventChannel
import org.json.JSONObject
import java.lang.ref.WeakReference


class TagEventHandler(activity: WeakReference<Activity>) : EventChannel.StreamHandler {
private var _activity: WeakReference<Activity> = activity
private val nfcAdapter: NfcAdapter = getDefaultAdapter(_activity.get())

private val uiThreadHandler: Handler = Handler(Looper.getMainLooper())

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
val argsMap = arguments as? Map<*, *>
val technologies = argsMap!!["technologies"] as Int

val pollHandler = NfcAdapter.ReaderCallback { tag ->

// common fields
val type: String
val id = tag.id.toHexString()
val standard: String
// ISO 14443 Type A
var atqa = ""
var sak = ""
// ISO 14443 Type B
var protocolInfo = ""
var applicationData = ""
// ISO 7816
var historicalBytes = ""
var hiLayerResponse = ""
// NFC-F / Felica
var manufacturer = ""
var systemCode = ""
// NFC-V
var dsfId = ""
// NDEF
var ndefAvailable = false
var ndefWritable = false
var ndefCanMakeReadOnly = false
var ndefCapacity = 0
var ndefType = ""

if (tag.techList.contains(NfcA::class.java.name)) {
val aTag = NfcA.get(tag)
atqa = aTag.atqa.toHexString()
sak = byteArrayOf(aTag.sak.toByte()).toHexString()
tagTechnology = aTag
when {
tag.techList.contains(IsoDep::class.java.name) -> {
standard = "ISO 14443-4 (Type A)"
type = "iso7816"
val isoDep = IsoDep.get(tag)
tagTechnology = isoDep
historicalBytes = isoDep.historicalBytes.toHexString()
}

tag.techList.contains(MifareClassic::class.java.name) -> {
standard = "ISO 14443-3 (Type A)"
type = "mifare_classic"
with(MifareClassic.get(tag)) {
tagTechnology = this
mifareInfo = MifareInfo(
this.type,
size,
MifareClassic.BLOCK_SIZE,
blockCount,
sectorCount
)
}
}

tag.techList.contains(MifareUltralight::class.java.name) -> {
standard = "ISO 14443-3 (Type A)"
type = "mifare_ultralight"
with(MifareUltralight.get(tag)) {
tagTechnology = this
mifareInfo = MifareInfo.fromUltralight(this.type)
}
}

else -> {
standard = "ISO 14443-3 (Type A)"
type = "unknown"
}
}
} else if (tag.techList.contains(NfcB::class.java.name)) {
val bTag = NfcB.get(tag)
protocolInfo = bTag.protocolInfo.toHexString()
applicationData = bTag.applicationData.toHexString()
if (tag.techList.contains(IsoDep::class.java.name)) {
type = "iso7816"
standard = "ISO 14443-4 (Type B)"
val isoDep = IsoDep.get(tag)
tagTechnology = isoDep
hiLayerResponse = isoDep.hiLayerResponse.toHexString()
} else {
type = "unknown"
standard = "ISO 14443-3 (Type B)"
tagTechnology = bTag
}
} else if (tag.techList.contains(NfcF::class.java.name)) {
standard = "ISO 18092 (FeliCa)"
type = "iso18092"
val fTag = NfcF.get(tag)
manufacturer = fTag.manufacturer.toHexString()
systemCode = fTag.systemCode.toHexString()
tagTechnology = fTag
} else if (tag.techList.contains(NfcV::class.java.name)) {
standard = "ISO 15693"
type = "iso15693"
val vTag = NfcV.get(tag)
dsfId = vTag.dsfId.toHexString()
tagTechnology = vTag
} else {
type = "unknown"
standard = "unknown"
}

// detect ndef
if (tag.techList.contains(Ndef::class.java.name)) {
val ndefTag = Ndef.get(tag)
ndefTechnology = ndefTag
ndefAvailable = true
ndefType = ndefTag.type
ndefWritable = ndefTag.isWritable
ndefCanMakeReadOnly = ndefTag.canMakeReadOnly()
ndefCapacity = ndefTag.maxSize
}

val jsonResult = JSONObject(
mapOf(
"type" to type,
"id" to id,
"standard" to standard,
"atqa" to atqa,
"sak" to sak,
"historicalBytes" to historicalBytes,
"protocolInfo" to protocolInfo,
"applicationData" to applicationData,
"hiLayerResponse" to hiLayerResponse,
"manufacturer" to manufacturer,
"systemCode" to systemCode,
"dsfId" to dsfId,
"ndefAvailable" to ndefAvailable,
"ndefType" to ndefType,
"ndefWritable" to ndefWritable,
"ndefCanMakeReadOnly" to ndefCanMakeReadOnly,
"ndefCapacity" to ndefCapacity,
)
)

if (mifareInfo != null) {
with(mifareInfo!!) {
jsonResult.put(
"mifareInfo", JSONObject(
mapOf(
"type" to typeStr,
"size" to size,
"blockSize" to blockSize,
"blockCount" to blockCount,
"sectorCount" to sectorCount
)
)
)
}
}

uiThreadHandler.post {
events?.success(jsonResult.toString())
}
}

nfcAdapter.enableReaderMode(_activity.get(), pollHandler, technologies, null)
}

override fun onCancel(p0: Any?) {
nfcAdapter.disableReaderMode(_activity.get())
}

}
2 changes: 1 addition & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
AGPVersion=7.4.2
AGPVersion=8.5.1
KotlinVersion=1.9.23
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
59 changes: 59 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
String? _result, _writeResult, _mifareResult;
late TabController _tabController;
List<ndef.NDEFRecord>? _records;
Stream<NFCTag>? _stream;
StreamSubscription<NFCTag>? _streamSubscription;

@override
void dispose() {
Expand Down Expand Up @@ -147,6 +149,63 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
child: Text('Start polling'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () async {
try {
if(_stream != null) {
_streamSubscription?.cancel();
setState(() {
_stream = null;
});
return;
}
setState(() {
_stream = FlutterNfcKit.tags();
});
_streamSubscription = _stream!.listen((tag) {
_tag = tag;
_mifareResult = null;
if (tag.standard == "ISO 14443-4 (Type B)") {
FlutterNfcKit.transceive("00B0950000").then((result1) {
FlutterNfcKit.transceive(
"00A4040009A00000000386980701").then((result2) {
setState(() {
_result = '1: $result1\n2: $result2\n';
});
});
});
} else if (tag.type == NFCTagType.iso18092) {
FlutterNfcKit.transceive("060080080100").then((result1) {
setState(() {
_result = '1: $result1\n';
});
});
} else if (tag.ndefAvailable ?? false) {
FlutterNfcKit.readNDEFRecords().then((ndefRecords) {
var ndefString = '';
for (int i = 0; i < ndefRecords.length; i++) {
ndefString += '${i + 1}: ${ndefRecords[i]}\n';
}
setState(() {
_result = ndefString;
});
});
} else if (tag.type == NFCTagType.webusb) {
FlutterNfcKit.transceive(
"00A4040006D27600012401").then((r) {
print(r);
});
}
});
} catch (e) {
setState(() {
_result = 'error: $e';
});
}
},
child: Text(_stream != null ? 'Stop Streaming' : 'Start Streaming'),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: _tag != null
Expand Down
Loading
Loading