Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2013 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.keyboard.internal;
     18 
     19 import android.content.SharedPreferences;
     20 import android.text.TextUtils;
     21 import android.util.Log;
     22 
     23 import com.android.inputmethod.keyboard.EmojiPalettesView;
     24 import com.android.inputmethod.keyboard.Key;
     25 import com.android.inputmethod.keyboard.Keyboard;
     26 import com.android.inputmethod.latin.settings.Settings;
     27 import com.android.inputmethod.latin.utils.CollectionUtils;
     28 import com.android.inputmethod.latin.utils.StringUtils;
     29 
     30 import java.util.ArrayDeque;
     31 import java.util.ArrayList;
     32 import java.util.Collection;
     33 import java.util.List;
     34 
     35 /**
     36  * This is a Keyboard class where you can add keys dynamically shown in a grid layout
     37  */
     38 public class DynamicGridKeyboard extends Keyboard {
     39     private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
     40     private static final int TEMPLATE_KEY_CODE_0 = 0x30;
     41     private static final int TEMPLATE_KEY_CODE_1 = 0x31;
     42     private final Object mLock = new Object();
     43 
     44     private final SharedPreferences mPrefs;
     45     private final int mHorizontalStep;
     46     private final int mVerticalStep;
     47     private final int mColumnsNum;
     48     private final int mMaxKeyCount;
     49     private final boolean mIsRecents;
     50     private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque();
     51     private final ArrayDeque<Key> mPendingKeys = CollectionUtils.newArrayDeque();
     52 
     53     private Key[] mCachedGridKeys;
     54 
     55     public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
     56             final int maxKeyCount, final int categoryId, final int categoryPageId) {
     57         super(templateKeyboard);
     58         final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
     59         final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
     60         mHorizontalStep = Math.abs(key1.getX() - key0.getX());
     61         mVerticalStep = key0.getHeight() + mVerticalGap;
     62         mColumnsNum = mBaseWidth / mHorizontalStep;
     63         mMaxKeyCount = maxKeyCount;
     64         mIsRecents = categoryId == EmojiPalettesView.CATEGORY_ID_RECENTS;
     65         mPrefs = prefs;
     66     }
     67 
     68     private Key getTemplateKey(final int code) {
     69         for (final Key key : super.getKeys()) {
     70             if (key.getCode() == code) {
     71                 return key;
     72             }
     73         }
     74         throw new RuntimeException("Can't find template key: code=" + code);
     75     }
     76 
     77     public void addPendingKey(final Key usedKey) {
     78         synchronized (mLock) {
     79             mPendingKeys.addLast(usedKey);
     80         }
     81     }
     82 
     83     public void flushPendingRecentKeys() {
     84         synchronized (mLock) {
     85             while (!mPendingKeys.isEmpty()) {
     86                 addKey(mPendingKeys.pollFirst(), true);
     87             }
     88             saveRecentKeys();
     89         }
     90     }
     91 
     92     public void addKeyFirst(final Key usedKey) {
     93         addKey(usedKey, true);
     94         if (mIsRecents) {
     95             saveRecentKeys();
     96         }
     97     }
     98 
     99     public void addKeyLast(final Key usedKey) {
    100         addKey(usedKey, false);
    101     }
    102 
    103     private void addKey(final Key usedKey, final boolean addFirst) {
    104         if (usedKey == null) {
    105             return;
    106         }
    107         synchronized (mLock) {
    108             mCachedGridKeys = null;
    109             final GridKey key = new GridKey(usedKey);
    110             while (mGridKeys.remove(key)) {
    111                 // Remove duplicate keys.
    112             }
    113             if (addFirst) {
    114                 mGridKeys.addFirst(key);
    115             } else {
    116                 mGridKeys.addLast(key);
    117             }
    118             while (mGridKeys.size() > mMaxKeyCount) {
    119                 mGridKeys.removeLast();
    120             }
    121             int index = 0;
    122             for (final GridKey gridKey : mGridKeys) {
    123                 final int keyX0 = getKeyX0(index);
    124                 final int keyY0 = getKeyY0(index);
    125                 final int keyX1 = getKeyX1(index);
    126                 final int keyY1 = getKeyY1(index);
    127                 gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1);
    128                 index++;
    129             }
    130         }
    131     }
    132 
    133     private void saveRecentKeys() {
    134         final ArrayList<Object> keys = CollectionUtils.newArrayList();
    135         for (final Key key : mGridKeys) {
    136             if (key.getOutputText() != null) {
    137                 keys.add(key.getOutputText());
    138             } else {
    139                 keys.add(key.getCode());
    140             }
    141         }
    142         final String jsonStr = StringUtils.listToJsonStr(keys);
    143         Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
    144     }
    145 
    146     private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) {
    147         for (final DynamicGridKeyboard kbd : keyboards) {
    148             if (o instanceof Integer) {
    149                 final int code = (Integer) o;
    150                 final Key key = kbd.getKey(code);
    151                 if (key != null) {
    152                     return key;
    153                 }
    154             } else if (o instanceof String) {
    155                 final String outputText = (String) o;
    156                 final Key key = kbd.getKeyFromOutputText(outputText);
    157                 if (key != null) {
    158                     return key;
    159                 }
    160             } else {
    161                 Log.w(TAG, "Invalid object: " + o);
    162             }
    163         }
    164         return null;
    165     }
    166 
    167     public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
    168         final String str = Settings.readEmojiRecentKeys(mPrefs);
    169         final List<Object> keys = StringUtils.jsonStrToList(str);
    170         for (final Object o : keys) {
    171             addKeyLast(getKey(keyboards, o));
    172         }
    173     }
    174 
    175     private int getKeyX0(final int index) {
    176         final int column = index % mColumnsNum;
    177         return column * mHorizontalStep;
    178     }
    179 
    180     private int getKeyX1(final int index) {
    181         final int column = index % mColumnsNum + 1;
    182         return column * mHorizontalStep;
    183     }
    184 
    185     private int getKeyY0(final int index) {
    186         final int row = index / mColumnsNum;
    187         return row * mVerticalStep + mVerticalGap / 2;
    188     }
    189 
    190     private int getKeyY1(final int index) {
    191         final int row = index / mColumnsNum + 1;
    192         return row * mVerticalStep + mVerticalGap / 2;
    193     }
    194 
    195     @Override
    196     public Key[] getKeys() {
    197         synchronized (mLock) {
    198             if (mCachedGridKeys != null) {
    199                 return mCachedGridKeys;
    200             }
    201             mCachedGridKeys = mGridKeys.toArray(new Key[mGridKeys.size()]);
    202             return mCachedGridKeys;
    203         }
    204     }
    205 
    206     @Override
    207     public Key[] getNearestKeys(final int x, final int y) {
    208         // TODO: Calculate the nearest key index in mGridKeys from x and y.
    209         return getKeys();
    210     }
    211 
    212     static final class GridKey extends Key {
    213         private int mCurrentX;
    214         private int mCurrentY;
    215 
    216         public GridKey(final Key originalKey) {
    217             super(originalKey);
    218         }
    219 
    220         public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) {
    221             mCurrentX = x0;
    222             mCurrentY = y0;
    223             getHitBox().set(x0, y0, x1, y1);
    224         }
    225 
    226         @Override
    227         public int getX() {
    228             return mCurrentX;
    229         }
    230 
    231         @Override
    232         public int getY() {
    233             return mCurrentY;
    234         }
    235 
    236         @Override
    237         public boolean equals(final Object o) {
    238             if (!(o instanceof Key)) return false;
    239             final Key key = (Key)o;
    240             if (getCode() != key.getCode()) return false;
    241             if (!TextUtils.equals(getLabel(), key.getLabel())) return false;
    242             return TextUtils.equals(getOutputText(), key.getOutputText());
    243         }
    244 
    245         @Override
    246         public String toString() {
    247             return "GridKey: " + super.toString();
    248         }
    249     }
    250 }
    251