Advertisement
BankUpload Join!

Saturday, February 7, 2015

Read and produce Barcode with Delphi XE5 (Android)

There are several online articles about how to integrate the reading of barcodes in mobile applications Delphi (find the link also later in this post).

We will now see an approach that allows you to add the scanning capabilities and production of barcodes (and / or QR code) in a Delphi app (Android), without the need to install third-party components or fill in your app libraries of third parties.

Everything is based on the interaction, mediated by the operating system, with an existing application (widespread, free and available on Google Play Store) which is called Barcode Scanner (license details here ).



At the cost of a certain level of dependence on this third-party application, the benefit is to be able to make the most with little effort, easily creating features that may be sufficient in a number of situations.

Pro and con

I anticipate now what are the merits / flaws of this approach:

Versus

  1. It creates a dependency with Barcode Scanner (although only with regard to the functionality of reading / production of barcode, not to run in self of your app): your users will have to install on their devices that application (currently free) ;
  2. The functionality of reading (scan) of the barcode is based on the assumption that Barcode Scanner closes after scanning a code and copy the code read the clipboard of the device; if future versions of the application were to do otherwise, your application may not work;
  3. this solution is not crossplatform (is valid only for Android)

Pro

  1. All the complexity of implementation is delegated to Barcode Scanner (including support to numerous formats barcodes and management of the room);
  2. No need for third-party library to fill out with your application;
  3. Both reading the production of bar codes is solved with a few lines of code!

Other approaches and reference links

Intent

Intent is essentially a description of an operation to perform. Android Applications (activity) can interact through intent (calling and registering to serve) a little 'how applications Win32 / 64 can interact via ShellExecute or CreateProcess.
In Delphi are the units that help us to manage directly the Java wrapper classes involved and that allow us to make the necessary type conversions between data types and those Delphi Java. In particular there are three units that will serve us:

  1. Androidapi.JNI.GraphicsContentViewText, where it is called the wrapper JIntent;
  2. Androidapi.JNI.JavaTypes, where we find SharedActivity (a JActivity);
  3. FMX.Helpers.Android, which provides utility functions as StringToJString.
After including these units, we are in a position to declare and execute an intent.
As you can see in this file Java in Barcode Scanner , it exposes different intent including one to initiate a scan of a bar code and one to produce a bar code from a text content provided in the parameters dell'intent.

First step: production of a bar code

With these few lines of code we can prepare and carry out the intent of ENCODE Barcode Scanner:
  procedures TForm1.ButtonProduceClick (Sender: TObject);
 var
   Intent: JIntent;
 begin
   Intent: = TJIntent.JavaClass.init (StringToJString ('com.google.zxing.client.android.ENCODE'));
   Intent.setPackage (StringToJString ('com.google.zxing.client.android'));

   Intent.putExtra (StringToJString ('ENCODE_TYPE'), StringToJString ('TEXT_TYPE'));
   Intent.putExtra (StringToJString ('ENCODE_FORMAT'), StringToJString (ComboBoxFormat.Items [ComboboxFormat.ItemIndex]));
   Intent.putExtra (StringToJString ('ENCODE_DATA'), StringToJString (Memo1.Lines.Text));

   SharedActivityContext.startActivity (Intent);
 end;


  • The value ENCODE_DATA represents the content that we want to make with the barcode (and the contents of a memo on this form of our application);
  • ENCODE_FORMAT represents the type of bar code that we want to produce (in our example is taken from a combo box on the form). The formats currently supported are:
    "UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128", "ITF", "RSS_14", "RSS_EXPANDED", "QR_CODE", "DATA_MATRIX".
The result is that once launched the activity described by our intent, the application Barcode Scanner will be executed and will receive the parameters that we set nell'intent, producing for us a barcode of the specified type and with the indicated content.



Second step: reading a bar code

Leveraging a second intent available, we can ask Barcode Scanner to start in SCAN mode, presenting the user with the typical graphical interface to locate the room with a bar code and scan it. Once acquired code, the application makes a beep sound and closes, after copying to the clipboard the value scanned. This means that our application back in the foreground.

The code to perform the intent-SCAN is the following:
  var
   Intent: JIntent;
 begin
   // Empty the clipboard (to be sure not to match previous results)
   FClipBoardService.SetClipboard ('');
   WaitingForResults: = True;

   // Launch Barcode Scanner in SCAN mode
   Intent: = TJIntent.JavaClass.init (StringToJString ('com.google.zxing.client.android.SCAN'));
   Intent.setPackage (StringToJString ('com.google.zxing.client.android'));

   Intent.putExtra (StringToJString ('SCAN_MODE'), StringToJString ('ONE_D_MODE, QR_CODE_MODE, PRODUCT_MODE, DATA_MATRIX_MODE'));

   SharedActivityContext.startActivity (Intent);
 end;

In theory, the proper way to catch the end of the scanning would implement a handler foronActivityResult but this currently involves some complications techniques.
Therefore, we use a simple workaround: intercept the event that indicates that our application is again in the foreground ( excellent article Pawel Glowacki ) and read the contents of the clipboard to capture the text corresponding to the barcode read by Barcode Scanner.

Code to hook the events of the application and to obtain a reference to the clipboard:
  procedures TForm1.FormCreate (Sender: TObject);
 begin
   FWaitingForResults: = False;

   if not TPlatformServices.Current.SupportsPlatformService (IFMXClipboardService, IInterface (FClipBoardService)) then
     raise Exception.Create ('Can not get access to clipboard service!');

   if not TPlatformServices.Current.SupportsPlatformService (IFMXApplicationEventService, IInterface (FApplicationEventService)) then
     raise Exception.Create ('Can not get application event service');
   FApplicationEventService.SetApplicationEventHandler (ApplicationEventHandler);
 end;

Code for the event-handler of the changes of state of the application:
  function TForm1.ApplicationEventHandler (AAppEvent: TApplicationEvent;
   AContext: TObject): Boolean;
 var
   LClipboardContent: string;
   LFound: Boolean;
 begin
   houses of AAppEvent
     aeBecameActive:
       begin
         then if WaitingForResults
         begin
           WaitingForResults: = False;
           LClipboardContent: = FClipBoardService.GetClipboard.AsString;
           LFound: LClipboardContent = <> '';

           then if LFound
             Log ('Scan successful:' + LClipboardContent)
           else
             Log ('Scan failed (please retry)');
         end;
       end;
   end;
   Result: = True;
 end;

Log the procedure does nothing but write in the Memo main form.



Conclusions and material

That's all. As you see it is a few lines of code and the mechanism is simple but effective.
If your needs to read and produce barcode are not particularly excessive, an approach such as that illustrated a solution should be sufficient in most cases.

Link: Source Code Delphi XE5
Link: Demo APK (to be installed on your Android device) 

No comments:

Post a Comment