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.layout.customizer.LayoutCustomizer;
21import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
22import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
23import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
24
25import java.util.Locale;
26
27/**
28 * The base class of keyboard layout.
29 */
30public abstract class LayoutBase extends AbstractLayoutBase {
31    private final LayoutCustomizer mCustomizer;
32    private final Symbols mSymbols;
33    private final SymbolsShifted mSymbolsShifted;
34
35    LayoutBase(final LayoutCustomizer customizer, final Class<? extends Symbols> symbolsClass,
36            final Class<? extends SymbolsShifted> symbolsShiftedClass) {
37        mCustomizer = customizer;
38        try {
39            mSymbols = symbolsClass.getDeclaredConstructor(LayoutCustomizer.class)
40                    .newInstance(customizer);
41            mSymbolsShifted = symbolsShiftedClass.getDeclaredConstructor(LayoutCustomizer.class)
42                    .newInstance(customizer);
43        } catch (final Exception e) {
44            throw new RuntimeException("Unknown Symbols/SymbolsShifted class", e);
45        }
46    }
47
48    /**
49     * The layout name.
50     * @return the name of this layout.
51     */
52    public abstract String getName();
53
54    /**
55     * The locale of this layout.
56     * @return the locale of this layout.
57     */
58    public final Locale getLocale() { return mCustomizer.getLocale(); }
59
60    /**
61     * The layout customizer for this layout.
62     * @return the layout customizer;
63     */
64    public final LayoutCustomizer getCustomizer() { return mCustomizer; }
65
66    /**
67     * Helper method to create alphabet layout adding special function keys.
68     * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
69     *     layout
70     * @param isPhone true if requesting phone's layout.
71     * @return the {@link ExpectedKeyboardBuilder} object that is customized and have special keys.
72     */
73    ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
74            final boolean isPhone) {
75        final LayoutCustomizer customizer = getCustomizer();
76        final int numberOfRows = customizer.getNumberOfRows();
77        builder.setKeysOfRow(numberOfRows, (Object[])customizer.getSpaceKeys(isPhone));
78        builder.addKeysOnTheLeftOfRow(
79                numberOfRows, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
80        builder.addKeysOnTheRightOfRow(
81                numberOfRows, (Object[])customizer.getKeysRightToSpacebar(isPhone));
82        if (isPhone) {
83            builder.addKeysOnTheRightOfRow(numberOfRows - 1, DELETE_KEY)
84                    .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey())
85                    .addKeysOnTheRightOfRow(numberOfRows, customizer.getEnterKey(isPhone));
86        } else {
87            builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
88                    .addKeysOnTheRightOfRow(numberOfRows - 2, customizer.getEnterKey(isPhone))
89                    .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey())
90                    .addKeysOnTheRightOfRow(numberOfRows, customizer.getEmojiKey(isPhone));
91        }
92        builder.addKeysOnTheLeftOfRow(
93                numberOfRows - 1, (Object[])customizer.getLeftShiftKeys(isPhone));
94        builder.addKeysOnTheRightOfRow(
95                numberOfRows - 1, (Object[])customizer.getRightShiftKeys(isPhone));
96        return builder;
97    }
98
99    /**
100     * Get common alphabet layout. This layout doesn't contain any special keys.
101     *
102     * A keyboard layout is an array of rows, and a row consists of an array of
103     * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
104     *
105     * @param isPhone true if requesting phone's layout.
106     * @return the common alphabet keyboard layout.
107     */
108    abstract ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone);
109
110    /**
111     * Get common alphabet shifted layout. This layout doesn't contain any special keys.
112     *
113     * A keyboard layout is an array of rows, and a row consists of an array of
114     * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
115     *
116     * @param isPhone true if requesting phone's layout.
117     * @param elementId the element id of the requesting shifted mode.
118     * @return the common alphabet shifted keyboard layout.
119     */
120    ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
121        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
122                getCommonAlphabetLayout(isPhone));
123        getCustomizer().setAccentedLetters(builder, elementId);
124        builder.toUpperCase(getLocale());
125        return builder.build();
126    }
127
128    /**
129     * Get the complete expected keyboard layout.
130     *
131     * A keyboard layout is an array of rows, and a row consists of an array of
132     * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
133     *
134     * @param isPhone true if requesting phone's layout.
135     * @param elementId the element id of the requesting keyboard mode.
136     * @return the keyboard layout of the <code>elementId</code>.
137     */
138    public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
139        if (elementId == KeyboardId.ELEMENT_SYMBOLS) {
140            return mSymbols.getLayout(isPhone);
141        }
142        if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
143            return mSymbolsShifted.getLayout(isPhone);
144        }
145        final ExpectedKeyboardBuilder builder;
146        if (elementId == KeyboardId.ELEMENT_ALPHABET) {
147            builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
148            getCustomizer().setAccentedLetters(builder, elementId);
149        } else {
150            final ExpectedKey[][] commonLayout = getCommonAlphabetShiftLayout(isPhone, elementId);
151            if (commonLayout == null) {
152                return null;
153            }
154            builder = new ExpectedKeyboardBuilder(commonLayout);
155        }
156        convertCommonLayoutToKeyboard(builder, isPhone);
157        if (elementId != KeyboardId.ELEMENT_ALPHABET) {
158            builder.replaceKeysOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY);
159        }
160        return builder.build();
161    }
162}
163