From 51429bd8d6d2fed9636e1d252a5eb86567b846ec Mon Sep 17 00:00:00 2001 From: sanikoyes Date: Thu, 13 Mar 2014 16:58:03 +0800 Subject: [PATCH 1/4] fix android can't input unicode characters fix hide soft keyboard by press 'back' button, then click current focus text edit/line edit control, soft keyboard won't show again add features: press enter key with line edit control will hide soft keyboard --- .../java/src/com/android/godot/Godot.java | 19 ++- .../java/src/com/android/godot/GodotIO.java | 22 ++- .../java/src/com/android/godot/GodotView.java | 2 +- .../android/godot/input/GodotEditText.java | 133 +++++++++++++++ .../godot/input/GodotTextInputWrapper.java | 154 ++++++++++++++++++ scene/gui/line_edit.cpp | 5 + scene/gui/text_edit.cpp | 2 + 7 files changed, 326 insertions(+), 11 deletions(-) create mode 100644 platform/android/java/src/com/android/godot/input/GodotEditText.java create mode 100644 platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java index 5260f6149c9..d570fb0ebad 100644 --- a/platform/android/java/src/com/android/godot/Godot.java +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -53,6 +53,8 @@ import java.lang.reflect.Method; import java.util.List; import java.util.ArrayList; import android.provider.Settings.Secure; +import android.widget.FrameLayout; +import com.android.godot.input.*; public class Godot extends Activity implements SensorEventListener @@ -117,7 +119,7 @@ public class Godot extends Activity implements SensorEventListener private SensorManager mSensorManager; private Sensor mAccelerometer; - public RelativeLayout layout; + public FrameLayout layout; static public GodotIO io; @@ -143,13 +145,22 @@ public class Godot extends Activity implements SensorEventListener // mView = new GodotView(getApplication(),io,use_gl2); // setContentView(mView); - layout = new RelativeLayout(this); + layout = new FrameLayout(this); layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); setContentView(layout); + + // GodotEditText layout + GodotEditText edittext = new GodotEditText(this); + edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT)); + // ...add to FrameLayout + layout.addView(edittext); + mView = new GodotView(getApplication(),io,use_gl2, this); layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); mView.setKeepScreenOn(true); - + + edittext.setView(mView); + io.setEdit(edittext); } @Override protected void onCreate(Bundle icicle) { @@ -172,7 +183,7 @@ public class Godot extends Activity implements SensorEventListener mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); result_callback = null; - + // instanceSingleton( new GodotFacebook(this) ); diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java index 1f3967cb8f0..18edfd398d0 100644 --- a/platform/android/java/src/com/android/godot/GodotIO.java +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -47,6 +47,7 @@ import android.media.*; import android.hardware.*; import android.content.*; import android.content.pm.ActivityInfo; +import com.android.godot.input.*; //android.os.Build // Wrapper for native library @@ -55,7 +56,8 @@ public class GodotIO { AssetManager am; - Activity activity; + Godot activity; + GodotEditText edit; final int SCREEN_LANDSCAPE=0; final int SCREEN_PORTRAIT=1; @@ -320,7 +322,7 @@ public class GodotIO { - GodotIO(Activity p_activity) { + GodotIO(Godot p_activity) { am=p_activity.getAssets(); activity=p_activity; @@ -462,15 +464,19 @@ public class GodotIO { } public void showKeyboard(String p_existing_text) { + if(edit != null) + edit.showKeyboard(p_existing_text); - InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + //inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); }; public void hideKeyboard() { + if(edit != null) + edit.hideKeyboard(); - InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + //inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); }; public void setScreenOrientation(int p_orientation) { @@ -501,6 +507,10 @@ public class GodotIO { } }; + + public void setEdit(GodotEditText _edit) { + edit = _edit; + } protected static final String PREFS_FILE = "device_id.xml"; protected static final String PREFS_DEVICE_ID = "device_id"; diff --git a/platform/android/java/src/com/android/godot/GodotView.java b/platform/android/java/src/com/android/godot/GodotView.java index 093757bfb0f..b5ab81cb24a 100644 --- a/platform/android/java/src/com/android/godot/GodotView.java +++ b/platform/android/java/src/com/android/godot/GodotView.java @@ -61,7 +61,7 @@ import javax.microedition.khronos.opengles.GL10; * that matches it exactly (with regards to red/green/blue/alpha channels * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */ -class GodotView extends GLSurfaceView { +public class GodotView extends GLSurfaceView { private static String TAG = "GodotView"; private static final boolean DEBUG = false; private static Context ctx; diff --git a/platform/android/java/src/com/android/godot/input/GodotEditText.java b/platform/android/java/src/com/android/godot/input/GodotEditText.java new file mode 100644 index 00000000000..5898e95423c --- /dev/null +++ b/platform/android/java/src/com/android/godot/input/GodotEditText.java @@ -0,0 +1,133 @@ +package com.android.godot.input; +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.widget.EditText; +import com.android.godot.*; +import android.os.Handler; +import android.os.Message; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.EditorInfo; + +public class GodotEditText extends EditText { + // =========================================================== + // Constants + // =========================================================== + private final static int HANDLER_OPEN_IME_KEYBOARD = 2; + private final static int HANDLER_CLOSE_IME_KEYBOARD = 3; + + // =========================================================== + // Fields + // =========================================================== + private GodotView mView; + private GodotTextInputWrapper mInputWrapper; + private static Handler sHandler; + private String mOriginText; + + // =========================================================== + // Constructors + // =========================================================== + public GodotEditText(final Context context) { + super(context); + this.initView(); + } + + public GodotEditText(final Context context, final AttributeSet attrs) { + super(context, attrs); + this.initView(); + } + + public GodotEditText(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + this.initView(); + } + + protected void initView() { + this.setPadding(0, 0, 0, 0); + this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + + sHandler = new Handler() { + @Override + public void handleMessage(final Message msg) { + switch (msg.what) { + case HANDLER_OPEN_IME_KEYBOARD: + { + GodotEditText edit = (GodotEditText) msg.obj; + String text = edit.mOriginText; + if (edit.requestFocus()) + { + edit.removeTextChangedListener(edit.mInputWrapper); + edit.setText(""); + edit.append(text); + edit.mInputWrapper.setOriginText(text); + edit.addTextChangedListener(edit.mInputWrapper); + final InputMethodManager imm = (InputMethodManager) mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(edit, 0); + } + } + break; + + case HANDLER_CLOSE_IME_KEYBOARD: + { + GodotEditText edit = (GodotEditText) msg.obj; + + edit.removeTextChangedListener(mInputWrapper); + final InputMethodManager imm = (InputMethodManager) mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edit.getWindowToken(), 0); + edit.mView.requestFocus(); + } + break; + } + } + }; + } + + // =========================================================== + // Getter & Setter + // =========================================================== + public void setView(final GodotView view) { + this.mView = view; + if(mInputWrapper == null) + mInputWrapper = new GodotTextInputWrapper(mView, this); + this.setOnEditorActionListener(mInputWrapper); + view.requestFocus(); + } + + // =========================================================== + // Methods for/from SuperClass/Interfaces + // =========================================================== + @Override + public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { + super.onKeyDown(keyCode, keyEvent); + + /* Let GlSurfaceView get focus if back key is input. */ + if (keyCode == KeyEvent.KEYCODE_BACK) { + this.mView.requestFocus(); + } + + return true; + } + + // =========================================================== + // Methods + // =========================================================== + public void showKeyboard(String p_existing_text) { + this.mOriginText = p_existing_text; + + final Message msg = new Message(); + msg.what = HANDLER_OPEN_IME_KEYBOARD; + msg.obj = this; + sHandler.sendMessage(msg); + } + + public void hideKeyboard() { + final Message msg = new Message(); + msg.what = HANDLER_CLOSE_IME_KEYBOARD; + msg.obj = this; + sHandler.sendMessage(msg); + } + + // =========================================================== + // Inner and Anonymous Classes + // =========================================================== +} diff --git a/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java b/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java new file mode 100644 index 00000000000..b003dc2376b --- /dev/null +++ b/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java @@ -0,0 +1,154 @@ +package com.android.godot.input; +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import com.android.godot.*; + +public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListener { + // =========================================================== + // Constants + // =========================================================== + private static final String TAG = GodotTextInputWrapper.class.getSimpleName(); + + // =========================================================== + // Fields + // =========================================================== + private final GodotView mView; + private final GodotEditText mEdit; + private String mText; + private String mOriginText; + + // =========================================================== + // Constructors + // =========================================================== + + public GodotTextInputWrapper(final GodotView view, final GodotEditText edit) { + this.mView = view; + this.mEdit = edit; + } + + // =========================================================== + // Getter & Setter + // =========================================================== + + private boolean isFullScreenEdit() { + final TextView textField = this.mEdit; + final InputMethodManager imm = (InputMethodManager) textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + return imm.isFullscreenMode(); + } + + public void setOriginText(final String originText) { + this.mOriginText = originText; + } + + // =========================================================== + // Methods for/from SuperClass/Interfaces + // =========================================================== + + @Override + public void afterTextChanged(final Editable s) { + if (this.isFullScreenEdit()) { + return; + } + + //if (BuildConfig.DEBUG) { + //Log.d(TAG, "afterTextChanged: " + s); + //} + int nModified = s.length() - this.mText.length(); + if (nModified > 0) { + final String insertText = s.subSequence(this.mText.length(), s.length()).toString(); + for(int i = 0; i < insertText.length(); i++) { + int ch = insertText.codePointAt(i); + GodotLib.key(0, ch, false); + GodotLib.key(0, ch, true); + } + /* + if (BuildConfig.DEBUG) { + Log.d(TAG, "insertText(" + insertText + ")"); + } + */ + } else { + for (; nModified < 0; ++nModified) { + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); + /* + if (BuildConfig.DEBUG) { + Log.d(TAG, "deleteBackward"); + } + */ + } + } + this.mText = s.toString(); + } + + @Override + public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) { + /* + if (BuildConfig.DEBUG) { + Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); + } + */ + this.mText = pCharSequence.toString(); + } + + @Override + public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) { + + } + + @Override + public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) { + if (this.mEdit == pTextView && this.isFullScreenEdit()) { + // user press the action button, delete all old text and insert new text + for (int i = this.mOriginText.length(); i > 0; i--) { + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); + /* + if (BuildConfig.DEBUG) { + Log.d(TAG, "deleteBackward"); + } + */ + } + String text = pTextView.getText().toString(); + + /* If user input nothing, translate "\n" to engine. */ + if (text.compareTo("") == 0) { + text = "\n"; + } + + if ('\n' != text.charAt(text.length() - 1)) { + text += '\n'; + } + + for(int i = 0; i < text.length(); i++) { + int ch = text.codePointAt(i); + GodotLib.key(0, ch, false); + GodotLib.key(0, ch, true); + } + /* + if (BuildConfig.DEBUG) { + Log.d(TAG, "insertText(" + insertText + ")"); + } + */ + } + + if (pActionID == EditorInfo.IME_ACTION_DONE) { + this.mView.requestFocus(); + } + return false; + } + + // =========================================================== + // Methods + // =========================================================== + + // =========================================================== + // Inner and Anonymous Classes + // =========================================================== +} diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 22316acaba0..6c9db7484b9 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -79,6 +79,9 @@ void LineEdit::_input_event(InputEvent p_event) { } selection.creating=false; selection.doubleclick=false; + + // notify to show soft keyboard + notification(NOTIFICATION_FOCUS_ENTER); } update(); @@ -208,6 +211,8 @@ void LineEdit::_input_event(InputEvent p_event) { case KEY_RETURN: { emit_signal( "text_entered",text ); + // notify to hide soft keyboard + notification(NOTIFICATION_FOCUS_EXIT); return; } break; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 18c8c0e9f74..233e3bee66c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -857,6 +857,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { } else { selection.selecting_mode=Selection::MODE_NONE; + // notify to show soft keyboard + notification(NOTIFICATION_FOCUS_ENTER); } } break; From 14bbdcb139b35e6d206df1ab3176d34245b72329 Mon Sep 17 00:00:00 2001 From: sanikoyes Date: Thu, 13 Mar 2014 18:31:30 +0800 Subject: [PATCH 2/4] fix enter key --- .../com/android/godot/input/GodotTextInputWrapper.java | 8 ++++---- platform/android/java_glue.cpp | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java b/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java index b003dc2376b..537fa6aa767 100644 --- a/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/src/com/android/godot/input/GodotTextInputWrapper.java @@ -65,8 +65,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene final String insertText = s.subSequence(this.mText.length(), s.length()).toString(); for(int i = 0; i < insertText.length(); i++) { int ch = insertText.codePointAt(i); - GodotLib.key(0, ch, false); GodotLib.key(0, ch, true); + GodotLib.key(0, ch, false); } /* if (BuildConfig.DEBUG) { @@ -75,8 +75,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene */ } else { for (; nModified < 0; ++nModified) { - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); /* if (BuildConfig.DEBUG) { Log.d(TAG, "deleteBackward"); @@ -107,8 +107,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene if (this.mEdit == pTextView && this.isFullScreenEdit()) { // user press the action button, delete all old text and insert new text for (int i = this.mOriginText.length(); i > 0; i--) { - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); /* if (BuildConfig.DEBUG) { Log.d(TAG, "deleteBackward"); @@ -128,8 +128,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for(int i = 0; i < text.length(); i++) { int ch = text.codePointAt(i); - GodotLib.key(0, ch, false); GodotLib.key(0, ch, true); + GodotLib.key(0, ch, false); } /* if (BuildConfig.DEBUG) { diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 0f059921120..127aeb7756f 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -1199,14 +1199,16 @@ JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_key(JNIEnv * env, jobject ievent.key.mod.control=false; ievent.key.echo=false; - if (val == 61448) { + if (val == '\n') + { + ievent.key.scancode = KEY_ENTER; + }else if (val == 61448) { ievent.key.scancode = KEY_BACKSPACE; ievent.key.unicode = KEY_BACKSPACE; - }; - if (val == 61453) { + } else if (val == 61453) { ievent.key.scancode = KEY_ENTER; ievent.key.unicode = KEY_ENTER; - }; + } input_mutex->lock(); key_events.push_back(ievent); From f103b67326376b76100ddb6373c5962378d44119 Mon Sep 17 00:00:00 2001 From: sikakraa Date: Sun, 6 Apr 2014 02:13:36 +0300 Subject: [PATCH 3/4] Fixed Android crash by adding safety to the hideKeyboard() -function. --- platform/android/java/src/com/android/godot/GodotIO.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java index 4b6a44335c9..605e7ae369a 100644 --- a/platform/android/java/src/com/android/godot/GodotIO.java +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -472,8 +472,13 @@ public class GodotIO { public void hideKeyboard() { - InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + View v = activity.getCurrentFocus(); + if (v != null) { + inputMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } else { + inputMgr.hideSoftInputFromWindow(new View(activity).getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } }; public void setScreenOrientation(int p_orientation) { From bae5ad7c8bc14c44a3222017ca7b561d7aa514be Mon Sep 17 00:00:00 2001 From: sanikoyes Date: Sun, 6 Apr 2014 21:53:38 +0800 Subject: [PATCH 4/4] Move onKeyDown/onKeyUp from Godot to GodotView Press 'back' button should not terminate program, normal handle 'back' event in game logic --- .../java/src/com/android/godot/Godot.java | 10 ---------- .../java/src/com/android/godot/GodotView.java | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java index 5a1bac5e86b..0fa07e2490a 100644 --- a/platform/android/java/src/com/android/godot/Godot.java +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -346,16 +346,6 @@ public class Godot extends Activity implements SensorEventListener } - @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - GodotLib.key(keyCode, event.getUnicodeChar(0), false); - return super.onKeyUp(keyCode, event); - }; - - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - GodotLib.key(keyCode, event.getUnicodeChar(0), true); - return super.onKeyDown(keyCode, event); - } - public PaymentsManager getPaymentsManager() { return mPaymentsManager; } diff --git a/platform/android/java/src/com/android/godot/GodotView.java b/platform/android/java/src/com/android/godot/GodotView.java index b5ab81cb24a..1993be8d2c5 100644 --- a/platform/android/java/src/com/android/godot/GodotView.java +++ b/platform/android/java/src/com/android/godot/GodotView.java @@ -98,8 +98,24 @@ public class GodotView extends GLSurfaceView { return activity.gotTouchEvent(event); }; + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + GodotLib.key(keyCode, event.getUnicodeChar(0), false); + return super.onKeyUp(keyCode, event); + }; + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + GodotLib.key(keyCode, event.getUnicodeChar(0), true); + if (keyCode == KeyEvent.KEYCODE_BACK) { + // press 'back' button should not terminate program + // normal handle 'back' event in game logic + return true; + } + return super.onKeyDown(keyCode, event); + } + private void init(boolean translucent, int depth, int stencil) { + this.setFocusableInTouchMode(true); /* By default, GLSurfaceView() creates a RGB_565 opaque surface. * If we want a translucent one, we should change the surface's * format here, using PixelFormat.TRANSLUCENT for GL Surfaces