community-cordova-plugin-nfc 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,341 @@
1
+ package com.chariotsolutions.nfc.plugin;
2
+
3
+ import net.rim.device.api.i18n.MessageFormat;
4
+ import net.rim.device.api.io.nfc.NFCException;
5
+ import net.rim.device.api.io.nfc.emulation.VirtualNDEFTag;
6
+ import net.rim.device.api.io.nfc.ndef.NDEFMessage;
7
+ import net.rim.device.api.io.nfc.ndef.NDEFMessageListener;
8
+ import net.rim.device.api.io.nfc.ndef.NDEFRecord;
9
+ import net.rim.device.api.io.nfc.ndef.NDEFTagConnection;
10
+ import net.rim.device.api.io.nfc.readerwriter.*;
11
+ import net.rim.device.api.system.ControlledAccessException;
12
+ import org.apache.cordova.api.Plugin;
13
+ import org.apache.cordova.api.PluginResult;
14
+ import org.apache.cordova.api.PluginResult.Status;
15
+ import org.apache.cordova.json4j.JSONObject;
16
+ import org.apache.cordova.json4j.JSONArray;
17
+ import org.apache.cordova.json4j.JSONException;
18
+ import org.apache.cordova.util.Logger;
19
+
20
+ import javax.microedition.io.Connector;
21
+ import java.io.IOException;
22
+ import java.util.Hashtable;
23
+
24
+ // http://docs.blackberry.com/en/developers/deliverables/34480/Near_Field_Communication_1631111_11.jsp
25
+
26
+ // Bug with ControlledAccessException addDetectionListener
27
+ // http://supportforums.blackberry.com/t5/Java-Development/Listener-belongs-to-another-application-module/m-p/1333559#M176142
28
+
29
+ // TODO allow error detection listeners
30
+ // TODO should add new JS API to specify just the types we want
31
+ // nfc.addDetectionListener(ndefListener, new int[]{...});
32
+ // Target.NDEF_TAG, Target.ISO_14443_4 and Target.ISO_14443_3.
33
+
34
+ // TODO allow TAG LISTENER TO WRITE
35
+ public class NfcPlugin extends Plugin {
36
+
37
+ private static final String TAG = "NfcPlugin: ";
38
+
39
+ // supported actions
40
+ private static final String REGISTER_MIME_TYPE = "registerMimeType";
41
+ private static final String REGISTER_NDEF = "registerNdef";
42
+ // private static final String REGISTER_NDEF_FORMATABLE = "registerNdefFormatable";
43
+ private static final String REGISTER_DEFAULT_TAG = "registerTag";
44
+
45
+ private static final String WRITE_TAG = "writeTag";
46
+ private static final String ERASE_TAG = "eraseTag";
47
+
48
+ private static final String SHARE_TAG = "shareTag";
49
+ private static final String UNSHARE_TAG = "unshareTag";
50
+ private static final String INIT = "init";
51
+
52
+ private static final String REMOVE_MIME_TYPE = "removeMimeType";
53
+ private static final String REMOVE_NDEF = "removeNdef";
54
+ private static final String REMOVE_DEFAULT_TAG = "removeTag";
55
+
56
+ // event types
57
+ private static final String NDEF = "ndef";
58
+ private static final String NDEF_MIME = "ndef-mime";
59
+ // private static final String NDEF_FORMATABLE = "ndef-formatable";
60
+ private static final String TAG_DEFAULT = "tag";
61
+
62
+ private TagWritingListener ndefListener;
63
+ private DetectionListener tagListener;
64
+ private VirtualNDEFTag virtualTag;
65
+
66
+ private int WAIT_FOR_WRITE_MILLIS = 3000;
67
+
68
+ /**
69
+ * Executes the request and returns PluginResult.
70
+ *
71
+ * @param action
72
+ * The action to execute.
73
+ * @param args
74
+ * JSONArray of arguments for the plugin.
75
+ * @param callbackId
76
+ * The callback id used when calling back into JavaScript.
77
+ * @return A PluginResult object with a status and message.
78
+ */
79
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
80
+ Logger.debug(action + " " + args.toString());
81
+ PluginResult result;
82
+
83
+ try {
84
+ if(INIT.equals(action)) {
85
+ Logger.debug(TAG + " Enabling plugin");
86
+
87
+ if (args.length() > 0) {
88
+ WAIT_FOR_WRITE_MILLIS = args.getInt(0);
89
+ }
90
+
91
+ return new PluginResult(Status.OK);
92
+
93
+ } else if (REGISTER_MIME_TYPE.equals(action)) {
94
+ result = registerMimeListener(args);
95
+
96
+ } else if (REMOVE_MIME_TYPE.equals(action)) {
97
+ result = removeMimeListener(args);
98
+
99
+ } else if (REGISTER_NDEF.equals(action)) {
100
+ result = registerNdefListener();
101
+
102
+ } else if (REMOVE_NDEF.equals(action)) {
103
+ result = removeNdefListener();
104
+
105
+ } else if (REGISTER_DEFAULT_TAG.equals(action)) {
106
+ result = registerTagListener();
107
+
108
+ } else if (REMOVE_DEFAULT_TAG.equals(action)) {
109
+ result = removeTagListener();
110
+
111
+ } else if (WRITE_TAG.equals(action)) {
112
+ result = writeTag(args);
113
+
114
+ } else if (ERASE_TAG.equals(action)) {
115
+ result = eraseTag();
116
+
117
+ } else if (SHARE_TAG.equals(action)) {
118
+ result = shareTag(args);
119
+
120
+ } else if (UNSHARE_TAG.equals(action)) {
121
+ result = unshareTag();
122
+
123
+ } else {
124
+ result = new PluginResult(Status.INVALID_ACTION, TAG + "Invalid action: " + action);
125
+ }
126
+ } catch(NFCException e) {
127
+ Logger.err(e.toString(), e);
128
+ return new PluginResult(Status.ERROR, e.toString() + " action=" + action);
129
+
130
+ } catch(JSONException e) {
131
+ Logger.err(e.toString(), e);
132
+ return new PluginResult(Status.ERROR, e.toString() + " action=" + action);
133
+
134
+ } catch(ControlledAccessException e) {
135
+ // User didn't allow NFC
136
+ Logger.err(e.toString(), e);
137
+ return new PluginResult(Status.ERROR, e.toString() + " action=" + action);
138
+ } catch(SecurityException e) {
139
+ // IT policy doesn't allow NFC
140
+ Logger.err(e.toString(), e);
141
+ return new PluginResult(Status.ERROR, e.toString() + " action=" + action);
142
+ }
143
+
144
+ return result;
145
+ }
146
+
147
+ private PluginResult registerMimeListener(JSONArray args) throws NFCException, JSONException {
148
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
149
+ String mimeType = args.getString(0);
150
+
151
+ NDEFMessageListener listener = new NDEFMessageListener() {
152
+ public void onNDEFMessageDetected(final NDEFMessage msg)
153
+ {
154
+ NfcPlugin.this.fireNdefEvent(NDEF_MIME, msg, null);
155
+ }
156
+ };
157
+
158
+ nfc.addNDEFMessageListener(listener, NDEFRecord.TNF_MEDIA, mimeType, true);
159
+ return new PluginResult(Status.OK);
160
+ }
161
+
162
+ private PluginResult removeMimeListener(JSONArray args) throws NFCException, JSONException {
163
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
164
+ String mimeType = args.getString(0);
165
+ nfc.removeNDEFMessageListener(NDEFRecord.TNF_MEDIA, mimeType);
166
+ return new PluginResult(Status.OK);
167
+ }
168
+
169
+ private PluginResult registerNdefListener() throws NFCException, JSONException {
170
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
171
+ ndefListener = new TagWritingListener();
172
+ nfc.addDetectionListener(ndefListener, new int[]{Target.NDEF_TAG});
173
+ return new PluginResult(Status.OK);
174
+ }
175
+
176
+ private PluginResult removeNdefListener() throws NFCException, JSONException {
177
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
178
+ nfc.removeDetectionListener(ndefListener);
179
+ return new PluginResult(Status.OK);
180
+ }
181
+
182
+ private PluginResult registerTagListener() throws NFCException, JSONException {
183
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
184
+
185
+ tagListener = new DetectionListener() {
186
+
187
+ public void onTargetDetected(Target target) {
188
+
189
+ Hashtable props = Util.getTagProperties(target);
190
+
191
+ NDEFMessage message = null;
192
+ try {
193
+ NDEFTagConnection tagConnection = (NDEFTagConnection) Connector.open(target.getUri(Target.NDEF_TAG));
194
+ message = tagConnection.read(); // might want to handle NFCException different
195
+ } catch (IOException e) {
196
+ Logger.error("Failed reading tag " + e.toString());
197
+ }
198
+
199
+ fireNdefEvent(TAG_DEFAULT, message, props);
200
+
201
+ }
202
+ };
203
+ nfc.addDetectionListener(tagListener);
204
+ return new PluginResult(Status.OK);
205
+ }
206
+
207
+ private PluginResult removeTagListener() throws NFCException, JSONException {
208
+ ReaderWriterManager nfc = ReaderWriterManager.getInstance();
209
+ nfc.removeDetectionListener(tagListener);
210
+ return new PluginResult(Status.OK);
211
+ }
212
+
213
+ private PluginResult writeTag(JSONArray args) throws NFCException, JSONException {
214
+
215
+ NDEFMessage message = Util.jsonToNdefMessage(args.getString(0));
216
+
217
+ try {
218
+ if (ndefListener != null) {
219
+ ndefListener.write(message);
220
+ } else {
221
+ return new PluginResult(Status.IO_EXCEPTION, "Tag Write Failed (Lost Tag)");
222
+ }
223
+ } catch (TagLockedException e) {
224
+ Logger.debug("Tag is locked");
225
+ return new PluginResult(Status.ERROR, e.getMessage());
226
+ } catch (NotEnoughSpaceException e) {
227
+ Logger.debug("Tag capacity exceeded");
228
+ return new PluginResult(Status.ERROR, e.getMessage());
229
+ } catch (NFCException e) {
230
+ Logger.debug("Error writing tag");
231
+ return new PluginResult(Status.ERROR, e.getMessage());
232
+ } catch (IOException e) {
233
+ Logger.debug("Error connecting to tag");
234
+ return new PluginResult(Status.ERROR, e.getMessage());
235
+ }
236
+ return new PluginResult(Status.OK);
237
+ }
238
+
239
+ private PluginResult eraseTag() throws NFCException {
240
+
241
+ try {
242
+ if (ndefListener != null) {
243
+ ndefListener.erase();
244
+ } else {
245
+ return new PluginResult(Status.IO_EXCEPTION, "Erase Failed (Lost Tag)");
246
+ }
247
+ } catch (TagLockedException e) {
248
+ Logger.debug("Tag is locked");
249
+ return new PluginResult(Status.ERROR, e.getMessage());
250
+ } catch (NFCException e) {
251
+ Logger.debug("Error writing tag");
252
+ return new PluginResult(Status.ERROR, e.getMessage());
253
+ }
254
+ return new PluginResult(Status.OK);
255
+ }
256
+
257
+ private PluginResult shareTag(JSONArray args) throws NFCException, JSONException {
258
+
259
+ NDEFMessage message = Util.jsonToNdefMessage(args.getString(0));
260
+ virtualTag = new VirtualNDEFTag(message);
261
+ virtualTag.startEmulation();
262
+ return new PluginResult(Status.OK);
263
+ }
264
+
265
+ private PluginResult unshareTag() throws NFCException {
266
+ virtualTag.stopEmulation();
267
+ return new PluginResult(Status.OK);
268
+ }
269
+
270
+ //private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) {
271
+ private void fireNdefEvent(String type, NDEFMessage message, Hashtable props) {
272
+
273
+ String javascriptTemplate =
274
+ "var e = document.createEvent(''Events'');\n" +
275
+ "e.initEvent(''{0}'');\n" +
276
+ "e.tag = {1};\n" +
277
+ "document.dispatchEvent(e);";
278
+
279
+ JSONObject jsonObject = Util.ndefToJSON(message, props);
280
+ String tag = jsonObject.toString();
281
+
282
+ Object[] args = { type, tag };
283
+
284
+ String command = MessageFormat.format(javascriptTemplate, args);
285
+ Logger.debug(command);
286
+ this.invokeScript(command);
287
+ }
288
+
289
+ // The Android code calls nfc.write() after receiving an nfcEvent
290
+ // Blackberry wants tags to be written inside the DetectionListener event handler
291
+ // http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/io/nfc/readerwriter/DetectionListener.html
292
+ //
293
+ // WARNING Huge hack: sleep the listener thread so the Plugin can call Javascript and then Javascript can call
294
+ // nfc.write before onTargetDetected completes and target is invalid.
295
+ //
296
+ // Javascript has no idea when reads fail with errors. Need to look into registering error listeners.
297
+ class TagWritingListener implements DetectionListener {
298
+
299
+ NDEFTagConnection tagConnection;
300
+ Target target;
301
+ Thread t;
302
+
303
+ public void onTargetDetected(Target target) {
304
+ this.target = target;
305
+
306
+ try {
307
+ tagConnection = (NDEFTagConnection) Connector.open(target.getUri(Target.NDEF_TAG));
308
+ NDEFMessage message = tagConnection.read();
309
+ fireNdefEvent(NDEF, message, Util.getTagProperties(target));
310
+
311
+ t = Thread.currentThread();
312
+ try {
313
+ Thread.sleep(WAIT_FOR_WRITE_MILLIS);
314
+ } catch (InterruptedException e) {
315
+ Logger.debug("Detection Listener sleep interrupted.");
316
+ }
317
+ } catch (NFCException e) {
318
+ Logger.error("Failed to read NDEF tag" + e.toString());
319
+ } catch (IOException e) {
320
+ Logger.error("Failed to connect to NDEF tag" + e.toString());
321
+ }
322
+
323
+ }
324
+
325
+ public void write(NDEFMessage message) throws IOException {
326
+
327
+ tagConnection.write(message);
328
+ t.interrupt();
329
+
330
+ }
331
+
332
+ public void erase() throws NFCException {
333
+
334
+ tagConnection.erase();
335
+ t.interrupt();
336
+
337
+ }
338
+
339
+ }
340
+
341
+ }
@@ -0,0 +1,137 @@
1
+ package com.chariotsolutions.nfc.plugin;
2
+
3
+ import net.rim.device.api.io.nfc.NFCException;
4
+ import net.rim.device.api.io.nfc.ndef.NDEFMessage;
5
+ import net.rim.device.api.io.nfc.ndef.NDEFRecord;
6
+ import net.rim.device.api.io.nfc.readerwriter.Target;
7
+ import org.apache.cordova.json4j.JSONArray;
8
+ import org.apache.cordova.json4j.JSONException;
9
+ import org.apache.cordova.json4j.JSONObject;
10
+ import org.apache.cordova.util.Logger;
11
+
12
+ import java.util.*;
13
+
14
+ public class Util {
15
+
16
+ static final String TAG = "NfcPluginUtil: ";
17
+
18
+ static JSONObject ndefToJSON(NDEFMessage message, Hashtable props) { // Blackberry doesn't have Ndef object
19
+
20
+ JSONObject json = new JSONObject();
21
+ if (props == null) { props = new Hashtable(); }
22
+
23
+ try {
24
+ // TODO consider renaming properties to match Android
25
+ for (Enumeration keys = props.keys(); keys.hasMoreElements();) {
26
+ String key = (String)keys.nextElement();
27
+ String value = (String)props.get(key);
28
+
29
+ if (key.equals("SerialNumber")) {
30
+ byte[] serialNumber = value.getBytes();
31
+ json.put(fixName(key), byteArrayToJSON(serialNumber));
32
+ } else {
33
+ if (value.equals("TRUE")) {
34
+ json.put(fixName(key), true);
35
+ } else if (value.equals("FALSE")) {
36
+ json.put(fixName(key), false);
37
+ } else {
38
+ json.put(fixName(key), value);
39
+ }
40
+ }
41
+ }
42
+
43
+ json.put("ndefMessage", messageToJSON(message));
44
+ } catch (JSONException e) {
45
+ Logger.error("Failed to convert ndef into json: " + message.toString());
46
+ }
47
+ return json;
48
+ }
49
+
50
+ static String fixName(String key) {
51
+ return key.substring(0,1).toLowerCase() + key.substring(1);
52
+ }
53
+
54
+ static NDEFMessage jsonToNdefMessage(String ndefMessageAsJSON) throws JSONException, NFCException {
55
+ NDEFMessage message = new NDEFMessage();
56
+ NDEFRecord[] records = Util.jsonToNdefRecords(ndefMessageAsJSON);
57
+ message.setRecords(records);
58
+ return message;
59
+ }
60
+
61
+ static NDEFRecord[] jsonToNdefRecords(String ndefMessageAsJSON) throws JSONException, NFCException {
62
+ Logger.log(ndefMessageAsJSON);
63
+ JSONArray jsonRecords = new JSONArray(ndefMessageAsJSON);
64
+
65
+ NDEFRecord[] records = new NDEFRecord[jsonRecords.length()];
66
+ for (int i = 0; i < jsonRecords.length(); i++) {
67
+ JSONObject record = jsonRecords.getJSONObject(i);
68
+ byte tnf = (byte) record.getInt("tnf");
69
+ byte[] type = jsonToByteArray(record.getJSONArray("type"));
70
+ byte[] id = jsonToByteArray(record.getJSONArray("id"));
71
+ byte[] payload = jsonToByteArray(record.getJSONArray("payload"));
72
+
73
+ records[i] = new NDEFRecord();
74
+ records[i].setId(new String(id));
75
+ records[i].setType(tnf, new String(type));
76
+ records[i].setPayload(payload);
77
+ }
78
+ return records;
79
+ }
80
+
81
+ static JSONArray byteArrayToJSON(byte[] bytes) {
82
+ JSONArray json = new JSONArray();
83
+ for (int i = 0; i < bytes.length; i++) {
84
+ json.put(bytes[i]);
85
+ }
86
+ return json;
87
+ }
88
+
89
+ static byte[] jsonToByteArray(JSONArray json) throws JSONException {
90
+ byte[] b = new byte[json.length()];
91
+ for (int i = 0; i < json.length(); i++) {
92
+ b[i] = (byte) json.getInt(i);
93
+ }
94
+ return b;
95
+ }
96
+
97
+ static JSONArray messageToJSON(NDEFMessage message) throws JSONException {
98
+ if (message == null) {
99
+ return null;
100
+ }
101
+
102
+ JSONArray jsonArray = new JSONArray();
103
+ NDEFRecord[] records = message.getRecords();
104
+
105
+ for (int i=0; i < records.length; i++) {
106
+ jsonArray.put(recordToJSON(records[i]));
107
+ }
108
+
109
+ return jsonArray;
110
+ }
111
+
112
+ // Blackberry's API is a little nicer using String where Android uses byte[]
113
+ // Translating stuff to byte[] so both work the same
114
+ static JSONObject recordToJSON(NDEFRecord record) {
115
+ JSONObject json = new JSONObject();
116
+ try {
117
+ json.put("tnf", record.getTypeNameFormat());
118
+ json.put("type", byteArrayToJSON(record.getType().getBytes()));
119
+ json.put("id", byteArrayToJSON(record.getId().getBytes()));
120
+ json.put("payload", byteArrayToJSON(record.getPayload()));
121
+ } catch (JSONException e) {
122
+ //Not sure why this would happen, documentation is unclear.
123
+ Logger.err(TAG + "Failed to convert ndef record into json: " + record.toString(), e);
124
+ }
125
+ return json;
126
+ }
127
+
128
+ static Hashtable getTagProperties(Target target) {
129
+ Hashtable props = new Hashtable();
130
+ Enumeration propertyNames = target.getProperties();
131
+ while (propertyNames.hasMoreElements()) {
132
+ String name = (String)propertyNames.nextElement();
133
+ props.put(name, target.getProperty(name));
134
+ }
135
+ return props;
136
+ }
137
+ }