From 18d9c00d13cfc85592ed61fa835a0e6165861483 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Tue, 25 Nov 2025 22:30:20 +0100 Subject: [PATCH] NewContactActivity: properly implement edge-to-edge --- .../securesms/BaseActionBarActivity.java | 22 ++++- .../contacts/NewContactActivity.java | 3 +- .../thoughtcrime/securesms/util/ViewUtil.java | 89 +++++++++++++++++++ src/main/res/layout/new_contact_activity.xml | 18 ++-- src/main/res/layout/status_bar_background.xml | 10 +++ src/main/res/values/ids.xml | 5 ++ 6 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 src/main/res/layout/status_bar_background.xml diff --git a/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java b/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java index 0af814bed..fd16cfd0d 100644 --- a/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; @@ -11,12 +12,14 @@ import androidx.activity.EdgeToEdge; import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.WindowCompat; import androidx.fragment.app.Fragment; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.Prefs; +import org.thoughtcrime.securesms.util.ViewUtil; import java.lang.reflect.Field; @@ -33,11 +36,28 @@ public abstract class BaseActionBarActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { onPreCreate(); - EdgeToEdge.enable(this); // docs says to use: WindowCompat.enableEdgeToEdge(getWindow()); but it is not available + EdgeToEdge.enable(this); // TODO: docs says to use: WindowCompat.enableEdgeToEdge(getWindow()); but it requires dep androidx.core:core:1.17.0 which in turns requires to target SDK 36 super.onCreate(savedInstanceState); WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView()).setAppearanceLightStatusBars(false); // force white text in status bar } + @Override + protected void onPostCreate(@Nullable Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // For activities without a custom toolbar, apply insets to status_bar_background view + View statusBarBackground = findViewById(R.id.status_bar_background); + if (statusBarBackground != null) { + ViewUtil.applyWindowInsetsAsHeight(statusBarBackground); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + // elevation is set via status_bar_background view + // otherwise there is a drop-shadow at the top + actionBar.setElevation(0); + } + } + } + @Override protected void onResume() { super.onResume(); diff --git a/src/main/java/org/thoughtcrime/securesms/contacts/NewContactActivity.java b/src/main/java/org/thoughtcrime/securesms/contacts/NewContactActivity.java index b76151cb8..d6b6d2f4f 100644 --- a/src/main/java/org/thoughtcrime/securesms/contacts/NewContactActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/contacts/NewContactActivity.java @@ -42,9 +42,10 @@ public class NewContactActivity extends PassphraseRequiredActionBarActivity actionBar.setTitle(R.string.menu_new_classic_contact); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp); - actionBar.setElevation(0); // TODO: use custom toolbar instead } + ViewUtil.applyWindowInsets(findViewById(R.id.content_container)); + nameInput = ViewUtil.findById(this, R.id.name_text); addrInput = ViewUtil.findById(this, R.id.email_text); addrInput.setText(getIntent().getStringExtra(ADDR_EXTRA)); diff --git a/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java b/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java index 52776edc2..e377e298c 100644 --- a/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java @@ -41,10 +41,12 @@ import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; +import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.util.views.Stub; import chat.delta.util.ListenableFuture; @@ -294,4 +296,91 @@ public class ViewUtil { } return selection; } + + /** + * Apply window insets to a view by adding padding to avoid drawing elements behind system bars. + * Convenience method that applies insets to all sides. + * + * @param view The view to apply insets to + */ + public static void applyWindowInsets(@NonNull View view) { + applyWindowInsets(view, true, true, true, true); + } + + /** + * Apply window insets to a view by adding padding to avoid drawing elements behind system bars. + * + * This method stores the original padding values in view tags to ensure that + * padding doesn't accumulate on multiple inset applications. + * + * @param view The view to apply insets to + * @param left Whether to apply left inset + * @param top Whether to apply top inset + * @param right Whether to apply right inset + * @param bottom Whether to apply bottom inset + */ + public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) { + // 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 = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + + // 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( + left ? basePaddingLeft + insets.left : basePaddingLeft, + top ? basePaddingTop + insets.top : basePaddingTop, + right ? basePaddingRight + insets.right : basePaddingRight, + bottom ? basePaddingBottom + insets.bottom : basePaddingBottom + ); + + return windowInsets; + }); + + // 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. + * This is useful for creating a colored status bar background view. + * + * @param view The view whose height should be set to the status bar inset + */ + public static void applyWindowInsetsAsHeight(@NonNull View view) { + ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + + android.view.ViewGroup.LayoutParams params = v.getLayoutParams(); + if (params != null) { + params.height = insets.top; + v.setLayoutParams(params); + } + + return windowInsets; + }); + + // Request the initial insets to be dispatched if the view is attached + if (view.isAttachedToWindow()) { + ViewCompat.requestApplyInsets(view); + } + } + } diff --git a/src/main/res/layout/new_contact_activity.xml b/src/main/res/layout/new_contact_activity.xml index 52bd86423..54e153105 100644 --- a/src/main/res/layout/new_contact_activity.xml +++ b/src/main/res/layout/new_contact_activity.xml @@ -1,20 +1,24 @@ - + + + + - + + + diff --git a/src/main/res/layout/status_bar_background.xml b/src/main/res/layout/status_bar_background.xml new file mode 100644 index 000000000..140881961 --- /dev/null +++ b/src/main/res/layout/status_bar_background.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/main/res/values/ids.xml b/src/main/res/values/ids.xml index 045e125f3..8fd950072 100644 --- a/src/main/res/values/ids.xml +++ b/src/main/res/values/ids.xml @@ -1,3 +1,8 @@ + + + + +