Xamarin Android and DataWedge scanning process

Hi guys, how are you?

I have already written some pages about scanning barcodes. Every day I work with devices that have to scan barcodes. To add this functionality to my softwares I have always used native APIs because them give the best control to barcode scanner.

Another solution is to use DataWedge,  DataWedge enables all apps on the device (whether stock or added later) to acquire scanned data without using scanner APIs directly. I have never taken into consideration DataWedge because an external tool can’t give complete control in barcode reading process, especially because I have always thought that DataWedge works only in “Keyboard Emulation” mode, so my software should always have a Page with a TextBox focused, otherwise the barcode should be lost.

Some weeks ago I have found this article that describe how DataWedge can be used inside an application without Keyboard Emulation. Fantastic I say, I don’t need a full SDK only to scan some barcode.

I have written this project that demonstrate how we can

  • create a new DataWedge profile
  • customize it
  • receive barcodes from DataWedge
  • visualize them on an Activity

all in a Xamarin Android application (by now, DataWedge exists only for Android…).

The project has an activity that visualize a label to display barcodes read and a FAB that can activate the scanner. We can also activate the scanner using “hardware buttons”..

The interaction between application and DataWedge is carried out using Intent.

In next code there are some constants that help us to use Intent:

static string PROFILE_NAME = "XamAndroidDataWedge";

public static string ACTION_DATAWEDGE_FROM_6_2 = "com.symbol.datawedge.api.ACTION";
public static string EXTRA_CREATE_PROFILE = "com.symbol.datawedge.api.CREATE_PROFILE";
public static string EXTRA_ENABLE_DATAWEDGE = "com.symbol.datawedge.api.ENABLE_DATAWEDGE";
public static string EXTRA_SWITCH_TO_PROFILE = "com.symbol.datawedge.api.SWITCH_TO_PROFILE";
public static string EXTRA_SET_CONFIG = "com.symbol.datawedge.api.SET_CONFIG";
public static string DATAWEDGE_EXTRA_KEY_SCANNER_TRIGGER_CONTROL = "com.symbol.datawedge.api.SOFT_SCAN_TRIGGER";
public static string DATAWEDGE_EXTRA_VALUE_TOGGLE_SCANNER = "TOGGLE_SCANNING";

First of all take a look to OnResume event

protected override void OnResume()
{
    base.OnResume();

    EnableDataWedge();

    bool isUpdated = Preferences.Get("datawedge_updated", false);
    if (!isUpdated)
    {
        CreateProfile();
        UpdateProfile();

        Preferences.Set("datawedge_updated", true);
    }

    SwitchToProfile();

    // Register the broadcast receiver
    IntentFilter filterScan = new IntentFilter(ScanReceiver.IntentAction);
    filterScan.AddCategory(ScanReceiver.IntentCategory);
    RegisterReceiver(scanReceiver, filterScan);
        
}

This code do some interesting things, now I try to analize them.

EnableDataWedge

EnableDataWedge method enables DataWedge, that could be disable…

private void EnableDataWedge()
{
    Intent i = new Intent();
    i.SetAction(ACTION_DATAWEDGE_FROM_6_2);
    i.PutExtra(EXTRA_ENABLE_DATAWEDGE, true);
    SendBroadcast(i);
}

The “Action” is always ACTION_DATAWEDGE_FROM_6_2. You can set “Extra” depending by the functionality you need.

Create and update a DataWedge profile

After enabled DataWedge, the first time you use the application you have to create and update the DataWedge profile. In my demo, I check the first time the app is executed using a Preference.

bool isUpdated = Preferences.Get("datawedge_updated", false);

Preference is checked with Xamarin Essentials.

To create a profile, use this code:

private void CreateProfile()
{
    Intent i = new Intent();
    i.SetAction(ACTION_DATAWEDGE_FROM_6_2);
    i.PutExtra(EXTRA_CREATE_PROFILE, PROFILE_NAME);
    SendBroadcast(i);
}

PROFILE_NAME is the name you give to the profile. Opening DataWedge application you will find the profile and you can change profile’s properties manually.

Created the profile, you have to update it, adding some informations:

