Skip to main content

Android Implementation LWUITInputConnection

Please note these java.net forums are being decommissioned and use the new and improved forums at https://community.oracle.com/community/java.
6 replies [Last post]
madhurvyas
Offline
Joined: 2010-04-05
Points: 0

Hi,
I faced lot of trouble with Android Virtual Keyboard with LWUIT, after some changes to base implementation things are working fine for me. Now even the swype keypad is working, actions like GO NEXT and DONE are working. I'm sharing the code other users can use it and update if required. For using GO action you need a default command in form, and for using Next there should be nextfocusable TextField set to your current TextField. I'm posting just the code I changed.
First thing update AndroidImplementation.java:

public static boolean USE_BASE_INPUT_CONNECTION = <strong>false</strong>;

Then update AndroidViev.java:onCreateInputConnection()
TextArea txt = (TextArea) txtCmp;
            if (txt.isSingleLineTextArea()) {
                Command cmd = Display.getInstance().getCurrent().getDefaultCommand();
                Component cmp =txt.getNextFocusDown();
                if(cmp instanceof TextField){
                    editorInfo.imeOptions |= EditorInfo.IME_ACTION_NEXT;
                }
                else if(cmd!=null){
                    editorInfo.imeOptions |= EditorInfo.IME_ACTION_GO;
                }
                else{
                    editorInfo.imeOptions |= EditorInfo.IME_ACTION_DONE;
                }
            } else {
                editorInfo.imeOptions |= EditorInfo.IME_ACTION_NONE;
            }

Then update LWUITInputConnection:
public boolean performEditorAction(int actionCode) {
        if (Display.isInitialized() &amp;&amp; Display.getInstance().getCurrent() != null) {
            Component txtCmp = Display.getInstance().getCurrent().getFocused();
            if (txtCmp != null &amp;&amp; txtCmp instanceof TextField) {
                if (actionCode == EditorInfo.IME_ACTION_GO) {
                    Display.getInstance().setShowVirtualKeyboard(false);
                    Command cmd = Display.getInstance().getCurrent().getDefaultCommand();
                    if(cmd!=null){
                        Display.getInstance().getCurrent().dispatchCommand(cmd, new ActionEvent(cmd,null,0,0));
                        return true;
                    }else
                        return false;
                }
                else if (actionCode == EditorInfo.IME_ACTION_DONE) {
                    Display.getInstance().setShowVirtualKeyboard(false);
                    Command cmd = Display.getInstance().getCurrent().getDefaultCommand();
                    if(cmd!=null){
                        Display.getInstance().getCurrent().dispatchCommand(cmd, new ActionEvent(cmd,null,0,0));
                        return true;
                    }else
                        return false;
                } else if (actionCode == EditorInfo.IME_ACTION_NEXT) {
//                    Display.getInstance().setShowVirtualKeyboard(false);
                    txtCmp.getNextFocusDown().requestFocus();
                    Display.getInstance().setShowVirtualKeyboard(true);
//                    Display.getInstance().getCurrent().getNextFocusDown().requestFocus();
                    return true;
                }
            }
        }

        return super.performEditorAction(actionCode);
    }

override one more method of inputconnection for swype support, I only tested on Samsung GalaxyS. Sype usses InputConnection to delete text when we press back space.
public boolean deleteSurroundingText(int leftLength, int rightLength) {
        // TODO Auto-generated method stub
        CharSequence rightText = getTextAfterCursor(0, 0);
        CharSequence leftText = getTextBeforeCursor(0, 0);
        if(rightLength&gt;0){
            if(rightText.length()&gt;0){
                rightText = rightText.subSequence(rightLength, rightText.length());
            }
        }else if(leftLength&gt;0){
            if(leftText.length()&gt;0){
                leftText = leftText.subSequence(0, leftText.length()-leftLength);
            }
        }
        CharSequence text = leftText.toString()+rightText;
        if (Display.isInitialized() &amp;&amp; Display.getInstance().getCurrent() != null) {
            Component txtCmp = Display.getInstance().getCurrent().getFocused();
            if (txtCmp != null &amp;&amp; txtCmp instanceof TextField) {
                TextField t = (TextField) txtCmp;
                t.setText(text.toString());
                t.setCursorPosition(leftText.length());
                updateExtractedText();
                return true;
            }
        }
        return true;
    }

