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
Expand Up @@ -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;
Expand Down Expand Up @@ -58,6 +59,12 @@
private boolean callbackOnCancel = false;
private String htmlString;
private String messageId;

// Resize debouncing fields
private Handler resizeHandler = new Handler();

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
Handler.Handler
should be avoided because it has been deprecated.

Copilot Autofix

AI 9 days ago

To fix this issue, we should replace new Handler() with new Handler(Looper.getMainLooper()), which constructs a Handler associated with the main thread's message queue. This is the recommended approach per Android documentation starting in API level 30, and has the same effect as the deprecated default constructor in most cases targeting UI work.

The changes are confined to line 64 in the file iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java. To implement this fix, we also need to import android.os.Looper if it isn't already imported. The rest of the code that uses resizeHandler can remain unchanged.


Suggested changeset 1
iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
--- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
@@ -15,6 +15,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.Gravity;
@@ -61,7 +62,7 @@
     private String messageId;
     
     // Resize debouncing fields
-    private Handler resizeHandler = new Handler();
+    private Handler resizeHandler = new Handler(Looper.getMainLooper());
     private Runnable pendingResizeRunnable;
     private float lastContentHeight = -1;
     private static final int RESIZE_DEBOUNCE_DELAY_MS = 200;
EOF
@@ -15,6 +15,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
@@ -61,7 +62,7 @@
private String messageId;

// Resize debouncing fields
private Handler resizeHandler = new Handler();
private Handler resizeHandler = new Handler(Looper.getMainLooper());
private Runnable pendingResizeRunnable;
private float lastContentHeight = -1;
private static final int RESIZE_DEBOUNCE_DELAY_MS = 200;
Copilot is powered by AI and may make mistakes. Always verify output.
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;
Expand Down Expand Up @@ -105,6 +112,35 @@
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) {
Expand Down Expand Up @@ -144,6 +180,25 @@
}
});
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) {
Expand All @@ -162,41 +217,111 @@
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();

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
Handler.Handler
should be avoided because it has been deprecated.

Copilot Autofix

AI 9 days ago

To resolve the deprecated method usage, replace instances of new Handler() with new Handler(Looper.getMainLooper()) when the handler is to be used for main thread/UI actions (as is the case here, since DialogFragment callback code executes on the main thread). This is a trivial change: update the handler initialization on line 260 to pass Looper.getMainLooper() as an argument to the Handler constructor. To do this, we also need to import android.os.Looper, which is already provided in all Android environments.

Files to edit: only iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java.

  • Add the necessary import for android.os.Looper
  • Update line 260: change new Handler() to new Handler(Looper.getMainLooper())

Suggested changeset 1
iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
--- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
@@ -15,6 +15,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.Gravity;
@@ -257,7 +258,7 @@
                             lastOrientation = currentOrientation;
                             
                             // Use longer delay for orientation changes to allow layout to stabilize
-                            final Handler handler = new Handler();
+                            final Handler handler = new Handler(Looper.getMainLooper());
                             handler.postDelayed(new Runnable() {
                                 @Override
                                 public void run() {
EOF
@@ -15,6 +15,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
@@ -257,7 +258,7 @@
lastOrientation = currentOrientation;

// Use longer delay for orientation changes to allow layout to stabilize
final Handler handler = new Handler();
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Copilot is powered by AI and may make mistakes. Always verify output.
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;
}
}
}
};
}

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) {
Expand Down Expand Up @@ -226,6 +351,12 @@
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;
}
Expand Down Expand Up @@ -414,7 +545,50 @@

@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);
}

/**
Expand Down Expand Up @@ -462,9 +636,44 @@
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Loading