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.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