Skip to content

Commit 7b902d4

Browse files
committed
Add edge-to-edge support for the splash screen
Splash screen uses deprecated APIs for setting status bar and navigation bar colors. This PR updates splash screen to use window insets and draw behind the system bars.
1 parent ba8eb4d commit 7b902d4

File tree

7 files changed

+195
-52
lines changed

7 files changed

+195
-52
lines changed

androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/LauncherActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
package com.google.androidbrowserhelper.trusted;
1616

17+
import static androidx.core.view.WindowCompat.enableEdgeToEdge;
18+
1719
import android.app.Activity;
1820
import android.content.Intent;
1921
import android.content.pm.PackageManager;
@@ -36,6 +38,7 @@
3638
import androidx.browser.trusted.sharing.ShareData;
3739
import androidx.browser.trusted.sharing.ShareTarget;
3840
import androidx.core.content.ContextCompat;
41+
import androidx.core.view.WindowCompat;
3942

4043
import com.google.androidbrowserhelper.trusted.splashscreens.PwaWrapperSplashScreenStrategy;
4144

@@ -131,6 +134,7 @@ public class LauncherActivity extends Activity {
131134
@Override
132135
protected void onCreate(@Nullable Bundle savedInstanceState) {
133136
super.onCreate(savedInstanceState);
137+
enableEdgeToEdge(getWindow());
134138

135139
mStartupUptimeMillis = SystemClock.uptimeMillis();
136140
sLauncherActivitiesAlive++;

androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/Utils.java

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,54 +33,6 @@
3333
*/
3434
public class Utils {
3535

36-
/** Sets status bar color. Makes the icons dark if necessary. */
37-
public static void setStatusBarColor(Activity activity, @ColorInt int color) {
38-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
39-
activity.getWindow().setStatusBarColor(color);
40-
41-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
42-
&& shouldUseDarkIconsOnBackground(color)) {
43-
addSystemUiVisibilityFlag(activity, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
44-
}
45-
}
46-
47-
/** Sets navigation bar color. Makes the icons dark if necessary */
48-
public static void setNavigationBarColor(Activity activity, @ColorInt int color) {
49-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
50-
51-
activity.getWindow().setNavigationBarColor(color);
52-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
53-
&& shouldUseDarkIconsOnBackground(color)) {
54-
addSystemUiVisibilityFlag(activity, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
55-
}
56-
}
57-
58-
private static void addSystemUiVisibilityFlag(Activity activity, int flag) {
59-
View root = activity.getWindow().getDecorView().getRootView();
60-
int visibility = root.getSystemUiVisibility();
61-
visibility |= flag;
62-
root.setSystemUiVisibility(visibility);
63-
}
64-
65-
/**
66-
* Determines whether to use dark icons on a background with given color by comparing the
67-
* contrast ratio (https://www.w3.org/TR/WCAG20/#contrast-ratiodef) to a threshold.
68-
* This criterion matches the one used by Chrome:
69-
* https://chromium.googlesource.com/chromium/src/+/90ac05ba6cb9ab5d5df75f0cef62c950be3716c3/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java#215
70-
*/
71-
private static boolean shouldUseDarkIconsOnBackground(@ColorInt int backgroundColor) {
72-
float luminance = 0.2126f * luminanceOfColorComponent(Color.red(backgroundColor))
73-
+ 0.7152f * luminanceOfColorComponent(Color.green(backgroundColor))
74-
+ 0.0722f * luminanceOfColorComponent(Color.blue(backgroundColor));
75-
float contrast = Math.abs((1.05f) / (luminance + 0.05f));
76-
return contrast < 3;
77-
}
78-
79-
private static float luminanceOfColorComponent(float c) {
80-
c /= 255f;
81-
return (c < 0.03928f) ? c / 12.92f : (float) Math.pow((c + 0.055f) / 1.055f, 2.4f);
82-
}
83-
8436
/**
8537
* Converts drawable located at given resource id into a Bitmap.
8638
*/
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.google.androidbrowserhelper.trusted.splashscreens;
2+
3+
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
4+
5+
import android.app.Activity;
6+
import android.graphics.Color;
7+
import android.os.Build;
8+
import android.view.View;
9+
import android.view.ViewGroup;
10+
import android.widget.FrameLayout;
11+
12+
import androidx.annotation.ColorInt;
13+
import androidx.core.graphics.Insets;
14+
import androidx.core.view.ViewCompat;
15+
import androidx.core.view.WindowCompat;
16+
import androidx.core.view.WindowInsetsCompat;
17+
import androidx.core.view.WindowInsetsControllerCompat;
18+
19+
public class EdgeToEdgeController {
20+
private Activity mActivity;
21+
22+
private SystemBarBackgroundDrawable mSystemBarBackgroundDrawable;
23+
24+
public EdgeToEdgeController(Activity activity) {
25+
mActivity = activity;
26+
}
27+
28+
public FrameLayout getWrapperView(@ColorInt int defaultColor) {
29+
FrameLayout rootView = new FrameLayout(mActivity);
30+
rootView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
31+
mSystemBarBackgroundDrawable = new SystemBarBackgroundDrawable(defaultColor);
32+
rootView.setBackground(mSystemBarBackgroundDrawable);
33+
34+
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
35+
Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
36+
mSystemBarBackgroundDrawable.setSystemBarPaddings(systemBarInsets.top, systemBarInsets.bottom);
37+
v.setPadding(0, systemBarInsets.top, 0, systemBarInsets.bottom);
38+
v.invalidate();
39+
return insets;
40+
});
41+
42+
ViewCompat.setOnApplyWindowInsetsListener(mActivity.getWindow().getDecorView(), (v, insets) -> insets);
43+
44+
return rootView;
45+
}
46+
47+
public void setStatusBarColor(@ColorInt int color) {
48+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
49+
mActivity.getWindow().setStatusBarColor(color);
50+
}
51+
mSystemBarBackgroundDrawable.setStatusBarColor(color);
52+
if (shouldUseDarkIconsOnBackground(color)) {
53+
addSystemUiVisibilityFlag(mActivity, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
54+
WindowInsetsControllerCompat windowInsetsController =
55+
WindowCompat.getInsetsController(mActivity.getWindow(), mActivity.getWindow().getDecorView());
56+
if (windowInsetsController != null) {
57+
windowInsetsController.setAppearanceLightStatusBars(true);
58+
}
59+
}
60+
}
61+
62+
public void setNavigationBarColor(@ColorInt int color) {
63+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
64+
mActivity.getWindow().setNavigationBarColor(color);
65+
}
66+
mSystemBarBackgroundDrawable.setNavigationBarColor(color);
67+
if (shouldUseDarkIconsOnBackground(color)) {
68+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
69+
addSystemUiVisibilityFlag(mActivity, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
70+
}
71+
WindowInsetsControllerCompat windowInsetsController =
72+
WindowCompat.getInsetsController(mActivity.getWindow(), mActivity.getWindow().getDecorView());
73+
if (windowInsetsController != null) {
74+
windowInsetsController.setAppearanceLightNavigationBars(true);
75+
}
76+
}
77+
}
78+
79+
private static void addSystemUiVisibilityFlag(Activity activity, int flag) {
80+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return;
81+
82+
View root = activity.getWindow().getDecorView().getRootView();
83+
int visibility = root.getSystemUiVisibility();
84+
visibility |= flag;
85+
root.setSystemUiVisibility(visibility);
86+
}
87+
88+
/**
89+
* Determines whether to use dark icons on a background with given color by comparing the
90+
* contrast ratio (https://www.w3.org/TR/WCAG20/#contrast-ratiodef) to a threshold.
91+
* This criterion matches the one used by Chrome:
92+
* https://chromium.googlesource.com/chromium/src/+/90ac05ba6cb9ab5d5df75f0cef62c950be3716c3/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java#215
93+
*/
94+
private static boolean shouldUseDarkIconsOnBackground(@ColorInt int backgroundColor) {
95+
float luminance = 0.2126f * luminanceOfColorComponent(Color.red(backgroundColor))
96+
+ 0.7152f * luminanceOfColorComponent(Color.green(backgroundColor))
97+
+ 0.0722f * luminanceOfColorComponent(Color.blue(backgroundColor));
98+
float contrast = Math.abs((1.05f) / (luminance + 0.05f));
99+
return contrast < 3;
100+
}
101+
102+
private static float luminanceOfColorComponent(float c) {
103+
c /= 255f;
104+
return (c < 0.03928f) ? c / 12.92f : (float) Math.pow((c + 0.055f) / 1.055f, 2.4f);
105+
}
106+
}

androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/splashscreens/PwaWrapperSplashScreenStrategy.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.text.TextUtils;
2626
import android.util.Log;
2727
import android.view.ViewGroup;
28+
import android.widget.FrameLayout;
2829
import android.widget.ImageView;
2930

3031
import com.google.androidbrowserhelper.trusted.Utils;
@@ -89,6 +90,8 @@ public class PwaWrapperSplashScreenStrategy implements SplashScreenStrategy {
8990

9091
private boolean mStartChromeBeforeAnimationComplete;
9192

93+
private EdgeToEdgeController mEdgeToEdgeController;
94+
9295
/**
9396
* @param activity {@link Activity} on top of which a TWA is going to be launched.
9497
* @param drawableId Resource id of the Drawable of an image (e.g. logo) displayed in the
@@ -130,6 +133,8 @@ public void onTwaLaunchInitiated(String providerPackage, TrustedWebActivityInten
130133
return;
131134
}
132135

136+
mEdgeToEdgeController = new EdgeToEdgeController(mActivity);
137+
133138
showSplashScreen();
134139
if (mSplashImage != null) {
135140
customizeStatusAndNavBarDuringSplashScreen(providerPackage, builder);
@@ -157,7 +162,9 @@ private void showSplashScreen() {
157162
view.setImageMatrix(mTransformationMatrix);
158163
}
159164

160-
mActivity.setContentView(view);
165+
FrameLayout rootView = mEdgeToEdgeController.getWrapperView(mBackgroundColor);
166+
rootView.addView(view);
167+
mActivity.setContentView(rootView);
161168
}
162169

163170
/**
@@ -169,13 +176,13 @@ private void customizeStatusAndNavBarDuringSplashScreen(
169176
Integer navbarColor = sSystemBarColorPredictor.getExpectedNavbarColor(mActivity,
170177
providerPackage, builder);
171178
if (navbarColor != null) {
172-
Utils.setNavigationBarColor(mActivity, navbarColor);
179+
mEdgeToEdgeController.setNavigationBarColor(navbarColor);
173180
}
174181

175182
Integer statusBarColor = sSystemBarColorPredictor.getExpectedStatusBarColor(mActivity,
176183
providerPackage, builder);
177184
if (statusBarColor != null) {
178-
Utils.setStatusBarColor(mActivity, statusBarColor);
185+
mEdgeToEdgeController.setStatusBarColor(statusBarColor);
179186
}
180187
}
181188

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
2+
package com.google.androidbrowserhelper.trusted.splashscreens;
3+
4+
5+
import android.graphics.Canvas;
6+
import android.graphics.ColorFilter;
7+
import android.graphics.Paint;
8+
import android.graphics.PixelFormat;
9+
import android.graphics.Rect;
10+
import android.graphics.drawable.Drawable;
11+
12+
import androidx.annotation.ColorInt;
13+
14+
class SystemBarBackgroundDrawable extends Drawable {
15+
16+
private int mStatusBarPadding;
17+
private int mNavigationBarPadding;
18+
19+
private int mStatusBarColor;
20+
private int mNavigationBarColor;
21+
22+
public SystemBarBackgroundDrawable(@ColorInt int defaultColor) {
23+
mStatusBarColor = defaultColor;
24+
mNavigationBarColor = defaultColor;
25+
}
26+
27+
public void setSystemBarPaddings(int statusBarPadding, int navigationBarPadding) {
28+
mStatusBarPadding = statusBarPadding;
29+
mNavigationBarPadding = navigationBarPadding;
30+
}
31+
public void setNavigationBarColor(@ColorInt int navigationBarColor) {
32+
this.mNavigationBarColor = navigationBarColor;
33+
}
34+
35+
public void setStatusBarColor(@ColorInt int statusBarColor) {
36+
this.mStatusBarColor = statusBarColor;
37+
}
38+
39+
@Override
40+
public void draw(Canvas canvas) {
41+
Rect bounds = getBounds();
42+
43+
Paint paint = new Paint();
44+
paint.setStyle(Paint.Style.FILL);
45+
paint.setStrokeWidth(0); // Width of the rectangle's border
46+
47+
paint.setColor(mStatusBarColor);
48+
Rect statusBarRect = new Rect(bounds.left, bounds.top, bounds.right, bounds.top + mStatusBarPadding);
49+
canvas.drawRect(statusBarRect, paint);
50+
51+
paint.setColor(mNavigationBarColor);
52+
Rect navigationBarRect = new Rect(bounds.left, bounds.bottom - mNavigationBarPadding, bounds.right, bounds.bottom);
53+
canvas.drawRect(navigationBarRect, paint);
54+
}
55+
56+
// These methods must be implemented
57+
@Override
58+
public void setAlpha(int alpha) {}
59+
60+
@Override
61+
public void setColorFilter(ColorFilter colorFilter) {}
62+
63+
@Override
64+
public int getOpacity() {
65+
return PixelFormat.OPAQUE;
66+
}
67+
}

demos/twa-basic/src/main/AndroidManifest.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,19 @@
2020
android:icon="@mipmap/ic_launcher"
2121
android:label="@string/app_name"
2222
android:supportsRtl="true"
23+
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
2324
android:theme="@android:style/Theme.Translucent.NoTitleBar">
2425

2526
<meta-data
2627
android:name="asset_statements"
2728
android:resource="@string/asset_statements" />
2829

30+
<activity android:name="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity">
31+
<meta-data
32+
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
33+
android:value="https://airhorner.com" />
34+
</activity>
35+
2936
<activity android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
3037
android:label="@string/app_name"
3138
android:exported="true">

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ androidx-annotation = "1.9.1"
55
androidx-appcompat = "1.7.0"
66
androidx-browser = "1.10.0-alpha01"
77
androidx-constraintlayout = "2.2.0"
8-
androidx-core = "1.10.1"
8+
androidx-core = "1.17.0"
99
androidx-recyclerview = "1.1.0"
1010
androidx-test-espresso-core = "3.2.0"
1111
androidx-test-core = "1.4.0"

0 commit comments

Comments
 (0)