diff options
Diffstat (limited to 'java/src')
72 files changed, 668 insertions, 1391 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 9f7caa47e..bbda9f8e2 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -28,7 +28,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java index 1fb597ba6..b78c357ab 100644 --- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java @@ -16,42 +16,33 @@ package com.android.inputmethod.compat; +import android.annotation.TargetApi; import android.content.Context; -import android.provider.UserDictionary.Words; +import android.os.Build; +import android.provider.UserDictionary; -import java.lang.reflect.Method; import java.util.Locale; public final class UserDictionaryCompatUtils { - // UserDictionary.Words#addWord(Context, String, int, String, Locale) was introduced - // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). - private static final Method METHOD_addWord = CompatUtils.getMethod(Words.class, "addWord", - Context.class, String.class, int.class, String.class, Locale.class); - @SuppressWarnings("deprecation") public static void addWord(final Context context, final String word, final int freq, final String shortcut, final Locale locale) { - if (hasNewerAddWord()) { - CompatUtils.invoke(Words.class, null, METHOD_addWord, context, word, freq, shortcut, - locale); - } else { - // Fall back to the pre-JellyBean method. - final int localeType; - if (null == locale) { - localeType = Words.LOCALE_TYPE_ALL; - } else { - final Locale currentLocale = context.getResources().getConfiguration().locale; - if (locale.equals(currentLocale)) { - localeType = Words.LOCALE_TYPE_CURRENT; - } else { - localeType = Words.LOCALE_TYPE_ALL; - } - } - Words.addWord(context, word, freq, localeType); + if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + addWordWithShortcut(context, word, freq, shortcut, locale); + return; } + // Fall back to the pre-JellyBean method. + final Locale currentLocale = context.getResources().getConfiguration().locale; + final int localeType = currentLocale.equals(locale) + ? UserDictionary.Words.LOCALE_TYPE_CURRENT : UserDictionary.Words.LOCALE_TYPE_ALL; + UserDictionary.Words.addWord(context, word, freq, localeType); } - public static final boolean hasNewerAddWord() { - return null != METHOD_addWord; + // {@link UserDictionary.Words#addWord(Context,String,int,String,Locale)} was introduced + // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private static void addWordWithShortcut(final Context context, final String word, + final int freq, final String shortcut, final Locale locale) { + UserDictionary.Words.addWord(context, word, freq, shortcut, locale); } } diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index 92bcda455..a1226dc93 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -19,7 +19,7 @@ package com.android.inputmethod.event; import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import javax.annotation.Nonnull; diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 04a0f1e13..6b2094b9e 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -38,7 +38,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardRow; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index ac66f7c6a..cdd632bc8 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; public interface KeyboardActionListener { /** diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 3bcce4f69..89acc3cd3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -110,7 +110,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mThemeContext, editorInfo); final Resources res = mThemeContext.getResources(); final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 6feb1e7ad..b07693c76 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -41,6 +41,8 @@ import com.android.inputmethod.latin.utils.TypefaceUtils; import java.util.HashSet; +import javax.annotation.Nullable; + /** * A view that renders a virtual {@link Keyboard}. * @@ -557,9 +559,10 @@ public class KeyboardView extends View { * @param key key in the attached {@link Keyboard}. * @see #invalidateAllKeys */ - public void invalidateKey(final Key key) { - if (mInvalidateAllKeys) return; - if (key == null) return; + public void invalidateKey(@Nullable final Key key) { + if (key == null || mInvalidateAllKeys) { + return; + } mInvalidatedKeys.add(key); final int x = key.getX() + getPaddingLeft(); final int y = key.getY() + getPaddingTop(); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index e66523be0..f23db04f3 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -39,7 +39,7 @@ import android.view.ViewGroup; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate; import com.android.inputmethod.annotations.ExternallyReferenced; -import com.android.inputmethod.keyboard.internal.DrawingHandler; +import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView; import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview; import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview; @@ -56,14 +56,17 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; import java.util.Locale; import java.util.WeakHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * A view that is responsible for detecting key presses and touch movements. * @@ -108,7 +111,7 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration */ public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy, - MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks { + MoreKeysPanel.Controller { private static final String TAG = MainKeyboardView.class.getSimpleName(); /** Listener for {@link KeyboardActionListener}. */ @@ -164,11 +167,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final KeyDetector mKeyDetector; private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; - private final TimerHandler mKeyTimerHandler; + private final TimerHandler mTimerHandler; private final int mLanguageOnSpacebarHorizontalMargin; - private final DrawingHandler mDrawingHandler = new DrawingHandler(this); - private MainKeyboardAccessibilityDelegate mAccessibilityDelegate; public MainKeyboardView(final Context context, final AttributeSet attrs) { @@ -178,7 +179,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs); + final DrawingPreviewPlacerView drawingPreviewPlacerView = + new DrawingPreviewPlacerView(context, attrs); final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); @@ -186,7 +188,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); - mKeyTimerHandler = new TimerHandler( + mTimerHandler = new TimerHandler( this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime); final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( @@ -196,7 +198,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mKeyDetector = new KeyDetector( keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); - PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */); + PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final boolean forceNonDistinctMultitouch = prefs.getBoolean( @@ -246,15 +248,17 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview( mainKeyboardViewAttr); - mGestureFloatingTextDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mGestureFloatingTextDrawingPreview.setDrawingView(drawingPreviewPlacerView); mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(mainKeyboardViewAttr); - mGestureTrailsDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mGestureTrailsDrawingPreview.setDrawingView(drawingPreviewPlacerView); mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(mainKeyboardViewAttr); - mSlidingKeyInputDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mSlidingKeyInputDrawingPreview.setDrawingView(drawingPreviewPlacerView); mainKeyboardViewAttr.recycle(); + mDrawingPreviewPlacerView = drawingPreviewPlacerView; + final LayoutInflater inflater = LayoutInflater.from(getContext()); mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null); mMoreKeysKeyboardForActionContainer = inflater.inflate( @@ -307,17 +311,24 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack animatorToStart.setCurrentPlayTime(startTime); } - // Implements {@link TimerHander.Callbacks} method. - @Override - public void startWhileTypingFadeinAnimation() { - cancelAndStartAnimators( - mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); - } - + // Implements {@link DrawingProxy#startWhileTypingAnimation(int)}. + /** + * Called when a while-typing-animation should be started. + * @param fadeInOrOut {@link DrawingProxy#FADE_IN} starts while-typing-fade-in animation. + * {@link DrawingProxy#FADE_OUT} starts while-typing-fade-out animation. + */ @Override - public void startWhileTypingFadeoutAnimation() { - cancelAndStartAnimators( - mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); + public void startWhileTypingAnimation(final int fadeInOrOut) { + switch (fadeInOrOut) { + case DrawingProxy.FADE_IN: + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); + break; + case DrawingProxy.FADE_OUT: + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); + break; + } } @ExternallyReferenced @@ -379,7 +390,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void setKeyboard(final Keyboard keyboard) { // Remove any pending messages, except dismissing preview and key repeat. - mKeyTimerHandler.cancelLongPressTimers(); + mTimerHandler.cancelLongPressTimers(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); @@ -451,17 +462,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack windowContentView.addView(mDrawingPreviewPlacerView); } - // Implements {@link DrawingHandler.Callbacks} method. @Override - public void dismissAllKeyPreviews() { - mKeyPreviewChoreographer.dismissAllKeyPreviews(); - PointerTracker.setReleasedKeyGraphicsToAllKeys(); - } - - @Override - public void showKeyPreview(final Key key) { + public void showKeyPreview(@Nonnull final Key key) { // If the key is invalid or has no key preview, we must not show key preview. - if (key == null || key.noKeyPreview()) { + if (key.noKeyPreview()) { return; } final Keyboard keyboard = getKeyboard(); @@ -480,22 +484,21 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack getWidth(), mOriginCoords, mDrawingPreviewPlacerView, isHardwareAccelerated()); } - // Implements {@link TimerHandler.Callbacks} method. + // Implements {@link DrawingProxy#dismissKeyPreviewWithoutDelay(Key)}. @Override - public void dismissKeyPreviewWithoutDelay(final Key key) { + public void dismissKeyPreviewWithoutDelay(@Nonnull final Key key) { mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */); - // To redraw key top letter. invalidateKey(key); } @Override - public void dismissKeyPreview(final Key key) { - if (!isHardwareAccelerated()) { - // TODO: Implement preference option to control key preview method and duration. - mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key); + public void dismissKeyPreview(@Nonnull final Key key) { + if (isHardwareAccelerated()) { + mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */); return; } - mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */); + // TODO: Implement preference option to control key preview method and duration. + mTimerHandler.postDismissKeyPreview(key, mKeyPreviewDrawParams.getLingerTimeout()); } public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { @@ -503,14 +506,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } @Override - public void showSlidingKeyInputPreview(final PointerTracker tracker) { + public void showSlidingKeyInputPreview(@Nullable final PointerTracker tracker) { locatePreviewPlacerView(); - mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker); - } - - @Override - public void dismissSlidingKeyInputPreview() { - mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); + if (tracker != null) { + mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker); + } else { + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); + } } private void setGesturePreviewMode(final boolean isGestureTrailEnabled, @@ -519,20 +521,26 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled); } - // Implements {@link DrawingHandler.Callbacks} method. - @Override - public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { + public void showGestureFloatingPreviewText(@Nonnull final SuggestedWords suggestedWords, + final boolean dismissDelayed) { locatePreviewPlacerView(); - mGestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords); + final GestureFloatingTextDrawingPreview gestureFloatingTextDrawingPreview = + mGestureFloatingTextDrawingPreview; + gestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords); + if (dismissDelayed) { + mTimerHandler.postDismissGestureFloatingPreviewText( + mGestureFloatingPreviewTextLingerTimeout); + } } - public void dismissGestureFloatingPreviewText() { - locatePreviewPlacerView(); - mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout); + // Implements {@link DrawingProxy#dismissGestureFloatingPreviewTextWithoutDelay()}. + @Override + public void dismissGestureFloatingPreviewTextWithoutDelay() { + mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText(); } @Override - public void showGestureTrail(final PointerTracker tracker, + public void showGestureTrail(@Nonnull final PointerTracker tracker, final boolean showsFloatingPreviewText) { locatePreviewPlacerView(); if (showsFloatingPreviewText) { @@ -599,13 +607,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return moreKeysKeyboardView; } - // Implements {@link TimerHandler.Callbacks} method. + // Implements {@link DrawingProxy@onLongPress(PointerTracker)}. /** * Called when a key is long pressed. * @param tracker the pointer tracker which pressed the parent key */ @Override - public void onLongPress(final PointerTracker tracker) { + public void onLongPress(@Nonnull final PointerTracker tracker) { if (isShowingMoreKeysPanel()) { return; } @@ -660,7 +668,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); tracker.onShowMoreKeysPanel(moreKeysPanel); // TODO: Implement zoom in animation of more keys panel. - dismissKeyPreviewWithoutDelay(key); + mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */); } public boolean isInDraggingFinger() { @@ -673,6 +681,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void onShowMoreKeysPanel(final MoreKeysPanel panel) { locatePreviewPlacerView(); + // Dismiss another {@link MoreKeysPanel} that may be being showed. + onDismissMoreKeysPanel(); + // Dismiss all key previews that may be being showed. + PointerTracker.setReleasedKeyGraphicsToAllKeys(); + // Dismiss sliding key input preview that may be being showed. + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); panel.showInParent(mDrawingPreviewPlacerView); mMoreKeysPanel = panel; } @@ -695,15 +709,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public void startDoubleTapShiftKeyTimer() { - mKeyTimerHandler.startDoubleTapShiftKeyTimer(); + mTimerHandler.startDoubleTapShiftKeyTimer(); } public void cancelDoubleTapShiftKeyTimer() { - mKeyTimerHandler.cancelDoubleTapShiftKeyTimer(); + mTimerHandler.cancelDoubleTapShiftKeyTimer(); } public boolean isInDoubleTapShiftKeyTimeout() { - return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout(); + return mTimerHandler.isInDoubleTapShiftKeyTimeout(); } @Override @@ -712,9 +726,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return false; } if (mNonDistinctMultitouchHelper != null) { - if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { + if (me.getPointerCount() > 1 && mTimerHandler.isInKeyRepeat()) { // Key repeating timer will be canceled if 2 or more keys are in action. - mKeyTimerHandler.cancelKeyRepeatTimers(); + mTimerHandler.cancelKeyRepeatTimers(); } // Non distinct multitouch screen support mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector); @@ -738,11 +752,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public void cancelAllOngoingEvents() { - mKeyTimerHandler.cancelAllMessages(); - mDrawingHandler.cancelAllMessages(); - dismissAllKeyPreviews(); - dismissGestureFloatingPreviewText(); - dismissSlidingKeyInputPreview(); + mTimerHandler.cancelAllMessages(); + PointerTracker.setReleasedKeyGraphicsToAllKeys(); + mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText(); + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); PointerTracker.dismissAllMoreKeysPanels(); PointerTracker.cancelAllPointerTrackers(); } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index abcfff8a6..f0de86ff9 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -24,7 +24,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreKeysKeyboard extends Keyboard { diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index fe6270fb5..467f5150a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -31,9 +31,9 @@ import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingPoints; import com.android.inputmethod.keyboard.internal.GestureStrokeRecognitionParams; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.keyboard.internal.TypingTimeRecorder; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -41,6 +41,9 @@ import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public final class PointerTracker implements PointerTrackerQueue.Element, BatchInputArbiterListener { private static final String TAG = PointerTracker.class.getSimpleName(); @@ -50,12 +53,18 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT; public interface DrawingProxy { - public void invalidateKey(Key key); - public void showKeyPreview(Key key); - public void dismissKeyPreview(Key key); - public void showSlidingKeyInputPreview(PointerTracker tracker); - public void dismissSlidingKeyInputPreview(); - public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText); + public void invalidateKey(@Nullable Key key); + public void showKeyPreview(@Nonnull Key key); + public void dismissKeyPreview(@Nonnull Key key); + public void dismissKeyPreviewWithoutDelay(@Nonnull Key key); + public void onLongPress(@Nonnull PointerTracker tracker); + public static final int FADE_IN = 0; + public static final int FADE_OUT = 1; + public void startWhileTypingAnimation(final int fadeInOrOut); + public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker); + public void showGestureTrail(@Nonnull PointerTracker tracker, + boolean showsFloatingPreviewText); + public void dismissGestureFloatingPreviewTextWithoutDelay(); } public interface TimerProxy { @@ -163,6 +172,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, // The position and time at which first down event occurred. private long mDownTime; + @Nonnull private int[] mDownCoordinates = CoordinateUtils.newInstance(); private long mUpTime; @@ -416,6 +426,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mIsInDraggingFinger; } + @Nullable public Key getKey() { return mCurrentKey; } @@ -429,12 +440,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mKeyDetector.detectHitKey(x, y); } - private void setReleasedKeyGraphics(final Key key) { - sDrawingProxy.dismissKeyPreview(key); + private void setReleasedKeyGraphics(@Nullable final Key key) { if (key == null) { return; } + sDrawingProxy.dismissKeyPreview(key); // Even if the key is disabled, update the key release graphics just in case. updateReleaseKeyGraphics(key); @@ -518,7 +529,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mGestureStrokeDrawingPoints; } - public void getLastCoordinates(final int[] outCoords) { + public void getLastCoordinates(@Nonnull final int[] outCoords) { CoordinateUtils.set(outCoords, mLastX, mLastY); } @@ -526,7 +537,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mDownTime; } - public void getDownCoordinates(final int[] outCoords) { + public void getDownCoordinates(@Nonnull final int[] outCoords) { CoordinateUtils.copy(outCoords, mDownCoordinates); } @@ -765,7 +776,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void resetKeySelectionByDraggingFinger() { mIsInDraggingFinger = false; mIsInSlidingKeyInput = false; - sDrawingProxy.dismissSlidingKeyInputPreview(); + sDrawingProxy.showSlidingKeyInputPreview(null /* tracker */); } private void onGestureMoveEvent(final int x, final int y, final long eventTime, diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java index c8e832b93..a9711aed2 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -30,7 +30,6 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.Settings; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java index 755a99a2c..c76a9aca4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java @@ -22,6 +22,8 @@ import android.view.View; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.PointerTracker; +import javax.annotation.Nonnull; + /** * Abstract base class for previews that are drawn on DrawingPreviewPlacerView, e.g., * GestureFloatingTextDrawingPreview, GestureTrailsDrawingPreview, and @@ -32,7 +34,7 @@ public abstract class AbstractDrawingPreview { private boolean mPreviewEnabled; private boolean mHasValidGeometry; - public void setDrawingView(final DrawingPreviewPlacerView drawingView) { + public void setDrawingView(@Nonnull final DrawingPreviewPlacerView drawingView) { mDrawingView = drawingView; drawingView.addPreview(this); } @@ -61,7 +63,7 @@ public abstract class AbstractDrawingPreview { * @param width the width of {@link MainKeyboardView}. * @param height the height of {@link MainKeyboardView}. */ - public void setKeyboardViewGeometry(final int[] originCoords, final int width, + public void setKeyboardViewGeometry(@Nonnull final int[] originCoords, final int width, final int height) { mHasValidGeometry = (width > 0 && height > 0); } @@ -72,11 +74,11 @@ public abstract class AbstractDrawingPreview { * Draws the preview * @param canvas The canvas where the preview is drawn. */ - public abstract void drawPreview(final Canvas canvas); + public abstract void drawPreview(@Nonnull final Canvas canvas); /** * Set the position of the preview. * @param tracker The new location of the preview is based on the points in PointerTracker. */ - public abstract void setPreviewPosition(final PointerTracker tracker); + public abstract void setPreviewPosition(@Nonnull final PointerTracker tracker); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java b/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java index efb365e42..77d0e7a90 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java +++ b/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; /** * This class arbitrates batch input. diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java index 5797b7efd..2e2ed52dd 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java @@ -17,7 +17,7 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import android.text.TextUtils; diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java deleted file mode 100644 index 1a55359f5..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2013 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 com.android.inputmethod.keyboard.internal; - -import android.os.Message; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; - -import javax.annotation.Nonnull; - -// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so. -public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { - public interface Callbacks { - public void dismissKeyPreviewWithoutDelay(Key key); - public void dismissAllKeyPreviews(); - public void showGestureFloatingPreviewText(SuggestedWords suggestedWords); - } - - private static final int MSG_DISMISS_KEY_PREVIEW = 0; - private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; - - public DrawingHandler(@Nonnull final Callbacks ownerInstance) { - super(ownerInstance); - } - - @Override - public void handleMessage(final Message msg) { - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { - return; - } - switch (msg.what) { - case MSG_DISMISS_KEY_PREVIEW: - callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj); - break; - case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: - callbacks.showGestureFloatingPreviewText(SuggestedWords.getEmptyInstance()); - break; - } - } - - public void dismissKeyPreview(final long delay, final Key key) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); - } - - private void cancelAllDismissKeyPreviews() { - removeMessages(MSG_DISMISS_KEY_PREVIEW); - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { - return; - } - callbacks.dismissAllKeyPreviews(); - } - - public void dismissGestureFloatingPreviewText(final long delay) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); - } - - public void cancelAllMessages() { - cancelAllDismissKeyPreviews(); - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java index 37ea0f17b..330ec52f0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java @@ -29,6 +29,8 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.CoordinateUtils; +import javax.annotation.Nonnull; + /** * The class for single gesture preview text. The class for multiple gesture preview text will be * derived from it. @@ -110,7 +112,11 @@ public class GestureFloatingTextDrawingPreview extends AbstractDrawingPreview { // Nothing to do here. } - public void setSuggetedWords(final SuggestedWords suggestedWords) { + public void dismissGestureFloatingPreviewText() { + setSuggetedWords(SuggestedWords.getEmptyInstance()); + } + + public void setSuggetedWords(@Nonnull final SuggestedWords suggestedWords) { if (!isPreviewEnabled()) { return; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java index 7d09e9d2f..07ef00924 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds drawing points to represent a gesture stroke on the screen. diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java index 99ec18f4d..3e901114a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java @@ -18,9 +18,9 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds event points to recognize a gesture stroke. diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java index 67683e247..4d998e443 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java @@ -24,7 +24,7 @@ import android.graphics.Rect; import android.os.SystemClock; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds drawing points to represent a gesture trail. The gesture trail may contain diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index ddb193ead..d3764877c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -28,7 +28,6 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayDeque; import java.util.HashMap; -import java.util.HashSet; /** * This class controls pop up key previews. This class decides: @@ -69,12 +68,6 @@ public final class KeyPreviewChoreographer { return mShowingKeyPreviewViews.containsKey(key); } - public void dismissAllKeyPreviews() { - for (final Key key : new HashSet<>(mShowingKeyPreviewViews.keySet())) { - dismissKeyPreview(key, false /* withAnimation */); - } - } - public void dismissKeyPreview(final Key key, final boolean withAnimation) { if (key == null) { return; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index d33e53a61..63aab968b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -20,7 +20,7 @@ import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT; import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; /** * The string parser of the key specification. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 6c75fb6c1..c739bf3e0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -34,8 +34,8 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardTheme; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.ResourceUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.XmlParseUtils; import com.android.inputmethod.latin.utils.XmlParseUtils.ParseException; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index 4392dbcbd..a0bb406aa 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -22,9 +22,9 @@ import android.util.SparseIntArray; import com.android.inputmethod.compat.CharacterCompat; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java index 076abbf4d..456522535 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -22,33 +22,29 @@ import android.view.ViewConfiguration; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; -import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import javax.annotation.Nonnull; -// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. -public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { - public interface Callbacks { - public void startWhileTypingFadeinAnimation(); - public void startWhileTypingFadeoutAnimation(); - public void onLongPress(PointerTracker tracker); - } - +public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy> + implements TimerProxy { private static final int MSG_TYPING_STATE_EXPIRED = 0; private static final int MSG_REPEAT_KEY = 1; private static final int MSG_LONGPRESS_KEY = 2; private static final int MSG_LONGPRESS_SHIFT_KEY = 3; private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 4; private static final int MSG_UPDATE_BATCH_INPUT = 5; + private static final int MSG_DISMISS_KEY_PREVIEW = 6; + private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 7; private final int mIgnoreAltCodeKeyTimeout; private final int mGestureRecognitionUpdateTime; - public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, - final int gestureRecognitionUpdateTime) { + public TimerHandler(@Nonnull final DrawingProxy ownerInstance, + final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) { super(ownerInstance); mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime; @@ -56,26 +52,35 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple @Override public void handleMessage(final Message msg) { - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { + final DrawingProxy drawingProxy = getOwnerInstance(); + if (drawingProxy == null) { return; } - final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_TYPING_STATE_EXPIRED: - callbacks.startWhileTypingFadeinAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN); break; case MSG_REPEAT_KEY: - tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); + final PointerTracker tracker1 = (PointerTracker) msg.obj; + tracker1.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); break; case MSG_LONGPRESS_KEY: case MSG_LONGPRESS_SHIFT_KEY: cancelLongPressTimers(); - callbacks.onLongPress(tracker); + final PointerTracker tracker2 = (PointerTracker) msg.obj; + drawingProxy.onLongPress(tracker2); break; case MSG_UPDATE_BATCH_INPUT: - tracker.updateBatchInputByTimer(SystemClock.uptimeMillis()); - startUpdateBatchInputTimer(tracker); + final PointerTracker tracker3 = (PointerTracker) msg.obj; + tracker3.updateBatchInputByTimer(SystemClock.uptimeMillis()); + startUpdateBatchInputTimer(tracker3); + break; + case MSG_DISMISS_KEY_PREVIEW: + final Key key = (Key) msg.obj; + drawingProxy.dismissKeyPreviewWithoutDelay(key); + break; + case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: + drawingProxy.dismissGestureFloatingPreviewTextWithoutDelay(); break; } } @@ -141,8 +146,8 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple final boolean isTyping = isTypingState(); removeMessages(MSG_TYPING_STATE_EXPIRED); - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { + final DrawingProxy drawingProxy = getOwnerInstance(); + if (drawingProxy == null) { return; } @@ -150,7 +155,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple final int typedCode = typedKey.getCode(); if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { if (isTyping) { - callbacks.startWhileTypingFadeinAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN); } return; } @@ -160,7 +165,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple if (isTyping) { return; } - callbacks.startWhileTypingFadeoutAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_OUT); } @Override @@ -215,8 +220,18 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple removeMessages(MSG_UPDATE_BATCH_INPUT); } + public void postDismissKeyPreview(@Nonnull final Key key, final long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); + } + + public void postDismissGestureFloatingPreviewText(final long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); + } + public void cancelAllMessages() { cancelAllKeyTimers(); cancelAllUpdateBatchInputTimers(); + removeMessages(MSG_DISMISS_KEY_PREVIEW); + removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 76459f817..b5d0b446f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -21,9 +21,11 @@ import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; @@ -33,7 +35,6 @@ import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.JniUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; @@ -261,8 +262,8 @@ public final class BinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { @@ -273,12 +274,13 @@ public final class BinaryDictionary extends Dictionary { Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE); ngramContext.outputToArray(session.mPrevWordCodePointArrays, session.mIsBeginningOfSentenceArray); - final InputPointers inputPointers = composer.getInputPointers(); - final boolean isGesture = composer.isBatchMode(); + final InputPointers inputPointers = composedData.mInputPointers; + final boolean isGesture = composedData.mIsBatchMode; final int inputSize; if (!isGesture) { - inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - session.mInputCodePoints); + inputSize = + composedData.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + session.mInputCodePoints); if (inputSize < 0) { return null; } @@ -302,7 +304,7 @@ public final class BinaryDictionary extends Dictionary { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL; } // TOOD: Pass multiple previous words information for n-gram. - getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), + getSuggestionsNative(mNativeDict, proximityInfoHandle, getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), inputPointers.getYCoordinates(), inputPointers.getTimes(), inputPointers.getPointerIds(), session.mInputCodePoints, inputSize, diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 08e1983d4..59763c0fc 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -30,9 +30,9 @@ import android.util.Log; import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.personalization.AccountUtils; import com.android.inputmethod.latin.utils.ExecutorUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.io.File; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 28a62b283..7d7ed77e7 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -17,8 +17,8 @@ package com.android.inputmethod.latin; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -87,9 +87,9 @@ public abstract class Dictionary { /** * Searches for suggestions for a given context. - * @param composer the key sequence to match with coordinate info, as a WordComposer + * @param composedData the key sequence to match with coordinate info * @param ngramContext the context for n-gram. - * @param proximityInfo the object for key proximity. May be ignored by some implementations. + * @param proximityInfoHandle the handle for key proximity. Is ignored by some implementations. * @param settingsValuesForSuggestion the settings values used for the suggestion. * @param sessionId the session id. * @param weightForLocale the weight given to this locale, to multiply the output scores for @@ -99,8 +99,8 @@ public abstract class Dictionary { * a float array that has only one element. This can be updated when a different value is used. * @return the list of suggestions (possibly null if none) */ - abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + abstract public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel); @@ -203,8 +203,8 @@ public abstract class Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index a6d7205e2..96575f629 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -18,8 +18,8 @@ package com.android.inputmethod.latin; import android.util.Log; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -59,8 +59,8 @@ public final class DictionaryCollection extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { @@ -68,15 +68,15 @@ public final class DictionaryCollection extends Dictionary { if (dictionaries.isEmpty()) return null; // To avoid creating unnecessary objects, we get the list out of the first // dictionary and add the rest to it if not null, hence the get(0) - ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer, - ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId, + ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composedData, + ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (null == suggestions) suggestions = new ArrayList<>(); final int length = dictionaries.size(); for (int i = 1; i < length; ++ i) { - final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer, - ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId, - weightForLocale, inOutWeightOfLangModelVsSpatialModel); + final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions( + composedData, ngramContext, proximityInfoHandle, settingsValuesForSuggestion, + sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (null != sugg) suggestions.addAll(sugg); } return suggestions; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 4a22cde7b..d23639a0d 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -23,7 +23,6 @@ import android.util.Pair; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.ExpandableBinaryDictionary.UpdateEntriesForInputEventsCallback; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -683,7 +682,7 @@ public class DictionaryFacilitator { // TODO: Revise the way to fusion suggestion results. public SuggestionResults getSuggestionResults(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults( @@ -698,8 +697,8 @@ public class DictionaryFacilitator { ? dictionaryGroup.mWeightForGesturingInLocale : dictionaryGroup.mWeightForTypingInLocale; final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, + dictionary.getSuggestions(composer.getComposedDataSnapshot(), ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, weightForLocale, weightOfLangModelVsSpatialModel); if (null == dictionarySuggestions) continue; suggestionResults.addAll(dictionarySuggestions); diff --git a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java new file mode 100644 index 000000000..8116a4983 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014, 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 com.android.inputmethod.latin; + +import android.view.KeyEvent; + +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.settings.Settings; + +/** + * A class for detecting Emoji-Alt physical key. + */ +final class EmojiAltPhysicalKeyDetector { + // True if the Alt key has been used as a modifier. In this case the Alt key up isn't + // recognized as an emoji key. + private boolean mAltHasBeenUsedAsAModifier; + + /** + * Record a down key event. + * @param keyEvent a down key event. + */ + public void onKeyDown(final KeyEvent keyEvent) { + if (isAltKey(keyEvent)) { + mAltHasBeenUsedAsAModifier = false; + } + if (containsAltModifier(keyEvent)) { + mAltHasBeenUsedAsAModifier = true; + } + } + + /** + * Determine whether an up key event is a special key up or not. + * @param keyEvent an up key event. + */ + public void onKeyUp(final KeyEvent keyEvent) { + if (keyEvent.isCanceled()) { + // This key up event was a part of key combinations and should be ignored. + return; + } + if (!isAltKey(keyEvent)) { + mAltHasBeenUsedAsAModifier |= containsAltModifier(keyEvent); + return; + } + if (containsAltModifier(keyEvent)) { + mAltHasBeenUsedAsAModifier = true; + return; + } + if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) { + return; + } + if (!mAltHasBeenUsedAsAModifier) { + onEmojiAltKeyDetected(); + } + } + + private static void onEmojiAltKeyDetected() { + KeyboardSwitcher.getInstance().onToggleEmojiKeyboard(); + } + + private static boolean isAltKey(final KeyEvent keyEvent) { + final int keyCode = keyEvent.getKeyCode(); + return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; + } + + private static boolean containsAltModifier(final KeyEvent keyEvent) { + final int metaState = keyEvent.getMetaState(); + // TODO: Support multiple keyboards. Take device id into account. + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_ALT_LEFT: + // Return true if Left-Alt is pressed with Right-Alt pressed. + return (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0; + case KeyEvent.KEYCODE_ALT_RIGHT: + // Return true if Right-Alt is pressed with Left-Alt pressed. + return (metaState & KeyEvent.META_ALT_LEFT_ON) != 0; + default: + return (metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 702d1536a..bbffece57 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -20,8 +20,8 @@ import android.content.Context; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; @@ -480,8 +480,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { reloadDictionaryIfRequired(); @@ -494,9 +494,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return null; } final ArrayList<SuggestedWordInfo> suggestions = - mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, weightForLocale, - inOutWeightOfLangModelVsSpatialModel); + mBinaryDictionary.getSuggestions(composedData, ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (mBinaryDictionary.isCorrupted()) { Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. " + "Remove and regenerate it."); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 002222080..37effeead 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -24,8 +24,8 @@ import android.text.InputType; import android.util.Log; import android.view.inputmethod.EditorInfo; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java deleted file mode 100644 index d57a881c0..000000000 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.inputmethod.latin; - -import android.util.Log; -import android.util.SparseIntArray; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.ResizableIntArray; - -// TODO: This class is not thread-safe. -public final class InputPointers { - private static final String TAG = InputPointers.class.getSimpleName(); - private static final boolean DEBUG_TIME = false; - - private final int mDefaultCapacity; - private final ResizableIntArray mXCoordinates; - private final ResizableIntArray mYCoordinates; - private final ResizableIntArray mPointerIds; - private final ResizableIntArray mTimes; - - public InputPointers(int defaultCapacity) { - mDefaultCapacity = defaultCapacity; - mXCoordinates = new ResizableIntArray(defaultCapacity); - mYCoordinates = new ResizableIntArray(defaultCapacity); - mPointerIds = new ResizableIntArray(defaultCapacity); - mTimes = new ResizableIntArray(defaultCapacity); - } - - private void fillWithLastTimeUntil(final int index) { - final int fromIndex = mTimes.getLength(); - // Fill the gap with the latest time. - // See {@link #getTime(int)} and {@link #isValidTimeStamps()}. - if (fromIndex <= 0) { - return; - } - final int fillLength = index - fromIndex + 1; - if (fillLength <= 0) { - return; - } - final int lastTime = mTimes.get(fromIndex - 1); - mTimes.fill(lastTime, fromIndex, fillLength); - } - - public void addPointerAt(int index, int x, int y, int pointerId, int time) { - mXCoordinates.addAt(index, x); - mYCoordinates.addAt(index, y); - mPointerIds.addAt(index, pointerId); - if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { - fillWithLastTimeUntil(index); - } - mTimes.addAt(index, time); - } - - @UsedForTesting - void addPointer(int x, int y, int pointerId, int time) { - mXCoordinates.add(x); - mYCoordinates.add(y); - mPointerIds.add(pointerId); - mTimes.add(time); - } - - public void set(InputPointers ip) { - mXCoordinates.set(ip.mXCoordinates); - mYCoordinates.set(ip.mYCoordinates); - mPointerIds.set(ip.mPointerIds); - mTimes.set(ip.mTimes); - } - - public void copy(InputPointers ip) { - mXCoordinates.copy(ip.mXCoordinates); - mYCoordinates.copy(ip.mYCoordinates); - mPointerIds.copy(ip.mPointerIds); - mTimes.copy(ip.mTimes); - } - - /** - * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray} - * to the end of this. - * @param pointerId the pointer id of the source. - * @param times the source {@link ResizableIntArray} to read the event times from. - * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from. - * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from. - * @param startPos the starting index of the data in {@code times} and etc. - * @param length the number of data to be appended. - */ - public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates, - ResizableIntArray yCoordinates, int startPos, int length) { - if (length == 0) { - return; - } - mXCoordinates.append(xCoordinates, startPos, length); - mYCoordinates.append(yCoordinates, startPos, length); - mPointerIds.fill(pointerId, mPointerIds.getLength(), length); - mTimes.append(times, startPos, length); - } - - /** - * Shift to the left by elementCount, discarding elementCount pointers at the start. - * @param elementCount how many elements to shift. - */ - public void shift(final int elementCount) { - mXCoordinates.shift(elementCount); - mYCoordinates.shift(elementCount); - mPointerIds.shift(elementCount); - mTimes.shift(elementCount); - } - - public void reset() { - final int defaultCapacity = mDefaultCapacity; - mXCoordinates.reset(defaultCapacity); - mYCoordinates.reset(defaultCapacity); - mPointerIds.reset(defaultCapacity); - mTimes.reset(defaultCapacity); - } - - public int getPointerSize() { - return mXCoordinates.getLength(); - } - - public int[] getXCoordinates() { - return mXCoordinates.getPrimitiveArray(); - } - - public int[] getYCoordinates() { - return mYCoordinates.getPrimitiveArray(); - } - - public int[] getPointerIds() { - return mPointerIds.getPrimitiveArray(); - } - - /** - * Gets the time each point was registered, in milliseconds, relative to the first event in the - * sequence. - * @return The time each point was registered, in milliseconds, relative to the first event in - * the sequence. - */ - public int[] getTimes() { - if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { - if (!isValidTimeStamps()) { - throw new RuntimeException("Time stamps are invalid."); - } - } - return mTimes.getPrimitiveArray(); - } - - @Override - public String toString() { - return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes - + " x=" + mXCoordinates + " y=" + mYCoordinates; - } - - private boolean isValidTimeStamps() { - final int[] times = mTimes.getPrimitiveArray(); - final int[] pointerIds = mPointerIds.getPrimitiveArray(); - final SparseIntArray lastTimeOfPointers = new SparseIntArray(); - final int size = getPointerSize(); - for (int i = 0; i < size; ++i) { - final int pointerId = pointerIds[i]; - final int time = times[i]; - final int lastTime = lastTimeOfPointers.get(pointerId, time); - if (time < lastTime) { - // dump - for (int j = 0; j < size; ++j) { - Log.d(TAG, "--- (" + j + ") " + times[j]); - } - return false; - } - lastTimeOfPointers.put(pointerId, time); - } - return true; - } -} diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index c4c389411..9fcdb2229 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import com.android.inputmethod.event.Event; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 11cbec378..3fa127005 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -77,6 +77,7 @@ import com.android.inputmethod.keyboard.TextDecoratorUi; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.define.ProductionFlags; import com.android.inputmethod.latin.inputlogic.InputLogic; @@ -167,7 +168,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); - private final SpecialKeyDetector mSpecialKeyDetector; + private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector = + new EmojiAltPhysicalKeyDetector(); private StatsUtilsManager mStatsUtilsManager; // Working variable for {@link #startShowingInputView()} and // {@link #onEvaluateInputViewShown()}. @@ -545,7 +547,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSettings = Settings.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); - mSpecialKeyDetector = new SpecialKeyDetector(this); mStatsUtilsManager = StatsUtilsManager.getInstance(); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); @@ -1500,14 +1501,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // This method must run on the UI Thread. - void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, + void showGesturePreviewAndSuggestionStrip(@Nonnull final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { showSuggestionStrip(suggestedWords); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); - if (dismissGestureFloatingPreviewText) { - mainKeyboardView.dismissGestureFloatingPreviewText(); - } + mainKeyboardView.showGestureFloatingPreviewText(suggestedWords, + dismissGestureFloatingPreviewText /* dismissDelayed */); } // Called from PointerTracker through the KeyboardActionListener interface @@ -1765,7 +1764,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyDown(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1786,7 +1786,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyUp(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyUp(keyCode, keyEvent); } diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java index 0ccea4732..82a13274d 100644 --- a/java/src/com/android/inputmethod/latin/NgramContext.java +++ b/java/src/com/android/inputmethod/latin/NgramContext.java @@ -20,7 +20,7 @@ import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index 93598d2fb..a65304cd0 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index bc8bd831c..7b1a53a6e 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -16,8 +16,8 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -50,16 +50,16 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { if (mLock.readLock().tryLock()) { try { - return mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, weightForLocale, - inOutWeightOfLangModelVsSpatialModel); + return mBinaryDictionary.getSuggestions(composedData, ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); } finally { mLock.readLock().unlock(); } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 0763ef807..834f747d9 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -35,6 +35,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; @@ -42,7 +43,6 @@ import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.NgramContextUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SpannableStringUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import javax.annotation.Nonnull; diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 17df1eab4..9b4619d35 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -21,11 +21,11 @@ import android.text.TextUtils; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; import java.util.ArrayList; @@ -140,8 +140,8 @@ public final class Suggest { : typedWord; final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion, - SESSION_ID_TYPING); + wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), + settingsValuesForSuggestion, SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount, @@ -160,24 +160,36 @@ public final class Suggest { || (consideredWord.length() > 1 && !didRemoveTypedWord); final boolean hasAutoCorrection; - // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because - // any attempt to do auto-correction is already shielded with a test for this flag; at the - // same time, it feels wrong that the SuggestedWord object includes information about - // the current settings. It may also be useful to know, when the setting is off, whether - // the word *would* have been auto-corrected. - if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions - || suggestionResults.isEmpty() || wordComposer.hasDigits() - || wordComposer.isMostlyCaps() || wordComposer.isResumed() + // If correction is not enabled, we never auto-correct. This is for example for when + // the setting "Auto-correction" is "off": we still suggest, but we don't auto-correct. + if (!isCorrectionEnabled + // If the word does not allow to be auto-corrected, then we don't auto-correct. + || !allowsToBeAutoCorrected + // If we are doing prediction, then we never auto-correct of course + || resultsArePredictions + // If we don't have suggestion results, we can't evaluate the first suggestion + // for auto-correction + || suggestionResults.isEmpty() + // If the word has digits, we never auto-correct because it's likely the word + // was type with a lot of care + || wordComposer.hasDigits() + // If the word is mostly caps, we never auto-correct because this is almost + // certainly intentional (and careful input) + || wordComposer.isMostlyCaps() + // We never auto-correct when suggestions are resumed because it would be unexpected + || wordComposer.isResumed() + // If we don't have a main dictionary, we never want to auto-correct. The reason + // for this is, the user may have a contact whose name happens to match a valid + // word in their language, and it will unexpectedly auto-correct. For example, if + // the user types in English with no dictionary and has a "Will" in their contact + // list, "will" would always auto-correct to "Will" which is unwanted. Hence, no + // main dict => no auto-correct. Also, it would probably get obnoxious quickly. + // TODO: now that we have personalization, we may want to re-evaluate this decision || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary() + // If the first suggestion is a shortcut we never auto-correct to it, regardless + // of how strong it is (whitelist entries are not KIND_SHORTCUT but KIND_WHITELIST). + // TODO: we may want to have shortcut-only entries auto-correct in the future. || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { - // If we don't have a main dictionary, we never want to auto-correct. The reason for - // this is, the user may have a contact whose name happens to match a valid word in - // their language, and it will unexpectedly auto-correct. For example, if the user - // types in English with no dictionary and has a "Will" in their contact list, "will" - // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no - // auto-correct. - // Also, shortcuts should never auto-correct unless they are whitelist entries. - // TODO: we may want to have shortcut-only entries auto-correct in the future. hasAutoCorrection = false; } else { final SuggestedWordInfo firstSuggestion = suggestionResults.first(); @@ -235,8 +247,8 @@ public final class Suggest { final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion, - SESSION_ID_GESTURE); + wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), + settingsValuesForSuggestion, SESSION_ID_GESTURE); // For transforming words that don't come from a dictionary, because it's our best bet final Locale defaultLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index a6428896e..c51e20f21 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -21,13 +21,15 @@ import android.view.inputmethod.CompletionInfo; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import javax.annotation.Nonnull; + public class SuggestedWords { public static final int INDEX_OF_TYPED_WORD = 0; public static final int INDEX_OF_AUTO_CORRECTION = 1; @@ -46,6 +48,7 @@ public class SuggestedWords { public static final int MAX_SUGGESTIONS = 18; private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); + @Nonnull private static final SuggestedWords EMPTY = new SuggestedWords( EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE); @@ -210,6 +213,7 @@ public class SuggestedWords { return result; } + @Nonnull public static final SuggestedWords getEmptyInstance() { return SuggestedWords.EMPTY; } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 8830521c7..fa55319d2 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -19,10 +19,12 @@ package com.android.inputmethod.latin; import com.android.inputmethod.event.CombinerChain; import com.android.inputmethod.event.Event; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Collections; @@ -89,6 +91,10 @@ public final class WordComposer { refreshTypedWordCache(); } + public ComposedData getComposedDataSnapshot() { + return new ComposedData(getInputPointers(), isBatchMode(), mTypedWordCache.toString()); + } + /** * Restart the combiners, possibly with a new spec. * @param combiningSpec The spec string for combining. This is found in the extra value. @@ -133,38 +139,6 @@ public final class WordComposer { return mCodePointSize; } - /** - * Copy the code points in the typed word to a destination array of ints. - * - * If the array is too small to hold the code points in the typed word, nothing is copied and - * -1 is returned. - * - * @param destination the array of ints. - * @return the number of copied code points. - */ - public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - final int[] destination) { - // This method can be called on a separate thread and mTypedWordCache can change while we - // are executing this method. - final String typedWord = mTypedWordCache.toString(); - // lastIndex is exclusive - final int lastIndex = typedWord.length() - - StringUtils.getTrailingSingleQuotesCount(typedWord); - if (lastIndex <= 0) { - // The string is empty or contains only single quotes. - return 0; - } - - // 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(typedWord, 0, lastIndex); - if (codePointSize > destination.length) { - return -1; - } - return StringUtils.copyCodePointsAndReturnCodePointCount(destination, typedWord, 0, - lastIndex, true /* downCase */); - } - public boolean isSingleLetter() { return size() == 1; } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index a67f46108..bafea178e 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -41,7 +41,6 @@ import com.android.inputmethod.keyboard.TextDecorator; import com.android.inputmethod.keyboard.TextDecoratorUiOperator; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LastComposedWord; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.NgramContext; @@ -52,6 +51,8 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; @@ -61,7 +62,6 @@ import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StatsUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index 5f391dd9b..ddc4ad99c 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -21,10 +21,10 @@ import android.os.HandlerThread; import android.os.Message; import com.android.inputmethod.compat.LooperCompatUtils; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; +import com.android.inputmethod.latin.common.InputPointers; /** * A helper to manage deferred tasks for the input logic. diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 78d79ae50..5925bdc4e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -171,14 +171,17 @@ public final class FormatSpec { // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType(). public static final int VERSION2 = 2; public static final int VERSION201 = 201; + public static final int VERSION202 = 202; public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201; // Dictionary version used for testing. public static final int VERSION4_ONLY_FOR_TESTING = 399; public static final int VERSION401 = 401; public static final int VERSION4 = 402; public static final int VERSION4_DEV = 403; - static final int MINIMUM_SUPPORTED_VERSION = VERSION2; - static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV; + static final int MINIMUM_SUPPORTED_STATIC_VERSION = VERSION202; + static final int MAXIMUM_SUPPORTED_STATIC_VERSION = VERSION202; + static final int MINIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4; + static final int MAXIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4_DEV; // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index e7808e46e..388d57816 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -21,8 +21,8 @@ import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.CombinedFormatUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java index a9884ba13..554edc85c 100644 --- a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java @@ -19,9 +19,9 @@ package com.android.inputmethod.latin.settings; import android.os.Bundle; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.define.ProductionFlags; - /** * "Appearance" settings sub screen. */ @@ -30,8 +30,8 @@ public final class AppearanceSettingsFragment extends SubScreenFragment { public void onCreate(final Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_appearance); - if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED - || !Settings.getInstance().getCurrent().isTablet()) { + if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED || + Constants.isPhone(Settings.readScreenMetrics(getResources()))) { removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD); } } @@ -43,4 +43,4 @@ public final class AppearanceSettingsFragment extends SubScreenFragment { findPreference(Settings.PREF_CUSTOM_INPUT_STYLES)); ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME)); } -}
\ No newline at end of file +} diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 768cba9b2..6fffb8e9d 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -27,10 +27,10 @@ package com.android.inputmethod.latin.settings; public final class DebugSettings { public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; - public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY = - "force_physical_keyboard_special_key"; public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = "pref_has_custom_key_preview_animation_params"; + public static final String PREF_RESIZE_KEYBOARD = "pref_resize_keyboard"; + public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = "pref_key_preview_dismiss_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index 475f1def7..068f56df1 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -89,6 +89,8 @@ public final class DebugSettingsFragment extends SubScreenFragment defaultKeyPreviewDismissEndScale); setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, defaultKeyPreviewDismissEndScale); + setupKeyboardHeight( + DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE); mServiceNeedsRestart = false; mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE); @@ -142,8 +144,7 @@ public final class DebugSettingsFragment extends SubScreenFragment mServiceNeedsRestart = true; return; } - if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH) - || key.equals(DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY)) { + if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { mServiceNeedsRestart = true; return; } @@ -251,4 +252,51 @@ public final class DebugSettingsFragment extends SubScreenFragment public void feedbackValue(final int value) {} }); } + + private void setupKeyboardHeight(final String prefKey, final float defaultValue) { + final SharedPreferences prefs = getSharedPreferences(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return getPercentageFromValue( + Settings.readKeyboardHeight(prefs, key, defaultValue)); + } + + @Override + public int readDefaultValue(final String key) { + return getPercentageFromValue(defaultValue); + } + + @Override + public String getValueText(final int value) { + return String.format(Locale.ROOT, "%d%%", value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java index c9e9dc8d9..5c416ab18 100644 --- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java +++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java @@ -46,14 +46,15 @@ public class LocalSettingsConstants { // correctly set for it to work on a new device. DebugSettings.PREF_DEBUG_MODE, DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, - DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY, DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, + DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + DebugSettings.PREF_RESIZE_KEYBOARD, DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW }; diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 5e23693d2..16c053474 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -29,11 +29,11 @@ import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.StatsUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.Collections; import java.util.Locale; @@ -97,6 +97,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang "pref_vibration_duration_settings"; public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume"; public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; + public static final String PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY = + "pref_enable_emoji_alt_physical_key"; public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = "pref_gesture_floating_preview_text"; @@ -211,6 +213,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return mSettingsValues.mBlockPotentiallyOffensive; } + public static int readScreenMetrics(final Resources res) { + return res.getInteger(R.integer.config_screen_metrics); + } + // Accessed from the settings interface, hence public public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, final Resources res) { @@ -357,6 +363,12 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; } + public static float readKeyboardHeight(final SharedPreferences prefs, + final String prefKey, final float defaultValue) { + final float percentage = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); + return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue; + } + public static boolean readUseFullscreenMode(final Resources res) { return res.getBoolean(R.bool.config_use_fullscreen_mode); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 660be06d6..509b41fd3 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -29,7 +29,6 @@ import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; @@ -51,6 +50,7 @@ public class SettingsValues { private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue"; private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity"; private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds + public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100% // From resources: public final SpacingAndPunctuations mSpacingAndPunctuations; @@ -80,6 +80,7 @@ public class SettingsValues { public final boolean mSlidingKeyInputPreviewEnabled; public final boolean mPhraseGestureEnabled; public final int mKeyLongpressTimeout; + public final boolean mEnableEmojiAltPhysicalKey; public final boolean mEnableMetricsLogging; public final boolean mShouldShowLxxSuggestionUi; // Use split layout for keyboard. @@ -110,6 +111,8 @@ public class SettingsValues { // Debug settings public final boolean mIsInternal; public final boolean mHasCustomKeyPreviewAnimationParams; + public final boolean mHasKeyboardResize; + public final float mKeyboardHeightScale; public final int mKeyPreviewShowUpDuration; public final int mKeyPreviewDismissDuration; public final float mKeyPreviewShowUpStartXScale; @@ -157,7 +160,7 @@ public class SettingsValues { mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration()); mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true); mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false); - mScreenMetrics = res.getInteger(R.integer.config_screen_metrics); + mScreenMetrics = Settings.readScreenMetrics(res); mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI && prefs.getBoolean(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, true); @@ -166,6 +169,8 @@ public class SettingsValues { mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res); mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res); + mEnableEmojiAltPhysicalKey = prefs.getBoolean( + Settings.PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY, true); mAutoCorrectionThreshold = readAutoCorrectionThreshold(res, autoCorrectionThresholdRawValue); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); @@ -183,6 +188,9 @@ public class SettingsValues { mIsInternal = Settings.isInternal(prefs); mHasCustomKeyPreviewAnimationParams = prefs.getBoolean( DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false); + mHasKeyboardResize = prefs.getBoolean(DebugSettings.PREF_RESIZE_KEYBOARD, false); + mKeyboardHeightScale = Settings.readKeyboardHeight( + prefs, DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, DEFAULT_SIZE_SCALE); mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); @@ -221,11 +229,6 @@ public class SettingsValues { return mEnableMetricsLogging; } - public boolean isTablet() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET; - } - public boolean isApplicationSpecifiedCompletionsOn() { return mInputAttributes.mApplicationSpecifiedCompletionOn; } diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 46aef89c4..70d97a5ba 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -23,7 +23,7 @@ import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.PunctuationSuggestions; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 315e3696b..bcf7bbfdc 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -168,7 +168,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService DictionaryFacilitator dictionaryFacilitatorForLocale = mDictionaryFacilitatorCache.get(locale); return dictionaryFacilitatorForLocale.getSuggestionResults(composer, ngramContext, - proximityInfo, mSettingsValuesForSuggestion, sessionId); + proximityInfo.getNativeProximityInfo(), mSettingsValuesForSuggestion, + sessionId); } finally { if (sessionId != null) { mSessionIdPool.add(sessionId); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index ac395bf02..2c690aea7 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -28,7 +28,7 @@ import android.view.textservice.TextInfo; import com.android.inputmethod.compat.TextInfoCompatUtils; import com.android.inputmethod.latin.NgramContext; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.SpannableStringUtils; import java.util.ArrayList; import java.util.Locale; @@ -71,9 +71,10 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck if (!subText.toString().contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { continue; } - final CharSequence[] splitTexts = StringUtils.split(subText, + // Split preserving spans. + final CharSequence[] splitTexts = SpannableStringUtils.split(subText, AndroidSpellCheckerService.SINGLE_QUOTE, - true /* preserveTrailingEmptySegments */ ); + true /* preserveTrailingEmptySegments */); if (splitTexts == null || splitTexts.length <= 1) { continue; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 514bfca85..3ad8fb910 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -34,11 +34,11 @@ import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.ScriptUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index b18fcc744..b71bd1f50 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -344,12 +344,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (mSuggestedWords.size() <= mStartIndexOfMoreSuggestions) { return false; } - // Dismiss another {@link MoreKeysPanel} that may be being showed, for example - // {@link MoreKeysKeyboardView}. - mMainKeyboardView.onDismissMoreKeysPanel(); - // Dismiss all key previews and sliding key input preview that may be being showed. - mMainKeyboardView.dismissAllKeyPreviews(); - mMainKeyboardView.dismissSlidingKeyInputPreview(); final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java index 727485724..1d7e7d683 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java @@ -282,7 +282,6 @@ public class UserDictionarySettings extends ListFragment { } private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { - private AlphabetIndexer mIndexer; private ViewBinder mViewBinder = new ViewBinder() { diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 02ace6a1e..2aac7c57a 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -31,6 +31,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java index ce25fe6a4..23ffde2a2 100644 --- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.personalization.PersonalizationHelper; diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index b087e9ee8..0dbc7c858 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -21,8 +21,10 @@ import android.text.TextUtils; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import java.util.ArrayList; import java.util.Locale; public final class CapsModeUtils { @@ -325,4 +327,31 @@ public final class CapsModeUtils { // Here we arrived at the start of the line. This should behave exactly like whitespace. return (START == state || LETTER == state) ? noCaps : caps; } + + /** + * Convert capitalize mode flags into human readable text. + * + * @param capsFlags The modes flags to be converted. It may be any combination of + * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and + * {@link TextUtils#CAP_MODE_SENTENCES}. + * @return the text that describe the <code>capsMode</code>. + */ + public static String flagsToString(final int capsFlags) { + final int capsFlagsMask = TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS + | TextUtils.CAP_MODE_SENTENCES; + if ((capsFlags & ~capsFlagsMask) != 0) { + return "unknown<0x" + Integer.toHexString(capsFlags) + ">"; + } + final ArrayList<String> builder = new ArrayList<>(); + if ((capsFlags & android.text.TextUtils.CAP_MODE_CHARACTERS) != 0) { + builder.add("characters"); + } + if ((capsFlags & android.text.TextUtils.CAP_MODE_WORDS) != 0) { + builder.add("words"); + } + if ((capsFlags & android.text.TextUtils.CAP_MODE_SENTENCES) != 0) { + builder.add("sentences"); + } + return builder.isEmpty() ? "none" : TextUtils.join("|", builder); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java index d0df72479..01f5e1079 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java @@ -19,17 +19,30 @@ package com.android.inputmethod.latin.utils; import java.util.ArrayList; import java.util.Collection; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Utility methods for working with collections. + */ public final class CollectionUtils { private CollectionUtils() { // This utility class is not publicly instantiable. } - public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) { - if (array == null) { - throw new NullPointerException(); - } + /** + * Converts a sub-range of the given array to an ArrayList of the appropriate type. + * @param array Array to be converted. + * @param start First index inclusive to be converted. + * @param end Last index exclusive to be converted. + * @throws IllegalArgumentException if start or end are out of range or start > end. + */ + @Nonnull + public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start, + final int end) { if (start < 0 || start > end || end > array.length) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Invalid start: " + start + " end: " + end + + " with array.length: " + array.length); } final ArrayList<E> list = new ArrayList<>(end - start); @@ -44,7 +57,7 @@ public final class CollectionUtils { * @param c Collection to test. * @return Whether c contains no elements. */ - public static boolean isNullOrEmpty(final Collection<?> c) { + public static boolean isNullOrEmpty(@Nullable final Collection<?> c) { return c == null || c.isEmpty(); } } diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java index 4e0f5f583..8699f2ce7 100644 --- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java @@ -36,7 +36,8 @@ public class CombinedFormatUtils { public static final String WORD_TAG = "word"; public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence"; public static final String NOT_A_WORD_TAG = "not_a_word"; - public static final String BLACKLISTED_TAG = "blacklisted"; + public static final String POSSIBLY_OFFENSIVE_TAG = "possibly_offensive"; + public static final String TRUE_VALUE = "true"; public static String formatAttributeMap(final HashMap<String, String> attributeMap) { final StringBuilder builder = new StringBuilder(); @@ -61,13 +62,13 @@ public class CombinedFormatUtils { builder.append(","); builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo)); if (wordProperty.mIsBeginningOfSentence) { - builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true"); + builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=" + TRUE_VALUE); } if (wordProperty.mIsNotAWord) { - builder.append("," + NOT_A_WORD_TAG + "=true"); + builder.append("," + NOT_A_WORD_TAG + "=" + TRUE_VALUE); } if (wordProperty.mIsPossiblyOffensive) { - builder.append("," + BLACKLISTED_TAG + "=true"); + builder.append("," + POSSIBLY_OFFENSIVE_TAG + "=" + TRUE_VALUE); } builder.append("\n"); if (wordProperty.mHasShortcuts) { @@ -111,4 +112,8 @@ public class CombinedFormatUtils { } return builder.toString(); } + + public static boolean isLiteralTrue(final String value) { + return TRUE_VALUE.equalsIgnoreCase(value); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java index 87df013a6..3a9705904 100644 --- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin.utils; -import java.util.Arrays; +import javax.annotation.Nonnull; public final class CoordinateUtils { private static final int INDEX_X = 0; @@ -27,32 +27,35 @@ public final class CoordinateUtils { // This utility class is not publicly instantiable. } + @Nonnull public static int[] newInstance() { return new int[ELEMENT_SIZE]; } - public static int x(final int[] coords) { + public static int x(@Nonnull final int[] coords) { return coords[INDEX_X]; } - public static int y(final int[] coords) { + public static int y(@Nonnull final int[] coords) { return coords[INDEX_Y]; } - public static void set(final int[] coords, final int x, final int y) { + public static void set(@Nonnull final int[] coords, final int x, final int y) { coords[INDEX_X] = x; coords[INDEX_Y] = y; } - public static void copy(final int[] destination, final int[] source) { + public static void copy(@Nonnull final int[] destination, @Nonnull final int[] source) { destination[INDEX_X] = source[INDEX_X]; destination[INDEX_Y] = source[INDEX_Y]; } + @Nonnull public static int[] newCoordinateArray(final int arraySize) { return new int[ELEMENT_SIZE * arraySize]; } + @Nonnull public static int[] newCoordinateArray(final int arraySize, final int defaultX, final int defaultY) { final int[] result = new int[ELEMENT_SIZE * arraySize]; @@ -62,30 +65,30 @@ public final class CoordinateUtils { return result; } - public static int xFromArray(final int[] coordsArray, final int index) { + public static int xFromArray(@Nonnull final int[] coordsArray, final int index) { return coordsArray[ELEMENT_SIZE * index + INDEX_X]; } - public static int yFromArray(final int[] coordsArray, final int index) { + public static int yFromArray(@Nonnull final int[] coordsArray, final int index) { return coordsArray[ELEMENT_SIZE * index + INDEX_Y]; } - public static int[] coordinateFromArray(final int[] coordsArray, final int index) { - final int baseIndex = ELEMENT_SIZE * index; - return Arrays.copyOfRange(coordsArray, baseIndex, baseIndex + ELEMENT_SIZE); + @Nonnull + public static int[] coordinateFromArray(@Nonnull final int[] coordsArray, final int index) { + final int[] coords = newInstance(); + set(coords, xFromArray(coordsArray, index), yFromArray(coordsArray, index)); + return coords; } - public static void setXYInArray(final int[] coordsArray, final int index, + public static void setXYInArray(@Nonnull final int[] coordsArray, final int index, final int x, final int y) { final int baseIndex = ELEMENT_SIZE * index; coordsArray[baseIndex + INDEX_X] = x; coordsArray[baseIndex + INDEX_Y] = y; } - public static void setCoordinateInArray(final int[] coordsArray, final int index, - final int[] coords) { - final int baseIndex = ELEMENT_SIZE * index; - coordsArray[baseIndex + INDEX_X] = coords[INDEX_X]; - coordsArray[baseIndex + INDEX_Y] = coords[INDEX_Y]; + public static void setCoordinateInArray(@Nonnull final int[] coordsArray, final int index, + @Nonnull final int[] coords) { + setXYInArray(coordsArray, index, x(coords), y(coords)); } } diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 8f0f9bb44..9c6a94810 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -40,6 +40,7 @@ import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; /** @@ -249,8 +250,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr composer.setComposingWord(codePoints, coordinates); final SuggestionResults suggestionResults; synchronized (mLock) { - suggestionResults = dictionaryFacilitator.getSuggestionResults( - composer, NgramContext.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(), + suggestionResults = dictionaryFacilitator.getSuggestionResults(composer, + NgramContext.EMPTY_PREV_WORDS_INFO, + keyboard.getProximityInfo().getNativeProximityInfo(), settingsValuesForSuggestion, 0 /* sessionId */); } if (suggestionResults.isEmpty()) { diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java index e3cac97f0..a381649a4 100644 --- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java +++ b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.utils; +import com.android.inputmethod.latin.common.StringUtils; + import java.util.Locale; /** @@ -49,6 +51,17 @@ public class RecapitalizeStatus { } } + public static String modeToString(final int recapitalizeMode) { + switch (recapitalizeMode) { + case NOT_A_RECAPITALIZE_MODE: return "undefined"; + case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase"; + case CAPS_MODE_ALL_LOWER: return "allLower"; + case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper"; + case CAPS_MODE_ALL_UPPER: return "allUpper"; + default: return "unknown<" + recapitalizeMode + ">"; + } + } + /** * We store the location of the cursor and the string that was there before the recapitalize * action was done, and the location of the cursor and the string that was there after. diff --git a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java deleted file mode 100644 index 64c9e2cff..000000000 --- a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.inputmethod.latin.utils; - -import java.util.Arrays; - -// TODO: This class is not thread-safe. -public final class ResizableIntArray { - private int[] mArray; - private int mLength; - - public ResizableIntArray(final int capacity) { - reset(capacity); - } - - public int get(final int index) { - if (index < mLength) { - return mArray[index]; - } - throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index); - } - - public void addAt(final int index, final int val) { - if (index < mLength) { - mArray[index] = val; - } else { - mLength = index; - add(val); - } - } - - public void add(final int val) { - final int currentLength = mLength; - ensureCapacity(currentLength + 1); - mArray[currentLength] = val; - mLength = currentLength + 1; - } - - /** - * Calculate the new capacity of {@code mArray}. - * @param minimumCapacity the minimum capacity that the {@code mArray} should have. - * @return the new capacity that the {@code mArray} should have. Returns zero when there is no - * need to expand {@code mArray}. - */ - private int calculateCapacity(final int minimumCapacity) { - final int currentCapcity = mArray.length; - if (currentCapcity < minimumCapacity) { - final int nextCapacity = currentCapcity * 2; - // The following is the same as return Math.max(minimumCapacity, nextCapacity); - return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity; - } - return 0; - } - - private void ensureCapacity(final int minimumCapacity) { - final int newCapacity = calculateCapacity(minimumCapacity); - if (newCapacity > 0) { - // TODO: Implement primitive array pool. - mArray = Arrays.copyOf(mArray, newCapacity); - } - } - - public int getLength() { - return mLength; - } - - public void setLength(final int newLength) { - ensureCapacity(newLength); - mLength = newLength; - } - - public void reset(final int capacity) { - // TODO: Implement primitive array pool. - mArray = new int[capacity]; - mLength = 0; - } - - public int[] getPrimitiveArray() { - return mArray; - } - - public void set(final ResizableIntArray ip) { - // TODO: Implement primitive array pool. - mArray = ip.mArray; - mLength = ip.mLength; - } - - public void copy(final ResizableIntArray ip) { - final int newCapacity = calculateCapacity(ip.mLength); - if (newCapacity > 0) { - // TODO: Implement primitive array pool. - mArray = new int[newCapacity]; - } - System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); - mLength = ip.mLength; - } - - public void append(final ResizableIntArray src, final int startPos, final int length) { - if (length == 0) { - return; - } - final int currentLength = mLength; - final int newLength = currentLength + length; - ensureCapacity(newLength); - System.arraycopy(src.mArray, startPos, mArray, currentLength, length); - mLength = newLength; - } - - public void fill(final int value, final int startPos, final int length) { - if (startPos < 0 || length < 0) { - throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length); - } - final int endPos = startPos + length; - ensureCapacity(endPos); - Arrays.fill(mArray, startPos, endPos, value); - if (mLength < endPos) { - mLength = endPos; - } - } - - /** - * Shift to the left by elementCount, discarding elementCount pointers at the start. - * @param elementCount how many elements to shift. - */ - public void shift(final int elementCount) { - System.arraycopy(mArray, elementCount, mArray, 0, mLength - elementCount); - mLength -= elementCount; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < mLength; i++) { - if (i != 0) { - sb.append(","); - } - sb.append(mArray[i]); - } - return "[" + sb + "]"; - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index d1fc642f3..cc0d470df 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -26,6 +26,7 @@ import android.util.TypedValue; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.settings.SettingsValues; import java.util.ArrayList; import java.util.HashMap; @@ -186,6 +187,15 @@ public final class ResourceUtils { return dm.widthPixels; } + public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) { + final int defaultKeyboardHeight = getDefaultKeyboardHeight(res); + if (settingsValues.mHasKeyboardResize) { + // mKeyboardHeightScale Ranges from [.5,1.2], from xml/prefs_screen_debug.xml + return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale); + } + return defaultKeyboardHeight; + } + public static int getDefaultKeyboardHeight(final Resources res) { final DisplayMetrics dm = res.getDisplayMetrics(); final String keyboardHeightInDp = getDeviceOverrideValue( diff --git a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java index cea1d1d58..c41817fe6 100644 --- a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java @@ -24,6 +24,12 @@ import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.text.style.URLSpan; +import com.android.inputmethod.annotations.UsedForTesting; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public final class SpannableStringUtils { /** * Copies the spans from the region <code>start...end</code> in @@ -125,4 +131,53 @@ public final class SpannableStringUtils { final URLSpan[] spans = spanned.getSpans(startIndex - 1, endIndex + 1, URLSpan.class); return null != spans && spans.length > 0; } + + /** + * Splits the given {@code charSequence} with at occurrences of the given {@code regex}. + * <p> + * This is equivalent to + * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)} + * except that the spans are preserved in the result array. + * </p> + * @param charSequence the character sequence to be split. + * @param regex the regex pattern to be used as the separator. + * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty + * segments. Otherwise, trailing empty segments will be removed before being returned. + * @return the array which contains the result. All the spans in the <code>charSequence</code> + * is preserved. + */ + @UsedForTesting + public static CharSequence[] split(final CharSequence charSequence, final String regex, + final boolean preserveTrailingEmptySegments) { + // A short-cut for non-spanned strings. + if (!(charSequence instanceof Spanned)) { + // -1 means that trailing empty segments will be preserved. + return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0); + } + + // Hereafter, emulate String.split for CharSequence. + final ArrayList<CharSequence> sequences = new ArrayList<>(); + final Matcher matcher = Pattern.compile(regex).matcher(charSequence); + int nextStart = 0; + boolean matched = false; + while (matcher.find()) { + sequences.add(charSequence.subSequence(nextStart, matcher.start())); + nextStart = matcher.end(); + matched = true; + } + if (!matched) { + // never matched. preserveTrailingEmptySegments is ignored in this case. + return new CharSequence[] { charSequence }; + } + sequences.add(charSequence.subSequence(nextStart, charSequence.length())); + if (!preserveTrailingEmptySegments) { + for (int i = sequences.size() - 1; i >= 0; --i) { + if (!TextUtils.isEmpty(sequences.get(i))) { + break; + } + sequences.remove(i); + } + } + return sequences.toArray(new CharSequence[sequences.size()]); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java deleted file mode 100644 index f96ed0468..000000000 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.inputmethod.latin.utils; - -import android.text.Spanned; -import android.text.TextUtils; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.common.Constants; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class StringUtils { - public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case - public static final int CAPITALIZE_FIRST = 1; // First only - public static final int CAPITALIZE_ALL = 2; // All caps - - private static final String EMPTY_STRING = ""; - - private static final char CHAR_LINE_FEED = 0X000A; - private static final char CHAR_VERTICAL_TAB = 0X000B; - private static final char CHAR_FORM_FEED = 0X000C; - private static final char CHAR_CARRIAGE_RETURN = 0X000D; - private static final char CHAR_NEXT_LINE = 0X0085; - private static final char CHAR_LINE_SEPARATOR = 0X2028; - private static final char CHAR_PARAGRAPH_SEPARATOR = 0X2029; - - private StringUtils() { - // This utility class is not publicly instantiable. - } - - public static int codePointCount(final CharSequence text) { - if (TextUtils.isEmpty(text)) return 0; - return Character.codePointCount(text, 0, text.length()); - } - - public static String newSingleCodePointString(int codePoint) { - if (Character.charCount(codePoint) == 1) { - // Optimization: avoid creating a temporary array for characters that are - // represented by a single char value - return String.valueOf((char) codePoint); - } - // For surrogate pair - return new String(Character.toChars(codePoint)); - } - - public static boolean containsInArray(final String text, final String[] array) { - for (final String element : array) { - if (text.equals(element)) return true; - } - return false; - } - - /** - * Comma-Splittable Text is similar to Comma-Separated Values (CSV) but has much simpler syntax. - * Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain - * a comma character in it. - */ - private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ","; - - public static boolean containsInCommaSplittableText(final String text, - final String extraValues) { - if (TextUtils.isEmpty(extraValues)) { - return false; - } - return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT)); - } - - public static String removeFromCommaSplittableTextIfExists(final String text, - final String extraValues) { - if (TextUtils.isEmpty(extraValues)) { - return EMPTY_STRING; - } - final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT); - if (!containsInArray(text, elements)) { - return extraValues; - } - final ArrayList<String> result = new ArrayList<>(elements.length - 1); - for (final String element : elements) { - if (!text.equals(element)) { - result.add(element); - } - } - return TextUtils.join(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT, result); - } - - /** - * Remove duplicates from an array of strings. - * - * This method will always keep the first occurrence of all strings at their position - * in the array, removing the subsequent ones. - */ - public static void removeDupes(final ArrayList<String> suggestions) { - if (suggestions.size() < 2) return; - int i = 1; - // Don't cache suggestions.size(), since we may be removing items - while (i < suggestions.size()) { - final String cur = suggestions.get(i); - // Compare each suggestion with each previous suggestion - for (int j = 0; j < i; j++) { - final String previous = suggestions.get(j); - if (TextUtils.equals(cur, previous)) { - suggestions.remove(i); - i--; - break; - } - } - i++; - } - } - - public static String capitalizeFirstCodePoint(final String s, final Locale locale) { - if (s.length() <= 1) { - return s.toUpperCase(locale); - } - // Please refer to the comment below in - // {@link #capitalizeFirstAndDowncaseRest(String,Locale)} as this has the same shortcomings - final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff); - } - - public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) { - if (s.length() <= 1) { - return s.toUpperCase(locale); - } - // TODO: fix the bugs below - // - This does not work for Greek, because it returns upper case instead of title case. - // - It does not work for Serbian, because it fails to account for the "lj" character, - // which should be "Lj" in title case and "LJ" in upper case. - // - It does not work for Dutch, because it fails to account for the "ij" digraph when it's - // written as two separate code points. They are two different characters but both should - // be capitalized as "IJ" as if they were a single letter in most words (not all). If the - // unicode char for the ligature is used however, it works. - final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale); - } - - private static final int[] EMPTY_CODEPOINTS = {}; - - public static int[] toCodePointArray(final CharSequence charSequence) { - return toCodePointArray(charSequence, 0, charSequence.length()); - } - - /** - * Converts a range of a string to an array of code points. - * @param charSequence the source string. - * @param startIndex the start index inside the string in java chars, inclusive. - * @param endIndex the end index inside the string in java chars, exclusive. - * @return a new array of code points. At most endIndex - startIndex, but possibly less. - */ - public static int[] toCodePointArray(final CharSequence charSequence, - final int startIndex, final int endIndex) { - final int length = charSequence.length(); - if (length <= 0) { - return EMPTY_CODEPOINTS; - } - final int[] codePoints = - new int[Character.codePointCount(charSequence, startIndex, endIndex)]; - copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex, - false /* downCase */); - return codePoints; - } - - /** - * Copies the codepoints in a CharSequence to an int array. - * - * This method assumes there is enough space in the array to store the code points. The size - * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this - * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. - * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while - * this method is running, or the behavior is undefined. - * This method can optionally downcase code points before copying them, but it pays no attention - * to locale while doing so. - * - * @param destination the int array. - * @param charSequence the CharSequence. - * @param startIndex the start index inside the string in java chars, inclusive. - * @param endIndex the end index inside the string in java chars, exclusive. - * @param downCase if this is true, code points will be downcased before being copied. - * @return the number of copied code points. - */ - public static int copyCodePointsAndReturnCodePointCount(final int[] destination, - final CharSequence charSequence, final int startIndex, final int endIndex, - final boolean downCase) { - int destIndex = 0; - for (int index = startIndex; index < endIndex; - index = Character.offsetByCodePoints(charSequence, index, 1)) { - final int codePoint = Character.codePointAt(charSequence, index); - // TODO: stop using this, as it's not aware of the locale and does not always do - // the right thing. - destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint; - destIndex++; - } - return destIndex; - } - - public static int[] toSortedCodePointArray(final String string) { - final int[] codePoints = toCodePointArray(string); - Arrays.sort(codePoints); - return codePoints; - } - - /** - * Construct a String from a code point array - * - * @param codePoints a code point array that is null terminated when its logical length is - * shorter than the array length. - * @return a string constructed from the code point array. - */ - public static String getStringFromNullTerminatedCodePointArray(final int[] codePoints) { - int stringLength = codePoints.length; - for (int i = 0; i < codePoints.length; i++) { - if (codePoints[i] == 0) { - stringLength = i; - break; - } - } - return new String(codePoints, 0 /* offset */, stringLength); - } - - // This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE. - public static int getCapitalizationType(final String text) { - // If the first char is not uppercase, then the word is either all lower case or - // camel case, and in either case we return CAPITALIZE_NONE. - final int len = text.length(); - int index = 0; - for (; index < len; index = text.offsetByCodePoints(index, 1)) { - if (Character.isLetter(text.codePointAt(index))) { - break; - } - } - if (index == len) return CAPITALIZE_NONE; - if (!Character.isUpperCase(text.codePointAt(index))) { - return CAPITALIZE_NONE; - } - int capsCount = 1; - int letterCount = 1; - for (index = text.offsetByCodePoints(index, 1); index < len; - index = text.offsetByCodePoints(index, 1)) { - if (1 != capsCount && letterCount != capsCount) break; - final int codePoint = text.codePointAt(index); - if (Character.isUpperCase(codePoint)) { - ++capsCount; - ++letterCount; - } else if (Character.isLetter(codePoint)) { - // We need to discount non-letters since they may not be upper-case, but may - // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME") - ++letterCount; - } - } - // We know the first char is upper case. So we want to test if either every letter other - // than the first is lower case, or if they are all upper case. If the string is exactly - // one char long, then we will arrive here with letterCount 1, and this is correct, too. - if (1 == capsCount) return CAPITALIZE_FIRST; - return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE); - } - - public static boolean isIdenticalAfterUpcase(final String text) { - final int length = text.length(); - int i = 0; - while (i < length) { - final int codePoint = text.codePointAt(i); - if (Character.isLetter(codePoint) && !Character.isUpperCase(codePoint)) { - return false; - } - i += Character.charCount(codePoint); - } - return true; - } - - public static boolean isIdenticalAfterDowncase(final String text) { - final int length = text.length(); - int i = 0; - while (i < length) { - final int codePoint = text.codePointAt(i); - if (Character.isLetter(codePoint) && !Character.isLowerCase(codePoint)) { - return false; - } - i += Character.charCount(codePoint); - } - return true; - } - - public static boolean isIdenticalAfterCapitalizeEachWord(final String text, - final int[] sortedSeparators) { - boolean needsCapsNext = true; - final int len = text.length(); - for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) { - final int codePoint = text.codePointAt(i); - if (Character.isLetter(codePoint)) { - if ((needsCapsNext && !Character.isUpperCase(codePoint)) - || (!needsCapsNext && !Character.isLowerCase(codePoint))) { - return false; - } - } - // We need a capital letter next if this is a separator. - needsCapsNext = (Arrays.binarySearch(sortedSeparators, codePoint) >= 0); - } - return true; - } - - // TODO: like capitalizeFirst*, this does not work perfectly for Dutch because of the IJ digraph - // which should be capitalized together in *some* cases. - public static String capitalizeEachWord(final String text, final int[] sortedSeparators, - final Locale locale) { - final StringBuilder builder = new StringBuilder(); - boolean needsCapsNext = true; - final int len = text.length(); - for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) { - final String nextChar = text.substring(i, text.offsetByCodePoints(i, 1)); - if (needsCapsNext) { - builder.append(nextChar.toUpperCase(locale)); - } else { - builder.append(nextChar.toLowerCase(locale)); - } - // We need a capital letter next if this is a separator. - needsCapsNext = (Arrays.binarySearch(sortedSeparators, nextChar.codePointAt(0)) >= 0); - } - return builder.toString(); - } - - /** - * Approximates whether the text before the cursor looks like a URL. - * - * This is not foolproof, but it should work well in the practice. - * Essentially it walks backward from the cursor until it finds something that's not a letter, - * digit, or common URL symbol like underscore. If it hasn't found a period yet, then it - * does not look like a URL. - * If the text: - * - starts with www and contains a period - * - starts with a slash preceded by either a slash, whitespace, or start-of-string - * Then it looks like a URL and we return true. Otherwise, we return false. - * - * Note: this method is called quite often, and should be fast. - * - * TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the - * code complexity, but ideally it should not. It's acceptable for now. - */ - public static boolean lastPartLooksLikeURL(final CharSequence text) { - int i = text.length(); - if (0 == i) return false; - int wCount = 0; - int slashCount = 0; - boolean hasSlash = false; - boolean hasPeriod = false; - int codePoint = 0; - while (i > 0) { - codePoint = Character.codePointBefore(text, i); - if (codePoint < Constants.CODE_PERIOD || codePoint > 'z') { - // Handwavy heuristic to see if that's a URL character. Anything between period - // and z. This includes all lower- and upper-case ascii letters, period, - // underscore, arrobase, question mark, equal sign. It excludes spaces, exclamation - // marks, double quotes... - // Anything that's not a URL-like character causes us to break from here and - // evaluate normally. - break; - } - if (Constants.CODE_PERIOD == codePoint) { - hasPeriod = true; - } - if (Constants.CODE_SLASH == codePoint) { - hasSlash = true; - if (2 == ++slashCount) { - return true; - } - } else { - slashCount = 0; - } - if ('w' == codePoint) { - ++wCount; - } else { - wCount = 0; - } - i = Character.offsetByCodePoints(text, i, -1); - } - // End of the text run. - // If it starts with www and includes a period, then it looks like a URL. - if (wCount >= 3 && hasPeriod) return true; - // If it starts with a slash, and the code point before is whitespace, it looks like an URL. - if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) return true; - // If it has both a period and a slash, it looks like an URL. - if (hasPeriod && hasSlash) return true; - // Otherwise, it doesn't look like an URL. - return false; - } - - /** - * Examines the string and returns whether we're inside a double quote. - * - * This is used to decide whether we should put an automatic space before or after a double - * quote character. If we're inside a quotation, then we want to close it, so we want a space - * after and not before. Otherwise, we want to open the quotation, so we want a space before - * and not after. Exception: after a digit, we never want a space because the "inch" or - * "minutes" use cases is dominant after digits. - * In the practice, we determine whether we are in a quotation or not by finding the previous - * double quote character, and looking at whether it's followed by whitespace. If so, that - * was a closing quotation mark, so we're not inside a double quote. If it's not followed - * by whitespace, then it was an opening quotation mark, and we're inside a quotation. - * - * @param text the text to examine. - * @return whether we're inside a double quote. - */ - public static boolean isInsideDoubleQuoteOrAfterDigit(final CharSequence text) { - int i = text.length(); - if (0 == i) return false; - int codePoint = Character.codePointBefore(text, i); - if (Character.isDigit(codePoint)) return true; - int prevCodePoint = 0; - while (i > 0) { - codePoint = Character.codePointBefore(text, i); - if (Constants.CODE_DOUBLE_QUOTE == codePoint) { - // If we see a double quote followed by whitespace, then that - // was a closing quote. - if (Character.isWhitespace(prevCodePoint)) return false; - } - if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) { - // If we see a double quote preceded by whitespace, then that - // was an opening quote. No need to continue seeking. - return true; - } - i -= Character.charCount(codePoint); - prevCodePoint = codePoint; - } - // We reached the start of text. If the first char is a double quote, then we're inside - // a double quote. Otherwise we're not. - return Constants.CODE_DOUBLE_QUOTE == codePoint; - } - - public static boolean isEmptyStringOrWhiteSpaces(final String s) { - final int N = codePointCount(s); - for (int i = 0; i < N; ++i) { - if (!Character.isWhitespace(s.codePointAt(i))) { - return false; - } - } - return true; - } - - @UsedForTesting - public static String byteArrayToHexString(final byte[] bytes) { - if (bytes == null || bytes.length == 0) { - return EMPTY_STRING; - } - final StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b & 0xff)); - } - return sb.toString(); - } - - /** - * Convert hex string to byte array. The string length must be an even number. - */ - @UsedForTesting - public static byte[] hexStringToByteArray(final String hexString) { - if (TextUtils.isEmpty(hexString)) { - return null; - } - final int N = hexString.length(); - if (N % 2 != 0) { - throw new NumberFormatException("Input hex string length must be an even number." - + " Length = " + N); - } - final byte[] bytes = new byte[N / 2]; - for (int i = 0; i < N; i += 2) { - bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) - + Character.digit(hexString.charAt(i + 1), 16)); - } - return bytes; - } - - public static String toUpperCaseOfStringForLocale(final String text, - final boolean needsToUpperCase, final Locale locale) { - if (text == null || !needsToUpperCase) return text; - return text.toUpperCase(locale); - } - - public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, - final Locale locale) { - if (!Constants.isLetterCode(code) || !needsToUpperCase) return code; - final String text = newSingleCodePointString(code); - final String casedText = toUpperCaseOfStringForLocale( - text, needsToUpperCase, locale); - return codePointCount(casedText) == 1 - ? casedText.codePointAt(0) : Constants.CODE_UNSPECIFIED; - } - - public static int getTrailingSingleQuotesCount(final CharSequence charSequence) { - final int lastIndex = charSequence.length() - 1; - int i = lastIndex; - while (i >= 0 && charSequence.charAt(i) == Constants.CODE_SINGLE_QUOTE) { - --i; - } - return lastIndex - i; - } - - /** - * Splits the given {@code charSequence} with at occurrences of the given {@code regex}. - * <p> - * This is equivalent to - * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)} - * except that the spans are preserved in the result array. - * </p> - * @param charSequence the character sequence to be split. - * @param regex the regex pattern to be used as the separator. - * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty - * segments. Otherwise, trailing empty segments will be removed before being returned. - * @return the array which contains the result. All the spans in the <code>charSequence</code> - * is preserved. - */ - @UsedForTesting - public static CharSequence[] split(final CharSequence charSequence, final String regex, - final boolean preserveTrailingEmptySegments) { - // A short-cut for non-spanned strings. - if (!(charSequence instanceof Spanned)) { - // -1 means that trailing empty segments will be preserved. - return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0); - } - - // Hereafter, emulate String.split for CharSequence. - final ArrayList<CharSequence> sequences = new ArrayList<>(); - final Matcher matcher = Pattern.compile(regex).matcher(charSequence); - int nextStart = 0; - boolean matched = false; - while (matcher.find()) { - sequences.add(charSequence.subSequence(nextStart, matcher.start())); - nextStart = matcher.end(); - matched = true; - } - if (!matched) { - // never matched. preserveTrailingEmptySegments is ignored in this case. - return new CharSequence[] { charSequence }; - } - sequences.add(charSequence.subSequence(nextStart, charSequence.length())); - if (!preserveTrailingEmptySegments) { - for (int i = sequences.size() - 1; i >= 0; --i) { - if (!TextUtils.isEmpty(sequences.get(i))) { - break; - } - sequences.remove(i); - } - } - return sequences.toArray(new CharSequence[sequences.size()]); - } - - @UsedForTesting - public static class Stringizer<E> { - public String stringize(final E element) { - return element != null ? element.toString() : "null"; - } - - @UsedForTesting - public final String join(final E[] array) { - return joinStringArray(toStringArray(array), null /* delimiter */); - } - - @UsedForTesting - public final String join(final E[] array, final String delimiter) { - return joinStringArray(toStringArray(array), delimiter); - } - - protected String[] toStringArray(final E[] array) { - final String[] stringArray = new String[array.length]; - for (int index = 0; index < array.length; index++) { - stringArray[index] = stringize(array[index]); - } - return stringArray; - } - - protected String joinStringArray(final String[] stringArray, final String delimiter) { - if (stringArray == null) { - return "null"; - } - if (delimiter == null) { - return Arrays.toString(stringArray); - } - final StringBuilder sb = new StringBuilder(); - for (int index = 0; index < stringArray.length; index++) { - sb.append(index == 0 ? "[" : delimiter); - sb.append(stringArray[index]); - } - return sb + "]"; - } - } - - /** - * Returns whether the last composed word contains line-breaking character (e.g. CR or LF). - * @param text the text to be examined. - * @return {@code true} if the last composed word contains line-breaking separator. - */ - @UsedForTesting - public static boolean hasLineBreakCharacter(final String text) { - if (TextUtils.isEmpty(text)) { - return false; - } - for (int i = text.length() - 1; i >= 0; --i) { - final char c = text.charAt(i); - switch (c) { - case CHAR_LINE_FEED: - case CHAR_VERTICAL_TAB: - case CHAR_FORM_FEED: - case CHAR_CARRIAGE_RETURN: - case CHAR_NEXT_LINE: - case CHAR_LINE_SEPARATOR: - case CHAR_PARAGRAPH_SEPARATOR: - return true; - } - } - return false; - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index eb85c1baf..55c1dc9e5 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -28,10 +28,10 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Locale; /** @@ -59,7 +59,8 @@ public final class SubtypeLocaleUtils { // Keyboard layout to subtype name resource id map. private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>(); // Exceptional locale whose name should be displayed in Locale.ROOT. - static final HashSet<String> sExceptionalLocaleDisplayedInRootLocale = new HashSet<>(); + private static final HashMap<String, Integer> sExceptionalLocaleDisplayedInRootLocale = + new HashMap<>(); // Exceptional locale to subtype name resource id map. private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name with layout resource id map. @@ -73,6 +74,8 @@ public final class SubtypeLocaleUtils { "string/subtype_with_layout_"; private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX = "string/subtype_no_language_"; + private static final String SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX = + "string/subtype_in_root_locale_"; // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap = @@ -117,7 +120,10 @@ public final class SubtypeLocaleUtils { final String[] exceptionalLocaleInRootLocale = res.getStringArray( R.array.subtype_locale_displayed_in_root_locale); for (int i = 0; i < exceptionalLocaleInRootLocale.length; i++) { - sExceptionalLocaleDisplayedInRootLocale.add(exceptionalLocaleInRootLocale[i]); + final String localeString = exceptionalLocaleInRootLocale[i]; + final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + localeString; + final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME); + sExceptionalLocaleDisplayedInRootLocale.put(localeString, resId); } final String[] exceptionalLocales = res.getStringArray( @@ -171,7 +177,7 @@ public final class SubtypeLocaleUtils { if (NO_LANGUAGE.equals(localeString)) { return sResources.getConfiguration().locale; } - if (sExceptionalLocaleDisplayedInRootLocale.contains(localeString)) { + if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { return Locale.ROOT; } return LocaleUtils.constructLocaleFromString(localeString); @@ -190,7 +196,7 @@ public final class SubtypeLocaleUtils { public static String getSubtypeLanguageDisplayName(final String localeString) { final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); final String languageString; - if (sExceptionalLocaleDisplayedInRootLocale.contains(localeString)) { + if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { languageString = localeString; } else { final Locale locale = LocaleUtils.constructLocaleFromString(localeString); @@ -205,7 +211,16 @@ public final class SubtypeLocaleUtils { // No language subtype should be displayed in system locale. return sResources.getString(R.string.subtype_no_language); } - final Integer exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); + final Integer exceptionalNameResId; + if (displayLocale.equals(Locale.ROOT) + && sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { + exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(localeString); + } else if (sExceptionalLocaleToNameIdsMap.containsKey(localeString)) { + exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); + } else { + exceptionalNameResId = null; + } + final String displayName; if (exceptionalNameResId != null) { final RunInLocale<String> getExceptionalName = new RunInLocale<String>() { diff --git a/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java b/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java index d3fa0c748..86a5b19ec 100644 --- a/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java +++ b/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java @@ -21,6 +21,7 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.DistracterFilter.HandlingType; |