Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
* master:
  Adding back NDEF as supported NFC format
  Fix: Android SDK 34 removes support for NFC push
  Fix: Return URL from intent when tag is unavailable
  iOS fixes
  Fix: 'new' is not available error in XCode 14.3
  Chore: Merging fixes from other forks
  • Loading branch information
David Wettstein authored and dwettstein committed Jul 29, 2024
2 parents 5e14da1 + 1e741fb commit ad4192d
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 157 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -481,7 +480,6 @@ Function `nfc.unshare` stops sharing data via peer-to-peer.

### Supported Platforms

- Android
- Windows
- BlackBerry 7
- BlackBerry 10
Expand Down
4 changes: 2 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@
<config-file parent="com.apple.developer.nfc.readersession.formats" platform="ios" target="*-Debug.plist">
<array>
<string>NDEF</string>
<string>TAG</string>
<string>TAG</string>
</array>
</config-file>
<config-file parent="com.apple.developer.nfc.readersession.formats" platform="ios" target="*-Release.plist">
<array>
<string>NDEF</string>
<string>TAG</string>
<string>TAG</string>
</array>
</config-file>

Expand Down
210 changes: 62 additions & 148 deletions src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
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.Handler;
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";
Expand All @@ -48,10 +49,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";
Expand Down Expand Up @@ -82,15 +79,12 @@ public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCom
private final List<IntentFilter> intentFilters = new ArrayList<>();
private final ArrayList<String[]> 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;

private PostponedPluginResult postponedPluginResult = null;

Expand Down Expand Up @@ -190,18 +184,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);
} else if (action.equalsIgnoreCase(ENABLED)) {
Expand Down Expand Up @@ -323,13 +305,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());

Expand All @@ -340,6 +315,59 @@ 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);
final String data = intent.getDataString();
Log.d(TAG, "data " + data);

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));
} 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");
}
}
} 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.error("NO_INTENT");
}
}
} else {
Log.d(TAG, "parseLaunchIntent: NO_INTENT");
callbackContext.error("NO_INTENT");
}
}

private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException {
String mimeType = data.getString(0);
removeIntentFilter(mimeType);
Expand Down Expand Up @@ -472,35 +500,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);
Expand All @@ -517,7 +516,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, PendingIntent.FLAG_IMMUTABLE);
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); // PendingIntent.FLAG_IMMUTABLE instead of 0?
}
}
}

Expand Down Expand Up @@ -579,10 +584,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.");
Expand All @@ -609,77 +610,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);
}
Expand Down Expand Up @@ -874,22 +804,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.
* *
Expand Down
10 changes: 5 additions & 5 deletions src/ios/NfcPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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];
}
}

Expand Down Expand Up @@ -400,20 +400,20 @@ - (void)startScanSession:(CDVInvokedUrlCommand*)command {

if (self.shouldUseTagReaderSession) {
NSLog(@"Using NFCTagReaderSession");
self.nfcSession = [[NFCTagReaderSession new]
self.nfcSession = [[NFCTagReaderSession alloc]
initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693)
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.";
[self.nfcSession beginSession];

} 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];
Expand Down

0 comments on commit ad4192d

Please sign in to comment.