Repository: customerio/customerio-android
Pull request #537: chore: refactor in-app view calls to delegates
Author: login: mrehan27
Created: 2025-05-09T09:32:02Z | Merged: 2025-05-12T10:10:50Z

part of: [MBL-1091](https://linear.app/customerio/issue/MBL-1091/unified-action-handling-for-in-app-messages)

### Changes

- Added `EngineWebViewDelegate` to decouple controllers from `EngineWebView`
- Added `InAppHostViewDelegate` to decouple controllers from host in-app views
- Renamed `InAppMessageViewListener` to `InAppMessageViewCallback` (and similarly for child classes) for clearer naming

### PRs Stack:

- #535
- #536
- #537 👈🏻

### Notes

- Tests are not yet included to keep changes small and reviewable. Tests will be added once refactoring is complete.
- There are no intentional logic changes unless explicitly noted in comments. If you spot any, please flag them.


Diff:
diff --git a/messaginginapp/api/messaginginapp.api b/messaginginapp/api/messaginginapp.api
index e83123cc1..b7928caec 100644
--- a/messaginginapp/api/messaginginapp.api
+++ b/messaginginapp/api/messaginginapp.api
@@ -155,7 +155,7 @@ public abstract interface class io/customer/messaginginapp/gist/presentation/Gis
 	public abstract fun onMessageShown (Lio/customer/messaginginapp/gist/data/model/Message;)V
 }
 
