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 @@
+
+
+
+
+