private void UpdateProfile()
{
    Bundle profileConfig = new Bundle();
    profileConfig.PutString("PROFILE_NAME", PROFILE_NAME); // Set the profile name
    profileConfig.PutString("PROFILE_ENABLED", "true"); // Enable the profile
    profileConfig.PutString("CONFIG_MODE", "UPDATE");
    Bundle barcodeConfig = new Bundle();
    barcodeConfig.PutString("PLUGIN_NAME", "BARCODE");
    barcodeConfig.PutString("RESET_CONFIG", "true"); // This is the default but never hurts to specify

    // Here you define scanner properties
    Bundle barcodeProps = new Bundle();

    barcodeProps.PutString("scanner_input_enabled", "true");
    barcodeProps.PutString("scanner_selection", "auto"); // Could also specify a number here, the id returned from ENUMERATE_SCANNERS.
    // Do NOT use "Auto" here (with a capital 'A'), it must be lower case.
    barcodeProps.PutString("decoder_ean8", "true");
    barcodeProps.PutString("decoder_ean13", "true");
    barcodeProps.PutString("decoder_code39", "true");
    barcodeProps.PutString("decoder_code128", "true");
    barcodeProps.PutString("decoder_upca", "true");
    barcodeProps.PutString("decoder_upce0", "true");
    barcodeProps.PutString("decoder_upce1", "true");
    barcodeProps.PutString("decoder_d2of5", "true");
    barcodeProps.PutString("decoder_i2of5", "true");
    barcodeProps.PutString("decoder_aztec", "true");
    barcodeProps.PutString("decoder_pdf417", "true");
    barcodeProps.PutString("decoder_qrcode", "true");

    barcodeConfig.PutBundle("PARAM_LIST", barcodeProps);
    profileConfig.PutBundle("PLUGIN_CONFIG", barcodeConfig);

    Bundle appConfig = new Bundle();
    appConfig.PutString("PACKAGE_NAME", this.PackageName); // Associate the profile with this app
    appConfig.PutStringArray("ACTIVITY_LIST", new String[] { "*" });
    profileConfig.PutParcelableArray("APP_LIST", new Bundle[] { appConfig });
    SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, profileConfig);
    // You can only configure one plugin at a time, we have done the barcode input, now do the intent output
    profileConfig.Remove("PLUGIN_CONFIG");
    Bundle intentConfig = new Bundle();
    intentConfig.PutString("PLUGIN_NAME", "INTENT");
    intentConfig.PutString("RESET_CONFIG", "true");
    Bundle intentProps = new Bundle();
    intentProps.PutString("intent_output_enabled", "true");
    // intentProps.PutString("intent_action", DataWedgeReceiver.IntentAction); // We can use this when we're going to define the DataWedgeReceiver class
    intentProps.PutString("intent_action", "barcodescanner.RECVR");
    intentProps.PutString("intent_delivery", "2");
    intentConfig.PutBundle("PARAM_LIST", intentProps);
    profileConfig.PutBundle("PLUGIN_CONFIG", intentConfig);
    SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, profileConfig);

}

 

The interesting thing is when you set the “intent_action”. It’s the action that will be used in a BroadcastReceive to catch the barcode sent from DataWedge.

SwitchToProfile

This code set the current profile used by the application. I think it is not necessary, but it is not a bad idea to add it.

private void SwitchToProfile()
{
    Intent i = new Intent();
    i.SetAction(ACTION_DATAWEDGE_FROM_6_2);
    i.PutExtra(EXTRA_SWITCH_TO_PROFILE, PROFILE_NAME);
    SendBroadcast(i);
}

Register the receiver

After DataWedge profile is created, we have only to register the BroadcastReceiver that will receive all barcodes sent from DataWedge:

 // Register the broadcast receiver
IntentFilter filterScan = new IntentFilter(ScanReceiver.IntentAction);
filterScan.AddCategory(ScanReceiver.IntentCategory);
RegisterReceiver(scanReceiver, filterScan);

ScanReceiver is defined as:

ScanReceiver scanReceiver;

and in OnCreate you find:

 scanReceiver = new ScanReceiver(_textViewReadBarcode);

I am not a good Android programmer (I always develop application using Xamarin Forms…) so I am not sure that passing a TextView to a BroadcastReceiver it’s the correct way to visualize receive data inside a TextView, but it works.

You should also unregister the BroadcastReceiver inside OnSleep event:

protected override void OnPause()
{
    UnregisterReceiver(scanReceiver);
    base.OnPause();
}

ScanReceiver

This is the BroadcastReceiver I use to receive barcodes from DataWedge. It is well commented (commented are not mine…) so I have nothing to say about it.

The only think I would like to say before greet you is about the last commented line.

 //MessagingCenter.Send<App, string>((App)Xamarin.Forms.Application.Current, "ScanBarcode", data);

This line of code can be used to send the barcode, using MessagingCenter, to a Xamarin Forms application! This means that you can use the full code present in this demo project also for a Xamarin Forms application.

This is the BroadcastReceiver:

[BroadcastReceiver]
public class ScanReceiver : BroadcastReceiver
{
    public static DateTime UltimaLetturaBarcode = DateTime.MinValue;

    // This intent string contains the source of the data as a string 
    private static string SOURCE_TAG = "com.motorolasolutions.emdk.datawedge.source";
    // This intent string contains the barcode symbology as a string 
    private static string LABEL_TYPE_TAG = "com.motorolasolutions.emdk.datawedge.label_type";
    // This intent string contains the captured data as a string 
    // (in the case of MSR this data string contains a concatenation of the track data) 
    private static string DATA_STRING_TAG = "com.motorolasolutions.emdk.datawedge.data_string";
    // Intent Action for our operation
    public static string IntentAction = "barcodescanner.RECVR";
    public static string IntentCategory = "android.intent.category.DEFAULT";
    private TextView _textViewReadBarcode;

    public ScanReceiver(TextView textViewReadBarcode)
    {
        _textViewReadBarcode = textViewReadBarcode;
    }

    public ScanReceiver()
    {

    }

    //public event EventHandler<string> scanDataReceived;

    public override void OnReceive(Context context, Intent i)
    {
        // check the intent action is for us 
        if (i.Action.Equals(IntentAction))
        {
            // define a string that will hold our output 
            String Out = "";
            // get the source of the data 
            String source = i.GetStringExtra(SOURCE_TAG);
            // save it to use later 
            if (source == null)
                source = "scanner";
            // get the data from the intent 
            String data = i.GetStringExtra(DATA_STRING_TAG);
            // let's define a variable for the data length 
            int data_len = 0;
            // and set it to the length of the data 
            if (data != null)
                data_len = data.Length;
            string sLabelType = "";
            // check if the data has come from the barcode scanner 
            if (source.Equals("scanner"))
            {
                // check if there is anything in the data 
                if (data != null && data.Length > 0)
                {
                    // we have some data, so let's get it's symbology 
                    sLabelType = i.GetStringExtra(LABEL_TYPE_TAG);
                    // check if the string is empty 
                    if (sLabelType != null && sLabelType.Length > 0)
                    {
                        // format of the label type string is LABEL-TYPE-SYMBOLOGY 
                        // so let's skip the LABEL-TYPE- portion to get just the symbology 
                        sLabelType = sLabelType.Substring(11);
                    }
                    else
                    {
                        // the string was empty so let's set it to "Unknown" 
                        sLabelType = "Unknown";
                    }

                    // let's construct the beginning of our output string 
                    Out = "Scanner " + "Symbology: " + sLabelType + ", Length: " + data_len.ToString() + ", Data: " + data.ToString() + "\r\n";
                }
            }
            // check if the data has come from the MSR 
            if (source.Equals("msr"))
            {
                // construct the beginning of our output string 
                Out = "Source: MSR, Length: " + data_len.ToString() + ", Data: " + data.ToString() + "\r\n";
            }

            System.Diagnostics.Debug.WriteLine(Out);

            data = data.Replace('\n', ' ').TrimEnd();
            _textViewReadBarcode.Text = data;
            //MessagingCenter.Send<App, string>((App)Xamarin.Forms.Application.Current, "ScanBarcode", data);
        }
    }
}

Toggle()

Then last piece of code is relative to activate barcode scanner using the FAB button.

private void Toggle()
{
    Intent intent = new Intent();
    intent.SetAction(MainActivity.ACTION_DATAWEDGE_FROM_6_2);
    intent.PutExtra(MainActivity.DATAWEDGE_EXTRA_KEY_SCANNER_TRIGGER_CONTROL,        MainActivity.DATAWEDGE_EXTRA_VALUE_TOGGLE_SCANNER);
    SendBroadcast(intent);
}

 

Conclusion

Actually DataWedge is, for me, the correct way to interact with a barcode scanner (on devices that have DataWedge installed, of course), and Intents give you the full control of DataWedge platform. And here you can find all DataWedge API.

 

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...