I have a game that uses a callback to Java from C++ to force open the soft keyboard when the user touches the screen. The Java code is simply this:
this._inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
This has worked fine for a while but recently we've been receiving complaints from some Motorola Droid users that the soft keyboard fails to open for them. Since we've only recently started to get these complaints and it's a number of users I'm thinking it was some kind of update to those devices.
Is there a better way I can force the keyboard to open? All the links I find online talk about using textbox controls and such but my app is primarily C++ and doesn't use the standard controls at all.
I don't know if this is related to your problem, but I was running into some issues using only InputMethodManager.toggleSoftInput() when devices would sometimes get "out of sync" and hide when I wanted to show and vice versa.
I've had some success by taking advantage of the fact that while IMM.showSoftInput() won't show a keyboard, IMM.hideSoftInputFromWindow() will reliably close one, so when I want to show a keyboard I now call IMM.hideSoftInputFromWindow() followed by IMM.toggleSoftInput(), and use IMM.hideSoftInputFromWindow() by itself to hide one.
[A day later...]
Writing the above yesterday made me rethink how I was dealing with the soft keyboard (I mean, showSoftinput() does work, just not the way we expected it to) and so here is a better way to do it:
First, you need to set up your view so that Android knows it can have a soft keyboard - described in the docs for InputMethodManager. In my case I have a single view derived from GLSurfaceView and so I added:
setFocusable(true);
setFocusableInTouchMode(true);
to the constructor and then the following 2 overrides:
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
outAttrs.actionLabel = "";
outAttrs.hintText = "";
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.label = "";
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
outAttrs.inputType = InputType.TYPE_NULL;
return new BaseInputConnection(this, false);
}
#Override
public boolean onCheckIsTextEditor ()
{
return true;
}
Now I can show the keyboard with:
InputMethodManager mgr = (InputMethodManager)mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(mView, 0);
and the keypresses get reported via the view's onKeyUp() and onKeyDown() methods.
Hiding it is still done using hideSoftInputFromWindow()
Related
In a current fullscreen opengl based project I work on, I have some GL based graphical elements, notably a text entry field. For the use to enter text when this element has the focus, I display the soft keyboard (which appears fine).
On android version before 5.0, the Google Keyboard was working fine, sending key events like for hardware keyboards. On android Lollipop, some other keyboards like Swiftkey or the free Hacker's keyboard are still working, but the Google Keyboard isn't anymore.
When pressing a key on the Google Keyboard on Lollipop, no visual feedback appears on the keyboard itself and my application receives the touch events as if the keyboard was not shown (but it is). The 'hardware' back key works fine though.
The view used in the app is a SurfaceView (and it's not a TextView). I've overridden onCheckIsTextEditor and I return a specific InputConnection from onCreateInputConnection where I've set the inputType to be TYPE_NULL.
Note that onCreateInputConnection doesn't seem to be called.
This app is compiled with android level 15 compatibility.
Any idea what would prevent the keyboard to accept touch events?
What should I do to debug the touch events flow?
I finally found a workaround to my problem, even though I don't really understand exactly why it works. This solution is partially based on what Cocos2d-x does for input on Android.
I created an android EditText (actually a class than inherits EditText in which I overrode onKeyUp and onKeyDown, this is to track focus and the back key).
Instead of having the SurfaceView the sole element of the activity, I created a layout that has the fake edit text in the background (but not fullscreen), and the SurfaceView on top:
private FrameLayout setupView(View androidView)
{
ViewGroup.LayoutParams framelayout_params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout = new FrameLayout(this);
getFrameLayout().setLayoutParams(framelayout_params);
ViewGroup.LayoutParams edittext_layout_params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
edittext = new FakeEditText(this);
edittext.setLayoutParams(edittext_layout_params);
// make sure this edit text is not fullscreen
edittext.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
// ...add to FrameLayout
frameLayout.addView(edittext);
frameLayout.addView(androidView);
return frameLayout;
}
I also added a TextWatcher linked to the fake edit text, this is mainly to capture the text entered by the user and send it back to the GL based edit text I have on the SurfaceView (in my case, when the afterTextChanged method is called, I transform the received characters to internal keydown/keyup events that are routed to my GL control).
When the virtual keyboard needs to be shown (for example when the GL text field has focus), I set the fake edit text content from the GL text field content, and attach this TextWatcher to the fake edit text, and attach the virtual keyboard to the android fake edit text.
// Control is the base class of my GL controls
private void uiShowVirtualKeyboard(Control control)
{
if (fakeEdit.requestFocus()) {
// textWrapper is our TextWatcher
fakeEdit.removeTextChangedListener(textWrapper);
fakeEdit.setText("");
// get the text from the GL Text entry
final String text = control.getTextContent();
// and make sure it's in the android EditText at start
fakeEdit.append(text);
// listen to user changes
fakeEdit.addTextChangedListener(textWrapper);
// show the virtual keyboard
InputMethodManager imm = (InputMethodManager) fAndroidActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(fakeEdit, InputMethodManager.SHOW_FORCED);
}
}
I had exactly the same problem. Google keyboard did no show up correctly and passed touch input through it's buttons.
As it turned out Google keyboard was not happy with the default settings of EditorInfo class passed into onCreateInputConnection for a view. If you fill in at least imeOptions field and leave the rest to their default values it will work, even if you return null from the function.
In order to fix it i've added these lines to my SurfaceView subclass:
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE | InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN;
return super.onCreateInputConnection(outAttrs);
}
thanks for any help up front. I will admit ahead of time that my Java is still immature, so I feel like I'm in over my head. I've been searching almost all day for this answer and trying various approaches to no avail.
I am using a commercial game engine which exports a game to an Eclipse project with a GLSurfaceView class implementation. They have a hook to send single key codes into the game engine for key down and key up events, and I want to use those hooks to send keys from the Android keyboard (soft or hard).
Overriding the view.onKeyUp, onKeyDown, and onKeyMultiple interfaces allows me to get almost everything I'm looking to do, but when an IME like Swype is used, I still have one remaining problem.
When I swype the word, it shows up in the floating window (I believe swype is drawing this), but then it doesn't come across in the onKeyUp, onKeyDown, or onKeyMultiple callbacks. If I hit space or any other letter on the keyboard, I then get the onKeyMultiple callback which sends the string I swyped previously.
I thought I could just call finishComposingText() in my own InputConnection, so I overrode the View.onCreateInputConnection method in the GLSurfaceView class, and then called finishComposingText() in the DrawFrame() method for the game engine, and it worked somewhat. It actually immediately sent the text through the onKeyMultiple callback, but then if I hit a key, it sent the same text again, duplicating it in my "3D edit box". I also didn't like the idea of calling that every frame.
I'm just completely lost on what approach I should take to get the swype results to show up instantly after I swype without having to press a key. The game engine is not using a TextView or similar Android widget, so I tried the following as well with no luck:
public InputConnection onCreateInputConnection ( EditorInfo outAttrs )
{
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE ;
outAttrs.inputType = InputType.TYPE_NULL;
oInputC = new BaseInputConnection ( this, false ) ;
return oInputC;
}
oInputC is just a static variable I can use to call the finishComposingText() method.
Could anyone suggest an approach or reference? I have found only a couple of other questions like this, and they were both unanswered. It seems not many people are creating their own text edit boxes and need to implement the backend for them, but that's essentially what I need to do.
I am facing the same issue, but I think I have found solution.
I was checking android source code of TextView and EditableInputConnection to see how they are doing: it turns out that's its not so easy.
I think I managed to make it work correctly, with my custom editor. This is how my input connection looks like.
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
outAttrs.actionLabel = "";
outAttrs.hintText = "";
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.label = "";
outAttrs.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
final InputConnection in = new BaseInputConnection(this, false){
private HudInputElement getInputElement(){...}
#Override
public boolean setComposingText(CharSequence text,
int newCursorPosition) {
B2DEngine.getLogger().info("composing text: "+text+","+newCursorPosition);
HudInputElement input = getInputElement();
if(input!=null){
input.setComposingText(text.toString());
}
return super.setComposingText(text, newCursorPosition);
}
#Override
public boolean finishComposingText() {
HudInputElement input = getInputElement();
if(input!=null){
input.doneComposing();
}
return super.finishComposingText();
}
#Override
public boolean commitText(CharSequence text, int newCursorPosition) {
B2DEngine.getLogger().info("commit:"+text.toString()+","+this.getEditable().toString());
HudInputElement input = getInputElement();
if(input!=null){
input.doneComposing();
}
return super.commitText(text, newCursorPosition);
}
};
return in;
}
Since soft input keyboards supports all sorts of way to autocomplete text, currently entered word is treated differently. You have to ensure that when setComposingText is called, you have to replace the text that was previously received. Only clear this flag when user presses a button or finishComposingText or commitText are called.
P.S. Don't forget to test other keyboards on the market.
Is there any way to show software keyboard with USB keyboard connected (in my case RFID reader)?
I tried to force show it using InputManager (with these or similar parameters), but with no luck
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED,0);
Important notice - I know that there is a button in status/system bar to show it, but this button is not visible to user (Kiosk app).
You need to override the InputMethodService method onEvaluateInputViewShown() to evaluate to true even when there is a hard keyboard. See onEvaluateInputShown() and the Soft Input View section of InputMethodService. Try creating your own custom InputMethodService class to override this method.
EDIT: The source for onEvaluateInputShown() should help. The solution should be as simple as creating your own class that extends InputMethodService and overriding this one method, which is only a couple of lines long. Make sure to add your custom service to your manifest as well.
From Source:
"Override this to control when the soft input area should be shown to the user. The default implementation only shows the input view when there is no hard keyboard or the keyboard is hidden. If you change what this returns, you will need to call updateInputViewShown() yourself whenever the returned value may have changed to have it re-evalauted and applied."
public boolean onEvaluateInputViewShown() {
Configuration config = getResources().getConfiguration();
return config.keyboard == Configuration.KEYBOARD_NOKEYS
|| config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES;
}
Here are the possible configurations you can check for. Configuration.KEYBOARD_NOKEYS corresponds to no hardware keyboard. This method returns true (soft keyboard should be shown) if there is no hardware keyboard or if the hardware keyboard is hidden. Removing both of these checks and simply returning true should make the software keyboard visible even if a hardware keyboard is attached.
Try (not tested):
public boolean onEvaluateInputViewShown() {
return true;
}
Since this return value will not change, you won't need to call updateInputViewShown() yourself. If you modify this method differently, be sure to remember this detail.
The soft keyboard can have unpredictable behaviour on different platforms. First in your code, ensure you have an editable input control. Eg, if you have an EditText, you could use:
((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(myEditText, InputMethodManager.SHOW_FORCED);
However, you can just show and hide it whenever you want using:
//show keyboard:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
//hide keyboard :
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
You could also add any of these events inside OnCreate or some other method of the controls.
If however for some reason any of the above fails, your best option might be to use an alternative keyboard, e.g. Compass Keyboard,
OR
You could even build yours:
See an example of a keyboard implementing the inputmethodservice.KeyboardView
You might also want to take a look at the GingerBread Keyboard source.
If your app has the WRITE_SECURE_SETTINGS permission (available to system apps or Android Things apps) it can set the show_ime_with_hard_keyboard system setting which will enable soft keyboard even if a hard keyboard is plugged:
Settings.Secure.putInt(getContentResolver(), "show_ime_with_hard_keyboard", 1);
This worked in my app, interestingly, also an kiosk app.
This is a bit stripped, I did some checks beforehand, whether IMM is null and such.
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInputFromWindow(someInputView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0);
according to this https://stackoverflow.com/a/24287780/2233069, I made working solution for Kiosk mode.
boolean hardwareKeyboardPlugged=false;
....
mEditText.setOnFocusChangeListener(this);//in onCreate()
....
#Override
public void onResume() {
//protect from barcode scanner overriding keys
hardwareKeyboardPlugged=(getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
super.onResume();
}
....
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
if (hardwareKeyboardPlugged){
//protect from barcode scanner overriding keys
hardwareKeyboardPlugged=false;
((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)).showInputMethodPicker();
Toast.makeText(this, "USB device detected. Turn OFF hardware keyboard to enable soft keyboard!", Toast.LENGTH_LONG).show();
}
}
I'm writing a Android NDK application, (targeted at API 8), which is based on the hello-gl2 example and so uses a single full-screen OpenGL2.0 view.
I need to bring up the on-screen keyboard and so from the info available on this site i can now bring up the keyboard, send the keyup events to my native application for processing and close the keyboard. All is well until i switch to Swype or any other more-advanced-then-stock keyboard input manager and it doesn't work.
After reading the KeyEvent developer doc class overview what i'm seeing seems to be the expected behaviour:
You should never rely on receiving KeyEvents for any key on a soft
input method.
So i've been trying to work out a way to capture the output of a IME which i can then pass down to my native application. Unfortunately i'm not getting very far with this at all.
I've got this code in my extended GLSurfaceView class:
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
outAttrs.actionLabel = "";
outAttrs.hintText = "";
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.label = "";
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
outAttrs.inputType = InputType.TYPE_NULL;
return new BaseInputConnection(this, false);
}
#Override
public boolean onCheckIsTextEditor()
{
return true;
}
And i'm calling:
public boolean showSoftInput(View view, int flags)
passing in the view returned from getCurrentFocus, from native code to display the keyboard.
Is it possible to be able to extract the text from the on-screen software keyboard not using the keyup event? Is it possible to update what i've got to be able to do this or should i be looking at a completely different method?
Apologies for the questions, as you can tell, the non-native Java side of Android development is quite new to me.
As a summary my current implementation is based on this answer which allows the soft-keyboard text to be captured using key events however i'd like to capture the text not using key events.. (possibly a TextWatcher.. though my View isn't editable?).
Many thanks for any help or advice offered,
Andy
We are working on our first android app and it has been a very enjoyable experience so far. It is almost complete, but before release we are having some considerations ,mainly about android soft keyboard.
We have a couple of EditText fields that are used to enter numbers. We would like to capture the event when user presses enter, and do some calcuations and saving on this callback.
The problem is that we are not getting a fixed event as different phones have different keyboards. Some have 'Done' button and our HTC phones have 'Enter' buttons. We tried using the imeOptions as 'done' but that had no effect on the HTC phones.
We also know that the keyboard can be dismissed by hitting the back button. So my question is if there is a reliable way to know when the user has stopped entering or when the keyboard is hidden, just like textFieldShouldReturn callback in iphone sdk(which will always fire when keyboard goes down, independent of what key caused it to go down)..
In other words, how an android developer handles soft keyboard? I check for KeyEvent.KEYCODE_ENTER on editText onClick() event and do my tasks there.It is working on my HTC android, but not on my friends Nexus phone, which has a done button instead of enter. There onClick is not even called. How a developer handles this?
EDIT: After losing half of my hair, and with the help of some good friends here
I have tried all your suggestions but at the end by using onEditorActionListener along with onKeyListener method did the trick for me. In onEdit callback of onEditorActionListener I checked for KeyCode ACTION_DONE, which did get called on keyboards with done button. On keyboards which has enter onKey gets called. In onKey method I checked for KEYCODE_BACK also, so that hardware back press event also can be handled. I haven't yet found out a android device with done and enter on the keyboard (seriously), still I even handled that case with a flag. Thanks #Femi for suggesting onEditorActionListener, and thanks for all friends for your help. But the answer to my original question
Q: Is there an reliable and easier way to know android soft keyboard resigns (callback that works on every phone)
Ans : No, All methods suggested here and all methods suggested on other sites are not straightforward. And I think handling an event for keyboard return key is the most basic thing for any operating system. Google, are you there?
Since it seems that you are catching the KEYCODE_ENTER event, you might be able to use this: http://developer.android.com/reference/android/widget/TextView.html#setOnEditorActionListener%28android.widget.TextView.OnEditorActionListener%29. In theory this will let you detect whatever the input method end action is (whether its back, done, enter, or whatever) and respond to it.
Let me know if that works for you.
Wouldn't you also need to perform those calculations when the user is leaving the TextView on a hardware keyboard? I wouldn't focus on the keyboard, but on the TextView itself. If so, what you probably want is setTransformationMethod
You'd have to implement a custom TransformationMethod, specifically the method getTransformation, which transforms a source CharSequence into another one. You can then use the onFocusChanged to apply this only when the focus is lost for that TextView.
I found a solution on this SO page:
Intercept back button from soft keyboard
The answer from mhradek has 0 votes but it seems to be working.
The idea is to extend the base layout of your activity so that you can override the dispatchKeyEventPreIme method and do what you want regarding the KeyEvent passed. Note that you are responsible for managing the soft keyboard.
I am using it and I can definitely intercept key strokes (the back button for example) without the soft keyboard "eating" them. I have yet to play more with it in order to see what is possible and what is not.
I hope it helps.
Have you tried implementing custom EditText view, where you override dispatchKeyEventPreIme? Just like in answer posted by Arnaud (referencing Intercept back button from soft keyboard) but instead of using custom layout use custom EditText and override:
#Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
if(KeyEvent.KEYCODE_BACK == event.getKeyCode()) {
//this hides soft keyboard in super.dispatchKeyEventPreIme(event)
}
return super.dispatchKeyEventPreIme(event);
}
I suggested this solution in this question
I cant believe Google doesnt have a keyboard independant callback for this case
Wow, I cant believe that neither. I am having a similar problem at the moment.
In addition to the IME ACTION I check for focus changes on the EditFields. This is fine for most of the time, but won't work always.
I found a way to be notified when the keyboard is being hidden, but it's not a complete solution yet (and I'm not sure whether it's a good idea), but I don't have the time to continue right now, so I thought I can drop the start of the idea here...:
Write your own EditText(extend EditText) and override onCreateInputConnection. In your onCreateInputConnection return your own implementation of InputConnection (you can simply extend BasicInputConnection.
The InputConnections "finishComposingText()" method is always called when the keyboard is being hidden (also when the user presses the back-key).
This is the code, and maybe someone else has an idea, why the entered text is not shown in this editfield ;-)
public class MyEditText extends EditText{
public MyEditText(Context context) {
super(context);
}
public MyEditText(Context context, AttributeSet attrs) {
super(context);
}
public MyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
System.out.println("onCreateInputConnection, "+outAttrs.actionId);
return new MyInputConnection(this,true);
}
private class MyInputConnection extends BaseInputConnection{
public MyInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
#Override
public boolean finishComposingText() {
System.out.println("FINISH");
return super.finishComposingText();
}
}
}
JPM
I have not tried this but, reading the documentation, it seems possible.
// From an activity, you can call
if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
// your code here
}
This code is working fine for me with HTC and default Android keyboard:
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// handle enter key on keyboard
if (actionId == EditorInfo.IME_ACTION_SEND ||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN)) {
if (uid != null) {
// hide keyboard
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
// perform other stuff
return true;
}
}
return false;
}
});
Using the following in the editText´s XML:
android:imeOptions="actionSend"
Of course you could also use something else like send, just make sure to change it in both the XML and Java code.