11package org .stereolux .cordova .serial ;
22
33import java .io .IOException ;
4+ import java .util .concurrent .ExecutorService ;
5+ import java .util .concurrent .Executors ;
46import java .nio .ByteBuffer ;
57import java .util .List ;
68
1921import android .hardware .usb .UsbDeviceConnection ;
2022import android .hardware .usb .UsbManager ;
2123import android .util .Log ;
24+ import android .util .Base64 ;
2225
2326import com .hoho .android .usbserial .driver .UsbSerialDriver ;
2427import com .hoho .android .usbserial .driver .UsbSerialPort ;
2528import com .hoho .android .usbserial .driver .UsbSerialProber ;
2629
30+ import com .hoho .android .usbserial .util .SerialInputOutputManager ;
31+ import com .hoho .android .usbserial .util .HexDump ;
32+
33+
2734/**
2835 * Cordova plugin to communicate with the android serial port
2936 * @author Xavier Seignard <[email protected] > @@ -37,6 +44,8 @@ public class Serial extends CordovaPlugin {
3744 private static final String ACTION_READ = "readSerial" ;
3845 private static final String ACTION_WRITE = "writeSerial" ;
3946 private static final String ACTION_CLOSE = "closeSerial" ;
47+ private static final String ACTION_READ_CALLBACK = "registerReadCallback" ;
48+
4049 // UsbManager instance to deal with permission and opening
4150 private UsbManager manager ;
4251 // The current driver that handle the serial port
@@ -49,6 +58,37 @@ public class Serial extends CordovaPlugin {
4958
5059 private final ByteBuffer mReadBuffer = ByteBuffer .allocate (BUFSIZ );
5160
61+ // Connection info
62+ int baudRate ;
63+ int dataBits ;
64+ int stopBits ;
65+ int parity ;
66+ boolean setDTR ;
67+
68+ boolean portPaused ;
69+
70+ private CallbackContext readCallback ;
71+
72+
73+ private final ExecutorService mExecutor = Executors .newSingleThreadExecutor ();
74+ private SerialInputOutputManager mSerialIoManager ;
75+
76+ private final SerialInputOutputManager .Listener mListener =
77+ new SerialInputOutputManager .Listener () {
78+
79+ @ Override
80+ public void onRunError (Exception e ) {
81+ Log .d (TAG , "Runner stopped." );
82+ }
83+
84+ @ Override
85+ public void onNewData (final byte [] data ) {
86+ Serial .this .updateReceivedData (data );
87+ }
88+ };
89+
90+
91+
5292 /**
5393 * Overridden execute method
5494 * @param action the string representation of the action to execute
@@ -88,6 +128,11 @@ else if (ACTION_CLOSE.equals(action)) {
88128 closeSerial (callbackContext );
89129 return true ;
90130 }
131+ // Register read callback
132+ else if (ACTION_READ_CALLBACK .equals (action )) {
133+ registerReadCallback (callbackContext );
134+ return true ;
135+ }
91136 // the action doesn't exist
92137 return false ;
93138 }
@@ -127,29 +172,35 @@ public void run() {
127172 });
128173 }
129174
175+
130176 /**
131- * Open the serial port
177+ * Open the serial port from Cordova
132178 * @param opts a {@link JSONObject} containing the connection paramters
133179 * @param callbackContext the cordova {@link CallbackContext}
134180 */
135181 private void openSerial (final JSONObject opts , final CallbackContext callbackContext ) {
182+
136183 cordova .getThreadPool ().execute (new Runnable () {
137184 public void run () {
185+
138186 UsbDeviceConnection connection = manager .openDevice (driver .getDevice ());
187+
139188 if (connection != null ) {
140189 // get first port and open it
141190 port = driver .getPorts ().get (0 );
191+
142192 try {
143- port . open ( connection );
193+
144194 // get connection params or the default values
145- int baudRate = opts .has ("baudRate" ) ? opts .getInt ("baudRate" ) : 9600 ;
146- int dataBits = opts .has ("dataBits" ) ? opts .getInt ("dataBits" ) : UsbSerialPort .DATABITS_8 ;
147- int stopBits = opts .has ("stopBits" ) ? opts .getInt ("stopBits" ) : UsbSerialPort .STOPBITS_1 ;
148- int parity = opts .has ("parity" ) ? opts .getInt ("parity" ) : UsbSerialPort .PARITY_NONE ;
195+ baudRate = opts .has ("baudRate" ) ? opts .getInt ("baudRate" ) : 9600 ;
196+ dataBits = opts .has ("dataBits" ) ? opts .getInt ("dataBits" ) : UsbSerialPort .DATABITS_8 ;
197+ stopBits = opts .has ("stopBits" ) ? opts .getInt ("stopBits" ) : UsbSerialPort .STOPBITS_1 ;
198+ parity = opts .has ("parity" ) ? opts .getInt ("parity" ) : UsbSerialPort .PARITY_NONE ;
199+ setDTR = opts .has ("dtr" ) && opts .getBoolean ("dtr" );
200+
201+ port .open (connection );
149202 port .setParameters (baudRate , dataBits , stopBits , parity );
150- if (opts .has ("dtr" ) && opts .getBoolean ("dtr" )) {
151- port .setDTR (true );
152- }
203+ if (setDTR ) port .setDTR (true );
153204 }
154205 catch (IOException e ) {
155206 // deal with error
@@ -161,13 +212,15 @@ public void run() {
161212 Log .d (TAG , e .getMessage ());
162213 callbackContext .error (e .getMessage ());
163214 }
215+
164216 Log .d (TAG , "Serial port opened!" );
165217 callbackContext .success ("Serial port opened!" );
166218 }
167219 else {
168220 Log .d (TAG , "Cannot connect to the device!" );
169221 callbackContext .error ("Cannot connect to the device!" );
170222 }
223+ onDeviceStateChange ();
171224 }
172225 });
173226 }
@@ -254,7 +307,174 @@ public void run() {
254307 Log .d (TAG , e .getMessage ());
255308 callbackContext .error (e .getMessage ());
256309 }
310+ onDeviceStateChange ();
257311 }
258312 });
259313 }
314+
315+
316+ private void stopIoManager () {
317+ if (mSerialIoManager != null ) {
318+ Log .i (TAG , "Stopping io manager .." );
319+ mSerialIoManager .stop ();
320+ mSerialIoManager = null ;
321+ }
322+ }
323+
324+ private void startIoManager () {
325+ if (driver != null ) {
326+ Log .i (TAG , "Starting io manager .." );
327+ mSerialIoManager = new SerialInputOutputManager (port , mListener );
328+ mExecutor .submit (mSerialIoManager );
329+ }
330+ }
331+
332+ private void onDeviceStateChange () {
333+ stopIoManager ();
334+ startIoManager ();
335+ }
336+
337+ /**
338+ * Dispatch read data to javascript
339+ * @param data
340+ */
341+ private void updateReceivedData (byte [] data ) {
342+
343+ if ( readCallback != null ) {
344+
345+ // final String message = "Read " + data.length + " bytes: \n" + HexDump.dumpHexString(data) + "\n\n";
346+ // Log.d(TAG, message);
347+
348+ JSONObject returnObj = new JSONObject ();
349+ addProperty (returnObj , "length" , data .length );
350+ addPropertyBytes (returnObj , "data" , data );
351+
352+ PluginResult result = new PluginResult (PluginResult .Status .OK , returnObj );
353+ result .setKeepCallback (true );
354+ readCallback .sendPluginResult (result );
355+ }
356+
357+ }
358+
359+
360+ /**
361+ * Register callback for read data
362+ * @param callbackContext the cordova {@link CallbackContext}
363+ */
364+ private void registerReadCallback (final CallbackContext callbackContext ) {
365+
366+ Log .d (TAG , "Registering Callback" );
367+
368+
369+ cordova .getThreadPool ().execute (new Runnable () {
370+ public void run () {
371+
372+ Log .d (TAG , "Registering Read Callback" );
373+ readCallback = callbackContext ;
374+
375+ JSONObject returnObj = new JSONObject ();
376+ addProperty (returnObj , "registerReadCallback" , "true" );
377+
378+ //Keep the callback
379+ PluginResult pluginResult = new PluginResult (PluginResult .Status .OK , returnObj );
380+ pluginResult .setKeepCallback (true );
381+ callbackContext .sendPluginResult (pluginResult );
382+
383+ }
384+ });
385+
386+ }
387+
388+
389+
390+ @ Override
391+ public void onPause (boolean multitasking ) {
392+
393+ stopIoManager ();
394+ if (port != null ) {
395+ try {
396+ port .close ();
397+ } catch (IOException e ) {
398+ // Ignore.
399+ }
400+ portPaused = true ;
401+ port = null ;
402+ }
403+
404+ }
405+
406+ @ Override
407+ public void onResume (boolean multitasking ) {
408+
409+ Log .d (TAG , "Resumed, driver=" + driver );
410+ if (driver == null && portPaused ) {
411+ Log .d (TAG , "No serial device to resume." );
412+ } else {
413+
414+ UsbDeviceConnection connection = manager .openDevice (driver .getDevice ());
415+
416+ if (connection != null ) {
417+ // get first port and open it
418+ port = driver .getPorts ().get (0 );
419+
420+ try {
421+
422+ port .open (connection );
423+ port .setParameters (baudRate , dataBits , stopBits , parity );
424+ if (setDTR ) port .setDTR (true );
425+ }
426+ catch (IOException e ) {
427+ // deal with error
428+ Log .d (TAG , e .getMessage ());
429+ }
430+ Log .d (TAG , "Serial port opened!" );
431+ }
432+ else {
433+ Log .d (TAG , "Cannot connect to the device!" );
434+ }
435+
436+ Log .d (TAG , "Serial device: " + driver .getClass ().getSimpleName ());
437+ }
438+ onDeviceStateChange ();
439+ }
440+
441+
442+
443+ /**
444+ * The final call you receive before your activity is destroyed.
445+ */
446+ @ Override
447+ public void onDestroy () {
448+
449+ Log .d (TAG , "Destroy, port=" + port );
450+
451+ if (port != null )
452+ try {
453+ port .close ();
454+ }
455+ catch (IOException e ) {
456+ Log .d (TAG , e .getMessage ());
457+ }
458+
459+ onDeviceStateChange ();
460+
461+ }
462+
463+
464+ private void addProperty (JSONObject obj , String key , Object value )
465+ {
466+ try
467+ {
468+ obj .put (key , value );
469+ }
470+ catch (JSONException e ){}
471+ }
472+
473+ private void addPropertyBytes (JSONObject obj , String key , byte [] bytes )
474+ {
475+ String string = Base64 .encodeToString (bytes , Base64 .NO_WRAP );
476+ addProperty (obj , key , string );
477+ }
478+
479+
260480}
0 commit comments