diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
18 files changed, 187 insertions, 112 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java index c628c5b09..063f2113d 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java @@ -159,8 +159,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider // Add the virtual children of the root View. final Keyboard keyboard = mKeyboardView.getKeyboard(); - final Key[] keys = keyboard.getKeys(); - for (Key key : keys) { + for (final Key key : keyboard.getSortedKeys()) { final int childVirtualViewId = generateVirtualViewIdForKey(key); rootInfo.addChild(mKeyboardView, childVirtualViewId); } @@ -308,8 +307,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider } mVirtualViewIdToKey.clear(); - final Key[] keys = keyboard.getKeys(); - for (Key key : keys) { + for (final Key key : keyboard.getSortedKeys()) { final int virtualViewId = generateVirtualViewIdForKey(key); mVirtualViewIdToKey.put(virtualViewId, key); } diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java index 8822ccec9..d8b5758a6 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java @@ -58,9 +58,10 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -123,7 +124,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange "places", "symbols", "emoticons" }; - private static final int[] sCategoryIcon = new int[] { + private static final int[] sCategoryIcon = { R.drawable.ic_emoji_recent_light, R.drawable.ic_emoji_people_light, R.drawable.ic_emoji_objects_light, @@ -133,6 +134,14 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange 0 }; private static final String[] sCategoryLabel = { null, null, null, null, null, null, ":-)" }; + private static final int[] sAccessibilityDescriptionResourceIdsForCategories = { + R.string.spoken_descrption_emoji_category_recents, + R.string.spoken_descrption_emoji_category_people, + R.string.spoken_descrption_emoji_category_objects, + R.string.spoken_descrption_emoji_category_nature, + R.string.spoken_descrption_emoji_category_places, + R.string.spoken_descrption_emoji_category_symbols, + R.string.spoken_descrption_emoji_category_emoticons }; private static final int[] sCategoryElementId = { KeyboardId.ELEMENT_EMOJI_RECENTS, KeyboardId.ELEMENT_EMOJI_CATEGORY1, @@ -142,6 +151,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange KeyboardId.ELEMENT_EMOJI_CATEGORY5, KeyboardId.ELEMENT_EMOJI_CATEGORY6 }; private final SharedPreferences mPrefs; + private final Resources mRes; private final int mMaxPageKeyCount; private final KeyboardLayoutSet mLayoutSet; private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap(); @@ -156,6 +166,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange public EmojiCategory(final SharedPreferences prefs, final Resources res, final KeyboardLayoutSet layoutSet) { mPrefs = prefs; + mRes = res; mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count); mLayoutSet = layoutSet; for (int i = 0; i < sCategoryName.length; ++i) { @@ -206,6 +217,10 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return sCategoryLabel[categoryId]; } + public String getAccessibilityDescription(final int categoryId) { + return mRes.getString(sAccessibilityDescriptionResourceIdsForCategories[categoryId]); + } + public ArrayList<CategoryProperties> getShownCategories() { return mShownCategories; } @@ -283,7 +298,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange private int getCategoryPageCount(final int categoryId) { final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); - return (keyboard.getKeys().length - 1) / mMaxPageKeyCount + 1; + return (keyboard.getSortedKeys().size() - 1) / mMaxPageKeyCount + 1; } // Returns a pair of the category id and the category page id from the view pager's page @@ -332,7 +347,8 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); - final Key[][] sortedKeys = sortKeysIntoPages(keyboard.getKeys(), mMaxPageKeyCount); + final Key[][] sortedKeys = sortKeysIntoPages( + keyboard.getSortedKeys(), mMaxPageKeyCount); for (int pageId = 0; pageId < sortedKeys.length; ++pageId) { final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs, mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), @@ -380,13 +396,13 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } }; - private static Key[][] sortKeysIntoPages(final Key[] inKeys, final int maxPageCount) { - final Key[] keys = Arrays.copyOf(inKeys, inKeys.length); - Arrays.sort(keys, 0, keys.length, EMOJI_KEY_COMPARATOR); - final int pageCount = (keys.length - 1) / maxPageCount + 1; + private static Key[][] sortKeysIntoPages(final List<Key> inKeys, final int maxPageCount) { + final ArrayList<Key> keys = CollectionUtils.newArrayList(inKeys); + Collections.sort(keys, EMOJI_KEY_COMPARATOR); + final int pageCount = (keys.size() - 1) / maxPageCount + 1; final Key[][] retval = new Key[pageCount][maxPageCount]; - for (int i = 0; i < keys.length; ++i) { - retval[i / maxPageCount][i % maxPageCount] = keys[i]; + for (int i = 0; i < keys.size(); ++i) { + retval[i / maxPageCount][i % maxPageCount] = keys.get(i); } return retval; } @@ -447,12 +463,14 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( R.layout.emoji_keyboard_tab_icon, null); iconView.setImageResource(mEmojiCategory.getCategoryIcon(categoryId)); + iconView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId)); tspec.setIndicator(iconView); } if (mEmojiCategory.getCategoryLabel(categoryId) != null) { final TextView textView = (TextView)LayoutInflater.from(getContext()).inflate( R.layout.emoji_keyboard_tab_label, null); textView.setText(mEmojiCategory.getCategoryLabel(categoryId)); + textView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId)); textView.setTextColor(mTabLabelColor); tspec.setIndicator(textView); } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 4fd3bac2f..f646a03c2 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -25,6 +25,9 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; +import java.util.Collections; +import java.util.List; + /** * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard * consists of rows of keys. @@ -74,10 +77,10 @@ public class Keyboard { /** Maximum column for more keys keyboard */ public final int mMaxMoreKeysKeyboardColumn; - /** Array of keys and icons in this keyboard */ - private final Key[] mKeys; - public final Key[] mShiftKeys; - public final Key[] mAltCodeKeysWhileTyping; + /** List of keys in this keyboard */ + private final List<Key> mSortedKeys; + public final List<Key> mShiftKeys; + public final List<Key> mAltCodeKeysWhileTyping; public final KeyboardIconsSet mIconsSet; private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray(); @@ -100,15 +103,16 @@ public class Keyboard { mTopPadding = params.mTopPadding; mVerticalGap = params.mVerticalGap; - mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]); - mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]); - mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray( - new Key[params.mAltCodeKeysWhileTyping.size()]); + mSortedKeys = Collections.unmodifiableList( + CollectionUtils.newArrayList(params.mSortedKeys)); + mShiftKeys = Collections.unmodifiableList(params.mShiftKeys); + mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping); mIconsSet = params.mIconsSet; mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(), params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, - mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection); + mMostCommonKeyWidth, mMostCommonKeyHeight, mSortedKeys, + params.mTouchPositionCorrection); mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled; } @@ -127,7 +131,7 @@ public class Keyboard { mTopPadding = keyboard.mTopPadding; mVerticalGap = keyboard.mVerticalGap; - mKeys = keyboard.mKeys; + mSortedKeys = keyboard.mSortedKeys; mShiftKeys = keyboard.mShiftKeys; mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping; mIconsSet = keyboard.mIconsSet; @@ -152,17 +156,14 @@ public class Keyboard { return mProximityInfo; } - public Key[] getKeys() { - return mKeys; - } - - public Key getKeyFromOutputText(final String outputText) { - for (final Key key : getKeys()) { - if (outputText.equals(key.getOutputText())) { - return key; - } - } - return null; + /** + * Return the sorted list of keys of this keyboard. + * The keys are sorted from top-left to bottom-right order. + * The list may contain {@link Spacer} object as well. + * @return the sorted unmodifiable list of {@link Key}s of this keyboard. + */ + public List<Key> getSortedKeys() { + return mSortedKeys; } public Key getKey(final int code) { @@ -175,7 +176,7 @@ public class Keyboard { return mKeyCache.valueAt(index); } - for (final Key key : getKeys()) { + for (final Key key : getSortedKeys()) { if (key.getCode() == code) { mKeyCache.put(code, key); return key; @@ -191,7 +192,7 @@ public class Keyboard { return true; } - for (final Key key : getKeys()) { + for (final Key key : getSortedKeys()) { if (key == aKey) { mKeyCache.put(key.getCode(), key); return true; @@ -209,10 +210,10 @@ public class Keyboard { * Returns the array of the keys that are closest to the given point. * @param x the x-coordinate of the point * @param y the y-coordinate of the point - * @return the array of the nearest keys to the given point. If the given + * @return the list of the nearest keys to the given point. If the given * point is out of range, then an array of size zero is returned. */ - public Key[] getNearestKeys(final int x, final int y) { + public List<Key> getNearestKeys(final int x, final int y) { // Avoid dead pixels at edges of the keyboard final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1)); final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1)); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 18e51d392..8ca00b005 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -289,7 +289,7 @@ public class KeyboardView extends View { // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. if (drawAllKeys || isHardwareAccelerated) { // Draw all keys. - for (final Key key : mKeyboard.getKeys()) { + for (final Key key : mKeyboard.getSortedKeys()) { onDrawKey(key, canvas, paint); } } else { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java index abff202b7..5a9d4755f 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java @@ -43,7 +43,7 @@ public final class MoreKeysDetector extends KeyDetector { Key nearestKey = null; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; - for (final Key key : keyboard.getKeys()) { + for (final Key key : keyboard.getSortedKeys()) { final int dist = key.squaredDistanceToEdge(touchX, touchY); if (dist < nearestDist) { nearestKey = key; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index a0316696c..3746d5b79 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -22,9 +22,13 @@ import android.util.Log; import com.android.inputmethod.keyboard.internal.TouchPositionCorrection; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class ProximityInfo { private static final String TAG = ProximityInfo.class.getSimpleName(); @@ -34,7 +38,7 @@ public class ProximityInfo { public static final int MAX_PROXIMITY_CHARS_SIZE = 16; /** Number of key widths from current touch point to search for nearest keys. */ private static final float SEARCH_DISTANCE = 1.2f; - private static final Key[] EMPTY_KEY_ARRAY = new Key[0]; + private static final List<Key> EMPTY_KEY_LIST = Collections.emptyList(); private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f; private final int mGridWidth; @@ -47,13 +51,13 @@ public class ProximityInfo { private final int mKeyboardHeight; private final int mMostCommonKeyWidth; private final int mMostCommonKeyHeight; - private final Key[] mKeys; - private final Key[][] mGridNeighbors; + private final List<Key> mSortedKeys; + private final List<Key>[] mGridNeighbors; private final String mLocaleStr; ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight, final int minWidth, final int height, final int mostCommonKeyWidth, - final int mostCommonKeyHeight, final Key[] keys, + final int mostCommonKeyHeight, final List<Key> sortedKeys, final TouchPositionCorrection touchPositionCorrection) { if (TextUtils.isEmpty(localeStr)) { mLocaleStr = ""; @@ -69,8 +73,8 @@ public class ProximityInfo { mKeyboardHeight = height; mMostCommonKeyHeight = mostCommonKeyHeight; mMostCommonKeyWidth = mostCommonKeyWidth; - mKeys = keys; - mGridNeighbors = new Key[mGridSize][]; + mSortedKeys = sortedKeys; + mGridNeighbors = new List[mGridSize]; if (minWidth == 0 || height == 0) { // No proximity required. Keyboard might be more keys keyboard. return; @@ -99,7 +103,7 @@ public class ProximityInfo { return key.getCode() >= Constants.CODE_SPACE; } - private static int getProximityInfoKeysCount(final Key[] keys) { + private static int getProximityInfoKeysCount(final List<Key> keys) { int count = 0; for (final Key key : keys) { if (needsProximityInfo(key)) { @@ -110,14 +114,14 @@ public class ProximityInfo { } private long createNativeProximityInfo(final TouchPositionCorrection touchPositionCorrection) { - final Key[][] gridNeighborKeys = mGridNeighbors; + final List<Key>[] gridNeighborKeys = mGridNeighbors; final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE); for (int i = 0; i < mGridSize; ++i) { - final int proximityCharsLength = gridNeighborKeys[i].length; + final int proximityCharsLength = gridNeighborKeys[i].size(); int infoIndex = i * MAX_PROXIMITY_CHARS_SIZE; for (int j = 0; j < proximityCharsLength; ++j) { - final Key neighborKey = gridNeighborKeys[i][j]; + final Key neighborKey = gridNeighborKeys[i].get(j); // Excluding from proximityCharsArray if (!needsProximityInfo(neighborKey)) { continue; @@ -142,8 +146,8 @@ public class ProximityInfo { } } - final Key[] keys = mKeys; - final int keyCount = getProximityInfoKeysCount(keys); + final List<Key> sortedKeys = mSortedKeys; + final int keyCount = getProximityInfoKeysCount(sortedKeys); final int[] keyXCoordinates = new int[keyCount]; final int[] keyYCoordinates = new int[keyCount]; final int[] keyWidths = new int[keyCount]; @@ -153,8 +157,8 @@ public class ProximityInfo { final float[] sweetSpotCenterYs; final float[] sweetSpotRadii; - for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) { - final Key key = keys[keyIndex]; + for (int infoIndex = 0, keyIndex = 0; keyIndex < sortedKeys.size(); keyIndex++) { + final Key key = sortedKeys.get(keyIndex); // Excluding from key coordinate arrays if (!needsProximityInfo(key)) { continue; @@ -177,8 +181,8 @@ public class ProximityInfo { final int rows = touchPositionCorrection.getRows(); final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight); - for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) { - final Key key = keys[keyIndex]; + for (int infoIndex = 0, keyIndex = 0; keyIndex < sortedKeys.size(); keyIndex++) { + final Key key = sortedKeys.get(keyIndex); // Excluding from touch position correction arrays if (!needsProximityInfo(key)) { continue; @@ -240,7 +244,7 @@ public class ProximityInfo { private void computeNearestNeighbors() { final int defaultWidth = mMostCommonKeyWidth; - final int keyCount = mKeys.length; + final int keyCount = mSortedKeys.size(); final int gridSize = mGridNeighbors.length; final int threshold = (int) (defaultWidth * SEARCH_DISTANCE); final int thresholdSquared = threshold * threshold; @@ -259,7 +263,7 @@ public class ProximityInfo { final int[] neighborCountPerCell = new int[gridSize]; final int halfCellWidth = mCellWidth / 2; final int halfCellHeight = mCellHeight / 2; - for (final Key key : mKeys) { + for (final Key key : mSortedKeys) { if (key.isSpacer()) continue; /* HOW WE PRE-SELECT THE CELLS (iterate over only the relevant cells, instead of all of them) @@ -353,9 +357,13 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get } for (int i = 0; i < gridSize; ++i) { - final int base = i * keyCount; - mGridNeighbors[i] = - Arrays.copyOfRange(neighborsFlatBuffer, base, base + neighborCountPerCell[i]); + final int indexStart = i * keyCount; + final int indexEnd = indexStart + neighborCountPerCell[i]; + final ArrayList<Key> neighbords = CollectionUtils.newArrayList(indexEnd - indexStart); + for (int index = indexStart; index < indexEnd; index++) { + neighbords.add(neighborsFlatBuffer[index]); + } + mGridNeighbors[i] = Collections.unmodifiableList(neighbords); } } @@ -369,7 +377,7 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get if (primaryKeyCode > Constants.CODE_SPACE) { dest[index++] = primaryKeyCode; } - final Key[] nearestKeys = getNearestKeys(x, y); + final List<Key> nearestKeys = getNearestKeys(x, y); for (Key key : nearestKeys) { if (index >= destLength) { break; @@ -385,9 +393,9 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get } } - public Key[] getNearestKeys(final int x, final int y) { + public List<Key> getNearestKeys(final int x, final int y) { if (mGridNeighbors == null) { - return EMPTY_KEY_ARRAY; + return EMPTY_KEY_LIST; } if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) { int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); @@ -395,6 +403,6 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get return mGridNeighbors[index]; } } - return EMPTY_KEY_ARRAY; + return EMPTY_KEY_LIST; } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java index e2fd39017..397c098ce 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java @@ -30,6 +30,7 @@ import com.android.inputmethod.latin.utils.JsonUtils; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -50,7 +51,7 @@ public class DynamicGridKeyboard extends Keyboard { private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque(); private final ArrayDeque<Key> mPendingKeys = CollectionUtils.newArrayDeque(); - private Key[] mCachedGridKeys; + private List<Key> mCachedGridKeys; public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard, final int maxKeyCount, final int categoryId) { @@ -66,7 +67,7 @@ public class DynamicGridKeyboard extends Keyboard { } private Key getTemplateKey(final int code) { - for (final Key key : super.getKeys()) { + for (final Key key : super.getSortedKeys()) { if (key.getCode() == code) { return key; } @@ -146,9 +147,10 @@ public class DynamicGridKeyboard extends Keyboard { private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards, final int code) { for (final DynamicGridKeyboard keyboard : keyboards) { - final Key key = keyboard.getKey(code); - if (key != null) { - return key; + for (final Key key : keyboard.getSortedKeys()) { + if (key.getCode() == code) { + return key; + } } } return null; @@ -156,10 +158,11 @@ public class DynamicGridKeyboard extends Keyboard { private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards, final String outputText) { - for (final DynamicGridKeyboard kbd : keyboards) { - final Key key = kbd.getKeyFromOutputText(outputText); - if (key != null) { - return key; + for (final DynamicGridKeyboard keyboard : keyboards) { + for (final Key key : keyboard.getSortedKeys()) { + if (outputText.equals(key.getOutputText())) { + return key; + } } } return null; @@ -205,20 +208,22 @@ public class DynamicGridKeyboard extends Keyboard { } @Override - public Key[] getKeys() { + public List<Key> getSortedKeys() { synchronized (mLock) { if (mCachedGridKeys != null) { return mCachedGridKeys; } - mCachedGridKeys = mGridKeys.toArray(new Key[mGridKeys.size()]); + final ArrayList<Key> cachedKeys = CollectionUtils.newArrayList(mGridKeys.size()); + cachedKeys.addAll(mGridKeys); + mCachedGridKeys = Collections.unmodifiableList(cachedKeys); return mCachedGridKeys; } } @Override - public Key[] getNearestKeys(final int x, final int y) { + public List<Key> getNearestKeys(final int x, final int y) { // TODO: Calculate the nearest key index in mGridKeys from x and y. - return getKeys(); + return getSortedKeys(); } static final class GridKey extends Key { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java index 153391eed..a61a79b85 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -24,6 +24,8 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; +import java.util.Comparator; +import java.util.SortedSet; import java.util.TreeSet; public class KeyboardParams { @@ -58,7 +60,8 @@ public class KeyboardParams { public int GRID_WIDTH; public int GRID_HEIGHT; - public final TreeSet<Key> mKeys = CollectionUtils.newTreeSet(); // ordered set + // Keys are sorted from top-left to bottom-right order. + public final SortedSet<Key> mSortedKeys = new TreeSet<Key>(ROW_COLUMN_COMPARATOR); public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList(); public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); @@ -75,8 +78,20 @@ public class KeyboardParams { public final TouchPositionCorrection mTouchPositionCorrection = new TouchPositionCorrection(); + // Comparator to sort {@link Key}s from top-left to bottom-right order. + private static final Comparator<Key> ROW_COLUMN_COMPARATOR = new Comparator<Key>() { + @Override + public int compare(final Key lhs, final Key rhs) { + if (lhs.getY() < rhs.getY()) return -1; + if (lhs.getY() > rhs.getY()) return 1; + if (lhs.getX() < rhs.getX()) return -1; + if (lhs.getX() > rhs.getX()) return 1; + return 0; + } + }; + protected void clearKeys() { - mKeys.clear(); + mSortedKeys.clear(); mShiftKeys.clear(); clearHistogram(); } @@ -88,7 +103,7 @@ public class KeyboardParams { // Ignore zero width {@link Spacer}. return; } - mKeys.add(key); + mSortedKeys.add(key); if (isSpacer) { return; } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index e9b8431e7..a351ee974 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -262,7 +262,7 @@ public final class BinaryDictionary extends Dictionary { final int inputSize; if (!isGesture) { inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - mInputCodePoints, MAX_WORD_LENGTH); + mInputCodePoints); if (inputSize < 0) { return null; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index 7cb218fbe..0b6258a7f 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -421,7 +421,19 @@ public class DictionaryFacilitatorForSuggest { final String suggestionLowerCase = suggestion.toLowerCase(dictionaries.mLocale); final String secondWord; if (wasAutoCapitalized) { - secondWord = suggestionLowerCase; + if (isValidWord(suggestion, false /* ignoreCase */) + && !isValidWord(suggestionLowerCase, false /* ignoreCase */)) { + // If the word was auto-capitalized and exists only as a capitalized word in the + // dictionary, then we must not downcase it before registering it. For example, + // the name of the contacts in start-of-sentence position would come here with the + // wasAutoCapitalized flag: if we downcase it, we'd register a lower-case version + // of that contact's name which would end up popping in suggestions. + secondWord = suggestion; + } else { + // If however the word is not in the dictionary, or exists as a lower-case word + // only, then we consider that was a lower-case word that had been auto-capitalized. + secondWord = suggestionLowerCase; + } } else { // HACK: We'd like to avoid adding the capitalized form of common words to the User // History dictionary in order to avoid suggesting them until the dictionary diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 1fdb8d16c..81b02c396 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -237,7 +237,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Otherwise, we'll do it when we can. latinIme.mKeyboardSwitcher.loadKeyboard(latinIme.getCurrentInputEditorInfo(), settingsValues, latinIme.getCurrentAutoCapsState(), - latinIme.getCurrentAutoCapsState()); + latinIme.getCurrentRecapitalizeState()); } break; } @@ -501,7 +501,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - registerReceiver(mReceiver, filter); + registerReceiver(mConnectivityAndRingerModeChangeReceiver, filter); final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); @@ -630,7 +630,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onDestroy() { mInputLogic.mSuggest.mDictionaryFacilitator.closeDictionaries(); mSettings.onDestroy(); - unregisterReceiver(mReceiver); + unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.getInstance().onDestroy(); } @@ -645,6 +645,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting public void recycle() { + unregisterReceiver(mDictionaryPackInstallReceiver); + unregisterReceiver(mDictionaryDumpBroadcastReceiver); + unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); mInputLogic.recycle(); } @@ -816,9 +819,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best // effort to work around this bug. mInputLogic.mConnection.tryFixLyingCursorPosition(); - if (isDifferentTextField) { - mHandler.postResumeSuggestions(); - } + mHandler.postResumeSuggestions(); canReachInputConnection = true; } @@ -1635,7 +1636,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // boolean onKeyMultiple(final int keyCode, final int count, final KeyEvent event); // receive ringer mode change and network state change. - private BroadcastReceiver mReceiver = new BroadcastReceiver() { + private BroadcastReceiver mConnectivityAndRingerModeChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 1268e5aac..d755195f2 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -129,22 +129,25 @@ public final class WordComposer { * -1 is returned. * * @param destination the array of ints. - * @param maxSize the size of the array. * @return the number of copied code points. */ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - final int[] destination, final int maxSize) { - final int i = mTypedWordCache.length() - 1 - trailingSingleQuotesCount(); - if (i < 0) { + final int[] destination) { + // lastIndex is exclusive + final int lastIndex = mTypedWordCache.length() - trailingSingleQuotesCount(); + if (lastIndex <= 0) { // The string is empty or contains only single quotes. return 0; } - final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i); - if (codePointSize > maxSize) { + + // The following function counts the number of code points in the text range which begins + // at index 0 and extends to the character at lastIndex. + final int codePointSize = Character.codePointCount(mTypedWordCache, 0, lastIndex); + if (codePointSize > destination.length) { return -1; } return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, - i + 1, true /* downCase */); + lastIndex, true /* downCase */); } public boolean isSingleLetter() { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 491d98074..f1f906042 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -1284,6 +1284,10 @@ public final class InputLogic { || !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces // If no suggestions are requested, don't try restarting suggestions. || !settingsValues.isSuggestionsRequested() + // If we are currently in a batch input, we must not resume suggestions, or the result + // of the batch input will replace the new composition. This may happen in the corner case + // that the app moves the cursor on its own accord during a batch input. + || mInputLogicHandler.isInBatchInput() // If the cursor is not touching a word, or if there is a selection, return right away. || mConnection.hasSelection() // If we don't know the cursor location, return. diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index 64bba681f..9dbe2c38b 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -110,6 +110,10 @@ class InputLogicHandler implements Handler.Callback { } } + public boolean isInBatchInput() { + return mInBatchInput; + } + /** * Fetch suggestions corresponding to an update of a batch input. * @param batchPointers the updated pointers, including the part that was passed last time. diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 3cdd07361..a0793b133 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -240,6 +240,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (TextUtils.isEmpty(importantNoticeTitle)) { return false; } + if (isShowingMoreSuggestionPanel()) { + dismissMoreSuggestionsPanel(); + } mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, importantNoticeTitle); mStripVisibilityGroup.showImportantNoticeStrip(); mImportantNoticeStrip.setOnClickListener(this); diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java index ee9718ad3..ed502ed3d 100644 --- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java @@ -47,8 +47,13 @@ public class ExecutorUtils { public static void shutdownAllExecutors() { synchronized(sExecutorMap) { for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) { - executor.shutdown(); - sExecutorMap.remove(executor); + executor.execute(new Runnable() { + @Override + public void run() { + executor.shutdown(); + sExecutorMap.remove(executor); + } + }); } } } diff --git a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java b/java/src/com/android/inputmethod/latin/utils/RunInLocale.java index 2c9e3b191..1ea16e6ef 100644 --- a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java +++ b/java/src/com/android/inputmethod/latin/utils/RunInLocale.java @@ -30,25 +30,23 @@ public abstract class RunInLocale<T> { * Execute {@link #job(Resources)} method in specified system locale exclusively. * * @param res the resources to use. - * @param newLocale the locale to change to. + * @param newLocale the locale to change to. Run in system locale if null. * @return the value returned from {@link #job(Resources)}. */ public T runInLocale(final Resources res, final Locale newLocale) { synchronized (sLockForRunInLocale) { final Configuration conf = res.getConfiguration(); - final Locale oldLocale = conf.locale; - final boolean needsChange = (newLocale != null && !newLocale.equals(oldLocale)); + if (newLocale == null || newLocale.equals(conf.locale)) { + return job(res); + } + final Locale savedLocale = conf.locale; try { - if (needsChange) { - conf.locale = newLocale; - res.updateConfiguration(conf, null); - } + conf.locale = newLocale; + res.updateConfiguration(conf, null); return job(res); } finally { - if (needsChange) { - conf.locale = oldLocale; - res.updateConfiguration(conf, null); - } + conf.locale = savedLocale; + res.updateConfiguration(conf, null); } } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b1f54c0b4..d907dd1b0 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -1362,7 +1362,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey, isPasswordView, kid.mSupportsSwitchingToShortcutIme, kid.mHasShortcutKey, kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth, - keyboard.mOccupiedHeight, keyboard.getKeys()); + keyboard.mOccupiedHeight, keyboard.getSortedKeys()); } /** |