If you want to use "Enter Key" to fire default commands add one more case for Enter Key to AndroidView:internalKeyCodeTranslate()
            case KeyEvent.KEYCODE_DPAD_CENTER:
            case KeyEvent.KEYCODE_ENTER:
                return AndroidImplementation.DROID_IMPL_KEY_FIRE;

I hope some of this code can make its way to inccubator so I dont need to merge with every update.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
thorsten_s
Offline
Joined: 2008-08-15
Points: 0

Looks good, I will probably add it. Except for the last part that translates enter keys to fire events. That would go a little far for a default configuration. If only my device would work properly and even call the performEditorAction method. Maybe I should try out this Swype thingy.

madhurvyas
Offline
Joined: 2010-04-05
Points: 0

Thanks Thorsten_s,
for making the android port and quick reply.
This last part makes the default command work on keyboards which dont come with DONE, NEXT or GO support. I saw this behaviour in HTC third party swype keyboard. calling the performEditorAction is better option though.
The swype fix can be improved, I did it in hurry, wasnt able to make how to update the text after deletion so copied code from commitText().

thorsten_s
Offline
Joined: 2008-08-15
Points: 0

The only problem I see with the return-> default command would be that this would effectively disable the possibility to receive newline chars. So triggering the commands when listening for the newline key on the LWUIT level would be a better approach I think. Having written that, for single line text fields it could make sense. I might have to take another look.

The whole LWUITInputconnection seems a very complex approach to me. I had another look at how the android EditText does it and found this solution, it would be great if you could try it out.
In the AndroidView.onCreateInputConnection(..) return a BaseInputConnection using a custom Editable like this (find LWUITEditable below):

                final Editable editable = new LWUITEditable(txt);
                return new BaseInputConnection(this, true) {

                    @Override
                    public Editable getEditable() {
                        return editable;
                    }
                };

package com.sun.lwuit.impl.android;

import android.text.Editable;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextWatcher;
import com.sun.lwuit.Display;
import com.sun.lwuit.TextArea;
import com.sun.lwuit.TextField;

/**
*
*/
public class LWUITEditable extends SpannableStringBuilder implements TextWatcher {

    private TextArea t;

    public LWUITEditable(TextArea t) {
        this.t = t;
        String text = t.getText();
        if (text == null) {
            text = &quot;&quot;;
        }
        this.append(text);
        Selection.setSelection(this, Math.max(0, t.getCursorPosition()));
        this.setSpan(this, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE /*| (PRIORITY &lt;&lt; Spanned.SPAN_PRIORITY_SHIFT)*/);
    }

    public void beforeTextChanged(CharSequence cs, int i, int i1, int i2) {
    }

    public void onTextChanged(CharSequence cs, int i, int i1, int i2) {
    }

    public void afterTextChanged(Editable edtbl) {
        if (edtbl == this) {
            final String text = this.toString();
            final int cursor = Selection.getSelectionEnd(this);
            this.removeSpan(this);
            this.setSpan(this, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE /*| (PRIORITY &lt;&lt; Spanned.SPAN_PRIORITY_SHIFT)*/);
            Display.getInstance().callSerially(new Runnable() {

                public void run() {
                    t.setText(text);
                    if (cursor &gt;= 0 &amp;&amp; t instanceof TextField) {
                        ((TextField) t).setCursorPosition(cursor);
                    }
                }
            });
        }
    }
}
madhurvyas
Offline
Joined: 2010-04-05
Points: 0

This looks good, everything seems to work, but IME actions are not handled so they dont work, if we override performEditorAction things work.
As for newline char, we are not using the newline char anyways, if we dont handle it, its just added as extra charcter to password fields, in normal/multiline textfields its discarded probably. As for Textarea its native, still I dont see a new line working on that also, its by default single line.
Swype is working, but delete doesnt work correctly if we move the cursor somewhere in middle of text.

thorsten_s
Offline
Joined: 2008-08-15
Points: 0

Thanks for testing. I will move the anonymous BaseInputConnection stuff into the LWUITInputConnection class and remove most of the current code from that class, except the performEditorAction() method. Then I need to find a way to track the cursor position. After that it should work just fine. Can't do it right now, though.

madhurvyas
Offline
Joined: 2010-04-05
Points: 0

thank,
Please take your time to add it to inccubator. till that time I will use my current code. its working fine on most devices ;)