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
This commit is contained in:
Manuel Beck
2026-04-20 17:48:48 +02:00
committed by GitHub
parent b1705d83c5
commit 0bfbc20b84
@@ -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);
}
}
/**