Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package com.google.androidbrowserhelper.trusted;

import static androidx.core.view.WindowCompat.enableEdgeToEdge;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
Expand All @@ -36,6 +38,7 @@
import androidx.browser.trusted.sharing.ShareData;
import androidx.browser.trusted.sharing.ShareTarget;
import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat;

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

Expand Down Expand Up @@ -131,6 +134,7 @@ public class LauncherActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
enableEdgeToEdge(getWindow());

mStartupUptimeMillis = SystemClock.uptimeMillis();
sLauncherActivitiesAlive++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,54 +33,6 @@
*/
public class Utils {

/** Sets status bar color. Makes the icons dark if necessary. */
public static void setStatusBarColor(Activity activity, @ColorInt int color) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
activity.getWindow().setStatusBarColor(color);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& shouldUseDarkIconsOnBackground(color)) {
addSystemUiVisibilityFlag(activity, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}

/** Sets navigation bar color. Makes the icons dark if necessary */
public static void setNavigationBarColor(Activity activity, @ColorInt int color) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;

activity.getWindow().setNavigationBarColor(color);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& shouldUseDarkIconsOnBackground(color)) {
addSystemUiVisibilityFlag(activity, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}

private static void addSystemUiVisibilityFlag(Activity activity, int flag) {
View root = activity.getWindow().getDecorView().getRootView();
int visibility = root.getSystemUiVisibility();
visibility |= flag;
root.setSystemUiVisibility(visibility);
}

/**
* Determines whether to use dark icons on a background with given color by comparing the
* contrast ratio (https://www.w3.org/TR/WCAG20/#contrast-ratiodef) to a threshold.
* This criterion matches the one used by Chrome:
* https://chromium.googlesource.com/chromium/src/+/90ac05ba6cb9ab5d5df75f0cef62c950be3716c3/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java#215
*/
private static boolean shouldUseDarkIconsOnBackground(@ColorInt int backgroundColor) {
float luminance = 0.2126f * luminanceOfColorComponent(Color.red(backgroundColor))
+ 0.7152f * luminanceOfColorComponent(Color.green(backgroundColor))
+ 0.0722f * luminanceOfColorComponent(Color.blue(backgroundColor));
float contrast = Math.abs((1.05f) / (luminance + 0.05f));
return contrast < 3;
}

private static float luminanceOfColorComponent(float c) {
c /= 255f;
return (c < 0.03928f) ? c / 12.92f : (float) Math.pow((c + 0.055f) / 1.055f, 2.4f);
}

/**
* Converts drawable located at given resource id into a Bitmap.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.google.androidbrowserhelper.trusted.splashscreens;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.ColorInt;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;

public class EdgeToEdgeController {
private Activity mActivity;

private SystemBarBackgroundDrawable mSystemBarBackgroundDrawable;

public EdgeToEdgeController(Activity activity) {
mActivity = activity;
}

public FrameLayout getWrapperView(@ColorInt int defaultColor) {
FrameLayout rootView = new FrameLayout(mActivity);
rootView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mSystemBarBackgroundDrawable = new SystemBarBackgroundDrawable(defaultColor);
rootView.setBackground(mSystemBarBackgroundDrawable);

ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
mSystemBarBackgroundDrawable.setSystemBarPaddings(systemBarInsets.top, systemBarInsets.bottom);
v.setPadding(0, systemBarInsets.top, 0, systemBarInsets.bottom);
v.invalidate();
return insets;
});

ViewCompat.setOnApplyWindowInsetsListener(mActivity.getWindow().getDecorView(), (v, insets) -> insets);

return rootView;
}

public void setStatusBarColor(@ColorInt int color) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
mActivity.getWindow().setStatusBarColor(color);
}
mSystemBarBackgroundDrawable.setStatusBarColor(color);
if (shouldUseDarkIconsOnBackground(color)) {
addSystemUiVisibilityFlag(mActivity, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
WindowInsetsControllerCompat windowInsetsController =
WindowCompat.getInsetsController(mActivity.getWindow(), mActivity.getWindow().getDecorView());
if (windowInsetsController != null) {
windowInsetsController.setAppearanceLightStatusBars(true);
}
}
}

public void setNavigationBarColor(@ColorInt int color) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
mActivity.getWindow().setNavigationBarColor(color);
}
mSystemBarBackgroundDrawable.setNavigationBarColor(color);
if (shouldUseDarkIconsOnBackground(color)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
addSystemUiVisibilityFlag(mActivity, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
WindowInsetsControllerCompat windowInsetsController =
WindowCompat.getInsetsController(mActivity.getWindow(), mActivity.getWindow().getDecorView());
if (windowInsetsController != null) {
windowInsetsController.setAppearanceLightNavigationBars(true);
}
}
}

private static void addSystemUiVisibilityFlag(Activity activity, int flag) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return;

View root = activity.getWindow().getDecorView().getRootView();
int visibility = root.getSystemUiVisibility();
visibility |= flag;
root.setSystemUiVisibility(visibility);
}

/**
* Determines whether to use dark icons on a background with given color by comparing the
* contrast ratio (https://www.w3.org/TR/WCAG20/#contrast-ratiodef) to a threshold.
* This criterion matches the one used by Chrome:
* https://chromium.googlesource.com/chromium/src/+/90ac05ba6cb9ab5d5df75f0cef62c950be3716c3/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java#215
*/
private static boolean shouldUseDarkIconsOnBackground(@ColorInt int backgroundColor) {
float luminance = 0.2126f * luminanceOfColorComponent(Color.red(backgroundColor))
+ 0.7152f * luminanceOfColorComponent(Color.green(backgroundColor))
+ 0.0722f * luminanceOfColorComponent(Color.blue(backgroundColor));
float contrast = Math.abs((1.05f) / (luminance + 0.05f));
return contrast < 3;
}

private static float luminanceOfColorComponent(float c) {
c /= 255f;
return (c < 0.03928f) ? c / 12.92f : (float) Math.pow((c + 0.055f) / 1.055f, 2.4f);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

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

private boolean mStartChromeBeforeAnimationComplete;

private EdgeToEdgeController mEdgeToEdgeController;

/**
* @param activity {@link Activity} on top of which a TWA is going to be launched.
* @param drawableId Resource id of the Drawable of an image (e.g. logo) displayed in the
Expand Down Expand Up @@ -130,6 +133,8 @@ public void onTwaLaunchInitiated(String providerPackage, TrustedWebActivityInten
return;
}

mEdgeToEdgeController = new EdgeToEdgeController(mActivity);

showSplashScreen();
if (mSplashImage != null) {
customizeStatusAndNavBarDuringSplashScreen(providerPackage, builder);
Expand Down Expand Up @@ -157,7 +162,9 @@ private void showSplashScreen() {
view.setImageMatrix(mTransformationMatrix);
}

mActivity.setContentView(view);
FrameLayout rootView = mEdgeToEdgeController.getWrapperView(mBackgroundColor);
rootView.addView(view);
mActivity.setContentView(rootView);
}

/**
Expand All @@ -169,13 +176,13 @@ private void customizeStatusAndNavBarDuringSplashScreen(
Integer navbarColor = sSystemBarColorPredictor.getExpectedNavbarColor(mActivity,
providerPackage, builder);
if (navbarColor != null) {
Utils.setNavigationBarColor(mActivity, navbarColor);
mEdgeToEdgeController.setNavigationBarColor(navbarColor);
}

Integer statusBarColor = sSystemBarColorPredictor.getExpectedStatusBarColor(mActivity,
providerPackage, builder);
if (statusBarColor != null) {
Utils.setStatusBarColor(mActivity, statusBarColor);
mEdgeToEdgeController.setStatusBarColor(statusBarColor);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

package com.google.androidbrowserhelper.trusted.splashscreens;


import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;

import androidx.annotation.ColorInt;

class SystemBarBackgroundDrawable extends Drawable {

private int mStatusBarPadding;
private int mNavigationBarPadding;

private int mStatusBarColor;
private int mNavigationBarColor;

public SystemBarBackgroundDrawable(@ColorInt int defaultColor) {
mStatusBarColor = defaultColor;
mNavigationBarColor = defaultColor;
}

public void setSystemBarPaddings(int statusBarPadding, int navigationBarPadding) {
mStatusBarPadding = statusBarPadding;
mNavigationBarPadding = navigationBarPadding;
}
public void setNavigationBarColor(@ColorInt int navigationBarColor) {
this.mNavigationBarColor = navigationBarColor;
}

public void setStatusBarColor(@ColorInt int statusBarColor) {
this.mStatusBarColor = statusBarColor;
}

@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();

Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(0); // Width of the rectangle's border

paint.setColor(mStatusBarColor);
Rect statusBarRect = new Rect(bounds.left, bounds.top, bounds.right, bounds.top + mStatusBarPadding);
canvas.drawRect(statusBarRect, paint);

paint.setColor(mNavigationBarColor);
Rect navigationBarRect = new Rect(bounds.left, bounds.bottom - mNavigationBarPadding, bounds.right, bounds.bottom);
canvas.drawRect(navigationBarRect, paint);
}

// These methods must be implemented
@Override
public void setAlpha(int alpha) {}

@Override
public void setColorFilter(ColorFilter colorFilter) {}

@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
7 changes: 7 additions & 0 deletions demos/twa-basic/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar">

<meta-data
android:name="asset_statements"
android:resource="@string/asset_statements" />

<activity android:name="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity">
<meta-data
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
android:value="https://airhorner.com" />
</activity>

<activity android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
android:label="@string/app_name"
android:exported="true">
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ androidx-annotation = "1.9.1"
androidx-appcompat = "1.7.0"
androidx-browser = "1.10.0-alpha01"
androidx-constraintlayout = "2.2.0"
androidx-core = "1.10.1"
androidx-core = "1.17.0"
androidx-recyclerview = "1.1.0"
androidx-test-espresso-core = "3.2.0"
androidx-test-core = "1.4.0"
Expand Down