Home | History | Annotate | Download | only in layout
      1 /*
      2  * Copyright (C) 2014 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.layout;
     18 
     19 import com.android.inputmethod.keyboard.KeyboardId;
     20 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
     21 import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
     22 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
     23 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
     24 import com.android.inputmethod.latin.Constants;
     25 
     26 import java.util.Locale;
     27 
     28 /**
     29  * The base class of keyboard layout.
     30  */
     31 public abstract class LayoutBase extends AbstractLayoutBase {
     32     /**
     33      * This class is used to customize common keyboard layout to language specific layout.
     34      */
     35     public static class LayoutCustomizer {
     36         private final Locale mLocale;
     37 
     38         // Empty keys definition to remove keys by adding this.
     39         protected static final ExpectedKey[] EMPTY_KEYS = joinKeys();
     40 
     41         public LayoutCustomizer(final Locale locale) {
     42             mLocale = locale;
     43         }
     44 
     45         public final Locale getLocale() {
     46             return mLocale;
     47         }
     48 
     49         public int getNumberOfRows() {
     50             return 4;
     51         }
     52 
     53         /**
     54          * Set accented letters to common layout.
     55          * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
     56          *        layout.
     57          * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
     58          *        "more keys".
     59          */
     60         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
     61             return builder;
     62         }
     63 
     64         /**
     65          * Get the function key to switch to alphabet layout.
     66          * @return the {@link ExpectedKey} of the alphabet key.
     67          */
     68         public ExpectedKey getAlphabetKey() { return ALPHABET_KEY; }
     69 
     70         /**
     71          * Get the function key to switch to symbols layout.
     72          * @return the {@link ExpectedKey} of the symbols key.
     73          */
     74         public ExpectedKey getSymbolsKey() { return SYMBOLS_KEY; }
     75 
     76         /**
     77          * Get the function key to switch to symbols shift layout.
     78          * @param isPhone true if requesting phone's key.
     79          * @return the {@link ExpectedKey} of the symbols shift key.
     80          */
     81         public ExpectedKey getSymbolsShiftKey(boolean isPhone) {
     82             return isPhone ? SYMBOLS_SHIFT_KEY : TABLET_SYMBOLS_SHIFT_KEY;
     83         }
     84 
     85         /**
     86          * Get the function key to switch from symbols shift to symbols layout.
     87          * @return the {@link ExpectedKey} of the back to symbols key.
     88          */
     89         public ExpectedKey getBackToSymbolsKey() { return BACK_TO_SYMBOLS_KEY; }
     90 
     91         /**
     92          * Get the currency key.
     93          * @return the {@link ExpectedKey} of the currency key.
     94          */
     95         public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_DOLLAR; }
     96 
     97         /**
     98          * Get other currencies keys.
     99          * @return the array of {@link ExpectedKey} that represents other currency keys.
    100          */
    101         public ExpectedKey[] getOtherCurrencyKeys() {
    102             return SymbolsShifted.CURRENCIES_OTHER_THAN_DOLLAR;
    103         }
    104 
    105         /**
    106          * Get "more keys" of double quotation mark.
    107          * @return the array of {@link ExpectedKey} of more double quotation marks in natural order.
    108          */
    109         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_9LR; }
    110 
    111         /**
    112          * Get "more keys" of single quotation mark.
    113          * @return the array of {@link ExpectedKey} of more single quotation marks in natural order.
    114          */
    115         public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_9LR; }
    116 
    117         /**
    118          * Get double angle quotation marks in natural order.
    119          * @return the array of {@link ExpectedKey} of double angle quotation marks in natural
    120          *         order.
    121          */
    122         public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_LR; }
    123 
    124         /**
    125          * Get single angle quotation marks in natural order.
    126          * @return the array of {@link ExpectedKey} of single angle quotation marks in natural
    127          *         order.
    128          */
    129         public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_LR; }
    130 
    131         /**
    132          * Get the left shift keys.
    133          * @param isPhone true if requesting phone's keys.
    134          * @return the array of {@link ExpectedKey} that should be placed at left edge of the
    135          *         keyboard.
    136          */
    137         public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
    138             return joinKeys(SHIFT_KEY);
    139         }
    140 
    141         /**
    142          * Get the right shift keys.
    143          * @param isPhone true if requesting phone's keys.
    144          * @return the array of {@link ExpectedKey} that should be placed at right edge of the
    145          *         keyboard.
    146          */
    147         public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
    148             return isPhone ? EMPTY_KEYS : joinKeys(EXCLAMATION_AND_QUESTION_MARKS, SHIFT_KEY);
    149         }
    150 
    151         /**
    152          * Get the enter key.
    153          * @param isPhone true if requesting phone's key.
    154          * @return the array of {@link ExpectedKey} that should be placed as an enter key.
    155          */
    156         public ExpectedKey getEnterKey(final boolean isPhone) {
    157             return isPhone ? key(ENTER_KEY, EMOJI_ACTION_KEY) : ENTER_KEY;
    158         }
    159 
    160         /**
    161          * Get the emoji key.
    162          * @param isPhone true if requesting phone's key.
    163          * @return the array of {@link ExpectedKey} that should be placed as an emoji key.
    164          */
    165         public ExpectedKey getEmojiKey(final boolean isPhone) {
    166             return EMOJI_NORMAL_KEY;
    167         }
    168 
    169         /**
    170          * Get the space keys.
    171          * @param isPhone true if requesting phone's keys.
    172          * @return the array of {@link ExpectedKey} that should be placed at the center of the
    173          *         keyboard.
    174          */
    175         public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
    176             return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY);
    177         }
    178 
    179         /**
    180          * Get the keys left to the spacebar.
    181          * @param isPhone true if requesting phone's keys.
    182          * @return the array of {@link ExpectedKey} that should be placed at left of the spacebar.
    183          */
    184         public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
    185             // U+002C: "," COMMA
    186             return joinKeys(key("\u002C", SETTINGS_KEY));
    187         }
    188 
    189         /**
    190          * Get the keys right to the spacebar.
    191          * @param isPhone true if requesting phone's keys.
    192          * @return the array of {@link ExpectedKey} that should be placed at right of the spacebar.
    193          */
    194         public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
    195             final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone));
    196             return joinKeys(periodKey);
    197         }
    198 
    199         /**
    200          * Get "more keys" for the punctuation key (usually the period key).
    201          * @param isPhone true if requesting phone's keys.
    202          * @return the array of {@link ExpectedKey} that are "more keys" of the punctuation key.
    203          */
    204         public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
    205             return isPhone ? PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
    206         }
    207     }
    208 
    209     /**
    210      * The layout customize class for countries that use Euro.
    211      */
    212     public static class EuroCustomizer extends LayoutCustomizer {
    213         public EuroCustomizer(final Locale locale) {
    214             super(locale);
    215         }
    216 
    217         @Override
    218         public final ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
    219 
    220         @Override
    221         public final ExpectedKey[] getOtherCurrencyKeys() {
    222             return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
    223         }
    224     }
    225 
    226     private final LayoutCustomizer mCustomizer;
    227     private final Symbols mSymbols;
    228     private final SymbolsShifted mSymbolsShifted;
    229 
    230     LayoutBase(final LayoutCustomizer customizer, final Class<? extends Symbols> symbolsClass,
    231             final Class<? extends SymbolsShifted> symbolsShiftedClass) {
    232         mCustomizer = customizer;
    233         try {
    234             mSymbols = symbolsClass.getDeclaredConstructor(LayoutCustomizer.class)
    235                     .newInstance(customizer);
    236             mSymbolsShifted = symbolsShiftedClass.getDeclaredConstructor(LayoutCustomizer.class)
    237                     .newInstance(customizer);
    238         } catch (final Exception e) {
    239             throw new RuntimeException("Unknown Symbols/SymbolsShifted class", e);
    240         }
    241     }
    242 
    243     /**
    244      * The layout name.
    245      * @return the name of this layout.
    246      */
    247     public abstract String getName();
    248 
    249     /**
    250      * The locale of this layout.
    251      * @return the locale of this layout.
    252      */
    253     public final Locale getLocale() { return mCustomizer.getLocale(); }
    254 
    255     /**
    256      * The layout customizer for this layout.
    257      * @return the layout customizer;
    258      */
    259     public final LayoutCustomizer getCustomizer() { return mCustomizer; }
    260 
    261     // Icon ids.
    262     private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
    263             KeyboardIconsSet.NAME_DELETE_KEY);
    264     private static final int ICON_SPACE = KeyboardIconsSet.getIconId(
    265             KeyboardIconsSet.NAME_SPACE_KEY);
    266     private static final int ICON_TAB = KeyboardIconsSet.getIconId(
    267             KeyboardIconsSet.NAME_TAB_KEY);
    268     private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
    269             KeyboardIconsSet.NAME_SHORTCUT_KEY);
    270     private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
    271             KeyboardIconsSet.NAME_SETTINGS_KEY);
    272     private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId(
    273             KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY);
    274     private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
    275             KeyboardIconsSet.NAME_ENTER_KEY);
    276     private static final int ICON_EMOJI_ACTION = KeyboardIconsSet.getIconId(
    277             KeyboardIconsSet.NAME_EMOJI_ACTION_KEY);
    278     private static final int ICON_EMOJI_NORMAL = KeyboardIconsSet.getIconId(
    279             KeyboardIconsSet.NAME_EMOJI_NORMAL_KEY);
    280     private static final int ICON_SHIFT = KeyboardIconsSet.getIconId(
    281             KeyboardIconsSet.NAME_SHIFT_KEY);
    282     private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId(
    283             KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED);
    284     private static final int ICON_ZWNJ = KeyboardIconsSet.getIconId(
    285             KeyboardIconsSet.NAME_ZWNJ_KEY);
    286     private static final int ICON_ZWJ = KeyboardIconsSet.getIconId(
    287             KeyboardIconsSet.NAME_ZWJ_KEY);
    288 
    289     // Functional keys.
    290     public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
    291     public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB);
    292     public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
    293     public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
    294     public static final ExpectedKey LANGUAGE_SWITCH_KEY = key(
    295             ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH);
    296     public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
    297     public static final ExpectedKey EMOJI_ACTION_KEY = key(ICON_EMOJI_ACTION, Constants.CODE_EMOJI);
    298     public static final ExpectedKey EMOJI_NORMAL_KEY = key(ICON_EMOJI_NORMAL, Constants.CODE_EMOJI);
    299     public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE);
    300     static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK);
    301     public static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT,
    302             Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
    303     public static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT,
    304             Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
    305     static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL);
    306     static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL);
    307     static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT);
    308     static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT);
    309     static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT);
    310 
    311     // U+00A1: "" INVERTED EXCLAMATION MARK
    312     // U+00BF: "" INVERTED QUESTION MARK
    313     static final ExpectedKey[] EXCLAMATION_AND_QUESTION_MARKS = joinKeys(
    314             key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF")));
    315     // U+200C: ZERO WIDTH NON-JOINER
    316     // U+200D: ZERO WIDTH JOINER
    317     static final ExpectedKey ZWNJ_KEY = key(ICON_ZWNJ, "\u200C");
    318     static final ExpectedKey ZWJ_KEY = key(ICON_ZWJ, "\u200D");
    319     // Domain key
    320     public static final ExpectedKey DOMAIN_KEY =
    321             key(".com", joinMoreKeys(".net", ".org", ".gov", ".edu")).preserveCase();
    322 
    323     // Punctuation more keys for phone form factor.
    324     public static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
    325             ",", "?", "!", "#", ")", "(", "/", ";",
    326             "'", "@", ":", "-", "\"", "+", "%", "&");
    327     // Punctuation more keys for tablet form factor.
    328     public static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
    329             ",", "'", "#", ")", "(", "/", ";",
    330             "@", ":", "-", "\"", "+", "%", "&");
    331 
    332     /**
    333      * Helper method to create alphabet layout adding special function keys.
    334      * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
    335      *     layout
    336      * @param isPhone true if requesting phone's layout.
    337      * @return the {@link ExpectedKeyboardBuilder} object that is customized and have special keys.
    338      */
    339     ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
    340             final boolean isPhone) {
    341         final LayoutCustomizer customizer = getCustomizer();
    342         final int numberOfRows = customizer.getNumberOfRows();
    343         builder.setKeysOfRow(numberOfRows, (Object[])customizer.getSpaceKeys(isPhone));
    344         builder.addKeysOnTheLeftOfRow(
    345                 numberOfRows, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
    346         builder.addKeysOnTheRightOfRow(
    347                 numberOfRows, (Object[])customizer.getKeysRightToSpacebar(isPhone));
    348         if (isPhone) {
    349             builder.addKeysOnTheRightOfRow(numberOfRows - 1, DELETE_KEY)
    350                     .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey())
    351                     .addKeysOnTheRightOfRow(numberOfRows, customizer.getEnterKey(isPhone));
    352         } else {
    353             builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
    354                     .addKeysOnTheRightOfRow(numberOfRows - 2, customizer.getEnterKey(isPhone))
    355                     .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey())
    356                     .addKeysOnTheRightOfRow(numberOfRows, customizer.getEmojiKey(isPhone));
    357         }
    358         builder.addKeysOnTheLeftOfRow(
    359                 numberOfRows - 1, (Object[])customizer.getLeftShiftKeys(isPhone));
    360         builder.addKeysOnTheRightOfRow(
    361                 numberOfRows - 1, (Object[])customizer.getRightShiftKeys(isPhone));
    362         return builder;
    363     }
    364 
    365     /**
    366      * Get common alphabet layout. This layout doesn't contain any special keys.
    367      *
    368      * A keyboard layout is an array of rows, and a row consists of an array of
    369      * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
    370      *
    371      * @param isPhone true if requesting phone's layout.
    372      * @return the common alphabet keyboard layout.
    373      */
    374     abstract ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone);
    375 
    376     /**
    377      * Get common alphabet shifted layout. This layout doesn't contain any special keys.
    378      *
    379      * A keyboard layout is an array of rows, and a row consists of an array of
    380      * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
    381      *
    382      * @param isPhone true if requesting phone's layout.
    383      * @param elementId the element id of the requesting shifted mode.
    384      * @return the common alphabet shifted keyboard layout.
    385      */
    386     ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
    387         final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
    388                 getCommonAlphabetLayout(isPhone));
    389         getCustomizer().setAccentedLetters(builder);
    390         builder.toUpperCase(getLocale());
    391         return builder.build();
    392     }
    393 
    394     /**
    395      * Get the complete expected keyboard layout.
    396      *
    397      * A keyboard layout is an array of rows, and a row consists of an array of
    398      * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
    399      *
    400      * @param isPhone true if requesting phone's layout.
    401      * @param elementId the element id of the requesting keyboard mode.
    402      * @return the keyboard layout of the <code>elementId</code>.
    403      */
    404     public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
    405         if (elementId == KeyboardId.ELEMENT_SYMBOLS) {
    406             return mSymbols.getLayout(isPhone);
    407         }
    408         if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
    409             return mSymbolsShifted.getLayout(isPhone);
    410         }
    411         final ExpectedKeyboardBuilder builder;
    412         if (elementId == KeyboardId.ELEMENT_ALPHABET) {
    413             builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
    414             getCustomizer().setAccentedLetters(builder);
    415         } else {
    416             final ExpectedKey[][] commonLayout = getCommonAlphabetShiftLayout(isPhone, elementId);
    417             if (commonLayout == null) {
    418                 return null;
    419             }
    420             builder = new ExpectedKeyboardBuilder(commonLayout);
    421         }
    422         convertCommonLayoutToKeyboard(builder, isPhone);
    423         if (elementId != KeyboardId.ELEMENT_ALPHABET) {
    424             builder.replaceKeysOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY);
    425         }
    426         return builder.build();
    427     }
    428 }
    429