Skip to content

Commit d4fb8c4

Browse files
Merge pull request #259 from MihaiCristianCondrea/codex/implement-edge-to-edge-ui-in-compose
Refine edge-to-edge handling across screens
2 parents 92f1ae3 + 047e43f commit d4fb8c4

File tree

5 files changed

+162
-18
lines changed

5 files changed

+162
-18
lines changed

app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java

Lines changed: 157 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@
55
import android.view.ViewGroup;
66

77
import androidx.annotation.NonNull;
8+
import androidx.annotation.Nullable;
9+
import androidx.appcompat.widget.Toolbar;
810
import androidx.core.graphics.Insets;
11+
import androidx.core.view.MarginLayoutParamsCompat;
912
import androidx.core.view.ViewCompat;
1013
import androidx.core.view.ViewGroupCompat;
1114
import androidx.core.view.WindowCompat;
1215
import androidx.core.view.WindowInsetsCompat;
1316

1417
import com.d4rk.androidtutorials.java.R;
18+
import com.google.android.material.appbar.AppBarLayout;
19+
import com.google.android.material.bottomappbar.BottomAppBar;
20+
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
21+
import com.google.android.material.floatingactionbutton.FloatingActionButton;
22+
import com.google.android.material.navigation.NavigationBarView;
1523

