You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
[Android] Implement native input dialog support
This commit is contained in:
@@ -105,7 +105,7 @@
|
|||||||
<param index="3" name="callback" type="Callable" />
|
<param index="3" name="callback" type="Callable" />
|
||||||
<description>
|
<description>
|
||||||
Shows a text input dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [String] parameter which contains the text field's contents.
|
Shows a text input dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [String] parameter which contains the text field's contents.
|
||||||
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG_INPUT] feature. Supported platforms include macOS and Windows.
|
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG_INPUT] feature. Supported platforms include macOS, Windows, and Android.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="dialog_show">
|
<method name="dialog_show">
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
|
|||||||
case FEATURE_MOUSE:
|
case FEATURE_MOUSE:
|
||||||
//case FEATURE_MOUSE_WARP:
|
//case FEATURE_MOUSE_WARP:
|
||||||
//case FEATURE_NATIVE_DIALOG:
|
//case FEATURE_NATIVE_DIALOG:
|
||||||
//case FEATURE_NATIVE_DIALOG_INPUT:
|
case FEATURE_NATIVE_DIALOG_INPUT:
|
||||||
//case FEATURE_NATIVE_DIALOG_FILE:
|
//case FEATURE_NATIVE_DIALOG_FILE:
|
||||||
//case FEATURE_NATIVE_ICON:
|
//case FEATURE_NATIVE_ICON:
|
||||||
//case FEATURE_WINDOW_TRANSPARENCY:
|
//case FEATURE_WINDOW_TRANSPARENCY:
|
||||||
@@ -176,6 +176,19 @@ bool DisplayServerAndroid::clipboard_has() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error DisplayServerAndroid::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
|
||||||
|
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||||
|
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||||
|
input_dialog_callback = p_callback;
|
||||||
|
return godot_java->show_input_dialog(p_title, p_description, p_partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayServerAndroid::emit_input_dialog_callback(String p_text) {
|
||||||
|
if (input_dialog_callback.is_valid()) {
|
||||||
|
input_dialog_callback.call_deferred(p_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TypedArray<Rect2> DisplayServerAndroid::get_display_cutouts() const {
|
TypedArray<Rect2> DisplayServerAndroid::get_display_cutouts() const {
|
||||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||||
ERR_FAIL_NULL_V(godot_io_java, Array());
|
ERR_FAIL_NULL_V(godot_io_java, Array());
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ class DisplayServerAndroid : public DisplayServer {
|
|||||||
|
|
||||||
Callable system_theme_changed;
|
Callable system_theme_changed;
|
||||||
|
|
||||||
|
Callable input_dialog_callback;
|
||||||
|
|
||||||
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
|
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
|
||||||
|
|
||||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||||
@@ -116,6 +118,9 @@ public:
|
|||||||
virtual String clipboard_get() const override;
|
virtual String clipboard_get() const override;
|
||||||
virtual bool clipboard_has() const override;
|
virtual bool clipboard_has() const override;
|
||||||
|
|
||||||
|
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||||
|
void emit_input_dialog_callback(String p_text);
|
||||||
|
|
||||||
virtual TypedArray<Rect2> get_display_cutouts() const override;
|
virtual TypedArray<Rect2> get_display_cutouts() const override;
|
||||||
virtual Rect2i get_display_safe_area() const override;
|
virtual Rect2i get_display_safe_area() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="text_edit_height">48dp</dimen>
|
<dimen name="text_edit_height">48dp</dimen>
|
||||||
|
<dimen name="input_dialog_padding_horizontal">10dp</dimen>
|
||||||
|
<dimen name="input_dialog_padding_vertical">5dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -55,4 +55,7 @@
|
|||||||
<string name="kilobytes_per_second">%1$s KB/s</string>
|
<string name="kilobytes_per_second">%1$s KB/s</string>
|
||||||
<string name="time_remaining">Time remaining: %1$s</string>
|
<string name="time_remaining">Time remaining: %1$s</string>
|
||||||
<string name="time_remaining_notification">%1$s left</string>
|
<string name="time_remaining_notification">%1$s left</string>
|
||||||
|
|
||||||
|
<!-- Labels for the dialog action buttons -->
|
||||||
|
<string name="dialog_ok">OK</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import android.os.*
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -81,6 +82,7 @@ import java.util.*
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core component used to interface with the native layer of the engine.
|
* Core component used to interface with the native layer of the engine.
|
||||||
*
|
*
|
||||||
@@ -772,7 +774,7 @@ class Godot(private val context: Context) {
|
|||||||
val builder = AlertDialog.Builder(activity)
|
val builder = AlertDialog.Builder(activity)
|
||||||
builder.setMessage(message).setTitle(title)
|
builder.setMessage(message).setTitle(title)
|
||||||
builder.setPositiveButton(
|
builder.setPositiveButton(
|
||||||
"OK"
|
R.string.dialog_ok
|
||||||
) { dialog: DialogInterface, id: Int ->
|
) { dialog: DialogInterface, id: Int ->
|
||||||
okCallback?.run()
|
okCallback?.run()
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
@@ -876,6 +878,31 @@ class Godot(private val context: Context) {
|
|||||||
mClipboard.setPrimaryClip(clip)
|
mClipboard.setPrimaryClip(clip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup a dialog to input text.
|
||||||
|
*/
|
||||||
|
@Keep
|
||||||
|
private fun showInputDialog(title: String, message: String, existingText: String) {
|
||||||
|
val activity: Activity = getActivity() ?: return
|
||||||
|
val inputField = EditText(activity)
|
||||||
|
val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_horizontal)
|
||||||
|
val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_vertical)
|
||||||
|
inputField.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical)
|
||||||
|
inputField.setText(existingText)
|
||||||
|
runOnUiThread {
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
builder.setMessage(message).setTitle(title).setView(inputField)
|
||||||
|
builder.setPositiveButton(R.string.dialog_ok) {
|
||||||
|
dialog: DialogInterface, id: Int ->
|
||||||
|
GodotLib.inputDialogCallback(inputField.text.toString())
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys the Godot Engine and kill the process it's running in.
|
* Destroys the Godot Engine and kill the process it's running in.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -224,6 +224,11 @@ public class GodotLib {
|
|||||||
*/
|
*/
|
||||||
public static native void onNightModeChanged();
|
public static native void onNightModeChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on the input dialog submitted.
|
||||||
|
*/
|
||||||
|
public static native void inputDialogCallback(String p_text);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked on the GL thread to configure the height of the virtual keyboard.
|
* Invoked on the GL thread to configure the height of the virtual keyboard.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -540,6 +540,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) {
|
||||||
|
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||||
|
if (ds) {
|
||||||
|
String text = jstring_to_string(p_text, env);
|
||||||
|
ds->emit_input_dialog_callback(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
|
||||||
String permission = jstring_to_string(p_permission, env);
|
String permission = jstring_to_string(p_permission, env);
|
||||||
if (permission == "android.permission.RECORD_AUDIO" && p_result) {
|
if (permission == "android.permission.RECORD_AUDIO" && p_result) {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
|
|||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
|
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_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_onNightModeChanged(JNIEnv *env, jclass clazz);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
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);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
|
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
|
|||||||
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
|
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
|
||||||
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
|
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
|
||||||
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
|
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
|
||||||
|
_show_input_dialog = p_env->GetMethodID(godot_class, "showInputDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||||
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
|
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
|
||||||
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
|
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
|
||||||
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
|
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
|
||||||
@@ -268,6 +269,23 @@ bool GodotJavaWrapper::has_clipboard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error GodotJavaWrapper::show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text) {
|
||||||
|
if (_show_input_dialog) {
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||||
|
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
|
||||||
|
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
|
||||||
|
jstring jStrExistingText = env->NewStringUTF(p_existing_text.utf8().get_data());
|
||||||
|
env->CallVoidMethod(godot_instance, _show_input_dialog, jStrTitle, jStrMessage, jStrExistingText);
|
||||||
|
env->DeleteLocalRef(jStrTitle);
|
||||||
|
env->DeleteLocalRef(jStrMessage);
|
||||||
|
env->DeleteLocalRef(jStrExistingText);
|
||||||
|
return OK;
|
||||||
|
} else {
|
||||||
|
return ERR_UNCONFIGURED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GodotJavaWrapper::request_permission(const String &p_name) {
|
bool GodotJavaWrapper::request_permission(const String &p_name) {
|
||||||
if (_request_permission) {
|
if (_request_permission) {
|
||||||
JNIEnv *env = get_jni_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ private:
|
|||||||
jmethodID _get_clipboard = nullptr;
|
jmethodID _get_clipboard = nullptr;
|
||||||
jmethodID _set_clipboard = nullptr;
|
jmethodID _set_clipboard = nullptr;
|
||||||
jmethodID _has_clipboard = nullptr;
|
jmethodID _has_clipboard = nullptr;
|
||||||
|
jmethodID _show_input_dialog = nullptr;
|
||||||
jmethodID _request_permission = nullptr;
|
jmethodID _request_permission = nullptr;
|
||||||
jmethodID _request_permissions = nullptr;
|
jmethodID _request_permissions = nullptr;
|
||||||
jmethodID _get_granted_permissions = nullptr;
|
jmethodID _get_granted_permissions = nullptr;
|
||||||
@@ -103,6 +104,7 @@ public:
|
|||||||
void set_clipboard(const String &p_text);
|
void set_clipboard(const String &p_text);
|
||||||
bool has_has_clipboard();
|
bool has_has_clipboard();
|
||||||
bool has_clipboard();
|
bool has_clipboard();
|
||||||
|
Error show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text);
|
||||||
bool request_permission(const String &p_name);
|
bool request_permission(const String &p_name);
|
||||||
bool request_permissions();
|
bool request_permissions();
|
||||||
Vector<String> get_granted_permissions() const;
|
Vector<String> get_granted_permissions() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user