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