Home | History | Annotate | Download | only in suggestions
      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