1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin.suggestions; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.graphics.Paint; 22 import android.graphics.drawable.Drawable; 23 24 import com.android.inputmethod.keyboard.Key; 25 import com.android.inputmethod.keyboard.Keyboard; 26 import com.android.inputmethod.keyboard.KeyboardActionListener; 27 import com.android.inputmethod.keyboard.TypefaceUtils; 28 import com.android.inputmethod.keyboard.internal.KeyboardBuilder; 29 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; 30 import com.android.inputmethod.keyboard.internal.KeyboardParams; 31 import com.android.inputmethod.latin.R; 32 import com.android.inputmethod.latin.SuggestedWords; 33 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 34 import com.android.inputmethod.latin.Utils; 35 36 public final class MoreSuggestions extends Keyboard { 37 public static final int SUGGESTION_CODE_BASE = 1024; 38 39 public final SuggestedWords mSuggestedWords; 40 41 public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter { 42 public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info); 43 } 44 45 MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) { 46 super(params); 47 mSuggestedWords = suggestedWords; 48 } 49 50 private static final class MoreSuggestionsParam extends KeyboardParams { 51 private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS]; 52 private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS]; 53 private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS]; 54 private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS]; 55 private static final int MAX_COLUMNS_IN_ROW = 3; 56 private int mNumRows; 57 public Drawable mDivider; 58 public int mDividerWidth; 59 60 public MoreSuggestionsParam() { 61 super(); 62 } 63 64 public int layout(final SuggestedWords suggestedWords, final int fromPos, 65 final int maxWidth, final int minWidth, final int maxRow, final Paint paint, 66 final Resources res) { 67 clearKeys(); 68 mDivider = res.getDrawable(R.drawable.more_suggestions_divider); 69 mDividerWidth = mDivider.getIntrinsicWidth(); 70 final float padding = res.getDimension(R.dimen.more_suggestions_key_horizontal_padding); 71 72 int row = 0; 73 int pos = fromPos, rowStartPos = fromPos; 74 final int size = Math.min(suggestedWords.size(), SuggestionStripView.MAX_SUGGESTIONS); 75 while (pos < size) { 76 final String word = suggestedWords.getWord(pos); 77 // TODO: Should take care of text x-scaling. 78 mWidths[pos] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding); 79 final int numColumn = pos - rowStartPos + 1; 80 final int columnWidth = 81 (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn; 82 if (numColumn > MAX_COLUMNS_IN_ROW 83 || !fitInWidth(rowStartPos, pos + 1, columnWidth)) { 84 if ((row + 1) >= maxRow) { 85 break; 86 } 87 mNumColumnsInRow[row] = pos - rowStartPos; 88 rowStartPos = pos; 89 row++; 90 } 91 mColumnOrders[pos] = pos - rowStartPos; 92 mRowNumbers[pos] = row; 93 pos++; 94 } 95 mNumColumnsInRow[row] = pos - rowStartPos; 96 mNumRows = row + 1; 97 mBaseWidth = mOccupiedWidth = Math.max( 98 minWidth, calcurateMaxRowWidth(fromPos, pos)); 99 mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; 100 return pos - fromPos; 101 } 102 103 private boolean fitInWidth(final int startPos, final int endPos, final int width) { 104 for (int pos = startPos; pos < endPos; pos++) { 105 if (mWidths[pos] > width) 106 return false; 107 } 108 return true; 109 } 110 111 private int calcurateMaxRowWidth(final int startPos, final int endPos) { 112 int maxRowWidth = 0; 113 int pos = startPos; 114 for (int row = 0; row < mNumRows; row++) { 115 final int numColumnInRow = mNumColumnsInRow[row]; 116 int maxKeyWidth = 0; 117 while (pos < endPos && mRowNumbers[pos] == row) { 118 maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]); 119 pos++; 120 } 121 maxRowWidth = Math.max(maxRowWidth, 122 maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1)); 123 } 124 return maxRowWidth; 125 } 126 127 private static final int[][] COLUMN_ORDER_TO_NUMBER = { 128 { 0, }, 129 { 1, 0, }, 130 { 2, 0, 1}, 131 }; 132 133 public int getNumColumnInRow(final int pos) { 134 return mNumColumnsInRow[mRowNumbers[pos]]; 135 } 136 137 public int getColumnNumber(final int pos) { 138 final int columnOrder = mColumnOrders[pos]; 139 final int numColumn = getNumColumnInRow(pos); 140 return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder]; 141 } 142 143 public int getX(final int pos) { 144 final int columnNumber = getColumnNumber(pos); 145 return columnNumber * (getWidth(pos) + mDividerWidth); 146 } 147 148 public int getY(final int pos) { 149 final int row = mRowNumbers[pos]; 150 return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding; 151 } 152 153 public int getWidth(final int pos) { 154 final int numColumnInRow = getNumColumnInRow(pos); 155 return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow; 156 } 157 158 public void markAsEdgeKey(final Key key, final int pos) { 159 final int row = mRowNumbers[pos]; 160 if (row == 0) 161 key.markAsBottomEdge(this); 162 if (row == mNumRows - 1) 163 key.markAsTopEdge(this); 164 165 final int numColumnInRow = mNumColumnsInRow[row]; 166 final int column = getColumnNumber(pos); 167 if (column == 0) 168 key.markAsLeftEdge(this); 169 if (column == numColumnInRow - 1) 170 key.markAsRightEdge(this); 171 } 172 } 173 174 public static final class Builder extends KeyboardBuilder<MoreSuggestionsParam> { 175 private final MoreSuggestionsView mPaneView; 176 private SuggestedWords mSuggestedWords; 177 private int mFromPos; 178 private int mToPos; 179 180 public Builder(final Context context, final MoreSuggestionsView paneView) { 181 super(context, new MoreSuggestionsParam()); 182 mPaneView = paneView; 183 } 184 185 public Builder layout(final SuggestedWords suggestedWords, final int fromPos, 186 final int maxWidth, final int minWidth, final int maxRow, 187 final Keyboard parentKeyboard) { 188 final int xmlId = R.xml.kbd_suggestions_pane_template; 189 load(xmlId, parentKeyboard.mId); 190 mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; 191 192 mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight); 193 final int count = mParams.layout(suggestedWords, fromPos, maxWidth, minWidth, maxRow, 194 mPaneView.newLabelPaint(null /* key */), mResources); 195 mFromPos = fromPos; 196 mToPos = fromPos + count; 197 mSuggestedWords = suggestedWords; 198 return this; 199 } 200 201 @Override 202 public MoreSuggestions build() { 203 final MoreSuggestionsParam params = mParams; 204 for (int pos = mFromPos; pos < mToPos; pos++) { 205 final int x = params.getX(pos); 206 final int y = params.getY(pos); 207 final int width = params.getWidth(pos); 208 final String word = mSuggestedWords.getWord(pos); 209 final String info = Utils.getDebugInfo(mSuggestedWords, pos); 210 final int index = pos + SUGGESTION_CODE_BASE; 211 final Key key = new Key( 212 params, word, info, KeyboardIconsSet.ICON_UNDEFINED, index, null, x, y, 213 width, params.mDefaultRowHeight, 0); 214 params.markAsEdgeKey(key, pos); 215 params.onAddKey(key); 216 final int columnNumber = params.getColumnNumber(pos); 217 final int numColumnInRow = params.getNumColumnInRow(pos); 218 if (columnNumber < numColumnInRow - 1) { 219 final Divider divider = new Divider(params, params.mDivider, x + width, y, 220 params.mDividerWidth, params.mDefaultRowHeight); 221 params.onAddKey(divider); 222 } 223 } 224 return new MoreSuggestions(params, mSuggestedWords); 225 } 226 } 227 228 private static final class Divider extends Key.Spacer { 229 private final Drawable mIcon; 230 231 public Divider(final KeyboardParams params, final Drawable icon, final int x, 232 final int y, final int width, final int height) { 233 super(params, x, y, width, height); 234 mIcon = icon; 235 } 236 237 @Override 238 public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) { 239 // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the 240 // constructor. 241 // TODO: Drawable itself should have an alpha value. 242 mIcon.setAlpha(128); 243 return mIcon; 244 } 245 } 246 } 247