Skip to content

Commit 87d5ea4

Browse files
cursoragentabdul
andcommitted
Fix IAM display rendering issues on foldable Android devices
This commit addresses rendering issues with In-App Messages (IAM) on foldable Android devices like Samsung Galaxy Fold/Flip. Root cause: - Foldable devices change screen size without triggering orientation changes - The SDK only listened for CONFIG_ORIENTATION changes, missing fold/unfold events - ViewUtils used deprecated APIs that don't properly handle multi-window scenarios Changes: 1. ViewUtils.kt: - Added API 30+ (Android 11+) implementation using WindowMetrics API - getWindowHeight() now uses currentWindowMetrics for accurate dimensions - getWindowWidth() and getFullbleedWindowWidth() updated similarly - Properly handles foldable device screen size changes 2. ApplicationService.kt: - Added screen size change detection via onConfigurationChanged - Tracks screenWidthDp/screenHeightDp to detect fold/unfold events - New onScreenSizeChanged() method triggers IAM view recreation - Fires onActivityStopped/onActivityAvailable to recalculate IAM dimensions 3. WebViewManager.kt: - Updated comment to clarify fold/unfold handling alongside rotation The fix ensures IAMs are properly resized and repositioned when users fold/unfold their devices, preventing cut-off content and mispositioned messages. Co-authored-by: abdul <abdul@onesignal.com>
1 parent b164424 commit 87d5ea4

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/ViewUtils.kt

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import android.graphics.Rect
99
import android.os.Build
1010
import android.view.View
1111
import android.view.Window
12+
import android.view.WindowManager
1213
import kotlin.math.roundToInt
1314

1415
object ViewUtils {
@@ -19,19 +20,32 @@ object ViewUtils {
1920
// Due to differences in accounting for keyboard, navigation bar, and status bar between
2021
// Android versions have different implementation here
2122
fun getWindowHeight(activity: Activity): Int {
22-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
23+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
24+
getWindowHeightAPI30Plus(activity)
25+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
2326
getWindowHeightAPI23Plus(activity)
2427
} else {
2528
getWindowHeightLollipop(activity)
2629
}
2730
}
2831

32+
@Suppress("DEPRECATION")
2933
private fun getDisplaySizeY(activity: Activity): Int {
3034
val point = Point()
3135
activity.windowManager.defaultDisplay.getSize(point)
3236
return point.y
3337
}
3438

39+
@TargetApi(Build.VERSION_CODES.R)
40+
private fun getWindowHeightAPI30Plus(activity: Activity): Int {
41+
val windowMetrics = activity.windowManager.currentWindowMetrics
42+
val insets =
43+
windowMetrics.windowInsets.getInsetsIgnoringVisibility(
44+
android.view.WindowInsets.Type.systemBars(),
45+
)
46+
return windowMetrics.bounds.height() - insets.top - insets.bottom
47+
}
48+
3549
// Requirement: Ensure DecorView is ready by using OSViewUtils.decorViewReady
3650
@TargetApi(Build.VERSION_CODES.M)
3751
private fun getWindowHeightAPI23Plus(activity: Activity): Int {
@@ -87,7 +101,10 @@ object ViewUtils {
87101
}
88102

89103
fun getFullbleedWindowWidth(activity: Activity): Int {
90-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
104+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
105+
val windowMetrics = activity.windowManager.currentWindowMetrics
106+
windowMetrics.bounds.width()
107+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
91108
val decorView = activity.window.decorView
92109
decorView.width
93110
} else {
@@ -96,6 +113,20 @@ object ViewUtils {
96113
}
97114

98115
fun getWindowWidth(activity: Activity): Int {
99-
return getWindowVisibleDisplayFrame(activity).width()
116+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
117+
getWindowWidthAPI30Plus(activity)
118+
} else {
119+
getWindowVisibleDisplayFrame(activity).width()
120+
}
121+
}
122+
123+
@TargetApi(Build.VERSION_CODES.R)
124+
private fun getWindowWidthAPI30Plus(activity: Activity): Int {
125+
val windowMetrics = activity.windowManager.currentWindowMetrics
126+
val insets =
127+
windowMetrics.windowInsets.getInsetsIgnoringVisibility(
128+
android.view.WindowInsets.Type.systemBars(),
129+
)
130+
return windowMetrics.bounds.width() - insets.left - insets.right
100131
}
101132
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/impl/ApplicationService.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On
8383

8484
val configuration =
8585
object : ComponentCallbacks {
86+
private var lastScreenWidthDp: Int = 0
87+
private var lastScreenHeightDp: Int = 0
88+
8689
override fun onConfigurationChanged(newConfig: Configuration) {
8790
// If Activity contains the configChanges orientation flag, re-create the view this way
8891
if (current != null &&
@@ -93,6 +96,27 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On
9396
) {
9497
onOrientationChanged(newConfig.orientation, current!!)
9598
}
99+
100+
// Handle foldable device screen size changes (fold/unfold events)
101+
// Foldable devices trigger CONFIG_SCREEN_SIZE without orientation change
102+
if (current != null && hasScreenSizeChanged(newConfig)) {
103+
Logging.debug(
104+
"ApplicationService.onConfigurationChanged: Screen size changed " +
105+
"(foldable device fold/unfold detected) - " +
106+
"width: ${newConfig.screenWidthDp}dp, height: ${newConfig.screenHeightDp}dp",
107+
)
108+
onScreenSizeChanged(current!!)
109+
}
110+
lastScreenWidthDp = newConfig.screenWidthDp
111+
lastScreenHeightDp = newConfig.screenHeightDp
112+
}
113+
114+
private fun hasScreenSizeChanged(newConfig: Configuration): Boolean {
115+
if (lastScreenWidthDp == 0 && lastScreenHeightDp == 0) {
116+
return false
117+
}
118+
return newConfig.screenWidthDp != lastScreenWidthDp ||
119+
newConfig.screenHeightDp != lastScreenHeightDp
96120
}
97121

98122
override fun onLowMemory() {}
@@ -368,6 +392,23 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On
368392
handleFocus()
369393
}
370394

395+
/**
396+
* Handles screen size changes that occur on foldable devices when folding/unfolding.
397+
* Unlike orientation changes, foldable devices can change screen dimensions significantly
398+
* without changing orientation (e.g., Samsung Galaxy Fold going from cover screen to main screen).
399+
* This triggers the same view recreation flow as orientation changes to ensure IAMs are
400+
* properly resized and repositioned.
401+
*/
402+
private fun onScreenSizeChanged(activity: Activity) {
403+
// Remove view
404+
activityLifecycleNotifier.fire { it.onActivityStopped(activity) }
405+
406+
// Show view with new dimensions
407+
activityLifecycleNotifier.fire { it.onActivityAvailable(activity) }
408+
409+
activity.window.decorView.viewTreeObserver.addOnGlobalLayoutListener(this)
410+
}
411+
371412
private fun handleLostFocus() {
372413
if (isInForeground) {
373414
Logging.debug("ApplicationService.handleLostFocus: application is now out of focus")

OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/display/impl/WebViewManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ internal class WebViewManager(
263263
showMessageView(lastPageHeight)
264264
}
265265
} else {
266-
// Activity rotated
266+
// Activity rotated or screen size changed (e.g., foldable device fold/unfold)
267267
calculateHeightAndShowWebViewAfterNewActivity()
268268
}
269269
}

0 commit comments

Comments
 (0)