|
|
|
@@ -316,6 +316,23 @@ public class ViewUtil {
|
|
|
|
|
return Insets.max(systemBars, displayCutout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get combined insets from status bar, navigation bar and display cutout areas,
|
|
|
|
|
* excluding the IME (soft keyboard).
|
|
|
|
|
*
|
|
|
|
|
* @param windowInsets The window insets to extract from
|
|
|
|
|
* @return Combined insets excluding IME
|
|
|
|
|
*/
|
|
|
|
|
private static Insets getCombinedInsetsExcludingIme(@NonNull WindowInsetsCompat windowInsets) {
|
|
|
|
|
Insets systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
|
|
|
Insets displayCutout = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout());
|
|
|
|
|
|
|
|
|
|
// Combine systemBars (which excludes IME) with displayCutout using max to handle notches/cutouts
|
|
|
|
|
Insets combined = Insets.max(systemBars, displayCutout);
|
|
|
|
|
|
|
|
|
|
return combined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply window insets to a view by adding margin to avoid drawing it behind system bars.
|
|
|
|
|
* Convenience method that applies insets to all sides.
|
|
|
|
@@ -451,6 +468,67 @@ public class ViewUtil {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars,
|
|
|
|
|
* excluding IME (soft keyboard) insets. This is useful for views that should respect navigation
|
|
|
|
|
* bars but handle keyboard resizing through the window's soft input mode.
|
|
|
|
|
*
|
|
|
|
|
* This method stores the original padding values in view tags to ensure that
|
|
|
|
|
* padding doesn't accumulate on multiple inset applications.
|
|
|
|
|
*
|
|
|
|
|
* Note: This feature is only enabled on API 30+ (Android 11+) where WindowInsets APIs
|
|
|
|
|
* work correctly. On older API levels, the method returns early and the view will use
|
|
|
|
|
* default system bar handling (content may be drawn behind system bars).
|
|
|
|
|
*
|
|
|
|
|
* @param view The view to apply insets to
|
|
|
|
|
*/
|
|
|
|
|
public static void applyWindowInsetsExcludingIme(@NonNull View view) {
|
|
|
|
|
// Only enable on API 30+ where WindowInsets APIs work correctly
|
|
|
|
|
if (!isEdgeToEdgeSupported()) return;
|
|
|
|
|
|
|
|
|
|
// Store the original padding as a tag only if not already stored
|
|
|
|
|
// This prevents losing the true original padding on subsequent calls
|
|
|
|
|
if (view.getTag(R.id.tag_window_insets_padding_left) == null) {
|
|
|
|
|
view.setTag(R.id.tag_window_insets_padding_left, view.getPaddingLeft());
|
|
|
|
|
view.setTag(R.id.tag_window_insets_padding_top, view.getPaddingTop());
|
|
|
|
|
view.setTag(R.id.tag_window_insets_padding_right, view.getPaddingRight());
|
|
|
|
|
view.setTag(R.id.tag_window_insets_padding_bottom, view.getPaddingBottom());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
|
|
|
|
|
Insets insets = getCombinedInsetsExcludingIme(windowInsets);
|
|
|
|
|
|
|
|
|
|
// Retrieve the original padding values from tags with null checks
|
|
|
|
|
Integer leftTag = (Integer) v.getTag(R.id.tag_window_insets_padding_left);
|
|
|
|
|
Integer topTag = (Integer) v.getTag(R.id.tag_window_insets_padding_top);
|
|
|
|
|
Integer rightTag = (Integer) v.getTag(R.id.tag_window_insets_padding_right);
|
|
|
|
|
Integer bottomTag = (Integer) v.getTag(R.id.tag_window_insets_padding_bottom);
|
|
|
|
|
int basePaddingLeft = leftTag != null ? leftTag : 0;
|
|
|
|
|
int basePaddingTop = topTag != null ? topTag : 0;
|
|
|
|
|
int basePaddingRight = rightTag != null ? rightTag : 0;
|
|
|
|
|
int basePaddingBottom = bottomTag != null ? bottomTag : 0;
|
|
|
|
|
|
|
|
|
|
v.setPadding(
|
|
|
|
|
basePaddingLeft + insets.left,
|
|
|
|
|
basePaddingTop + insets.top,
|
|
|
|
|
basePaddingRight + insets.right,
|
|
|
|
|
basePaddingBottom + insets.bottom
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Consume IME insets to prevent them from affecting child views (like WebView)
|
|
|
|
|
// This stops the double-padding issue where both window resize and inset padding occur
|
|
|
|
|
WindowInsetsCompat withoutIme = new WindowInsetsCompat.Builder(windowInsets)
|
|
|
|
|
.setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE)
|
|
|
|
|
.build();
|
|
|
|
|
return withoutIme;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Request the initial insets to be dispatched if the view is attached
|
|
|
|
|
if (view.isAttachedToWindow()) {
|
|
|
|
|
ViewCompat.requestApplyInsets(view);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply the top status bar inset as the height of a view.
|
|
|
|
|
*/
|
|
|
|
|