diff --git a/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java b/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java index 7b71d3cf905..6cb232e76db 100644 --- a/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java +++ b/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java @@ -812,6 +812,13 @@ protected void onDestroy() { @Override public void onBackPressed() { + // Emit the cross-platform common:BackButtonPressed event to Go. If a Go + // listener calls event.Cancel(), the back action is suppressed (the + // listener handles navigation itself). Otherwise fall through to the + // default: WebView goBack if history exists, else exit. + if (bridge != null && bridge.onBackPressed()) { + return; + } if (webView != null && webView.canGoBack()) { webView.goBack(); } else { diff --git a/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/WailsBridge.java b/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/WailsBridge.java index bdce3334c4c..1dc4ac4016d 100644 --- a/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/WailsBridge.java +++ b/v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/WailsBridge.java @@ -119,6 +119,7 @@ public class WailsBridge { private static native void nativeMainThreadCallback(int callbackID); private static native void nativeEmitSystemEvent(String name, String json); private static native void nativeEmitEvent(String name, String json); + private static native boolean nativeOnBackPressed(); public WailsBridge(Activity activity) { this.activity = activity; @@ -187,6 +188,15 @@ public void onLowMemory() { if (initialized) nativeOnLowMemory(); } + /** + * Notify Go that the back button was pressed. Returns true if a Go listener + * consumed the event (caller should NOT perform default back navigation). + */ + public boolean onBackPressed() { + if (initialized) return nativeOnBackPressed(); + return false; + } + /** * Notify Go that the page finished loading. */ diff --git a/v3/pkg/application/application_android.go b/v3/pkg/application/application_android.go index dba0d0c93e9..13873154c69 100644 --- a/v3/pkg/application/application_android.go +++ b/v3/pkg/application/application_android.go @@ -729,6 +729,59 @@ func Java_com_wails_app_WailsBridge_nativeOnLowMemory(env *C.JNIEnv, obj C.jobje emitAndroidApplicationEvent(events.Android.ApplicationLowMemory) } +// Java_com_wails_app_WailsBridge_nativeOnBackPressed is called from MainActivity +// when the user presses the back button/gesture. It emits the cross-platform +// common:BackButtonPressed event and returns true if a registered hook cancelled +// it (meaning Java should NOT perform the default goBack/exit behavior). +// +// To intercept the back button, register a hook (not a listener): +// +// app.Event.RegisterApplicationEventHook(events.Common.BackButtonPressed, func(e *application.ApplicationEvent) { +// e.Cancel() // suppress default back navigation +// }) +// +// Hooks run synchronously before the JNI call returns; listeners run +// asynchronously and cannot reliably cancel in time. +// +//export Java_com_wails_app_WailsBridge_nativeOnBackPressed +func Java_com_wails_app_WailsBridge_nativeOnBackPressed(env *C.JNIEnv, obj C.jobject) C.jboolean { + globalAppLock.RLock() + app := globalApp + globalAppLock.RUnlock() + if app == nil { + return C.JNI_FALSE + } + + event := newApplicationEvent(events.Common.BackButtonPressed) + eventID := uint(events.Common.BackButtonPressed) + + // Run hooks synchronously. We cannot use handleApplicationEvent because + // it returns early if no listeners are registered (before reaching hooks). + // Hooks are the correct cancellation mechanism here. + app.applicationEventHooksLock.RLock() + hooks := app.applicationEventHooks[eventID] + app.applicationEventHooksLock.RUnlock() + for _, hook := range hooks { + hook.callback(event) + if event.IsCancelled() { + return C.JNI_TRUE + } + } + + // Also dispatch to listeners (async, non-cancelable) for observability. + app.applicationEventListenersLock.RLock() + listeners := app.applicationEventListeners[eventID] + app.applicationEventListenersLock.RUnlock() + for _, listener := range listeners { + go func() { + defer func() { recover() }() + listener.callback(event) + }() + } + + return C.JNI_FALSE +} + // Java_com_wails_app_WailsBridge_nativeEmitSystemEvent is the funnel the // Android system-event receivers (battery, network, lock, theme) call to // deliver a typed android: application event with its JSON payload. diff --git a/v3/pkg/events/events.go b/v3/pkg/events/events.go index d09ae67ecee..89402d4ac7e 100644 --- a/v3/pkg/events/events.go +++ b/v3/pkg/events/events.go @@ -39,6 +39,7 @@ type commonEvents struct { ScreenLocked ApplicationEventType ScreenUnlocked ApplicationEventType LowMemory ApplicationEventType + BackButtonPressed ApplicationEventType } func newCommonEvents() commonEvents { @@ -76,6 +77,7 @@ func newCommonEvents() commonEvents { ScreenLocked: 1289, ScreenUnlocked: 1290, LowMemory: 1291, + BackButtonPressed: 1296, } } @@ -872,4 +874,5 @@ var eventToJS = map[uint]string{ 1289: "common:ScreenLocked", 1290: "common:ScreenUnlocked", 1291: "common:LowMemory", + 1296: "common:BackButtonPressed", } diff --git a/v3/pkg/events/events.txt b/v3/pkg/events/events.txt index 16a719f5608..ec913dfe1c9 100644 --- a/v3/pkg/events/events.txt +++ b/v3/pkg/events/events.txt @@ -26,6 +26,7 @@ common:WindowZoom common:WindowZoomIn common:WindowZoomOut common:WindowZoomReset +common:BackButtonPressed linux:ApplicationStartup linux:SystemDidWake! linux:SystemThemeChanged diff --git a/v3/pkg/events/known_events.go b/v3/pkg/events/known_events.go index 4bdbd3ab53f..d1382b70fc6 100644 --- a/v3/pkg/events/known_events.go +++ b/v3/pkg/events/known_events.go @@ -34,6 +34,7 @@ var knownEvents = map[string]struct{}{ "common:WindowZoomIn": {}, "common:WindowZoomOut": {}, "common:WindowZoomReset": {}, + "common:BackButtonPressed": {}, "linux:ApplicationStartup": {}, "linux:SystemDidWake": {}, "linux:SystemThemeChanged": {},