Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a90b7f01 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Add methods to InputConnection: setComposingRegion() to select a region of...

Add methods to InputConnection: setComposingRegion() to select a region of text for correction, and getSelectedText()
to return the selected text.

setComposingRegion:

The TextView may choose to highlight the text in some way (underline for now) to indicate
that the text is selected for correction, if the IME wants to provider alternatives.

Choosing an alternative in the IME can then call IC.commitText() to replace the highlighted
(not selected) text with a different candidate.

This change also ensures that any existing spans/styles are not wiped out. So we can now
correct rich text as well.

getSelectedText:

This is a convenience to get the selected text instead of using extracted text that is
more heavy weight. Existing getTextBeforeCursor() and getTextAfterCursor() fail to
retrieve the selected text, only what's before and after the selection.

Change-Id: Ieb5ecd5ff947ea04958589f501e7bd5228e00fb5
parent 846eb30f
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -198034,6 +198034,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getSelectedText"
 return="java.lang.CharSequence"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getTextAfterCursor"
 return="java.lang.CharSequence"
 abstract="false"
@@ -198144,6 +198157,21 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
<method name="setComposingRegion"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="start" type="int">
</parameter>
<parameter name="end" type="int">
</parameter>
</method>
<method name="setComposingSpans"
 return="void"
 abstract="false"
@@ -199138,6 +199166,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getSelectedText"
 return="java.lang.CharSequence"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getTextAfterCursor"
 return="java.lang.CharSequence"
 abstract="true"
@@ -199235,6 +199276,21 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
<method name="setComposingRegion"
 return="boolean"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="start" type="int">
</parameter>
<parameter name="end" type="int">
</parameter>
</method>
<method name="setComposingText"
 return="boolean"
 abstract="true"
@@ -199427,6 +199483,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getSelectedText"
 return="java.lang.CharSequence"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="getTextAfterCursor"
 return="java.lang.CharSequence"
 abstract="false"
@@ -199524,6 +199593,21 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
<method name="setComposingRegion"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="start" type="int">
</parameter>
<parameter name="end" type="int">
</parameter>
</method>
<method name="setComposingText"
 return="boolean"
 abstract="false"
