diff --git a/android/src/main/java/com/darryncampbell/rndatawedgeintents/RNDataWedgeIntentsModule.java b/android/src/main/java/com/darryncampbell/rndatawedgeintents/RNDataWedgeIntentsModule.java index aaf6318..16f763c 100644 --- a/android/src/main/java/com/darryncampbell/rndatawedgeintents/RNDataWedgeIntentsModule.java +++ b/android/src/main/java/com/darryncampbell/rndatawedgeintents/RNDataWedgeIntentsModule.java @@ -13,8 +13,12 @@ import android.content.IntentFilter; import android.os.Bundle; import org.json.JSONObject; +import org.json.JSONException; import org.json.JSONArray; import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; import java.util.Observable; import java.util.Observer; @@ -22,16 +26,22 @@ import java.util.HashMap; import java.util.Calendar; import java.util.Arrays; +import java.util.Iterator; import java.lang.SecurityException; import java.text.SimpleDateFormat; import java.text.ParseException; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.facebook.react.bridge.ReadableType; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; @@ -43,6 +53,7 @@ public class RNDataWedgeIntentsModule extends ReactContextBaseJavaModule impleme private static final String TAG = RNDataWedgeIntentsModule.class.getSimpleName(); + // THESE ACTIONS ARE DEPRECATED, PLEASE SPECIFY THE ACTION AS PART OF THE CALL TO sendBroadcastWithExtras private static final String ACTION_SOFTSCANTRIGGER = "com.symbol.datawedge.api.ACTION_SOFTSCANTRIGGER"; private static final String ACTION_SCANNERINPUTPLUGIN = "com.symbol.datawedge.api.ACTION_SCANNERINPUTPLUGIN"; private static final String ACTION_ENUMERATESCANNERS = "com.symbol.datawedge.api.ACTION_ENUMERATESCANNERS"; @@ -60,6 +71,8 @@ public class RNDataWedgeIntentsModule extends ReactContextBaseJavaModule impleme // Enumerated Scanner receiver private static final String ACTION_ENUMERATEDLISET = "com.symbol.datawedge.api.ACTION_ENUMERATEDSCANNERLIST"; private static final String KEY_ENUMERATEDSCANNERLIST = "DWAPI_KEY_ENUMERATEDSCANNERLIST"; + // END DEPRECATED PROPERTIES + // Scan data receiver private static final String RECEIVED_SCAN_SOURCE = "com.symbol.datawedge.source"; private static final String RECEIVED_SCAN_DATA = "com.symbol.datawedge.data_string"; @@ -159,9 +172,167 @@ public void sendIntent(String action, String parameterValue) this.reactContext.sendBroadcast(dwIntent); } + @ReactMethod + public void sendBroadcastWithExtras(ReadableMap obj) throws JSONException + { + String action = obj.hasKey("action") ? obj.getString("action") : null; + Intent i = new Intent(); + if (action != null) + i.setAction(action); + + Map intentMap = recursivelyDeconstructReadableMap(obj); + Map extrasMap = null; + if (intentMap.containsKey("extras") && intentMap.get("extras") != null && + intentMap.get("extras") instanceof Map) + extrasMap = (Map) intentMap.get("extras"); + + for (String key : extrasMap.keySet()) { + Object value = extrasMap.get(key); + String valueStr = String.valueOf(value); + // If type is text html, the extra text must sent as HTML + if (value instanceof Boolean) { + i.putExtra(key, Boolean.valueOf(valueStr)); + } else if(value instanceof Integer) { + i.putExtra(key, Integer.valueOf(valueStr)); + } else if(value instanceof Long) { + i.putExtra(key, Long.valueOf(valueStr)); + } else if(value instanceof Double) { + i.putExtra(key, Double.valueOf(valueStr)); + } else if(valueStr.startsWith("{")) + { + // UI has passed a JSON object + Bundle bundle = toBundle(new JSONObject(valueStr)); + i.putExtra(key, bundle); + } + else { + i.putExtra(key, valueStr); + } + } + + this.reactContext.sendBroadcast(i); + } + + // Credit: https://github.com/facebook/react-native/issues/4655 + private Map recursivelyDeconstructReadableMap(ReadableMap readableMap) { + ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + Map deconstructedMap = new HashMap<>(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + ReadableType type = readableMap.getType(key); + switch (type) { + case Null: + deconstructedMap.put(key, null); + break; + case Boolean: + deconstructedMap.put(key, readableMap.getBoolean(key)); + break; + case Number: + deconstructedMap.put(key, readableMap.getDouble(key)); + break; + case String: + deconstructedMap.put(key, readableMap.getString(key)); + break; + case Map: + deconstructedMap.put(key, recursivelyDeconstructReadableMap(readableMap.getMap(key))); + break; + case Array: + deconstructedMap.put(key, recursivelyDeconstructReadableArray(readableMap.getArray(key))); + break; + default: + throw new IllegalArgumentException("Could not convert object with key: " + key + "."); + } + } + return deconstructedMap; + } + + // Credit: https://github.com/facebook/react-native/issues/4655 + private List recursivelyDeconstructReadableArray(ReadableArray readableArray) { + List deconstructedList = new ArrayList<>(readableArray.size()); + for (int i = 0; i < readableArray.size(); i++) { + ReadableType indexType = readableArray.getType(i); + switch(indexType) { + case Null: + deconstructedList.add(i, null); + break; + case Boolean: + deconstructedList.add(i, readableArray.getBoolean(i)); + break; + case Number: + deconstructedList.add(i, readableArray.getDouble(i)); + break; + case String: + deconstructedList.add(i, readableArray.getString(i)); + break; + case Map: + deconstructedList.add(i, recursivelyDeconstructReadableMap(readableArray.getMap(i))); + break; + case Array: + deconstructedList.add(i, recursivelyDeconstructReadableArray(readableArray.getArray(i))); + break; + default: + throw new IllegalArgumentException("Could not convert object at index " + i + "."); + } + } + return deconstructedList; + } + + // https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent/blob/master/src/android/IntentShim.java + private Bundle toBundle(final JSONObject obj) { + Bundle returnBundle = new Bundle(); + if (obj == null) { + return null; + } + try { + Iterator keys = obj.keys(); + while(keys.hasNext()) + { + String key = (String)keys.next(); + Object compare = obj.get(key); + if (obj.get(key) instanceof String) + returnBundle.putString(key, obj.getString(key)); + else if (obj.get(key) instanceof Boolean) + returnBundle.putBoolean(key, obj.getBoolean(key)); + else if (obj.get(key) instanceof Integer) + returnBundle.putInt(key, obj.getInt(key)); + else if (obj.get(key) instanceof Long) + returnBundle.putLong(key, obj.getLong(key)); + else if (obj.get(key) instanceof Double) + returnBundle.putDouble(key, obj.getDouble(key)); + else if (obj.get(key).getClass().isArray() || obj.get(key) instanceof JSONArray) + { + JSONArray jsonArray = obj.getJSONArray(key); + int length = jsonArray.length(); + if (jsonArray.get(0) instanceof String) + { + String[] stringArray = new String[length]; + for (int j = 0; j < length; j++) + stringArray[j] = jsonArray.getString(j); + returnBundle.putStringArray(key, stringArray); + //returnBundle.putParcelableArray(key, obj.get); + } + else + { + Bundle[] bundleArray = new Bundle[length]; + for (int k = 0; k < length ; k++) + bundleArray[k] = toBundle(jsonArray.getJSONObject(k)); + returnBundle.putParcelableArray(key, bundleArray); + } + } + else if (obj.get(key) instanceof JSONObject) + returnBundle.putBundle(key, toBundle((JSONObject)obj.get(key))); + } + } + catch (JSONException e) { + e.printStackTrace(); + } + + return returnBundle; + } + @ReactMethod public void registerReceiver(String action, String category) { + // THIS METHOD IS DEPRECATED Log.d(TAG, "Registering an Intent filter for action: " + action); this.registeredAction = action; this.registeredCategory = category; @@ -181,6 +352,46 @@ public void registerReceiver(String action, String category) this.reactContext.registerReceiver(scannedDataBroadcastReceiver, filter); } + @ReactMethod + public void registerBroadcastReceiver(ReadableMap filterObj) + { + try + { + this.reactContext.unregisterReceiver(genericReceiver); + } + catch (IllegalArgumentException e) + { + // Expected behaviour if there was not a previously registered receiver. + } + IntentFilter filter = new IntentFilter(); + if (filterObj.hasKey("filterActions")) + { + ReadableType type = filterObj.getType("filterActions"); + if (type == ReadableType.Array) + { + ReadableArray actionsArray = filterObj.getArray("filterActions"); + for (int i = 0; i < actionsArray.size(); i++) + { + filter.addAction(actionsArray.getString(i)); + } + } + } + if (filterObj.hasKey("filterCategories")) + { + ReadableType type = filterObj.getType("filterCategories"); + if (type == ReadableType.Array) + { + ReadableArray categoriesArray = filterObj.getArray("filterCategories"); + for (int i = 0; i < categoriesArray.size(); i++) + { + filter.addCategory(categoriesArray.getString(i)); + } + } + } + this.reactContext.registerReceiver(genericReceiver, filter); + + } + // Broadcast receiver for the response to the Enumerate Scanner API public BroadcastReceiver myEnumerateScannersBroadcastReceiver = new BroadcastReceiver() { @@ -202,6 +413,16 @@ public void onReceive(Context context, Intent intent) { } }; + public BroadcastReceiver genericReceiver = new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) { + Log.v(TAG, "Received Broadcast from DataWedge"); + intent.putExtra("v2API", true); + ObservableObject.getInstance().updateValue(intent); + } + }; + // Sending events to JavaScript as defined in the native-modules documentation. // Note: Callbacks can only be invoked a single time so are not a suitable interface for barcode scans. private void sendEvent(ReactContext reactContext, @@ -215,6 +436,16 @@ private void sendEvent(ReactContext reactContext, public void update(Observable observable, Object data) { Intent intent = (Intent)data; + + if (intent.hasExtra("v2API")) + { + Bundle intentBundle = intent.getExtras(); + intentBundle.remove("com.symbol.datawedge.decode_data"); // fb converter cannot cope with byte arrays + intentBundle.remove("com.motorolasolutions.emdk.datawedge.decode_data"); // fb converter cannot cope with byte arrays + WritableMap map = Arguments.fromBundle(intentBundle); + sendEvent(this.reactContext, "datawedge_broadcast_intent", map); + } + String action = intent.getAction(); if (action.equals(ACTION_ENUMERATEDLISET)) { diff --git a/index.js b/index.js index 5c33468..ec29fec 100644 --- a/index.js +++ b/index.js @@ -21,9 +21,17 @@ var DataWedgeIntents = { DISABLE_PLUGIN: RNDataWedgeIntents.DISABLE_PLUGIN, sendIntent(action, parameterValue) { + // THIS METHOD IS DEPRECATED, use SendBroadcastWithExtras RNDataWedgeIntents.sendIntent(action, parameterValue); }, + sendBroadcastWithExtras(extrasObject) { + RNDataWedgeIntents.sendBroadcastWithExtras(extrasObject); + }, + registerBroadcastReceiver(filter) { + RNDataWedgeIntents.registerBroadcastReceiver(filter); + }, registerReceiver(action, category) { + // THIS METHOD IS DEPRECATED, use registerBroadcastReceiver RNDataWedgeIntents.registerReceiver(action, category); }, }; diff --git a/package.json b/package.json index a95c0b2..e9a2e5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-datawedge-intents", - "version": "0.0.2", + "version": "0.1.0", "description": "React Native Android module to interface with Zebra's DataWedge using Android Intents to control the barcode scanner and retrieve scanned data", "main": "index.js", "scripts": { @@ -26,6 +26,6 @@ "repository": "https://github.com/darryncampbell/react-native-datawedge-intents", "bugs": "https://github.com/darryncampbell/react-native-datawedge-intents/issues", "peerDependencies": { - "react-native": ">=0.27" + "react-native": ">=0.56" } }