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.internal.KeyboardBuilder;
     28 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
     29 import com.android.inputmethod.keyboard.internal.KeyboardParams;
     30 import com.android.inputmethod.latin.R;
     31 import com.android.inputmethod.latin.SuggestedWords;
     32 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
     33 import com.android.inputmethod.latin.utils.TypefaceUtils;
     34 
     35 public final class MoreSuggestions extends Keyboard {
     36     public static final int SUGGESTION_CODE_BASE = 1024;
     37 
     38     public final SuggestedWords mSuggestedWords;
     39 
     40     public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
     41         public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
     42     }
     43 
     44     MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) {
     45         super(params);
     46         mSuggestedWords = suggestedWords;
     47     }
     48 
     49     private static final class MoreSuggestionsParam extends KeyboardParams {
     50         private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
     51         private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
     52         private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
     53         private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
     54         private static final int MAX_COLUMNS_IN_ROW = 3;
     55         private int mNumRows;
     56         public Drawable mDivider;
     57         public int mDividerWidth;
     58 
     59         public MoreSuggestionsParam() {
     60             super();
     61         }
     62 
     63         public int layout(final SuggestedWords suggestedWords, final int fromIndex,
     64                 final int maxWidth, final int minWidth, final int maxRow, final Paint paint,
     65                 final Resources res) {
     66             clearKeys();
     67             mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
     68             mDividerWidth = mDivider.getIntrinsicWidth();
     69             final float padding = res.getDimension(R.dimen.more_suggestions_key_horizontal_padding);
     70 
     71             int row = 0;
     72             int index = fromIndex;
     73             int rowStartIndex = fromIndex;
     74             final int size = Math.min(suggestedWords.size(), SuggestionStripView.MAX_SUGGESTIONS);
     75             while (index < size) {
     76                 final String word = suggestedWords.getWord(index);
     77                 // TODO: Should take care of text x-scaling.
     78                 mWidths[index] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding);
     79                 final int numColumn = index - rowStartIndex + 1;
     80                 final int columnWidth =
     81                         (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
     82                 if (numColumn > MAX_COLUMNS_IN_ROW
     83                         || !fitInWidth(rowStartIndex, index + 1, columnWidth)) {
     84                     if ((row + 1) >= maxRow) {
     85                         break;
     86                     }
     87                     mNumColumnsInRow[row] = index - rowStartIndex;
     88                     rowStartIndex = index;
     89                     row++;
     90                 }
     91                 mColumnOrders[index] = index - rowStartIndex;
     92                 mRowNumbers[index] = row;
     93                 index++;
     94             }
     95             mNumColumnsInRow[row] = index - rowStartIndex;
     96             mNumRows = row + 1;
     97             mBaseWidth = mOccupiedWidth = Math.max(
     98                     minWidth, calcurateMaxRowWidth(fromIndex, index));
     99             mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
    100             return index - fromIndex;
    101         }
    102 
    103         private boolean fitInWidth(final int startIndex, final int endIndex, final int width) {
    104             for (int index = startIndex; index < endIndex; index++) {
    105                 if (mWidths[index] > width)
    106                     return false;
    107             }
    108             return true;
    109         }
    110 
    111         private int calcurateMaxRowWidth(final int startIndex, final int endIndex) {
    112             int maxRowWidth = 0;
    113             int index = startIndex;
    114             for (int row = 0; row < mNumRows; row++) {
    115                 final int numColumnInRow = mNumColumnsInRow[row];
    116                 int maxKeyWidth = 0;
    117                 while (index < endIndex && mRowNumbers[index] == row) {
    118                     maxKeyWidth = Math.max(maxKeyWidth, mWidths[index]);
    119                     index++;
    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 index) {
    134             return mNumColumnsInRow[mRowNumbers[index]];
    135         }
    136 
    137         public int getColumnNumber(final int index) {
    138             final int columnOrder = mColumnOrders[index];
    139             final int numColumn = getNumColumnInRow(index);
    140             return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
    141         }
    142 
    143         public int getX(final int index) {
    144             final int columnNumber = getColumnNumber(index);
    145             return columnNumber * (getWidth(index) + mDividerWidth);
    146         }
    147 
    148         public int getY(final int index) {
    149             final int row = mRowNumbers[index];
    150             return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
    151         }
    152 
    153         public int getWidth(final int index) {
    154             final int numColumnInRow = getNumColumnInRow(index);
    155             return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
    156         }
    157 
    158         public void markAsEdgeKey(final Key key, final int index) {
    159             final int row = mRowNumbers[index];
    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(index);
    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 mFromIndex;
    178         private int mToIndex;
    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 fromIndex,
    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, fromIndex, maxWidth, minWidth, maxRow,
    194                     mPaneView.newLabelPaint(null /* key */), mResources);
    195             mFromIndex = fromIndex;
    196             mToIndex = fromIndex + count;
    197             mSuggestedWords = suggestedWords;
    198             return this;
    199         }
    200 
    201         @Override
    202         public MoreSuggestions build() {
    203             final MoreSuggestionsParam params = mParams;
    204             for (int index = mFromIndex; index < mToIndex; index++) {
    205                 final int x = params.getX(index);
    206                 final int y = params.getY(index);
    207                 final int width = params.getWidth(index);
    208                 final String word = mSuggestedWords.getWord(index);
    209                 final String info = mSuggestedWords.getDebugString(index);
    210                 final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE;
    211                 final Key key = new Key(
    212                         params, word, info, KeyboardIconsSet.ICON_UNDEFINED, indexInMoreSuggestions,
    213                         null /* outputText */, x, y, width, params.mDefaultRowHeight,
    214                         0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL);
    215                 params.markAsEdgeKey(key, index);
    216                 params.onAddKey(key);
    217                 final int columnNumber = params.getColumnNumber(index);
    218                 final int numColumnInRow = params.getNumColumnInRow(index);
    219                 if (columnNumber < numColumnInRow - 1) {
    220                     final Divider divider = new Divider(params, params.mDivider, x + width, y,
    221                             params.mDividerWidth, params.mDefaultRowHeight);
    222                     params.onAddKey(divider);
    223                 }
    224             }
    225             return new MoreSuggestions(params, mSuggestedWords);
    226         }
    227     }
    228 
    229     private static final class Divider extends Key.Spacer {
    230         private final Drawable mIcon;
    231 
    232         public Divider(final KeyboardParams params, final Drawable icon, final int x,
    233                 final int y, final int width, final int height) {
    234             super(params, x, y, width, height);
    235             mIcon = icon;
    236         }
    237 
    238         @Override
    239         public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
    240             // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
    241             // constructor.
    242             // TODO: Drawable itself should have an alpha value.
    243             mIcon.setAlpha(128);
    244             return mIcon;
    245         }
    246     }
    247 }
    248