From b462db5adbde9c3da7a668dc7e7e7530b3806510 Mon Sep 17 00:00:00 2001 From: Anish Mishra Date: Fri, 4 Apr 2025 16:16:24 +0530 Subject: [PATCH] Android: Hardware keyboard connection status callback Adds a DisplayServer method to register hardware keyboard connection change callback. --- doc/classes/DisplayServer.xml | 8 ++++++++ platform/android/display_server_android.cpp | 10 ++++++++++ platform/android/display_server_android.h | 6 ++++-- .../java/lib/src/org/godotengine/godot/Godot.kt | 1 - .../lib/src/org/godotengine/godot/GodotLib.java | 5 +++++ .../org/godotengine/godot/input/GodotEditText.java | 7 ------- .../godotengine/godot/input/GodotInputHandler.java | 14 ++++++++++++++ platform/android/java_godot_lib_jni.cpp | 7 +++++++ platform/android/java_godot_lib_jni.h | 1 + servers/display_server.cpp | 1 + servers/display_server.h | 1 + 11 files changed, 51 insertions(+), 10 deletions(-) diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 1ff4e1a5e7d..fafdf6e9426 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1204,6 +1204,14 @@ [b]Note:[/b] On iOS, this method has no effect if [member ProjectSettings.display/window/handheld/orientation] is not set to [constant SCREEN_SENSOR]. + + + + + Sets the [param callable] that should be called when hardware keyboard is connected/disconnected. [param callable] should accept a single [bool] parameter indicating whether the keyboard is connected (true) or disconnected (false). + [b]Note:[/b] This method is only implemented on Android. + + diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 5db5d85aa6b..b174b178553 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -145,6 +145,16 @@ void DisplayServerAndroid::emit_system_theme_changed() { } } +void DisplayServerAndroid::set_hardware_keyboard_connection_change_callback(const Callable &p_callable) { + hardware_keyboard_connection_changed = p_callable; +} + +void DisplayServerAndroid::emit_hardware_keyboard_connection_changed(bool p_connected) { + if (hardware_keyboard_connection_changed.is_valid()) { + hardware_keyboard_connection_changed.call_deferred(p_connected); + } +} + void DisplayServerAndroid::clipboard_set(const String &p_text) { GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); ERR_FAIL_NULL(godot_java); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 894ef311fa1..fb1212141cc 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -89,6 +89,7 @@ class DisplayServerAndroid : public DisplayServer { Callable rect_changed_callback; Callable system_theme_changed; + Callable hardware_keyboard_connection_changed; Callable dialog_callback; Callable input_dialog_callback; @@ -114,11 +115,10 @@ public: virtual void tts_resume() override; virtual void tts_stop() override; - void emit_system_theme_changed(); - virtual bool is_dark_mode_supported() const override; virtual bool is_dark_mode() const override; virtual void set_system_theme_change_callback(const Callable &p_callable) override; + void emit_system_theme_changed(); virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; @@ -159,6 +159,8 @@ public: virtual void virtual_keyboard_hide() override; virtual int virtual_keyboard_get_height() const override; virtual bool has_hardware_keyboard() const override; + virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) override; + void emit_hardware_keyboard_connection_changed(bool p_connected); virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 9ab30e5f5b6..6a5c5eb260e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -77,7 +77,6 @@ import org.godotengine.godot.xr.XRMode import java.io.File import java.io.FileInputStream import java.io.InputStream -import java.lang.Exception import java.security.MessageDigest import java.util.* import java.util.concurrent.atomic.AtomicBoolean diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index d70915a9b37..5e599432d91 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -268,6 +268,11 @@ public class GodotLib { */ public static native void onNightModeChanged(); + /** + * Invoked on the hardware keyboard connected/disconnected. + */ + public static native void hardwareKeyboardConnected(boolean connected); + /** * Invoked on the file picker closed. */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index cacc1643e30..68f5ffd37cf 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -265,13 +265,6 @@ public class GodotEditText extends EditText { } public boolean hasHardwareKeyboard() { - Configuration config = getResources().getConfiguration(); - boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && - config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; - if (hasHardwareKeyboardConfig) { - return true; - } - return mRenderView.getInputHandler().hasHardwareKeyboard(); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 695d56c24f8..205aae3347a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -90,6 +90,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens private int cachedRotation = -1; private boolean overrideVolumeButtons = false; + private boolean hasHardwareKeyboardConfig = false; public GodotInputHandler(Context context, Godot godot) { this.godot = godot; @@ -105,6 +106,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.scaleGestureDetector.setStylusScaleEnabled(true); } + Configuration config = context.getResources().getConfiguration(); + hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && + config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; } /** @@ -143,6 +147,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens } boolean hasHardwareKeyboard() { + if (hasHardwareKeyboardConfig) { + return true; + } return !mHardwareKeyboardIds.isEmpty(); } @@ -797,5 +804,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens public void onConfigurationChanged(Configuration newConfig) { updateCachedRotation(); + + boolean newHardwareKeyboardConfig = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS && + newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + if (hasHardwareKeyboardConfig != newHardwareKeyboardConfig) { + hasHardwareKeyboardConfig = newHardwareKeyboardConfig; + GodotLib.hardwareKeyboardConnected(hasHardwareKeyboard()); + } } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 196dfd88f46..066cf86d1c2 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -548,6 +548,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN } } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected) { + DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); + if (ds) { + ds->emit_hardware_keyboard_connection_changed(p_connected); + } +} + JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) { DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); if (ds) { diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 47a5a62fb93..7cdc22d7189 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -68,6 +68,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorProjectMetad JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index a8ae645d894..1ff679b4b69 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -1033,6 +1033,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("virtual_keyboard_get_height"), &DisplayServer::virtual_keyboard_get_height); ClassDB::bind_method(D_METHOD("has_hardware_keyboard"), &DisplayServer::has_hardware_keyboard); + ClassDB::bind_method(D_METHOD("set_hardware_keyboard_connection_change_callback", "callable"), &DisplayServer::set_hardware_keyboard_connection_change_callback); ClassDB::bind_method(D_METHOD("cursor_set_shape", "shape"), &DisplayServer::cursor_set_shape); ClassDB::bind_method(D_METHOD("cursor_get_shape"), &DisplayServer::cursor_get_shape); diff --git a/servers/display_server.h b/servers/display_server.h index e26d6833e0c..0cb87fcd73e 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -271,6 +271,7 @@ public: virtual Color get_accent_color() const { return Color(0, 0, 0, 0); } virtual Color get_base_color() const { return Color(0, 0, 0, 0); } virtual void set_system_theme_change_callback(const Callable &p_callable) {} + virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) {} private: static bool window_early_clear_override_enabled;