From a252bd89751360c9217ce91a815da91e94c0bdb5 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 4 Sep 2025 11:35:42 +0800 Subject: [PATCH 1/4] Support safe area in API <= 34 and API >= 35 in React Native --- .../android/build.gradle | 1 + .../reactnative/WebKitWebViewActivity.java | 153 +++++++++++++++++- .../android/src/main/res/values/styles.xml | 2 +- 3 files changed, 150 insertions(+), 6 deletions(-) diff --git a/packages/authgear-react-native/android/build.gradle b/packages/authgear-react-native/android/build.gradle index cce19058..14cdabf7 100644 --- a/packages/authgear-react-native/android/build.gradle +++ b/packages/authgear-react-native/android/build.gradle @@ -121,6 +121,7 @@ dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules + implementation 'androidx.core:core:1.12.0' implementation 'androidx.browser:browser:1.2.0' implementation "androidx.biometric:biometric:1.2.0-alpha03" // NOTE(backup): Please search NOTE(backup) before you update security-crypto or tink-android. diff --git a/packages/authgear-react-native/android/src/main/java/com/authgear/reactnative/WebKitWebViewActivity.java b/packages/authgear-react-native/android/src/main/java/com/authgear/reactnative/WebKitWebViewActivity.java index 1f40fb6d..bfb76dcc 100644 --- a/packages/authgear-react-native/android/src/main/java/com/authgear/reactnative/WebKitWebViewActivity.java +++ b/packages/authgear-react-native/android/src/main/java/com/authgear/reactnative/WebKitWebViewActivity.java @@ -7,20 +7,26 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Message; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.FrameLayout; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; @@ -28,8 +34,14 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import androidx.core.content.res.ResourcesCompat; +import androidx.core.graphics.Insets; import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.util.TypedValueCompat; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; public class WebKitWebViewActivity extends AppCompatActivity { @@ -38,7 +50,11 @@ public class WebKitWebViewActivity extends AppCompatActivity { private static final int MENU_ID_CANCEL = 1; private static final int TAG_FILE_CHOOSER = 1; + private FrameLayout mRootFrameLayout; + private Toolbar mToolbar; + private FrameLayout mToolbarFrameLayout; private WebView mWebView; + private Insets mLastSeenInsets; private Uri result; private StartActivityHandles> handles = new StartActivityHandles<>(); @@ -104,6 +120,19 @@ private MyWebViewClient(WebKitWebViewActivity activity) { this.activity = activity; } + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + // onPageStarted is not always called, but when it is called, it is called before + // onPageFinished. + // Therefore, we put the edge-to-edge handling here hoping that + // the safe area insets can be set as soon as possible. + + view.evaluateJavascript(USERSCRIPT_USER_SELECT_NONE, null); + this.activity.handleNonEdgeToEdge(); + this.activity.handleEdgeToEdge(); + } + @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); @@ -115,6 +144,8 @@ public void onPageFinished(WebView view, String url) { // The caveat is that the script is run in the main frame only. // But we do not actually use iframes so it does not matter. view.evaluateJavascript(USERSCRIPT_USER_SELECT_NONE, null); + this.activity.handleNonEdgeToEdge(); + this.activity.handleEdgeToEdge(); } @TargetApi(Build.VERSION_CODES.N) @@ -240,10 +271,75 @@ public static Intent createIntent(Context ctx, Options options) { return intent; } + private float getActionBarSizeInDp() { + float actionBarSizeInDp = 44f; + TypedValue tv = new TypedValue(); + if (this.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + int actionBarSizeInPx = TypedValue.complexToDimensionPixelSize(tv.data, this.getResources().getDisplayMetrics()); + actionBarSizeInDp = TypedValueCompat.pxToDp((float) actionBarSizeInPx, this.getResources().getDisplayMetrics()); + } + return actionBarSizeInDp; + } + + private void applyInsetsToWebView(Insets safeAreaInsets) { + float actionBarSizeInDp = this.getActionBarSizeInDp(); + DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); + float actionBarSizeInPx = TypedValueCompat.dpToPx(actionBarSizeInDp, displayMetrics); + float top = TypedValueCompat.pxToDp((float) safeAreaInsets.top + actionBarSizeInPx, displayMetrics); + float right = TypedValueCompat.pxToDp((float) safeAreaInsets.right, displayMetrics); + float bottom = TypedValueCompat.pxToDp((float) safeAreaInsets.bottom, displayMetrics); + float left = TypedValueCompat.pxToDp((float) safeAreaInsets.left, displayMetrics); + + String safeAreaJs = + "document.documentElement.style.setProperty('--safe-area-inset-top', '" + top + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-right', '" + right + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-bottom', '" + bottom + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-left', '" + left + "px');"; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + this.mWebView.evaluateJavascript(safeAreaJs, null); + } + } + + private void handleNonEdgeToEdge() { + // In non edge-to-edge, the insets listener is not called. + // So we have to apply the insets here. + Insets insets = this.mLastSeenInsets == null ? Insets.NONE : this.mLastSeenInsets; + this.applyInsetsToWebView(insets); + } + + private void handleEdgeToEdge() { + // In edge-to-edge, we ask the system to invoke the insets listener. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + this.mRootFrameLayout.requestApplyInsets(); + } + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + this.mRootFrameLayout = new FrameLayout(this); + this.mRootFrameLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); + + this.mToolbarFrameLayout = new FrameLayout(this); + this.mToolbarFrameLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + )); + + float actionBarSizeInDp = this.getActionBarSizeInDp(); + + this.mToolbar = new Toolbar(this); + this.mToolbar.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + (int) TypedValueCompat.dpToPx(actionBarSizeInDp, this.getResources().getDisplayMetrics()) + )); + this.setSupportActionBar(this.mToolbar); + Options options = this.getOptions(); // Do not show title. @@ -251,7 +347,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // Configure navigation bar background color. if (options.actionBarBackgroundColor != null) { - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(options.actionBarBackgroundColor)); + ColorDrawable colorDrawable = new ColorDrawable(options.actionBarBackgroundColor); + getSupportActionBar().setBackgroundDrawable(colorDrawable); + this.mToolbarFrameLayout.setBackgroundDrawable(colorDrawable); } // Show back button. @@ -266,17 +364,62 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // Configure web view. this.mWebView = new WebView(this); + this.mWebView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); this.mWebView.getSettings().setSupportMultipleWindows(true); this.mWebView.getSettings().setDomStorageEnabled(true); - this.setContentView(this.mWebView); this.mWebView.setWebViewClient(new MyWebViewClient(this)); this.mWebView.setWebChromeClient(new MyWebChromeClient(this)); WebSettings webSettings = this.mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); - if (savedInstanceState == null) { - this.mWebView.loadUrl(options.url.toString()); - } + this.mRootFrameLayout.addView(this.mWebView); + this.mRootFrameLayout.addView(this.mToolbarFrameLayout); + this.mToolbarFrameLayout.addView(this.mToolbar); + this.setContentView(this.mRootFrameLayout); + + ViewCompat.setOnApplyWindowInsetsListener(this.mRootFrameLayout, new OnApplyWindowInsetsListener() { + + @Override + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + Insets safeAreaInsets = insets.getInsets( + WindowInsetsCompat.Type.systemBars() | + WindowInsetsCompat.Type.displayCutout() | + WindowInsetsCompat.Type.ime() + ); + + WebKitWebViewActivity.this.mLastSeenInsets = safeAreaInsets; + + ViewGroup.MarginLayoutParams toolbarParams = (ViewGroup.MarginLayoutParams) mToolbar.getLayoutParams(); + toolbarParams.setMargins( + safeAreaInsets.left, + safeAreaInsets.top, + safeAreaInsets.right, + 0 + ); + + WebKitWebViewActivity.this.applyInsetsToWebView(safeAreaInsets); + + return WindowInsetsCompat.CONSUMED; + } + }); + + this.mRootFrameLayout.post(new Runnable() { + @Override + public void run() { + // We want the content view to draw at least once before loading the URL. + // + // In non edge-to-edge, the insets listener is never called so mLastSeenInsets is null. + // + // In edge-to-edge, the insets listener will be called at least once in the first draw, + // so by the time onPageStart / onPageFinished is called, mLastSeenInsets is not null. + if (savedInstanceState == null) { + WebKitWebViewActivity.this.mWebView.loadUrl(options.url.toString()); + } + } + }); } @Override diff --git a/packages/authgear-react-native/android/src/main/res/values/styles.xml b/packages/authgear-react-native/android/src/main/res/values/styles.xml index d1ac9ea0..8dc20c8b 100644 --- a/packages/authgear-react-native/android/src/main/res/values/styles.xml +++ b/packages/authgear-react-native/android/src/main/res/values/styles.xml @@ -1,4 +1,4 @@ - From 5738c811ddd8d9ea16cd6aed827e7257061de597 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 4 Sep 2025 11:36:22 +0800 Subject: [PATCH 2/4] Configure action bar colors in React Native example --- example/reactnative/src/screens/MainScreen.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/reactnative/src/screens/MainScreen.tsx b/example/reactnative/src/screens/MainScreen.tsx index 385e8419..65def99c 100644 --- a/example/reactnative/src/screens/MainScreen.tsx +++ b/example/reactnative/src/screens/MainScreen.tsx @@ -387,6 +387,8 @@ const HomeScreen: React.FC = () => { }, android: { wechatRedirectURI, + actionBarBackgroundColor: 0x00ffffff, + actionBarButtonTintColor: 0xff000000, }, sendWechatAuthRequest, }) From 4b7e5819a51441c27d74e4c87f9edb905ba9839e Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 4 Sep 2025 11:47:52 +0800 Subject: [PATCH 3/4] Support safe area in API <= 34 and API >= 35 in Capacitor --- .../authgear-capacitor/android/build.gradle | 1 + .../capacitor/WebKitWebViewActivity.java | 153 +++++++++++++++++- .../android/src/main/res/values/styles.xml | 2 +- 3 files changed, 150 insertions(+), 6 deletions(-) diff --git a/packages/authgear-capacitor/android/build.gradle b/packages/authgear-capacitor/android/build.gradle index 64975048..9d5fb3b4 100644 --- a/packages/authgear-capacitor/android/build.gradle +++ b/packages/authgear-capacitor/android/build.gradle @@ -52,6 +52,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation 'androidx.core:core:1.12.0' implementation 'androidx.browser:browser:1.2.0' implementation "androidx.biometric:biometric:1.2.0-alpha05" // NOTE(backup): Please search NOTE(backup) before you update security-crypto or tink-android. diff --git a/packages/authgear-capacitor/android/src/main/java/com/authgear/capacitor/WebKitWebViewActivity.java b/packages/authgear-capacitor/android/src/main/java/com/authgear/capacitor/WebKitWebViewActivity.java index 26f986f3..8fdc7c57 100644 --- a/packages/authgear-capacitor/android/src/main/java/com/authgear/capacitor/WebKitWebViewActivity.java +++ b/packages/authgear-capacitor/android/src/main/java/com/authgear/capacitor/WebKitWebViewActivity.java @@ -7,20 +7,26 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Message; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.FrameLayout; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; @@ -28,8 +34,14 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import androidx.core.content.res.ResourcesCompat; +import androidx.core.graphics.Insets; import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.util.TypedValueCompat; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; public class WebKitWebViewActivity extends AppCompatActivity { @@ -38,7 +50,11 @@ public class WebKitWebViewActivity extends AppCompatActivity { private static final int MENU_ID_CANCEL = 1; private static final int TAG_FILE_CHOOSER = 1; + private FrameLayout mRootFrameLayout; + private Toolbar mToolbar; + private FrameLayout mToolbarFrameLayout; private WebView mWebView; + private Insets mLastSeenInsets; private Uri result; private StartActivityHandles> handles = new StartActivityHandles<>(); @@ -85,6 +101,19 @@ private MyWebViewClient(WebKitWebViewActivity activity) { this.activity = activity; } + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + // onPageStarted is not always called, but when it is called, it is called before + // onPageFinished. + // Therefore, we put the edge-to-edge handling here hoping that + // the safe area insets can be set as soon as possible. + + view.evaluateJavascript(USERSCRIPT_USER_SELECT_NONE, null); + this.activity.handleNonEdgeToEdge(); + this.activity.handleEdgeToEdge(); + } + @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); @@ -96,6 +125,8 @@ public void onPageFinished(WebView view, String url) { // The caveat is that the script is run in the main frame only. // But we do not actually use iframes so it does not matter. view.evaluateJavascript(USERSCRIPT_USER_SELECT_NONE, null); + this.activity.handleNonEdgeToEdge(); + this.activity.handleEdgeToEdge(); } @TargetApi(Build.VERSION_CODES.N) @@ -199,10 +230,75 @@ public static Intent createIntent(Context ctx, Options options) { return intent; } + private float getActionBarSizeInDp() { + float actionBarSizeInDp = 44f; + TypedValue tv = new TypedValue(); + if (this.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + int actionBarSizeInPx = TypedValue.complexToDimensionPixelSize(tv.data, this.getResources().getDisplayMetrics()); + actionBarSizeInDp = TypedValueCompat.pxToDp((float) actionBarSizeInPx, this.getResources().getDisplayMetrics()); + } + return actionBarSizeInDp; + } + + private void applyInsetsToWebView(Insets safeAreaInsets) { + float actionBarSizeInDp = this.getActionBarSizeInDp(); + DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); + float actionBarSizeInPx = TypedValueCompat.dpToPx(actionBarSizeInDp, displayMetrics); + float top = TypedValueCompat.pxToDp((float) safeAreaInsets.top + actionBarSizeInPx, displayMetrics); + float right = TypedValueCompat.pxToDp((float) safeAreaInsets.right, displayMetrics); + float bottom = TypedValueCompat.pxToDp((float) safeAreaInsets.bottom, displayMetrics); + float left = TypedValueCompat.pxToDp((float) safeAreaInsets.left, displayMetrics); + + String safeAreaJs = + "document.documentElement.style.setProperty('--safe-area-inset-top', '" + top + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-right', '" + right + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-bottom', '" + bottom + "px');\n" + + "document.documentElement.style.setProperty('--safe-area-inset-left', '" + left + "px');"; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + this.mWebView.evaluateJavascript(safeAreaJs, null); + } + } + + private void handleNonEdgeToEdge() { + // In non edge-to-edge, the insets listener is not called. + // So we have to apply the insets here. + Insets insets = this.mLastSeenInsets == null ? Insets.NONE : this.mLastSeenInsets; + this.applyInsetsToWebView(insets); + } + + private void handleEdgeToEdge() { + // In edge-to-edge, we ask the system to invoke the insets listener. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + this.mRootFrameLayout.requestApplyInsets(); + } + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + this.mRootFrameLayout = new FrameLayout(this); + this.mRootFrameLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); + + this.mToolbarFrameLayout = new FrameLayout(this); + this.mToolbarFrameLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + )); + + float actionBarSizeInDp = this.getActionBarSizeInDp(); + + this.mToolbar = new Toolbar(this); + this.mToolbar.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + (int) TypedValueCompat.dpToPx(actionBarSizeInDp, this.getResources().getDisplayMetrics()) + )); + this.setSupportActionBar(this.mToolbar); + Options options = this.getOptions(); // Do not show title. @@ -210,7 +306,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // Configure navigation bar background color. if (options.actionBarBackgroundColor != null) { - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(options.actionBarBackgroundColor)); + ColorDrawable colorDrawable = new ColorDrawable(options.actionBarBackgroundColor); + getSupportActionBar().setBackgroundDrawable(colorDrawable); + this.mToolbarFrameLayout.setBackgroundDrawable(colorDrawable); } // Show back button. @@ -225,17 +323,62 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // Configure web view. this.mWebView = new WebView(this); + this.mWebView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); this.mWebView.getSettings().setSupportMultipleWindows(true); this.mWebView.getSettings().setDomStorageEnabled(true); - this.setContentView(this.mWebView); this.mWebView.setWebViewClient(new MyWebViewClient(this)); this.mWebView.setWebChromeClient(new MyWebChromeClient(this)); WebSettings webSettings = this.mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); - if (savedInstanceState == null) { - this.mWebView.loadUrl(options.url.toString()); - } + this.mRootFrameLayout.addView(this.mWebView); + this.mRootFrameLayout.addView(this.mToolbarFrameLayout); + this.mToolbarFrameLayout.addView(this.mToolbar); + this.setContentView(this.mRootFrameLayout); + + ViewCompat.setOnApplyWindowInsetsListener(this.mRootFrameLayout, new OnApplyWindowInsetsListener() { + + @Override + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + Insets safeAreaInsets = insets.getInsets( + WindowInsetsCompat.Type.systemBars() | + WindowInsetsCompat.Type.displayCutout() | + WindowInsetsCompat.Type.ime() + ); + + WebKitWebViewActivity.this.mLastSeenInsets = safeAreaInsets; + + ViewGroup.MarginLayoutParams toolbarParams = (ViewGroup.MarginLayoutParams) mToolbar.getLayoutParams(); + toolbarParams.setMargins( + safeAreaInsets.left, + safeAreaInsets.top, + safeAreaInsets.right, + 0 + ); + + WebKitWebViewActivity.this.applyInsetsToWebView(safeAreaInsets); + + return WindowInsetsCompat.CONSUMED; + } + }); + + this.mRootFrameLayout.post(new Runnable() { + @Override + public void run() { + // We want the content view to draw at least once before loading the URL. + // + // In non edge-to-edge, the insets listener is never called so mLastSeenInsets is null. + // + // In edge-to-edge, the insets listener will be called at least once in the first draw, + // so by the time onPageStart / onPageFinished is called, mLastSeenInsets is not null. + if (savedInstanceState == null) { + WebKitWebViewActivity.this.mWebView.loadUrl(options.url.toString()); + } + } + }); } @Override diff --git a/packages/authgear-capacitor/android/src/main/res/values/styles.xml b/packages/authgear-capacitor/android/src/main/res/values/styles.xml index d1ac9ea0..8dc20c8b 100644 --- a/packages/authgear-capacitor/android/src/main/res/values/styles.xml +++ b/packages/authgear-capacitor/android/src/main/res/values/styles.xml @@ -1,4 +1,4 @@ - From 719997d36621a03f5fb1eebbd65d6e349d64a588 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 4 Sep 2025 11:54:05 +0800 Subject: [PATCH 4/4] Configure action bar colors in Capacitor example --- example/capacitor/src/pages/Home.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/capacitor/src/pages/Home.tsx b/example/capacitor/src/pages/Home.tsx index 00e869e0..5c78293e 100644 --- a/example/capacitor/src/pages/Home.tsx +++ b/example/capacitor/src/pages/Home.tsx @@ -252,6 +252,10 @@ function AuthgearDemo() { modalPresentationStyle: "fullScreen", isInspectable: true, }, + android: { + actionBarBackgroundColor: 0x00ffffff, + actionBarButtonTintColor: 0xff000000, + }, }) : undefined, isSSOEnabled,