diff options
author | Amith Yamasani <yamasani@google.com> | 2010-08-27 12:14:46 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-08-27 12:14:46 -0700 |
commit | f956fd6d1325fcb2e5ce9b8dac95a9a40824ead8 (patch) | |
tree | f2cf912dc82e332dc448f9eb741fe608adf7d2a1 | |
parent | 93637d3ee7b2346426b315627c8fcaf9b4782f93 (diff) | |
parent | 4b4114d155b4f3d5cc4179934cb6403b1776261d (diff) | |
download | frameworks_base-f956fd6d1325fcb2e5ce9b8dac95a9a40824ead8.zip frameworks_base-f956fd6d1325fcb2e5ce9b8dac95a9a40824ead8.tar.gz frameworks_base-f956fd6d1325fcb2e5ce9b8dac95a9a40824ead8.tar.bz2 |
am 4b4114d1: am a90b7f01: Add methods to InputConnection: setComposingRegion() to select a region of text for correction, and getSelectedText() to return the selected text.
Merge commit '4b4114d155b4f3d5cc4179934cb6403b1776261d'
* commit '4b4114d155b4f3d5cc4179934cb6403b1776261d':
Add methods to InputConnection: setComposingRegion() to select a region of text for correction, and getSelectedText()
8 files changed, 299 insertions, 34 deletions
diff --git a/api/current.xml b/api/current.xml index 41d87c4..158e251 100644 --- a/api/current.xml +++ b/api/current.xml @@ -208193,6 +208193,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" @@ -208303,6 +208316,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" @@ -209297,6 +209325,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" @@ -209394,6 +209435,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" @@ -209586,6 +209642,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" @@ -209683,6 +209752,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" diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 6ac1633..3801948 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -84,9 +84,14 @@ 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); } @@ -312,6 +318,31 @@ public class BaseInputConnection implements InputConnection { } /** + * 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. @@ -479,7 +542,32 @@ public class BaseInputConnection implements InputConnection { content.clear(); } } - + + 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 { diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 8b6831e..3b8a364 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -80,6 +80,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 * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode} for @@ -162,6 +177,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 * composing styling or other state that was around it. The cursor diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index 210559a..b73f9bb 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -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(); } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index a765e38..986ba38 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -31,9 +31,10 @@ import java.lang.ref.WeakReference; public class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; - + 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; @@ -50,7 +52,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_REPORT_FULLSCREEN_MODE = 100; private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; - + private WeakReference<InputConnection> mInputConnection; private Looper mMainLooper; @@ -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 diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 02cb9e4..333fc82 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -65,4 +65,8 @@ import com.android.internal.view.IInputContextCallback; void clearMetaKeyStates(int states); void performPrivateCommand(String action, in Bundle data); + + void setComposingRegion(int start, int end); + + void getSelectedText(int flags, int seq, IInputContextCallback callback); } diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl index 9b8c43c..661066b 100644 --- a/core/java/com/android/internal/view/IInputContextCallback.aidl +++ b/core/java/com/android/internal/view/IInputContextCallback.aidl @@ -26,4 +26,5 @@ oneway interface IInputContextCallback { void setTextAfterCursor(CharSequence textAfterCursor, int seq); void setCursorCapsMode(int capsMode, int seq); void setExtractedText(in ExtractedText extractedText, int seq); + void setSelectedText(CharSequence selectedText, int seq); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 3c44e58..08c3026 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -16,8 +16,6 @@ package com.android.internal.view; -import com.android.internal.view.IInputContext; - import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; @@ -38,6 +36,7 @@ public class InputConnectionWrapper implements InputConnection { public boolean mHaveValue; public CharSequence mTextBeforeCursor; public CharSequence mTextAfterCursor; + public CharSequence mSelectedText; public ExtractedText mExtractedText; public int mCursorCapsMode; @@ -114,6 +113,19 @@ public class InputConnectionWrapper implements InputConnection { } } + public void setSelectedText(CharSequence selectedText, int seq) { + synchronized (this) { + if (seq == mSeq) { + mSelectedText = selectedText; + mHaveValue = true; + notifyAll(); + } else { + Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq + + ") in setSelectedText, ignoring."); + } + } + } + public void setCursorCapsMode(int capsMode, int seq) { synchronized (this) { if (seq == mSeq) { @@ -203,6 +215,24 @@ public class InputConnectionWrapper implements InputConnection { return value; } + public CharSequence getSelectedText(int flags) { + CharSequence value = null; + try { + InputContextCallback callback = InputContextCallback.getInstance(); + mIInputContext.getSelectedText(flags, callback.mSeq, callback); + synchronized (callback) { + callback.waitForResultLocked(); + if (callback.mHaveValue) { + value = callback.mSelectedText; + } + } + callback.dispose(); + } catch (RemoteException e) { + return null; + } + return value; + } + public int getCursorCapsMode(int reqModes) { int value = 0; try { @@ -283,7 +313,16 @@ public class InputConnectionWrapper implements InputConnection { return false; } } - + + public boolean setComposingRegion(int start, int end) { + try { + mIInputContext.setComposingRegion(start, end); + return true; + } catch (RemoteException e) { + return false; + } + } + public boolean setComposingText(CharSequence text, int newCursorPosition) { try { mIInputContext.setComposingText(text, newCursorPosition); |