diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb6045b4..37c869736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] +### Fixed +- In-app messages now match the host app's system bar behavior instead of overriding it. This fixes fullscreen in-apps drawing content behind the status bar. + ## [3.6.5] ### Fixed - Fixed IterableEmbeddedView not having an empty constructor and causing crashes diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java index b6b9a99f6..5a36b9bb0 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java @@ -2,10 +2,8 @@ import android.app.Activity; import android.app.Dialog; -import android.content.Context; import android.content.DialogInterface; import android.graphics.Color; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -16,8 +14,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.util.DisplayMetrics; -import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.OrientationEventListener; @@ -35,6 +31,7 @@ import androidx.core.graphics.ColorUtils; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; +import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.DialogFragment; @@ -78,6 +75,7 @@ public class IterableInAppFragmentHTMLNotification extends DialogFragment implem private boolean shouldAnimate; private double inAppBackgroundAlpha; private String inAppBackgroundColor; + private boolean hostIsEdgeToEdge; public static IterableInAppFragmentHTMLNotification createInstance(@NonNull String htmlString, boolean callbackOnCancel, @NonNull IterableHelper.IterableUrlCallback clickCallback, @NonNull IterableInAppLocation location, @NonNull String messageId, @NonNull Double backgroundAlpha, @NonNull Rect padding) { return IterableInAppFragmentHTMLNotification.createInstance(htmlString, callbackOnCancel, clickCallback, location, messageId, backgroundAlpha, padding, false, new IterableInAppMessage.InAppBgColor(null, 0.0f)); @@ -176,13 +174,8 @@ public void onCancel(DialogInterface dialog) { applyWindowGravity(dialog.getWindow(), "onCreateDialog"); } - if (getInAppLayout(insetPadding) == InAppLayout.FULLSCREEN) { - dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - } else if (getInAppLayout(insetPadding) != InAppLayout.TOP) { - // For TOP layout in-app, status bar will be opaque so that the in-app content does not overlap with translucent status bar. - // For other non-fullscreen in-apps layouts (BOTTOM and CENTER), status bar will be translucent - dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - } + hostIsEdgeToEdge = isHostActivityEdgeToEdge(); + configureSystemBarBehavior(dialog.getWindow()); return dialog; } @@ -191,10 +184,6 @@ public void onCancel(DialogInterface dialog) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - if (getInAppLayout(insetPadding) == InAppLayout.FULLSCREEN) { - getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - } - // Set initial window gravity based on inset padding (only for non-fullscreen) if (getInAppLayout(insetPadding) != InAppLayout.FULLSCREEN) { applyWindowGravity(getDialog().getWindow(), "onCreateView"); @@ -295,9 +284,7 @@ public void run() { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - // Handle edge-to-edge insets with modern approach (only for non-fullscreen) - // Full screen in-apps should not have padding from system bars - if (getInAppLayout(insetPadding) != InAppLayout.FULLSCREEN) { + if (getInAppLayout(insetPadding) != InAppLayout.FULLSCREEN && hostIsEdgeToEdge) { ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> { Insets sysBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(0, sysBars.top, 0, sysBars.bottom); @@ -600,30 +587,11 @@ public void run() { return; } - DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); Window window = notification.getDialog().getWindow(); Rect insetPadding = notification.insetPadding; - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - Point size = new Point(); - - // Get the correct screen size based on api level - // https://stackoverflow.com/questions/35780980/getting-the-actual-screen-height-android - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - display.getRealSize(size); - } else { - display.getSize(size); - } - - int webViewWidth = size.x; - int webViewHeight = size.y; - - //Check if the dialog is full screen if (insetPadding.bottom == 0 && insetPadding.top == 0) { - //Handle full screen - window.setLayout(webViewWidth, webViewHeight); - getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); } else { // Resize the WebView directly with explicit size float relativeHeight = height * getResources().getDisplayMetrics().density; @@ -710,6 +678,56 @@ static int roundToNearest90Degrees(int orientation) { } } + private void configureSystemBarBehavior(Window window) { + if (window == null) return; + Activity activity = getActivity(); + if (activity == null || activity.getWindow() == null) return; + + if (hostIsEdgeToEdge) { + WindowCompat.setDecorFitsSystemWindows(window, false); + window.setStatusBarColor(Color.TRANSPARENT); + window.setNavigationBarColor(Color.TRANSPARENT); + } else { + window.setStatusBarColor(activity.getWindow().getStatusBarColor()); + window.setNavigationBarColor(activity.getWindow().getNavigationBarColor()); + } + } + + private boolean isHostActivityEdgeToEdge() { + Activity activity = getActivity(); + if (activity == null || activity.getWindow() == null) return false; + + if (hasEdgeToEdgeLegacyFlags(activity)) { + return true; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return isContentDrawnBehindSystemBars(activity); + } + + return false; + } + + private boolean hasEdgeToEdgeLegacyFlags(Activity activity) { + int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); + return (flags & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0; + } + + private boolean isContentDrawnBehindSystemBars(Activity activity) { + View contentView = activity.findViewById(android.R.id.content); + if (contentView == null) return false; + + int contentTop = getViewTopPositionInWindow(contentView); + boolean statusBarPushesContentDown = contentTop > 0; + return !statusBarPushesContentDown; + } + + private int getViewTopPositionInWindow(View view) { + int[] position = new int[2]; + view.getLocationInWindow(position); + return position[1]; + } + /** * Sets the window gravity based on inset padding *