Use the modern OnBackInvokedCallback method to trap the back button on newer Android devices (#16052)

This commit is contained in:
Rachel Blackman
2026-07-27 15:43:23 -07:00
committed by GitHub
parent f4337eded2
commit 412a7c5db6
5 changed files with 74 additions and 2 deletions

View File

@@ -71,7 +71,7 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@style/AppTheme"
android:enableOnBackInvokedCallback="false"
android:enableOnBackInvokedCallback="true"
android:hardwareAccelerated="true" >
<!-- Example of setting SDL hints from AndroidManifest.xml:

View File

@@ -24,6 +24,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
@@ -50,6 +51,8 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -784,6 +787,47 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
}
}
static OnBackInvokedCallback backButtonCallback = new OnBackInvokedCallback() {
Handler mBackKeyHandler;
@Override
public void onBackInvoked() {
if (mBackKeyHandler == null) {
mBackKeyHandler = new Handler(Looper.getMainLooper());
}
onNativeKeyDown(KeyEvent.KEYCODE_BACK);
mBackKeyHandler.postDelayed(new Runnable() {
@Override
public void run() {
onNativeKeyUp(KeyEvent.KEYCODE_BACK);
}
}, 500);
}
};
static boolean mBackKeyTrapEnabled = false;
public static void setBackButtonTrapEnabled(boolean enabled) {
if ( Build.VERSION.SDK_INT < 33 ) {
// We're old enough that we just use the old onBackPressed behavior.
return;
}
// Check so we don't register the same callback twice.
if (enabled == mBackKeyTrapEnabled) {
return;
}
if (enabled) {
mSingleton.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, backButtonCallback);
}
else {
mSingleton.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(backButtonCallback);
}
mBackKeyTrapEnabled = enabled;
}
// File dialog types
private static final int SDL_FILEDIALOG_OPENFILE = 0;
private static final int SDL_FILEDIALOG_SAVEFILE = 1;

View File

@@ -101,6 +101,7 @@ static jmethodID midManualBackButton;
static jmethodID midShowFileDialog;
#endif // !SDL_DIALOG_DISABLED
static jmethodID midGetPreferredLocales;
static jmethodID midSetBackButtonTrapEnabled;
#ifndef SDL_VIDEO_DISABLED
// Video/surface method signatures
@@ -681,6 +682,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZILjava/lang/String;I)Z");
#endif // !SDL_DIALOG_DISABLED
midGetPreferredLocales = (*env)->GetStaticMethodID(env, mActivityClass, "getPreferredLocales", "()Ljava/lang/String;");
midSetBackButtonTrapEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setBackButtonTrapEnabled", "(Z)V");
if (!midGetContext ||
!midGetDeviceFormFactor ||
@@ -698,7 +700,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
#ifndef SDL_DIALOG_DISABLED
!midShowFileDialog ||
#endif
!midGetPreferredLocales) {
!midGetPreferredLocales ||
!midSetBackButtonTrapEnabled) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some core Java callbacks, do you have the latest version of SDLActivity.java?");
}
@@ -3714,4 +3717,14 @@ bool Android_JNI_ShowFileDialog(
}
#endif // !SDL_DIALOG_DISABLED
void Android_JNI_SetBackButtonTrapActive(bool enabled)
{
if (!midSetBackButtonTrapEnabled) {
return;
}
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midSetBackButtonTrapEnabled, enabled);
}
#endif // SDL_PLATFORM_ANDROID

View File

@@ -60,6 +60,7 @@ void Android_LockActivityMutex(void);
void Android_UnlockActivityMutex(void);
void Android_SetAllowRecreateActivity(bool enabled);
void Android_JNI_SetBackButtonTrapActive(bool enabled);
#ifndef SDL_VIDEO_DISABLED
#include <EGL/eglplatform.h>

View File

@@ -28,6 +28,7 @@
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_windowevents_c.h"
#include "../../SDL_hints_c.h"
#include "SDL_androidvideo.h"
#include "SDL_androidgl.h"
@@ -81,6 +82,16 @@ static void Android_DeleteDevice(SDL_VideoDevice *device)
SDL_free(device);
}
// Input is part of video for Android, so this goes here.
static void SDLCALL Android_Video_InternalHintCallback(
void *userdata,
const char *name,
const char *oldvalue,
const char *newvalue)
{
Android_JNI_SetBackButtonTrapActive(SDL_GetStringBoolean(newvalue, false));
}
static SDL_VideoDevice *Android_CreateDevice(void)
{
SDL_VideoDevice *device;
@@ -185,6 +196,8 @@ bool Android_VideoInit(SDL_VideoDevice *_this)
display->current_orientation = Android_JNI_GetDisplayCurrentOrientation();
display->content_scale = Android_ScreenDensity;
SDL_AddHintCallback(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, Android_Video_InternalHintCallback, 0);
Android_InitTouch();
Android_InitMouse();
@@ -195,6 +208,7 @@ bool Android_VideoInit(SDL_VideoDevice *_this)
void Android_VideoQuit(SDL_VideoDevice *_this)
{
SDL_RemoveHintCallback(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, Android_Video_InternalHintCallback, 0);
Android_QuitMouse();
Android_QuitTouch();
}