From 1eab75cfde516ecd52659fb3abff3ce99ab5375b Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Fri, 19 Sep 2025 13:34:46 +0300 Subject: [PATCH] Enable edge-to-edge handling across activities --- .../onboarding/OnboardingActivity.java | 3 + .../ui/screens/startup/StartupActivity.java | 3 + .../java/utils/EdgeToEdgeDelegate.java | 130 +++++++++++++++--- .../main/res/layout/activity_onboarding.xml | 3 +- app/src/main/res/layout/activity_startup.xml | 3 +- app/src/main/res/values/ids.xml | 4 + 6 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 app/src/main/res/values/ids.xml diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java index 62787321..7e6181fe 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java @@ -17,6 +17,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityOnboardingBinding; import com.d4rk.androidtutorials.java.ui.screens.main.MainActivity; import com.d4rk.androidtutorials.java.ui.screens.startup.StartupViewModel; +import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; import com.google.android.ump.ConsentInformation; @@ -39,6 +40,8 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityOnboardingBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + EdgeToEdgeDelegate.apply(this, binding.getRoot()); + viewModel = new ViewModelProvider(this).get(OnboardingViewModel.class); // Fallback: show the consent form again if required. diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/startup/StartupActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/startup/StartupActivity.java index ab0cbe43..d273514a 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/startup/StartupActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/startup/StartupActivity.java @@ -10,6 +10,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityStartupBinding; import com.d4rk.androidtutorials.java.startup.StartupInitializer; import com.d4rk.androidtutorials.java.ui.screens.onboarding.OnboardingActivity; +import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.google.android.ump.ConsentRequestParameters; import dagger.hilt.android.AndroidEntryPoint; @@ -26,6 +27,8 @@ protected void onCreate(Bundle savedInstanceState) { ActivityStartupBinding binding = ActivityStartupBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + EdgeToEdgeDelegate.apply(this, binding.getRoot()); + StartupInitializer.schedule(this); viewModel = new ViewModelProvider(this).get(StartupViewModel.class); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java b/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java index 61466132..7769903b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java @@ -2,39 +2,135 @@ import android.app.Activity; import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; +import androidx.core.view.ViewGroupCompat; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; +import com.d4rk.androidtutorials.java.R; + public final class EdgeToEdgeDelegate { private EdgeToEdgeDelegate() { // Utility class } - public static void apply(Activity activity, View container) { - WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); - - ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> { - Insets bars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); - v.setPadding(bars.left, bars.top, bars.right, bars.bottom); - return WindowInsetsCompat.CONSUMED; - }); + public static void apply(Activity activity, View view) { + enableEdgeToEdge(activity); + applyInsetsAsPadding( + view, + WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), + true, + true, + true, + true + ); } public static void applyBottomBar(Activity activity, View container, View bottomNavigationView) { - WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); + enableEdgeToEdge(activity); + applyInsetsAsPadding( + container, + WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), + true, + true, + true, + false + ); + applyInsetsAsPadding( + bottomNavigationView, + WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), + true, + false, + true, + true + ); + } + + private static void enableEdgeToEdge(Activity activity) { + if (activity == null) { + return; + } + WindowCompat.enableEdgeToEdge(activity.getWindow()); + } + + private static void applyInsetsAsPadding(View view, int insetTypes, + boolean applyStart, boolean applyTop, + boolean applyEnd, boolean applyBottom) { + if (view == null) { + return; + } + + if (view instanceof ViewGroup viewGroup) { + ViewGroupCompat.installCompatInsetsDispatch(viewGroup); + } - ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> { - Insets bars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); - v.setPadding(bars.left, bars.top, bars.right, 0); + InsetsPadding basePadding = (InsetsPadding) view.getTag(R.id.tag_edge_to_edge_padding); + if (basePadding == null) { + basePadding = new InsetsPadding( + view.getPaddingStart(), + view.getPaddingTop(), + view.getPaddingEnd(), + view.getPaddingBottom() + ); + view.setTag(R.id.tag_edge_to_edge_padding, basePadding); + } - if (bottomNavigationView != null) { - bottomNavigationView.setPadding(0, 0, 0, bars.bottom); - } - return WindowInsetsCompat.CONSUMED; + InsetsPadding padding = basePadding; + ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(insetTypes); + int start = applyStart ? insets.left : 0; + int top = applyTop ? insets.top : 0; + int end = applyEnd ? insets.right : 0; + int bottom = applyBottom ? insets.bottom : 0; + + ViewCompat.setPaddingRelative( + v, + padding.start + start, + padding.top + top, + padding.end + end, + padding.bottom + bottom + ); + return windowInsets; }); + + requestApplyInsetsWhenAttached(view); + } + + private static void requestApplyInsetsWhenAttached(@NonNull View view) { + if (ViewCompat.isAttachedToWindow(view)) { + ViewCompat.requestApplyInsets(view); + } else { + view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.removeOnAttachStateChangeListener(this); + ViewCompat.requestApplyInsets(v); + } + + @Override + public void onViewDetachedFromWindow(View v) { + // No-op + } + }); + } + } + + private static final class InsetsPadding { + final int start; + final int top; + final int end; + final int bottom; + + InsetsPadding(int start, int top, int end, int bottom) { + this.start = start; + this.top = top; + this.end = end; + this.bottom = bottom; + } } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml index 37b0c7db..5cf6d862 100644 --- a/app/src/main/res/layout/activity_onboarding.xml +++ b/app/src/main/res/layout/activity_onboarding.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> + + +