Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.onesignal.common

import com.onesignal.debug.internal.logging.Logging

/**
* Global feature switch for foldable device IAM display improvements.
* When enabled, uses modern WindowMetrics API and detects screen size changes.
*/
internal object FoldableIAMFeature {
@Volatile
var isEnabled: Boolean = false
private set

fun updateEnabled(
enabled: Boolean,
source: String,
) {
val previous = isEnabled
isEnabled = enabled

if (previous != enabled) {
Logging.info("OneSignal: FoldableIAMFeature changed to isEnabled=$enabled (source=$source)")
} else {
Logging.debug("OneSignal: FoldableIAMFeature unchanged (isEnabled=$enabled, source=$source)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,35 @@ object ViewUtils {
// Due to differences in accounting for keyboard, navigation bar, and status bar between
// Android versions have different implementation here
fun getWindowHeight(activity: Activity): Int {
// When foldable IAM fix is enabled and API 30+, use WindowMetrics for accurate dimensions
if (FoldableIAMFeature.isEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return getWindowHeightAPI30Plus(activity)
}
// Legacy behavior
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindowHeightAPI23Plus(activity)
} else {
getWindowHeightLollipop(activity)
}
}

@Suppress("DEPRECATION")
private fun getDisplaySizeY(activity: Activity): Int {
val point = Point()
activity.windowManager.defaultDisplay.getSize(point)
return point.y
}

@TargetApi(Build.VERSION_CODES.R)
private fun getWindowHeightAPI30Plus(activity: Activity): Int {
val windowMetrics = activity.windowManager.currentWindowMetrics
val insets =
windowMetrics.windowInsets.getInsetsIgnoringVisibility(
android.view.WindowInsets.Type.systemBars(),
)
return windowMetrics.bounds.height() - insets.top - insets.bottom
}

// Requirement: Ensure DecorView is ready by using OSViewUtils.decorViewReady
@TargetApi(Build.VERSION_CODES.M)
private fun getWindowHeightAPI23Plus(activity: Activity): Int {
Expand Down Expand Up @@ -61,6 +77,7 @@ object ViewUtils {
return rect
}

@Suppress("DEPRECATION")
fun getCutoutAndStatusBarInsets(activity: Activity): IntArray {
val frame = getWindowVisibleDisplayFrame(activity)
val contentView = activity.window.findViewById<View>(Window.ID_ANDROID_CONTENT)
Expand All @@ -87,6 +104,12 @@ object ViewUtils {
}

fun getFullbleedWindowWidth(activity: Activity): Int {
// When foldable IAM fix is enabled and API 30+, use WindowMetrics for accurate dimensions
if (FoldableIAMFeature.isEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = activity.windowManager.currentWindowMetrics
return windowMetrics.bounds.width()
}
// Legacy behavior
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val decorView = activity.window.decorView
decorView.width
Expand All @@ -96,6 +119,21 @@ object ViewUtils {
}

fun getWindowWidth(activity: Activity): Int {
// When foldable IAM fix is enabled and API 30+, use WindowMetrics for accurate dimensions
if (FoldableIAMFeature.isEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return getWindowWidthAPI30Plus(activity)
}
// Legacy behavior
return getWindowVisibleDisplayFrame(activity).width()
}

@TargetApi(Build.VERSION_CODES.R)
private fun getWindowWidthAPI30Plus(activity: Activity): Int {
val windowMetrics = activity.windowManager.currentWindowMetrics
val insets =
windowMetrics.windowInsets.getInsetsIgnoringVisibility(
android.view.WindowInsets.Type.systemBars(),
)
return windowMetrics.bounds.width() - insets.left - insets.right
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.onesignal.common.AndroidUtils
import com.onesignal.common.DeviceUtils
import com.onesignal.common.FoldableIAMFeature
import com.onesignal.common.events.EventProducer
import com.onesignal.common.threading.Waiter
import com.onesignal.core.internal.application.ActivityLifecycleHandlerBase
Expand Down Expand Up @@ -83,6 +84,9 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On

val configuration =
object : ComponentCallbacks {
private var lastScreenWidthDp: Int = 0
private var lastScreenHeightDp: Int = 0

override fun onConfigurationChanged(newConfig: Configuration) {
// If Activity contains the configChanges orientation flag, re-create the view this way
if (current != null &&
Expand All @@ -93,6 +97,28 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On
) {
onOrientationChanged(newConfig.orientation, current!!)
}

// Handle foldable device screen size changes (fold/unfold events)
// Only enabled when FoldableIAMFeature is on
// Foldable devices trigger CONFIG_SCREEN_SIZE without orientation change
if (FoldableIAMFeature.isEnabled && current != null && hasScreenSizeChanged(newConfig)) {
Logging.debug(
"ApplicationService.onConfigurationChanged: Screen size changed " +
"(foldable device fold/unfold detected) - " +
"width: ${newConfig.screenWidthDp}dp, height: ${newConfig.screenHeightDp}dp",
)
onScreenSizeChanged(current!!)
}
lastScreenWidthDp = newConfig.screenWidthDp
lastScreenHeightDp = newConfig.screenHeightDp
}

private fun hasScreenSizeChanged(newConfig: Configuration): Boolean {
if (lastScreenWidthDp == 0 && lastScreenHeightDp == 0) {
return false
}
return newConfig.screenWidthDp != lastScreenWidthDp ||
newConfig.screenHeightDp != lastScreenHeightDp
}

override fun onLowMemory() {}
Expand Down Expand Up @@ -368,6 +394,23 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On
handleFocus()
}

/**
* Handles screen size changes that occur on foldable devices when folding/unfolding.
* Unlike orientation changes, foldable devices can change screen dimensions significantly
* without changing orientation (e.g., Samsung Galaxy Fold going from cover screen to main screen).
* This triggers the same view recreation flow as orientation changes to ensure IAMs are
* properly resized and repositioned.
*/
private fun onScreenSizeChanged(activity: Activity) {
// Remove view
activityLifecycleNotifier.fire { it.onActivityStopped(activity) }

// Show view with new dimensions
activityLifecycleNotifier.fire { it.onActivityAvailable(activity) }

activity.window.decorView.viewTreeObserver.addOnGlobalLayoutListener(this)
}

private fun handleLostFocus() {
if (isInForeground) {
Logging.debug("ApplicationService.handleLostFocus: application is now out of focus")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ internal enum class FeatureFlag(
"SDK_050800_BACKGROUND_THREADING",
FeatureActivationMode.APP_STARTUP
),

/**
* Enables improved IAM display handling for foldable devices.
* When enabled:
* - Uses WindowMetrics API (API 30+) for accurate window dimensions
* - Detects screen size changes from fold/unfold events
* - Recalculates IAM dimensions when screen size changes
*/
SDK_050800_FOLDABLE_IAM_FIX(
"SDK_050800_FOLDABLE_IAM_FIX",
FeatureActivationMode.IMMEDIATE
),
;

fun isEnabledIn(enabledKeys: Set<String>): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.onesignal.core.internal.features

import com.onesignal.common.FoldableIAMFeature
import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler
import com.onesignal.common.modeling.ModelChangeTags
import com.onesignal.common.modeling.ModelChangedArgs
Expand Down Expand Up @@ -113,6 +114,11 @@ internal class FeatureManager(
enabled = enabled,
source = "FeatureManager:${feature.activationMode}"
)
FeatureFlag.SDK_050800_FOLDABLE_IAM_FIX ->
FoldableIAMFeature.updateEnabled(
enabled = enabled,
source = "FeatureManager:${feature.activationMode}"
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ internal class WebViewManager(
showMessageView(lastPageHeight)
}
} else {
// Activity rotated
// Activity rotated or screen size changed (e.g., foldable device fold/unfold)
calculateHeightAndShowWebViewAfterNewActivity()
}
}
Expand Down
Loading