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