1624
public final class EdgeToEdgeDelegate {
1725

@@ -21,37 +29,133 @@ private EdgeToEdgeDelegate() {
2129

2230
public static void apply(Activity activity, View view) {
2331
enableEdgeToEdge(activity);
24-
applyInsetsAsPadding(
32+
applyInsetsInternal(
2533
view,
26-
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),
27-
true,
28-
true
34+
null,
35+
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()
2936
);
3037
}
3138

3239
public static void applyBottomBar(Activity activity, View container, View bottomNavigationView) {
3340
enableEdgeToEdge(activity);
34-
applyInsetsAsPadding(
41+
applyInsetsInternal(
3542
container,
36-
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),
37-
true,
38-
false
39-
);
40-
applyInsetsAsPadding(
4143
bottomNavigationView,
42-
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),
43-
false,
44-
true
44+
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()
4545
);
4646
}
4747

48+
private static void applyInsetsInternal(View root, @Nullable View explicitBottomBar, int insetTypes) {
49+
if (root == null) {
50+
return;
51+
}
52+
53+
View topBar = null;
54+
View bottomBar = explicitBottomBar;
55+
if (root instanceof ViewGroup viewGroup) {
56+
topBar = findTopBar(viewGroup);
57+
if (bottomBar == null) {
58+
bottomBar = findBottomBar(viewGroup);
59+
}
60+
applyInsetsToFloatingButtons(viewGroup, insetTypes);
61+
}
62+
63+
if (topBar != null && topBar != root) {
64+
applyInsetsAsPadding(topBar, insetTypes, true, false);
65+
}
66+
if (bottomBar != null && bottomBar != root) {
67+
applyInsetsAsPadding(bottomBar, insetTypes, false, true);
68+
}
69+
70+
boolean applyTop = topBar == null || topBar == root;
71+
boolean applyBottom = bottomBar == null || bottomBar == root;
72+
applyInsetsAsPadding(root, insetTypes, applyTop, applyBottom);
73+
}
74+
4875
private static void enableEdgeToEdge(Activity activity) {
4976
if (activity == null) {
5077
return;
5178
}
5279
WindowCompat.enableEdgeToEdge(activity.getWindow());
5380
}
5481

82+
@Nullable
83+
private static View findTopBar(View view) {
84+
if (!isVisible(view)) {
85+
return null;
86+
}
87+
if (isTopBar(view)) {
88+
return view;
89+
}
90+
if (view instanceof ViewGroup viewGroup) {
91+
for (int i = 0; i < viewGroup.getChildCount(); i++) {
92+
View child = viewGroup.getChildAt(i);
93+
View topBar = findTopBar(child);
94+
if (topBar != null) {
95+
return topBar;
96+
}
97+
}
98+
}
99+
return null;
100+
}
101+
102+
@Nullable
103+
private static View findBottomBar(View view) {
104+
if (!isVisible(view)) {
105+
return null;
106+
}
107+
if (isBottomBar(view)) {
108+
return view;
109+
}
110+
if (view instanceof ViewGroup viewGroup) {
111+
for (int i = 0; i < viewGroup.getChildCount(); i++) {
112+
View child = viewGroup.getChildAt(i);
113+
View bottomBar = findBottomBar(child);
114+
if (bottomBar != null) {
115+
return bottomBar;
116+
}
117+
}
118+
}
119+
return null;
120+
}
121+
122+
private static void applyInsetsToFloatingButtons(View view, int insetTypes) {
123+
if (!isVisible(view)) {
124+
return;
125+
}
126+
if (view instanceof ExtendedFloatingActionButton || view instanceof FloatingActionButton) {
127+
applyInsetsAsMargin(view, insetTypes, false, true);
128+
return;
129+
}
130+
if (view instanceof ViewGroup viewGroup) {
131+
for (int i = 0; i < viewGroup.getChildCount(); i++) {
132+
applyInsetsToFloatingButtons(viewGroup.getChildAt(i), insetTypes);
133+
}
134+
}
135+
}
136+
137+
private static boolean isTopBar(View view) {
138+
int id = view.getId();
139+
return view instanceof AppBarLayout
140+
|| view instanceof Toolbar
141+
|| id == R.id.app_bar_layout
142+
|| id == R.id.toolbar
143+
|| id == R.id.top_app_bar;
144+
}
145+
146+
private static boolean isBottomBar(View view) {
147+
int id = view.getId();
148+
return view instanceof NavigationBarView
149+
|| view instanceof BottomAppBar
150+
|| id == R.id.nav_view
151+
|| id == R.id.bottom_nav
152+
|| id == R.id.bottomBar;
153+
}
154+
155+
private static boolean isVisible(View view) {
156+
return view.getVisibility() == View.VISIBLE;
157+
}
158+
55159
private static void applyInsetsAsPadding(View view, int insetTypes,
56160
boolean applyTop,
57161
boolean applyBottom) {
@@ -95,6 +199,43 @@ private static void applyInsetsAsPadding(View view, int insetTypes,
95199
requestApplyInsetsWhenAttached(view);
96200
}
97201

202+
private static void applyInsetsAsMargin(View view, int insetTypes,
203+
boolean applyTop,
204+
boolean applyBottom) {
205+
if (view == null) {
206+
return;
207+
}
208+
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
209+
if (!(layoutParams instanceof ViewGroup.MarginLayoutParams marginLayoutParams)) {
210+
return;
211+
}
212+
213+
InsetsMargin baseMargin = (InsetsMargin) view.getTag(R.id.tag_edge_to_edge_margin);
214+
if (baseMargin == null) {
215+
baseMargin = new InsetsMargin(
216+
MarginLayoutParamsCompat.getMarginStart(marginLayoutParams),
217+
marginLayoutParams.topMargin,
218+
MarginLayoutParamsCompat.getMarginEnd(marginLayoutParams),
219+
marginLayoutParams.bottomMargin
220+
);
221+
view.setTag(R.id.tag_edge_to_edge_margin, baseMargin);
222+
}
223+
224+
InsetsMargin margin = baseMargin;
225+
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
226+
Insets insets = windowInsets.getInsets(insetTypes);
227+
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
228+
MarginLayoutParamsCompat.setMarginStart(lp, margin.start + insets.left);
229+
lp.topMargin = margin.top + (applyTop ? insets.top : 0);
230+
MarginLayoutParamsCompat.setMarginEnd(lp, margin.end + insets.right);
231+
lp.bottomMargin = margin.bottom + (applyBottom ? insets.bottom : 0);
232+
v.setLayoutParams(lp);
233+
return windowInsets;
234+
});
235+
236+
requestApplyInsetsWhenAttached(view);
237+
}
238+
98239
private static void requestApplyInsetsWhenAttached(@NonNull View view) {
99240
if (ViewCompat.isAttachedToWindow(view)) { // FIXME: 'isAttachedToWindow(android.view.@org.jspecify.annotations.NonNull View)' is deprecated
100241
ViewCompat.requestApplyInsets(view);
@@ -116,4 +257,7 @@ public void onViewDetachedFromWindow(@NonNull View v) {
116257

117258
private record InsetsPadding(int start, int top, int end, int bottom) {
118259
}
260+
261+
private record InsetsMargin(int start, int top, int end, int bottom) {
262+
}
119263
}

app/src/main/res/layout/activity_help.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
android:id="@+id/container"
55
android:layout_width="match_parent"
66
android:layout_height="match_parent"
7-
android:layout_marginHorizontal="24dp"
8-
android:layout_marginVertical="24dp"
9-
android:fitsSystemWindows="true">
7+
android:paddingHorizontal="24dp"
8+
android:paddingVertical="24dp">
109

1110
<com.google.android.material.textview.MaterialTextView
1211
android:id="@+id/text_view_faq"

app/src/main/res/values/ids.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<item name="tag_edge_to_edge_padding" type="id" />
4+
<item name="tag_edge_to_edge_margin" type="id" />
45
</resources>

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
agp = "8.13.0"
33
firebaseCrashlyticsPlugin = "3.0.6"
44
googleServices = "4.4.3"
5-
ossLicensesPlugin = "0.10.6"
5+
ossLicensesPlugin = "0.10.9"
66
appcompat = "1.7.1"
77
appUpdate = "2.1.0"
88
billing = "8.0.0"

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pluginManagement {
88
resolutionStrategy {
99
eachPlugin {
1010
if (requested.id.id == 'com.google.android.gms.oss-licenses-plugin') {
11-
useModule('com.google.android.gms:oss-licenses-plugin:0.10.6')
11+
useModule('com.google.android.gms:oss-licenses-plugin:0.10.9')
1212
}
1313
}
1414
}

0 commit comments

Comments
 (0)