From 63f903b999f708a49bf718b978869b7873dee911 Mon Sep 17 00:00:00 2001 From: Sachin Agarwal Date: Wed, 23 Feb 2022 13:46:36 +0530 Subject: [PATCH 1/7] Fix Android 12 - FLAG_IMMUTABLE or FLAG_MUTABLE The plugin works perfectly in all devices except for Andorid 12. I get following error ``` Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent ``` I see the problem is in `createPendingIntent` Method ``` private void createPendingIntent() { if (pendingIntent == null) { 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); // Problem in the last param here } } ``` This PR solves the problem by just adding the requried flag which also works for lower android versions --- src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index e8256d83..198a7bc5 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -483,7 +483,7 @@ 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); + pendingIntent = PendingIntent.getActivity(activity, 0, intent, PendingIntent.FLAG_IMMUTABLE); } } From fc46a1fad26adc674433f4a2a3ebc1f68ed40c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 25 May 2022 00:36:22 +0200 Subject: [PATCH 2/7] fix(launch): use another bundled solution see https://github.com/chariotsolutions/phonegap-nfc/issues/457#issuecom ment-988285150 --- .../nfc/plugin/NfcPlugin.java | 1797 ++++++++--------- .../com/chariotsolutions/nfc/plugin/Util.java | 7 +- 2 files changed, 900 insertions(+), 904 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index e329e152..d219cc06 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -5,6 +5,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.Iterator; import java.util.List; @@ -32,1033 +33,1023 @@ import android.nfc.tech.NdefFormatable; import android.nfc.tech.TagTechnology; import android.os.Bundle; +import android.os.Handler; import android.os.Parcelable; import android.util.Log; public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCompleteCallback { - private static final String REGISTER_MIME_TYPE = "registerMimeType"; - private static final String REMOVE_MIME_TYPE = "removeMimeType"; - private static final String REGISTER_NDEF = "registerNdef"; - private static final String REMOVE_NDEF = "removeNdef"; - private static final String REGISTER_NDEF_FORMATABLE = "registerNdefFormatable"; - private static final String REGISTER_DEFAULT_TAG = "registerTag"; - private static final String REMOVE_DEFAULT_TAG = "removeTag"; - 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"; - private static final String PARSE_LAUNCH_INTENT = "parseLaunchIntent"; - - private static final String NDEF = "ndef"; - private static final String NDEF_MIME = "ndef-mime"; - private static final String NDEF_FORMATABLE = "ndef-formatable"; - private static final String TAG_DEFAULT = "tag"; - - private static final String READER_MODE = "readerMode"; - private static final String DISABLE_READER_MODE = "disableReaderMode"; - - // TagTechnology IsoDep, NfcA, NfcB, NfcV, NfcF, MifareClassic, MifareUltralight - private static final String CONNECT = "connect"; - private static final String CLOSE = "close"; - private static final String TRANSCEIVE = "transceive"; - private TagTechnology tagTechnology = null; - private Class tagTechnologyClass; - - private static final String CHANNEL = "channel"; - - private static final String STATUS_NFC_OK = "NFC_OK"; - private static final String STATUS_NO_NFC = "NO_NFC"; - private static final String STATUS_NFC_DISABLED = "NFC_DISABLED"; - private static final String STATUS_NDEF_PUSH_DISABLED = "NDEF_PUSH_DISABLED"; - - private static final String TAG = "NfcPlugin"; - 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 { + private static final String REGISTER_MIME_TYPE = "registerMimeType"; + private static final String REMOVE_MIME_TYPE = "removeMimeType"; + private static final String REGISTER_NDEF = "registerNdef"; + private static final String REMOVE_NDEF = "removeNdef"; + private static final String REGISTER_NDEF_FORMATABLE = "registerNdefFormatable"; + private static final String REGISTER_DEFAULT_TAG = "registerTag"; + private static final String REMOVE_DEFAULT_TAG = "removeTag"; + 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"; + + private static final String NDEF = "ndef"; + private static final String NDEF_MIME = "ndef-mime"; + private static final String NDEF_FORMATABLE = "ndef-formatable"; + private static final String TAG_DEFAULT = "tag"; + + private static final String READER_MODE = "readerMode"; + private static final String DISABLE_READER_MODE = "disableReaderMode"; + + // TagTechnology IsoDep, NfcA, NfcB, NfcV, NfcF, MifareClassic, MifareUltralight + private static final String CONNECT = "connect"; + private static final String CLOSE = "close"; + private static final String TRANSCEIVE = "transceive"; + private TagTechnology tagTechnology = null; + private Class tagTechnologyClass; + + private static final String CHANNEL = "channel"; + + private static final String STATUS_NFC_OK = "NFC_OK"; + private static final String STATUS_NO_NFC = "NO_NFC"; + private static final String STATUS_NFC_DISABLED = "NFC_DISABLED"; + private static final String STATUS_NDEF_PUSH_DISABLED = "NDEF_PUSH_DISABLED"; + + private static final String TAG = "NfcPlugin"; + 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; + + private PostponedPluginResult postponedPluginResult = null; + + class PostponedPluginResult { + private Date moment; + private PluginResult pluginResult; + + PostponedPluginResult(Date moment, PluginResult pluginResult) { + this.moment = moment; + this.pluginResult = pluginResult; + } - Log.d(TAG, "execute " + action); + boolean isValid() { + return this.moment.after(new Date(new Date().getTime() - 30000)); + } + } - // showSettings can be called if NFC is disabled - // might want to skip this if NO_NFC - if (action.equalsIgnoreCase(SHOW_SETTINGS)) { - showSettings(callbackContext); - return true; - } + @Override + public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { - // the channel is set up when the plugin starts - if (action.equalsIgnoreCase(CHANNEL)) { - channelCallback = callbackContext; - return true; // short circuit - } + Log.d(TAG, "execute " + action); +// Log.d(TAG, "execute postponedPluginResult.toString() " + postponedPluginResult.toString()); - // allow reader mode to be disabled even if nfc is disabled - if (action.equalsIgnoreCase(DISABLE_READER_MODE)) { - disableReaderMode(callbackContext); - return true; // short circuit - } + // showSettings can be called if NFC is disabled + // might want to skip this if NO_NFC + if (action.equalsIgnoreCase(SHOW_SETTINGS)) { + showSettings(callbackContext); + return true; + } - if (!getNfcStatus().equals(STATUS_NFC_OK)) { - callbackContext.error(getNfcStatus()); - return true; // short circuit - } + // the channel is set up when the plugin starts +// if (action.equalsIgnoreCase(CHANNEL)) { + if (action.equalsIgnoreCase(READER_MODE)) { + readerModeCallback = callbackContext; - createPendingIntent(); + if (postponedPluginResult != null) { + Log.i(TAG, "Postponed plugin result available"); - if (action.equalsIgnoreCase(READER_MODE)) { - int flags = data.getInt(0); - readerMode(flags, callbackContext); + if (postponedPluginResult.isValid()) { + Log.i(TAG, "Postponed plugin result is valid, resending it now"); - } else if (action.equalsIgnoreCase(REGISTER_MIME_TYPE)) { - registerMimeType(data, callbackContext); + readerModeCallback.sendPluginResult(postponedPluginResult.pluginResult); + } else { + Log.i(TAG, "Postponed plugin result not valid anymore, so ignoring it"); + } - } else if (action.equalsIgnoreCase(REMOVE_MIME_TYPE)) { - removeMimeType(data, callbackContext); + postponedPluginResult = null; + } - } else if (action.equalsIgnoreCase(REGISTER_NDEF)) { - registerNdef(callbackContext); + return true; // short circuit + } - } else if (action.equalsIgnoreCase(REMOVE_NDEF)) { - removeNdef(callbackContext); + // allow reader mode to be disabled even if nfc is disabled + if (action.equalsIgnoreCase(DISABLE_READER_MODE)) { + disableReaderMode(callbackContext); + return true; // short circuit + } - } else if (action.equalsIgnoreCase(REGISTER_NDEF_FORMATABLE)) { - registerNdefFormatable(callbackContext); + if (!getNfcStatus().equals(STATUS_NFC_OK)) { + callbackContext.error(getNfcStatus()); + return true; // short circuit + } - } else if (action.equals(REGISTER_DEFAULT_TAG)) { - registerDefaultTag(callbackContext); + createPendingIntent(); - } else if (action.equals(REMOVE_DEFAULT_TAG)) { - removeDefaultTag(callbackContext); + if (action.equalsIgnoreCase(READER_MODE)) { + int flags = data.getInt(0); + readerMode(flags, callbackContext); - } else if (action.equalsIgnoreCase(WRITE_TAG)) { - writeTag(data, callbackContext); + } else if (action.equalsIgnoreCase(REGISTER_MIME_TYPE)) { + registerMimeType(data, callbackContext); - } else if (action.equalsIgnoreCase(MAKE_READ_ONLY)) { - makeReadOnly(callbackContext); + } else if (action.equalsIgnoreCase(REMOVE_MIME_TYPE)) { + removeMimeType(data, callbackContext); - } else if (action.equalsIgnoreCase(ERASE_TAG)) { - eraseTag(callbackContext); + } else if (action.equalsIgnoreCase(REGISTER_NDEF)) { + registerNdef(callbackContext); - } else if (action.equalsIgnoreCase(SHARE_TAG)) { - shareTag(data, callbackContext); + } else if (action.equalsIgnoreCase(REMOVE_NDEF)) { + removeNdef(callbackContext); - } else if (action.equalsIgnoreCase(UNSHARE_TAG)) { - unshareTag(callbackContext); + } else if (action.equalsIgnoreCase(REGISTER_NDEF_FORMATABLE)) { + registerNdefFormatable(callbackContext); - } else if (action.equalsIgnoreCase(HANDOVER)) { - handover(data, callbackContext); + } else if (action.equals(REGISTER_DEFAULT_TAG)) { + registerDefaultTag(callbackContext); - } else if (action.equalsIgnoreCase(STOP_HANDOVER)) { - stopHandover(callbackContext); + } else if (action.equals(REMOVE_DEFAULT_TAG)) { + removeDefaultTag(callbackContext); - } else if (action.equalsIgnoreCase(INIT)) { - init(callbackContext); + } else if (action.equalsIgnoreCase(WRITE_TAG)) { + writeTag(data, callbackContext); - } else if (action.equalsIgnoreCase(PARSE_LAUNCH_INTENT)) { - parseLaunchIntent(callbackContext); + } else if (action.equalsIgnoreCase(MAKE_READ_ONLY)) { + makeReadOnly(callbackContext); - } else if (action.equalsIgnoreCase(ENABLED)) { - // status is checked before every call - // if code made it here, NFC is enabled - callbackContext.success(STATUS_NFC_OK); + } else if (action.equalsIgnoreCase(ERASE_TAG)) { + eraseTag(callbackContext); - } else if (action.equalsIgnoreCase(CONNECT)) { - String tech = data.getString(0); - int timeout = data.optInt(1, -1); - connect(tech, timeout, callbackContext); + } else if (action.equalsIgnoreCase(SHARE_TAG)) { + shareTag(data, callbackContext); - } else if (action.equalsIgnoreCase(TRANSCEIVE)) { - CordovaArgs args = new CordovaArgs(data); // execute is using the old signature with JSON data + } else if (action.equalsIgnoreCase(UNSHARE_TAG)) { + unshareTag(callbackContext); - byte[] command = args.getArrayBuffer(0); - transceive(command, callbackContext); + } else if (action.equalsIgnoreCase(HANDOVER)) { + handover(data, callbackContext); - } else if (action.equalsIgnoreCase(CLOSE)) { - close(callbackContext); + } else if (action.equalsIgnoreCase(STOP_HANDOVER)) { + stopHandover(callbackContext); - } else { - // invalid action - return false; - } + } else if (action.equalsIgnoreCase(INIT)) { + init(callbackContext); + } else if (action.equalsIgnoreCase(ENABLED)) { + // status is checked before every call + // if code made it here, NFC is enabled + callbackContext.success(STATUS_NFC_OK); - return true; - } + } else if (action.equalsIgnoreCase(CONNECT)) { + String tech = data.getString(0); + int timeout = data.optInt(1, -1); + connect(tech, timeout, callbackContext); - @Override - protected void pluginInitialize() { - super.pluginInitialize(); - NfcActivity.onPluginInitialize(); - } + } else if (action.equalsIgnoreCase(TRANSCEIVE)) { + CordovaArgs args = new CordovaArgs(data); // execute is using the old signature with JSON data - private String getNfcStatus() { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - if (nfcAdapter == null) { - return STATUS_NO_NFC; - } else if (!nfcAdapter.isEnabled()) { - return STATUS_NFC_DISABLED; - } else { - return STATUS_NFC_OK; - } - } + byte[] command = args.getArrayBuffer(0); + transceive(command, callbackContext); - private void readerMode(int flags, CallbackContext callbackContext) { - Bundle extras = new Bundle(); // not used - readerModeCallback = callbackContext; - getActivity().runOnUiThread(() -> { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - nfcAdapter.enableReaderMode(getActivity(), callback, flags, extras); - }); + } else if (action.equalsIgnoreCase(CLOSE)) { + close(callbackContext); + } else { + // invalid action + return false; } - private void disableReaderMode(CallbackContext callbackContext) { - getActivity().runOnUiThread(() -> { - readerModeCallback = null; - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - if (nfcAdapter != null) { - nfcAdapter.disableReaderMode(getActivity()); - } - callbackContext.success(); - }); + return true; + } + + private String getNfcStatus() { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + if (nfcAdapter == null) { + return STATUS_NO_NFC; + } else if (!nfcAdapter.isEnabled()) { + return STATUS_NFC_DISABLED; + } else { + return STATUS_NFC_OK; } + } + + private void readerMode(int flags, CallbackContext callbackContext) { + Bundle extras = new Bundle(); // not used + readerModeCallback = callbackContext; + getActivity().runOnUiThread(() -> { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + nfcAdapter.enableReaderMode(getActivity(), callback, flags, extras); + }); + + } + + private void disableReaderMode(CallbackContext callbackContext) { + getActivity().runOnUiThread(() -> { + readerModeCallback = null; + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + if (nfcAdapter != null) { + nfcAdapter.disableReaderMode(getActivity()); + } + callbackContext.success(); + }); + } + + private NfcAdapter.ReaderCallback callback = new NfcAdapter.ReaderCallback() { + @Override + public void onTagDiscovered(Tag tag) { + + JSONObject json; + + // If the tag supports Ndef, try and return an Ndef message + List techList = Arrays.asList(tag.getTechList()); + if (techList.contains(Ndef.class.getName())) { + Ndef ndef = Ndef.get(tag); + json = Util.ndefToJSON(ndef); + } else { + json = Util.tagToJSON(tag, null); + } + + Intent tagIntent = new Intent(); + tagIntent.putExtra(NfcAdapter.EXTRA_TAG, tag); + setIntent(tagIntent); + + PluginResult result = new PluginResult(PluginResult.Status.OK, json); + result.setKeepCallback(true); + if (readerModeCallback != null) { + readerModeCallback.sendPluginResult(result); + } else { + Log.i(TAG, "readerModeCallback is null - reader mode probably disabled in the meantime"); + } - private NfcAdapter.ReaderCallback callback = new NfcAdapter.ReaderCallback() { - @Override - public void onTagDiscovered(Tag tag) { - - JSONObject json; - - // If the tag supports Ndef, try and return an Ndef message - List techList = Arrays.asList(tag.getTechList()); - if (techList.contains(Ndef.class.getName())) { - Ndef ndef = Ndef.get(tag); - json = Util.ndefToJSON(ndef); - } else { - json = Util.tagToJSON(tag); - } - - Intent tagIntent = new Intent(); - tagIntent.putExtra(NfcAdapter.EXTRA_TAG, tag); - savedIntent = tagIntent; - setIntent(tagIntent); - - PluginResult result = new PluginResult(PluginResult.Status.OK, json); - result.setKeepCallback(true); - if (readerModeCallback != null) { - readerModeCallback.sendPluginResult(result); - } else { - Log.i(TAG, "readerModeCallback is null - reader mode probably disabled in the meantime"); - } - - } - }; - - private void registerDefaultTag(CallbackContext callbackContext) { - addTagFilter(); - restartNfc(); - callbackContext.success(); } - - private void removeDefaultTag(CallbackContext callbackContext) { - removeTagFilter(); - restartNfc(); - callbackContext.success(); + }; + + private void registerDefaultTag(CallbackContext callbackContext) { + addTagFilter(); + restartNfc(); + callbackContext.success(); + } + + private void removeDefaultTag(CallbackContext callbackContext) { + removeTagFilter(); + restartNfc(); + callbackContext.success(); + } + + private void registerNdefFormatable(CallbackContext callbackContext) { + addTechList(new String[]{NdefFormatable.class.getName()}); + restartNfc(); + callbackContext.success(); + } + + private void registerNdef(CallbackContext callbackContext) { + addTechList(new String[]{Ndef.class.getName()}); + restartNfc(); + callbackContext.success(); + } + + private void removeNdef(CallbackContext callbackContext) { + removeTechList(new String[]{Ndef.class.getName()}); + restartNfc(); + 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()); + + startNfc(); + if (!recycledIntent()) { + parseMessage(); } - - private void registerNdefFormatable(CallbackContext callbackContext) { - addTechList(new String[]{NdefFormatable.class.getName()}); - restartNfc(); - callbackContext.success(); + callbackContext.success(); + } + + private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { + String mimeType = data.getString(0); + removeIntentFilter(mimeType); + restartNfc(); + callbackContext.success(); + } + + private void registerMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { + String mimeType = ""; + try { + mimeType = data.getString(0); + intentFilters.add(createIntentFilter(mimeType)); + restartNfc(); + callbackContext.success(); + } catch (MalformedMimeTypeException e) { + callbackContext.error("Invalid MIME Type " + mimeType); } + } - private void registerNdef(CallbackContext callbackContext) { - addTechList(new String[]{Ndef.class.getName()}); - restartNfc(); - callbackContext.success(); - } - - private void removeNdef(CallbackContext callbackContext) { - removeTechList(new String[]{Ndef.class.getName()}); - restartNfc(); - 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()); + // Cheating and writing an empty record. We may actually be able to erase some tag types. + private void eraseTag(CallbackContext callbackContext) { + Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + NdefRecord[] records = { + new NdefRecord(NdefRecord.TNF_EMPTY, new byte[0], new byte[0], new byte[0]) + }; + writeNdefMessage(new NdefMessage(records), tag, callbackContext); + } - startNfc(); - if (!recycledIntent()) { - parseMessage(); - } - callbackContext.success(); + private void writeTag(JSONArray data, CallbackContext callbackContext) throws JSONException { + if (getIntent() == null) { // TODO remove this and handle LostTag + callbackContext.error("Failed to write tag, received null intent"); } - 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)); + Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + NdefRecord[] records = Util.jsonToNdefRecords(data.getString(0)); + writeNdefMessage(new NdefMessage(records), tag, callbackContext); + } + + private void writeNdefMessage(final NdefMessage message, final Tag tag, final CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + Ndef ndef = Ndef.get(tag); + if (ndef != null) { + ndef.connect(); + + if (ndef.isWritable()) { + int size = message.toByteArray().length; + if (ndef.getMaxSize() < size) { + callbackContext.error("Tag capacity is " + ndef.getMaxSize() + + " bytes, message is " + size + " bytes."); } else { - callbackContext.error("NO_INTENT"); + ndef.writeNdefMessage(message); + callbackContext.success(); } + } else { + callbackContext.error("Tag is read only"); + } + ndef.close(); } else { - callbackContext.error("NO_INTENT"); - } - } - - private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { - String mimeType = data.getString(0); - removeIntentFilter(mimeType); - restartNfc(); - callbackContext.success(); - } - - private void registerMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { - String mimeType = ""; - try { - mimeType = data.getString(0); - intentFilters.add(createIntentFilter(mimeType)); - restartNfc(); + NdefFormatable formatable = NdefFormatable.get(tag); + if (formatable != null) { + formatable.connect(); + formatable.format(message); callbackContext.success(); - } catch (MalformedMimeTypeException e) { - callbackContext.error("Invalid MIME Type " + mimeType); + formatable.close(); + } else { + callbackContext.error("Tag doesn't support NDEF"); + } } + } catch (FormatException e) { + callbackContext.error(e.getMessage()); + } catch (TagLostException e) { + callbackContext.error(e.getMessage()); + } catch (IOException e) { + callbackContext.error(e.getMessage()); + } + }); + } + + private void makeReadOnly(final CallbackContext callbackContext) { + + if (getIntent() == null) { // Lost Tag + callbackContext.error("Failed to make tag read only, received null intent"); + return; } - // Cheating and writing an empty record. We may actually be able to erase some tag types. - private void eraseTag(CallbackContext callbackContext) { - Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - NdefRecord[] records = { - new NdefRecord(NdefRecord.TNF_EMPTY, new byte[0], new byte[0], new byte[0]) - }; - writeNdefMessage(new NdefMessage(records), tag, callbackContext); + final Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag == null) { + callbackContext.error("Failed to make tag read only, tag is null"); + return; } - private void writeTag(JSONArray data, CallbackContext callbackContext) throws JSONException { - if (getIntent() == null) { // TODO remove this and handle LostTag - callbackContext.error("Failed to write tag, received null intent"); - } + cordova.getThreadPool().execute(() -> { + boolean success = false; + String message = "Could not make tag read only"; - Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - NdefRecord[] records = Util.jsonToNdefRecords(data.getString(0)); - writeNdefMessage(new NdefMessage(records), tag, callbackContext); - } + Ndef ndef = Ndef.get(tag); - private void writeNdefMessage(final NdefMessage message, final Tag tag, final CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - Ndef ndef = Ndef.get(tag); - if (ndef != null) { - ndef.connect(); - - if (ndef.isWritable()) { - int size = message.toByteArray().length; - if (ndef.getMaxSize() < size) { - callbackContext.error("Tag capacity is " + ndef.getMaxSize() + - " bytes, message is " + size + " bytes."); - } else { - ndef.writeNdefMessage(message); - callbackContext.success(); - } - } else { - callbackContext.error("Tag is read only"); - } - ndef.close(); - } else { - NdefFormatable formatable = NdefFormatable.get(tag); - if (formatable != null) { - formatable.connect(); - formatable.format(message); - callbackContext.success(); - formatable.close(); - } else { - callbackContext.error("Tag doesn't support NDEF"); - } - } - } catch (FormatException e) { - callbackContext.error(e.getMessage()); - } catch (TagLostException e) { - callbackContext.error(e.getMessage()); - } catch (IOException e) { - callbackContext.error(e.getMessage()); - } - }); - } + try { + if (ndef != null) { - private void makeReadOnly(final CallbackContext callbackContext) { + ndef.connect(); - if (getIntent() == null) { // Lost Tag - callbackContext.error("Failed to make tag read only, received null intent"); - return; - } + if (!ndef.isWritable()) { + message = "Tag is not writable"; + } else if (ndef.canMakeReadOnly()) { + success = ndef.makeReadOnly(); + } else { + message = "Tag can not be made read only"; + } - final Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag == null) { - callbackContext.error("Failed to make tag read only, tag is null"); - return; + } else { + message = "Tag is not NDEF"; } - cordova.getThreadPool().execute(() -> { - boolean success = false; - String message = "Could not make tag read only"; - - Ndef ndef = Ndef.get(tag); - - try { - if (ndef != null) { - - ndef.connect(); - - if (!ndef.isWritable()) { - message = "Tag is not writable"; - } else if (ndef.canMakeReadOnly()) { - success = ndef.makeReadOnly(); - } else { - message = "Tag can not be made read only"; - } - - } else { - message = "Tag is not NDEF"; - } - - } catch (IOException e) { - Log.e(TAG, "Failed to make tag read only", e); - if (e.getMessage() != null) { - message = e.getMessage(); - } else { - message = e.toString(); - } - } - - if (success) { - callbackContext.success(); - } else { - callbackContext.error(message); - } - }); - } - - 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)); + } catch (IOException e) { + Log.e(TAG, "Failed to make tag read only", e); + if (e.getMessage() != null) { + message = e.getMessage(); + } else { + message = e.toString(); } + } - startNdefBeam(callbackContext, uri); - } - - private void stopHandover(CallbackContext callbackContext) { - stopNdefBeam(); - handoverCallback = null; + if (success) { callbackContext.success(); + } else { + callbackContext.error(message); + } + }); + } + + 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)); } - 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); - getActivity().startActivity(intent); - } else { - Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); - getActivity().startActivity(intent); - } - callbackContext.success(); + 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); + getActivity().startActivity(intent); + } else { + Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); + getActivity().startActivity(intent); } - - private void createPendingIntent() { - if (pendingIntent == null) { - 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); - } + callbackContext.success(); + } + + private void createPendingIntent() { + if (pendingIntent == null) { + 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); } - - private void addTechList(String[] list) { - this.addTechFilter(); - this.addToTechList(list); + } + + private void addTechList(String[] list) { + this.addTechFilter(); + this.addToTechList(list); + } + + private void removeTechList(String[] list) { + this.removeTechFilter(); + this.removeFromTechList(list); + } + + private void addTechFilter() { + intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)); + } + + private void removeTechFilter() { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intentFilter.getAction(0))) { + iterator.remove(); + } } - - private void removeTechList(String[] list) { - this.removeTechFilter(); - this.removeFromTechList(list); + } + + private void addTagFilter() { + intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)); + } + + private void removeTagFilter() { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intentFilter.getAction(0))) { + iterator.remove(); + } } + } - private void addTechFilter() { - intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)); - } + private void restartNfc() { + stopNfc(); + startNfc(); + } - private void removeTechFilter() { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intentFilter.getAction(0))) { - iterator.remove(); - } - } - } + private void startNfc() { + createPendingIntent(); // onResume can call startNfc before execute - private void addTagFilter() { - intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)); - } + getActivity().runOnUiThread(() -> { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - private void removeTagFilter() { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intentFilter.getAction(0))) { - iterator.remove(); - } + if (nfcAdapter != null && !getActivity().isFinishing()) { + try { + IntentFilter[] intentFilters = getIntentFilters(); + String[][] techLists = getTechLists(); + // don't start NFC unless some intent filters or tech lists have been added, + // because empty lists act as wildcards and receives ALL scan events + 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."); } - } - private void restartNfc() { - stopNfc(); - startNfc(); - } + } + }); + } - private void startNfc() { - createPendingIntent(); // onResume can call startNfc before execute - - getActivity().runOnUiThread(() -> { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - - if (nfcAdapter != null && !getActivity().isFinishing()) { - try { - IntentFilter[] intentFilters = getIntentFilters(); - String[][] techLists = getTechLists(); - // don't start NFC unless some intent filters or tech lists have been added, - // because empty lists act as wildcards and receives ALL scan events - 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."); - } + private void stopNfc() { + Log.d(TAG, "stopNfc"); + getActivity().runOnUiThread(() -> { - } - }); - } + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - private void stopNfc() { - Log.d(TAG, "stopNfc"); - getActivity().runOnUiThread(() -> { + if (nfcAdapter != null) { + try { + nfcAdapter.disableForegroundDispatch(getActivity()); + } catch (IllegalStateException e) { + // issue 125 - user exits app with back button while nfc + Log.w(TAG, "Illegal State Exception stopping NFC. Assuming application is terminating."); + } + } + }); + } - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + private void startNdefBeam(final CallbackContext callbackContext, final Uri[] uris) { + getActivity().runOnUiThread(() -> { - if (nfcAdapter != null) { - try { - nfcAdapter.disableForegroundDispatch(getActivity()); - } catch (IllegalStateException e) { - // issue 125 - user exits app with back button while nfc - Log.w(TAG, "Illegal State Exception stopping NFC. Assuming application is terminating."); - } - } - }); - } + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - private void startNdefBeam(final CallbackContext callbackContext, final Uri[] uris) { - getActivity().runOnUiThread(() -> { + 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()); - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); + result.setKeepCallback(true); + handoverCallback = callbackContext; + callbackContext.sendPluginResult(result); - 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()); - } - } - }); + } 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); + } + + private void removeFromTechList(String[] techs) { + Iterator iterator = techLists.iterator(); + while (iterator.hasNext()) { + String[] list = iterator.next(); + if (Arrays.equals(list, techs)) { + iterator.remove(); + } } - - 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 removeIntentFilter(String mimeType) { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + String mt = intentFilter.getDataType(0); + if (mimeType.equals(mt)) { + iterator.remove(); + } + } + } + + private IntentFilter createIntentFilter(String mimeType) throws MalformedMimeTypeException { + IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); + intentFilter.addDataType(mimeType); + return intentFilter; + } + + private PendingIntent getPendingIntent() { + return pendingIntent; + } + + private IntentFilter[] getIntentFilters() { + return intentFilters.toArray(new IntentFilter[intentFilters.size()]); + } + + private String[][] getTechLists() { + //noinspection ToArrayCallWithZeroLengthArrayArgument + return techLists.toArray(new String[0][0]); + } + + private void parseMessage() { + cordova.getThreadPool().execute(() -> { + Log.d(TAG, "parseMessage " + getIntent()); + Intent intent = getIntent(); + String action = intent.getAction(); + Log.d(TAG, "action " + action); + if (action == null) { + return; + } + + 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); + fireNdefEvent(NDEF_MIME, ndef, messages); + + } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { + for (String tagTech : tag.getTechList()) { + Log.d(TAG, tagTech); + if (tagTech.equals(NdefFormatable.class.getName())) { + fireNdefFormatableEvent(tag); + } else if (tagTech.equals(Ndef.class.getName())) { // + Ndef ndef = Ndef.get(tag); + fireNdefEvent(NDEF, ndef, messages); + } + } + } + + if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { + fireTagEvent(tag, messages); + } + + setIntent(new Intent()); + }); + } + + // Send the event data through a channel so the JavaScript side can fire the event + private void sendEvent(String type, JSONObject tag) { + + try { + JSONObject event = new JSONObject(); + event.put("type", type); // TAG_DEFAULT, NDEF, NDEF_MIME, NDEF_FORMATABLE + event.put("tag", tag); // JSON representing the NFC tag and NDEF messages + + PluginResult result = new PluginResult(PluginResult.Status.OK, event); + result.setKeepCallback(true); + + if (channelCallback != null) { + channelCallback.sendPluginResult(result); + } else { + postponedPluginResult = new PostponedPluginResult(new Date(), result); + } + } catch (JSONException e) { + Log.e(TAG, "Error sending NFC event through the channel", e); } - private void stopNdefPush() { - getActivity().runOnUiThread(() -> { - - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + } - if (nfcAdapter != null) { - nfcAdapter.setNdefPushMessage(null, getActivity()); - } + private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) { + JSONObject json = buildNdefJSON(ndef, messages); + sendEvent(type, json); + } - }); - } + private void fireNdefFormatableEvent(Tag tag) { + sendEvent(NDEF_FORMATABLE, Util.tagToJSON(tag, null)); + } - private void stopNdefBeam() { - getActivity().runOnUiThread(() -> { + private void fireTagEvent(Tag tag, Parcelable[] messages) { + sendEvent(TAG_DEFAULT, Util.tagToJSON(tag, messages)); + } - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + private JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) { - if (nfcAdapter != null) { - nfcAdapter.setBeamPushUris(null, getActivity()); - } + JSONObject json = Util.ndefToJSON(ndef); - }); - } + // ndef is null for peer-to-peer + // ndef and messages are null for ndef format-able + if (ndef == null && messages != null) { - private void addToTechList(String[] techs) { - techLists.add(techs); - } + try { - private void removeFromTechList(String[] techs) { - Iterator iterator = techLists.iterator(); - while (iterator.hasNext()) { - String[] list = iterator.next(); - if (Arrays.equals(list, techs)) { - iterator.remove(); - } + if (messages.length > 0) { + NdefMessage message = (NdefMessage) messages[0]; + json.put("ndefMessage", Util.messageToJSON(message)); + // guessing type, would prefer a more definitive way to determine type + json.put("type", "NDEF Push Protocol"); } - } - private void removeIntentFilter(String mimeType) { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - String mt = intentFilter.getDataType(0); - if (mimeType.equals(mt)) { - iterator.remove(); - } + if (messages.length > 1) { + Log.wtf(TAG, "Expected one ndefMessage but found " + messages.length); } - } - private IntentFilter createIntentFilter(String mimeType) throws MalformedMimeTypeException { - IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); - intentFilter.addDataType(mimeType); - return intentFilter; + } catch (JSONException e) { + // shouldn't happen + Log.e(Util.TAG, "Failed to convert ndefMessage into json", e); + } } + return json; + } - private PendingIntent getPendingIntent() { - return pendingIntent; - } + private boolean recycledIntent() { // TODO this is a kludge, find real solution - private IntentFilter[] getIntentFilters() { - return intentFilters.toArray(new IntentFilter[intentFilters.size()]); + int flags = getIntent().getFlags(); + if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) { + Log.i(TAG, "Launched from history, killing recycled intent"); + setIntent(new Intent()); + return true; } - - private String[][] getTechLists() { - //noinspection ToArrayCallWithZeroLengthArrayArgument - return techLists.toArray(new String[0][0]); + return false; + } + + @Override + public void onPause(boolean multitasking) { + Log.d(TAG, "onPause " + getIntent()); + super.onPause(multitasking); + if (multitasking) { + // nfc can't run in background + stopNfc(); } - - private void parseMessage() { - cordova.getThreadPool().execute(() -> { - Log.d(TAG, "parseMessage " + getIntent()); - Intent intent = getIntent(); - String action = intent.getAction(); - Log.d(TAG, "action " + action); - if (action == null) { - return; - } - - 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); - fireNdefEvent(NDEF_MIME, ndef, messages); - - } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { - for (String tagTech : tag.getTechList()) { - Log.d(TAG, tagTech); - if (tagTech.equals(NdefFormatable.class.getName())) { - fireNdefFormatableEvent(tag); - } else if (tagTech.equals(Ndef.class.getName())) { // - Ndef ndef = Ndef.get(tag); - fireNdefEvent(NDEF, ndef, messages); - } - } - } - - if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { - fireTagEvent(tag); - } - - setIntent(new Intent()); - }); + } + + @Override + public void onResume(boolean multitasking) { + Log.d(TAG, "onResume " + getIntent()); + super.onResume(multitasking); + startNfc(); + } + + @Override + public void onNewIntent(Intent intent) { + Log.d(TAG, "onNewIntent " + intent); + super.onNewIntent(intent); + setIntent(intent); + savedIntent = intent; + parseMessage(); + } + + private Activity getActivity() { + return this.cordova.getActivity(); + } + + private Intent getIntent() { + return getActivity().getIntent(); + } + + 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); } - // Send the event data through a channel so the JavaScript side can fire the event - private void sendEvent(String type, JSONObject tag) { - - try { - JSONObject event = new JSONObject(); - event.put("type", type); // TAG_DEFAULT, NDEF, NDEF_MIME, NDEF_FORMATABLE - event.put("tag", tag); // JSON representing the NFC tag and NDEF messages - - PluginResult result = new PluginResult(PluginResult.Status.OK, event); - result.setKeepCallback(true); - channelCallback.sendPluginResult(result); - } catch (JSONException e) { - Log.e(TAG, "Error sending NFC event through the channel", e); + } + + /** + * Enable I/O operations to the tag from this TagTechnology object. + * * + * + * @param tech TagTechnology class name e.g. 'android.nfc.tech.IsoDep' or 'android.nfc.tech.NfcV' + * @param timeout tag timeout + * @param callbackContext Cordova callback context + */ + private void connect(final String tech, final int timeout, final CallbackContext callbackContext) { + this.cordova.getThreadPool().execute(() -> { + try { + + Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag == null && savedIntent != null) { + tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); } - } - - private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) { - JSONObject json = buildNdefJSON(ndef, messages); - sendEvent(type, json); - } - - private void fireNdefFormatableEvent(Tag tag) { - sendEvent(NDEF_FORMATABLE, Util.tagToJSON(tag)); - } - - private void fireTagEvent(Tag tag) { - sendEvent(TAG_DEFAULT, Util.tagToJSON(tag)); - } - - private JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) { - - JSONObject json = Util.ndefToJSON(ndef); - - // ndef is null for peer-to-peer - // ndef and messages are null for ndef format-able - if (ndef == null && messages != null) { - - try { - - if (messages.length > 0) { - NdefMessage message = (NdefMessage) messages[0]; - json.put("ndefMessage", Util.messageToJSON(message)); - // guessing type, would prefer a more definitive way to determine type - json.put("type", "NDEF Push Protocol"); - } - - if (messages.length > 1) { - Log.wtf(TAG, "Expected one ndefMessage but found " + messages.length); - } - - } catch (JSONException e) { - // shouldn't happen - Log.e(Util.TAG, "Failed to convert ndefMessage into json", e); - } + if (tag == null) { + Log.e(TAG, "No Tag"); + callbackContext.error("No Tag"); + return; } - return json; - } - private boolean recycledIntent() { // TODO this is a kludge, find real solution - - int flags = getIntent().getFlags(); - if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) { - Log.i(TAG, "Launched from history, killing recycled intent"); - setIntent(new Intent()); - return true; + JSONObject resultObject = new JSONObject(); + + // get technologies supported by this tag + List techList = Arrays.asList(tag.getTechList()); + if (techList.contains(tech)) { + // use reflection to call the static function Tech.get(tag) + tagTechnologyClass = Class.forName(tech); + Method method = tagTechnologyClass.getMethod("get", Tag.class); + tagTechnology = (TagTechnology) method.invoke(null, tag); + + // If the tech supports it, return maxTransceiveLength and return it to the user + try { + Method maxTransceiveLengthMethod = tagTechnologyClass.getMethod("getMaxTransceiveLength"); + resultObject.put("maxTransceiveLength", maxTransceiveLengthMethod.invoke(tagTechnology)); + } catch (NoSuchMethodException e) { + // Some technologies do not support this, so just ignore. + } catch (JSONException e) { + Log.e(TAG, "Error serializing JSON", e); + } } - return false; - } - @Override - public void onPause(boolean multitasking) { - Log.d(TAG, "onPause " + getIntent()); - super.onPause(multitasking); - if (multitasking) { - // nfc can't run in background - stopNfc(); + if (tagTechnology == null) { + callbackContext.error("Tag does not support " + tech); + return; } - } - - @Override - public void onResume(boolean multitasking) { - Log.d(TAG, "onResume " + getIntent()); - super.onResume(multitasking); - startNfc(); - } - - @Override - public void onNewIntent(Intent intent) { - Log.d(TAG, "onNewIntent " + intent); - super.onNewIntent(intent); - setIntent(intent); - savedIntent = intent; - parseMessage(); - } - - private Activity getActivity() { - return this.cordova.getActivity(); - } - private Intent getIntent() { - return getActivity().getIntent(); + tagTechnology.connect(); + setTimeout(timeout); + callbackContext.success(resultObject); + + } catch (IOException ex) { + Log.e(TAG, "Tag connection failed", ex); + callbackContext.error("Tag connection failed"); + + // Users should never get these reflection errors + } catch (ClassNotFoundException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (NoSuchMethodException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } + }); + } + + // Call tagTech setTimeout with reflection or fail silently + private void setTimeout(int timeout) { + if (timeout < 0) { + return; } - - private void setIntent(Intent intent) { - getActivity().setIntent(intent); + try { + Method setTimeout = tagTechnologyClass.getMethod("setTimeout", int.class); + setTimeout.invoke(tagTechnology, timeout); + } catch (NoSuchMethodException e) { + // ignore + } catch (IllegalAccessException e) { + // ignore + } catch (InvocationTargetException e) { + // ignore } - - @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); + } + + /** + * Disable I/O operations to the tag from this TagTechnology object, and release resources. + * + * @param callbackContext Cordova callback context + */ + private void close(CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + + if (tagTechnology != null && tagTechnology.isConnected()) { + tagTechnology.close(); + tagTechnology = null; + callbackContext.success(); + } else { + // connection already gone + callbackContext.success(); } - } - - /** - * Enable I/O operations to the tag from this TagTechnology object. - * * - * - * @param tech TagTechnology class name e.g. 'android.nfc.tech.IsoDep' or 'android.nfc.tech.NfcV' - * @param timeout tag timeout - * @param callbackContext Cordova callback context - */ - private void connect(final String tech, final int timeout, final CallbackContext callbackContext) { - this.cordova.getThreadPool().execute(() -> { - try { - - Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag == null && savedIntent != null) { - tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - } - - if (tag == null) { - Log.e(TAG, "No Tag"); - callbackContext.error("No Tag"); - return; - } - - JSONObject resultObject = new JSONObject(); - - // get technologies supported by this tag - List techList = Arrays.asList(tag.getTechList()); - if (techList.contains(tech)) { - // use reflection to call the static function Tech.get(tag) - tagTechnologyClass = Class.forName(tech); - Method method = tagTechnologyClass.getMethod("get", Tag.class); - tagTechnology = (TagTechnology) method.invoke(null, tag); - - // If the tech supports it, return maxTransceiveLength and return it to the user - try { - Method maxTransceiveLengthMethod = tagTechnologyClass.getMethod("getMaxTransceiveLength"); - resultObject.put("maxTransceiveLength", maxTransceiveLengthMethod.invoke(tagTechnology)); - } catch(NoSuchMethodException e) { - // Some technologies do not support this, so just ignore. - } catch(JSONException e) { - Log.e(TAG, "Error serializing JSON", e); - } - } - - if (tagTechnology == null) { - callbackContext.error("Tag does not support " + tech); - return; - } - - tagTechnology.connect(); - setTimeout(timeout); - callbackContext.success(resultObject); - - } catch (IOException ex) { - Log.e(TAG, "Tag connection failed", ex); - callbackContext.error("Tag connection failed"); - - // Users should never get these reflection errors - } catch (ClassNotFoundException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (NoSuchMethodException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (IllegalAccessException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (InvocationTargetException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } - }); - } - - // Call tagTech setTimeout with reflection or fail silently - private void setTimeout(int timeout) { - if (timeout < 0) { - return; + } catch (IOException ex) { + Log.e(TAG, "Error closing nfc connection", ex); + callbackContext.error("Error closing nfc connection " + ex.getLocalizedMessage()); + } + }); + } + + /** + * Send raw commands to the tag and receive the response. + * + * @param data byte[] command to be passed to the tag + * @param callbackContext Cordova callback context + */ + private void transceive(final byte[] data, final CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + if (tagTechnology == null) { + Log.e(TAG, "No Tech"); + callbackContext.error("No Tech"); + return; } - try { - Method setTimeout = tagTechnologyClass.getMethod("setTimeout", int.class); - setTimeout.invoke(tagTechnology, timeout); - } catch (NoSuchMethodException e) { - // ignore - } catch (IllegalAccessException e) { - // ignore - } catch (InvocationTargetException e) { - // ignore + if (!tagTechnology.isConnected()) { + Log.e(TAG, "Not connected"); + callbackContext.error("Not connected"); + return; } - } - /** - * Disable I/O operations to the tag from this TagTechnology object, and release resources. - * - * @param callbackContext Cordova callback context - */ - private void close(CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - - if (tagTechnology != null && tagTechnology.isConnected()) { - tagTechnology.close(); - tagTechnology = null; - callbackContext.success(); - } else { - // connection already gone - callbackContext.success(); - } - - } catch (IOException ex) { - Log.e(TAG, "Error closing nfc connection", ex); - callbackContext.error("Error closing nfc connection " + ex.getLocalizedMessage()); - } - }); - } - - /** - * Send raw commands to the tag and receive the response. - * - * @param data byte[] command to be passed to the tag - * @param callbackContext Cordova callback context - */ - private void transceive(final byte[] data, final CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - if (tagTechnology == null) { - Log.e(TAG, "No Tech"); - callbackContext.error("No Tech"); - return; - } - if (!tagTechnology.isConnected()) { - Log.e(TAG, "Not connected"); - callbackContext.error("Not connected"); - return; - } - - // Use reflection so we can support many tag types - Method transceiveMethod = tagTechnologyClass.getMethod("transceive", byte[].class); - @SuppressWarnings("PrimitiveArrayArgumentToVarargsMethod") - byte[] response = (byte[]) transceiveMethod.invoke(tagTechnology, data); - - callbackContext.success(response); - - } catch (NoSuchMethodException e) { - String error = "TagTechnology " + tagTechnologyClass.getName() + " does not have a transceive function"; - Log.e(TAG, error, e); - callbackContext.error(error); - } catch (NullPointerException e) { - // This can happen if the tag has been closed while we're still working with it from the thread pool. - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (IllegalAccessException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (InvocationTargetException e) { - Log.e(TAG, e.getMessage(), e); - Throwable cause = e.getCause(); - callbackContext.error(cause.getMessage()); - } - }); - } + // Use reflection so we can support many tag types + Method transceiveMethod = tagTechnologyClass.getMethod("transceive", byte[].class); + @SuppressWarnings("PrimitiveArrayArgumentToVarargsMethod") + byte[] response = (byte[]) transceiveMethod.invoke(tagTechnology, data); + + callbackContext.success(response); + + } catch (NoSuchMethodException e) { + String error = "TagTechnology " + tagTechnologyClass.getName() + " does not have a transceive function"; + Log.e(TAG, error, e); + callbackContext.error(error); + } catch (NullPointerException e) { + // This can happen if the tag has been closed while we're still working with it from the thread pool. + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(TAG, e.getMessage(), e); + Throwable cause = e.getCause(); + callbackContext.error(cause.getMessage()); + } + }); + } } diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java index 5b90e8f5..d7ab22e2 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java @@ -1,5 +1,6 @@ package com.chariotsolutions.nfc.plugin; +import android.os.Parcelable; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.Tag; @@ -49,13 +50,17 @@ static JSONObject ndefToJSON(Ndef ndef) { return json; } - static JSONObject tagToJSON(Tag tag) { + static JSONObject tagToJSON(Tag tag, Parcelable[] messages) { JSONObject json = new JSONObject(); if (tag != null) { try { json.put("id", byteArrayToJSON(tag.getId())); json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); + + if (messages != null && messages.length > 0) { + json.put("ndefMessage", messageToJSON((NdefMessage)messages[0])); + } } catch (JSONException e) { Log.e(TAG, "Failed to convert tag into json: " + tag.toString(), e); } From 9ab3e5f176d9dbe60df23f3fa9b013f7b61080ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 25 May 2022 00:37:44 +0200 Subject: [PATCH 3/7] fix: use channelCallback instead of readerMode --- .../src/com/chariotsolutions/nfc/plugin/NfcPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index d219cc06..d4e49c4f 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -124,7 +124,7 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo // the channel is set up when the plugin starts // if (action.equalsIgnoreCase(CHANNEL)) { if (action.equalsIgnoreCase(READER_MODE)) { - readerModeCallback = callbackContext; + channelCallback = callbackContext; if (postponedPluginResult != null) { Log.i(TAG, "Postponed plugin result available"); @@ -132,7 +132,7 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo if (postponedPluginResult.isValid()) { Log.i(TAG, "Postponed plugin result is valid, resending it now"); - readerModeCallback.sendPluginResult(postponedPluginResult.pluginResult); + channelCallback.sendPluginResult(postponedPluginResult.pluginResult); } else { Log.i(TAG, "Postponed plugin result not valid anymore, so ignoring it"); } From 1a9a6044abeba2f59e56a0412c8c56e4c1fd5fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 25 May 2022 00:37:52 +0200 Subject: [PATCH 4/7] style: format utils --- .../com/chariotsolutions/nfc/plugin/Util.java | 210 +++++++++--------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java index d7ab22e2..3625a4b6 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java @@ -16,130 +16,130 @@ public class Util { - static final String TAG = "NfcPlugin"; - - static JSONObject ndefToJSON(Ndef ndef) { - JSONObject json = new JSONObject(); - - if (ndef != null) { - try { - - Tag tag = ndef.getTag(); - // tag is going to be null for NDEF_FORMATABLE until NfcUtil.parseMessage is refactored - if (tag != null) { - json.put("id", byteArrayToJSON(tag.getId())); - json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); - } - - json.put("type", translateType(ndef.getType())); - json.put("maxSize", ndef.getMaxSize()); - json.put("isWritable", ndef.isWritable()); - json.put("ndefMessage", messageToJSON(ndef.getCachedNdefMessage())); - // Workaround for bug in ICS (Android 4.0 and 4.0.1) where - // mTag.getTagService(); of the Ndef object sometimes returns null - // see http://issues.mroland.at/index.php?do=details&task_id=47 - try { - json.put("canMakeReadOnly", ndef.canMakeReadOnly()); - } catch (NullPointerException e) { - json.put("canMakeReadOnly", JSONObject.NULL); - } - } catch (JSONException e) { - Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e); - } - } - return json; - } + static final String TAG = "NfcPlugin"; + + static JSONObject ndefToJSON(Ndef ndef) { + JSONObject json = new JSONObject(); - static JSONObject tagToJSON(Tag tag, Parcelable[] messages) { - JSONObject json = new JSONObject(); + if (ndef != null) { + try { + Tag tag = ndef.getTag(); + // tag is going to be null for NDEF_FORMATABLE until NfcUtil.parseMessage is refactored if (tag != null) { - try { - json.put("id", byteArrayToJSON(tag.getId())); - json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); - - if (messages != null && messages.length > 0) { - json.put("ndefMessage", messageToJSON((NdefMessage)messages[0])); - } - } catch (JSONException e) { - Log.e(TAG, "Failed to convert tag into json: " + tag.toString(), e); - } + json.put("id", byteArrayToJSON(tag.getId())); + json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); } - return json; - } - static String translateType(String type) { - String translation; - if (type.equals(Ndef.NFC_FORUM_TYPE_1)) { - translation = "NFC Forum Type 1"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_2)) { - translation = "NFC Forum Type 2"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_3)) { - translation = "NFC Forum Type 3"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_4)) { - translation = "NFC Forum Type 4"; - } else { - translation = type; + json.put("type", translateType(ndef.getType())); + json.put("maxSize", ndef.getMaxSize()); + json.put("isWritable", ndef.isWritable()); + json.put("ndefMessage", messageToJSON(ndef.getCachedNdefMessage())); + // Workaround for bug in ICS (Android 4.0 and 4.0.1) where + // mTag.getTagService(); of the Ndef object sometimes returns null + // see http://issues.mroland.at/index.php?do=details&task_id=47 + try { + json.put("canMakeReadOnly", ndef.canMakeReadOnly()); + } catch (NullPointerException e) { + json.put("canMakeReadOnly", JSONObject.NULL); } - return translation; + } catch (JSONException e) { + Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e); + } } + return json; + } - static NdefRecord[] jsonToNdefRecords(String ndefMessageAsJSON) throws JSONException { - JSONArray jsonRecords = new JSONArray(ndefMessageAsJSON); - NdefRecord[] records = new NdefRecord[jsonRecords.length()]; - for (int i = 0; i < jsonRecords.length(); i++) { - JSONObject record = jsonRecords.getJSONObject(i); - byte tnf = (byte) record.getInt("tnf"); - byte[] type = jsonToByteArray(record.getJSONArray("type")); - byte[] id = jsonToByteArray(record.getJSONArray("id")); - byte[] payload = jsonToByteArray(record.getJSONArray("payload")); - records[i] = new NdefRecord(tnf, type, id, payload); - } - return records; - } + static JSONObject tagToJSON(Tag tag, Parcelable[] messages) { + JSONObject json = new JSONObject(); - static JSONArray byteArrayToJSON(byte[] bytes) { - JSONArray json = new JSONArray(); - for (byte aByte : bytes) { - json.put(aByte); + if (tag != null) { + try { + json.put("id", byteArrayToJSON(tag.getId())); + json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); + + if (messages != null && messages.length > 0) { + json.put("ndefMessage", messageToJSON((NdefMessage)messages[0])); } - return json; + } catch (JSONException e) { + Log.e(TAG, "Failed to convert tag into json: " + tag.toString(), e); + } + } + return json; + } + + static String translateType(String type) { + String translation; + if (type.equals(Ndef.NFC_FORUM_TYPE_1)) { + translation = "NFC Forum Type 1"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_2)) { + translation = "NFC Forum Type 2"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_3)) { + translation = "NFC Forum Type 3"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_4)) { + translation = "NFC Forum Type 4"; + } else { + translation = type; } + return translation; + } + + static NdefRecord[] jsonToNdefRecords(String ndefMessageAsJSON) throws JSONException { + JSONArray jsonRecords = new JSONArray(ndefMessageAsJSON); + NdefRecord[] records = new NdefRecord[jsonRecords.length()]; + for (int i = 0; i < jsonRecords.length(); i++) { + JSONObject record = jsonRecords.getJSONObject(i); + byte tnf = (byte) record.getInt("tnf"); + byte[] type = jsonToByteArray(record.getJSONArray("type")); + byte[] id = jsonToByteArray(record.getJSONArray("id")); + byte[] payload = jsonToByteArray(record.getJSONArray("payload")); + records[i] = new NdefRecord(tnf, type, id, payload); + } + return records; + } - static byte[] jsonToByteArray(JSONArray json) throws JSONException { - byte[] b = new byte[json.length()]; - for (int i = 0; i < json.length(); i++) { - b[i] = (byte) json.getInt(i); - } - return b; + static JSONArray byteArrayToJSON(byte[] bytes) { + JSONArray json = new JSONArray(); + for (byte aByte : bytes) { + json.put(aByte); } + return json; + } - static JSONArray messageToJSON(NdefMessage message) { - if (message == null) { - return null; - } + static byte[] jsonToByteArray(JSONArray json) throws JSONException { + byte[] b = new byte[json.length()]; + for (int i = 0; i < json.length(); i++) { + b[i] = (byte) json.getInt(i); + } + return b; + } - List list = new ArrayList(); + static JSONArray messageToJSON(NdefMessage message) { + if (message == null) { + return null; + } - for (NdefRecord ndefRecord : message.getRecords()) { - list.add(recordToJSON(ndefRecord)); - } + List list = new ArrayList(); - return new JSONArray(list); + for (NdefRecord ndefRecord : message.getRecords()) { + list.add(recordToJSON(ndefRecord)); } - static JSONObject recordToJSON(NdefRecord record) { - JSONObject json = new JSONObject(); - try { - json.put("tnf", record.getTnf()); - json.put("type", byteArrayToJSON(record.getType())); - json.put("id", byteArrayToJSON(record.getId())); - json.put("payload", byteArrayToJSON(record.getPayload())); - } catch (JSONException e) { - //Not sure why this would happen, documentation is unclear. - Log.e(TAG, "Failed to convert ndef record into json: " + record.toString(), e); - } - return json; + return new JSONArray(list); + } + + static JSONObject recordToJSON(NdefRecord record) { + JSONObject json = new JSONObject(); + try { + json.put("tnf", record.getTnf()); + json.put("type", byteArrayToJSON(record.getType())); + json.put("id", byteArrayToJSON(record.getId())); + json.put("payload", byteArrayToJSON(record.getPayload())); + } catch (JSONException e) { + //Not sure why this would happen, documentation is unclear. + Log.e(TAG, "Failed to convert ndef record into json: " + record.toString(), e); } + return json; + } } From 470fe73251d5e322d16f7b3e5688f8f60c4f5e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 25 May 2022 23:28:15 +0200 Subject: [PATCH 5/7] chore: bump version to v1.2.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 47ea5fa9..ac882120 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "phonegap-nfc", - "version": "1.2.0", + "version": "1.2.1", "description": "Near Field Communication (NFC) Plugin. Read and write NDEF messages to NFC tags and share NDEF messages with peers.", "cordova": { "id": "phonegap-nfc", @@ -33,4 +33,4 @@ "url": "https://github.com/chariotsolutions/phonegap-nfc/issues" }, "homepage": "https://github.com/chariotsolutions/phonegap-nfc#readme" -} +} \ No newline at end of file From 7927579fd9329b329f819d78e28207f4a4902c89 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Mon, 29 Jul 2024 21:50:36 +0200 Subject: [PATCH 6/7] revert: reset indent from 2 back to 4 spaces --- .../nfc/plugin/NfcPlugin.java | 1786 ++++++++--------- .../com/chariotsolutions/nfc/plugin/Util.java | 210 +- 2 files changed, 998 insertions(+), 998 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index d4e49c4f..3c82776b 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -38,1018 +38,1018 @@ import android.util.Log; public class NfcPlugin extends CordovaPlugin implements NfcAdapter.OnNdefPushCompleteCallback { - private static final String REGISTER_MIME_TYPE = "registerMimeType"; - private static final String REMOVE_MIME_TYPE = "removeMimeType"; - private static final String REGISTER_NDEF = "registerNdef"; - private static final String REMOVE_NDEF = "removeNdef"; - private static final String REGISTER_NDEF_FORMATABLE = "registerNdefFormatable"; - private static final String REGISTER_DEFAULT_TAG = "registerTag"; - private static final String REMOVE_DEFAULT_TAG = "removeTag"; - 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"; - - private static final String NDEF = "ndef"; - private static final String NDEF_MIME = "ndef-mime"; - private static final String NDEF_FORMATABLE = "ndef-formatable"; - private static final String TAG_DEFAULT = "tag"; - - private static final String READER_MODE = "readerMode"; - private static final String DISABLE_READER_MODE = "disableReaderMode"; - - // TagTechnology IsoDep, NfcA, NfcB, NfcV, NfcF, MifareClassic, MifareUltralight - private static final String CONNECT = "connect"; - private static final String CLOSE = "close"; - private static final String TRANSCEIVE = "transceive"; - private TagTechnology tagTechnology = null; - private Class tagTechnologyClass; - - private static final String CHANNEL = "channel"; - - private static final String STATUS_NFC_OK = "NFC_OK"; - private static final String STATUS_NO_NFC = "NO_NFC"; - private static final String STATUS_NFC_DISABLED = "NFC_DISABLED"; - private static final String STATUS_NDEF_PUSH_DISABLED = "NDEF_PUSH_DISABLED"; - - private static final String TAG = "NfcPlugin"; - 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; - - private PostponedPluginResult postponedPluginResult = null; - - class PostponedPluginResult { - private Date moment; - private PluginResult pluginResult; - - PostponedPluginResult(Date moment, PluginResult pluginResult) { - this.moment = moment; - this.pluginResult = pluginResult; - } + private static final String REGISTER_MIME_TYPE = "registerMimeType"; + private static final String REMOVE_MIME_TYPE = "removeMimeType"; + private static final String REGISTER_NDEF = "registerNdef"; + private static final String REMOVE_NDEF = "removeNdef"; + private static final String REGISTER_NDEF_FORMATABLE = "registerNdefFormatable"; + private static final String REGISTER_DEFAULT_TAG = "registerTag"; + private static final String REMOVE_DEFAULT_TAG = "removeTag"; + 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"; + + private static final String NDEF = "ndef"; + private static final String NDEF_MIME = "ndef-mime"; + private static final String NDEF_FORMATABLE = "ndef-formatable"; + private static final String TAG_DEFAULT = "tag"; + + private static final String READER_MODE = "readerMode"; + private static final String DISABLE_READER_MODE = "disableReaderMode"; + + // TagTechnology IsoDep, NfcA, NfcB, NfcV, NfcF, MifareClassic, MifareUltralight + private static final String CONNECT = "connect"; + private static final String CLOSE = "close"; + private static final String TRANSCEIVE = "transceive"; + private TagTechnology tagTechnology = null; + private Class tagTechnologyClass; + + private static final String CHANNEL = "channel"; + + private static final String STATUS_NFC_OK = "NFC_OK"; + private static final String STATUS_NO_NFC = "NO_NFC"; + private static final String STATUS_NFC_DISABLED = "NFC_DISABLED"; + private static final String STATUS_NDEF_PUSH_DISABLED = "NDEF_PUSH_DISABLED"; + + private static final String TAG = "NfcPlugin"; + 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; + + private PostponedPluginResult postponedPluginResult = null; + + class PostponedPluginResult { + private Date moment; + private PluginResult pluginResult; + + PostponedPluginResult(Date moment, PluginResult pluginResult) { + this.moment = moment; + this.pluginResult = pluginResult; + } - boolean isValid() { - return this.moment.after(new Date(new Date().getTime() - 30000)); + boolean isValid() { + return this.moment.after(new Date(new Date().getTime() - 30000)); + } } - } - @Override - public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { + @Override + public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { - Log.d(TAG, "execute " + action); -// Log.d(TAG, "execute postponedPluginResult.toString() " + postponedPluginResult.toString()); + Log.d(TAG, "execute " + action); +// Log.d(TAG, "execute postponedPluginResult.toString() " + postponedPluginResult.toString()); - // showSettings can be called if NFC is disabled - // might want to skip this if NO_NFC - if (action.equalsIgnoreCase(SHOW_SETTINGS)) { - showSettings(callbackContext); - return true; - } + // showSettings can be called if NFC is disabled + // might want to skip this if NO_NFC + if (action.equalsIgnoreCase(SHOW_SETTINGS)) { + showSettings(callbackContext); + return true; + } - // the channel is set up when the plugin starts -// if (action.equalsIgnoreCase(CHANNEL)) { - if (action.equalsIgnoreCase(READER_MODE)) { - channelCallback = callbackContext; + // the channel is set up when the plugin starts +// if (action.equalsIgnoreCase(CHANNEL)) { + if (action.equalsIgnoreCase(READER_MODE)) { + channelCallback = callbackContext; - if (postponedPluginResult != null) { - Log.i(TAG, "Postponed plugin result available"); + if (postponedPluginResult != null) { + Log.i(TAG, "Postponed plugin result available"); - if (postponedPluginResult.isValid()) { - Log.i(TAG, "Postponed plugin result is valid, resending it now"); + if (postponedPluginResult.isValid()) { + Log.i(TAG, "Postponed plugin result is valid, resending it now"); - channelCallback.sendPluginResult(postponedPluginResult.pluginResult); - } else { - Log.i(TAG, "Postponed plugin result not valid anymore, so ignoring it"); + channelCallback.sendPluginResult(postponedPluginResult.pluginResult); + } else { + Log.i(TAG, "Postponed plugin result not valid anymore, so ignoring it"); + } + + postponedPluginResult = null; + } + + return true; // short circuit } - postponedPluginResult = null; - } + // allow reader mode to be disabled even if nfc is disabled + if (action.equalsIgnoreCase(DISABLE_READER_MODE)) { + disableReaderMode(callbackContext); + return true; // short circuit + } - return true; // short circuit - } + if (!getNfcStatus().equals(STATUS_NFC_OK)) { + callbackContext.error(getNfcStatus()); + return true; // short circuit + } - // allow reader mode to be disabled even if nfc is disabled - if (action.equalsIgnoreCase(DISABLE_READER_MODE)) { - disableReaderMode(callbackContext); - return true; // short circuit - } + createPendingIntent(); - if (!getNfcStatus().equals(STATUS_NFC_OK)) { - callbackContext.error(getNfcStatus()); - return true; // short circuit - } + if (action.equalsIgnoreCase(READER_MODE)) { + int flags = data.getInt(0); + readerMode(flags, callbackContext); - createPendingIntent(); + } else if (action.equalsIgnoreCase(REGISTER_MIME_TYPE)) { + registerMimeType(data, callbackContext); - if (action.equalsIgnoreCase(READER_MODE)) { - int flags = data.getInt(0); - readerMode(flags, callbackContext); + } else if (action.equalsIgnoreCase(REMOVE_MIME_TYPE)) { + removeMimeType(data, callbackContext); - } else if (action.equalsIgnoreCase(REGISTER_MIME_TYPE)) { - registerMimeType(data, callbackContext); + } else if (action.equalsIgnoreCase(REGISTER_NDEF)) { + registerNdef(callbackContext); - } else if (action.equalsIgnoreCase(REMOVE_MIME_TYPE)) { - removeMimeType(data, callbackContext); + } else if (action.equalsIgnoreCase(REMOVE_NDEF)) { + removeNdef(callbackContext); - } else if (action.equalsIgnoreCase(REGISTER_NDEF)) { - registerNdef(callbackContext); + } else if (action.equalsIgnoreCase(REGISTER_NDEF_FORMATABLE)) { + registerNdefFormatable(callbackContext); - } else if (action.equalsIgnoreCase(REMOVE_NDEF)) { - removeNdef(callbackContext); + } else if (action.equals(REGISTER_DEFAULT_TAG)) { + registerDefaultTag(callbackContext); - } else if (action.equalsIgnoreCase(REGISTER_NDEF_FORMATABLE)) { - registerNdefFormatable(callbackContext); + } else if (action.equals(REMOVE_DEFAULT_TAG)) { + removeDefaultTag(callbackContext); - } else if (action.equals(REGISTER_DEFAULT_TAG)) { - registerDefaultTag(callbackContext); + } else if (action.equalsIgnoreCase(WRITE_TAG)) { + writeTag(data, callbackContext); - } else if (action.equals(REMOVE_DEFAULT_TAG)) { - removeDefaultTag(callbackContext); + } else if (action.equalsIgnoreCase(MAKE_READ_ONLY)) { + makeReadOnly(callbackContext); - } else if (action.equalsIgnoreCase(WRITE_TAG)) { - writeTag(data, callbackContext); + } else if (action.equalsIgnoreCase(ERASE_TAG)) { + eraseTag(callbackContext); - } else if (action.equalsIgnoreCase(MAKE_READ_ONLY)) { - makeReadOnly(callbackContext); + } else if (action.equalsIgnoreCase(SHARE_TAG)) { + shareTag(data, callbackContext); - } else if (action.equalsIgnoreCase(ERASE_TAG)) { - eraseTag(callbackContext); + } else if (action.equalsIgnoreCase(UNSHARE_TAG)) { + unshareTag(callbackContext); - } else if (action.equalsIgnoreCase(SHARE_TAG)) { - shareTag(data, callbackContext); + } else if (action.equalsIgnoreCase(HANDOVER)) { + handover(data, callbackContext); - } else if (action.equalsIgnoreCase(UNSHARE_TAG)) { - unshareTag(callbackContext); + } else if (action.equalsIgnoreCase(STOP_HANDOVER)) { + stopHandover(callbackContext); - } else if (action.equalsIgnoreCase(HANDOVER)) { - handover(data, callbackContext); + } else if (action.equalsIgnoreCase(INIT)) { + init(callbackContext); + } else if (action.equalsIgnoreCase(ENABLED)) { + // status is checked before every call + // if code made it here, NFC is enabled + callbackContext.success(STATUS_NFC_OK); - } else if (action.equalsIgnoreCase(STOP_HANDOVER)) { - stopHandover(callbackContext); + } else if (action.equalsIgnoreCase(CONNECT)) { + String tech = data.getString(0); + int timeout = data.optInt(1, -1); + connect(tech, timeout, callbackContext); - } else if (action.equalsIgnoreCase(INIT)) { - init(callbackContext); - } else if (action.equalsIgnoreCase(ENABLED)) { - // status is checked before every call - // if code made it here, NFC is enabled - callbackContext.success(STATUS_NFC_OK); + } else if (action.equalsIgnoreCase(TRANSCEIVE)) { + CordovaArgs args = new CordovaArgs(data); // execute is using the old signature with JSON data - } else if (action.equalsIgnoreCase(CONNECT)) { - String tech = data.getString(0); - int timeout = data.optInt(1, -1); - connect(tech, timeout, callbackContext); + byte[] command = args.getArrayBuffer(0); + transceive(command, callbackContext); - } else if (action.equalsIgnoreCase(TRANSCEIVE)) { - CordovaArgs args = new CordovaArgs(data); // execute is using the old signature with JSON data + } else if (action.equalsIgnoreCase(CLOSE)) { + close(callbackContext); - byte[] command = args.getArrayBuffer(0); - transceive(command, callbackContext); + } else { + // invalid action + return false; + } - } else if (action.equalsIgnoreCase(CLOSE)) { - close(callbackContext); + return true; + } - } else { - // invalid action - return false; + private String getNfcStatus() { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + if (nfcAdapter == null) { + return STATUS_NO_NFC; + } else if (!nfcAdapter.isEnabled()) { + return STATUS_NFC_DISABLED; + } else { + return STATUS_NFC_OK; + } } - return true; - } - - private String getNfcStatus() { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - if (nfcAdapter == null) { - return STATUS_NO_NFC; - } else if (!nfcAdapter.isEnabled()) { - return STATUS_NFC_DISABLED; - } else { - return STATUS_NFC_OK; + private void readerMode(int flags, CallbackContext callbackContext) { + Bundle extras = new Bundle(); // not used + readerModeCallback = callbackContext; + getActivity().runOnUiThread(() -> { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + nfcAdapter.enableReaderMode(getActivity(), callback, flags, extras); + }); + } - } - - private void readerMode(int flags, CallbackContext callbackContext) { - Bundle extras = new Bundle(); // not used - readerModeCallback = callbackContext; - getActivity().runOnUiThread(() -> { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - nfcAdapter.enableReaderMode(getActivity(), callback, flags, extras); - }); - - } - - private void disableReaderMode(CallbackContext callbackContext) { - getActivity().runOnUiThread(() -> { - readerModeCallback = null; - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - if (nfcAdapter != null) { - nfcAdapter.disableReaderMode(getActivity()); - } - callbackContext.success(); - }); - } - - private NfcAdapter.ReaderCallback callback = new NfcAdapter.ReaderCallback() { - @Override - public void onTagDiscovered(Tag tag) { - - JSONObject json; - - // If the tag supports Ndef, try and return an Ndef message - List techList = Arrays.asList(tag.getTechList()); - if (techList.contains(Ndef.class.getName())) { - Ndef ndef = Ndef.get(tag); - json = Util.ndefToJSON(ndef); - } else { - json = Util.tagToJSON(tag, null); - } - - Intent tagIntent = new Intent(); - tagIntent.putExtra(NfcAdapter.EXTRA_TAG, tag); - setIntent(tagIntent); - - PluginResult result = new PluginResult(PluginResult.Status.OK, json); - result.setKeepCallback(true); - if (readerModeCallback != null) { - readerModeCallback.sendPluginResult(result); - } else { - Log.i(TAG, "readerModeCallback is null - reader mode probably disabled in the meantime"); - } + private void disableReaderMode(CallbackContext callbackContext) { + getActivity().runOnUiThread(() -> { + readerModeCallback = null; + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + if (nfcAdapter != null) { + nfcAdapter.disableReaderMode(getActivity()); + } + callbackContext.success(); + }); } - }; - - private void registerDefaultTag(CallbackContext callbackContext) { - addTagFilter(); - restartNfc(); - callbackContext.success(); - } - - private void removeDefaultTag(CallbackContext callbackContext) { - removeTagFilter(); - restartNfc(); - callbackContext.success(); - } - - private void registerNdefFormatable(CallbackContext callbackContext) { - addTechList(new String[]{NdefFormatable.class.getName()}); - restartNfc(); - callbackContext.success(); - } - - private void registerNdef(CallbackContext callbackContext) { - addTechList(new String[]{Ndef.class.getName()}); - restartNfc(); - callbackContext.success(); - } - - private void removeNdef(CallbackContext callbackContext) { - removeTechList(new String[]{Ndef.class.getName()}); - restartNfc(); - 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()); - - startNfc(); - if (!recycledIntent()) { - parseMessage(); + + private NfcAdapter.ReaderCallback callback = new NfcAdapter.ReaderCallback() { + @Override + public void onTagDiscovered(Tag tag) { + + JSONObject json; + + // If the tag supports Ndef, try and return an Ndef message + List techList = Arrays.asList(tag.getTechList()); + if (techList.contains(Ndef.class.getName())) { + Ndef ndef = Ndef.get(tag); + json = Util.ndefToJSON(ndef); + } else { + json = Util.tagToJSON(tag, null); + } + + Intent tagIntent = new Intent(); + tagIntent.putExtra(NfcAdapter.EXTRA_TAG, tag); + setIntent(tagIntent); + + PluginResult result = new PluginResult(PluginResult.Status.OK, json); + result.setKeepCallback(true); + if (readerModeCallback != null) { + readerModeCallback.sendPluginResult(result); + } else { + Log.i(TAG, "readerModeCallback is null - reader mode probably disabled in the meantime"); + } + + } + }; + + private void registerDefaultTag(CallbackContext callbackContext) { + addTagFilter(); + restartNfc(); + callbackContext.success(); } - callbackContext.success(); - } - - private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { - String mimeType = data.getString(0); - removeIntentFilter(mimeType); - restartNfc(); - callbackContext.success(); - } - - private void registerMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { - String mimeType = ""; - try { - mimeType = data.getString(0); - intentFilters.add(createIntentFilter(mimeType)); - restartNfc(); - callbackContext.success(); - } catch (MalformedMimeTypeException e) { - callbackContext.error("Invalid MIME Type " + mimeType); + + private void removeDefaultTag(CallbackContext callbackContext) { + removeTagFilter(); + restartNfc(); + callbackContext.success(); } - } - // Cheating and writing an empty record. We may actually be able to erase some tag types. - private void eraseTag(CallbackContext callbackContext) { - Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - NdefRecord[] records = { - new NdefRecord(NdefRecord.TNF_EMPTY, new byte[0], new byte[0], new byte[0]) - }; - writeNdefMessage(new NdefMessage(records), tag, callbackContext); - } + private void registerNdefFormatable(CallbackContext callbackContext) { + addTechList(new String[]{NdefFormatable.class.getName()}); + restartNfc(); + callbackContext.success(); + } - private void writeTag(JSONArray data, CallbackContext callbackContext) throws JSONException { - if (getIntent() == null) { // TODO remove this and handle LostTag - callbackContext.error("Failed to write tag, received null intent"); + private void registerNdef(CallbackContext callbackContext) { + addTechList(new String[]{Ndef.class.getName()}); + restartNfc(); + callbackContext.success(); } - Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - NdefRecord[] records = Util.jsonToNdefRecords(data.getString(0)); - writeNdefMessage(new NdefMessage(records), tag, callbackContext); - } - - private void writeNdefMessage(final NdefMessage message, final Tag tag, final CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - Ndef ndef = Ndef.get(tag); - if (ndef != null) { - ndef.connect(); - - if (ndef.isWritable()) { - int size = message.toByteArray().length; - if (ndef.getMaxSize() < size) { - callbackContext.error("Tag capacity is " + ndef.getMaxSize() + - " bytes, message is " + size + " bytes."); - } else { - ndef.writeNdefMessage(message); - callbackContext.success(); - } - } else { - callbackContext.error("Tag is read only"); - } - ndef.close(); - } else { - NdefFormatable formatable = NdefFormatable.get(tag); - if (formatable != null) { - formatable.connect(); - formatable.format(message); + private void removeNdef(CallbackContext callbackContext) { + removeTechList(new String[]{Ndef.class.getName()}); + restartNfc(); + 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()); + + startNfc(); + if (!recycledIntent()) { + parseMessage(); + } + callbackContext.success(); + } + + private void removeMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { + String mimeType = data.getString(0); + removeIntentFilter(mimeType); + restartNfc(); + callbackContext.success(); + } + + private void registerMimeType(JSONArray data, CallbackContext callbackContext) throws JSONException { + String mimeType = ""; + try { + mimeType = data.getString(0); + intentFilters.add(createIntentFilter(mimeType)); + restartNfc(); callbackContext.success(); - formatable.close(); - } else { - callbackContext.error("Tag doesn't support NDEF"); - } + } catch (MalformedMimeTypeException e) { + callbackContext.error("Invalid MIME Type " + mimeType); } - } catch (FormatException e) { - callbackContext.error(e.getMessage()); - } catch (TagLostException e) { - callbackContext.error(e.getMessage()); - } catch (IOException e) { - callbackContext.error(e.getMessage()); - } - }); - } - - private void makeReadOnly(final CallbackContext callbackContext) { - - if (getIntent() == null) { // Lost Tag - callbackContext.error("Failed to make tag read only, received null intent"); - return; } - final Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag == null) { - callbackContext.error("Failed to make tag read only, tag is null"); - return; + // Cheating and writing an empty record. We may actually be able to erase some tag types. + private void eraseTag(CallbackContext callbackContext) { + Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + NdefRecord[] records = { + new NdefRecord(NdefRecord.TNF_EMPTY, new byte[0], new byte[0], new byte[0]) + }; + writeNdefMessage(new NdefMessage(records), tag, callbackContext); } - cordova.getThreadPool().execute(() -> { - boolean success = false; - String message = "Could not make tag read only"; + private void writeTag(JSONArray data, CallbackContext callbackContext) throws JSONException { + if (getIntent() == null) { // TODO remove this and handle LostTag + callbackContext.error("Failed to write tag, received null intent"); + } - Ndef ndef = Ndef.get(tag); + Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + NdefRecord[] records = Util.jsonToNdefRecords(data.getString(0)); + writeNdefMessage(new NdefMessage(records), tag, callbackContext); + } - try { - if (ndef != null) { + private void writeNdefMessage(final NdefMessage message, final Tag tag, final CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + Ndef ndef = Ndef.get(tag); + if (ndef != null) { + ndef.connect(); + + if (ndef.isWritable()) { + int size = message.toByteArray().length; + if (ndef.getMaxSize() < size) { + callbackContext.error("Tag capacity is " + ndef.getMaxSize() + + " bytes, message is " + size + " bytes."); + } else { + ndef.writeNdefMessage(message); + callbackContext.success(); + } + } else { + callbackContext.error("Tag is read only"); + } + ndef.close(); + } else { + NdefFormatable formatable = NdefFormatable.get(tag); + if (formatable != null) { + formatable.connect(); + formatable.format(message); + callbackContext.success(); + formatable.close(); + } else { + callbackContext.error("Tag doesn't support NDEF"); + } + } + } catch (FormatException e) { + callbackContext.error(e.getMessage()); + } catch (TagLostException e) { + callbackContext.error(e.getMessage()); + } catch (IOException e) { + callbackContext.error(e.getMessage()); + } + }); + } - ndef.connect(); + private void makeReadOnly(final CallbackContext callbackContext) { - if (!ndef.isWritable()) { - message = "Tag is not writable"; - } else if (ndef.canMakeReadOnly()) { - success = ndef.makeReadOnly(); - } else { - message = "Tag can not be made read only"; - } + if (getIntent() == null) { // Lost Tag + callbackContext.error("Failed to make tag read only, received null intent"); + return; + } - } else { - message = "Tag is not NDEF"; + final Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag == null) { + callbackContext.error("Failed to make tag read only, tag is null"); + return; } - } catch (IOException e) { - Log.e(TAG, "Failed to make tag read only", e); - if (e.getMessage() != null) { - message = e.getMessage(); - } else { - message = e.toString(); + cordova.getThreadPool().execute(() -> { + boolean success = false; + String message = "Could not make tag read only"; + + Ndef ndef = Ndef.get(tag); + + try { + if (ndef != null) { + + ndef.connect(); + + if (!ndef.isWritable()) { + message = "Tag is not writable"; + } else if (ndef.canMakeReadOnly()) { + success = ndef.makeReadOnly(); + } else { + message = "Tag can not be made read only"; + } + + } else { + message = "Tag is not NDEF"; + } + + } catch (IOException e) { + Log.e(TAG, "Failed to make tag read only", e); + if (e.getMessage() != null) { + message = e.getMessage(); + } else { + message = e.toString(); + } + } + + if (success) { + callbackContext.success(); + } else { + callbackContext.error(message); + } + }); + } + + 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)); } - } - if (success) { + startNdefBeam(callbackContext, uri); + } + + private void stopHandover(CallbackContext callbackContext) { + stopNdefBeam(); + handoverCallback = null; callbackContext.success(); - } else { - callbackContext.error(message); - } - }); - } - - 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); - getActivity().startActivity(intent); - } else { - Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); - getActivity().startActivity(intent); + 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); + getActivity().startActivity(intent); + } else { + Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); + getActivity().startActivity(intent); + } + callbackContext.success(); } - callbackContext.success(); - } - - private void createPendingIntent() { - if (pendingIntent == null) { - 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); + + private void createPendingIntent() { + if (pendingIntent == null) { + 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); + } } - } - - private void addTechList(String[] list) { - this.addTechFilter(); - this.addToTechList(list); - } - - private void removeTechList(String[] list) { - this.removeTechFilter(); - this.removeFromTechList(list); - } - - private void addTechFilter() { - intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)); - } - - private void removeTechFilter() { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intentFilter.getAction(0))) { - iterator.remove(); - } + + private void addTechList(String[] list) { + this.addTechFilter(); + this.addToTechList(list); } - } - - private void addTagFilter() { - intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)); - } - - private void removeTagFilter() { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intentFilter.getAction(0))) { - iterator.remove(); - } + + private void removeTechList(String[] list) { + this.removeTechFilter(); + this.removeFromTechList(list); } - } - private void restartNfc() { - stopNfc(); - startNfc(); - } + private void addTechFilter() { + intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)); + } - private void startNfc() { - createPendingIntent(); // onResume can call startNfc before execute + private void removeTechFilter() { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intentFilter.getAction(0))) { + iterator.remove(); + } + } + } - getActivity().runOnUiThread(() -> { - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + private void addTagFilter() { + intentFilters.add(new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)); + } - if (nfcAdapter != null && !getActivity().isFinishing()) { - try { - IntentFilter[] intentFilters = getIntentFilters(); - String[][] techLists = getTechLists(); - // don't start NFC unless some intent filters or tech lists have been added, - // because empty lists act as wildcards and receives ALL scan events - 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."); + private void removeTagFilter() { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intentFilter.getAction(0))) { + iterator.remove(); + } } + } - } - }); - } + private void restartNfc() { + stopNfc(); + startNfc(); + } - private void stopNfc() { - Log.d(TAG, "stopNfc"); - getActivity().runOnUiThread(() -> { + private void startNfc() { + createPendingIntent(); // onResume can call startNfc before execute + + getActivity().runOnUiThread(() -> { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + + if (nfcAdapter != null && !getActivity().isFinishing()) { + try { + IntentFilter[] intentFilters = getIntentFilters(); + String[][] techLists = getTechLists(); + // don't start NFC unless some intent filters or tech lists have been added, + // because empty lists act as wildcards and receives ALL scan events + 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."); + } - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + } + }); + } - if (nfcAdapter != null) { - try { - nfcAdapter.disableForegroundDispatch(getActivity()); - } catch (IllegalStateException e) { - // issue 125 - user exits app with back button while nfc - Log.w(TAG, "Illegal State Exception stopping NFC. Assuming application is terminating."); - } - } - }); - } + private void stopNfc() { + Log.d(TAG, "stopNfc"); + getActivity().runOnUiThread(() -> { - private void startNdefBeam(final CallbackContext callbackContext, final Uri[] uris) { - getActivity().runOnUiThread(() -> { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + if (nfcAdapter != null) { + try { + nfcAdapter.disableForegroundDispatch(getActivity()); + } catch (IllegalStateException e) { + // issue 125 - user exits app with back button while nfc + Log.w(TAG, "Illegal State Exception stopping NFC. Assuming application is terminating."); + } + } + }); + } - 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()); + private void startNdefBeam(final CallbackContext callbackContext, final Uri[] uris) { + getActivity().runOnUiThread(() -> { - PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); - result.setKeepCallback(true); - handoverCallback = callbackContext; - callbackContext.sendPluginResult(result); + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - } 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); - } - - private void removeFromTechList(String[] techs) { - Iterator iterator = techLists.iterator(); - while (iterator.hasNext()) { - String[] list = iterator.next(); - if (Arrays.equals(list, techs)) { - iterator.remove(); - } - } - } - - private void removeIntentFilter(String mimeType) { - Iterator iterator = intentFilters.iterator(); - while (iterator.hasNext()) { - IntentFilter intentFilter = iterator.next(); - String mt = intentFilter.getDataType(0); - if (mimeType.equals(mt)) { - iterator.remove(); - } + 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 IntentFilter createIntentFilter(String mimeType) throws MalformedMimeTypeException { - IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); - intentFilter.addDataType(mimeType); - return intentFilter; - } - - private PendingIntent getPendingIntent() { - return pendingIntent; - } - - private IntentFilter[] getIntentFilters() { - return intentFilters.toArray(new IntentFilter[intentFilters.size()]); - } - - private String[][] getTechLists() { - //noinspection ToArrayCallWithZeroLengthArrayArgument - return techLists.toArray(new String[0][0]); - } - - private void parseMessage() { - cordova.getThreadPool().execute(() -> { - Log.d(TAG, "parseMessage " + getIntent()); - Intent intent = getIntent(); - String action = intent.getAction(); - Log.d(TAG, "action " + action); - if (action == null) { - return; - } - - 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); - fireNdefEvent(NDEF_MIME, ndef, messages); - - } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { - for (String tagTech : tag.getTechList()) { - Log.d(TAG, tagTech); - if (tagTech.equals(NdefFormatable.class.getName())) { - fireNdefFormatableEvent(tag); - } else if (tagTech.equals(Ndef.class.getName())) { // - Ndef ndef = Ndef.get(tag); - fireNdefEvent(NDEF, ndef, messages); - } - } - } - - if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { - fireTagEvent(tag, messages); - } - - setIntent(new Intent()); - }); - } - - // Send the event data through a channel so the JavaScript side can fire the event - private void sendEvent(String type, JSONObject tag) { - - try { - JSONObject event = new JSONObject(); - event.put("type", type); // TAG_DEFAULT, NDEF, NDEF_MIME, NDEF_FORMATABLE - event.put("tag", tag); // JSON representing the NFC tag and NDEF messages - - PluginResult result = new PluginResult(PluginResult.Status.OK, event); - result.setKeepCallback(true); - - if (channelCallback != null) { - channelCallback.sendPluginResult(result); - } else { - postponedPluginResult = new PostponedPluginResult(new Date(), result); - } - } catch (JSONException e) { - Log.e(TAG, "Error sending NFC event through the channel", e); + + 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()); - private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) { - JSONObject json = buildNdefJSON(ndef, messages); - sendEvent(type, json); - } + if (nfcAdapter != null) { + nfcAdapter.setNdefPushMessage(null, getActivity()); + } - private void fireNdefFormatableEvent(Tag tag) { - sendEvent(NDEF_FORMATABLE, Util.tagToJSON(tag, null)); - } + }); + } - private void fireTagEvent(Tag tag, Parcelable[] messages) { - sendEvent(TAG_DEFAULT, Util.tagToJSON(tag, messages)); - } + private void stopNdefBeam() { + getActivity().runOnUiThread(() -> { - private JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) { + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); - JSONObject json = Util.ndefToJSON(ndef); + if (nfcAdapter != null) { + nfcAdapter.setBeamPushUris(null, getActivity()); + } - // ndef is null for peer-to-peer - // ndef and messages are null for ndef format-able - if (ndef == null && messages != null) { + }); + } - try { + private void addToTechList(String[] techs) { + techLists.add(techs); + } - if (messages.length > 0) { - NdefMessage message = (NdefMessage) messages[0]; - json.put("ndefMessage", Util.messageToJSON(message)); - // guessing type, would prefer a more definitive way to determine type - json.put("type", "NDEF Push Protocol"); + private void removeFromTechList(String[] techs) { + Iterator iterator = techLists.iterator(); + while (iterator.hasNext()) { + String[] list = iterator.next(); + if (Arrays.equals(list, techs)) { + iterator.remove(); + } } + } - if (messages.length > 1) { - Log.wtf(TAG, "Expected one ndefMessage but found " + messages.length); + private void removeIntentFilter(String mimeType) { + Iterator iterator = intentFilters.iterator(); + while (iterator.hasNext()) { + IntentFilter intentFilter = iterator.next(); + String mt = intentFilter.getDataType(0); + if (mimeType.equals(mt)) { + iterator.remove(); + } } + } - } catch (JSONException e) { - // shouldn't happen - Log.e(Util.TAG, "Failed to convert ndefMessage into json", e); - } + private IntentFilter createIntentFilter(String mimeType) throws MalformedMimeTypeException { + IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); + intentFilter.addDataType(mimeType); + return intentFilter; } - return json; - } - private boolean recycledIntent() { // TODO this is a kludge, find real solution + private PendingIntent getPendingIntent() { + return pendingIntent; + } - int flags = getIntent().getFlags(); - if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) { - Log.i(TAG, "Launched from history, killing recycled intent"); - setIntent(new Intent()); - return true; + private IntentFilter[] getIntentFilters() { + return intentFilters.toArray(new IntentFilter[intentFilters.size()]); } - return false; - } - - @Override - public void onPause(boolean multitasking) { - Log.d(TAG, "onPause " + getIntent()); - super.onPause(multitasking); - if (multitasking) { - // nfc can't run in background - stopNfc(); + + private String[][] getTechLists() { + //noinspection ToArrayCallWithZeroLengthArrayArgument + return techLists.toArray(new String[0][0]); } - } - - @Override - public void onResume(boolean multitasking) { - Log.d(TAG, "onResume " + getIntent()); - super.onResume(multitasking); - startNfc(); - } - - @Override - public void onNewIntent(Intent intent) { - Log.d(TAG, "onNewIntent " + intent); - super.onNewIntent(intent); - setIntent(intent); - savedIntent = intent; - parseMessage(); - } - - private Activity getActivity() { - return this.cordova.getActivity(); - } - - private Intent getIntent() { - return getActivity().getIntent(); - } - - 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); + + private void parseMessage() { + cordova.getThreadPool().execute(() -> { + Log.d(TAG, "parseMessage " + getIntent()); + Intent intent = getIntent(); + String action = intent.getAction(); + Log.d(TAG, "action " + action); + if (action == null) { + return; + } + + 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); + fireNdefEvent(NDEF_MIME, ndef, messages); + + } else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { + for (String tagTech : tag.getTechList()) { + Log.d(TAG, tagTech); + if (tagTech.equals(NdefFormatable.class.getName())) { + fireNdefFormatableEvent(tag); + } else if (tagTech.equals(Ndef.class.getName())) { // + Ndef ndef = Ndef.get(tag); + fireNdefEvent(NDEF, ndef, messages); + } + } + } + + if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { + fireTagEvent(tag, messages); + } + + setIntent(new Intent()); + }); } - } - - /** - * Enable I/O operations to the tag from this TagTechnology object. - * * - * - * @param tech TagTechnology class name e.g. 'android.nfc.tech.IsoDep' or 'android.nfc.tech.NfcV' - * @param timeout tag timeout - * @param callbackContext Cordova callback context - */ - private void connect(final String tech, final int timeout, final CallbackContext callbackContext) { - this.cordova.getThreadPool().execute(() -> { - try { - - Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag == null && savedIntent != null) { - tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + // Send the event data through a channel so the JavaScript side can fire the event + private void sendEvent(String type, JSONObject tag) { + + try { + JSONObject event = new JSONObject(); + event.put("type", type); // TAG_DEFAULT, NDEF, NDEF_MIME, NDEF_FORMATABLE + event.put("tag", tag); // JSON representing the NFC tag and NDEF messages + + PluginResult result = new PluginResult(PluginResult.Status.OK, event); + result.setKeepCallback(true); + + if (channelCallback != null) { + channelCallback.sendPluginResult(result); + } else { + postponedPluginResult = new PostponedPluginResult(new Date(), result); + } + } catch (JSONException e) { + Log.e(TAG, "Error sending NFC event through the channel", e); } - if (tag == null) { - Log.e(TAG, "No Tag"); - callbackContext.error("No Tag"); - return; + } + + private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) { + JSONObject json = buildNdefJSON(ndef, messages); + sendEvent(type, json); + } + + private void fireNdefFormatableEvent(Tag tag) { + sendEvent(NDEF_FORMATABLE, Util.tagToJSON(tag, null)); + } + + private void fireTagEvent(Tag tag, Parcelable[] messages) { + sendEvent(TAG_DEFAULT, Util.tagToJSON(tag, messages)); + } + + private JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) { + + JSONObject json = Util.ndefToJSON(ndef); + + // ndef is null for peer-to-peer + // ndef and messages are null for ndef format-able + if (ndef == null && messages != null) { + + try { + + if (messages.length > 0) { + NdefMessage message = (NdefMessage) messages[0]; + json.put("ndefMessage", Util.messageToJSON(message)); + // guessing type, would prefer a more definitive way to determine type + json.put("type", "NDEF Push Protocol"); + } + + if (messages.length > 1) { + Log.wtf(TAG, "Expected one ndefMessage but found " + messages.length); + } + + } catch (JSONException e) { + // shouldn't happen + Log.e(Util.TAG, "Failed to convert ndefMessage into json", e); + } } + return json; + } + + private boolean recycledIntent() { // TODO this is a kludge, find real solution - JSONObject resultObject = new JSONObject(); - - // get technologies supported by this tag - List techList = Arrays.asList(tag.getTechList()); - if (techList.contains(tech)) { - // use reflection to call the static function Tech.get(tag) - tagTechnologyClass = Class.forName(tech); - Method method = tagTechnologyClass.getMethod("get", Tag.class); - tagTechnology = (TagTechnology) method.invoke(null, tag); - - // If the tech supports it, return maxTransceiveLength and return it to the user - try { - Method maxTransceiveLengthMethod = tagTechnologyClass.getMethod("getMaxTransceiveLength"); - resultObject.put("maxTransceiveLength", maxTransceiveLengthMethod.invoke(tagTechnology)); - } catch (NoSuchMethodException e) { - // Some technologies do not support this, so just ignore. - } catch (JSONException e) { - Log.e(TAG, "Error serializing JSON", e); - } + int flags = getIntent().getFlags(); + if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) { + Log.i(TAG, "Launched from history, killing recycled intent"); + setIntent(new Intent()); + return true; } + return false; + } - if (tagTechnology == null) { - callbackContext.error("Tag does not support " + tech); - return; + @Override + public void onPause(boolean multitasking) { + Log.d(TAG, "onPause " + getIntent()); + super.onPause(multitasking); + if (multitasking) { + // nfc can't run in background + stopNfc(); } + } - tagTechnology.connect(); - setTimeout(timeout); - callbackContext.success(resultObject); - - } catch (IOException ex) { - Log.e(TAG, "Tag connection failed", ex); - callbackContext.error("Tag connection failed"); - - // Users should never get these reflection errors - } catch (ClassNotFoundException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (NoSuchMethodException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (IllegalAccessException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (InvocationTargetException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } - }); - } - - // Call tagTech setTimeout with reflection or fail silently - private void setTimeout(int timeout) { - if (timeout < 0) { - return; + @Override + public void onResume(boolean multitasking) { + Log.d(TAG, "onResume " + getIntent()); + super.onResume(multitasking); + startNfc(); } - try { - Method setTimeout = tagTechnologyClass.getMethod("setTimeout", int.class); - setTimeout.invoke(tagTechnology, timeout); - } catch (NoSuchMethodException e) { - // ignore - } catch (IllegalAccessException e) { - // ignore - } catch (InvocationTargetException e) { - // ignore + + @Override + public void onNewIntent(Intent intent) { + Log.d(TAG, "onNewIntent " + intent); + super.onNewIntent(intent); + setIntent(intent); + savedIntent = intent; + parseMessage(); } - } - - /** - * Disable I/O operations to the tag from this TagTechnology object, and release resources. - * - * @param callbackContext Cordova callback context - */ - private void close(CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - - if (tagTechnology != null && tagTechnology.isConnected()) { - tagTechnology.close(); - tagTechnology = null; - callbackContext.success(); - } else { - // connection already gone - callbackContext.success(); + + private Activity getActivity() { + return this.cordova.getActivity(); + } + + private Intent getIntent() { + return getActivity().getIntent(); + } + + 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); } - } catch (IOException ex) { - Log.e(TAG, "Error closing nfc connection", ex); - callbackContext.error("Error closing nfc connection " + ex.getLocalizedMessage()); - } - }); - } - - /** - * Send raw commands to the tag and receive the response. - * - * @param data byte[] command to be passed to the tag - * @param callbackContext Cordova callback context - */ - private void transceive(final byte[] data, final CallbackContext callbackContext) { - cordova.getThreadPool().execute(() -> { - try { - if (tagTechnology == null) { - Log.e(TAG, "No Tech"); - callbackContext.error("No Tech"); - return; + } + + /** + * Enable I/O operations to the tag from this TagTechnology object. + * * + * + * @param tech TagTechnology class name e.g. 'android.nfc.tech.IsoDep' or 'android.nfc.tech.NfcV' + * @param timeout tag timeout + * @param callbackContext Cordova callback context + */ + private void connect(final String tech, final int timeout, final CallbackContext callbackContext) { + this.cordova.getThreadPool().execute(() -> { + try { + + Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag == null && savedIntent != null) { + tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + } + + if (tag == null) { + Log.e(TAG, "No Tag"); + callbackContext.error("No Tag"); + return; + } + + JSONObject resultObject = new JSONObject(); + + // get technologies supported by this tag + List techList = Arrays.asList(tag.getTechList()); + if (techList.contains(tech)) { + // use reflection to call the static function Tech.get(tag) + tagTechnologyClass = Class.forName(tech); + Method method = tagTechnologyClass.getMethod("get", Tag.class); + tagTechnology = (TagTechnology) method.invoke(null, tag); + + // If the tech supports it, return maxTransceiveLength and return it to the user + try { + Method maxTransceiveLengthMethod = tagTechnologyClass.getMethod("getMaxTransceiveLength"); + resultObject.put("maxTransceiveLength", maxTransceiveLengthMethod.invoke(tagTechnology)); + } catch (NoSuchMethodException e) { + // Some technologies do not support this, so just ignore. + } catch (JSONException e) { + Log.e(TAG, "Error serializing JSON", e); + } + } + + if (tagTechnology == null) { + callbackContext.error("Tag does not support " + tech); + return; + } + + tagTechnology.connect(); + setTimeout(timeout); + callbackContext.success(resultObject); + + } catch (IOException ex) { + Log.e(TAG, "Tag connection failed", ex); + callbackContext.error("Tag connection failed"); + + // Users should never get these reflection errors + } catch (ClassNotFoundException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (NoSuchMethodException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } + }); + } + + // Call tagTech setTimeout with reflection or fail silently + private void setTimeout(int timeout) { + if (timeout < 0) { + return; } - if (!tagTechnology.isConnected()) { - Log.e(TAG, "Not connected"); - callbackContext.error("Not connected"); - return; + try { + Method setTimeout = tagTechnologyClass.getMethod("setTimeout", int.class); + setTimeout.invoke(tagTechnology, timeout); + } catch (NoSuchMethodException e) { + // ignore + } catch (IllegalAccessException e) { + // ignore + } catch (InvocationTargetException e) { + // ignore } + } - // Use reflection so we can support many tag types - Method transceiveMethod = tagTechnologyClass.getMethod("transceive", byte[].class); - @SuppressWarnings("PrimitiveArrayArgumentToVarargsMethod") - byte[] response = (byte[]) transceiveMethod.invoke(tagTechnology, data); - - callbackContext.success(response); - - } catch (NoSuchMethodException e) { - String error = "TagTechnology " + tagTechnologyClass.getName() + " does not have a transceive function"; - Log.e(TAG, error, e); - callbackContext.error(error); - } catch (NullPointerException e) { - // This can happen if the tag has been closed while we're still working with it from the thread pool. - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (IllegalAccessException e) { - Log.e(TAG, e.getMessage(), e); - callbackContext.error(e.getMessage()); - } catch (InvocationTargetException e) { - Log.e(TAG, e.getMessage(), e); - Throwable cause = e.getCause(); - callbackContext.error(cause.getMessage()); - } - }); - } + /** + * Disable I/O operations to the tag from this TagTechnology object, and release resources. + * + * @param callbackContext Cordova callback context + */ + private void close(CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + + if (tagTechnology != null && tagTechnology.isConnected()) { + tagTechnology.close(); + tagTechnology = null; + callbackContext.success(); + } else { + // connection already gone + callbackContext.success(); + } + + } catch (IOException ex) { + Log.e(TAG, "Error closing nfc connection", ex); + callbackContext.error("Error closing nfc connection " + ex.getLocalizedMessage()); + } + }); + } + + /** + * Send raw commands to the tag and receive the response. + * + * @param data byte[] command to be passed to the tag + * @param callbackContext Cordova callback context + */ + private void transceive(final byte[] data, final CallbackContext callbackContext) { + cordova.getThreadPool().execute(() -> { + try { + if (tagTechnology == null) { + Log.e(TAG, "No Tech"); + callbackContext.error("No Tech"); + return; + } + if (!tagTechnology.isConnected()) { + Log.e(TAG, "Not connected"); + callbackContext.error("Not connected"); + return; + } + + // Use reflection so we can support many tag types + Method transceiveMethod = tagTechnologyClass.getMethod("transceive", byte[].class); + @SuppressWarnings("PrimitiveArrayArgumentToVarargsMethod") + byte[] response = (byte[]) transceiveMethod.invoke(tagTechnology, data); + + callbackContext.success(response); + + } catch (NoSuchMethodException e) { + String error = "TagTechnology " + tagTechnologyClass.getName() + " does not have a transceive function"; + Log.e(TAG, error, e); + callbackContext.error(error); + } catch (NullPointerException e) { + // This can happen if the tag has been closed while we're still working with it from the thread pool. + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(TAG, e.getMessage(), e); + callbackContext.error(e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(TAG, e.getMessage(), e); + Throwable cause = e.getCause(); + callbackContext.error(cause.getMessage()); + } + }); + } } diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java index 3625a4b6..350ebd15 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/Util.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/Util.java @@ -16,130 +16,130 @@ public class Util { - static final String TAG = "NfcPlugin"; - - static JSONObject ndefToJSON(Ndef ndef) { - JSONObject json = new JSONObject(); + static final String TAG = "NfcPlugin"; + + static JSONObject ndefToJSON(Ndef ndef) { + JSONObject json = new JSONObject(); + + if (ndef != null) { + try { + + Tag tag = ndef.getTag(); + // tag is going to be null for NDEF_FORMATABLE until NfcUtil.parseMessage is refactored + if (tag != null) { + json.put("id", byteArrayToJSON(tag.getId())); + json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); + } + + json.put("type", translateType(ndef.getType())); + json.put("maxSize", ndef.getMaxSize()); + json.put("isWritable", ndef.isWritable()); + json.put("ndefMessage", messageToJSON(ndef.getCachedNdefMessage())); + // Workaround for bug in ICS (Android 4.0 and 4.0.1) where + // mTag.getTagService(); of the Ndef object sometimes returns null + // see http://issues.mroland.at/index.php?do=details&task_id=47 + try { + json.put("canMakeReadOnly", ndef.canMakeReadOnly()); + } catch (NullPointerException e) { + json.put("canMakeReadOnly", JSONObject.NULL); + } + } catch (JSONException e) { + Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e); + } + } + return json; + } - if (ndef != null) { - try { + static JSONObject tagToJSON(Tag tag, Parcelable[] messages) { + JSONObject json = new JSONObject(); - Tag tag = ndef.getTag(); - // tag is going to be null for NDEF_FORMATABLE until NfcUtil.parseMessage is refactored if (tag != null) { - json.put("id", byteArrayToJSON(tag.getId())); - json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); + try { + json.put("id", byteArrayToJSON(tag.getId())); + json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); + + if (messages != null && messages.length > 0) { + json.put("ndefMessage", messageToJSON((NdefMessage)messages[0])); + } + } catch (JSONException e) { + Log.e(TAG, "Failed to convert tag into json: " + tag.toString(), e); + } } + return json; + } - json.put("type", translateType(ndef.getType())); - json.put("maxSize", ndef.getMaxSize()); - json.put("isWritable", ndef.isWritable()); - json.put("ndefMessage", messageToJSON(ndef.getCachedNdefMessage())); - // Workaround for bug in ICS (Android 4.0 and 4.0.1) where - // mTag.getTagService(); of the Ndef object sometimes returns null - // see http://issues.mroland.at/index.php?do=details&task_id=47 - try { - json.put("canMakeReadOnly", ndef.canMakeReadOnly()); - } catch (NullPointerException e) { - json.put("canMakeReadOnly", JSONObject.NULL); + static String translateType(String type) { + String translation; + if (type.equals(Ndef.NFC_FORUM_TYPE_1)) { + translation = "NFC Forum Type 1"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_2)) { + translation = "NFC Forum Type 2"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_3)) { + translation = "NFC Forum Type 3"; + } else if (type.equals(Ndef.NFC_FORUM_TYPE_4)) { + translation = "NFC Forum Type 4"; + } else { + translation = type; } - } catch (JSONException e) { - Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e); - } + return translation; } - return json; - } - static JSONObject tagToJSON(Tag tag, Parcelable[] messages) { - JSONObject json = new JSONObject(); - - if (tag != null) { - try { - json.put("id", byteArrayToJSON(tag.getId())); - json.put("techTypes", new JSONArray(Arrays.asList(tag.getTechList()))); - - if (messages != null && messages.length > 0) { - json.put("ndefMessage", messageToJSON((NdefMessage)messages[0])); + static NdefRecord[] jsonToNdefRecords(String ndefMessageAsJSON) throws JSONException { + JSONArray jsonRecords = new JSONArray(ndefMessageAsJSON); + NdefRecord[] records = new NdefRecord[jsonRecords.length()]; + for (int i = 0; i < jsonRecords.length(); i++) { + JSONObject record = jsonRecords.getJSONObject(i); + byte tnf = (byte) record.getInt("tnf"); + byte[] type = jsonToByteArray(record.getJSONArray("type")); + byte[] id = jsonToByteArray(record.getJSONArray("id")); + byte[] payload = jsonToByteArray(record.getJSONArray("payload")); + records[i] = new NdefRecord(tnf, type, id, payload); } - } catch (JSONException e) { - Log.e(TAG, "Failed to convert tag into json: " + tag.toString(), e); - } - } - return json; - } - - static String translateType(String type) { - String translation; - if (type.equals(Ndef.NFC_FORUM_TYPE_1)) { - translation = "NFC Forum Type 1"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_2)) { - translation = "NFC Forum Type 2"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_3)) { - translation = "NFC Forum Type 3"; - } else if (type.equals(Ndef.NFC_FORUM_TYPE_4)) { - translation = "NFC Forum Type 4"; - } else { - translation = type; - } - return translation; - } - - static NdefRecord[] jsonToNdefRecords(String ndefMessageAsJSON) throws JSONException { - JSONArray jsonRecords = new JSONArray(ndefMessageAsJSON); - NdefRecord[] records = new NdefRecord[jsonRecords.length()]; - for (int i = 0; i < jsonRecords.length(); i++) { - JSONObject record = jsonRecords.getJSONObject(i); - byte tnf = (byte) record.getInt("tnf"); - byte[] type = jsonToByteArray(record.getJSONArray("type")); - byte[] id = jsonToByteArray(record.getJSONArray("id")); - byte[] payload = jsonToByteArray(record.getJSONArray("payload")); - records[i] = new NdefRecord(tnf, type, id, payload); + return records; } - return records; - } - static JSONArray byteArrayToJSON(byte[] bytes) { - JSONArray json = new JSONArray(); - for (byte aByte : bytes) { - json.put(aByte); + static JSONArray byteArrayToJSON(byte[] bytes) { + JSONArray json = new JSONArray(); + for (byte aByte : bytes) { + json.put(aByte); + } + return json; } - return json; - } - static byte[] jsonToByteArray(JSONArray json) throws JSONException { - byte[] b = new byte[json.length()]; - for (int i = 0; i < json.length(); i++) { - b[i] = (byte) json.getInt(i); + static byte[] jsonToByteArray(JSONArray json) throws JSONException { + byte[] b = new byte[json.length()]; + for (int i = 0; i < json.length(); i++) { + b[i] = (byte) json.getInt(i); + } + return b; } - return b; - } - static JSONArray messageToJSON(NdefMessage message) { - if (message == null) { - return null; - } + static JSONArray messageToJSON(NdefMessage message) { + if (message == null) { + return null; + } - List list = new ArrayList(); + List list = new ArrayList(); - for (NdefRecord ndefRecord : message.getRecords()) { - list.add(recordToJSON(ndefRecord)); + for (NdefRecord ndefRecord : message.getRecords()) { + list.add(recordToJSON(ndefRecord)); + } + + return new JSONArray(list); } - return new JSONArray(list); - } - - static JSONObject recordToJSON(NdefRecord record) { - JSONObject json = new JSONObject(); - try { - json.put("tnf", record.getTnf()); - json.put("type", byteArrayToJSON(record.getType())); - json.put("id", byteArrayToJSON(record.getId())); - json.put("payload", byteArrayToJSON(record.getPayload())); - } catch (JSONException e) { - //Not sure why this would happen, documentation is unclear. - Log.e(TAG, "Failed to convert ndef record into json: " + record.toString(), e); + static JSONObject recordToJSON(NdefRecord record) { + JSONObject json = new JSONObject(); + try { + json.put("tnf", record.getTnf()); + json.put("type", byteArrayToJSON(record.getType())); + json.put("id", byteArrayToJSON(record.getId())); + json.put("payload", byteArrayToJSON(record.getPayload())); + } catch (JSONException e) { + //Not sure why this would happen, documentation is unclear. + Log.e(TAG, "Failed to convert ndef record into json: " + record.toString(), e); + } + return json; } - return json; - } } From 382f37fc5f2a08bfdabd863ce91854f6929e2631 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Mon, 29 Jul 2024 21:52:03 +0200 Subject: [PATCH 7/7] fix: add missing param of method tagToJSON --- .../src/com/chariotsolutions/nfc/plugin/NfcPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java index 39dec0a1..6233776e 100644 --- a/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java +++ b/src/android/src/com/chariotsolutions/nfc/plugin/NfcPlugin.java @@ -342,10 +342,10 @@ private void parseLaunchIntent(final CallbackContext callbackContext) { if (ndef != null) { callbackContext.success(buildNdefJSON(ndef, messages)); } else { - callbackContext.success(Util.tagToJSON(tag)); + callbackContext.success(Util.tagToJSON(tag, messages)); } } else if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { - callbackContext.success(Util.tagToJSON(tag)); + callbackContext.success(Util.tagToJSON(tag, messages)); } else { if (data != null) { callbackContext.success(data);