diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
new file mode 100644
index 000000000..d387a1573
--- /dev/null
+++ b/java/res/layout/more_suggestions.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 73e1aff96..4bf0e2092 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -59,6 +59,8 @@
0.01in
36dip
+ 36dip
+ 160sp
63dip
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 942bc726f..6ce23cc70 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -67,6 +67,7 @@
0.05in
44dip
+ 44dip
15.0mm
0.3in
12dip
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index f0340bc70..9b307e6f3 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -70,9 +70,8 @@
0.05in
44dip
-
- 18mm
+ 44dip
+ 200sp
15.0mm
46dip
8dip
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index fdeca258f..9d3426842 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -27,6 +27,7 @@
+
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 2c4b35ef8..b7609a4ac 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -77,9 +77,11 @@
0.05in
40dip
-
- 100sp
+ 12dip
+ 40dip
+ 0.2in
+ 12%
+ 200sp
63dip
0dip
44dip
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index ddc0634f4..4cc2a5306 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -79,6 +79,11 @@
- @dimen/mini_keyboard_horizontal_edges_padding
- @dimen/mini_keyboard_horizontal_edges_padding
+
@@ -235,6 +240,11 @@
parent="MiniKeyboardPanelStyle.IceCreamSandwich"
>
+
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index 494bae600..35390d111 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -22,6 +22,7 @@
- @style/MiniKeyboardView.Stone
- @style/MiniKeyboardPanelStyle
- @style/SuggestionsStripBackgroundStyle
+ - @style/SuggestionsPaneViewStyle
- @style/SuggestionBackgroundStyle
- @style/SuggestionPreviewBackgroundStyle
- @style/CandidateViewStyle
diff --git a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
index d97649965..9955fe8fd 100644
--- a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
diff --git a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
index 7d39d1a3a..1c15a5e9c 100644
--- a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
diff --git a/java/res/xml/kbd_mini_keyboard_template.xml b/java/res/xml/kbd_mini_keyboard_template.xml
index 79db081a1..d25878b48 100644
--- a/java/res/xml/kbd_mini_keyboard_template.xml
+++ b/java/res/xml/kbd_mini_keyboard_template.xml
@@ -20,8 +20,6 @@
diff --git a/java/res/xml/kbd_suggestions_pane_template.xml b/java/res/xml/kbd_suggestions_pane_template.xml
new file mode 100644
index 000000000..21316e6bb
--- /dev/null
+++ b/java/res/xml/kbd_suggestions_pane_template.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
index 520466c2a..f331662d7 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java
@@ -88,16 +88,7 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel {
}
}
- private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy() {
- @Override
- public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
- @Override
- public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {}
- @Override
- public void cancelLongPressTimer() {}
- @Override
- public void cancelKeyTimers() {}
- };
+ private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter();
private final KeyboardActionListener mMiniKeyboardListener =
new KeyboardActionListener.Adapter() {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d4f580d21..0314867b3 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -76,6 +76,17 @@ public class PointerTracker {
public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker);
public void cancelLongPressTimer();
public void cancelKeyTimers();
+
+ public static class Adapter implements TimerProxy {
+ @Override
+ public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
+ @Override
+ public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {}
+ @Override
+ public void cancelLongPressTimer() {}
+ @Override
+ public void cancelKeyTimers() {}
+ }
}
private static KeyboardSwitcher sKeyboardSwitcher;
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index b9ded31cb..0d355d01e 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -23,6 +23,7 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
+import android.os.SystemClock;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
@@ -34,8 +35,10 @@ 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;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
@@ -46,6 +49,9 @@ 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.MoreKeysPanel;
+import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.ArrayList;
@@ -58,16 +64,22 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
- private static final int MAX_SUGGESTIONS = 18;
+ public static final int MAX_SUGGESTIONS = 18;
private static final boolean DBG = LatinImeLogger.sDBG;
private final ViewGroup mCandidatesPlacer;
private final ViewGroup mCandidatesStrip;
+ // TODO: Remove these pane related fields and stuffs.
private ViewGroup mCandidatesPane;
private ViewGroup mCandidatesPaneContainer;
private View mKeyboardView;
+ private final View mMoreSuggestionsContainer;
+ private final MoreSuggestionsView mMoreSuggestionsView;
+ private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
+ private final PopupWindow mMoreSuggestionsWindow;
+
private final ArrayList mWords = new ArrayList();
private final ArrayList mInfos = new ArrayList();
private final ArrayList mDividers = new ArrayList();
@@ -159,7 +171,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mDividerHeight = divider.getMeasuredHeight();
final Resources res = word.getResources();
- mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
+ mCandidateStripHeight = res.getDimensionPixelSize(R.dimen.candidate_strip_height);
}
}
@@ -257,7 +269,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private final int mColorTypedWord;
private final int mColorAutoCorrect;
private final int mColorSuggestedCandidate;
- private final int mCandidateCountInStrip;
+ public final int mCandidateCountInStrip;
private final float mCenterCandidateWeight;
private final int mCenterCandidateIndex;
private final Drawable mMoreCandidateHint;
@@ -449,7 +461,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
}
-
return countInStrip;
}
@@ -585,6 +596,15 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mInfos);
mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos);
mStripParams.mWordToSaveView.setOnClickListener(this);
+
+ mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
+ mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
+ .findViewById(R.id.more_suggestions_view);
+ mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
+ mMoreSuggestionsWindow = new PopupWindow(context);
+ mMoreSuggestionsWindow.setWindowLayoutMode(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ mMoreSuggestionsWindow.setBackgroundDrawable(null);
}
/**
@@ -621,8 +641,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
final int width = getWidth();
final int countInStrip = mStripParams.layout(
mSuggestions, mCandidatesStrip, mCandidatesPlacer, width);
- mPaneParams.layout(
- mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
}
private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
@@ -787,6 +805,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mCandidatesStrip.removeAllViews();
mCandidatesPane.removeAllViews();
closeCandidatesPane();
+ mMoreSuggestionsWindow.dismiss();
}
private void hidePreview() {
@@ -823,15 +842,102 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
+ private final KeyboardActionListener mMoreSuggestionsListener =
+ new KeyboardActionListener.Adapter() {
+ @Override
+ public boolean onCustomRequest(int requestCode) {
+ final int index = requestCode;
+ final CharSequence word = mSuggestions.getWord(index);
+ mListener.pickSuggestionManually(index, word);
+ mMoreSuggestionsView.dismissMoreKeysPanel();
+ return true;
+ }
+
+ @Override
+ public void onCancelInput() {
+ mMoreSuggestionsView.dismissMoreKeysPanel();
+ }
+ };
+
+ private final MoreKeysPanel.Controller mMoreSuggestionsController =
+ new MoreKeysPanel.Controller() {
+ @Override
+ public boolean dismissMoreKeysPanel() {
+ if (mMoreSuggestionsWindow.isShowing()) {
+ mMoreSuggestionsWindow.dismiss();
+ return true;
+ }
+ return false;
+ }
+ };
+
@Override
public boolean onLongClick(View view) {
- if (mStripParams.mMoreSuggestionsAvailable) {
- toggleCandidatesPane();
+ final SuggestionsStripParams params = mStripParams;
+ if (params.mMoreSuggestionsAvailable) {
+ final int stripWidth = getWidth();
+ 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.mCandidateCountInStrip, maxWidth, maxHeight);
+ 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;
+ 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);
+ view.setPressed(false);
+ // TODO: Should gray out the keyboard here as well?
return true;
}
return false;
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent me) {
+ if (!mMoreSuggestionsWindow.isShowing()) {
+ return super.dispatchTouchEvent(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, 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;
+ }
+ return true;
+ }
+
@Override
public void onClick(View view) {
if (view == mStripParams.mWordToSaveView) {
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
new file mode 100644
index 000000000..0446fb2a8
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
@@ -0,0 +1,203 @@
+/*
+ * 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.latin;
+
+import android.graphics.Paint;
+import android.text.TextUtils;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+public class MoreSuggestions extends Keyboard {
+ private static final boolean DBG = LatinImeLogger.sDBG;
+
+ public static final int SUGGESTION_CODE_BASE = 1024;
+
+ private MoreSuggestions(Builder.MoreSuggestionsParam params) {
+ super(params);
+ }
+
+ public static class Builder extends KeyboardBuilder {
+ private final MoreSuggestionsView mPaneView;
+ private SuggestedWords mSuggestions;
+ private int mFromPos;
+ private int mToPos;
+
+ public static class MoreSuggestionsParam extends KeyboardParams {
+ private final int[] mWidths = new int[CandidateView.MAX_SUGGESTIONS];
+ private final int[] mRowNumbers = new int[CandidateView.MAX_SUGGESTIONS];
+ private final int[] mColumnOrders = new int[CandidateView.MAX_SUGGESTIONS];
+ private final int[] mNumColumnsInRow = new int[CandidateView.MAX_SUGGESTIONS];
+ 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) {
+ clearKeys();
+ final Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ final int padding = (int) view.getContext().getResources()
+ .getDimension(R.dimen.more_suggestions_key_horizontal_padding);
+
+ int row = 0;
+ int pos = fromPos, rowStartPos = fromPos;
+ final int size = Math.min(suggestions.size(), CandidateView.MAX_SUGGESTIONS);
+ while (pos < size) {
+ final CharSequence word = suggestions.getWord(pos);
+ // TODO: Should take care of text x-scaling.
+ mWidths[pos] = (int)view.getDefaultLabelWidth(word, paint) + padding;
+ final int numColumn = pos - rowStartPos + 1;
+ if (numColumn > MAX_COLUMNS_IN_ROW
+ || !fitInWidth(rowStartPos, pos + 1, maxWidth / numColumn)) {
+ if ((row + 1) * mDefaultRowHeight > maxHeight) {
+ break;
+ }
+ mNumColumnsInRow[row] = pos - rowStartPos;
+ rowStartPos = pos;
+ row++;
+ }
+ mColumnOrders[pos] = pos - rowStartPos;
+ mRowNumbers[pos] = row;
+ pos++;
+ }
+ mNumColumnsInRow[row] = pos - rowStartPos;
+ mNumRows = row + 1;
+ mWidth = mOccupiedWidth = calcurateMaxRowWidth(fromPos, pos);
+ mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
+ return pos - fromPos;
+ }
+
+ private boolean fitInWidth(int startPos, int endPos, int width) {
+ for (int pos = startPos; pos < endPos; pos++) {
+ if (mWidths[pos] > width)
+ return false;
+ }
+ return true;
+ }
+
+ private int calcurateMaxRowWidth(int startPos, int endPos) {
+ int maxRowWidth = 0;
+ int pos = startPos;
+ for (int row = 0; row < mNumRows; row++) {
+ final int numColumn = mNumColumnsInRow[row];
+ int maxKeyWidth = 0;
+ while (pos < endPos && mRowNumbers[pos] == row) {
+ maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
+ pos++;
+ }
+ maxRowWidth = Math.max(maxRowWidth, maxKeyWidth * numColumn);
+ }
+ return maxRowWidth;
+ }
+
+ private static final int[][] COLUMN_ORDER_TO_NUMBER = {
+ { 0, },
+ { 1, 0, },
+ { 2, 0, 1},
+ };
+
+ private int getColumnNumber(int pos) {
+ final int columnOrder = mColumnOrders[pos];
+ final int numColumn = mNumColumnsInRow[mRowNumbers[pos]];
+ return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
+ }
+
+ public int getX(int pos) {
+ final int columnNumber = getColumnNumber(pos);
+ return columnNumber * getWidth(pos);
+ }
+
+ public int getY(int pos) {
+ final int row = mRowNumbers[pos];
+ return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
+ }
+
+ public int getWidth(int pos) {
+ final int row = mRowNumbers[pos];
+ final int numColumn = mNumColumnsInRow[row];
+ return mWidth / numColumn;
+ }
+
+ public int getFlags(int pos) {
+ int rowFlags = 0;
+
+ final int row = mRowNumbers[pos];
+ if (row == 0)
+ rowFlags |= Keyboard.EDGE_BOTTOM;
+ if (row == mNumRows - 1)
+ rowFlags |= Keyboard.EDGE_TOP;
+
+ final int numColumn = mNumColumnsInRow[row];
+ final int column = getColumnNumber(pos);
+ if (column == 0)
+ rowFlags |= Keyboard.EDGE_LEFT;
+ if (column == numColumn - 1)
+ rowFlags |= Keyboard.EDGE_RIGHT;
+
+ return rowFlags;
+ }
+ }
+
+ public Builder(MoreSuggestionsView paneView) {
+ super(paneView.getContext(), new MoreSuggestionsParam());
+ mPaneView = paneView;
+ }
+
+ public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth,
+ int maxHeight) {
+ 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);
+ mFromPos = fromPos;
+ mToPos = fromPos + count;
+ mSuggestions = suggestions;
+ return this;
+ }
+
+ private static String getDebugInfo(SuggestedWords suggestions, int pos) {
+ if (!DBG) return null;
+ final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
+ if (wordInfo == null) return null;
+ final String info = wordInfo.getDebugString();
+ if (TextUtils.isEmpty(info)) return null;
+ return info;
+ }
+
+ @Override
+ public MoreSuggestions build() {
+ final MoreSuggestionsParam params = mParams;
+ for (int pos = mFromPos; pos < mToPos; pos++) {
+ final String word = mSuggestions.getWord(pos).toString();
+ final String info = getDebugInfo(mSuggestions, pos);
+ final int index = pos + SUGGESTION_CODE_BASE;
+ final Key key = new Key(
+ params, word, info, null, index, null, params.getX(pos), params.getY(pos),
+ params.getWidth(pos), params.mDefaultRowHeight, params.getFlags(pos));
+ params.onAddKey(key);
+ }
+ return new MoreSuggestions(params);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
new file mode 100644
index 000000000..828490112
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
@@ -0,0 +1,237 @@
+/*
+ * 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.latin;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.PopupWindow;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardView;
+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.TimerProxy;
+
+import java.util.List;
+
+/**
+ * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
+ * key presses and touch movements.
+ */
+public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
+ private final int[] mCoordinates = new int[2];
+
+ private final KeyDetector mKeyDetector;
+
+ private Controller mController;
+ private KeyboardActionListener mListener;
+ private int mOriginX;
+ private int mOriginY;
+
+ private static class SuggestionsPaneKeyDetector extends KeyDetector {
+ private final int mSlideAllowanceSquare;
+ private final int mSlideAllowanceSquareTop;
+
+ public SuggestionsPaneKeyDetector(float slideAllowance) {
+ super(/* keyHysteresisDistance */0);
+ mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
+ // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
+ mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
+ }
+
+ @Override
+ public boolean alwaysAllowsSlidingInput() {
+ return true;
+ }
+
+ @Override
+ protected int getMaxNearbyKeys() {
+ // No nearby key will be returned.
+ return 1;
+ }
+
+ @Override
+ public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
+ final List keys = getKeyboard().mKeys;
+ final int touchX = getTouchX(x);
+ final int touchY = getTouchY(y);
+
+ int nearestIndex = NOT_A_KEY;
+ int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
+ final int keyCount = keys.size();
+ for (int index = 0; index < keyCount; index++) {
+ final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
+ if (dist < nearestDist) {
+ nearestIndex = index;
+ nearestDist = dist;
+ }
+ }
+
+ if (allCodes != null && nearestIndex != NOT_A_KEY)
+ allCodes[0] = keys.get(nearestIndex).mCode;
+ return nearestIndex;
+ }
+ }
+
+ private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter();
+
+ private final KeyboardActionListener mSuggestionsPaneListener =
+ new KeyboardActionListener.Adapter() {
+ @Override
+ public void onPress(int primaryCode, boolean withSliding) {
+ mListener.onPress(primaryCode, withSliding);
+ }
+
+ @Override
+ public void onRelease(int primaryCode, boolean withSliding) {
+ mListener.onRelease(primaryCode, withSliding);
+ }
+
+ @Override
+ public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
+ mListener.onCustomRequest(primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE);
+ }
+
+ @Override
+ public void onCancelInput() {
+ mListener.onCancelInput();
+ }
+ };
+
+ public MoreSuggestionsView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.suggestionsPaneViewStyle);
+ }
+
+ public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final Resources res = context.getResources();
+ // Override default ProximityKeyDetector.
+ mKeyDetector = new SuggestionsPaneKeyDetector(res.getDimension(
+ R.dimen.more_suggestions_slide_allowance));
+ // Remove gesture detector on suggestions pane
+ setKeyPreviewPopupEnabled(false, 0);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard != null) {
+ final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+ final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+ setMeasuredDimension(width, height);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ public void setKeyboard(Keyboard keyboard) {
+ super.setKeyboard(keyboard);
+ mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+ -getPaddingTop() + mVerticalCorrection);
+ }
+
+ @Override
+ public KeyDetector getKeyDetector() {
+ return mKeyDetector;
+ }
+
+ @Override
+ public KeyboardActionListener getKeyboardActionListener() {
+ return mSuggestionsPaneListener;
+ }
+
+ @Override
+ public DrawingProxy getDrawingProxy() {
+ return this;
+ }
+
+ @Override
+ public TimerProxy getTimerProxy() {
+ return EMPTY_TIMER_PROXY;
+ }
+
+ @Override
+ public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ // Suggestions pane needs no pop-up key preview displayed, so we pass always false with a
+ // delay of 0. The delay does not matter actually since the popup is not shown anyway.
+ super.setKeyPreviewPopupEnabled(false, 0);
+ }
+
+ @Override
+ public void setShifted(boolean shifted) {
+ // Nothing to do with.
+ }
+
+ @Override
+ public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
+ PopupWindow window, KeyboardActionListener listener) {
+ mController = controller;
+ mListener = listener;
+ final View container = (View)getParent();
+ final MoreSuggestions pane = (MoreSuggestions)getKeyboard();
+
+ parentView.getLocationInWindow(mCoordinates);
+ final int paneLeft = pointX - (pane.mOccupiedWidth / 2) + parentView.getPaddingLeft();
+ final int x = wrapUp(Math.max(0, Math.min(paneLeft,
+ parentView.getWidth() - pane.mOccupiedWidth))
+ - container.getPaddingLeft() + mCoordinates[0],
+ container.getMeasuredWidth(), 0, parentView.getWidth());
+ final int y = pointY
+ - (container.getMeasuredHeight() - container.getPaddingBottom())
+ + parentView.getPaddingTop() + mCoordinates[1];
+
+ window.setContentView(container);
+ window.setWidth(container.getMeasuredWidth());
+ window.setHeight(container.getMeasuredHeight());
+ window.showAtLocation(parentView, Gravity.NO_GRAVITY, x, y);
+
+ mOriginX = x + container.getPaddingLeft() - mCoordinates[0];
+ mOriginY = y + container.getPaddingTop() - mCoordinates[1];
+ }
+
+ private static int wrapUp(int x, int width, int left, int right) {
+ if (x < left)
+ return left;
+ if (x + width > right)
+ return right - width;
+ return x;
+ }
+
+ @Override
+ public boolean dismissMoreKeysPanel() {
+ return mController.dismissMoreKeysPanel();
+ }
+
+ @Override
+ public int translateX(int x) {
+ return x - mOriginX;
+ }
+
+ @Override
+ public int translateY(int y) {
+ return y - mOriginY;
+ }
+}