summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--api/system-current.txt3
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java163
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java27
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java4
-rw-r--r--core/java/android/widget/AbsListView.java5
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java21
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl5
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java11
10 files changed, 238 insertions, 7 deletions
diff --git a/api/current.txt b/api/current.txt
index 629d832c0c3c..c2e6ff3140c5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -43334,6 +43334,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public static int getComposingSpanEnd(android.text.Spannable);
@@ -43499,6 +43500,7 @@ package android.view.inputmethod {
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
+ method public abstract boolean deleteSurroundingTextInCodePoints(int, int);
method public abstract boolean endBatchEdit();
method public abstract boolean finishComposingText();
method public abstract int getCursorCapsMode(int);
@@ -43529,6 +43531,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public int getCursorCapsMode(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a36ce05acca..3ec14c8a4b37 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -45688,6 +45688,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public static int getComposingSpanEnd(android.text.Spannable);
@@ -45853,6 +45854,7 @@ package android.view.inputmethod {
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
+ method public abstract boolean deleteSurroundingTextInCodePoints(int, int);
method public abstract boolean endBatchEdit();
method public abstract boolean finishComposingText();
method public abstract int getCursorCapsMode(int);
@@ -45883,6 +45885,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public int getCursorCapsMode(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 21b101fef449..bc18278233b9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -43350,6 +43350,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public static int getComposingSpanEnd(android.text.Spannable);
@@ -43515,6 +43516,7 @@ package android.view.inputmethod {
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
method public abstract boolean deleteSurroundingText(int, int);
+ method public abstract boolean deleteSurroundingTextInCodePoints(int, int);
method public abstract boolean endBatchEdit();
method public abstract boolean finishComposingText();
method public abstract int getCursorCapsMode(int);
@@ -43545,6 +43547,7 @@ package android.view.inputmethod {
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
method public boolean deleteSurroundingText(int, int);
+ method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
method public int getCursorCapsMode(int);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 6e5e5915bb6f..5919f9e89669 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -230,7 +230,7 @@ public class BaseInputConnection implements InputConnection {
b = tmp;
}
- // ignore the composing text.
+ // Ignore the composing text.
int ca = getComposingSpanStart(content);
int cb = getComposingSpanEnd(content);
if (cb < ca) {
@@ -266,6 +266,167 @@ public class BaseInputConnection implements InputConnection {
return true;
}
+ private static int INVALID_INDEX = -1;
+ private static int findIndexBackward(final CharSequence cs, final int from,
+ final int numCodePoints) {
+ int currentIndex = from;
+ boolean waitingHighSurrogate = false;
+ final int N = cs.length();
+ if (currentIndex < 0 || N < currentIndex) {
+ return INVALID_INDEX; // The starting point is out of range.
+ }
+ if (numCodePoints < 0) {
+ return INVALID_INDEX; // Basically this should not happen.
+ }
+ int remainingCodePoints = numCodePoints;
+ while (true) {
+ if (remainingCodePoints == 0) {
+ return currentIndex; // Reached to the requested length in code points.
+ }
+
+ --currentIndex;
+ if (currentIndex < 0) {
+ if (waitingHighSurrogate) {
+ return INVALID_INDEX; // An invalid surrogate pair is found.
+ }
+ return 0; // Reached to the beginning of the text w/o any invalid surrogate pair.
+ }
+ final char c = cs.charAt(currentIndex);
+ if (waitingHighSurrogate) {
+ if (!java.lang.Character.isHighSurrogate(c)) {
+ return INVALID_INDEX; // An invalid surrogate pair is found.
+ }
+ waitingHighSurrogate = false;
+ --remainingCodePoints;
+ continue;
+ }
+ if (!java.lang.Character.isSurrogate(c)) {
+ --remainingCodePoints;
+ continue;
+ }
+ if (java.lang.Character.isHighSurrogate(c)) {
+ return INVALID_INDEX; // A invalid surrogate pair is found.
+ }
+ waitingHighSurrogate = true;
+ }
+ }
+
+ private static int findIndexForward(final CharSequence cs, final int from,
+ final int numCodePoints) {
+ int currentIndex = from;
+ boolean waitingLowSurrogate = false;
+ final int N = cs.length();
+ if (currentIndex < 0 || N < currentIndex) {
+ return INVALID_INDEX; // The starting point is out of range.
+ }
+ if (numCodePoints < 0) {
+ return INVALID_INDEX; // Basically this should not happen.
+ }
+ int remainingCodePoints = numCodePoints;
+
+ while (true) {
+ if (remainingCodePoints == 0) {
+ return currentIndex; // Reached to the requested length in code points.
+ }
+
+ if (currentIndex >= N) {
+ if (waitingLowSurrogate) {
+ return INVALID_INDEX; // An invalid surrogate pair is found.
+ }
+ return N; // Reached to the end of the text w/o any invalid surrogate pair.
+ }
+ final char c = cs.charAt(currentIndex);
+ if (waitingLowSurrogate) {
+ if (!java.lang.Character.isLowSurrogate(c)) {
+ return INVALID_INDEX; // An invalid surrogate pair is found.
+ }
+ --remainingCodePoints;
+ waitingLowSurrogate = false;
+ ++currentIndex;
+ continue;
+ }
+ if (!java.lang.Character.isSurrogate(c)) {
+ --remainingCodePoints;
+ ++currentIndex;
+ continue;
+ }
+ if (java.lang.Character.isLowSurrogate(c)) {
+ return INVALID_INDEX; // A invalid surrogate pair is found.
+ }
+ waitingLowSurrogate = true;
+ ++currentIndex;
+ }
+ }
+
+ /**
+ * The default implementation performs the deletion around the current selection position of the
+ * editable text.
+ * @param beforeLength The number of characters before the cursor to be deleted, in code points.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code points.
+ * If this is greater than the number of existing characters between the cursor and
+ * the end of the text, then this method does not fail but deletes all the characters in
+ * that range.
+ */
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
+ + " / " + afterLength);
+ final Editable content = getEditable();
+ if (content == null) return false;
+
+ beginBatchEdit();
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ // Ignore the composing text.
+ int ca = getComposingSpanStart(content);
+ int cb = getComposingSpanEnd(content);
+ if (cb < ca) {
+ int tmp = ca;
+ ca = cb;
+ cb = tmp;
+ }
+ if (ca != -1 && cb != -1) {
+ if (ca < a) a = ca;
+ if (cb > b) b = cb;
+ }
+
+ if (a >= 0 && b >= 0) {
+ final int start = findIndexBackward(content, a, Math.max(beforeLength, 0));
+ if (start != INVALID_INDEX) {
+ final int end = findIndexForward(content, b, Math.max(afterLength, 0));
+ if (end != INVALID_INDEX) {
+ final int numDeleteBefore = a - start;
+ if (numDeleteBefore > 0) {
+ content.delete(start, a);
+ }
+ final int numDeleteAfter = end - b;
+ if (numDeleteAfter > 0) {
+ content.delete(b - numDeleteBefore, end - numDeleteBefore);
+ }
+ }
+ }
+ // NOTE: You may think we should return false here if start and/or end is INVALID_INDEX,
+ // but the truth is that IInputConnectionWrapper running in the middle of IPC calls
+ // always returns true to the IME without waiting for the completion of this method as
+ // IInputConnectionWrapper#isAtive() returns true. This is actually why some methods
+ // including this method look like asynchronous calls from the IME.
+ }
+
+ endBatchEdit();
+
+ return true;
+ }
+
/**
* The default implementation removes the composing state from the
* current editable text. In addition, only if dummy mode, a key event is
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index be7bc14f0515..eb773e23fa62 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -348,6 +348,33 @@ public interface InputConnection {
public boolean deleteSurroundingText(int beforeLength, int afterLength);
/**
+ * A variant of {@link #deleteSurroundingText(int, int)}. Major differences are:
+ *
+ * <ul>
+ * <li>The lengths are supplied in code points, not in Java chars or in glyphs.</>
+ * <li>This method does nothing if there are one or more invalid surrogate pairs in the
+ * requested range.</li>
+ * </ul>
+ *
+ * <p><strong>Editor authors:</strong> In addition to the requirement in
+ * {@link #deleteSurroundingText(int, int)}, make sure to do nothing when one ore more invalid
+ * surrogate pairs are found in the requested range.</p>
+ *
+ * @see #deleteSurroundingText(int, int)
+ *
+ * @param beforeLength The number of characters before the cursor to be deleted, in code points.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code points.
+ * If this is greater than the number of existing characters between the cursor and
+ * the end of the text, then this method does not fail but deletes all the characters in
+ * that range.
+ * @return true on success, false if the input connection is no longer valid.
+ */
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
+
+ /**
* Replace the currently composing text with the given text, and
* set the new cursor position. Any composing text set previously
* will be removed automatically.
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 231aa070672a..e5ae42299dba 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -62,6 +62,10 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.getExtractedText(request, flags);
}
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ return mTarget.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ }
+
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
return mTarget.deleteSurroundingText(beforeLength, afterLength);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e1ce9fe7c039..6241a4cd3462 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5803,6 +5803,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ return getTarget().deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ }
+
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
return getTarget().setComposingText(text, newCursorPosition);
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 85ec29c4634c..b77cc6365d8a 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -49,6 +49,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
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;
+ private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
private static final int DO_BEGIN_BATCH_EDIT = 90;
private static final int DO_END_BATCH_EDIT = 95;
private static final int DO_REPORT_FULLSCREEN_MODE = 100;
@@ -155,9 +156,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
}
- public void deleteSurroundingText(int leftLength, int rightLength) {
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
- leftLength, rightLength));
+ beforeLength, afterLength));
+ }
+
+ public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
+ beforeLength, afterLength));
}
public void beginBatchEdit() {
@@ -389,6 +395,15 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
ic.deleteSurroundingText(msg.arg1, msg.arg2);
return;
}
+ case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
+ InputConnection ic = mInputConnection.get();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
+ return;
+ }
+ ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
+ return;
+ }
case DO_BEGIN_BATCH_EDIT: {
InputConnection ic = mInputConnection.get();
if (ic == null || !isActive()) {
@@ -454,7 +469,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
Message obtainMessageII(int what, int arg1, int arg2) {
return mH.obtainMessage(what, arg1, arg2);
}
-
+
Message obtainMessageO(int what, Object arg1) {
return mH.obtainMessage(what, 0, 0, arg1);
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index fd2b513fcb1a..f7ec420ba676 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -38,8 +38,9 @@ import com.android.internal.view.IInputContextCallback;
void getExtractedText(in ExtractedTextRequest request, int flags, int seq,
IInputContextCallback callback);
-
- void deleteSurroundingText(int leftLength, int rightLength);
+
+ void deleteSurroundingText(int beforeLength, int afterLength);
+ void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
void setComposingText(CharSequence text, int newCursorPosition);
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 7dc927f744f6..94790c158559 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -400,7 +400,7 @@ public class InputConnectionWrapper implements InputConnection {
return false;
}
}
-
+
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
try {
mIInputContext.deleteSurroundingText(beforeLength, afterLength);
@@ -410,6 +410,15 @@ public class InputConnectionWrapper implements InputConnection {
}
}
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ try {
+ mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
public boolean reportFullscreenMode(boolean enabled) {
try {
mIInputContext.reportFullscreenMode(enabled);