-public final class io/customer/messaginginapp/gist/presentation/GistModalActivity : androidx/appcompat/app/AppCompatActivity, io/customer/messaginginapp/ui/bridge/ModalInAppMessageViewListener, io/customer/sdk/tracking/TrackableScreen {
+public final class io/customer/messaginginapp/gist/presentation/GistModalActivity : androidx/appcompat/app/AppCompatActivity, io/customer/messaginginapp/ui/bridge/ModalInAppMessageViewCallback, io/customer/sdk/tracking/TrackableScreen {
 	public static final field Companion Lio/customer/messaginginapp/gist/presentation/GistModalActivity$Companion;
 	public fun <init> ()V
 	public fun finish ()V
@@ -417,7 +417,7 @@ public abstract interface class io/customer/messaginginapp/type/InlineMessageAct
 	public abstract fun onActionClick (Lio/customer/messaginginapp/type/InAppMessage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 }
 
-public final class io/customer/messaginginapp/ui/InlineInAppMessageView : android/widget/FrameLayout, io/customer/messaginginapp/ui/bridge/InlineInAppMessageViewListener {
+public final class io/customer/messaginginapp/ui/InlineInAppMessageView : android/widget/FrameLayout, io/customer/messaginginapp/ui/bridge/InlineInAppMessageViewCallback {
 	public fun <init> (Landroid/content/Context;)V
 	public fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;)V
 	public fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;I)V
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/GistModalActivity.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/GistModalActivity.kt
index c44760f21..667e0775a 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/GistModalActivity.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/GistModalActivity.kt
@@ -20,7 +20,7 @@ import io.customer.messaginginapp.gist.utilities.ModalAnimationUtil
 import io.customer.messaginginapp.state.InAppMessagingAction
 import io.customer.messaginginapp.state.InAppMessagingState
 import io.customer.messaginginapp.state.ModalMessageState
-import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewCallback
 import io.customer.sdk.core.di.SDKComponent
 import io.customer.sdk.tracking.TrackableScreen
 import kotlinx.coroutines.Job
@@ -28,7 +28,7 @@ import kotlinx.coroutines.Job
 const val GIST_MESSAGE_INTENT: String = "GIST_MESSAGE"
 const val GIST_MODAL_POSITION_INTENT: String = "GIST_MODAL_POSITION"
 
-class GistModalActivity : AppCompatActivity(), ModalInAppMessageViewListener, TrackableScreen {
+class GistModalActivity : AppCompatActivity(), ModalInAppMessageViewCallback, TrackableScreen {
     private lateinit var binding: ActivityGistBinding
     private var elapsedTimer: ElapsedTimer = ElapsedTimer()
     private val inAppMessagingManager = SDKComponent.inAppMessagingManager
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/engine/EngineWebView.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/engine/EngineWebView.kt
index fb5f91a98..72608d639 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/engine/EngineWebView.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/gist/presentation/engine/EngineWebView.kt
@@ -21,6 +21,7 @@ import io.customer.messaginginapp.di.inAppMessagingManager
 import io.customer.messaginginapp.gist.data.model.engine.EngineWebConfiguration
 import io.customer.messaginginapp.gist.utilities.ElapsedTimer
 import io.customer.messaginginapp.state.InAppMessagingState
+import io.customer.messaginginapp.ui.bridge.EngineWebViewDelegate
 import io.customer.sdk.core.di.SDKComponent
 import java.util.Timer
 import java.util.TimerTask
@@ -28,9 +29,9 @@ import java.util.TimerTask
 internal class EngineWebView @JvmOverloads constructor(
     context: Context,
     attrs: AttributeSet? = null
-) : FrameLayout(context, attrs), EngineWebViewListener, LifecycleObserver {
+) : FrameLayout(context, attrs), EngineWebViewListener, LifecycleObserver, EngineWebViewDelegate {
 
-    var listener: EngineWebViewListener? = null
+    override var listener: EngineWebViewListener? = null
     private var timer: Timer? = null
     private var timerTask: TimerTask? = null
     private var webView: WebView? = null
@@ -54,6 +55,10 @@ internal class EngineWebView @JvmOverloads constructor(
         }
     }
 
+    override fun getView(): EngineWebView {
+        return this
+    }
+
     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
     fun onLifecycleResumed() {
         logger.info("EngineWebView onLifecycleResumed")
@@ -72,7 +77,7 @@ internal class EngineWebView @JvmOverloads constructor(
      * and the view is already removed from the parent.
      * It stops loading WebView, removes JavaScript interface, and clears reference to WebView.
      */
-    fun releaseResources() {
+    override fun releaseResources() {
         try {
             val view = webView ?: return
             logger.debug("Cleaning up EngineWebView")
@@ -104,14 +109,14 @@ internal class EngineWebView @JvmOverloads constructor(
         }
     }
 
-    fun stopLoading() {
+    override fun stopLoading() {
         webView?.stopLoading()
         // stop the timer and clean up
         bootstrapped()
     }
 
     @SuppressLint("SetJavaScriptEnabled")
-    fun setup(configuration: EngineWebConfiguration) {
+    override fun setup(configuration: EngineWebConfiguration) {
         setupTimeout()
         elapsedTimer.start("Engine render for message: ${configuration.messageId}")
         val messageData = mapOf("options" to configuration)
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/InlineInAppMessageView.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/InlineInAppMessageView.kt
index eafda902f..43cceaedf 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/InlineInAppMessageView.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/InlineInAppMessageView.kt
@@ -13,7 +13,8 @@ import androidx.core.graphics.drawable.DrawableCompat
 import io.customer.messaginginapp.R
 import io.customer.messaginginapp.type.InlineMessageActionListener
 import io.customer.messaginginapp.ui.bridge.AndroidInAppPlatformDelegate
-import io.customer.messaginginapp.ui.bridge.InlineInAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.InAppHostViewDelegateImpl
+import io.customer.messaginginapp.ui.bridge.InlineInAppMessageViewCallback
 import io.customer.messaginginapp.ui.controller.InlineInAppMessageViewController
 import io.customer.messaginginapp.ui.extensions.resolveThemeColor
 
@@ -37,9 +38,9 @@ class InlineInAppMessageView @JvmOverloads constructor(
     @AttrRes defStyleAttr: Int = 0,
     @StyleRes defStyleRes: Int = 0
 ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes),
-    InlineInAppMessageViewListener {
+    InlineInAppMessageViewCallback {
     private val controller = InlineInAppMessageViewController(
-        viewDelegate = this,
+        viewDelegate = InAppHostViewDelegateImpl(view = this),
         platformDelegate = AndroidInAppPlatformDelegate(view = this)
     )
     private val progressIndicator: ProgressBar = ProgressBar(context)
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/ModalInAppMessageView.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/ModalInAppMessageView.kt
index 8cf859921..e6f5cd887 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/ModalInAppMessageView.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/ModalInAppMessageView.kt
@@ -7,7 +7,8 @@ import androidx.annotation.AttrRes
 import androidx.annotation.StyleRes
 import io.customer.messaginginapp.gist.data.model.Message
 import io.customer.messaginginapp.ui.bridge.AndroidInAppPlatformDelegate
-import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.InAppHostViewDelegateImpl
+import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewCallback
 import io.customer.messaginginapp.ui.controller.ModalInAppMessageViewController
 
 /**
@@ -22,11 +23,11 @@ internal class ModalInAppMessageView @JvmOverloads constructor(
     @StyleRes defStyleRes: Int = 0
 ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
     private val controller = ModalInAppMessageViewController(
-        viewDelegate = this,
+        viewDelegate = InAppHostViewDelegateImpl(view = this),
         platformDelegate = AndroidInAppPlatformDelegate(view = this)
     )
 
-    internal fun setViewCallback(viewCallback: ModalInAppMessageViewListener) {
+    internal fun setViewCallback(viewCallback: ModalInAppMessageViewCallback) {
         controller.viewCallback = viewCallback
     }
 
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/EngineWebViewDelegate.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/EngineWebViewDelegate.kt
new file mode 100644
index 000000000..bd9be6136
--- /dev/null
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/EngineWebViewDelegate.kt
@@ -0,0 +1,23 @@
+package io.customer.messaginginapp.ui.bridge
+
+import android.view.View
+import io.customer.messaginginapp.gist.data.model.engine.EngineWebConfiguration
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebView
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebViewListener
+
+/**
+ * Delegate interface to decouple [EngineWebView] from its consumers.
+ * Provides a minimal set of operations to control and configure the view without directly
+ * depending on its implementation.
+ * Useful for testing, mocking, or swapping the view behind the interface.
+ */
+internal interface EngineWebViewDelegate {
+    var listener: EngineWebViewListener?
+
+    fun setup(configuration: EngineWebConfiguration)
+    fun stopLoading()
+    fun releaseResources()
+    fun getView(): View
+    fun setAlpha(alpha: Float)
+    fun bringToFront()
+}
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppHostViewDelegate.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppHostViewDelegate.kt
new file mode 100644
index 000000000..e89caed63
--- /dev/null
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppHostViewDelegate.kt
@@ -0,0 +1,50 @@
+package io.customer.messaginginapp.ui.bridge
+
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebView
+
+/**
+ * Delegate interface that abstracts host view operations for in-app messages.
+ * Allows operations like adding/removing child views, creating new view instances,
+ * and posting actions on UI thread without directly depending on Android framework.
+ * Designed for easier testing and mocking.
+ */
+internal interface InAppHostViewDelegate {
+    var isVisible: Boolean
+
+    fun addView(delegate: EngineWebViewDelegate)
+    fun removeView(delegate: EngineWebViewDelegate)
+    fun createEngineWebViewInstance(): EngineWebViewDelegate
+    fun post(action: () -> Unit)
+}
+
+/**
+ * Default implementation of [InAppHostViewDelegate] that wraps a real Android ViewGroup.
+ * Simplifies implementation by providing a concrete way to manage child views and UI actions.
+ */
+internal class InAppHostViewDelegateImpl(
+    private val view: ViewGroup
+) : InAppHostViewDelegate {
+    override var isVisible: Boolean
+        get() = view.isVisible
+        set(value) {
+            view.isVisible = value
+        }
+
+    override fun addView(delegate: EngineWebViewDelegate) {
+        view.addView(delegate.getView())
+    }
+
+    override fun removeView(delegate: EngineWebViewDelegate) {
+        view.removeView(delegate.getView())
+    }
+
+    override fun createEngineWebViewInstance(): EngineWebViewDelegate {
+        return EngineWebView(view.context)
+    }
+
+    override fun post(action: () -> Unit) {
+        view.post(action)
+    }
+}
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppMessageViewCallback.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppMessageViewCallback.kt
index f0d1abffc..a5ec4bf8a 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppMessageViewCallback.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppMessageViewCallback.kt
@@ -3,19 +3,19 @@ package io.customer.messaginginapp.ui.bridge
 /**
  * Base callback interface to send events from in-app messages view to the host component.
  */
-internal interface InAppMessageViewListener {
+internal interface InAppMessageViewCallback {
     fun onViewSizeChanged(width: Int, height: Int) {}
 }
 
 /**
  * Callback interface to send events from modal in-app messages view to the host component.
  */
-internal interface ModalInAppMessageViewListener : InAppMessageViewListener
+internal interface ModalInAppMessageViewCallback : InAppMessageViewCallback
 
 /**
  * Callback interface to send events from in-app messages view to the host component.
  */
-internal interface InlineInAppMessageViewListener : InAppMessageViewListener {
+internal interface InlineInAppMessageViewCallback : InAppMessageViewCallback {
     fun onLoadingStarted()
     fun onLoadingFinished()
     fun onNoMessageToDisplay()
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InAppMessageViewController.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InAppMessageViewController.kt
index 46490ce82..fadf6cceb 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InAppMessageViewController.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InAppMessageViewController.kt
@@ -1,17 +1,17 @@
 package io.customer.messaginginapp.ui.controller
 
 import android.content.ActivityNotFoundException
-import android.view.ViewGroup
 import androidx.annotation.UiThread
 import io.customer.messaginginapp.di.inAppMessagingManager
 import io.customer.messaginginapp.gist.data.model.Message
 import io.customer.messaginginapp.gist.data.model.engine.EngineWebConfiguration
-import io.customer.messaginginapp.gist.presentation.engine.EngineWebView
 import io.customer.messaginginapp.gist.presentation.engine.EngineWebViewListener
 import io.customer.messaginginapp.state.InAppMessagingAction
 import io.customer.messaginginapp.type.InAppMessage
 import io.customer.messaginginapp.type.InlineMessageActionListener
-import io.customer.messaginginapp.ui.bridge.InAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.EngineWebViewDelegate
+import io.customer.messaginginapp.ui.bridge.InAppHostViewDelegate
+import io.customer.messaginginapp.ui.bridge.InAppMessageViewCallback
 import io.customer.messaginginapp.ui.bridge.InAppPlatformDelegate
 import io.customer.sdk.core.di.SDKComponent
 
@@ -20,15 +20,15 @@ import io.customer.sdk.core.di.SDKComponent
  * Encapsulates logic for view state transitions, sizing, lifecycle, and WebView interaction.
  * Designed to decouple business logic from Android view classes for better testability and reuse.
  */
-internal abstract class InAppMessageViewController<ViewCallback : InAppMessageViewListener>(
+internal abstract class InAppMessageViewController<ViewCallback : InAppMessageViewCallback>(
     protected val type: String,
     protected val platformDelegate: InAppPlatformDelegate,
-    protected val viewDelegate: ViewGroup
+    protected val viewDelegate: InAppHostViewDelegate
 ) : EngineWebViewListener {
     internal val logger = SDKComponent.logger
     internal val inAppMessagingManager = SDKComponent.inAppMessagingManager
 
-    internal var engineWebViewDelegate: EngineWebView? = null
+    internal var engineWebViewDelegate: EngineWebViewDelegate? = null
     internal var currentMessage: Message? = null
     internal var currentRoute: String? = null
     internal var viewCallback: ViewCallback? = null
@@ -54,7 +54,7 @@ internal abstract class InAppMessageViewController<ViewCallback : InAppMessageVi
             return
         }
 
-        val delegate = EngineWebView(viewDelegate.context)
+        val delegate = viewDelegate.createEngineWebViewInstance()
         engineWebViewDelegate = delegate
 
         delegate.setAlpha(0.0f)
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InlineInAppMessageViewController.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InlineInAppMessageViewController.kt
index 3797c5a5a..15718566a 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InlineInAppMessageViewController.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/InlineInAppMessageViewController.kt
@@ -1,20 +1,19 @@
 package io.customer.messaginginapp.ui.controller
 
-import android.view.ViewGroup
 import androidx.annotation.UiThread
-import androidx.core.view.isVisible
 import io.customer.messaginginapp.gist.data.model.Message
 import io.customer.messaginginapp.gist.utilities.ElapsedTimer
 import io.customer.messaginginapp.state.InAppMessagingAction
 import io.customer.messaginginapp.state.InAppMessagingState
 import io.customer.messaginginapp.state.InlineMessageState
+import io.customer.messaginginapp.ui.bridge.InAppHostViewDelegate
 import io.customer.messaginginapp.ui.bridge.InAppPlatformDelegate
-import io.customer.messaginginapp.ui.bridge.InlineInAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.InlineInAppMessageViewCallback
 
 internal class InlineInAppMessageViewController(
-    viewDelegate: ViewGroup,
+    viewDelegate: InAppHostViewDelegate,
     platformDelegate: InAppPlatformDelegate
-) : InAppMessageViewController<InlineInAppMessageViewListener>(
+) : InAppMessageViewController<InlineInAppMessageViewCallback>(
     type = "Inline",
     platformDelegate = platformDelegate,
     viewDelegate = viewDelegate
diff --git a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/ModalInAppMessageViewController.kt b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/ModalInAppMessageViewController.kt
index 1ab773891..ebd50d257 100644
--- a/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/ModalInAppMessageViewController.kt
+++ b/messaginginapp/src/main/java/io/customer/messaginginapp/ui/controller/ModalInAppMessageViewController.kt
@@ -1,14 +1,14 @@
 package io.customer.messaginginapp.ui.controller
 
-import android.view.ViewGroup
 import io.customer.messaginginapp.state.InAppMessagingAction
+import io.customer.messaginginapp.ui.bridge.InAppHostViewDelegate
 import io.customer.messaginginapp.ui.bridge.InAppPlatformDelegate
-import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewListener
+import io.customer.messaginginapp.ui.bridge.ModalInAppMessageViewCallback
 
 internal class ModalInAppMessageViewController(
-    viewDelegate: ViewGroup,
+    viewDelegate: InAppHostViewDelegate,
     platformDelegate: InAppPlatformDelegate
-) : InAppMessageViewController<ModalInAppMessageViewListener>(
+) : InAppMessageViewController<ModalInAppMessageViewCallback>(
     type = "Modal",
     viewDelegate = viewDelegate,
     platformDelegate = platformDelegate


Reviews:
user: login: mrehan27
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T09:45:01Z
user: login: Shahroz16
body: 💯 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T10:00:35Z

Review comments:
diff_hunk: @@ -0,0 +1,23 @@
+package io.customer.messaginginapp.ui.bridge
+
+import android.view.View
+import io.customer.messaginginapp.gist.data.model.engine.EngineWebConfiguration
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebView
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebViewListener
+
+/**
+ * Delegate interface to decouple [EngineWebView] from its consumers.
+ * Provides a minimal set of operations to control and configure the view without directly
+ * depending on its implementation.
+ * Useful for testing, mocking, or swapping the view behind the interface.
+ */
+internal interface EngineWebViewDelegate {
+    var listener: EngineWebViewListener?
+
+    fun setup(configuration: EngineWebConfiguration)
+    fun stopLoading()
+    fun releaseResources()
+    fun getView(): View
+    fun setAlpha(alpha: Float)
+    fun bringToFront()
path: messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/EngineWebViewDelegate.kt
user: login: mrehan27
body: I reused same names from `View` class in this delegate to avoid reimplementation and keep the overall implementation simpler.
created_at: 2025-05-09T09:44:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+package io.customer.messaginginapp.ui.bridge
+
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import io.customer.messaginginapp.gist.presentation.engine.EngineWebView
+
+/**
+ * Delegate interface that abstracts host view operations for in-app messages.
+ * Allows operations like adding/removing child views, creating new view instances,
+ * and posting actions on UI thread without directly depending on Android framework.
+ * Designed for easier testing and mocking.
+ */
+internal interface InAppHostViewDelegate {
+    var isVisible: Boolean
+
+    fun addView(delegate: EngineWebViewDelegate)
+    fun removeView(delegate: EngineWebViewDelegate)
+    fun createEngineWebViewInstance(): EngineWebViewDelegate
+    fun post(action: () -> Unit)
path: messaginginapp/src/main/java/io/customer/messaginginapp/ui/bridge/InAppHostViewDelegate.kt
user: login: mrehan27
body: Same as above, reused same names from `View` class in this delegate to avoid reimplementation and keep the overall implementation simpler.
created_at: 2025-05-09T09:44:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-09T09:32:16Z
body: <!-- sample app builds --> 
# Sample app builds 📱

Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request. 

---
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-09T09:35:08Z
body: ## [Codecov](https://app.codecov.io/gh/customerio/customerio-android/pull/537?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) Report
Attention: Patch coverage is `0%` with `26 lines` in your changes missing coverage. Please review.
> Please [upload](https://docs.codecov.com/docs/codecov-uploader) report for BASE (`feature/inline-in-app-messages@fcc03bb`). [Learn more](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#section-missing-base-commit) about missing BASE report.

| [Files with missing lines](https://app.codecov.io/gh/customerio/customerio-android/pull/537?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) | Patch % | Lines |
|---|---|---|
| [.../messaginginapp/ui/bridge/InAppHostViewDelegate.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2Fbridge%2FInAppHostViewDelegate.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9icmlkZ2UvSW5BcHBIb3N0Vmlld0RlbGVnYXRlLmt0) | 0.00% | [12 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...ginapp/ui/controller/InAppMessageViewController.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2Fcontroller%2FInAppMessageViewController.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9jb250cm9sbGVyL0luQXBwTWVzc2FnZVZpZXdDb250cm9sbGVyLmt0) | 0.00% | [4 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...inginapp/gist/presentation/engine/EngineWebView.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fgist%2Fpresentation%2Fengine%2FEngineWebView.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC9naXN0L3ByZXNlbnRhdGlvbi9lbmdpbmUvRW5naW5lV2ViVmlldy5rdA==) | 0.00% | [3 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...ssaginginapp/ui/bridge/InAppMessageViewCallback.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2Fbridge%2FInAppMessageViewCallback.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9icmlkZ2UvSW5BcHBNZXNzYWdlVmlld0NhbGxiYWNrLmt0) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...saginginapp/gist/presentation/GistModalActivity.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fgist%2Fpresentation%2FGistModalActivity.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC9naXN0L3ByZXNlbnRhdGlvbi9HaXN0TW9kYWxBY3Rpdml0eS5rdA==) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...stomer/messaginginapp/ui/InlineInAppMessageView.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2FInlineInAppMessageView.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9JbmxpbmVJbkFwcE1lc3NhZ2VWaWV3Lmt0) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...ustomer/messaginginapp/ui/ModalInAppMessageView.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2FModalInAppMessageView.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9Nb2RhbEluQXBwTWVzc2FnZVZpZXcua3Q=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [.../ui/controller/InlineInAppMessageViewController.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2Fcontroller%2FInlineInAppMessageViewController.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9jb250cm9sbGVyL0lubGluZUluQXBwTWVzc2FnZVZpZXdDb250cm9sbGVyLmt0) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |
| [...p/ui/controller/ModalInAppMessageViewController.kt](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&filepath=messaginginapp%2Fsrc%2Fmain%2Fjava%2Fio%2Fcustomer%2Fmessaginginapp%2Fui%2Fcontroller%2FModalInAppMessageViewController.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio#diff-bWVzc2FnaW5naW5hcHAvc3JjL21haW4vamF2YS9pby9jdXN0b21lci9tZXNzYWdpbmdpbmFwcC91aS9jb250cm9sbGVyL01vZGFsSW5BcHBNZXNzYWdlVmlld0NvbnRyb2xsZXIua3Q=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/customerio/customerio-android/pull/537?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio) |

<details><summary>Additional details and impacted files</summary>


```diff
@@                        Coverage Diff                        @@
##             feature/inline-in-app-messages     #537   +/-   ##
=================================================================
  Coverage                                  ?   46.99%           
  Complexity                                ?      354           
=================================================================
  Files                                     ?      114           
  Lines                                     ?     3379           
  Branches                                  ?      443           
=================================================================
  Hits                                      ?     1588           
  Misses                                    ?     1665           
  Partials                                  ?      126           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/customerio/customerio-android/pull/537?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=customerio).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T09:38:16Z
body: Build available to test
Version: `mbl-1091-inapp-delegates-SNAPSHOT`
Repository: `https://s01.oss.sonatype.org/content/repositories/snapshots/`
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T09:38:41Z
body: * kotlin_compose: `mbl-1091-inapp-delegates (1746783187)`
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T09:38:47Z
body: * java_layout: `mbl-1091-inapp-delegates (1746783189)`
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T09:39:39Z
body: ## 📏 SDK Binary Size Comparison Report

No changes detected in SDK binary size ✅

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-12T10:15:32Z
body: * java_layout: `mbl-1091-inapp-delegates (1747044685)`
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-12T10:16:34Z
body: * kotlin_compose: `mbl-1091-inapp-delegates (1747044686)`
author_association: NONE