From 4b61641efd0d7542514333c78d3407401c57b00a Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Tue, 2 Nov 2021 23:53:59 -0700 Subject: [PATCH 01/11] Save intent on reader callback Currently attempting to write a tag will fail because the tag information is not persisted in `savedIntent` as part of the reader session. This only affects Android. --- src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index e8256d83..dcc938d1 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -246,6 +246,7 @@ public void onTagDiscovered(Tag tag) { Intent tagIntent = new Intent(); tagIntent.putExtra(NfcAdapter.EXTRA_TAG, tag); + savedIntent = tagIntent; setIntent(tagIntent); PluginResult result = new PluginResult(PluginResult.Status.OK, json); From f318607d59091c8881c8d96b4f99f504c2a14843 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Fri, 5 Nov 2021 15:58:00 -0700 Subject: [PATCH 02/11] Add the ability to read tag during app launch --- README.md | 38 +++++++++++++++ .../nfc/plugin/NfcActivity.java | 48 +++++++++++++++++++ .../nfc/plugin/NfcPlugin.java | 47 ++++++++++++++++++ www/phonegap-nfc.js | 4 ++ 4 files changed, 137 insertions(+) create mode 100644 src/android/src/com/chariotsolutions/nfc/plugin/NfcActivity.java diff --git a/README.md b/README.md index 4df50fb7..8e58c1af 100644 --- a/README.md +++ b/README.md @@ -1280,6 +1280,44 @@ We have found it necessary to add `android:noHistory="true"` to the activity ele See the Android documentation for more information about [filtering for NFC intents](http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#ndef-disc). +## Accessing Tag information during application launch + +If your application is launched because an NFC tag was read, the intent passed to your main activity will be the `LAUNCH` intent, rather than one of the NFC tag intents (e.g. `NDEF_DISCOVERED`). In order to capture the NFC tag intent that was used to launch your application, you need to associate the NFC tag intent filter with an activity other than Cordova's `MainActivity`. + +You can do this following these steps: + +1. Add a section to your `AndroidManifest.xml` file with the appropriate NFC `intent-filter` that points to the activity `com.chariotsolutions.nfc.plugin.NfcActivity`. + * If you are developing in a multi-platform environment, you can do this by adding the following to your `config.xml`: + +```xml + + + + + + + + + + + + +``` + +2. On your app, during or after the Cordova `deviceready` event, call the `parseLaunchIntent` function, for example: + +```javascript +document.addEventListener("deviceready", onDeviceReady, false); + +function onDeviceReady() { + nfc.parseLaunchIntent(function(tag) { + console.log('Application was launched with tag: ' + JSON.stringify(tag)); + }); +} +``` + +The above will work whether your app was launched for the first time or simply brought to the foreground. + Testing ======= diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcActivity.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcActivity.java new file mode 100644 index 00000000..6b16c66a --- /dev/null +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcActivity.java @@ -0,0 +1,48 @@ +package com.chariotsolutions.nfc.plugin; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +public class NfcActivity extends Activity +{ + private static Intent launchIntent = null; + private static boolean initialized = false; + + public static final String TAG = "NfcActivity"; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + Log.i(TAG, "onCreate: " + getIntent().getAction()); + launchIntent = getIntent(); + + if (!initialized) { + // This looks like a cold start (Cordova has not launched) so send a launch intent + final Intent launchIntent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + if (launchIntent != null) { + launchIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(launchIntent); + } else { + Log.w(TAG, "onCreate: no launch intent defined!"); + } + } + + finish(); + } + + public static void onPluginInitialize() { + Log.i(TAG, "onPluginInitialize"); + initialized = true; + } + + public static Intent getLaunchIntent() { + final Intent result = launchIntent; + launchIntent = null; + Log.i(TAG, "getLaunchIntent: " + result); + return result; + } +} diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index dcc938d1..2a631d6e 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -53,6 +53,7 @@ public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCom private static final String ENABLED = "enabled"; private static final String INIT = "init"; private static final String SHOW_SETTINGS = "showSettings"; + private static final String PARSE_LAUNCH_INTENT = "parseLaunchIntent"; private static final String NDEF = "ndef"; private static final String NDEF_MIME = "ndef-mime"; @@ -170,6 +171,9 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo } else if (action.equalsIgnoreCase(INIT)) { init(callbackContext); + } else if (action.equalsIgnoreCase(PARSE_LAUNCH_INTENT)) { + parseLaunchIntent(callbackContext); + } else if (action.equalsIgnoreCase(ENABLED)) { // status is checked before every call // if code made it here, NFC is enabled @@ -197,6 +201,12 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo return true; } + @Override + protected void pluginInitialize() { + super.pluginInitialize(); + NfcActivity.onPluginInitialize(); + } + private String getNfcStatus() { NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); if (nfcAdapter == null) { @@ -307,6 +317,43 @@ private void init(CallbackContext callbackContext) { callbackContext.success(); } + private void parseLaunchIntent(final CallbackContext callbackContext) { + final Intent intent = NfcActivity.getLaunchIntent(); + if (intent != null) { + Log.d(TAG, "parseLaunchIntent " + intent); + String action = intent.getAction(); + Log.d(TAG, "action " + action); + + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES)); + + if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) { + Ndef ndef = Ndef.get(tag); + callbackContext.success(buildNdefJSON(ndef, messages)); + return; + } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { + Ndef ndef = null; + for (String tagTech : tag.getTechList()) { + Log.d(TAG, tagTech); + if (tagTech.equals(Ndef.class.getName())) { // + ndef = Ndef.get(tag); + } + } + if (ndef != null) { + callbackContext.success(buildNdefJSON(ndef, messages)); + } else { + callbackContext.success(Util.tagToJSON(tag)); + } + } else if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { + callbackContext.success(Util.tagToJSON(tag)); + } else { + callbackContext.error("NO_INTENT"); + } + } else { + callbackContext.error("NO_INTENT"); + } + } + private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { String mimeType = data.getString(0); removeIntentFilter(mimeType); diff --git a/www/phonegap-nfc.js b/www/phonegap-nfc.js index 7349bb7a..811ed110 100644 --- a/www/phonegap-nfc.js +++ b/www/phonegap-nfc.js @@ -500,6 +500,10 @@ var nfc = { cordova.exec(win, fail, "NfcPlugin", "showSettings", []); }, + parseLaunchIntent: function (win, fail) { + cordova.exec(win, fail, "NfcPlugin", "parseLaunchIntent", []); + }, + // iOS only - scan for NFC NDEF tag using NFCNDEFReaderSession scanNdef: function (options) { return new Promise(function(resolve, reject) { From d0fa13c4458771e04338cedd9858458ed85e56f8 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Sun, 7 Nov 2021 00:22:48 -0700 Subject: [PATCH 03/11] Add new source file to plugin manifest --- plugin.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin.xml b/plugin.xml index d2dddbe4..a2d42aa2 100644 --- a/plugin.xml +++ b/plugin.xml @@ -26,6 +26,8 @@ + Date: Sat, 13 Nov 2021 21:23:22 -0800 Subject: [PATCH 04/11] Add support for background app launch in iOS --- README.md | 42 +++++++++++++++++++- src/ios/NfcPlugin.h | 15 +++++++ src/ios/NfcPlugin.m | 97 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e58c1af..1e522466 100644 --- a/README.md +++ b/README.md @@ -1264,6 +1264,46 @@ Or to scan only Plain Text tags use See the [BlackBerry documentation](http://developer.blackberry.com/native/documentation/cascades/device_comm/nfc/receiving_content.html) for more info. +# Launching your iOS Application when Scanning a Tag + +On iOS, your application can be launched when an NFC tag is read, if the tag contains an NDEF message with a Universal Link (URL). This is optional and requires configuration on your application entitlements. The Universal Link needs to point to an actual HTTPS server that contains an `apple-app-site-association` that links the URL with your app. + +In a nutshell, you need to: + +1. Setup an associated domain relationship between your app and the Universal Link URL of your choosing. See: https://developer.apple.com/documentation/xcode/supporting-associated-domains + +2. Add an associated domain entitlement to your app in XCode. See: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_associated-domains + * If you are developing in a multi-platform environment, you can do this by adding the following to your `config.xml`: + +```xml + + + + applinks:myapp.com + + + + + applinks:myapp.com + + + +``` + +3. On your app, during or after the Cordova `deviceready` event, call the `parseLaunchIntent` plugin function, for example: + +```javascript +document.addEventListener("deviceready", onDeviceReady, false); + +function onDeviceReady() { + nfc.parseLaunchIntent(function(tag) { + console.log('Application was launched with tag: ' + JSON.stringify(tag)); + }); +} +``` + + * You may want to do the same on the `onresume` event to support the case where your app was already running in the background. + # Launching your Android Application when Scanning a Tag On Android, intents can be used to launch your application when a NFC tag is read. This is optional and configured in AndroidManifest.xml. @@ -1316,7 +1356,7 @@ function onDeviceReady() { } ``` -The above will work whether your app was launched for the first time or simply brought to the foreground. + * You may want to do the same on the `onresume` event to support the case where your app was already running in the background. Testing ======= diff --git a/src/ios/NfcPlugin.h b/src/ios/NfcPlugin.h index 46391af1..739df3fe 100644 --- a/src/ios/NfcPlugin.h +++ b/src/ios/NfcPlugin.h @@ -11,16 +11,26 @@ #import #import +#import "AppDelegate.h" + @interface NfcPlugin : CDVPlugin { } // iOS Specific API +// Cordova lifecycle events +- (void) onPause; +- (void) onResume; + // deprecated use scanNdef or scanTag - (void)beginSession:(CDVInvokedUrlCommand *)command; // deprecated use stopScan - (void)invalidateSession:(CDVInvokedUrlCommand *)command; +// Handle launch data +- (void)parseLaunchIntent:(CDVInvokedUrlCommand *)command; +- (void)messageReceived:(NFCNDEFMessage *)message; + // Added iOS 13 - (void)scanNdef:(CDVInvokedUrlCommand *)command; - (void)scanTag:(CDVInvokedUrlCommand *)command; @@ -37,4 +47,9 @@ @end +@interface AppDelegate (nfcDelegate) + - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler ; + @property (nonatomic, strong) IBOutlet NSUserActivity* userActivity; +@end + #endif diff --git a/src/ios/NfcPlugin.m b/src/ios/NfcPlugin.m index 9b40f251..4edb2612 100644 --- a/src/ios/NfcPlugin.m +++ b/src/ios/NfcPlugin.m @@ -5,6 +5,50 @@ // (c) 2107-2020 Don Coleman #import "NfcPlugin.h" +#import + +static void * UserActivityPropertyKey = &UserActivityPropertyKey; +static NFCNDEFMessage * LaunchMessage = nil; +static NfcPlugin* Listener = nil; + +@implementation AppDelegate (nfcDelegate) + +- (NSUserActivity *)userActivity { + return objc_getAssociatedObject(self, UserActivityPropertyKey); +} + +- (void)setUserActivity:(NSUserActivity *)activity { + objc_setAssociatedObject(self, UserActivityPropertyKey, activity, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)application:(UIApplication *)application +continueUserActivity:(NSUserActivity *)userActivity + restorationHandler:(void (^)(NSArray *))restorationHandler { + if (@available(iOS 12, *)) { + if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) + { + NFCNDEFMessage *messagePayload = userActivity.ndefMessagePayload; + if (messagePayload.records.count > 0 && messagePayload.records[0].typeNameFormat != NFCTypeNameFormatEmpty) + { + NSLog(@"nfcDelegate - received NDEF message"); + if (Listener != nil) + { + [Listener messageReceived:messagePayload]; + } + else + { + LaunchMessage = messagePayload; + } + self.userActivity = userActivity; + return YES; + } + } + } + + return NO; +} + +@end @interface NfcPlugin() { NSString* sessionCallbackId; @@ -20,6 +64,7 @@ @interface NfcPlugin() { @property (nonatomic, assign) BOOL keepSessionOpen; @property (strong, nonatomic) NFCReaderSession *nfcSession API_AVAILABLE(ios(11.0)); @property (strong, nonatomic) NFCNDEFMessage *messageToWrite API_AVAILABLE(ios(11.0)); +@property BOOL hasListener; @end @implementation NfcPlugin @@ -47,6 +92,54 @@ - (void)channel:(CDVInvokedUrlCommand *)command { channelCallbackId = [command.callbackId copy]; } +- (void)onPause { + Listener = nil; +} + +- (void)onResume { + if (self.hasListener) { + Listener = self; + } +} + +- (void)parseLaunchIntent:(CDVInvokedUrlCommand *)command { + NSLog(@"parseLaunchIntent"); + + NFCNDEFMessage* ndefMessage = LaunchMessage; + LaunchMessage = nil; + + CDVPluginResult* pluginResult; + + if (ndefMessage == nil) + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"NO_INTENT"]; + } + else + { + NSDictionary* parsedMessage = [self buildTagDictionary:ndefMessage metaData:nil]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:parsedMessage]; + } + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)messageReceived:(NFCNDEFMessage *)ndefMessage { + NSLog(@"messageReceived"); + + NSMutableDictionary *nfcEvent = [NSMutableDictionary new]; + nfcEvent[@"type"] = @"ndef"; + nfcEvent[@"tag"] = [self buildTagDictionary:ndefMessage metaData:nil]; + + if (channelCallbackId) { + NSLog(@"Sending NFC data via channelCallbackId so an NDEF event fires)"); + + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:nfcEvent]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:channelCallbackId]; + } +} + + - (void)beginSession:(CDVInvokedUrlCommand*)command { NSLog(@"beginSession"); NSLog(@"WARNING: beginSession is deprecated. Use scanNdef or scanTag."); @@ -172,6 +265,8 @@ - (void)invalidateSession:(CDVInvokedUrlCommand*)command { // Nothing happens here, the event listener is registered in JavaScript - (void)registerNdef:(CDVInvokedUrlCommand *)command { NSLog(@"registerNdef"); + self.hasListener = TRUE; + Listener = self; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } @@ -179,6 +274,8 @@ - (void)registerNdef:(CDVInvokedUrlCommand *)command { // Nothing happens here, the event listener is removed in JavaScript - (void)removeNdef:(CDVInvokedUrlCommand *)command { NSLog(@"removeNdef"); + self.hasListener = FALSE; + Listener = nil; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } From 00f985722a6b052a181f4cc55617d62b099e36c8 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Fri, 26 Nov 2021 22:37:34 -0800 Subject: [PATCH 05/11] Use method swizzling to extend AppDelegate * To play nice with other plugins which may define the `continueUserActivity` method in AppDelegate --- src/ios/NfcPlugin.h | 5 ++--- src/ios/NfcPlugin.m | 17 +++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ios/NfcPlugin.h b/src/ios/NfcPlugin.h index 739df3fe..ce2f1dfa 100644 --- a/src/ios/NfcPlugin.h +++ b/src/ios/NfcPlugin.h @@ -47,9 +47,8 @@ @end -@interface AppDelegate (nfcDelegate) - - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler ; - @property (nonatomic, strong) IBOutlet NSUserActivity* userActivity; +@interface AppDelegate (PhonegapNfc) + - (BOOL)application:(UIApplication *)application swizzledContinueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler ; @end #endif diff --git a/src/ios/NfcPlugin.m b/src/ios/NfcPlugin.m index 4edb2612..33ade225 100644 --- a/src/ios/NfcPlugin.m +++ b/src/ios/NfcPlugin.m @@ -11,18 +11,16 @@ static NFCNDEFMessage * LaunchMessage = nil; static NfcPlugin* Listener = nil; -@implementation AppDelegate (nfcDelegate) +@implementation AppDelegate (PhonegapNfc) -- (NSUserActivity *)userActivity { - return objc_getAssociatedObject(self, UserActivityPropertyKey); -} - -- (void)setUserActivity:(NSUserActivity *)activity { - objc_setAssociatedObject(self, UserActivityPropertyKey, activity, OBJC_ASSOCIATION_RETAIN_NONATOMIC); ++ (void)load { + Method original = class_getInstanceMethod(self, @selector(application:continueUserActivity:restorationHandler:)); + Method swizzled = class_getInstanceMethod(self, @selector(application:swizzledContinueUserActivity:restorationHandler:)); + method_exchangeImplementations(original, swizzled); } - (BOOL)application:(UIApplication *)application -continueUserActivity:(NSUserActivity *)userActivity +swizzledContinueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler { if (@available(iOS 12, *)) { if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) @@ -39,13 +37,12 @@ - (BOOL)application:(UIApplication *)application { LaunchMessage = messagePayload; } - self.userActivity = userActivity; return YES; } } } - return NO; + return [self application:application swizzledContinueUserActivity:userActivity restorationHandler:restorationHandler]; } @end From 1a647c28725a22295b1e66408f594cdbbc996c50 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Thu, 16 Mar 2023 07:56:14 -0700 Subject: [PATCH 06/11] Chore: Merging fixes from other forks --- .../src/com/chariotsolutions/nfc/plugin/NfcPlugin.java | 9 ++++++++- src/ios/NfcPlugin.m | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index 2a631d6e..61d97d7a 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -31,6 +31,7 @@ import android.nfc.tech.Ndef; import android.nfc.tech.NdefFormatable; import android.nfc.tech.TagTechnology; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; @@ -531,7 +532,13 @@ private void createPendingIntent() { Activity activity = getActivity(); Intent intent = new Intent(activity, activity.getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pendingIntent = PendingIntent.getActivity(activity, 0, intent, 0); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + pendingIntent = PendingIntent.getActivity(activity, 0, intent, PendingIntent.FLAG_MUTABLE); + } else { + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + pendingIntent = PendingIntent.getActivity(activity, 0, intent, 0); + } } } diff --git a/src/ios/NfcPlugin.m b/src/ios/NfcPlugin.m index 33ade225..07e31b2d 100644 --- a/src/ios/NfcPlugin.m +++ b/src/ios/NfcPlugin.m @@ -216,7 +216,7 @@ - (void)writeTag:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(13.0)){ NSLog(@"Using NFCTagReaderSession"); self.nfcSession = [[NFCTagReaderSession new] - initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693) + initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) delegate:self queue:dispatch_get_main_queue()]; } else { @@ -401,7 +401,7 @@ - (void)startScanSession:(CDVInvokedUrlCommand*)command { if (self.shouldUseTagReaderSession) { NSLog(@"Using NFCTagReaderSession"); self.nfcSession = [[NFCTagReaderSession new] - initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693) + initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) delegate:self queue:dispatch_get_main_queue()]; } else { NSLog(@"Using NFCNDEFReaderSession"); From 85ddc8823b17acb76cc552c31364e231267ff4dd Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Thu, 13 Apr 2023 18:40:11 -0700 Subject: [PATCH 07/11] Fix: 'new' is not available error in XCode 14.3 --- src/ios/NfcPlugin.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ios/NfcPlugin.m b/src/ios/NfcPlugin.m index 07e31b2d..2e07f83a 100644 --- a/src/ios/NfcPlugin.m +++ b/src/ios/NfcPlugin.m @@ -215,13 +215,13 @@ - (void)writeTag:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(13.0)){ if (self.shouldUseTagReaderSession) { NSLog(@"Using NFCTagReaderSession"); - self.nfcSession = [[NFCTagReaderSession new] + self.nfcSession = [[NFCTagReaderSession alloc] initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) delegate:self queue:dispatch_get_main_queue()]; } else { NSLog(@"Using NFCTagReaderSession"); - self.nfcSession = [[NFCNDEFReaderSession new]initWithDelegate:self queue:nil invalidateAfterFirstRead:FALSE]; + self.nfcSession = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:FALSE]; } } @@ -400,12 +400,12 @@ - (void)startScanSession:(CDVInvokedUrlCommand*)command { if (self.shouldUseTagReaderSession) { NSLog(@"Using NFCTagReaderSession"); - self.nfcSession = [[NFCTagReaderSession new] + self.nfcSession = [[NFCTagReaderSession alloc] initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) delegate:self queue:dispatch_get_main_queue()]; } else { NSLog(@"Using NFCNDEFReaderSession"); - self.nfcSession = [[NFCNDEFReaderSession new]initWithDelegate:self queue:nil invalidateAfterFirstRead:TRUE]; + self.nfcSession = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:TRUE]; } sessionCallbackId = [command.callbackId copy]; self.nfcSession.alertMessage = @"Hold near NFC tag to scan."; @@ -413,7 +413,7 @@ - (void)startScanSession:(CDVInvokedUrlCommand*)command { } else if (@available(iOS 11.0, *)) { NSLog(@"iOS < 13, using NFCNDEFReaderSession"); - self.nfcSession = [[NFCNDEFReaderSession new]initWithDelegate:self queue:nil invalidateAfterFirstRead:TRUE]; + self.nfcSession = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:TRUE]; sessionCallbackId = [command.callbackId copy]; self.nfcSession.alertMessage = @"Hold near NFC tag to scan."; [self.nfcSession beginSession]; From 22999679a87de34be51a99f9da40fdf1fb611dfb Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Thu, 3 Aug 2023 16:26:36 -0700 Subject: [PATCH 08/11] iOS fixes - iOS 14 and above no longer require NDEF entitlement - Tags ISO18092 are not supported without custom entitlements --- plugin.xml | 2 -- src/ios/NfcPlugin.m | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugin.xml b/plugin.xml index a2d42aa2..10584380 100644 --- a/plugin.xml +++ b/plugin.xml @@ -122,13 +122,11 @@ - NDEF TAG - NDEF TAG diff --git a/src/ios/NfcPlugin.m b/src/ios/NfcPlugin.m index 2e07f83a..80df838f 100644 --- a/src/ios/NfcPlugin.m +++ b/src/ios/NfcPlugin.m @@ -216,7 +216,7 @@ - (void)writeTag:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(13.0)){ NSLog(@"Using NFCTagReaderSession"); self.nfcSession = [[NFCTagReaderSession alloc] - initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) + initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693) delegate:self queue:dispatch_get_main_queue()]; } else { @@ -401,7 +401,7 @@ - (void)startScanSession:(CDVInvokedUrlCommand*)command { if (self.shouldUseTagReaderSession) { NSLog(@"Using NFCTagReaderSession"); self.nfcSession = [[NFCTagReaderSession alloc] - initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693 | NFCPollingISO18092) + initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693) delegate:self queue:dispatch_get_main_queue()]; } else { NSLog(@"Using NFCNDEFReaderSession"); From 620792eadd15e0d7cec82247c413b84a5aa0d20a Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Tue, 30 Jan 2024 06:56:21 -0800 Subject: [PATCH 09/11] Fix: Return URL from intent when tag is unavailable --- .../nfc/plugin/NfcPlugin.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index 61d97d7a..6ed267ad 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -324,33 +324,49 @@ private void parseLaunchIntent(final CallbackContext callbackContext) { Log.d(TAG, "parseLaunchIntent " + intent); String action = intent.getAction(); Log.d(TAG, "action " + action); + final String data = intent.getDataString(); + Log.d(TAG, "data " + data); - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES)); + try { + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES)); - if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) { - Ndef ndef = Ndef.get(tag); - callbackContext.success(buildNdefJSON(ndef, messages)); - return; - } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { - Ndef ndef = null; - for (String tagTech : tag.getTechList()) { - Log.d(TAG, tagTech); - if (tagTech.equals(Ndef.class.getName())) { // - ndef = Ndef.get(tag); + if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) { + Ndef ndef = Ndef.get(tag); + callbackContext.success(buildNdefJSON(ndef, messages)); + } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { + Ndef ndef = null; + for (String tagTech : tag.getTechList()) { + Log.d(TAG, tagTech); + if (tagTech.equals(Ndef.class.getName())) { // + ndef = Ndef.get(tag); + } + } + if (ndef != null) { + callbackContext.success(buildNdefJSON(ndef, messages)); + } else { + callbackContext.success(Util.tagToJSON(tag)); + } + } else if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { + callbackContext.success(Util.tagToJSON(tag)); + } else { + if (data != null) { + callbackContext.success(data); + } else { + callbackContext.error("NO_INTENT"); } } - if (ndef != null) { - callbackContext.success(buildNdefJSON(ndef, messages)); + } catch (Exception exception) { + Log.e(TAG, "failed to parse tag from launch intent", exception); + if (data != null) { + // Fallback to returning the URL obtained from the intent + callbackContext.success(data); } else { - callbackContext.success(Util.tagToJSON(tag)); + callbackContext.error("NO_INTENT"); } - } else if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { - callbackContext.success(Util.tagToJSON(tag)); - } else { - callbackContext.error("NO_INTENT"); } } else { + Log.d(TAG, "parseLaunchIntent: NO_INTENT"); callbackContext.error("NO_INTENT"); } } From fb106a0a7a43d3ffdef1f25e1c574eec7d501d94 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Thu, 8 Feb 2024 05:40:59 -0800 Subject: [PATCH 10/11] Fix: Android SDK 34 removes support for NFC push --- README.md | 2 - .../nfc/plugin/NfcPlugin.java | 148 +----------------- 2 files changed, 1 insertion(+), 149 deletions(-) diff --git a/README.md b/README.md index 1e522466..1ac3792f 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,6 @@ Function `nfc.share` writes an NdefMessage via peer-to-peer. This should appear ### Supported Platforms -- Android - Windows - BlackBerry 7 - BlackBerry 10 @@ -481,7 +480,6 @@ Function `nfc.unshare` stops sharing data via peer-to-peer. ### Supported Platforms -- Android - Windows - BlackBerry 7 - BlackBerry 10 diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index 6ed267ad..7abe95bc 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -36,7 +36,7 @@ import android.os.Parcelable; import android.util.Log; -public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCompleteCallback { +public class NfcPlugin extends CordovaPlugin { private static final String REGISTER_MIME_TYPE = "registerMimeType"; private static final String REMOVE_MIME_TYPE = "removeMimeType"; private static final String REGISTER_NDEF = "registerNdef"; @@ -47,10 +47,6 @@ public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCom private static final String WRITE_TAG = "writeTag"; private static final String MAKE_READ_ONLY = "makeReadOnly"; private static final String ERASE_TAG = "eraseTag"; - private static final String SHARE_TAG = "shareTag"; - private static final String UNSHARE_TAG = "unshareTag"; - private static final String HANDOVER = "handover"; // Android Beam - private static final String STOP_HANDOVER = "stopHandover"; private static final String ENABLED = "enabled"; private static final String INIT = "init"; private static final String SHOW_SETTINGS = "showSettings"; @@ -82,15 +78,12 @@ public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCom private final List intentFilters = new ArrayList<>(); private final ArrayList techLists = new ArrayList<>(); - private NdefMessage p2pMessage = null; private PendingIntent pendingIntent = null; private Intent savedIntent = null; private CallbackContext readerModeCallback; private CallbackContext channelCallback; - private CallbackContext shareTagCallback; - private CallbackContext handoverCallback; @Override public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { @@ -157,18 +150,6 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo } else if (action.equalsIgnoreCase(ERASE_TAG)) { eraseTag(callbackContext); - } else if (action.equalsIgnoreCase(SHARE_TAG)) { - shareTag(data, callbackContext); - - } else if (action.equalsIgnoreCase(UNSHARE_TAG)) { - unshareTag(callbackContext); - - } else if (action.equalsIgnoreCase(HANDOVER)) { - handover(data, callbackContext); - - } else if (action.equalsIgnoreCase(STOP_HANDOVER)) { - stopHandover(callbackContext); - } else if (action.equalsIgnoreCase(INIT)) { init(callbackContext); @@ -301,13 +282,6 @@ private void removeNdef(CallbackContext callbackContext) { callbackContext.success(); } - private void unshareTag(CallbackContext callbackContext) { - p2pMessage = null; - stopNdefPush(); - shareTagCallback = null; - callbackContext.success(); - } - private void init(CallbackContext callbackContext) { Log.d(TAG, "Enabling plugin " + getIntent()); @@ -503,35 +477,6 @@ private void makeReadOnly(final CallbackContext callbackContext) { }); } - private void shareTag(JSONArray data, CallbackContext callbackContext) throws JSONException { - NdefRecord[] records = Util.jsonToNdefRecords(data.getString(0)); - this.p2pMessage = new NdefMessage(records); - - startNdefPush(callbackContext); - } - - // setBeamPushUris - // Every Uri you provide must have either scheme 'file' or scheme 'content'. - // Note that this takes priority over setNdefPush - // - // See http://developer.android.com/reference/android/nfc/NfcAdapter.html#setBeamPushUris(android.net.Uri[],%20android.app.Activity) - private void handover(JSONArray data, CallbackContext callbackContext) throws JSONException { - - Uri[] uri = new Uri[data.length()]; - - for (int i = 0; i < data.length(); i++) { - uri[i] = Uri.parse(data.getString(i)); - } - - startNdefBeam(callbackContext, uri); - } - - private void stopHandover(CallbackContext callbackContext) { - stopNdefBeam(); - handoverCallback = null; - callbackContext.success(); - } - private void showSettings(CallbackContext callbackContext) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { Intent intent = new Intent(android.provider.Settings.ACTION_NFC_SETTINGS); @@ -616,10 +561,6 @@ private void startNfc() { if (intentFilters.length > 0 || techLists.length > 0) { nfcAdapter.enableForegroundDispatch(getActivity(), getPendingIntent(), intentFilters, techLists); } - - if (p2pMessage != null) { - nfcAdapter.setNdefPushMessage(p2pMessage, getActivity()); - } } catch (IllegalStateException e) { // issue 110 - user exits app with home button while nfc is initializing Log.w(TAG, "Illegal State Exception starting NFC. Assuming application is terminating."); @@ -646,77 +587,6 @@ private void stopNfc() { }); } - private void startNdefBeam(final CallbackContext callbackContext, final Uri[] uris) { - getActivity().runOnUiThread(() -> { - - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - - if (nfcAdapter == null) { - callbackContext.error(STATUS_NO_NFC); - } else if (!nfcAdapter.isNdefPushEnabled()) { - callbackContext.error(STATUS_NDEF_PUSH_DISABLED); - } else { - nfcAdapter.setOnNdefPushCompleteCallback(NfcPlugin.this, getActivity()); - try { - nfcAdapter.setBeamPushUris(uris, getActivity()); - - PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); - result.setKeepCallback(true); - handoverCallback = callbackContext; - callbackContext.sendPluginResult(result); - - } catch (IllegalArgumentException e) { - callbackContext.error(e.getMessage()); - } - } - }); - } - - private void startNdefPush(final CallbackContext callbackContext) { - getActivity().runOnUiThread(() -> { - - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - - if (nfcAdapter == null) { - callbackContext.error(STATUS_NO_NFC); - } else if (!nfcAdapter.isNdefPushEnabled()) { - callbackContext.error(STATUS_NDEF_PUSH_DISABLED); - } else { - nfcAdapter.setNdefPushMessage(p2pMessage, getActivity()); - nfcAdapter.setOnNdefPushCompleteCallback(NfcPlugin.this, getActivity()); - - PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); - result.setKeepCallback(true); - shareTagCallback = callbackContext; - callbackContext.sendPluginResult(result); - } - }); - } - - private void stopNdefPush() { - getActivity().runOnUiThread(() -> { - - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - - if (nfcAdapter != null) { - nfcAdapter.setNdefPushMessage(null, getActivity()); - } - - }); - } - - private void stopNdefBeam() { - getActivity().runOnUiThread(() -> { - - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - - if (nfcAdapter != null) { - nfcAdapter.setBeamPushUris(null, getActivity()); - } - - }); - } - private void addToTechList(String[] techs) { techLists.add(techs); } @@ -906,22 +776,6 @@ private void setIntent(Intent intent) { getActivity().setIntent(intent); } - @Override - public void onNdefPushComplete(NfcEvent event) { - - // handover (beam) take precedence over share tag (ndef push) - if (handoverCallback != null) { - PluginResult result = new PluginResult(PluginResult.Status.OK, "Beamed Message to Peer"); - result.setKeepCallback(true); - handoverCallback.sendPluginResult(result); - } else if (shareTagCallback != null) { - PluginResult result = new PluginResult(PluginResult.Status.OK, "Shared Message with Peer"); - result.setKeepCallback(true); - shareTagCallback.sendPluginResult(result); - } - - } - /** * Enable I/O operations to the tag from this TagTechnology object. * * From 1e741fbd2dd9461d7b9a0e73063980adf79a9699 Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Sun, 18 Feb 2024 13:10:45 -0800 Subject: [PATCH 11/11] Adding back NDEF as supported NFC format - Required by Apple store to submit the app with target of iOS 12 --- plugin.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin.xml b/plugin.xml index 10584380..d89d1af4 100644 --- a/plugin.xml +++ b/plugin.xml @@ -122,12 +122,14 @@ - TAG + NDEF + TAG - TAG + NDEF + TAG