Implement popup suggestions pane

Bug: 5023981
Change-Id: Ie1d69131dbf884a3f6a2beb3ac3427e5437c1486
main
Tadashi G. Takaoka 2011-08-24 14:44:46 +09:00
parent e5c705f164
commit 29e7b7ed6e
22 changed files with 656 additions and 31 deletions

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 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.
*/
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
style="?attr/miniKeyboardPanelStyle"
>
<com.android.inputmethod.latin.MoreSuggestionsView
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
android:id="@+id/more_suggestions_view"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
latin:keyLabelSize="@dimen/candidate_text_size"
latin:keyHintLetterRatio="@fraction/more_suggestions_info_ratio"
latin:keyHintLetterColor="@android:color/white"
/>
</LinearLayout>

View File

@ -59,6 +59,8 @@
<dimen name="key_preview_offset_ics">0.01in</dimen>
<dimen name="candidate_strip_height">36dip</dimen>
<dimen name="more_suggestions_row_height">36dip</dimen>
<dimen name="candidate_strip_minimum_height">160sp</dimen>
<dimen name="candidate_strip_fading_edge_length">63dip</dimen>
<!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
<!-- popup_key_height x 1.2 -->

View File

@ -67,6 +67,7 @@
<dimen name="key_preview_offset_ics">0.05in</dimen>
<dimen name="candidate_strip_height">44dip</dimen>
<dimen name="more_suggestions_row_height">44dip</dimen>
<dimen name="candidate_strip_padding">15.0mm</dimen>
<dimen name="candidate_min_width">0.3in</dimen>
<dimen name="candidate_padding">12dip</dimen>

View File

@ -70,9 +70,8 @@
<dimen name="key_preview_offset_ics">0.05in</dimen>
<dimen name="candidate_strip_height">44dip</dimen>
<!-- candidate_strip_minimum_height =
key_preview_height_holo - key_preview_offset_holo + alpha -->
<dimen name="candidate_strip_minimum_height">18mm</dimen>
<dimen name="more_suggestions_row_height">44dip</dimen>
<dimen name="candidate_strip_minimum_height">200sp</dimen>
<dimen name="candidate_strip_padding">15.0mm</dimen>
<dimen name="candidate_min_width">46dip</dimen>
<dimen name="candidate_padding">8dip</dimen>

View File

@ -27,6 +27,7 @@
<attr name="miniKeyboardPanelStyle" format="reference" />
<!-- Suggestions strip style -->
<attr name="suggestionsStripBackgroundStyle" format="reference" />
<attr name="suggestionsPaneViewStyle" format="reference" />
<attr name="suggestionBackgroundStyle" format="reference" />
<attr name="suggestionPreviewBackgroundStyle" format="reference" />
<attr name="candidateViewStyle" format="reference" />

View File

@ -77,9 +77,11 @@
<dimen name="key_preview_offset_ics">0.05in</dimen>
<dimen name="candidate_strip_height">40dip</dimen>
<!-- candidate_strip_minimum_height =
key_preview_height_holo - key_preview_offset_holo + alpha -->
<dimen name="candidate_strip_minimum_height">100sp</dimen>
<dimen name="more_suggestions_key_horizontal_padding">12dip</dimen>
<dimen name="more_suggestions_row_height">40dip</dimen>
<dimen name="more_suggestions_slide_allowance">0.2in</dimen>
<fraction name="more_suggestions_info_ratio">12%</fraction>
<dimen name="candidate_strip_minimum_height">200sp</dimen>
<dimen name="candidate_strip_fading_edge_length">63dip</dimen>
<dimen name="candidate_strip_padding">0dip</dimen>
<dimen name="candidate_min_width">44dip</dimen>

View File

@ -79,6 +79,11 @@
<item name="android:paddingLeft">@dimen/mini_keyboard_horizontal_edges_padding</item>
<item name="android:paddingRight">@dimen/mini_keyboard_horizontal_edges_padding</item>
</style>
<style
name="SuggestionsPaneViewStyle"
parent="MiniKeyboardView"
>
</style>
<style name="SuggestionsStripBackgroundStyle">
<item name="android:background">@drawable/keyboard_suggest_strip</item>
</style>
@ -235,6 +240,11 @@
parent="MiniKeyboardPanelStyle.IceCreamSandwich"
>
</style>
<style
name="SuggestionsPaneViewStyle.IceCreamSandwich"
parent="MiniKeyboardView.IceCreamSandwich"
>
</style>
<style
name="CandidateViewStyle.IceCreamSandwich"
parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"

View File

@ -22,6 +22,7 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
<item name="candidateViewStyle">@style/CandidateViewStyle</item>

View File

@ -22,6 +22,7 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
<item name="candidateViewStyle">@style/CandidateViewStyle</item>

View File

@ -22,6 +22,7 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Gingerbread</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
<item name="candidateViewStyle">@style/CandidateViewStyle</item>

View File

@ -22,6 +22,7 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView.IceCreamSandwich</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle.IceCreamSandwich</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle.IceCreamSandwich</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle.IceCreamSandwich</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
<item name="candidateViewStyle">@style/CandidateViewStyle.IceCreamSandwich</item>

View File

@ -22,7 +22,8 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
<item name="candidateViewStyle">@style/CandidateViewStyle</item>
</style>

View File

@ -22,6 +22,7 @@
<item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item>
<item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
<item name="suggestionsPaneViewStyle">@style/SuggestionsPaneViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
<item name="candidateViewStyle">@style/CandidateViewStyle</item>

View File

@ -20,8 +20,6 @@
<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="8%p"
latin:horizontalGap="@fraction/key_horizontal_gap"
latin:verticalGap="0px"
latin:rowHeight="@dimen/popup_key_height"
>
</Keyboard>

View File

@ -20,8 +20,6 @@
<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="5.0%p"
latin:horizontalGap="0px"
latin:verticalGap="0px"
latin:rowHeight="@dimen/popup_key_height"
>
</Keyboard>

View File

@ -20,8 +20,6 @@
<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="10%p"
latin:horizontalGap="@fraction/key_horizontal_gap"
latin:verticalGap="0px"
latin:rowHeight="@dimen/popup_key_height"
>
</Keyboard>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 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.
*/
-->
<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="10%p"
latin:rowHeight="@dimen/more_suggestions_row_height"
>
</Keyboard>

View File

@ -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() {

View File

@ -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;

View File

@ -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<TextView> mWords = new ArrayList<TextView>();
private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
private final ArrayList<View> mDividers = new ArrayList<View>();
@ -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) {

View File

@ -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<Builder.MoreSuggestionsParam> {
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);
}
}
}

View File

@ -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<Key> 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;
}
}