diff options
Diffstat (limited to 'java/src')
11 files changed, 247 insertions, 190 deletions
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java index b6b86a4a0..a6bb83adf 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java @@ -100,8 +100,7 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper public String getLocale() { if (mObj == null) return mDummyLocale; final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale); - if (TextUtils.isEmpty(s)) return DEFAULT_LOCALE; - return s; + return s != null ? s : DEFAULT_LOCALE; } public String getMode() { diff --git a/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java b/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java deleted file mode 100644 index 674cbe74b..000000000 --- a/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.compat; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; - -import java.lang.reflect.Field; - -public class LinearLayoutCompatUtils { - private static final String TAG = LinearLayoutCompatUtils.class.getSimpleName(); - - private static final Class<?> CLASS_R_STYLEABLE = CompatUtils.getClass( - "com.android.internal.R$styleable"); - private static final Field STYLEABLE_VIEW = CompatUtils.getField( - CLASS_R_STYLEABLE, "View"); - private static final Field STYLEABLE_VIEW_BACKGROUND = CompatUtils.getField( - CLASS_R_STYLEABLE, "View_background"); - private static final Object VALUE_STYLEABLE_VIEW = CompatUtils.getFieldValue( - null, null, STYLEABLE_VIEW); - private static final Integer VALUE_STYLEABLE_VIEW_BACKGROUND = - (Integer)CompatUtils.getFieldValue(null, null, STYLEABLE_VIEW_BACKGROUND); - - public static Drawable getBackgroundDrawable(Context context, AttributeSet attrs, - int defStyleAttr, int defStyleRes) { - if (!(VALUE_STYLEABLE_VIEW instanceof int[]) || VALUE_STYLEABLE_VIEW_BACKGROUND == null) { - Log.w(TAG, "Can't get View background attribute using reflection"); - return null; - } - - final int[] styleableView = (int[])VALUE_STYLEABLE_VIEW; - final TypedArray a = context.obtainStyledAttributes( - attrs, styleableView, defStyleAttr, defStyleRes); - final Drawable background = a.getDrawable(VALUE_STYLEABLE_VIEW_BACKGROUND); - a.recycle(); - return background; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index d9089e199..4ec16bdcf 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -438,8 +438,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mMoreKeysWindow = new PopupWindow(getContext()); mMoreKeysWindow.setBackgroundDrawable(null); mMoreKeysWindow.setAnimationStyle(R.style.MiniKeyboardAnimation); - // Allow popup window to be drawn off the screen. - mMoreKeysWindow.setClippingEnabled(false); } mMoreKeysPanel = moreKeysPanel; mMoreKeysPanelPointerTrackerId = tracker.mPointerId; @@ -552,7 +550,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mOldKeyIndex = tracker.getKeyIndexOn(lastX, lastY); tracker.onUpEvent(lastX, lastY, eventTime); } else if (pointerCount == 1 && oldPointerCount == 1) { - processMotionEvent(tracker, action, x, y, eventTime, this); + tracker.processMotionEvent(action, x, y, eventTime, this); } else { Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount + " (old " + oldPointerCount + ")"); @@ -575,32 +573,12 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke tracker.onMoveEvent(px, py, eventTime); } } else { - processMotionEvent(getPointerTracker(id), action, x, y, eventTime, this); + getPointerTracker(id).processMotionEvent(action, x, y, eventTime, this); } return true; } - private static void processMotionEvent(PointerTracker tracker, int action, int x, int y, - long eventTime, PointerTracker.KeyEventHandler handler) { - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - tracker.onDownEvent(x, y, eventTime, handler); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - tracker.onUpEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_MOVE: - tracker.onMoveEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_CANCEL: - tracker.onCancelEvent(x, y, eventTime); - break; - } - } - @Override public void closing() { super.closing(); diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index da91b62d9..ad8056cc0 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -123,7 +123,9 @@ public class MiniKeyboard extends Keyboard { } mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth; - mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; + // Need to subtract the bottom row's gutter only. + mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap + + mTopPadding + mBottomPadding; } // Return key position according to column count (0 is default). @@ -209,20 +211,9 @@ public class MiniKeyboard extends Keyboard { super(view.getContext(), new MiniKeyboardParams()); load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); - // HACK: Current mini keyboard design totally relies on the 9-patch - // padding about horizontal - // and vertical key spacing. To keep the visual of mini keyboard as - // is, these hacks are - // needed to keep having the same horizontal and vertical key - // spacing. - mParams.mHorizontalGap = 0; - mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; - // TODO: When we have correctly padded key background 9-patch - // drawables for mini keyboard, - // revert the above hacks and uncomment the following lines. - // mParams.mHorizontalGap = parentKeyboard.mHorizontalGap; - // mParams.mVerticalGap = parentKeyboard.mVerticalGap; - + // TODO: Mini keyboard's vertical gap is currently calculated heuristically. + // Should revise the algorithm. + mParams.mVerticalGap = parentKeyboard.mVerticalGap / 2; mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard; mMoreKeys = parentKey.mMoreKeys; diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java index 29861fe4a..0e6e129bb 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java @@ -79,10 +79,8 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { super(context, attrs, defStyle); final Resources res = context.getResources(); - // Override default ProximityKeyDetector. - mKeyDetector = new MoreKeysDetector(res.getDimension( - R.dimen.mini_keyboard_slide_allowance)); - // Remove gesture detector on mini-keyboard + mKeyDetector = new MoreKeysDetector( + res.getDimension(R.dimen.mini_keyboard_slide_allowance)); setKeyPreviewPopupEnabled(false, 0); } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 0314867b3..38c419dc6 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.res.Resources; import android.util.Log; +import android.view.MotionEvent; import android.widget.TextView; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; @@ -398,6 +399,26 @@ public class PointerTracker { return onMoveKeyInternal(x, y); } + public void processMotionEvent(int action, int x, int y, long eventTime, + KeyEventHandler handler) { + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + onDownEvent(x, y, eventTime, handler); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + onUpEvent(x, y, eventTime); + break; + case MotionEvent.ACTION_MOVE: + onMoveEvent(x, y, eventTime); + break; + case MotionEvent.ACTION_CANCEL: + onCancelEvent(x, y, eventTime); + break; + } + } + public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) { if (DEBUG_EVENT) printTouchEvent("onDownEvent:", x, y, eventTime); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index cea59fe0a..191ae5811 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -157,6 +157,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private Settings.Values mSettingsValues; + private View mKeyPreviewBackingView; private View mSuggestionsContainer; private int mSuggestionsStripHeight; private SuggestionsView mSuggestionsView; @@ -607,6 +608,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void setInputView(View view) { super.setInputView(view); + mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); mSuggestionsContainer = view.findViewById(R.id.suggestions_container); mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view); if (mSuggestionsView != null) @@ -945,12 +947,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true); if (isFullscreenMode()) { // No need to have extra space to show the key preview. - mSuggestionsContainer.setMinimumHeight(0); + mKeyPreviewBackingView.setVisibility(View.GONE); mSuggestionsContainer.setVisibility( shouldShowSuggestions ? View.VISIBLE : View.GONE); } else { // We must control the visibility of the suggestion strip in order to avoid clipped // key previews, even when we don't show the suggestion strip. + mKeyPreviewBackingView.setVisibility(View.VISIBLE); mSuggestionsContainer.setVisibility( shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE); } @@ -967,15 +970,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView == null || mSuggestionsContainer == null) return; - final int containerHeight = mSuggestionsContainer.getHeight(); - int touchY = containerHeight; + final int backingHeight = (mKeyPreviewBackingView.getVisibility() == View.GONE) ? 0 + : mKeyPreviewBackingView.getHeight(); + final int extraHeight = mSuggestionsContainer.getHeight() + backingHeight; + int touchY = extraHeight; // Need to set touchable region only if input view is being shown if (mKeyboardSwitcher.isInputViewShown()) { if (mSuggestionsContainer.getVisibility() == View.VISIBLE) { touchY -= mSuggestionsStripHeight; } final int touchWidth = inputView.getWidth(); - final int touchHeight = inputView.getHeight() + containerHeight + final int touchHeight = inputView.getHeight() + extraHeight // Extend touchable region below the keyboard. + EXTENDED_TOUCHABLE_REGION_HEIGHT; if (DEBUG) { diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java index a9e75b7b3..10d5b5c48 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java @@ -50,8 +50,8 @@ public class MoreSuggestions extends Keyboard { private static final int MAX_COLUMNS_IN_ROW = 3; private int mNumRows; - public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int maxHeight, - KeyboardView view) { + public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int minWidth, + int maxRow, KeyboardView view) { clearKeys(); final Paint paint = new Paint(); paint.setAntiAlias(true); @@ -68,7 +68,7 @@ public class MoreSuggestions extends Keyboard { final int numColumn = pos - rowStartPos + 1; if (numColumn > MAX_COLUMNS_IN_ROW || !fitInWidth(rowStartPos, pos + 1, maxWidth / numColumn)) { - if ((row + 1) * mDefaultRowHeight > maxHeight) { + if ((row + 1) >= maxRow) { break; } mNumColumnsInRow[row] = pos - rowStartPos; @@ -81,7 +81,7 @@ public class MoreSuggestions extends Keyboard { } mNumColumnsInRow[row] = pos - rowStartPos; mNumRows = row + 1; - mWidth = mOccupiedWidth = calcurateMaxRowWidth(fromPos, pos); + mWidth = mOccupiedWidth = Math.max(minWidth, calcurateMaxRowWidth(fromPos, pos)); mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; return pos - fromPos; } @@ -163,13 +163,14 @@ public class MoreSuggestions extends Keyboard { } public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth, - int maxHeight) { + int minWidth, int maxRow) { final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard(); final int xmlId = R.xml.kbd_suggestions_pane_template; load(keyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2; - final int count = mParams.layout(suggestions, fromPos, maxWidth, maxHeight, mPaneView); + final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow, + mPaneView); mFromPos = fromPos; mToPos = fromPos + count; mSuggestions = suggestions; diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java index 9fd90241b..f595510a3 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.widget.PopupWindow; @@ -29,7 +30,9 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysDetector; import com.android.inputmethod.keyboard.MoreKeysPanel; +import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; +import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; @@ -40,7 +43,8 @@ import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { private final int[] mCoordinates = new int[2]; - private final KeyDetector mKeyDetector; + private final KeyDetector mModalPanelKeyDetector; + private final KeyDetector mSlidingPanelKeyDetector; private Controller mController; private KeyboardActionListener mListener; @@ -73,17 +77,16 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { }; public MoreSuggestionsView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.suggestionsPaneViewStyle); + this(context, attrs, R.attr.moreSuggestionsViewStyle); } public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final Resources res = context.getResources(); - // Override default ProximityKeyDetector. - mKeyDetector = new MoreKeysDetector(res.getDimension( - R.dimen.more_suggestions_slide_allowance)); - // Remove gesture detector on suggestions pane + mModalPanelKeyDetector = new KeyDetector(/* keyHysteresisDistance */ 0); + mSlidingPanelKeyDetector = new MoreKeysDetector( + res.getDimension(R.dimen.more_suggestions_slide_allowance)); setKeyPreviewPopupEnabled(false, 0); } @@ -102,13 +105,14 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { @Override public void setKeyboard(Keyboard keyboard) { super.setKeyboard(keyboard); - mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), + mModalPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop()); + mSlidingPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); } @Override public KeyDetector getKeyDetector() { - return mKeyDetector; + return mSlidingPanelKeyDetector; } @Override @@ -187,4 +191,39 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { public int translateY(int y) { return y - mOriginY; } + + private final KeyEventHandler mModalPanelKeyEventHandler = new KeyEventHandler() { + @Override + public KeyDetector getKeyDetector() { + return mModalPanelKeyDetector; + } + + @Override + public KeyboardActionListener getKeyboardActionListener() { + return mSuggestionsPaneListener; + } + + @Override + public DrawingProxy getDrawingProxy() { + return MoreSuggestionsView.this; + } + + @Override + public TimerProxy getTimerProxy() { + return EMPTY_TIMER_PROXY; + } + }; + + @Override + public boolean onTouchEvent(MotionEvent me) { + final int action = me.getAction(); + final long eventTime = me.getEventTime(); + final int index = me.getActionIndex(); + final int id = me.getPointerId(index); + final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); + tracker.processMotionEvent(action, x, y, eventTime, mModalPanelKeyEventHandler); + return true; + } } diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java index 10cd73dd3..22aa5e259 100644 --- a/java/src/com/android/inputmethod/latin/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java @@ -35,7 +35,6 @@ import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -45,10 +44,10 @@ import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.PopupWindow; +import android.widget.RelativeLayout; import android.widget.TextView; import com.android.inputmethod.compat.FrameLayoutCompatUtils; -import com.android.inputmethod.compat.LinearLayoutCompatUtils; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; @@ -58,7 +57,8 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.List; -public class SuggestionsView extends LinearLayout implements OnClickListener, OnLongClickListener { +public class SuggestionsView extends RelativeLayout implements OnClickListener, + OnLongClickListener { public interface Listener { public boolean addWordToDictionary(String word); public void pickSuggestionManually(int index, CharSequence word); @@ -69,7 +69,6 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On private static final boolean DBG = LatinImeLogger.sDBG; - private final ViewGroup mSuggestionsPlacer; private final ViewGroup mSuggestionsStrip; private KeyboardView mKeyboardView; @@ -146,12 +145,16 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On private static class SuggestionsViewParams { private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40; + private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; private static final int PUNCTUATIONS_IN_STRIP = 6; public final int mPadding; public final int mDividerWidth; public final int mSuggestionsStripHeight; public final int mSuggestionsCountInStrip; + public final int mMaxMoreSuggestionsRow; + public final float mMinMoreSuggestionsWidth; + public final int mMoreSuggestionsBottomGap; private final List<TextView> mWords; private final List<View> mDividers; @@ -211,10 +214,17 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On mCenterSuggestionWeight = a.getInt( R.styleable.SuggestionsView_centerSuggestionPercentile, DEFAULT_CENTER_SUGGESTION_PERCENTILE) / 100.0f; + mMaxMoreSuggestionsRow = a.getInt( + R.styleable.SuggestionsView_maxMoreSuggestionsRow, + DEFAULT_MAX_MORE_SUGGESTIONS_ROW); + mMinMoreSuggestionsWidth = getRatio(a, + R.styleable.SuggestionsView_minMoreSuggestionsWidth); a.recycle(); mCenterSuggestionIndex = mSuggestionsCountInStrip / 2; mMoreSuggestionsHint = res.getDrawable(R.drawable.more_suggestions_hint); + mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( + R.dimen.more_suggestions_bottom_gap); mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); @@ -225,6 +235,11 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); } + // Read fraction value in TypedArray as float. + private static float getRatio(TypedArray a, int index) { + return a.getFraction(index, 1000, 1000, 1) / 1000.0f; + } + private CharSequence getStyledSuggestionWord(SuggestedWords suggestions, int pos) { final CharSequence word = suggestions.getWord(pos); final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions); @@ -451,18 +466,7 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On } public SuggestionsView(Context context, AttributeSet attrs, int defStyle) { - // Note: Up to version 10 (Gingerbread) of the API, LinearLayout doesn't have 3-argument - // constructor. - // TODO: Call 3-argument constructor, super(context, attrs, defStyle), when we abandon - // backward compatibility with the version 10 or earlier of the API. - super(context, attrs); - if (defStyle != R.attr.suggestionsViewStyle) { - throw new IllegalArgumentException( - "can't accept defStyle other than R.attr.suggestionsViewStyle: defStyle=" - + defStyle); - } - setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable( - context, attrs, defStyle, R.style.SuggestionsViewStyle)); + super(context, attrs, defStyle); final LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.suggestions_strip, this); @@ -474,7 +478,6 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On mPreviewPopup.setContentView(mPreviewText); mPreviewPopup.setBackgroundDrawable(null); - mSuggestionsPlacer = (ViewGroup)findViewById(R.id.suggestions_placer); mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip); for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) { final TextView word = (TextView)inflater.inflate(R.layout.suggestion_word, null); @@ -500,6 +503,9 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On mMoreSuggestionsWindow.setWindowLayoutMode( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mMoreSuggestionsWindow.setBackgroundDrawable(null); + final Resources res = context.getResources(); + mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset( + R.dimen.more_suggestions_modal_tolerance); } /** @@ -527,7 +533,7 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On if (mSuggestions.size() == 0) return; - mParams.layout(mSuggestions, mSuggestionsStrip, mSuggestionsPlacer, getWidth()); + mParams.layout(mSuggestions, mSuggestionsStrip, this, getWidth()); } private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { @@ -648,9 +654,9 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On public void clear() { mShowingAutoCorrectionInverted = false; - mSuggestionsPlacer.removeAllViews(); - mSuggestionsPlacer.addView(mSuggestionsStrip); mSuggestionsStrip.removeAllViews(); + removeAllViews(); + addView(mSuggestionsStrip); dismissMoreSuggestions(); } @@ -730,27 +736,23 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); - final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); - // TODO: Revise how we determine the height - final int maxHeight = dm.heightPixels - mKeyboardView.getHeight() - getHeight() * 3; final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; - builder.layout(mSuggestions, params.mSuggestionsCountInStrip, maxWidth, maxHeight); + builder.layout(mSuggestions, params.mSuggestionsCountInStrip, maxWidth, + (int)(maxWidth * params.mMinMoreSuggestionsWidth), + params.mMaxMoreSuggestionsRow); mMoreSuggestionsView.setKeyboard(builder.build()); container.measure( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; final int pointX = stripWidth / 2; - final int pointY = 0; + final int pointY = -params.mMoreSuggestionsBottomGap; moreKeysPanel.showMoreKeysPanel( this, mMoreSuggestionsController, pointX, pointY, mMoreSuggestionsWindow, mMoreSuggestionsListener); - // TODO: Should figure out how to select the pointer tracker correctly. - final PointerTracker tracker = PointerTracker.getPointerTracker(0, moreKeysPanel); - final int translatedX = moreKeysPanel.translateX(tracker.getLastX()); - final int translatedY = moreKeysPanel.translateY(tracker.getLastY()); - tracker.onShowMoreKeysPanel( - translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel); + mCheckingIfModalOrSlidingMode = true; + mOriginX = mLastX; + mOriginY = mLastY; view.setPressed(false); mKeyboardView.dimEntireKeyboard(true); return true; @@ -758,34 +760,51 @@ public class SuggestionsView extends LinearLayout implements OnClickListener, On return false; } + // Working variables for onLongClick and dispatchTouchEvent. + private boolean mCheckingIfModalOrSlidingMode; + private int mLastX; + private int mLastY; + private int mOriginX; + private int mOriginY; + private final int mMoreSuggestionsModalTolerance; + @Override public boolean dispatchTouchEvent(MotionEvent me) { if (!mMoreSuggestionsWindow.isShowing()) { + mLastX = (int)me.getX(); + mLastY = (int)me.getY(); return super.dispatchTouchEvent(me); } + + final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; final int action = me.getAction(); final long eventTime = me.getEventTime(); final int index = me.getActionIndex(); final int id = me.getPointerId(index); - final PointerTracker tracker = PointerTracker.getPointerTracker(id, mMoreSuggestionsView); - final int x = mMoreSuggestionsView.translateX((int)me.getX(index)); - final int y = mMoreSuggestionsView.translateY((int)me.getY(index)); - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - tracker.onDownEvent(x, y, eventTime, mMoreSuggestionsView); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - tracker.onUpEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_MOVE: - tracker.onMoveEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_CANCEL: - tracker.onCancelEvent(x, y, eventTime); - break; + final PointerTracker tracker = PointerTracker.getPointerTracker(id, moreKeysPanel); + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); + final int translatedX = moreKeysPanel.translateX(x); + final int translatedY = moreKeysPanel.translateY(y); + + if (mCheckingIfModalOrSlidingMode) { + final int deltaX = Math.abs(x - mOriginX); + final int deltaY = Math.abs(y - mOriginY); + if (deltaX >= mMoreSuggestionsModalTolerance + || deltaY >= mMoreSuggestionsModalTolerance) { + // Decided to be in the sliding input mode + mCheckingIfModalOrSlidingMode = false; + tracker.onShowMoreKeysPanel( + translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel); + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) { + // Decided to be in the modal input mode + mCheckingIfModalOrSlidingMode = false; + } + return true; } + + // Process sliding motion events + tracker.processMotionEvent(action, translatedX, translatedY, eventTime, moreKeysPanel); return true; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index a6a5b6dd6..85c142f1e 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -34,6 +34,7 @@ import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.LocaleUtils; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; import com.android.inputmethod.latin.UserDictionary; import com.android.inputmethod.latin.Utils; @@ -62,18 +63,38 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private Map<String, Dictionary> mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); + private double mTypoThreshold; + + @Override public void onCreate() { + super.onCreate(); + mTypoThreshold = Double.parseDouble(getString(R.string.spellchecker_typo_threshold_value)); + } + @Override public Session createSession() { - return new AndroidSpellCheckerSession(); + return new AndroidSpellCheckerSession(this); } private static class SuggestionsGatherer implements WordCallback { + public static class Result { + public final String[] mSuggestions; + public final boolean mLooksLikeTypo; + public Result(final String[] gatheredSuggestions, final boolean looksLikeTypo) { + mSuggestions = gatheredSuggestions; + mLooksLikeTypo = looksLikeTypo; + } + } + private final int DEFAULT_SUGGESTION_LENGTH = 16; private final ArrayList<CharSequence> mSuggestions; private final int[] mScores; private final int mMaxLength; private int mLength = 0; - private boolean mSeenSuggestions = false; + + // The two following attributes are only ever filled if the requested max length + // is 0 (or less, which is treated the same). + private String mBestSuggestion = null; + private int mBestScore = Integer.MIN_VALUE; // As small as possible SuggestionsGatherer(final int maxLength) { mMaxLength = maxLength; @@ -89,14 +110,26 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // if it doesn't. See documentation for binarySearch. final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; - mSeenSuggestions = true; if (mLength < mMaxLength) { final int copyLen = mLength - insertIndex; ++mLength; System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); } else { - if (insertIndex == 0) return true; + if (insertIndex == 0) { + // If the maxLength is 0 (should never be less, but if it is, it's treated as 0) + // then we need to keep track of the best suggestion in mBestScore and + // mBestSuggestion. This is so that we know whether the best suggestion makes + // the score cutoff, since we need to know that to return a meaningful + // looksLikeTypo. + if (0 >= mMaxLength) { + if (score > mBestScore) { + mBestScore = score; + mBestSuggestion = new String(word, wordOffset, wordLength); + } + } + return true; + } System.arraycopy(mScores, 1, mScores, 0, insertIndex); mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); mSuggestions.remove(0); @@ -106,20 +139,41 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return true; } - public String[] getGatheredSuggestions() { - if (!mSeenSuggestions) return null; - if (0 == mLength) return EMPTY_STRING_ARRAY; - - if (DBG) { - if (mLength != mSuggestions.size()) { - Log.e(TAG, "Suggestion size is not the same as stored mLength"); + public Result getResults(final CharSequence originalText, final double threshold) { + final String[] gatheredSuggestions; + final boolean looksLikeTypo; + if (0 == mLength) { + // Either we found no suggestions, or we found some BUT the max length was 0. + // If we found some mBestSuggestion will not be null. If it is null, then + // we found none, regardless of the max length. + if (null == mBestSuggestion) { + gatheredSuggestions = null; + looksLikeTypo = false; + } else { + gatheredSuggestions = EMPTY_STRING_ARRAY; + final double normalizedScore = + Utils.calcNormalizedScore(originalText, mBestSuggestion, mBestScore); + looksLikeTypo = (normalizedScore > threshold); + } + } else { + if (DBG) { + if (mLength != mSuggestions.size()) { + Log.e(TAG, "Suggestion size is not the same as stored mLength"); + } } + Collections.reverse(mSuggestions); + Utils.removeDupes(mSuggestions); + // This returns a String[], while toArray() returns an Object[] which cannot be cast + // into a String[]. + gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY); + + final int bestScore = mScores[0]; + final CharSequence bestSuggestion = mSuggestions.get(0); + final double normalizedScore = + Utils.calcNormalizedScore(originalText, bestSuggestion, bestScore); + looksLikeTypo = (normalizedScore > threshold); } - Collections.reverse(mSuggestions); - Utils.removeDupes(mSuggestions); - // This returns a String[], while toArray() returns an Object[] which cannot be cast - // into a String[]. - return mSuggestions.toArray(EMPTY_STRING_ARRAY); + return new Result(gatheredSuggestions, looksLikeTypo); } } @@ -164,16 +218,22 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return new DictAndProximity(dictionaryCollection, proximityInfo); } - private class AndroidSpellCheckerSession extends Session { + private static class AndroidSpellCheckerSession extends Session { // Immutable, but need the locale which is not available in the constructor yet - DictionaryPool mDictionaryPool; + private DictionaryPool mDictionaryPool; // Likewise - Locale mLocale; + private Locale mLocale; + + private final AndroidSpellCheckerService mService; + + AndroidSpellCheckerSession(final AndroidSpellCheckerService service) { + mService = service; + } @Override public void onCreate() { final String localeString = getLocale(); - mDictionaryPool = getDictionaryPool(localeString); + mDictionaryPool = mService.getDictionaryPool(localeString); mLocale = LocaleUtils.constructLocaleFromString(localeString); } @@ -242,13 +302,14 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return EMPTY_SUGGESTIONS_INFO; } - final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); + final SuggestionsGatherer.Result result = + suggestionsGatherer.getResults(text, mService.mTypoThreshold); final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) - | (null != suggestions + | (result.mLooksLikeTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0); - return new SuggestionsInfo(flags, suggestions); + return new SuggestionsInfo(flags, result.mSuggestions); } } } |