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

Merge commit 'a90b7f0125389b9e1040d2be82aad4ef74ea6071' into gingerbread-plus-aosp

* commit 'a90b7f0125389b9e1040d2be82aad4ef74ea6071':
  Add methods to InputConnection: setComposingRegion() to select a region of text for correction, and getSelectedText()
This commit is contained in:
Amith Yamasani
2010-08-26 14:57:31 -07:00
committed by Android Git Automerger
8 changed files with 299 additions and 34 deletions

View File

@@ -198082,6 +198082,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"
@@ -198192,6 +198205,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"
@@ -199186,6 +199214,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"
@@ -199283,6 +199324,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"
@@ -199475,6 +199531,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"
@@ -199572,6 +199641,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"

View File

@@ -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);
}
@@ -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.
@@ -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 {

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);