From e39ca857a2ddfc1caa04b1044ff11c04dfbff89f Mon Sep 17 00:00:00 2001 From: Rajesh Date: Sun, 18 Jan 2026 21:37:23 +0530 Subject: [PATCH 1/2] MR-17 Curious Reader component logic to deal with "minimum" received sub-app payload data --- .../org/curiouslearning/container/WebApp.java | 47 +++++++++++++++++ .../handler/AppEventPayloadHandler.java | 7 +++ .../DefaultAppEventPayloadHandler.java | 21 ++++++++ .../core/subapp/payload/AppEventPayload.java | 13 +++++ .../validation/AppEventPayloadValidator.java | 52 +++++++++++++++++++ .../subapp/validation/ValidationResult.java | 20 +++++++ 6 files changed, 160 insertions(+) create mode 100644 app/src/main/java/org/curiouslearning/container/core/subapp/handler/AppEventPayloadHandler.java create mode 100644 app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java create mode 100644 app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java create mode 100644 app/src/main/java/org/curiouslearning/container/core/subapp/validation/AppEventPayloadValidator.java create mode 100644 app/src/main/java/org/curiouslearning/container/core/subapp/validation/ValidationResult.java diff --git a/app/src/main/java/org/curiouslearning/container/WebApp.java b/app/src/main/java/org/curiouslearning/container/WebApp.java index 15f36a32..70e23b43 100644 --- a/app/src/main/java/org/curiouslearning/container/WebApp.java +++ b/app/src/main/java/org/curiouslearning/container/WebApp.java @@ -16,14 +16,26 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ImageView; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import androidx.appcompat.app.AlertDialog; +import org.curiouslearning.container.core.subapp.payload.AppEventPayload; import org.curiouslearning.container.firebase.AnalyticsUtils; import org.curiouslearning.container.presentation.base.BaseActivity; import org.curiouslearning.container.utilities.ConnectionUtils; import org.curiouslearning.container.utilities.AudioPlayer; import io.sentry.Sentry; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import org.curiouslearning.container.core.subapp.payload.AppEventPayload; +import org.curiouslearning.container.core.subapp.validation.AppEventPayloadValidator; +import org.curiouslearning.container.core.subapp.validation.ValidationResult; +import org.curiouslearning.container.core.subapp.handler.AppEventPayloadHandler; +import org.curiouslearning.container.core.subapp.handler.DefaultAppEventPayloadHandler; + public class WebApp extends BaseActivity { @@ -187,6 +199,11 @@ public void onClick(DialogInterface dialog, int id) { public class WebAppInterface { private Context mContext; + private final Gson gson = new Gson(); + private final AppEventPayloadValidator validator = + new AppEventPayloadValidator(); + private final AppEventPayloadHandler handler = + new DefaultAppEventPayloadHandler(); WebAppInterface(Context context) { mContext = context; @@ -220,6 +237,36 @@ public void closeWebView(){ audioPlayer.play(WebApp.this, R.raw.sound_button_pressed); finish(); } + + @JavascriptInterface + public void logMessage(String payloadJson) { + + try { + if (payloadJson == null || payloadJson.trim().isEmpty()) { + Log.e("WebApp", "Rejected payload: empty JSON"); + return; + } + + AppEventPayload payload = + gson.fromJson(payloadJson, AppEventPayload.class); + + ValidationResult result = validator.validate(payload); + + if (!result.isValid) { + Log.e("WebApp", + "Payload rejected: " + result.errorMessage); + return; + } + + handler.handle(payload); + + } catch (JsonSyntaxException e) { + Log.e("WebApp", "Invalid JSON payload", e); + } catch (Exception e) { + Log.e("WebApp", "Unexpected error handling payload", e); + } + } + } public void setAppOrientation(String orientationType) { diff --git a/app/src/main/java/org/curiouslearning/container/core/subapp/handler/AppEventPayloadHandler.java b/app/src/main/java/org/curiouslearning/container/core/subapp/handler/AppEventPayloadHandler.java new file mode 100644 index 00000000..754d818c --- /dev/null +++ b/app/src/main/java/org/curiouslearning/container/core/subapp/handler/AppEventPayloadHandler.java @@ -0,0 +1,7 @@ +package org.curiouslearning.container.core.subapp.handler; + +import org.curiouslearning.container.core.subapp.payload.AppEventPayload; + +public interface AppEventPayloadHandler { + void handle(AppEventPayload payload); +} diff --git a/app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java b/app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java new file mode 100644 index 00000000..9e6a837a --- /dev/null +++ b/app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java @@ -0,0 +1,21 @@ +package org.curiouslearning.container.core.subapp.handler; + +import android.util.Log; + +import org.curiouslearning.container.core.subapp.payload.AppEventPayload; + +public class DefaultAppEventPayloadHandler + implements AppEventPayloadHandler { + + private static final String TAG = "AppEventHandler"; + + @Override + public void handle(AppEventPayload payload) { + + Log.d(TAG, + "Accepted payload | app_id=" + payload.app_id + + " collection=" + payload.collection + + " timestamp=" + payload.timestamp + ); + } +} diff --git a/app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java b/app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java new file mode 100644 index 00000000..087b786a --- /dev/null +++ b/app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java @@ -0,0 +1,13 @@ +package org.curiouslearning.container.core.subapp.payload; + +import java.util.Map; + +public class AppEventPayload { + + public String cr_user_id; + public String app_id; + public String collection; + public Object data; + public Map options; + public String timestamp; +} diff --git a/app/src/main/java/org/curiouslearning/container/core/subapp/validation/AppEventPayloadValidator.java b/app/src/main/java/org/curiouslearning/container/core/subapp/validation/AppEventPayloadValidator.java new file mode 100644 index 00000000..e6f49164 --- /dev/null +++ b/app/src/main/java/org/curiouslearning/container/core/subapp/validation/AppEventPayloadValidator.java @@ -0,0 +1,52 @@ +package org.curiouslearning.container.core.subapp.validation; + +import org.curiouslearning.container.core.subapp.payload.AppEventPayload; + +import java.util.Map; + +public class AppEventPayloadValidator { + + public ValidationResult validate(AppEventPayload payload) { + + if (payload == null) { + return ValidationResult.failure("Payload is null"); + } + + if (isEmpty(payload.cr_user_id)) { + return ValidationResult.failure("Missing cr_user_id"); + } + + if (isEmpty(payload.app_id)) { + return ValidationResult.failure("Missing app_id"); + } + + if (isEmpty(payload.collection)) { + return ValidationResult.failure("Missing collection"); + } + + if (payload.data == null) { + return ValidationResult.failure("Missing data"); + } + + if (isEmpty(payload.timestamp)) { + return ValidationResult.failure("Missing timestamp"); + } + + if (payload.options != null) { + for (Map.Entry entry : payload.options.entrySet()) { + String op = entry.getValue(); + if (!"add".equals(op) && !"replace".equals(op)) { + return ValidationResult.failure( + "Invalid option for field: " + entry.getKey() + ); + } + } + } + + return ValidationResult.success(); + } + + private boolean isEmpty(String value) { + return value == null || value.trim().isEmpty(); + } +} diff --git a/app/src/main/java/org/curiouslearning/container/core/subapp/validation/ValidationResult.java b/app/src/main/java/org/curiouslearning/container/core/subapp/validation/ValidationResult.java new file mode 100644 index 00000000..8c58aeeb --- /dev/null +++ b/app/src/main/java/org/curiouslearning/container/core/subapp/validation/ValidationResult.java @@ -0,0 +1,20 @@ +package org.curiouslearning.container.core.subapp.validation; + +public class ValidationResult { + + public final boolean isValid; + public final String errorMessage; + + private ValidationResult(boolean isValid, String errorMessage) { + this.isValid = isValid; + this.errorMessage = errorMessage; + } + + public static ValidationResult success() { + return new ValidationResult(true, null); + } + + public static ValidationResult failure(String message) { + return new ValidationResult(false, message); + } +} From 14e430c0e6c582be0acba144f6a693729ad76f9d Mon Sep 17 00:00:00 2001 From: Rajesh Date: Mon, 19 Jan 2026 12:22:21 +0530 Subject: [PATCH 2/2] Removed duplicate imports --- app/src/main/java/org/curiouslearning/container/WebApp.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/java/org/curiouslearning/container/WebApp.java b/app/src/main/java/org/curiouslearning/container/WebApp.java index 70e23b43..3694ca2f 100644 --- a/app/src/main/java/org/curiouslearning/container/WebApp.java +++ b/app/src/main/java/org/curiouslearning/container/WebApp.java @@ -20,15 +20,11 @@ import com.google.gson.JsonSyntaxException; import androidx.appcompat.app.AlertDialog; - -import org.curiouslearning.container.core.subapp.payload.AppEventPayload; import org.curiouslearning.container.firebase.AnalyticsUtils; import org.curiouslearning.container.presentation.base.BaseActivity; import org.curiouslearning.container.utilities.ConnectionUtils; import org.curiouslearning.container.utilities.AudioPlayer; import io.sentry.Sentry; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; import org.curiouslearning.container.core.subapp.payload.AppEventPayload; import org.curiouslearning.container.core.subapp.validation.AppEventPayloadValidator;