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