Skip to content

Conversation

nan-li
Copy link
Contributor

@nan-li nan-li commented Jun 26, 2025

Description

One Line Summary

Properly handle and dismiss in-app messages that are dismissed by back press.

Details

Motivation

Fixes poor behavior: When back button is pressed, the system automatically dismisses the in-app message, without the SDK's awareness. This leads to poor user experience as the SDK does not know of this message's dismissal and will inappropriately redisplay it.

Scope

  • Create new OSPopupWindow class extending PopupWindow
  • Adds additional code to trigger post-dismissal flows when we believe the system dismissed the IAM without our awareness
  • All new logic lives in InAppMessageView. A single instance of InAppMessageView can have multiple popupWindow instances such as when orientation changes. So, we need to hook into individual popup windows instead of the View. We mark the popup window as manually dismissed in the method removeAllViews(). This is sufficient as this method is called by every flow in the SDK that dismisses an IAM (clicking to close, activity change, drag to dismiss, etc). In our listener, if we believe the popup window was dismissed by the system bypassing OneSignal, we trigger messageController?.onMessageWasDismissed(). This call is appropriate as this is the root method that triggers the post-dismissal flows for an IAM. We considered doing more logic in the dismiss event but we cannot generalize here as some dismiss events should not trigger the post-dismissal flow, such as on orientation changes.

Example of system triggering popup window dismissal

 java.lang.Thread.State: RUNNABLE
	  at com.onesignal.inAppMessages.internal.display.impl.InAppMessageView$popupWindowListener$1.onDismiss(InAppMessageView.kt:91)
	  at com.onesignal.inAppMessages.internal.display.impl.OSPopupWindow.dismiss(OSPopupWindow.kt:32)
	  at android.widget.PopupWindow$PopupDecorView.dispatchKeyEvent(PopupWindow.java:2555)
	  at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:6584)
	  at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6450)
	  at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5910)
	  at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5967)
	  at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5933)
	  at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6098)
	  at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5941)
	  at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6155)
	  at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5914)
	  at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5967)
	  at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5933)
	  at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5941)
	  at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5914)
	  at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5967)
	  at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5933)
	  at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6131)
	  at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:6311)
	  at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3649)
	  at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:3169)
	  at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:3160)
	  at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3626)
	  at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:154)
	  at android.os.MessageQueue.nativePollOnce(MessageQueue.java:-1)
	  at android.os.MessageQueue.next(MessageQueue.java:335)
	  at android.os.Looper.loopOnce(Looper.java:161)
	  at android.os.Looper.loop(Looper.java:288)
	  at android.app.ActivityThread.main(ActivityThread.java:7872)
	  at java.lang.reflect.Method.invoke(Method.java:-1)
	  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

Testing

Unit testing

None

Manual testing

Android emulator API 33

Types of IAMs tested:

  • Standard modals
  • Full screen
  • Banner (note banners are not dismissed by the system on back press and remain on screen)

Scenarios tested:

  • Device orientation change (correctly does not trigger our listener)
  • Backgrounding and foregrounding app (correctly does not trigger our listener)
  • Click on buttons in IAM (correctly does not trigger our listener)
  • Drag to dismiss (correctly does not trigger our listener)
  • Click on button with URL (correctly does not trigger our listener)
  • Click on button that triggers a push prompt (correctly does not trigger our listener)
  • Back button press (correctly does trigger our listener)

Devices tested:

  • Android 5.1 (API level 22) - the SDK isn't fetching IAMs, no call to GET /iams made after user is created
  • Android 6.0 (API level 23) - the SDK isn't displaying IAMs, logs "InAppMessagesManager.attemptToShowInAppMessage: There is an IAM currently showing!" but none are
  • Android 7.0 (API level 24)
  • Android 9 (API level 28)
  • Android 11 (API level 30)
  • Android 13 (API level 33)
  • Android 15 (API level 35)
  • Android 16 (API level 36)

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

This change is Reviewable

@nan-li nan-li requested review from jkasten2 and jinliu9508 June 26, 2025 23:38
@nan-li nan-li force-pushed the iam_back_press branch 2 times, most recently from 997a21d to 222c20b Compare June 26, 2025 23:46
* By extending PopupWindow, we can mark it as dismissed by us, and we can add a custom listener to the dismiss event and receive this flag.
* In InAppMessageView which owns the popup window, it will listen to the popup window's dismissal event and trigger the post-dismissal flow. It also sets the manual flag when `removeAllViews()` is invoked.
Copy link
Member

@jkasten2 jkasten2 left a comment

Choose a reason for hiding this comment

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

I tested on Android 9 (API level 28) and if I press the back button it does dismiss the IAM, however if I background the app and resume the app it comes back.

  • If instead I tap the "X", the IAM doesn't come back, as I would expect.
  • I didn't see this issue when I tested on Android 14 (API level 34)

After more testing, this doesn't seem related, and also doesn't consistently happen.

Copy link
Member

@jkasten2 jkasten2 left a comment

Choose a reason for hiding this comment

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

Code changes look good. The only thing I would like to see is testing a few more versions of Android and including them in the PR.

@nan-li
Copy link
Contributor Author

nan-li commented Jun 30, 2025

@jkasten2 - I included the Android versions I was able to test. Some I ran into issues testing.

@nan-li nan-li requested a review from jkasten2 June 30, 2025 18:22
Copy link
Member

@jkasten2 jkasten2 left a comment

Choose a reason for hiding this comment

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

Thanks for testing all those Android versions, things look good now!

I noticed the Android 5 and 6 display issues are pre-existing as well, so thing can be done for them in this PR.

@nan-li nan-li merged commit ba267bf into main Jun 30, 2025
2 checks passed
@nan-li nan-li deleted the iam_back_press branch June 30, 2025 18:43
@jkasten2 jkasten2 mentioned this pull request Jun 30, 2025
nan-li added a commit that referenced this pull request Jul 8, 2025
Properly handle in-app messages dismissed by back press
@nan-li nan-li mentioned this pull request Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants