From 0fb31c15bad7a6156e8f92d78cafb81a76b5296e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <“ayyanchira.akshay@gmail.com”> Date: Wed, 10 Sep 2025 07:19:02 -0700 Subject: [PATCH] Cursor Iteration Needs reduction in code. Lot of repition. Heavy review and testing needed. Committing as inapps are looking stable and are sticking with the layout with back to back phone rotations when inapp performance would fail usually --- ...IterableInAppFragmentHTMLNotification.java | 241 ++++++++++++++++-- .../iterableapi/IterableWebChromeClient.java | 5 +- 2 files changed, 229 insertions(+), 17 deletions(-) diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java index 779c1c93c..df67a8248 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java @@ -26,6 +26,7 @@ import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; import android.widget.RelativeLayout; import androidx.annotation.NonNull; @@ -58,6 +59,12 @@ public class IterableInAppFragmentHTMLNotification extends DialogFragment implem private boolean callbackOnCancel = false; private String htmlString; private String messageId; + + // Resize debouncing fields + private Handler resizeHandler = new Handler(); + private Runnable pendingResizeRunnable; + private float lastContentHeight = -1; + private static final int RESIZE_DEBOUNCE_DELAY_MS = 200; private double backgroundAlpha; //TODO: remove in a future version private Rect insetPadding; @@ -105,6 +112,35 @@ public IterableInAppFragmentHTMLNotification() { insetPadding = new Rect(); this.setStyle(DialogFragment.STYLE_NO_FRAME, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar); } + + @Override + public void onStart() { + super.onStart(); + + // Set dialog positioning after the dialog is created and shown + Dialog dialog = getDialog(); + if (dialog != null) { + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams windowParams = window.getAttributes(); + int startGravity = getVerticalLocation(insetPadding); + + if (startGravity == Gravity.CENTER_VERTICAL) { + windowParams.gravity = Gravity.CENTER; + IterableLogger.d(TAG, "Set dialog gravity to CENTER in onStart"); + } else if (startGravity == Gravity.TOP) { + windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set dialog gravity to TOP in onStart"); + } else if (startGravity == Gravity.BOTTOM) { + windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set dialog gravity to BOTTOM in onStart"); + } + + window.setAttributes(windowParams); + IterableLogger.d(TAG, "Applied window gravity in onStart: " + windowParams.gravity); + } + } + } @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -144,6 +180,25 @@ public void onCancel(DialogInterface dialog) { } }); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + + // Set window gravity for the dialog + Window window = dialog.getWindow(); + WindowManager.LayoutParams windowParams = window.getAttributes(); + int dialogGravity = getVerticalLocation(insetPadding); + + if (dialogGravity == Gravity.CENTER_VERTICAL) { + windowParams.gravity = Gravity.CENTER; + IterableLogger.d(TAG, "Set dialog gravity to CENTER in onCreateDialog"); + } else if (dialogGravity == Gravity.TOP) { + windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set dialog gravity to TOP in onCreateDialog"); + } else if (dialogGravity == Gravity.BOTTOM) { + windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set dialog gravity to BOTTOM in onCreateDialog"); + } + + window.setAttributes(windowParams); + if (getInAppLayout(insetPadding) == InAppLayout.FULLSCREEN) { dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } else if (getInAppLayout(insetPadding) != InAppLayout.TOP) { @@ -162,23 +217,57 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c if (getInAppLayout(insetPadding) == InAppLayout.FULLSCREEN) { getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } + + // Set initial window gravity based on inset padding + Window window = getDialog().getWindow(); + WindowManager.LayoutParams windowParams = window.getAttributes(); + int windowGravity = getVerticalLocation(insetPadding); + + if (windowGravity == Gravity.CENTER_VERTICAL) { + windowParams.gravity = Gravity.CENTER; + IterableLogger.d(TAG, "Set initial CENTER window gravity in onCreateView"); + } else if (windowGravity == Gravity.TOP) { + windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set initial TOP window gravity in onCreateView"); + } else if (windowGravity == Gravity.BOTTOM) { + windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Set initial BOTTOM window gravity in onCreateView"); + } + + window.setAttributes(windowParams); webView = new IterableWebView(getContext()); webView.setId(R.id.webView); + + // Debug the HTML content + IterableLogger.d(TAG, "HTML content preview: " + (htmlString.length() > 200 ? htmlString.substring(0, 200) + "..." : htmlString)); + webView.createWithHtml(this, htmlString); if (orientationListener == null) { orientationListener = new OrientationEventListener(getContext(), SensorManager.SENSOR_DELAY_NORMAL) { + private int lastOrientation = -1; + // Resize the webView on device rotation public void onOrientationChanged(int orientation) { - if (loaded) { - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - runResizeScript(); - } - }, 1000); + if (loaded && webView != null) { + // Only trigger on significant orientation changes (90 degree increments) + int currentOrientation = ((orientation + 45) / 90) * 90; + if (currentOrientation != lastOrientation && lastOrientation != -1) { + lastOrientation = currentOrientation; + + // Use longer delay for orientation changes to allow layout to stabilize + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + IterableLogger.d(TAG, "Orientation changed, triggering resize"); + runResizeScript(); + } + }, 1500); // Increased delay for better stability + } else if (lastOrientation == -1) { + lastOrientation = currentOrientation; + } } } }; @@ -186,17 +275,53 @@ public void run() { orientationListener.enable(); - RelativeLayout relativeLayout = new RelativeLayout(this.getContext()); - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); - relativeLayout.setVerticalGravity(getVerticalLocation(insetPadding)); - relativeLayout.addView(webView, layoutParams); + // Create a FrameLayout as the main container for better positioning control + FrameLayout frameLayout = new FrameLayout(this.getContext()); + + // Create a RelativeLayout as a wrapper for the WebView + RelativeLayout webViewContainer = new RelativeLayout(this.getContext()); + + int gravity = getVerticalLocation(insetPadding); + IterableLogger.d(TAG, "Initial setup - gravity: " + gravity + " for inset padding: " + insetPadding); + + // Set FrameLayout gravity based on positioning + FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT + ); + + if (gravity == Gravity.CENTER_VERTICAL) { + containerParams.gravity = Gravity.CENTER; + IterableLogger.d(TAG, "Applied CENTER gravity to container"); + } else if (gravity == Gravity.TOP) { + containerParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Applied TOP gravity to container"); + } else if (gravity == Gravity.BOTTOM) { + containerParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + IterableLogger.d(TAG, "Applied BOTTOM gravity to container"); + } + + // Add WebView to the RelativeLayout container with WRAP_CONTENT for proper sizing + RelativeLayout.LayoutParams webViewParams = new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT + ); + webViewParams.addRule(RelativeLayout.CENTER_IN_PARENT); + webViewContainer.addView(webView, webViewParams); + + IterableLogger.d(TAG, "Added WebView with WRAP_CONTENT and CENTER_IN_PARENT rule"); + + // Add the container to the FrameLayout + frameLayout.addView(webViewContainer, containerParams); + + IterableLogger.d(TAG, "Created FrameLayout with positioned RelativeLayout container"); if (savedInstanceState == null || !savedInstanceState.getBoolean(IN_APP_OPEN_TRACKED, false)) { IterableApi.sharedInstance.trackInAppOpen(messageId, location); } prepareToShowWebView(); - return relativeLayout; + return frameLayout; } public void setLoaded(boolean loaded) { @@ -226,6 +351,12 @@ public void onStop() { public void onDestroy() { super.onDestroy(); + // Clean up pending resize operations + if (resizeHandler != null && pendingResizeRunnable != null) { + resizeHandler.removeCallbacks(pendingResizeRunnable); + pendingResizeRunnable = null; + } + if (this.getActivity() != null && this.getActivity().isChangingConfigurations()) { return; } @@ -414,7 +545,50 @@ private void processMessageRemoval() { @Override public void runResizeScript() { - resize(webView.getContentHeight()); + // Cancel any pending resize operation + if (pendingResizeRunnable != null) { + resizeHandler.removeCallbacks(pendingResizeRunnable); + } + + // Schedule a debounced resize operation + pendingResizeRunnable = new Runnable() { + @Override + public void run() { + performResizeWithValidation(); + } + }; + + resizeHandler.postDelayed(pendingResizeRunnable, RESIZE_DEBOUNCE_DELAY_MS); + } + + private void performResizeWithValidation() { + if (webView == null) { + IterableLogger.w(TAG, "WebView is null, skipping resize"); + return; + } + + float currentHeight = webView.getContentHeight(); + + // Validate content height + if (currentHeight <= 0) { + IterableLogger.w(TAG, "Invalid content height: " + currentHeight + "dp, skipping resize"); + return; + } + + // Check if height has stabilized (avoid unnecessary resizes for same height) + if (Math.abs(currentHeight - lastContentHeight) < 1.0f) { + IterableLogger.d(TAG, "Content height unchanged (" + currentHeight + "dp), skipping resize"); + return; + } + + lastContentHeight = currentHeight; + + IterableLogger.d( + TAG, + "💚 Resizing in-app to height: " + currentHeight + "dp" + ); + + resize(currentHeight); } /** @@ -462,9 +636,44 @@ public void run() { window.setLayout(webViewWidth, webViewHeight); getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } else { + // Resize the WebView directly with explicit size float relativeHeight = height * getResources().getDisplayMetrics().density; - RelativeLayout.LayoutParams webViewLayout = new RelativeLayout.LayoutParams(getResources().getDisplayMetrics().widthPixels, (int) relativeHeight); - webView.setLayoutParams(webViewLayout); + int newWebViewWidth = getResources().getDisplayMetrics().widthPixels; + int newWebViewHeight = (int) relativeHeight; + + // Set WebView to explicit size + RelativeLayout.LayoutParams webViewParams = new RelativeLayout.LayoutParams(newWebViewWidth, newWebViewHeight); + + // Apply positioning based on gravity + int resizeGravity = getVerticalLocation(insetPadding); + IterableLogger.d(TAG, "Resizing WebView directly - gravity: " + resizeGravity + " size: " + newWebViewWidth + "x" + newWebViewHeight + "px for inset padding: " + insetPadding); + + if (resizeGravity == Gravity.CENTER_VERTICAL) { + webViewParams.addRule(RelativeLayout.CENTER_IN_PARENT); + IterableLogger.d(TAG, "Applied CENTER_IN_PARENT to WebView"); + } else if (resizeGravity == Gravity.TOP) { + webViewParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); + webViewParams.addRule(RelativeLayout.CENTER_HORIZONTAL); + IterableLogger.d(TAG, "Applied TOP alignment to WebView"); + } else if (resizeGravity == Gravity.BOTTOM) { + webViewParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + webViewParams.addRule(RelativeLayout.CENTER_HORIZONTAL); + IterableLogger.d(TAG, "Applied BOTTOM alignment to WebView"); + } + + // Make dialog full screen to allow proper positioning + window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); + + // Apply the new layout params to WebView + webView.setLayoutParams(webViewParams); + + // Force layout updates + webView.requestLayout(); + if (webView.getParent() instanceof ViewGroup) { + ((ViewGroup) webView.getParent()).requestLayout(); + } + + IterableLogger.d(TAG, "Applied explicit size and positioning to WebView: " + newWebViewWidth + "x" + newWebViewHeight); } } catch (IllegalArgumentException e) { IterableLogger.e(TAG, "Exception while trying to resize an in-app message", e); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableWebChromeClient.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableWebChromeClient.java index 7631bbae0..8e630ca84 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableWebChromeClient.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableWebChromeClient.java @@ -12,6 +12,9 @@ public class IterableWebChromeClient extends WebChromeClient { @Override public void onProgressChanged(WebView view, int newProgress) { - inAppHTMLNotification.runResizeScript(); + // Only trigger resize when page is fully loaded (100%) to avoid multiple rapid calls + if (newProgress == 100) { + inAppHTMLNotification.runResizeScript(); + } } }