Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member
0 Kudos

The SAP Mobile Client is a java application and it can, consequently, leverage all your existing java tricks and libraries. In the following blog, I will describe how to use the Motorala Java SDK to implement a barcode scanner event listener. This technique can be used to enable other features made available by the Motorola SDK including audio player, camera, magstrip reader, battery state reader, RFID reader etc. This applies only to mobile devices in a disconnected scenario.

Just to clarify, SAP does provide built in drivers for many functions such as the bar code scanner. There are situations, though, when the desired driver is not available and one is required to leverage an external SDK. This blog demonstrates the latter.

 

Prerequisites and assumptions

It is assumed that you are comfortable deploying sda components to a physical handheld. It is also assumed that you have access to that handheld's file system.

Software Prerequisites

-Motorola Java SDK : Please visit Motorolla’s website for the latest SDK. Version 1.3 can be found here.

 

Instructions

1) Setup NWDS to reference SDK jar file.

As is common in NWDS, add the required jar to the classpath of the component project. Note that this reference is only a design time reference.

 

2) Create Scanner

Before I describe how the scanner works, here is the code

public interface SymbolScannerListener {

            public void onScan(String data);

      }

 

public class SymbolScanner implementsScannerListener, Runnable {

            Scanner scanner;

            ScannerDevice[] devList;

 

            boolean exiting;

            boolean stopScanning;

 

            boolean scanEnabled = false;

            boolean devSelWait = false;

 

            String scanData = "";

 

            Runnable listener;

           

            public SymbolScanner(finalSymbolScannerListener listener) {

                  this.setScannerListener(listener);

            }

     

            public voidsetScannerListener(final SymbolScannerListener listener) {

                  this.listener = new Runnable() {

                        public void run() {

                              try {

                                    listener.onScan(scanData);

                              } catch(Exception e) {

                                    logger.error(this.getClass(), e.getMessage(), e);

                              }

                        }

                  };

            }

 

            public void init() {

                  // Get physical scanner device list

                  try {

                        devList = Symbol.getScannerDeviceList();

                        if (devList == null) {

                              return;

                        }

 

                  } catch(java.lang.NoClassDefFoundError e) {

                        return;

                  } catch(java.lang.UnsatisfiedLinkError e) {

                        return;

                  }

                 

                  // Create and enable scanner

                  try {

                        scanner = new Scanner(devList[0]);

                        scanner.enable();

                        scanEnabled = true;

                  } catch (Exception e) {

                        logger.error(this.getClass(), e.getMessage(), e);

                        return;

                  }

            }

           

            private synchronized void go() {

     

                  //The following code segment demonstrates how to change decoder parameters. 

                  try {

                        if (devSelWait == true)

                              wait();

                              // Configure Decoder Parameters

                              Decoder decoderUPCE0 = new Decoder(Decoder.DECODER_UPCE0, "UPCE0", scanner);

 

                              // Get the decoder parameters for UPCE0 decoder

                              DecoderParams dpUPCE0 = decoderUPCE0.getParams();

 

                              // Change specific parameters

                              ((DecoderUPCE) dpUPCE0.params).ReportCheckDigit = true;

 

                              // Set the parameters in the underlying scan driver.

                              // The next scanner read call will use these new parameters

                              decoderUPCE0.setParams(dpUPCE0);

 

                              exiting = false;

                  while (!exiting) {

                        try {

                              if (!stopScanning) {

                                    // Submit a read request with a listener that is notified when the read completed.

                                    // Use the default parameters for this sample.

                                    // Submit the read only when this is the foreground application.

                                    scanner.read(null, this);

 

                                    // Wait until the read is completed to submit another read

                                    // or the application gained the focus.

                                    try {

                                          wait();

                                    } catch (InterruptedException ex) {

                                         

                                    }

 

                              } else {

                                    Thread.sleep(1000);

                              }

 

                        } catch (Exception e) {

                              if (!exiting)

                                    scanData += "Error: " + e.getMessage();

                              return;

                        }

                  }

 

                  } catch (Exception e) {

                        logger.error(this.getClass(), e.getMessage(), e);

                  } finally {

                        this.stopScanner();

                  }

                 

     

 

            }

 

            // When a scan is completed 'readNotify' is invoked with ScanReadInfo

            // object that contains the results of the scan

