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");
      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