My application has the need to enable and disable both the scanner and the msr at various points throughout a sequence of operations. For example, there are situations where scanner input is allowed but MSR input is not. My plan for implementing this behavior was to create a profile with characteristic type "MSR", emdk_name="theMsr" that initially has msr_input_enabled set to false and a characteristic type "Barcode", emdk_name="theScanner" that has scanner_input_enabled also set to false.
Then at the appropriate type I call processProfileAsync with name/value pair as follows:
to enable scanner: theScanner.scanner_input_enabled=true
to disable scanner: theScanner.scanner_input_enabled=false
to enable msr:theMsr.msr_input_enabled=true
to disable msr: theMsr.msr_input_enabled=false
Every time I call processProfileAsync the onData callback indicates success but it does not work reliably. Also, it seems to take upwards of one second to process the profile. My application needs to be able to switch the scanner and msr off/on very quickly and this one second delay is causing me problems.
WRT to reliability: It seems if you call processProfile twice in rapid succession there are problems. If I try to enable both devices by clicking the checkboxes as fast as I can I only get one onData callback (shouldn't I be getting two? one for each processProfile call) and it is random whether both devices will be enabled. Sometimes the scanner gets enabled sometimes the MSR. Same with disabling. the onData callback says the operation worked but the scanner is still enabled.
I wrote a simple test program that has check boxes to enable and disable the devices. What am I doing wrong?
Attached is project demonstrating the issues I'm seeing. Inline are my main activity source and profile.
Thanks
Start MainActivity.java-----------------------------------------
package com.example.johnfoley.myapplication;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import com.symbol.emdk.EMDKManager;
import com.symbol.emdk.EMDKManager.EMDKListener;
import com.symbol.emdk.EMDKResults;
import com.symbol.emdk.ProfileManager;
import com.symbol.emdk.payment.PaymentManager;
import static com.symbol.emdk.ProfileManager.CreateNameValuePair;
public class MainActivity extends Activity implements EMDKListener, ProfileManager.DataListener {
private String profileName = "QVSProfile";
private ProfileManager mProfileManager = null;
private EMDKManager emdkManager = null;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//The EMDKManager object will be created and returned in the callback. EMDKResults results = EMDKManager.getEMDKManager(getApplicationContext(), this);
//Check the return status of getEMDKManager if (results.statusCode == EMDKResults.STATUS_CODE.FAILURE) {
Log.d("", "getEMDKManager() failed");
}
}
@Override public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId();
//noinspection SimplifiableIfStatement if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override public void onOpened(EMDKManager emdkManager) {
if (emdkManager != null) {
//Get the ProfileManager object to process the profiles mProfileManager = (ProfileManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.PROFILE);
if (mProfileManager != null) {
try {
mProfileManager.addDataListener(this);
EMDKResults results = mProfileManager.processProfileAsync(profileName, ProfileManager.PROFILE_FLAG.SET, (String[]) null);
if (results.statusCode == EMDKResults.STATUS_CODE.FAILURE) {
Log.d("", "processProfile() failed");
}
} catch (Exception ex) {
Log.d("", "Exception in onOpened", ex);
}
}
}
}
@Override public void onDestroy() {
super.onDestroy();
//Clean up the objects created by EMDK manager if (emdkManager != null)
emdkManager.release();
}
@Override public void onClosed() {
}
public void onScannerEnabledClick(View view) {
boolean checked = ((CheckBox) view).isChecked();
String[] xtraData = new String[1];
xtraData[0] = CreateNameValuePair("theScanner", "scanner_input_enabled", checked ? "true" : "false");
EMDKResults results = mProfileManager.processProfileAsync(profileName, ProfileManager.PROFILE_FLAG.SET, xtraData);
Log.d("","SCANCMD:" + xtraData[0]);
if (results.statusCode == EMDKResults.STATUS_CODE.FAILURE) {
Log.d("", "processProfile() scanner enable/disable failed");
}
}
public void onMsrEnabledClick(View view) {
boolean checked = ((CheckBox) view).isChecked();
String[] xtraData = new String[1];
xtraData[0] = CreateNameValuePair("theMsr", "msr_input_enabled", checked ? "true" : "false");
EMDKResults results = mProfileManager.processProfileAsync(profileName, ProfileManager.PROFILE_FLAG.SET, xtraData);
Log.d("","MSRCMD:" + xtraData[0]);
if (results.statusCode == EMDKResults.STATUS_CODE.FAILURE) {
Log.d("", "processProfile() msr enable/disable failed");
}
}
@Override public void onData(ProfileManager.ResultData resultData) {
if (resultData.getResult().statusCode == EMDKResults.STATUS_CODE.FAILURE) {
Log.d("", "async process profile failed");
} else {
Log.d("", "async process profile succeeded");
}
}
@Override public void onNewIntent(Intent i) {
handleDecodeData(i);
}
private static final String SOURCE_TAG = "com.motorolasolutions.emdk.datawedge.source";
private static final String DATA_STRING_TAG = "com.motorolasolutions.emdk.datawedge.data_string";
private static final String LABEL_TYPE_TAG = "com.motorolasolutions.emdk.datawedge.label_type";
private static int msrReadCount = 0;
private void handleDecodeData(Intent i) {
// check the intent action is for us if (i.getAction().contentEquals("com.example.johnfoley.myapplication.RECVR")) {
String out = "";
// get the source of the data String source = i.getStringExtra(SOURCE_TAG);
// save it to use later if (source == null)
source = "scanner";
String data = i.getStringExtra(DATA_STRING_TAG);
Integer data_len = 0;
if (data != null)
data_len = data.length();
// check if the data has come from the barcode scanner if (source.equalsIgnoreCase("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 String 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 = sLabelType + ":";
}
}
// check if the data has come from the MSR if (source.equalsIgnoreCase("msr")) {
msrReadCount++;
// construct the beginning of our output string out = "(" + msrReadCount + ") ";
}
// let's get our edit box view EditText et = (EditText) findViewById(R.id.output);
et.setText(out + data);
}
}
public void onScannerData(String s) {
// let's get our edit box view EditText et = (EditText) findViewById(R.id.output);
et.setText(s);
}
}
End MainActivity.java-----------------------------------------
Start EMDKConfig.xml-----------------------------------------
End EMDKConfig.xml-----------------------------------------
2 Replies
John
Besides using Native Barcode API for Barcode and DataCapture Profile for MSR, you can also try this method, which is an intent based scheme for Datawedge:
1. Create four DataWedge profiles with four Scanner/MSR combinations (Do not set a application association to any of the profiles)
2. By using the SwitchToProfile API intent switch to the required profile. This way you do not have to modify the profiles and just switch to the required configuration by broadcasting a intent.
Following is the extract from the API documentation on how to
SwitchToProfile
DESCRIPTION
The SwitchToProfile API action can be used to switch to the specified profile.
PROFILES RECAP
DataWedge is based on profiles and plug-ins. A profile contains information on how DataWedge should behave with different applications.
Profile information consists of:
· Associated application
· Input plug-in configurations
· Output plug-in configurations
· Process plug-in configurations
DataWedge includes a default profile, Profile0, that is created automatically the first time DataWedge runs.
Using profiles, each application can have a specific DataWedge configuration. For example, each user application can have a profile which outputs scanned data in the required format when that application comes to the foreground.DataWedge can be configured to process the same set of captured data differently based on the requirements of each application.
Note
A single profile may be associated with one or many activities/apps, however, given an acitivty, only one profile may be associated to it.
USAGE SCENARIO
Let’s say an application has two activities. ActivityA only requires EAN13 barcodes to be scanned. ActivityB only requires MSR card data. ProfileB is configured to only scan EAN13 barcodes and is left un-associated. ProfileM is configured to only accept MSR input and is left un-associated. When ActivityA launches it uses switchToProfile to activate ProfileB. Similarily, when ActivityB launches it uses switchToProfile to activate ProfileM.
If another activity/app comes to the foreground, DataWedge auto profile switching will set the DataWedge profile accordingly either to the default profile or to an associated profile.
When ActivityA (or ActivityB) comes back to the foreground it will use switchToProfile to reset the profile back to ProfileB (or ProfileM).
FUNCTION PROTOTYPE
Intent i = new Intent();i.setAction(ACTION);i.putExtra(EXTRA_DATA, "");PARAMETERS
ACTION:
String "com.symbol.datawedge.api.ACTION_SWITCHTOPROFILE"
EXTRA_DATA:
String "com.symbol.datawedge.api.EXTRA_PROFILENAME"
:
The profile name to switch to as a string (case-sensitive).
RETURN VALUES
None.
Error and debug messages will be logged to the Android logging system which then can be viewed and filtered by the logcat command. You can use logcat from an ADB shell to view the log messages, e.g.
$ adb logcat -s DWAPIError messages will be logged for invalid actions, parameters and failures (e.g. profile not found or associated to an application).
EXAMPLE
// define action and data stringsString switchToProfile = "com.symbol.datawedge.api.ACTION_SWITCHTOPROFILE";String extraData = "com.symbol.datawedge.api.EXTRA_PROFILENAME"; public void onResume() { super.onResume(); // create the intent Intent I = new Intent(); // set the action to perform i.setAction(switchToProfile); // add additional info i.putExtra(extraData, "myProfile"); // send the intent to DataWedge context.this.sendBroadcast(i);}COMMENTS
This API function will have no effect if the specified profile does not exist or if the specified profile is associated to an application.
DataWedge has a one-to-one relationship between profiles and activities, i.e. only one profile can be associated to any given activity. When a profile is initially created, it is not associated to any application. Until this profile is associated to an activity it will never be activated. In this way it is possible to create multiple profiles that are un-associated.
This API function allows you to switch to one such un-associated profile.
For example, let’s say that ProfileA is one such un-associated profile and ProfileB is associated with activity B. Now, activity A is launched and uses this DataWedge API intent to switch to profileA. ProfileA will be active whilst activity A is in foreground. When another activity, say activity B, comes to the foreground DataWedge will automatically switch profile (to profileB which is associated to activity B for example). Then when activity A comes back to the foreground again, the app will need to use this DataWedge API intent to switch back to profileA. This would be done in the onResume method of activity A.
Note
Because DataWedge will automatically switch profile when your activity is paused, it is recommended that this API function be called from the onResume method of your activity.
Note
After switching to a profile, this un-associated profile does not get assigned to the application/activity and is available to be used in the future with a different app/activity.
Note
DataWedge automatic profile switching
For backward compatibility, DataWedge’s automatic profile switching is not affected by the above API commands, and this also why the above API commands only work with un-associated profiles and apps.
DataWedge auto profile switching works as follows…
Every second…
1. Set newProfileId to the associated profile id of the current foreground activity
2. If no associated profile found then set newProfileId to the associated profile id of the current foreground app
3. if no associated profile found then set newProfileId to the current default profile (note: this may not be Profile0)
4. Check the newProfileId against the currentProfileId
5. If they are different then…
a. deactivate current profile
b. activate new profile (newProfileId)
c. set currentProfileId = newProfileId
John,
Does your application allow for having scanning and MSR being performed on different activities? You can use the Activity Selector feature to help automate the process. Have you also considered using the Barcode APIs as opposed to using Data Capture profiles?