Overview
This guide provides a walk-through for creating an application that uses barcode scanning and RFID APIs to implement a scan-scan-write workflow. The application allows users to:
- Scan a product barcode
- Convert that barcode to a SGTIN-96 format EPC
- Scan a RFID tag with barcode
- Write the converted EPC to the scanned RFID tag
Note: The demo app in this guide is intended for tutorial purposes only and should not be used in production environments.
Prerequisites
Developer machine with latest Android studio
Developer machine with Zebra Android device USB driver installed
Zebra Android device with developer mode turned on
Reader regulatory is configured as per norms using RFID Manager or Zebra RFID Demo application
DataWedge profile configured for barcode scanning
Create The Project
Start by creating a new project in Android Studio. Call it Scan-Scan-Write
to match later references in this guide. For help, see the Android Studio tutorial.
Adding essentials
Modify the application's MainActivity.java
file to use the RFID SDK library.
Declare RFID readers fields:
private static Readers readers;` private static ArrayList
availableRFIDReaderList; private static ReaderDevice readerDevice; private static RFIDReader reader; private static String TAG = "DEMO"; -
Create SDK
Readers
instance// SDK if (readers == null) { readers = new Readers(this, ENUM_TRANSPORT.SERVICE_SERIAL); }
Note : For RFD40XX USB connectivity create reader instance with
readers = new Readers(this, ENUM_TRANSPORT.SERVICE_USB)
Note : For Bluetooth instance
readers = new Readers(this, ENUM_TRANSPORT.BLUETOOTH)
Note : For TC53E/EM45/ET6x device
readers = new Readers(this, ENUM_TRANSPORT.RE_SERIAL)
-
Create
AsyncTask
to retrieve available readers usingGetAvailableRFIDReaderList
new AsyncTask
() { @Override protected Boolean doInBackground(Void... voids) { try { if (readers != null) { if (readers.GetAvailableRFIDReaderList() != null) { availableRFIDReaderList = readers.GetAvailableRFIDReaderList(); if (availableRFIDReaderList.size() != 0) { // get first reader from list readerDevice = availableRFIDReaderList.get(0); reader = readerDevice.getRFIDReader(); if (!reader.isConnected()) { // Establish connection to the RFID Reader reader.connect(); ConfigureReader(); return true; } } } } } catch (InvalidUsageException e) { e.printStackTrace(); } catch (OperationFailureException e) { e.printStackTrace(); Log.d(TAG, "OperationFailureException " + e.getVendorMessage()); } return false; } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); if (aBoolean) { Toast.makeText(getApplicationContext(), "Reader Connected", Toast.LENGTH_LONG).show(); //textView.setText("Reader connected"); } } }.execute(); Add
ConfigureReader
method to configure the reader for trigger type and adding event handler / listeneraddEventsListener
private void ConfigureReader() { if (reader.isConnected()) { TriggerInfo triggerInfo = new TriggerInfo(); triggerInfo.StartTrigger.setTriggerType(START_TRIGGER_TYPE.START_TRIGGER_TYPE_IMMEDIATE); triggerInfo.StopTrigger.setTriggerType(STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_IMMEDIATE); try { // receive events from reader if (eventHandler == null) eventHandler = new EventHandler(); reader.Events.addEventsListener(eventHandler); // HH event reader.Events.setHandheldEvent(true); // tag event with tag data reader.Events.setTagReadEvent(true); // application will collect tag using getReadTags API reader.Events.setAttachTagDataWithReadEvent(false); // set trigger mode as rfid so scanner beam will not come reader.Config.setTriggerMode(ENUM_TRIGGER_MODE.RFID_MODE, true); // set start and stop triggers reader.Config.setStartTrigger(triggerInfo.StartTrigger); reader.Config.setStopTrigger(triggerInfo.StopTrigger); } catch (InvalidUsageException e) { e.printStackTrace(); } catch (OperationFailureException e) { e.printStackTrace(); } } }
Add
EventHandler
class to handle Reader operation events -eventReadNotify
and Reader status events -eventStatusNotify
// Read/Status Notify handler // Implement the RfidEventsListener class to receive event notifications public class EventHandler implements RfidEventsListener { // Read Event Notification public void eventReadNotify(RfidReadEvents e) { TagData[] tags = reader.Actions.getReadTags(100); int accessSuccess = 0; int accessFailure = 0; if (tags != null && currentState == WorkflowState.WRITING) { for (TagData tag : tags) { Log.d(TAG, tag.getTagID() + " :: " + tag.getOpCode() + " :: " + tag.getOpStatus()); if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK) { if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) { accessSuccess++; Log.d(TAG, "LOCK SUCCESS"); } } else if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_WRITE) { if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) { accessSuccess++; Log.d(TAG, "WRITE SUCCESS"); } } else if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ) { if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) { accessSuccess++; Log.d(TAG, "READ SUCCESS"); } } if (tag.getOpStatus() != ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) { accessFailure++; try { reader.Actions.TagAccess.OperationSequence.deleteAll(); } catch (Exception ex) { ex.printStackTrace(); } } if (accessSuccess == 4) { System.out.println(tag.getTagID() + " ALL Encoding Success "); try { reader.Actions.TagAccess.OperationSequence.deleteAll(); } catch (Exception ex) { ex.printStackTrace(); } } } // Update UI based on operation results final int finalAccessSuccess = accessSuccess; runOnUiThread(() -> { if (finalAccessSuccess == 4) { Toast.makeText(MainActivity.this, "Tag written successfully!", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Tag operation failed. Only " + finalAccessSuccess + " of 4 operations succeeded", Toast.LENGTH_LONG).show(); } currentState = WorkflowState.IDLE; updateStatusDisplay(); }); } } // Status Event Notification public void eventStatusNotify(RfidStatusEvents rfidStatusEvents) { Log.d(TAG, "Status Notification: " + rfidStatusEvents.StatusEventData.getStatusEventType()); } }
Running the application
Connection with device
Connect any android terminal with PC over USB
Make sure project is already built successfully
Click on
Run app (shift+F10)
from top right cornerNow Application should be launched on Zebra Android device.
Application should be showing
Reader connected
toast message on screen
Programming tips
GetAvailableRFIDReaderList
must be called from background thread, in current example usesAsyncTask
for same purposeAt time of exit, application shounld disconnect with reader using
Disconnect
API and free up SDK instance. Refer MainActivityOnDestroy
methodIn case of failures or exceptions, refer messages returned by API call to get more details
What's Next
Add code to perfrom Scan-Scan-Write.
Create states to track the multi-step operation.
// State tracking for scan-scan-write workflow private enum WorkflowState { IDLE, // Waiting to start FIRST_BARCODE_SCANNED, // First barcode scanned, waiting for RFID tag barcode SECOND_BARCODE_SCANNED, // Tag ID scanned, ready to write WRITING // Writing in progress } private WorkflowState currentState = WorkflowState.IDLE; private String scannedBarcode = null; private String scannedTagId = null; private EPC encodedEpc = null; private TextView statusTextView; private final String ACCESS_PASSWORD = "00000000";
Initialize Components
Set up the required components in the onCreate() method
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize the status TextView statusTextView = findViewById(R.id.statusTextView); // Initialize RFID reader if (readers == null) { readers = new Readers(this, ENUM_TRANSPORT.SERVICE_USB); } // Connect to the RFID reader new ConnectionTask().execute(); // Initialize barcode to RFID conversion API BarcodeToRfidApi.initialize(this); // Register the barcode receiver IntentFilter filter = new IntentFilter(); filter.addAction(DataWedge.INTENT_NAME); filter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(barcodeReceiver, filter, Context.RECEIVER_EXPORTED); // Set up scan-scan-write button Button btnScanScanWrite = findViewById(R.id.btnScanScanWrite); btnScanScanWrite.setOnClickListener(v -> { if (reader != null && reader.isConnected()) { // Reset the workflow currentState = WorkflowState.IDLE; scannedBarcode = null; scannedTagId = null; encodedEpc = null; updateStatusDisplay(); // Start the workflow - trigger barcode scanner startBarcodeScanning(); } else { Toast.makeText(this, "RFID Reader not connected", Toast.LENGTH_SHORT).show(); } }); }
Barcode Scanning
Trigger the barcode scanner using DataWedge
private void startBarcodeScanning() { // Trigger DataWedge scanner Intent dwIntent = new Intent(); dwIntent.setAction("com.symbol.datawedge.api.ACTION"); dwIntent.putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "START_SCANNING"); sendBroadcast(dwIntent); Toast.makeText(this, "Please scan a barcode", Toast.LENGTH_SHORT).show(); }
Processing Barcode Scans
Create a BroadcastReceiver to handle barcode scan results
private final BroadcastReceiver barcodeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action != null && action.equals(DataWedge.INTENT_NAME)) { // Barcode scan from DataWedge String barcode = intent.getStringExtra(DataWedge.DATAWEDGE_EXTRA); if (barcode != null && !barcode.isEmpty()) { Log.d(TAG, "Scanned barcode: " + barcode); // Stop barcode scanner Intent dwIntent = new Intent(); dwIntent.setAction("com.symbol.datawedge.api.ACTION"); dwIntent.putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "STOP_SCANNING"); context.sendBroadcast(dwIntent); // Process based on current state if (currentState == WorkflowState.IDLE) { // First barcode scan - product barcode try { // Process the barcode with BarcodeToRfidApi encodedEpc = BarcodeToRfidApi.encodeBarcodeToRfid(barcode, "1234567890"); Log.d(TAG, "Encoded EPC: " + encodedEpc.toString()); // Store barcode and update state scannedBarcode = barcode; currentState = WorkflowState.FIRST_BARCODE_SCANNED; updateStatusDisplay(); // Start second barcode scan after a short delay new Handler().postDelayed(() -> startBarcodeScanning(), 1000); } catch (Exception e) { Log.e(TAG, "Error processing barcode", e); Toast.makeText(context, "Error processing barcode: " + e.getMessage(), Toast.LENGTH_SHORT).show(); currentState = WorkflowState.IDLE; updateStatusDisplay(); } } else if (currentState == WorkflowState.FIRST_BARCODE_SCANNED) { // Second barcode scan - RFID tag ID scannedTagId = barcode; currentState = WorkflowState.SECOND_BARCODE_SCANNED; updateStatusDisplay(); // Ask user to confirm write runOnUiThread(() -> { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Confirm Write"); builder.setMessage("Write EPC " + encodedEpc.toString() + " to tag " + scannedTagId + "?"); builder.setPositiveButton("Write", (dialog, which) -> { unlockAndWriteEpcToTag(scannedTagId, encodedEpc); }); builder.setNegativeButton("Cancel", (dialog, which) -> { currentState = WorkflowState.IDLE; updateStatusDisplay(); }); builder.show(); }); } } } } };
Converting Barcode to RFID EPC Format
The BarcodeToRfidApi class provides the interface between barcode data and RFID EPC format
public class BarcodeToRfidApi { public static void initialize(Context context) { EPCConverter.initEpcConverter(context); DataWedge.setDWProfile(context); } public static EPC encodeBarcodeToRfid(String scannedUpc, String serialNumber) { EPC epc = EPCConverter.encodeSGTIN96(scannedUpc, serialNumber); return epc; } }
Create a filter to specify which tags should be operated on
private AccessFilter setAccessFilter(String tagID) { int len = tagID.length()/2; AccessFilter accessFilter = new AccessFilter(); byte[] tagMask = new byte[len]; byte[] tagData = hexStringToByteArray(tagID); for(int i= 0; i < len; i++){ tagMask[i] = (byte)0xff; } // Tag Pattern A accessFilter.TagPatternA.setMemoryBank(MEMORY_BANK.MEMORY_BANK_EPC); accessFilter.TagPatternA.setTagPattern(tagData); accessFilter.TagPatternA.setTagPatternBitCount(len*8); accessFilter.TagPatternA.setBitOffset(32); accessFilter.TagPatternA.setTagMask(tagMask); accessFilter.TagPatternA.setTagMaskBitCount(tagMask.length*8); accessFilter.setAccessFilterMatchPattern(FILTER_MATCH_PATTERN.A); return accessFilter; }
Writing to RFID Tag
Perform the write operation using a sequence of RFID tag access operations
private void unlockAndWriteEpcToTag(String tagId, EPC newEpc) { currentState = WorkflowState.WRITING; updateStatusDisplay(); try { //operation sequence for unlock-write-lock performTagOperationSequence(tagId, newEpc.toString()); Log.d(TAG, "Tag operation sequence started successfully"); } catch (InvalidUsageException | OperationFailureException e) { Log.e(TAG, "Error in tag operation sequence", e); runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show()); currentState = WorkflowState.IDLE; updateStatusDisplay(); } }
RFID Access Sequence Operation
Define the sequence of operations to unlock, write, and lock the RFID tag
private void performTagOperationSequence(String tagId, String epcData) throws OperationFailureException, InvalidUsageException { Log.d(TAG, "Starting operation sequence for tag: " + tagId); long accessPassword = Long.parseLong(ACCESS_PASSWORD, 16); try { reader.Actions.TagAccess.OperationSequence.deleteAll(); } catch (Exception e) { Log.w(TAG, "Error clearing operation sequence", e); } TagAccess tagAccess = reader.Actions.TagAccess; TagAccess.Sequence opSequence = tagAccess.new Sequence(tagAccess); // Operation 1: Read TID memory bank TagAccess.Sequence.Operation op1 = opSequence.new Operation(); op1.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ); op1.ReadAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_TID); op1.ReadAccessParams.setCount(1); op1.ReadAccessParams.setOffset(0); reader.Actions.TagAccess.OperationSequence.add(op1); // Operation 2: Unlock the EPC Memory TagAccess.Sequence.Operation unlockOp = opSequence.new Operation(); unlockOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK); unlockOp.LockAccessParams.setLockPrivilege(LOCK_DATA_FIELD.LOCK_EPC_MEMORY, LOCK_PRIVILEGE.LOCK_PRIVILEGE_UNLOCK); unlockOp.LockAccessParams.setAccessPassword(Long.decode("0X" + accessPassword)); reader.Actions.TagAccess.OperationSequence.add(unlockOp); // Operation 3: Write EPC to tag TagAccess.Sequence.Operation writeOp = opSequence.new Operation(); writeOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_WRITE); writeOp.WriteAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_EPC); writeOp.WriteAccessParams.setOffset(2); writeOp.WriteAccessParams.setWriteData(epcData); writeOp.WriteAccessParams.setWriteDataLength(epcData.length()/4); reader.Actions.TagAccess.OperationSequence.add(writeOp); // Operation 4: Lock the EPC Memory TagAccess.Sequence.Operation lockOp = opSequence.new Operation(); lockOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK); lockOp.LockAccessParams.setLockPrivilege(LOCK_DATA_FIELD.LOCK_EPC_MEMORY, LOCK_PRIVILEGE.LOCK_PRIVILEGE_READ_WRITE); lockOp.LockAccessParams.setAccessPassword(Long.decode("0X" + accessPassword)); reader.Actions.TagAccess.OperationSequence.add(lockOp); // Execute the sequence on the specific tag reader.Actions.TagAccess.OperationSequence.performSequence(setAccessFilter(tagId), null, null); }
package com.zebra.ssw.EncodeBarcodeToRFID;
import androidx.annotation.NonNull;
import java.io.Serializable;
public class EPC implements Serializable {
public static final EPC DEFAULT = new EPC("0000000000000");
private final String content;
public EPC(String s) {
content = s.toUpperCase();
}
}
Complete Implementation
Below is the complete implementation which combines all the code snippets discussed earlier. This class handles RFID reader initialization, connection, configuration, event handling, and performs scan-scan-write. Key features include:
- MainActivity: Manages the workflow and RFID operations. Controls the UI state transitions and implements the scan-scan-write process flow. Handles RFID reader connections and events, including tag access operations.
- EPCConverter: Converts barcode formats to RFID EPC format. Contains algorithms to properly encode UPC product codes into standardized SGTIN-96 EPC format following GS1 specifications.
- DataWedge: Handles barcode scanning integration. Provides constants and methods to communicate with Zebra's DataWedge service for triggering barcode scans and receiving scan results.
- EPC: Represents an Electronic Product Code used in RFID tags. Contains the data structure and formatting rules for EPCs, supporting different encoding schemes and providing string representation methods for display and debugging.
Complete MainActivity.java Implementation
package com.zebra.ssw;
import androidx.appcompat.app.AppCompatActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.zebra.ssw.EncodeBarcodeToRFID.DataWedge;
import com.zebra.ssw.EncodeBarcodeToRFID.EPC;
import com.zebra.rfid.api3.ACCESS_OPERATION_CODE;
import com.zebra.rfid.api3.ACCESS_OPERATION_STATUS;
import com.zebra.rfid.api3.AccessFilter;
import com.zebra.rfid.api3.ENUM_TRANSPORT;
import com.zebra.rfid.api3.FILTER_MATCH_PATTERN;
import com.zebra.rfid.api3.InvalidUsageException;
import com.zebra.rfid.api3.LOCK_DATA_FIELD;
import com.zebra.rfid.api3.LOCK_PRIVILEGE;
import com.zebra.rfid.api3.MEMORY_BANK;
import com.zebra.rfid.api3.OperationFailureException;
import com.zebra.rfid.api3.RFIDReader;
import com.zebra.rfid.api3.ReaderDevice;
import com.zebra.rfid.api3.Readers;
import com.zebra.rfid.api3.RfidEventsListener;
import com.zebra.rfid.api3.RfidReadEvents;
import com.zebra.rfid.api3.RfidStatusEvents;
import com.zebra.rfid.api3.TagAccess;
import com.zebra.rfid.api3.TagData;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.os.Handler;
public class MainActivity extends AppCompatActivity {
private final String TAG = "sswApp";
private Readers readers;
private ReaderDevice readerDevice;
private EventHandler eventHandler;
// State tracking for scan-scan-write workflow
private enum WorkflowState {
IDLE, // Waiting to start
FIRST_BARCODE_SCANNED, // First barcode scanned, waiting for RFID tag barcode
SECOND_BARCODE_SCANNED, // Tag ID scanned, ready to write
WRITING // Writing in progress
}
private WorkflowState currentState = WorkflowState.IDLE;
private String scannedBarcode = null;
private String scannedTagId = null;
private EPC encodedEpc = null;
private TextView statusTextView;
private final String ACCESS_PASSWORD = "00000000";
static RFIDReader reader;
private ArrayList availableRFIDReaderList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the status TextView
statusTextView = findViewById(R.id.statusTextView);
if (readers == null) {
readers = new Readers(this, ENUM_TRANSPORT.SERVICE_USB);
}
System.out.println("Connection BEGINS");
new ConnectionTask().execute();
BarcodeToRfidApi.initialize(this);
// Register the barcode receiver
IntentFilter filter = new IntentFilter();
filter.addAction(DataWedge.INTENT_NAME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(barcodeReceiver, filter, Context.RECEIVER_EXPORTED);
//scan-scan-write button
Button btnScanScanWrite = findViewById(R.id.btnScanScanWrite);
btnScanScanWrite.setOnClickListener(v -> {
if (reader != null && reader.isConnected()) {
// Reset the workflow
currentState = WorkflowState.IDLE;
scannedBarcode = null;
scannedTagId = null;
encodedEpc = null;
updateStatusDisplay();
// Start the workflow - trigger barcode scanner
startBarcodeScanning();
} else {
Toast.makeText(this, "RFID Reader not connected", Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onPause() {
super.onPause();
// Unregister barcode receiver
try {
unregisterReceiver(barcodeReceiver);
} catch (IllegalArgumentException e) {
// Receiver not registered
}
}
private void updateStatusDisplay() {
runOnUiThread(() -> {
// Null check to avoid crashes
if (statusTextView == null) {
Log.e(TAG, "Status TextView not initialized!");
return;
}
switch (currentState) {
case IDLE:
statusTextView.setText("Ready to scan product barcode");
break;
case FIRST_BARCODE_SCANNED:
statusTextView.setText("Product barcode scanned: " + scannedBarcode +
"\nNow scan RFID tag barcode");
break;
case SECOND_BARCODE_SCANNED:
statusTextView.setText("Product barcode: " + scannedBarcode +
"\nTag ID: " + scannedTagId +
"\nReady to write");
break;
case WRITING:
statusTextView.setText("Writing to tag...");
break;
}
});
}
private void startBarcodeScanning() {
// Trigger DataWedge scanner
Intent dwIntent = new Intent();
dwIntent.setAction("com.symbol.datawedge.api.ACTION");
dwIntent.putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "START_SCANNING");
sendBroadcast(dwIntent);
Toast.makeText(this, "Please scan a barcode", Toast.LENGTH_SHORT).show();
}
private final BroadcastReceiver barcodeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals(DataWedge.INTENT_NAME)) {
// Barcode scan from DataWedge
String barcode = intent.getStringExtra(DataWedge.DATAWEDGE_EXTRA);
if (barcode != null && !barcode.isEmpty()) {
Log.d(TAG, "Scanned barcode: " + barcode);
// Stop barcode scanner
Intent dwIntent = new Intent();
dwIntent.setAction("com.symbol.datawedge.api.ACTION");
dwIntent.putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "STOP_SCANNING");
context.sendBroadcast(dwIntent);
// Process based on current state
if (currentState == WorkflowState.IDLE) {
// First barcode scan - product barcode
try {
// Process the barcode with BarcodeToRfidApi
encodedEpc = BarcodeToRfidApi.encodeBarcodeToRfid(barcode, "1234567890");
Log.d(TAG, "Encoded EPC: " + encodedEpc.toString());
// Store barcode and update state
scannedBarcode = barcode;
currentState = WorkflowState.FIRST_BARCODE_SCANNED;
updateStatusDisplay();
// Start second barcode scan after a short delay
new Handler().postDelayed(() -> startBarcodeScanning(), 1000);
} catch (Exception e) {
Log.e(TAG, "Error processing barcode", e);
Toast.makeText(context, "Error processing barcode: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
currentState = WorkflowState.IDLE;
updateStatusDisplay();
}
}
else if (currentState == WorkflowState.FIRST_BARCODE_SCANNED) {
// Second barcode scan - RFID tag ID
scannedTagId = barcode;
currentState = WorkflowState.SECOND_BARCODE_SCANNED;
updateStatusDisplay();
// Ask user to confirm write
runOnUiThread(() -> {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Confirm Write");
builder.setMessage("Write EPC " + encodedEpc.toString() + " to tag " + scannedTagId + "?");
builder.setPositiveButton("Write", (dialog, which) -> {
unlockAndWriteEpcToTag(scannedTagId, encodedEpc);
});
builder.setNegativeButton("Cancel", (dialog, which) -> {
currentState = WorkflowState.IDLE;
updateStatusDisplay();
});
builder.show();
});
}
}
}
}
};
private void unlockAndWriteEpcToTag(String tagId, EPC newEpc) {
currentState = WorkflowState.WRITING;
updateStatusDisplay();
try {
//operation sequence for unlock-write-lock
performTagOperationSequence(tagId, newEpc.toString());
Log.d(TAG, "Tag operation sequence started successfully");
} catch (InvalidUsageException | OperationFailureException e) {
Log.e(TAG, "Error in tag operation sequence", e);
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"Error: " + e.getMessage(), Toast.LENGTH_LONG).show());
currentState = WorkflowState.IDLE;
updateStatusDisplay();
}
}
private void performTagOperationSequence(String tagId, String epcData)
throws OperationFailureException, InvalidUsageException {
Log.d(TAG, "Starting operation sequence for tag: " + tagId);
long accessPassword = Long.parseLong(ACCESS_PASSWORD, 16);
try {
reader.Actions.TagAccess.OperationSequence.deleteAll();
} catch (Exception e) {
Log.w(TAG, "Error clearing operation sequence", e);
}
TagAccess tagAccess = reader.Actions.TagAccess;
TagAccess.Sequence opSequence = tagAccess.new Sequence(tagAccess);
TagAccess.Sequence.Operation op1 = opSequence.new Operation();
op1.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ);
op1.ReadAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_TID);
op1.ReadAccessParams.setCount(1);
op1.ReadAccessParams.setOffset(0);
reader.Actions.TagAccess.OperationSequence.add(op1);
//operation 1: Unlock the EPC Memory
TagAccess.Sequence.Operation unlockOp = opSequence.new Operation();
unlockOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK);
unlockOp.LockAccessParams.setLockPrivilege(LOCK_DATA_FIELD.LOCK_EPC_MEMORY,
LOCK_PRIVILEGE.LOCK_PRIVILEGE_UNLOCK);
unlockOp.LockAccessParams.setAccessPassword(Long.decode("0X" + accessPassword));
reader.Actions.TagAccess.OperationSequence.add(unlockOp);
//operation 2: Write EPC to tag
TagAccess.Sequence.Operation writeOp = opSequence.new Operation();
writeOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_WRITE);
writeOp.WriteAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_EPC);
writeOp.WriteAccessParams.setOffset(2);
writeOp.WriteAccessParams.setWriteData(epcData);
writeOp.WriteAccessParams.setWriteDataLength(epcData.length()/4);
reader.Actions.TagAccess.OperationSequence.add(writeOp);
//operation 3: Lock the EPC Memory
TagAccess.Sequence.Operation lockOp = opSequence.new Operation();
lockOp.setAccessOperationCode(ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK);
lockOp.LockAccessParams.setLockPrivilege(LOCK_DATA_FIELD.LOCK_EPC_MEMORY,
LOCK_PRIVILEGE.LOCK_PRIVILEGE_READ_WRITE);
lockOp.LockAccessParams.setAccessPassword(Long.decode("0X" + accessPassword));
reader.Actions.TagAccess.OperationSequence.add(lockOp);
// Execute the sequence on the specific tag
reader.Actions.TagAccess.OperationSequence.performSequence(setAccessFilter(tagId), null, null);
}
//Utility functions
private AccessFilter setAccessFilter(String tagID) {
int len = tagID.length()/2;
AccessFilter accessFilter = new AccessFilter();
byte[] tagMask = new byte[len];
byte[] tagData = hexStringToByteArray(tagID);
for(int i= 0; i < len; i++){
tagMask[i] = (byte)0xff;
}
// Tag Pattern A
accessFilter.TagPatternA.setMemoryBank(MEMORY_BANK.MEMORY_BANK_EPC);
accessFilter.TagPatternA.setTagPattern(tagData);
accessFilter.TagPatternA.setTagPatternBitCount(len*8);
accessFilter.TagPatternA.setBitOffset(32);
accessFilter.TagPatternA.setTagMask(tagMask);
accessFilter.TagPatternA.setTagMaskBitCount(tagMask.length*8);
accessFilter.setAccessFilterMatchPattern(FILTER_MATCH_PATTERN.A);
return accessFilter;
}
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] buffer = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString
.charAt(i + 1)));
}
return buffer;
}
private static int toByte(char c) {
if (c >= '0' && c <= '9')
return (c - '0');
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
throw new InvalidParameterException("Invalid hex char '" + c + "'");
}
private class ConnectionTask extends AsyncTask {
protected Boolean doInBackground(Void... voids) {
try {
if (readers != null) {
System.out.println("Connection start");
if (readers.GetAvailableRFIDReaderList() != null) {
availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
if (availableRFIDReaderList.size() != 0) {
// get first reader from list
readerDevice = availableRFIDReaderList.get(0);
reader = readerDevice.getRFIDReader();
if (!reader.isConnected()) {
reader.connect();
ConfigureReader();
System.out.println("Reader Capabilities = "+ reader.ReaderCapabilities.getFirwareVersion());
return true;
}
}
}
}
} catch (InvalidUsageException e) {
e.printStackTrace();
} catch (OperationFailureException e) {
e.printStackTrace();
Log.d("Demo", "OperationFailureException " + e.getVendorMessage());
}
return false;
}
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean) {
Toast.makeText(getApplicationContext(), "Reader Connected", Toast.LENGTH_LONG).show();
}
}
}
private void ConfigureReader() {
if (reader.isConnected()) {
try {
if (eventHandler == null)
eventHandler = new EventHandler();
reader.Events.addEventsListener(eventHandler);
reader.Events.setHandheldEvent(true);
reader.Events.setTagReadEvent(true);
reader.Events.setReaderDisconnectEvent(true);
reader.Events.setInventoryStartEvent(true);
reader.Events.setInventoryStopEvent(true);
reader.Events.setAttachTagDataWithReadEvent(false);
} catch (InvalidUsageException e) {
e.printStackTrace();
} catch (OperationFailureException e) {
e.printStackTrace();
}
}
}
class EventHandler implements RfidEventsListener {
public void eventReadNotify(RfidReadEvents e) {
TagData[] tags = reader.Actions.getReadTags(100);
int accessSuccess = 0;
int accessFailure = 0;
if (tags != null && currentState == WorkflowState.WRITING) {
for (TagData tag : tags) {
Log.d(TAG, tag.getTagID() + " :: " + tag.getOpCode() + " :: " + tag.getOpStatus());
if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_LOCK) {
if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
accessSuccess++;
Log.d(TAG, "LOCK SUCCESS");
}
} else if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_WRITE) {
if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
accessSuccess++;
Log.d(TAG, "WRITE SUCCESS");
}
} else if (tag.getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ) {
if (tag.getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
accessSuccess++;
Log.d(TAG, "READ SUCCESS");
}
}
if (tag.getOpStatus() != ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
accessFailure++;
try {
reader.Actions.TagAccess.OperationSequence.deleteAll();
} catch (InvalidUsageException ex) {
ex.printStackTrace();
} catch (final OperationFailureException ex) {
ex.printStackTrace();
}
}
if (accessSuccess == 4) {
System.out.println(tag.getTagID() + " ALL Encoding Success ");
try {
reader.Actions.TagAccess.OperationSequence.deleteAll();
} catch (InvalidUsageException ex) {
ex.printStackTrace();
} catch (final OperationFailureException ex) {
ex.printStackTrace();
}
}
}
Log.d(TAG, "ACCESS SUCCESS: " + accessSuccess);
Log.d(TAG, "NOT ACCESS SUCCESS: " + accessFailure);
// Update UI based on operation results
final int finalAccessSuccess = accessSuccess;
runOnUiThread(() -> {
if (finalAccessSuccess == 4) {
Toast.makeText(MainActivity.this,
"Tag written successfully!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this,
"Tag operation failed. Only " + finalAccessSuccess + " of 4 operations succeeded",
Toast.LENGTH_LONG).show();
}
currentState = WorkflowState.IDLE;
updateStatusDisplay();
});
}
}
public void eventStatusNotify(RfidStatusEvents rfidStatusEvents) {
Log.d(TAG, "Status Notification: " + rfidStatusEvents.StatusEventData.getStatusEventType());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// Unregister barcode receiver
try {
unregisterReceiver(barcodeReceiver);
} catch (IllegalArgumentException e) {
// Receiver not registered, ignore
}
// Disconnect RFID reader
if (reader != null && reader.isConnected()) {
try {
reader.disconnect();
} catch (Exception e) {
Log.e(TAG, "Error disconnecting reader", e);
}
}
}
}
Complete EPCConverter.java Implementation
package com.zebra.ssw.EncodeBarcodeToRFID;
import android.content.Context;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class EPCConverter {
//Note: This may only be valid for the SGTIN-96 scheme
// The number of bits used to store the company prefix in the EPC depending on the partition value
private static final int[] COMPANY_PREFIX_BITS = {40, 37, 34, 30, 27, 24, 20};
// The number of digits in the company prefix depending on the partition value
private static final int[] COMPANY_PREFIX_DIGITS = {12, 11, 10, 9, 8, 7, 6};
// The number of bits used to store the item reference in the EPC depending on the partition value
private static final int[] ITEM_REF_BITS = {4, 7, 10, 14, 17, 20, 24};
// Header
private static final String SGTIN96_HEADER = "00110000"; // SGTIN-96 Header
// Filter Value
private static final String FILTER = "001"; // POS Item Filter Value
private static Map prefixMap;
private static final Object usageLock = new Object();
private EPCConverter() {}
public static void initEpcConverter(Context context) {
// Do this synchronously instead of async
synchronized (usageLock) {
String jsonData = loadJSONFromAsset(context);
prefixMap = buildPrefixMap(jsonData);
Log.d("EPCConverter", "Prefix map initialized with " +
(prefixMap != null ? prefixMap.size() : 0) + " entries");
}
}
/**
* This method encodes the given GTIN (Global Trade Item Number) and serial number into SGTIN-96 format.
*
* @param gtin - The Global Trade Item Number.
* @param serialNumber - The serial number.
* @return The encoded SGTIN-96 in hexadecimal format.
*/
public static EPC encodeSGTIN96(String gtin, String serialNumber){
synchronized (usageLock) {
if (gtin.length() < 14) { // Pad gtin string to 14 characters
gtin = new String(new char[14 - gtin.length()]).replace('\0', '0').concat(gtin);
}
Log.v("EPCConverter", "GTIN: " + gtin);
try {
// Get the length of the prefix in the GTIN
int gcpLength = getPrefixLength(prefixMap, gtin);
Log.d("EPCConverter", "GCP Len: " + gcpLength);
// Get the partition value which is dependent on the length of the prefix
int partitionValue = getPartition(gcpLength);
Log.d("EPCConverter", "Partition Val: " + partitionValue);
// Extract the company prefix from the GTIN
String companyPrefix = gtin.substring(1, gcpLength + 1);
// Extract the item reference from the GTIN
String itemReference = gtin.charAt(0) + gtin.substring(gcpLength + 1, 13);
Log.d("EPCConverter", companyPrefix + " " + itemReference);
// Convert the partition value to binary format
String partitionBinary = String.format("%3s", Integer.toBinaryString(partitionValue)).replace(' ', '0');
// Convert the company prefix to binary format
String companyPrefixBinary = String.format("%" + getCompanyPrefixBits(partitionValue) + "s", Long.toBinaryString(Long.parseLong(companyPrefix))).replace(' ', '0');
// Convert the item reference to binary format
String itemReferenceBinary = String.format("%" + getItemReferenceBits(partitionValue) + "s", Long.toBinaryString(Long.parseLong(itemReference))).replace(' ', '0');
// Convert the serial number to binary format
String serialNumberBinary = String.format("%38s", Long.toBinaryString(Long.parseLong(serialNumber))).replace(' ', '0');
// Combine all the binary strings to form the complete SGTIN-96 binary string
String sgtin96Binary = SGTIN96_HEADER + FILTER + partitionBinary + companyPrefixBinary + itemReferenceBinary + serialNumberBinary;
// Convert the binary string to hexadecimal format
StringBuilder hex = new StringBuilder();
for (int i = 0; i < sgtin96Binary.length(); i += 4) {
String chunk = sgtin96Binary.substring(i, i + 4);
hex.append(Integer.toString(Byte.parseByte(chunk, 2), 16));
}
// Return the hexadecimal string in uppercase
EPC epc = new EPC(hex.toString().toUpperCase());
return epc;
} catch (Exception e) {
Log.d("ENCODE", e.getMessage());
return EPC.DEFAULT;
}
}
}
/**
* This method returns the partition value which is dependent on the prefix length.
*
* @param prefixLength - The length of the prefix in the GTIN.
* @return The partition value.
*/
public static int getPartition(int prefixLength){
switch (prefixLength) {
case 12: return 0;
case 11: return 1;
case 10: return 2;
case 9: return 3;
case 8: return 4;
case 7: return 5;
case 6: return 6;
default: throw new IllegalArgumentException("Invalid GS1 Company Prefix Length");
}
}
/**
* This method returns the number of bits that should be used for the company prefix based on the partition value.
*
* @param partitionValue - The partition value.
* @return The number of bits for the company prefix.
*/
private static int getCompanyPrefixBits(int partitionValue) {
switch (partitionValue) {
case 0: return 40;
case 1: return 37;
case 2: return 34;
case 3: return 30;
case 4: return 27;
case 5: return 24;
case 6: return 20;
default: throw new IllegalArgumentException("Invalid Partition Value");
}
}
/**
* This method returns the number of bits that should be used for the item reference based on the partition value.
*
* @param partitionValue - The partition value.
* @return The number of bits for the item reference.
*/
private static int getItemReferenceBits(int partitionValue) {
switch (partitionValue) {
case 0: return 4;
case 1: return 7;
case 2: return 10;
case 3: return 14;
case 4: return 17;
case 5: return 20;
case 6: return 24;
default: throw new IllegalArgumentException("Invalid Partition Value");
}
}
/**
* This method gets the prefix length by checking the given GTIN against a map of known prefixes.
*
* @param prefixMap - The map of known prefixes.
* @param gtin - The Global Trade Item Number.
* @return The length of the prefix.
*/
private static int getPrefixLength(Map prefixMap, String gtin) {
for (int i = 4; i <= 12; i++){
String prefix = gtin.substring(1,i);
Integer gcpLength = prefixMap.get(prefix);
if(gcpLength != null){
return gcpLength;
}
}
throw new IllegalArgumentException("prefix not found");
}
//GCPMap Builder Functions:
/**
* This method loads a JSON file from the app's assets folder.
*
* @param context - The context of the calling activity.
* @return The JSON data in string format.
*/
private static String loadJSONFromAsset(Context context){
String json;
try {
InputStream is = context.getAssets().open("gcpprefixformatlist.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch(IOException e){
e.printStackTrace();
return null;
}
return json;
}
/**
* This method builds a map from the loaded JSON data where the keys are prefixes and the values are prefix lengths.
*
* @param jsonData - The JSON data in string format.
* @return The map of prefixes and their lengths.
*/
private static Map buildPrefixMap(String jsonData){
Map prefixMap = new HashMap<>();
try{
JSONObject obj = new JSONObject(jsonData);
JSONArray entries = obj.getJSONObject("GCPPrefixFormatList").getJSONArray("entry");
for (int i = 0; i < entries.length(); i++){
JSONObject entry = entries.getJSONObject(i);
prefixMap.put(entry.getString("prefix"), entry.getInt("gcpLength"));
}
}catch (Exception e){
e.printStackTrace();
}
return prefixMap;
}
}
Complete DataWedge.java Implementation
package com.zebra.ssw.EncodeBarcodeToRFID;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import java.util.ArrayList;
import com.zebra.ssw.R;
public class DataWedge {
private static final String APPLICATION_ID = "com.zebra.ssw";
public static final String INTENT_NAME = APPLICATION_ID + ".BARCODE";
public static final String DATAWEDGE_EXTRA = "com.symbol.datawedge.data_string";
private static final String ACTION_DATAWEDGE = "com.symbol.datawedge.api.ACTION";
private static final String EXTRA_SET_CONFIG = "com.symbol.datawedge.api.SET_CONFIG";
/**
* Setup the datawedge profile for application
* @param context Application Context
*/
public static void setDWProfile(Context context) {
//Main
Bundle bMain = new Bundle();
bMain.putString("PROFILE_NAME", context.getString(R.string.app_name));
bMain.putString("PROFILE_ENABLED","true");
bMain.putString("CONFIG_MODE","CREATE_IF_NOT_EXIST");
// Create APP_LIST bundle to associate app with profile
Bundle appConfig = new Bundle();
appConfig.putString("PACKAGE_NAME", context.getPackageName());
appConfig.putStringArray("ACTIVITY_LIST", new String[]{"*"});
bMain.putParcelableArray("APP_LIST", new Bundle[]{appConfig});
Intent dwIntent = new Intent();
dwIntent.setAction(ACTION_DATAWEDGE);
dwIntent.putExtra(EXTRA_SET_CONFIG, bMain);
context.sendBroadcast(dwIntent);
//PLUGIN_CONFIG
Bundle bConfig = new Bundle();
bConfig.putString("PLUGIN_NAME","INTENT");
bConfig.putString("RESET_CONFIG","true");
//PARAM_LIST
Bundle bParams = new Bundle();
bParams.putString("intent_output_enabled","true");
bParams.putString("intent_action",INTENT_NAME);
bParams.putString("intent_category", Intent.CATEGORY_DEFAULT);
bParams.putInt("intent_delivery",2); //Use "0" for Start Activity, "1" for Start Service, "2" for Broadcast
bConfig.putBundle("PARAM_LIST", bParams);
bMain.putBundle("PLUGIN_CONFIG", bConfig);
// APP_LIST
Bundle bundleApp1 = new Bundle();
bundleApp1.putStringArray("ACTIVITY_LIST", new String[]{ "com.zebra.findit.*" });
bMain.putParcelableArray("APP_LIST", new Bundle[] { bundleApp1 });
//Send Intent to add Config
Intent i = new Intent();
i.setAction("com.symbol.datawedge.api.ACTION");
i.putExtra("com.symbol.datawedge.api.SET_CONFIG", bMain);
context.sendBroadcast(i);
//****************** Turn Off Keystroke and RFID *****************************
bMain = new Bundle();
bMain.putString("PROFILE_NAME",context.getString(R.string.app_name));
bMain.putString("PROFILE_ENABLED","true");
bMain.putString("CONFIG_MODE","UPDATE");
ArrayList plugins = new ArrayList<>();
//KEYSTROKE PLUGIN_CONFIG
bConfig = new Bundle();
bConfig.putString("PLUGIN_NAME","KEYSTROKE");
bConfig.putString("RESET_CONFIG","true");
bParams = new Bundle();
bParams.putString("keystroke_output_enabled","false");
bConfig.putBundle("PARAM_LIST", bParams);
plugins.add(bConfig);
//RFID PLUGIN_CONFIG
bConfig = new Bundle();
bConfig.putString("PLUGIN_NAME","RFID");
bConfig.putString("RESET_CONFIG","true");
bParams = new Bundle();
bParams.putString("rfid_input_enabled", "false");
bConfig.putBundle("PARAM_LIST", bParams);
plugins.add(bConfig);
bMain.putParcelableArrayList("PLUGIN_CONFIG", plugins);
i.setAction("com.symbol.datawedge.api.ACTION");
i.putExtra("com.symbol.datawedge.api.SET_CONFIG", bMain);
context.sendBroadcast(i);
// Register this
registerProfile(context);
}
public static void registerProfile(Context context) {
Bundle bMain = new Bundle();
bMain.putString("com.symbol.datawedge.api.APPLICATION_NAME", APPLICATION_ID);
bMain.putString("com.symbol.datawedge.api.NOTIFICATION_TYPE", "PROFILE_SWITCH");
Bundle bParams = new Bundle();
bParams.putString("PROFILE_IMPORTED", "FULL_DB_IMPORTED");
bParams.putString("PROFILE_NAME", context.getString(R.string.app_name));
bMain.putBundle("com.symbol.datawedge.api.NOTIFICATION", bParams);
Intent i = new Intent();
i.setAction("com.symbol.datawedge.api.ACTION");
i.putExtra("com.symbol.datawedge.api.REGISTER_FOR_NOTIFICATION", bMain);
context.sendBroadcast(i);
}
}
Complete BarcodeToRfidApi.java Implementation
package com.zebra.ssw;
import android.content.Context;
import android.content.Intent;
import com.zebra.ssw.EncodeBarcodeToRFID.DataWedge;
import com.zebra.ssw.EncodeBarcodeToRFID.EPC;
import com.zebra.ssw.EncodeBarcodeToRFID.EPCConverter;
public class BarcodeToRfidApi {
public static void initialize(Context context) {
EPCConverter.initEpcConverter(context);
DataWedge.setDWProfile(context);
}
public static EPC encodeBarcodeToRfid(String scannedUpc, String serialNumber) {
EPC epc = EPCConverter.encodeSGTIN96(scannedUpc, serialNumber);
return epc;
}
}