summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/text/Selection.java3
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java105
-rw-r--r--core/java/android/text/method/WordIterator.java220
3 files changed, 220 insertions, 108 deletions
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index b18570a..679e2cc 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -16,10 +16,7 @@
package android.text;
-import android.util.Log;
-
import java.text.BreakIterator;
-import java.text.CharacterIterator;
/**
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index b25ba8d..d432dee 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -17,23 +17,14 @@
package android.text.method;
import android.graphics.Rect;
-import android.text.CharSequenceIterator;
-import android.text.Editable;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
-import android.text.Spanned;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.util.MathUtils;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
-import java.text.BreakIterator;
-import java.text.CharacterIterator;
-
/**
* A movement method that provides cursor movement and selection.
* Supports displaying the context menu on DPad Center.
@@ -332,102 +323,6 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
return sInstance;
}
- /**
- * Walks through cursor positions at word boundaries. Internally uses
- * {@link BreakIterator#getWordInstance()}, and caches {@link CharSequence}
- * for performance reasons.
- */
- private static class WordIterator implements Selection.PositionIterator {
- private CharSequence mCurrent;
- private boolean mCurrentDirty = false;
-
- private BreakIterator mIterator;
-
- private TextWatcher mWatcher = new TextWatcher() {
- /** {@inheritDoc} */
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- // ignored
- }
-
- /** {@inheritDoc} */
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- mCurrentDirty = true;
- }
-
- /** {@inheritDoc} */
- public void afterTextChanged(Editable s) {
- // ignored
- }
- };
-
- public void setCharSequence(CharSequence incoming) {
- if (mIterator == null) {
- mIterator = BreakIterator.getWordInstance();
- }
-
- // when incoming is different object, move listeners to new sequence
- // and mark as dirty so we reload contents.
- if (mCurrent != incoming) {
- if (mCurrent instanceof Editable) {
- ((Editable) mCurrent).removeSpan(mWatcher);
- }
-
- if (incoming instanceof Editable) {
- ((Editable) incoming).setSpan(
- mWatcher, 0, incoming.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- }
-
- mCurrent = incoming;
- mCurrentDirty = true;
- }
-
- if (mCurrentDirty) {
- final CharacterIterator charIterator = new CharSequenceIterator(mCurrent);
- mIterator.setText(charIterator);
-
- mCurrentDirty = false;
- }
- }
-
- private boolean isValidOffset(int offset) {
- return offset >= 0 && offset <= mCurrent.length();
- }
-
- private boolean isLetterOrDigit(int offset) {
- if (isValidOffset(offset)) {
- return Character.isLetterOrDigit(mCurrent.charAt(offset));
- } else {
- return false;
- }
- }
-
- /** {@inheritDoc} */
- public int preceding(int offset) {
- // always round cursor index into valid string index
- offset = MathUtils.constrain(offset, 0, mCurrent.length());
-
- do {
- offset = mIterator.preceding(offset);
- if (isLetterOrDigit(offset)) break;
- } while (isValidOffset(offset));
-
- return offset;
- }
-
- /** {@inheritDoc} */
- public int following(int offset) {
- // always round cursor index into valid string index
- offset = MathUtils.constrain(offset, 0, mCurrent.length());
-
- do {
- offset = mIterator.following(offset);
- if (isLetterOrDigit(offset - 1)) break;
- } while (isValidOffset(offset));
-
- return offset;
- }
- }
-
private WordIterator mWordIterator = new WordIterator();
private static final Object LAST_TAP_DOWN = new Object();
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
new file mode 100644
index 0000000..b250414
--- /dev/null
+++ b/core/java/android/text/method/WordIterator.java
@@ -0,0 +1,220 @@
+
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.method;
+
+import android.text.CharSequenceIterator;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.TextWatcher;
+
+import java.text.BreakIterator;
+import java.text.CharacterIterator;
+import java.util.Locale;
+
+/**
+ * Walks through cursor positions at word boundaries. Internally uses
+ * {@link BreakIterator#getWordInstance()}, and caches {@link CharSequence}
+ * for performance reasons.
+ *
+ * Also provides methods to determine word boundaries.
+ * {@hide}
+ */
+public class WordIterator implements Selection.PositionIterator {
+ private CharSequence mCurrent;
+ private boolean mCurrentDirty = false;
+
+ private BreakIterator mIterator;
+
+ /**
+ * Constructs a WordIterator using the default locale.
+ */
+ public WordIterator() {
+ this(Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new WordIterator for the specified locale.
+ * @param locale The locale to be used when analysing the text.
+ */
+ public WordIterator(Locale locale) {
+ mIterator = BreakIterator.getWordInstance(locale);
+ }
+
+ private final TextWatcher mWatcher = new TextWatcher() {
+ /** {@inheritDoc} */
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCurrentDirty = true;
+ }
+
+ /** {@inheritDoc} */
+ public void afterTextChanged(Editable s) {
+ // ignored
+ }
+ };
+
+ public void setCharSequence(CharSequence incoming) {
+ // When incoming is different object, move listeners to new sequence
+ // and mark as dirty so we reload contents.
+ if (mCurrent != incoming) {
+ if (mCurrent instanceof Editable) {
+ ((Editable) mCurrent).removeSpan(mWatcher);
+ }
+
+ if (incoming instanceof Editable) {
+ ((Editable) incoming).setSpan(
+ mWatcher, 0, incoming.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ mCurrent = incoming;
+ mCurrentDirty = true;
+ }
+
+ if (mCurrentDirty) {
+ final CharacterIterator charIterator = new CharSequenceIterator(mCurrent);
+ mIterator.setText(charIterator);
+
+ mCurrentDirty = false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int preceding(int offset) {
+ do {
+ offset = mIterator.preceding(offset);
+ if (offset == BreakIterator.DONE || isOnLetterOrDigit(offset)) {
+ break;
+ }
+ } while (true);
+
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ public int following(int offset) {
+ do {
+ offset = mIterator.following(offset);
+ if (offset == BreakIterator.DONE || isAfterLetterOrDigit(offset)) {
+ break;
+ }
+ } while (true);
+
+ return offset;
+ }
+
+ /** If <code>offset</code> is within a word, returns the index of the first character of that
+ * word, otherwise returns BreakIterator.DONE.
+ *
+ * The offsets that are considered to be part of a word are the indexes of its characters,
+ * <i>as well as</i> the index of its last character plus one.
+ * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+ *
+ * Valid range for offset is [0..textLength] (note the inclusive upper bound).
+ * The returned value is within [0..offset] or BreakIterator.DONE.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getBeginning(int offset) {
+ checkOffsetIsValid(offset);
+
+ if (isOnLetterOrDigit(offset)) {
+ if (mIterator.isBoundary(offset)) {
+ return offset;
+ } else {
+ return mIterator.preceding(offset);
+ }
+ } else {
+ if (isAfterLetterOrDigit(offset)) {
+ return mIterator.preceding(offset);
+ }
+ }
+ return BreakIterator.DONE;
+ }
+
+ /** If <code>offset</code> is within a word, returns the index of the last character of that
+ * word plus one, otherwise returns BreakIterator.DONE.
+ *
+ * The offsets that are considered to be part of a word are the indexes of its characters,
+ * <i>as well as</i> the index of its last character plus one.
+ * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+ *
+ * Valid range for offset is [0..textLength] (note the inclusive upper bound).
+ * The returned value is within [offset..textLength] or BreakIterator.DONE.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getEnd(int offset) {
+ checkOffsetIsValid(offset);
+
+ if (isAfterLetterOrDigit(offset)) {
+ if (mIterator.isBoundary(offset)) {
+ return offset;
+ } else {
+ return mIterator.following(offset);
+ }
+ } else {
+ if (isOnLetterOrDigit(offset)) {
+ return mIterator.following(offset);
+ }
+ }
+ return BreakIterator.DONE;
+ }
+
+ private boolean isAfterLetterOrDigit(int offset) {
+ if (offset - 1 >= 0) {
+ final char previousChar = mCurrent.charAt(offset - 1);
+ if (Character.isLetterOrDigit(previousChar)) return true;
+ if (offset - 2 >= 0) {
+ final char previousPreviousChar = mCurrent.charAt(offset - 2);
+ if (Character.isSurrogatePair(previousPreviousChar, previousChar)) {
+ final int codePoint = Character.toCodePoint(previousPreviousChar, previousChar);
+ return Character.isLetterOrDigit(codePoint);
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isOnLetterOrDigit(int offset) {
+ final int length = mCurrent.length();
+ if (offset < length) {
+ final char currentChar = mCurrent.charAt(offset);
+ if (Character.isLetterOrDigit(currentChar)) return true;
+ if (offset + 1 < length) {
+ final char nextChar = mCurrent.charAt(offset + 1);
+ if (Character.isSurrogatePair(currentChar, nextChar)) {
+ final int codePoint = Character.toCodePoint(currentChar, nextChar);
+ return Character.isLetterOrDigit(codePoint);
+ }
+ }
+ }
+ return false;
+ }
+
+ private void checkOffsetIsValid(int offset) {
+ if (offset < 0 || offset > mCurrent.length()) {
+ final String message = "Valid range is [0, " + mCurrent.length() + "]";
+ throw new IllegalArgumentException(message);
+ }
+ }
+}