Skip to content

Commit 909d193

Browse files
committed
Enable edge to edge and implement window insets listener
1 parent 3d09af8 commit 909d193

File tree

6 files changed

+169
-49
lines changed

6 files changed

+169
-49
lines changed

play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,17 @@
4545
import java.util.Locale;
4646
import java.util.Objects;
4747

48+
/**
49+
* @noinspection unused
50+
*/
4851
public abstract class AbstractAboutFragment extends Fragment {
4952

5053
protected abstract void collectLibraries(List<Library> libraries);
5154

5255
public static Drawable getIcon(Context context) {
5356
try {
5457
PackageManager pm = context.getPackageManager();
55-
return pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadIcon(pm);
58+
return Objects.requireNonNull(pm.getPackageInfo(context.getPackageName(), 0).applicationInfo).loadIcon(pm);
5659
} catch (PackageManager.NameNotFoundException e) {
5760
// Never happens, self package always exists!
5861
throw new RuntimeException(e);
@@ -62,7 +65,7 @@ public static Drawable getIcon(Context context) {
6265
public static String getAppName(Context context) {
6366
try {
6467
PackageManager pm = context.getPackageManager();
65-
CharSequence label = pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadLabel(pm);
68+
CharSequence label = Objects.requireNonNull(pm.getPackageInfo(context.getPackageName(), 0).applicationInfo).loadLabel(pm);
6669
if (TextUtils.isEmpty(label)) return context.getPackageName();
6770
return label.toString().trim();
6871
} catch (PackageManager.NameNotFoundException e) {
@@ -141,8 +144,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
141144

142145
Button btnCheckUpdates = aboutRoot.findViewById(R.id.btnCheckUpdates);
143146
btnCheckUpdates.setOnClickListener(v -> {
144-
UpdateChecker updateChecker = new UpdateChecker(getContext());
145-
updateChecker.checkForUpdates(() -> {
147+
Context context = getContext();
148+
if (context == null) return;
149+
UpdateChecker updateChecker = new UpdateChecker(context);
150+
updateChecker.checkForUpdates(v, () -> {
146151
});
147152
});
148153
return aboutRoot;

play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
package org.microg.tools.ui;
22

3+
import android.graphics.Color;
4+
import android.os.Build;
35
import android.os.Bundle;
46
import android.view.MenuItem;
7+
import android.view.View;
58
import android.view.ViewGroup;
9+
10+
import androidx.activity.EdgeToEdge;
11+
import androidx.activity.SystemBarStyle;
612
import androidx.appcompat.app.AppCompatActivity;
713
import androidx.appcompat.widget.Toolbar;
14+
import androidx.core.graphics.Insets;
15+
import androidx.core.view.ViewCompat;
16+
import androidx.core.view.WindowInsetsCompat;
17+
import androidx.core.widget.NestedScrollView;
818
import androidx.fragment.app.Fragment;
919
import androidx.fragment.app.FragmentTransaction;
1020

21+
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
22+
23+
import java.util.Objects;
24+
25+
/**
26+
* @noinspection RedundantCast, unused
27+
*/
1128
public abstract class AbstractSettingsActivity extends AppCompatActivity {
1229
protected boolean showHomeAsUp = false;
1330
protected int preferencesResource = 0;
@@ -17,12 +34,14 @@ public abstract class AbstractSettingsActivity extends AppCompatActivity {
1734

1835
@Override
1936
protected void onCreate(Bundle savedInstanceState) {
37+
enableEdgeToEdgeNoContrast();
2038
super.onCreate(savedInstanceState);
2139
setContentView(R.layout.settings_activity);
40+
2241
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
2342

2443
if (showHomeAsUp) {
25-
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
44+
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
2645
}
2746

2847
switchBar = (SwitchBar) findViewById(R.id.switch_bar);
@@ -32,9 +51,51 @@ protected void onCreate(Bundle savedInstanceState) {
3251
customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false));
3352
}
3453

35-
getSupportFragmentManager().beginTransaction()
36-
.replace(R.id.content_wrapper, getFragment())
37-
.commit();
54+
setupWindowInsets();
55+
56+
getSupportFragmentManager().beginTransaction().replace(R.id.content_wrapper, getFragment()).commit();
57+
}
58+
59+
private void setupWindowInsets() {
60+
View rootLayout = findViewById(R.id.root_layout);
61+
ExtendedFloatingActionButton fab = findViewById(R.id.preference_fab);
62+
NestedScrollView nestedScrollView = findViewById(R.id.nested_scroll_view);
63+
64+
final int initialScrollViewPaddingLeft = nestedScrollView.getPaddingLeft();
65+
final int initialScrollViewPaddingTop = nestedScrollView.getPaddingTop();
66+
final int initialScrollViewPaddingRight = nestedScrollView.getPaddingRight();
67+
final int initialScrollViewPaddingBottom = nestedScrollView.getPaddingBottom();
68+
69+
ViewGroup.MarginLayoutParams fabInitialParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
70+
final int initialFabMarginLeft = fabInitialParams.leftMargin;
71+
final int initialFabMarginRight = fabInitialParams.rightMargin;
72+
final int initialFabMarginBottom = fabInitialParams.bottomMargin;
73+
74+
ViewCompat.setOnApplyWindowInsetsListener(rootLayout, (v, windowInsets) -> {
75+
Insets systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
76+
77+
Insets imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
78+
79+
boolean imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime());
80+
int bottomInset = imeVisible ? imeInsets.bottom : systemBarsInsets.bottom;
81+
82+
nestedScrollView.setPadding(initialScrollViewPaddingLeft + systemBarsInsets.left, initialScrollViewPaddingTop, initialScrollViewPaddingRight + systemBarsInsets.right, initialScrollViewPaddingBottom + bottomInset);
83+
84+
ViewGroup.MarginLayoutParams fabParams = (ViewGroup.MarginLayoutParams) fab.getLayoutParams();
85+
fabParams.leftMargin = initialFabMarginLeft + systemBarsInsets.left;
86+
fabParams.rightMargin = initialFabMarginRight + systemBarsInsets.right;
87+
fabParams.bottomMargin = initialFabMarginBottom + systemBarsInsets.bottom;
88+
fab.setLayoutParams(fabParams);
89+
90+
return windowInsets;
91+
});
92+
}
93+
94+
private void enableEdgeToEdgeNoContrast() {
95+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
96+
EdgeToEdge.enable(this, SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT));
97+
getWindow().setNavigationBarContrastEnforced(false);
98+
}
3899
}
39100

40101
public void setCustomBarLayout(int layout) {
@@ -50,11 +111,7 @@ public SwitchBar getSwitchBar() {
50111
}
51112

52113
public void replaceFragment(Fragment fragment) {
53-
getSupportFragmentManager().beginTransaction()
54-
.addToBackStack("root")
55-
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
56-
.replace(R.id.content_wrapper, fragment)
57-
.commit();
114+
getSupportFragmentManager().beginTransaction().addToBackStack("root").setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).replace(R.id.content_wrapper, fragment).commit();
58115
}
59116

60117
@Override
@@ -76,4 +133,4 @@ protected Fragment getFragment() {
76133
fragment.setArguments(b);
77134
return fragment;
78135
}
79-
}
136+
}

play-services-core/microg-ui-tools/src/main/java/org/microg/tools/updater/UpdateChecker.java

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import android.os.Handler;
77
import android.os.Looper;
88
import android.view.View;
9+
import android.view.ViewGroup;
10+
11+
import androidx.core.view.ViewCompat;
12+
import androidx.core.view.WindowInsetsCompat;
913

1014
import com.google.android.material.snackbar.Snackbar;
1115

@@ -34,13 +38,13 @@ public UpdateChecker(Context context) {
3438
this.client = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();
3539
}
3640

37-
public void checkForUpdates(Runnable onComplete) {
41+
public void checkForUpdates(View view, Runnable onComplete) {
3842
CompletableFuture.supplyAsync(this::fetchLatestVersion).thenAccept(latestVersion -> runOnMainThread(() -> {
39-
handleLatestVersion(latestVersion);
43+
handleLatestVersion(latestVersion, view);
4044
onComplete.run();
4145
})).exceptionally(throwable -> {
4246
runOnMainThread(() -> {
43-
handleError(throwable);
47+
handleError(throwable, view);
4448
onComplete.run();
4549
});
4650
return null;
@@ -49,11 +53,9 @@ public void checkForUpdates(Runnable onComplete) {
4953

5054
private String fetchLatestVersion() {
5155
Request request = new Request.Builder().url(GITHUB_API_URL).build();
52-
5356
try (Response response = client.newCall(request).execute()) {
5457
if (response.isSuccessful() && response.body() != null) {
55-
String jsonData = response.body().string();
56-
return parseLatestVersion(jsonData);
58+
return parseLatestVersion(response.body().string());
5759
} else {
5860
throw new IOException("Unsuccessful response: " + response.code());
5961
}
@@ -71,54 +73,54 @@ private String parseLatestVersion(String jsonData) {
7173
}
7274
}
7375

74-
private void handleLatestVersion(String latestVersion) {
76+
private void handleLatestVersion(String latestVersion, View view) {
7577
Context context = contextRef.get();
76-
if (context == null) return;
77-
78-
View rootView = getRootView(context);
79-
if (rootView == null) return;
78+
if (context == null || view == null) return;
8079

8180
String appVersion = context.getString(R.string.github_tag_version);
8281

8382
if (appVersion.compareTo(latestVersion) < 0) {
84-
showSnackbarWithAction(rootView, context.getString(R.string.update_available), context.getString(R.string.snackbar_button_download), v -> openGitHubReleaseLink(context));
83+
showSnackbarWithAction(view, context.getString(R.string.update_available), context.getString(R.string.snackbar_button_download), v -> openGitHubReleaseLink(context));
8584
} else {
86-
showSnackbar(rootView, context.getString(R.string.no_update_available));
85+
showSnackbar(view, context.getString(R.string.no_update_available));
8786
}
8887
}
8988

90-
private void handleError(Throwable throwable) {
89+
private void handleError(Throwable throwable, View view) {
9190
Context context = contextRef.get();
92-
if (context == null) return;
91+
if (context == null || view == null) return;
9392

94-
View rootView = getRootView(context);
95-
if (rootView != null) {
96-
String errorMessage = throwable.getMessage() != null && throwable.getMessage().toLowerCase().contains("connection") ? context.getString(R.string.error_connection) + " " + throwable.getMessage() : context.getString(R.string.error_others) + " " + throwable.getMessage();
93+
String errorMessage = throwable.getMessage() != null && throwable.getMessage().toLowerCase().contains("connection") ? context.getString(R.string.error_connection) + " " + throwable.getMessage() : context.getString(R.string.error_others) + " " + throwable.getMessage();
94+
showSnackbar(view, errorMessage);
95+
}
9796

98-
showSnackbar(rootView, errorMessage);
99-
}
97+
private void showSnackbar(View view, String message) {
98+
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
99+
configureSnackbar(snackbar);
100+
snackbar.show();
100101
}
101102

102-
private void showSnackbar(View rootView, String message) {
103-
Snackbar.make(rootView, message, Snackbar.LENGTH_LONG).show();
103+
private void showSnackbarWithAction(View view, String message, String actionText, View.OnClickListener actionListener) {
104+
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(actionText, actionListener);
105+
configureSnackbar(snackbar);
106+
snackbar.show();
104107
}
105108

106-
private void showSnackbarWithAction(View rootView, String message, String actionText, View.OnClickListener actionListener) {
107-
Snackbar.make(rootView, message, Snackbar.LENGTH_LONG).setAction(actionText, actionListener).show();
109+
private void configureSnackbar(Snackbar snackbar) {
110+
ViewCompat.setOnApplyWindowInsetsListener(snackbar.getView(), (v, insets) -> {
111+
int bottomPadding = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
112+
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
113+
params.bottomMargin = bottomPadding;
114+
v.setLayoutParams(params);
115+
return insets;
116+
});
108117
}
109118

110119
private void openGitHubReleaseLink(Context context) {
111120
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(GITHUB_RELEASE_LINK));
112121
context.startActivity(intent);
113122
}
114123

115-
private View getRootView(Context context) {
116-
if (context instanceof android.app.Activity) {
117-
return ((android.app.Activity) context).findViewById(android.R.id.content);
118-
}
119-
return null;
120-
}
121-
122124
private void runOnMainThread(Runnable action) {
123125
new Handler(Looper.getMainLooper()).post(action);
124126
}

play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
1818
xmlns:app="http://schemas.android.com/apk/res-auto"
19+
android:id="@+id/root_layout"
1920
android:layout_width="match_parent"
2021
android:layout_height="match_parent"
2122
android:fitsSystemWindows="true"
@@ -57,8 +58,11 @@
5758
</com.google.android.material.appbar.AppBarLayout>
5859

5960
<androidx.core.widget.NestedScrollView
61+
android:id="@+id/nested_scroll_view"
6062
android:layout_width="match_parent"
6163
android:layout_height="match_parent"
64+
android:clipToPadding="false"
65+
android:fillViewport="true"
6266
android:scrollbars="none"
6367
app:layout_behavior="@string/appbar_scrolling_view_behavior">
6468

0 commit comments

Comments
 (0)