            public synchronized void readNotify(ScanReadInfo result) {

 

                  if (exiting) {

                        return;

                  }

                  scanData = "";

 

                  int status = result.getStatus();

                  switch (status) {

                  case Constants.READ_SUCCESS:

                        if (result.data instanceof TextData) {

                              scanData += ((TextData) result.data).text;

                        }

                        break;

                  case Constants.READ_ABORT:

                        // Read is cancelled when application is inactivated.

                        break;

                  case Constants.READ_INCOMPATIBLE: {

                        // Other scanning application submitted an impompatible read

                        // before us.

                        stopScanning = true;

                        scanData += ("Another application is using the scanner.
");

                        break;

                  }

                  case Constants.READ_PENDING:

                        scanData += "Read pending...
";

                        break;

                  case Constants.READ_TIMEOUT:

                        scanData += "Read timed out!
";

 

                        break;

                  default:

                        scanData += "Error : " + status + "
";

                  }

 

                  try {

                        this.executeRunable(listener);

                  } catch(RuntimeException e) {

                        logger.error(this.getClass(), e.getMessage(), e);

                  }

 

                  notifyAll();

 

            }

           

            private void stopScanner(){

                  if(scanner == null) return;

                  try {

                        scanner.cancelAll();

                  } catch(Exception e) {logger.error(this.getClass(), e.getMessage(), e);}

                  try {

                        scanner.disable();

                  } catch(Exception e) {logger.error(this.getClass(), e.getMessage(), e);}

            }

 

            private voidexecuteRunable(Runnable updateUiRunnable) {

           

                  try {

                        WDLite.asyncExec(updateUiRunnable);

                       

                  } catch(SecurityException e) {

                        throw newRuntimeException(e);

                  } catch(IllegalArgumentException e) {

                        throw newRuntimeException(e);

                  }

            }

 

            public synchronized void exit() {

                  exiting = true;

                  try {

                        // On exit, cancel all pending reads, disable the scanner device and exit the program

           

                        stopScanning = true;

                        notifyAll();

                  } catch (Exception e) {

                  }

            }

 

            public void run() {

                  try {

                        this.go();

                  } catch(java.lang.UnsatisfiedLinkError e) {

                        this.exit();

                  }

            }

 

            public boolean isScanEnabled() {

                  return scanEnabled;

            }

 

            public void stopScan() {

                  exit();

            }

       

       

The Symbol Scanner requires its own thread that is suspended with wait() . When the scan button is pushed, the scanner will invoke readNotify(ScanReadInfo). Because I wanted to segregate the symbol api from my Web Dynpro  application, I created my own scanner listener (SymbolScannerListener). So when the readNotify(ScanReadInfo) is executed it will, in turn, execute the custom listener asynchronously. We use the WDLite function asyncExec to run the listener asynchronously because WDLite has its own thread that manages the UI, preventing us from accessing the UI.

3) Implement the scanner in the component controller

The easiest way to get this to work for the first time is to simply throw the above code snippet into the component controller. Clearly, you will eventually want to define a separate component for the scanner classes and make you Web Dynpro application dependent on it. To start using the scanner defined above, put the following into the component controller:

 

      public void wdDoInit() {

//@@begin wdDoInit()

                             SymbolScannerListener infoListener = new

SymbolScannerListener() {

                  public void onScan(String data) {

                        WDLite.showMessage(0, "You scanned: " + data, BUTTONS_OK);

                  }

            };

                        scanner = newSymbolScanner(listener);

                        scanner.init();

                        if(scanner.isScanEnabled()) {

                              Thread thread;

                              thread = new Thread(scanner);

                              thread.start();

                        }

//@@end

      }

 

      public void wdDoExit() {

//@@begin wdDoExit()

                  scanner.stopScan();

//@@end

      }

 

 //@@begin others

     private SymbolScanner scanner = null

 

 

 

4) Install Motorola Java SDK on physical handheld

Copy the installation cab to the handheld and execute it. 

 

5) Copy required Jars into classpath

This can be done either by changing MIlaunch.properties or  by simply copying the required jars into MIlib.


6) Deploy application to handheld

Please see my Mobile 7.1 Application Deployment on deployment if you have not already tried it.

 

7) Start Scanning! 

 

Notes and Considerations 

Please note that the posted code is based in large part on the sample code provided by Symbol. Please consult their SDK for examples of how to use other features (such as the camara).

 

We had troubles deploying both custom java code and the required jar files to the handheld. As a workaround, we simply embedded our scanner code into our component controller as an inner class. As mentioned previously, we decided to manually copy the jar into the mobile application’s lib folder.