Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions src/main/java/org/medicmobile/webapp/mobile/ChtExternalApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ private void setIntentExtras(Intent intent, String key, JSONObject data) {
return;
}

// ODK does not have boolean data type
if (value instanceof String strValue && ("true".equals(strValue) || "false".equals(strValue))) {
value = Boolean.parseBoolean(strValue);
}

intent.putExtra(key, (Serializable) value);

} catch (Exception exception) {
Expand Down Expand Up @@ -234,7 +239,7 @@ public Optional<JSONObject> getData() {
return Optional.empty();
}

JSONObject json = parseBundleToJson(intent.getExtras());
JSONObject json = parseBundleToJson(null, intent.getExtras());
if (json == null) {
return Optional.empty();
}
Expand All @@ -244,13 +249,13 @@ public Optional<JSONObject> getData() {

//> PRIVATE

private JSONObject parseBundleToJson(Bundle bundle) {
private JSONObject parseBundleToJson(String parentKey, Bundle bundle) {
try {
JSONObject json = new JSONObject();
bundle
.keySet()
.iterator()
.forEachRemaining(key -> setValueInJson(key, json, bundle));
.forEachRemaining(key -> setValueInJson(parentKey, key, json, bundle));
return json;

} catch (Exception exception) {
Expand All @@ -260,33 +265,36 @@ private JSONObject parseBundleToJson(Bundle bundle) {
return null;
}

private void setValueInJson(String key, JSONObject json, Bundle bundle) {
private void setValueInJson(String parentKey, String childKey, JSONObject json, Bundle bundle) {
String key = ("detectedRdt".equals(parentKey) && "concerns".equals(childKey))
? "detectionConcerns"
: childKey;
try {
Object value = bundle.get(key);
Object value = bundle.get(childKey);

if (value instanceof Bitmap) {
json.put(key, parseBitmapImageToBase64((Bitmap) value));
json.put(key, parseBitmapImageToBase64((Bitmap) value, false));
return;
}

if (value instanceof Bundle) {
json.put(key, parseBundleToJson((Bundle) value));
json.put(key, parseBundleToJson(key, (Bundle) value));
return;
}

if (isBundleList(value)) {
JSONArray jsonArray = ((List<Bundle>) value)
.stream()
.map(this::parseBundleToJson)
.map(val -> this.parseBundleToJson(key, val))
.collect(Collector.of(JSONArray::new, JSONArray::put, JSONArray::put));

json.put(key, jsonArray);
return;
}

Optional<Uri> imagePath = getImageUri(value);
if (imagePath.isPresent()) {
json.put(key, getImageFromStoragePath(imagePath.get()));
boolean keepFullResolution = bundle.getBoolean("sampleImage", false);
json.put(key, getImageFromStoragePath(imagePath.get(), keepFullResolution));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tweak allows us to save the returned image uncompressed when samepleImage is true. This is needed to be able to return the images marked for sampling to Audere (they cannot use compressed versions of the images when training the model).

Note that the difference between 100 quality (full resolution) and 75 quality (compressed) may seem minor, but in my unscientific measurements I was seeing the ultimate size of the png file for the full resolution image to be ~5x greater than the compressed image. For example, when testing with my phone with a 16MP camera, the compressed image size (of the png attachment uploaded to Couch) was 1.4MB, but the uncompressed image size (saved when sampleImage: true) was 5.8MB.

return;
}

Expand Down Expand Up @@ -321,7 +329,7 @@ private Optional<Uri> getImageUri(Object value) {
return getUriFromFilePath(path);
}

private String getImageFromStoragePath(Uri filePath) {
private String getImageFromStoragePath(Uri filePath, boolean keepFullResolution) {
trace(this, "ChtExternalApp :: Retrieving image from storage path.");

try (
Expand All @@ -331,7 +339,7 @@ private String getImageFromStoragePath(Uri filePath) {
InputStream file = new FileInputStream(parcelFileDescriptor.getFileDescriptor());
){
Bitmap imgBitmap = BitmapFactory.decodeStream(file);
return parseBitmapImageToBase64(imgBitmap);
return parseBitmapImageToBase64(imgBitmap, keepFullResolution);

} catch (Exception exception) {
error(exception, "ChtExternalApp :: Failed to process image file from path: %s", filePath);
Expand All @@ -340,10 +348,11 @@ private String getImageFromStoragePath(Uri filePath) {
return null;
}

private String parseBitmapImageToBase64(Bitmap imgBitmap) throws IOException {
private String parseBitmapImageToBase64(Bitmap imgBitmap, boolean keepFullResolution) throws IOException {
try (ByteArrayOutputStream outputFile = new ByteArrayOutputStream()) {
trace(this, "ChtExternalApp :: Compressing image file.");
imgBitmap.compress(Bitmap.CompressFormat.JPEG, 75, outputFile);
int quality = keepFullResolution ? 100 : 75;
imgBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputFile);

trace(this, "ChtExternalApp :: Encoding image file to Base64.");
byte[] imageBytes = outputFile.toByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@ public class ChtExternalAppHandler {
}

String processResult(int resultCode, Intent intent) {
if (resultCode != RESULT_OK) {
String message = "ChtExternalAppHandler :: Bad result code: %s. The external app either: " +
"explicitly returned this result, didn't return any result or crashed during the operation.";

warn(this, message, resultCode);
return safeFormat("console.error('" + message + "')", resultCode);
}

try {
Optional<JSONObject> json = new ChtExternalApp
.Response(intent, this.context)
.getData();
String data = json.map(JSONObject::toString).orElse(null);

if (resultCode != RESULT_OK) {
String message = "ChtExternalAppHandler :: Bad result code: %s. The external app either: " +
"explicitly returned this result, did not return any result or crashed during the operation. "
+ data;

warn(this, message, resultCode);
return safeFormat("console.error('" + message + "')", resultCode);
}

return makeJavaScript(data);

} catch (Exception exception) {
Expand Down
Loading