+96 −29
Original line number Diff line number Diff line
@@ -86,7 +86,12 @@ public class BaseInputConnection implements InputConnection {
    }

    public static void setComposingSpans(Spannable text) {
        final Object[] sps = text.getSpans(0, text.length(), Object.class);
        setComposingSpans(text, 0, text.length());
    }

    /** @hide */
    public static void setComposingSpans(Spannable text, int start, int end) {
        final Object[] sps = text.getSpans(start, end, Object.class);
        if (sps != null) {
            for (int i=sps.length-1; i>=0; i--) {
                final Object o = sps[i];
@@ -94,18 +99,19 @@ public class BaseInputConnection implements InputConnection {
                    text.removeSpan(o);
                    continue;
                }

                final int fl = text.getSpanFlags(o);
                if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) 
                        != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
                    text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
                            (fl&Spanned.SPAN_POINT_MARK_MASK)
                            (fl & ~Spanned.SPAN_POINT_MARK_MASK)
                                    | Spanned.SPAN_COMPOSING
                                    | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                }
            }
        }

        text.setSpan(COMPOSING, 0, text.length(),
        text.setSpan(COMPOSING, start, end,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
    }
    
@@ -311,6 +317,31 @@ public class BaseInputConnection implements InputConnection {
        return TextUtils.substring(content, a - length, a);
    }

    /**
     * The default implementation returns the text currently selected, or null if none is
     * selected.
     */
    public CharSequence getSelectedText(int flags) {
        final Editable content = getEditable();
        if (content == null) return null;

        int a = Selection.getSelectionStart(content);
        int b = Selection.getSelectionEnd(content);

        if (a > b) {
            int tmp = a;
            a = b;
            b = tmp;
        }

        if (a == b) return null;

        if ((flags&GET_TEXT_WITH_STYLES) != 0) {
            return content.subSequence(a, b);
        }
        return TextUtils.substring(content, a, b);
    }

    /**
     * The default implementation returns the given amount of text from the
     * current cursor position in the buffer.
@@ -385,6 +416,38 @@ public class BaseInputConnection implements InputConnection {
        return true;
    }

    public boolean setComposingRegion(int start, int end) {
        final Editable content = getEditable();
        if (content != null) {
            beginBatchEdit();
            removeComposingSpans(content);
            int a = start;
            int b = end;
            if (a > b) {
                int tmp = a;
                a = b;
                b = tmp;
            }
            if (a < 0) a = 0;
            if (b > content.length()) b = content.length();

            ensureDefaultComposingSpans();
            if (mDefaultComposingSpans != null) {
                for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
                    content.setSpan(mDefaultComposingSpans[i], a, b,
                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
                }
            }

            content.setSpan(COMPOSING, a, b,
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);

            endBatchEdit();
            sendCurrentText();
        }
        return true;
    }

    /**
     * The default implementation changes the selection position in the
     * current editable text.
@@ -480,6 +543,31 @@ public class BaseInputConnection implements InputConnection {
        }
    }

    private void ensureDefaultComposingSpans() {
        if (mDefaultComposingSpans == null) {
            Context context;
            if (mTargetView != null) {
                context = mTargetView.getContext();
            } else if (mIMM.mServedView != null) {
                context = mIMM.mServedView.getContext();
            } else {
                context = null;
            }
            if (context != null) {
                TypedArray ta = context.getTheme()
                        .obtainStyledAttributes(new int[] {
                                com.android.internal.R.attr.candidatesTextStyleSpans
                        });
                CharSequence style = ta.getText(0);
                ta.recycle();
                if (style != null && style instanceof Spanned) {
                    mDefaultComposingSpans = ((Spanned)style).getSpans(
                            0, style.length(), Object.class);
                }
            }
        }
    }

    private void replaceText(CharSequence text, int newCursorPosition,
            boolean composing) {
        final Editable content = getEditable();
@@ -520,32 +608,11 @@ public class BaseInputConnection implements InputConnection {
            if (!(text instanceof Spannable)) {
                sp = new SpannableStringBuilder(text);
                text = sp;
                if (mDefaultComposingSpans == null) {
                    Context context;
                    if (mTargetView != null) {
                        context = mTargetView.getContext();
                    } else if (mIMM.mServedView != null) {
                        context = mIMM.mServedView.getContext();
                    } else {
                        context = null;
                    }
                    if (context != null) {
                        TypedArray ta = context.getTheme()
                                .obtainStyledAttributes(new int[] {
                                        com.android.internal.R.attr.candidatesTextStyleSpans
                                });
                        CharSequence style = ta.getText(0);
                        ta.recycle();
                        if (style != null && style instanceof Spanned) {
                            mDefaultComposingSpans = ((Spanned)style).getSpans(
                                    0, style.length(), Object.class);
                        }
                    }
                }
                ensureDefaultComposingSpans();
                if (mDefaultComposingSpans != null) {
                    for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
                        sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
                    }
                }
            } else {
+27 −0
Original line number Diff line number Diff line
@@ -79,6 +79,21 @@ public interface InputConnection {
     */
    public CharSequence getTextAfterCursor(int n, int flags);

    /**
     * Gets the selected text, if any.
     *
     * <p>This method may fail if either the input connection has become
     * invalid (such as its process crashing) or the client is taking too
     * long to respond with the text (it is given a couple of seconds to return).
     * In either case, a null is returned.
     *
     * @param flags Supplies additional options controlling how the text is
     * returned.  May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
     * @return Returns the text that is currently selected, if any, or null if
     * no text is selected.
     */
    public CharSequence getSelectedText(int flags);

    /**
     * Retrieve the current capitalization mode in effect at the current
     * cursor position in the text.  See
@@ -161,6 +176,18 @@ public interface InputConnection {
     */
    public boolean setComposingText(CharSequence text, int newCursorPosition);

    /**
     * Mark a certain region of text as composing text. Any composing text set
     * previously will be removed automatically. The default style for composing
     * text is used.
     *
     * @param start the position in the text at which the composing region begins
     * @param end the position in the text at which the composing region ends
     * @return Returns true on success, false if the input connection is no longer
     * valid.
     */
    public boolean setComposingRegion(int start, int end);

    /**
     * Have the text editor finish whatever composing text is currently
     * active.  This simply leaves the text as-is, removing any special
+8 −0
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ public class InputConnectionWrapper implements InputConnection {
        return mTarget.getTextAfterCursor(n, flags);
    }

    public CharSequence getSelectedText(int flags) {
        return mTarget.getSelectedText(flags);
    }

    public int getCursorCapsMode(int reqModes) {
        return mTarget.getCursorCapsMode(reqModes);
    }
@@ -67,6 +71,10 @@ public class InputConnectionWrapper implements InputConnection {
        return mTarget.setComposingText(text, newCursorPosition);
    }

    public boolean setComposingRegion(int start, int end) {
        return mTarget.setComposingRegion(start, end);
    }

    public boolean finishComposingText() {
        return mTarget.finishComposingText();
    }
+37 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {

    private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
    private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
    private static final int DO_GET_SELECTED_TEXT = 25;
    private static final int DO_GET_CURSOR_CAPS_MODE = 30;
    private static final int DO_GET_EXTRACTED_TEXT = 40;
    private static final int DO_COMMIT_TEXT = 50;
@@ -42,6 +43,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
    private static final int DO_PERFORM_EDITOR_ACTION = 58;
    private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
    private static final int DO_SET_COMPOSING_TEXT = 60;
    private static final int DO_SET_COMPOSING_REGION = 63;
    private static final int DO_FINISH_COMPOSING_TEXT = 65;
    private static final int DO_SEND_KEY_EVENT = 70;
    private static final int DO_DELETE_SURROUNDING_TEXT = 80;
@@ -92,6 +94,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
        dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
    }

    public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
        dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
    }

    public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
        dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
    }
@@ -122,6 +128,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
        dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
    }
    
    public void setComposingRegion(int start, int end) {
        dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
    }

    public void setComposingText(CharSequence text, int newCursorPosition) {
        dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
    }
@@ -206,6 +216,22 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
                }
                return;
            }
            case DO_GET_SELECTED_TEXT: {
                SomeArgs args = (SomeArgs)msg.obj;
                try {
                    InputConnection ic = mInputConnection.get();
                    if (ic == null || !isActive()) {
                        Log.w(TAG, "getSelectedText on inactive InputConnection");
                        args.callback.setSelectedText(null, args.seq);
                        return;
                    }
                    args.callback.setSelectedText(ic.getSelectedText(
                            msg.arg1), args.seq);
                } catch (RemoteException e) {
                    Log.w(TAG, "Got RemoteException calling setSelectedText", e);
                }
                return;
            }
            case DO_GET_CURSOR_CAPS_MODE: {
                SomeArgs args = (SomeArgs)msg.obj;
                try {
@@ -292,6 +318,15 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
                ic.setComposingText((CharSequence)msg.obj, msg.arg1);
                return;
            }
            case DO_SET_COMPOSING_REGION: {
                InputConnection ic = mInputConnection.get();
                if (ic == null || !isActive()) {
                    Log.w(TAG, "setComposingRegion on inactive InputConnection");
                    return;
                }
                ic.setComposingRegion(msg.arg1, msg.arg2);
                return;
            }
            case DO_FINISH_COMPOSING_TEXT: {
                InputConnection ic = mInputConnection.get();
                // Note we do NOT check isActive() here, because this is safe
Loading