Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
111 changes: 81 additions & 30 deletions src/main/java/org/medicmobile/webapp/mobile/ChtExternalApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class ChtExternalApp {

Expand Down Expand Up @@ -258,7 +259,7 @@ private JSONObject parseBundleToJson(Bundle bundle) {
bundle
.keySet()
.iterator()
.forEachRemaining(key -> setValueInJson(key, json, bundle));
.forEachRemaining(key -> setValueInJsonSafe(key, json, bundle));
return json;

} catch (Exception exception) {
Expand All @@ -268,38 +269,56 @@ private JSONObject parseBundleToJson(Bundle bundle) {
return null;
}

private void setValueInJson(String key, JSONObject json, Bundle bundle) {
try {
Object value = bundle.get(key);
private void setValueInJson(String key, JSONObject json, Bundle bundle) throws IOException, JSONException {
Object value = bundle.get(key); // NOSONAR

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

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

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

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

Optional<Uri> imagePath = getImageUri(value);
if (imagePath.isPresent()) {
json.put(key, getImageFromStoragePath(imagePath.get()));
return;
}
Optional<List<?>> primitiveListOpt = asPrimitiveList(value);
if (primitiveListOpt.isPresent()) {
// ODK/Enketo models a primitive multi-value list (e.g. a select_multiple question) as a
// space-delimited string.
String nodeList = primitiveListOpt
.stream()
.flatMap(List::stream)
.map(Object::toString)
.map(s -> s.replace(" ", "_"))
.collect(Collectors.joining(" "));
json.put(key, nodeList);
return;
}

json.put(key, JSONObject.wrap(value));
Optional<Uri> imagePath = getImageUri(value);
if (imagePath.isPresent()) {
boolean keepFullResolution = bundle.getBoolean("sampleImage", false);
json.put(key, getImageFromStoragePath(imagePath.get(), keepFullResolution));
return;
}

json.put(key, JSONObject.wrap(value));
}

private void setValueInJsonSafe(String key, JSONObject json, Bundle bundle) {
try {
setValueInJson(key, json, bundle);
} catch (Exception exception) {
error(exception, "ChtExternalApp :: Problem parsing bundle to json. Key=%s, Bundle=%s", key, bundle);
}
Expand All @@ -315,6 +334,37 @@ private boolean isBundleList(Object value) {
return !list.isEmpty() && list.get(0) instanceof Bundle;
}

private boolean isPrimitive(Object value) {
return value instanceof String ||
value instanceof Integer ||
value instanceof Long ||
value instanceof Double ||
value instanceof Float ||
value instanceof Boolean ||
value instanceof Short ||
value instanceof Character;
}

private List<?> getArrayAsList(Object array) {
int len = java.lang.reflect.Array.getLength(array);
List<Object> list = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
list.add(java.lang.reflect.Array.get(array, i));
}
return list;
}

private Optional<List<?>> asPrimitiveList(Object value) {
return Optional
.ofNullable(value)
.map(v -> v.getClass().isArray() ? getArrayAsList(v) : v)
.filter(List.class::isInstance)
.map(v -> (List<?>) v)
.filter(v -> !v.isEmpty())
.filter(v -> isPrimitive(v.get(0)))
.map(v -> (List<?>) v);
}

private Optional<Uri> getImageUri(Object value) {
if (!(value instanceof String)) {
return Optional.empty();
Expand All @@ -329,7 +379,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 @@ -339,7 +389,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 @@ -348,10 +398,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 @@ -265,16 +265,16 @@ public void processResponse_withSimpleData_buildJsonCorrectly() {
intent.putExtra("a.string.array", new String[]{"some text", "another text"});

String expectedJsonData = "{" +
"\"an.int.array\":[5,9]," +
"\"an.int.array\":\"5 9\"," +
"\"a.double\":2.8," +
"\"a.long\":2147483649," +
"\"a.string\":\"some text\"," +
"\"an.int\":5," +
"\"a.boolean.array\":[true,false,true]," +
"\"a.boolean.array\":\"true false true\"," +
"\"a.boolean\":true," +
"\"a.string.array\":[\"some text\",\"another text\"]," +
"\"a.long.array\":[2147483649,2147483700]," +
"\"a.double.array\":[2.8,5.5]" +
"\"a.string.array\":\"some_text another_text\"," +
"\"a.long.array\":\"2147483649 2147483700\"," +
"\"a.double.array\":\"2.8 5.5\"" +
"}";

//> WHEN
Expand Down Expand Up @@ -326,7 +326,7 @@ public void processResponse_withNestedObjects_buildJsonCorrectly() {
String expectedJsonData = "{" +
"\"people\":[" +
"{" +
"\"relatives\":[\"Pepe\",\"John\",\"Matt\"]," +
"\"relatives\":\"Pepe John Matt\"," +
"\"name\":\"Anna\"" +
"}," +
"{" +
Expand All @@ -343,9 +343,9 @@ public void processResponse_withNestedObjects_buildJsonCorrectly() {
"\"a.boolean\":true," +
"\"more.details\":{" +
"\"a.null\":null," +
"\"a.boolean.array\":[true,false,true]," +
"\"a.boolean.array\":\"true false true\"," +
"\"a.description\":\"some awesome data\"," +
"\"a.double.array\":[2.8,5.5]" +
"\"a.double.array\":\"2.8 5.5\"" +
"}" +
"}" +
"}";
Expand Down