-
Notifications
You must be signed in to change notification settings - Fork 33
MOB-11663-InApp-Calculations #949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
@@ -51,9 +52,12 @@ public class IterableInAppFragmentHTMLNotification extends DialogFragment implem | |
|
|
||
| private static final int DELAY_THRESHOLD_MS = 500; | ||
|
|
||
| @Nullable static IterableInAppFragmentHTMLNotification notification; | ||
| @Nullable static IterableHelper.IterableUrlCallback clickCallback; | ||
| @Nullable static IterableInAppLocation location; | ||
| @Nullable | ||
| static IterableInAppFragmentHTMLNotification notification; | ||
| @Nullable | ||
| static IterableHelper.IterableUrlCallback clickCallback; | ||
| @Nullable | ||
| static IterableInAppLocation location; | ||
|
|
||
| private IterableWebView webView; | ||
| private boolean loaded; | ||
|
|
@@ -62,6 +66,12 @@ public class IterableInAppFragmentHTMLNotification extends DialogFragment implem | |
| private String htmlString; | ||
| private String messageId; | ||
|
|
||
| // Resize debouncing fields | ||
| private Handler resizeHandler = new Handler(); | ||
Check noticeCode scanning / CodeQL Deprecated method or constructor invocation Note
Invoking
Handler.Handler Error loading related location Loading |
||
| 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; | ||
| private boolean shouldAnimate; | ||
|
|
@@ -92,6 +102,7 @@ public static IterableInAppFragmentHTMLNotification createInstance(@NonNull Stri | |
|
|
||
| /** | ||
| * Returns the notification instance currently being shown | ||
| * | ||
| * @return notification instance | ||
| */ | ||
| public static IterableInAppFragmentHTMLNotification getInstance() { | ||
|
|
@@ -109,6 +120,17 @@ public IterableInAppFragmentHTMLNotification() { | |
| 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) { | ||
| applyWindowGravity(dialog.getWindow(), "onStart"); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void onCreate(@Nullable Bundle savedInstanceState) { | ||
| super.onCreate(savedInstanceState); | ||
|
|
@@ -147,6 +169,10 @@ public void onCancel(DialogInterface dialog) { | |
| } | ||
| }); | ||
| dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); | ||
|
|
||
| // Set window gravity for the 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) { | ||
|
|
@@ -166,40 +192,84 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c | |
| getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); | ||
| } | ||
|
|
||
| // Set initial window gravity based on inset padding | ||
| applyWindowGravity(getDialog().getWindow(), "onCreateView"); | ||
|
|
||
| webView = new IterableWebView(getContext()); | ||
| webView.setId(R.id.webView); | ||
|
|
||
| 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 | ||
|
Comment on lines
+209
to
+215
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows for |
||
| final Handler handler = new Handler(); | ||
Check noticeCode scanning / CodeQL Deprecated method or constructor invocation Note
Invoking
Handler.Handler Error loading related location Loading |
||
| 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()); | ||
Ayyanchira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Create a RelativeLayout as a wrapper for the WebView | ||
| RelativeLayout webViewContainer = new RelativeLayout(this.getContext()); | ||
|
|
||
| int gravity = getVerticalLocation(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; | ||
| } else if (gravity == Gravity.TOP) { | ||
| containerParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; | ||
| } else if (gravity == Gravity.BOTTOM) { | ||
| containerParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; | ||
| } | ||
|
|
||
| // 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); | ||
|
|
||
| // Add the container to the FrameLayout | ||
| frameLayout.addView(webViewContainer, containerParams); | ||
|
|
||
| if (savedInstanceState == null || !savedInstanceState.getBoolean(IN_APP_OPEN_TRACKED, false)) { | ||
| IterableApi.sharedInstance.trackInAppOpen(messageId, location); | ||
| } | ||
|
|
||
| prepareToShowWebView(); | ||
| return relativeLayout; | ||
| return frameLayout; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -240,6 +310,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; | ||
| } | ||
|
|
@@ -388,7 +464,7 @@ private void hideWebView() { | |
|
|
||
| try { | ||
| Animation anim = AnimationUtils.loadAnimation(getContext(), | ||
| animationResource); | ||
| animationResource); | ||
| anim.setDuration(IterableConstants.ITERABLE_IN_APP_ANIMATION_DURATION); | ||
| webView.startAnimation(anim); | ||
| } catch (Exception e) { | ||
|
|
@@ -428,11 +504,55 @@ 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); | ||
| } | ||
|
|
||
| /** | ||
| * Resizes the dialog window based upon the size of its webView HTML content | ||
| * | ||
| * @param height | ||
| */ | ||
| public void resize(final float height) { | ||
|
|
@@ -447,7 +567,7 @@ public void run() { | |
| try { | ||
| // Since this is run asynchronously, notification might've been dismissed already | ||
| if (getContext() == null || notification == null || notification.getDialog() == null || | ||
| notification.getDialog().getWindow() == null || !notification.getDialog().isShowing()) { | ||
| notification.getDialog().getWindow() == null || !notification.getDialog().isShowing()) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -476,9 +596,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); | ||
|
|
@@ -489,6 +644,7 @@ public void run() { | |
|
|
||
| /** | ||
| * Returns the vertical position of the dialog for the given padding | ||
| * | ||
| * @param padding | ||
| * @return | ||
| */ | ||
|
|
@@ -502,6 +658,32 @@ int getVerticalLocation(Rect padding) { | |
| return gravity; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the window gravity based on inset padding | ||
| * | ||
| * @param window The dialog window to configure | ||
| * @param context Debug context string for logging | ||
| */ | ||
| private void applyWindowGravity(Window window, String context) { | ||
| if (window == null) { | ||
| return; | ||
| } | ||
|
|
||
| WindowManager.LayoutParams windowParams = window.getAttributes(); | ||
| int gravity = getVerticalLocation(insetPadding); | ||
|
|
||
| if (gravity == Gravity.CENTER_VERTICAL) { | ||
| windowParams.gravity = Gravity.CENTER; | ||
| } else if (gravity == Gravity.TOP) { | ||
| windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; | ||
| } else if (gravity == Gravity.BOTTOM) { | ||
| windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; | ||
| } | ||
|
|
||
| window.setAttributes(windowParams); | ||
| IterableLogger.d(TAG, "Set window gravity in " + context + ": " + windowParams.gravity); | ||
| } | ||
|
|
||
| InAppLayout getInAppLayout(Rect padding) { | ||
| if (padding.top == 0 && padding.bottom == 0) { | ||
| return InAppLayout.FULLSCREEN; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.