Android: Hardware keyboard connection status callback

Adds a DisplayServer method to register hardware keyboard connection change callback.
This commit is contained in:
Anish Mishra 2025-04-04 16:16:24 +05:30
parent 8bd9cdeea6
commit b462db5adb
11 changed files with 51 additions and 10 deletions

View file

@ -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].
</description>
</method>
<method name="set_hardware_keyboard_connection_change_callback">
<return type="void" />
<param index="0" name="callable" type="Callable" />
<description>
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.
</description>
</method>
<method name="set_icon">
<return type="void" />
<param index="0" name="image" type="Image" />

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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.
*/

View file

@ -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();
}

View file

@ -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());
}
}
}

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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;