From 0bfbc20b84ce83be72a3e7dd6a5bff38f0fcbff2 Mon Sep 17 00:00:00 2001 From: Manuel Beck Date: Mon, 20 Apr 2026 17:48:48 +0200 Subject: [PATCH] fix: overwritten backbutton not firing on first time by hardware button and never on back gesture (#1910) - When pressing the back button after the app starts nothing happens. When navigating to another page, the back button js event starts to fire. - On Android versions, which support the back gesture, nothing happens - This was due to the empty `OnBackPressedCallback.handleOnBackPressed` method, which has to forward its call to `cordova.getActivity().getOnBackPressedDispatcher().onBackPressed()`, but has to disable during this call the `backCallback`. - Generated-By: GPT-5.4, GitHub Copilot Chat - warning about back button override on the right * chore: resolve methods `registerBackPressedCallback` and `unregisterBackPressedCallback`` - The code moved to inline code --- .../src/org/apache/cordova/CoreAndroid.java | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/framework/src/org/apache/cordova/CoreAndroid.java b/framework/src/org/apache/cordova/CoreAndroid.java index a41b85db..969cc296 100755 --- a/framework/src/org/apache/cordova/CoreAndroid.java +++ b/framework/src/org/apache/cordova/CoreAndroid.java @@ -29,6 +29,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.os.SystemClock; import android.content.IntentFilter; import android.telephony.TelephonyManager; import android.view.KeyEvent; @@ -253,7 +254,6 @@ public class CoreAndroid extends CordovaPlugin { * @param override T=override, F=cancel override */ public void overrideBackbutton(boolean override) { - LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!"); if (cordova.getActivity() == null) { return; } @@ -263,15 +263,24 @@ public class CoreAndroid extends CordovaPlugin { @Override public void run() { if (shouldOverride) { + LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!"); synchronized (backButtonHandlerLock) { if (backCallback == null) { - registerBackPressedCallback(); + backCallback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + dispatchBackKeyEvent(); + } + }; + OnBackPressedDispatcherOwner backPressedDispatcherOwner = cordova.getActivity(); + backPressedDispatcherOwner.getOnBackPressedDispatcher().addCallback(backPressedDispatcherOwner, backCallback); } } } else { synchronized (backButtonHandlerLock) { if (backCallback != null) { - unregisterBackPressedCallback(); + backCallback.remove(); + backCallback = null; } } } @@ -281,29 +290,29 @@ public class CoreAndroid extends CordovaPlugin { }); } - /** - * Registers an AndroidX back callback so Cordova can keep routing back presses through its - * existing key dispatch path across Android versions without directly referencing newer - * platform-only back APIs. - */ - private void registerBackPressedCallback() { - final OnBackPressedDispatcherOwner backPressedDispatcherOwner = this.cordova.getActivity(); + private void dispatchBackKeyEvent() { + // Build a synthetic BACK key press (DOWN + UP) and route it through the WebView. + // This lets Cordova's existing key handling fire the JS "backbutton" event. + final long eventTime = SystemClock.uptimeMillis(); + final KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0); + final KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0); - backCallback = new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - // Intentionally empty. - // On modern Android versions, registering a callback keeps back handling - // routed through Cordova's existing key dispatch path. - } - }; + final boolean handledDown = webView.getView().dispatchKeyEvent(downEvent); + final boolean handledUp = webView.getView().dispatchKeyEvent(upEvent); - backPressedDispatcherOwner.getOnBackPressedDispatcher().addCallback(backPressedDispatcherOwner, backCallback); - } + // If either event was consumed, Cordova/WebView already handled back behavior. + if (handledDown || handledUp) { + return; + } - private void unregisterBackPressedCallback() { - backCallback.remove(); - backCallback = null; + // Otherwise, delegate to Android's default back dispatcher. + // Temporarily disable this callback to avoid recursive re-entry. + backCallback.setEnabled(false); + try { + cordova.getActivity().getOnBackPressedDispatcher().onBackPressed(); + } finally { + backCallback.setEnabled(true); + } } /**