Overview
Use content providers option allows applications to leverage DataWedge's content provider to retrieve scanned data from files larger than 500 KB, such as images, primarily with Decoder Signature and NextGen SimulScan. A content provider is an Android app component that encapsulates data to share between apps.
DataWedge's content provider only sends 768 KB (786,432 bytes) of raw data at once through Android's ContentProvider
class object. If the scanned data exceeds this limit, the remaining data is sent in a subsequent ContentProvider
object, whose URI is included in the first ContentProvider
, with the same size limit. The URI is a string used to query a content provider to retrieve the data, containing the provider and path. This process in breaking down the ContentProvider
into separate parts repeats until the actual file size is reached for the raw scanned data. The client application then needs to concatenate the raw data from all ContentProviders
to reconstruct the final full raw data object in byte[] form.
Requirements
- DataWedge version 8.0 or higher (find the version)
- Built-in camera on mobile computer
- For Zebra Professional Series devices, such as TC21 and TC26, a Mobility DNA Enterprise license is required.
Package Visibility
IMPORTANT: Due to package visibility restrictions imposed by Android 11 (API 30), DataWedge apps targeting Android 11 or later must include the following <queries>
element in the AndroidManifest.xml file:
<queries>
<package android:name="com.symbol.datawedge" />
</queries>
Content Provider
Follow the steps in the following sections to acquire data using Content Provider.
1. Change the Android Manifest
The following permission must be added to the manifest of the Android application so it can be granted access to the DataWedge content provider:
<uses-permission android:name="com.symbol.datawedge.permission.contentprovider" />
2. Enable Content Provider
Choose one of the following options to enable Content Provider:
2a. Enable Content Provider via DataWedge UI
To enable the content provider option via DataWedge UI, navigate to the Intent Output section of the DataWedge profile and enable Intent Output.
Intent Output
Then enable the option Use content providers.
Use Content Providers
2b. Enable Content Provider via DataWedge Intent API
To enable the content provider option via DataWedge Intent API, use intent key intent_use_content_provider with supported value true or false.
The following sample code demonstrates how to enable content provider for profile Profile009:
Bundle bMain = new Bundle();
ArrayList<Bundle> bundlePluginConfig = new ArrayList<>();
Bundle bConfigIntent = new Bundle();
Bundle bParamsIntent = new Bundle();
bParamsIntent.putString("intent_output_enabled", "true");
bParamsIntent.putString("intent_use_content_provider", "true");
bConfigIntent.putString("PLUGIN_NAME", "INTENT");
bConfigIntent.putString("RESET_CONFIG", "true");
bConfigIntent.putBundle("PARAM_LIST", bParamsIntent);
bundlePluginConfig.add(bConfigIntent);
bMain.putParcelableArrayList("PLUGIN_CONFIG", bundlePluginConfig);
bMain.putString("PROFILE_NAME", "Profile009");
bMain.putString("PROFILE_ENABLED", "true");
bMain.putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST");
Intent iSetConfig = new Intent();
iSetConfig.setAction("com.symbol.datawedge.api.ACTION");
iSetConfig.putExtra("com.symbol.datawedge.api.SET_CONFIG", bMain);
iSetConfig.putExtra("SEND_RESULT", "LAST_RESULT");
this.sendBroadcast(iSetConfig);
3. Process Intent Output with Content Provider
When configured to use the content provider, DataWedge first sends the scan data intent based on how it is configured in the Intent Output plugin.
An Intent extra contains a URI for the DataWedge content provider. The application must use the content provider URI to query the data into a Cursor
object using the ContentResolver
.
Parameters
Signature presence status - when it is required to check for the presence of a handwritten signature during Document Capture, DataWedge exposes a signature presence status parameter through the existing Content Provider cursor. This returns whether a signature is present or absent, determines if the signature presence status is supported, or if the signature presence status was not requested. Signature presence status should be requested when the Document Capture template is being created or edited.
Cursor Column Name | Column Value |
---|---|
field_signature_status | -2 – Signature presence status is not supported by the underlying scanner framework -1 – Signature presence status is not requested 0 – Signature is not present 1 – Signature is present |
Sample Code
Process scan data with NextGen SimulScan
The following sample code demonstrates how to process scan data with NextGen SimulScan:
public class MainActivity extends AppCompatActivity {
public static final String DATA_NEXT_URI = "next_data_uri";
public static final String FULL_DATA_SIZE = "full_data_size";
public static final String RAW_DATA_SIZE = "data_buffer_size";
public static final String DECODE_DATA_EXTRA = "com.symbol.datawedge.decode_data";
public static final String INTENT_OUTPUT_ACTION = "com.symbol.genericdata.INTENT_OUTPUT";
public static final String LABEL_TYPE_SIGNATURE = "LABEL-TYPE-SIGNATURE";
public static final String FIELD_DATA_URI = "com.symbol.datawedge.field_data_uri";
public static final String DECODED_MODE = "com.symbol.datawedge.decoded_mode";
public static final String DATA_TAG = "com.symbol.datawedge.data";
public static final String LABEL_TYPE = "label_type";
public static final String FIELD_LABEL_TYPE = "field_label_type";
public static final String DATA_STRING = "field_string_data";
public static final String DECODE_DATA = "field_raw_data";
public static final String IMAGE_WIDTH_TAG = "field_image_width";
public static final String IMAGE_HEIGHT_TAG = "field_image_height";
public static final String SINGLE_DECODE_MODE = "single_decode";
public static final String MULTIPLE_DECODE_MODE = "multiple_decode";
public static final String STRING_DATA_KEY = "com.symbol.datawedge.data_string";
public static final String LABEL_TYPE_TAG = "com.symbol.datawedge.label_type";
public static final String STRING_DATA_KEY_SINGLE_BARCODE = "data_string";
private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
try {
String action = intent.getAction();
Bundle extras = intent.getExtras();
/* ###### Processing scanned data from Intent output [Start] ###### */
if (action.equals(INTENT_OUTPUT_ACTION)) {
Thread dataProcessingThrad = new Thread(new Runnable() {
@Override
public void run() {
Bundle data = intent.getExtras();
if (data != null) {
String decodedMode = data.getString(DECODED_MODE);
/* ###### Processing scanned data when ScanningMode is set as "Single" [Start] ###### */
if (decodedMode.equals(SINGLE_DECODE_MODE)) {
processSingleDecode(data);
}
/* ###### Processing scanned data when ScanningMode is set as "Single" [Finish] ###### */
/* ###### Processing scanned data when ScanningMode is set as "SimulScan" [Start] ###### */
else if (decodedMode.equals(MULTIPLE_DECODE_MODE)) {
processMultipleDecode(data);
}
/* ###### Processing scanned data when ScanningMode is set as "SimulScan" [Finish] ###### */
}
}
});
dataProcessingThrad.start();
}
/* ###### Processing scanned data from Intent output [Finish] ###### */
} catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
};
private void processSingleDecode(Bundle data)
{
String decodeDataUri = data.getString(DECODE_DATA_EXTRA);
String barcodeData = "";
//Check if the data coming through the content provider.
if(decodeDataUri != null) {
//Data is coming through the content provider, using a Cursor object to extract data
Cursor cursor = getContentResolver()
.query(Uri.parse(decodeDataUri), null, null, null);
if (cursor != null) {
cursor.moveToFirst();
String labelType = cursor
.getString(cursor.getColumnIndex(LABEL_TYPE));
String dataString = cursor
.getString(cursor.getColumnIndex(STRING_DATA_KEY_SINGLE_BARCODE));
barcodeData += "\nLabel type: " + labelType;
barcodeData += "\nString data: " + dataString;
}
}
else
{
//Data is coming through the Intent bundle itself
String labelType = data.getString(LABEL_TYPE_TAG);
String dataString = data.getString(STRING_DATA_KEY);
barcodeData += "\nLabel type: " + labelType;
barcodeData += "\nString data: " + dataString;
}
TextView txtBarcodeData = new TextView(getApplicationContext());
txtBarcodeData.setText(barcodeData);
showInUI(txtBarcodeData, null);
updateStatus("Data processing successful");
}
private void processMultipleDecode(Bundle data)
{
ArrayList<Bundle> fields = data.getParcelableArrayList(DATA_TAG);
if(fields == null) //Content provider is not enabled in Intent Output plugin or Scanning mode is not selected as "SimulScan"
{
updateStatus("Content provider is not enabled in Intent Output plugin " +
"or Scanning mode is not selected as \"SimulScan\".\nPlease check and try again");
return;
}
//Iterate through each field
for (Bundle field : fields) {
String decodeDataUri = field.getString(FIELD_DATA_URI);
Cursor cursor = null;
if(decodeDataUri != null)
cursor = getContentResolver().query(Uri.parse(decodeDataUri),
null, null, null);
if (cursor != null) {
int imgWidth = 0;
int imgHeight = 0;
cursor.moveToFirst();
String strResultStatusData = "";
String labelType = cursor.
getString(cursor.getColumnIndex(FIELD_LABEL_TYPE));
strResultStatusData += "\nLabel type: " + labelType;
if (labelType.equals(LABEL_TYPE_SIGNATURE)) {
strResultStatusData += "\nImage data: ";
imgWidth = cursor.getInt(cursor.getColumnIndex(IMAGE_WIDTH_TAG));
imgHeight = cursor.getInt(cursor.getColumnIndex(IMAGE_HEIGHT_TAG));
} else {
String dataString = cursor
.getString(cursor.getColumnIndex(DATA_STRING));
strResultStatusData += "\nString data: " + dataString;
}
String nextURI = cursor.getString(cursor.getColumnIndex(DATA_NEXT_URI));
byte[] binaryData = null;
if (nextURI.isEmpty()) { //No data chunks. All data are available in one chunk
binaryData = cursor.getBlob(cursor.getColumnIndex(DECODE_DATA));
} else {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final String fullDataSize = cursor
.getString(cursor.getColumnIndex(FULL_DATA_SIZE));
int bufferSize = cursor.getInt(cursor
.getColumnIndex(RAW_DATA_SIZE));
baos.write(cursor.getBlob(cursor
.getColumnIndex(DECODE_DATA))); //Read the first chunk from initial set
while (!nextURI.isEmpty()) {
Cursor imageDataCursor = getContentResolver()
.query(Uri.parse(nextURI), null,
null, null);
if (imageDataCursor != null) {
imageDataCursor.moveToFirst();
bufferSize += imageDataCursor
.getInt(imageDataCursor
.getColumnIndex(RAW_DATA_SIZE));
byte[] bufferData = imageDataCursor
.getBlob(imageDataCursor
.getColumnIndex(DECODE_DATA));
baos.write(bufferData);
nextURI = imageDataCursor
.getString(imageDataCursor
.getColumnIndex(DATA_NEXT_URI));
}
imageDataCursor.close();
updateStatus("Data being processed, please wait..\n" +
bufferSize + "/" + fullDataSize + " bytes merged");
}
binaryData = baos.toByteArray();
baos.close();
} catch (final Exception ex) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, ex.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
}
}
final TextView txtBarcodeData = new TextView(getApplicationContext());
txtBarcodeData.setText(strResultStatusData);
showInUI(txtBarcodeData,null);
if (labelType.equals(LABEL_TYPE_SIGNATURE)) {
try {
//-- Creating YUV Image and Bitmap Image [Start]
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuvImage = new YuvImage(binaryData, ImageFormat.NV21,
imgWidth, imgHeight, null);
yuvImage.compressToJpeg(new Rect(0, 0, imgWidth, imgHeight),
50, out);
byte[] imageBytes = out.toByteArray();
Bitmap bmp = null;
if (binaryData != null) {
bmp = BitmapFactory.decodeByteArray(imageBytes, 0,
imageBytes.length);
}
final ImageView img = new ImageView(getApplicationContext());
img.setImageBitmap(bmp);
showInUI(null,img);
//-- Creating YUV Image and Bitmap Image [Finish]
}
catch (final Exception ex)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
"Error: " + ex.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
}
updateStatus("Data processing successful");
}
void updateStatus(String status)
{
//Show status in UI
}
private void showInUI(final TextView textView, final ImageView imageView)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
if(textView != null)
//Add text view to UI
if(imageView != null)
//Add image view to UI
}
});
}
}
Signature Capture Sample Code
The following sample code demonstrates how to process scan data with Signature Capture:
public static final String BARCODE_IMAGE_FORMAT_TAG = "image_format";
public static final String BARCODE_SIGNATURE_TYPE_TAG = "signature_type";
public static final String BARCODE_IMAGE_SIZE_TAG = "image_size";
public static final String BARCODE_IMAGE_DATA_TAG = "com.symbol.datawedge.image_data";
public static final String SIGNATURE_IMAGE_DATA = "image_data";
public static final String DATA_NEXT_URI = "next_data_uri";
public static final String FULL_DATA_SIZE = "full_data_size";
public static final String RAW_DATA_SIZE = "data_buffer_size";
public static final String INTENT_OUTPUT_ACTION = "data_buffer_size";
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context,final Intent intent) {
try {
String action = intent.getAction();
//Process scanned data from Intent output
if (action.equals(INTENT_OUTPUT_ACTION)) {
Bundle data = intent.getExtras();
if (data != null) {
String imageDataUri = data.getString(BARCODE_IMAGE_DATA_TAG);
if(imageDataUri == null)
return;
Cursor cursor = getContentResolver().query(Uri.parse(imageDataUri), null, null, null);
if (cursor != null) {
cursor.moveToFirst();
String format = cursor.getString(cursor.getColumnIndex(BARCODE_IMAGE_FORMAT_TAG));
String sigType = cursor.getString(cursor.getColumnIndex(BARCODE_SIGNATURE_TYPE_TAG));
String size = cursor.getString(cursor.getColumnIndex(BARCODE_IMAGE_SIZE_TAG));
String text = "Image format: " + format + "\n";
text += "Signature type: " + sigType + "\n";
text += "Image size: " + size + "\n";
final TextView txt = new TextView(getApplicationContext());
txt.setText(text);
String nextURI = cursor.getString(cursor.getColumnIndex(DATA_NEXT_URI));
byte[] binaryData = null;
if (nextURI.isEmpty()) { //No data chunks. All data are available in one chunk
binaryData = cursor.getBlob(cursor.getColumnIndex(SIGNATURE_IMAGE_DATA));
} else {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final String fullDataSize = cursor.getString(cursor.getColumnIndex(FULL_DATA_SIZE));
int bufferSize = cursor.getInt(cursor.getColumnIndex(RAW_DATA_SIZE));
baos.write(cursor.getBlob(cursor.getColumnIndex(SIGNATURE_IMAGE_DATA))); //Read the first chunk from initial set
while (!nextURI.isEmpty()) {
Cursor imageDataCursor = getContentResolver().query(Uri.parse(nextURI), null, null, null);
if (imageDataCursor != null) {
imageDataCursor.moveToFirst();
bufferSize += imageDataCursor.getInt(imageDataCursor.getColumnIndex(RAW_DATA_SIZE));
byte[] bufferData = imageDataCursor.getBlob(imageDataCursor.getColumnIndex(SIGNATURE_IMAGE_DATA));
baos.write(bufferData);
nextURI = imageDataCursor.getString(imageDataCursor.getColumnIndex(DATA_NEXT_URI));
}
imageDataCursor.close();
final int finalBufferSize = bufferSize;
Log.d("TAG","Data being processed, please wait..\n" + finalBufferSize + "/" + fullDataSize + " bytes merged");
}
binaryData = baos.toByteArray();
baos.close();
}
catch (final Exception ex)
{
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Bitmap bmp = null;
if (binaryData != null) {
bmp = BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length);
}
final ImageView img = new ImageView(getApplicationContext());
img.setImageBitmap(bmp);
}
Log.d("TAG","Data processing successful");
}
}
} catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
};
Related Guides: