Merge "Fix PC Qwerty layout"
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index 41bf36b..bd4143d 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.event;
 
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
 /**
  * Class representing a generic input event as handled by Latin IME.
@@ -49,6 +50,8 @@
     final public static int EVENT_MODE_KEY = 3;
     // An event corresponding to a gesture.
     final public static int EVENT_GESTURE = 4;
+    // An event corresponding to the manual pick of a suggestion.
+    final public static int EVENT_SUGGESTION_PICKED = 5;
 
     // 0 is a valid code point, so we use -1 here.
     final public static int NOT_A_CODE_POINT = -1;
@@ -85,31 +88,50 @@
     // Some flags that can't go into the key code. It's a bit field of FLAG_*
     final private int mFlags;
 
+    // If this is of type EVENT_SUGGESTION_PICKED, this must not be null (and must be null in
+    // other cases).
+    final public SuggestedWordInfo mSuggestedWordInfo;
+
     // The next event, if any. Null if there is no next event yet.
     final public Event mNextEvent;
 
     // This method is private - to create a new event, use one of the create* utility methods.
     private Event(final int type, final int codePoint, final int keyCode, final int x, final int y,
-            final int flags, final Event next) {
+            final SuggestedWordInfo suggestedWordInfo, final int flags, final Event next) {
         mType = type;
         mCodePoint = codePoint;
         mKeyCode = keyCode;
         mX = x;
         mY = y;
+        mSuggestedWordInfo = suggestedWordInfo;
         mFlags = flags;
         mNextEvent = next;
+        // Sanity checks
+        // mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED
+        if (EVENT_SUGGESTION_PICKED == mType) {
+            if (null == mSuggestedWordInfo) {
+                throw new RuntimeException("Wrong event: SUGGESTION_PICKED event must have a "
+                        + "non-null SuggestedWordInfo");
+            }
+        } else {
+            if (null != mSuggestedWordInfo) {
+                throw new RuntimeException("Wrong event: only SUGGESTION_PICKED events may have " +
+                        "a non-null SuggestedWordInfo");
+            }
+        }
     }
 
     public static Event createSoftwareKeypressEvent(final int codePoint, final int keyCode,
             final int x, final int y) {
-        return new Event(EVENT_INPUT_KEYPRESS, codePoint, keyCode, x, y, FLAG_NONE, null);
+        return new Event(EVENT_INPUT_KEYPRESS, codePoint, keyCode, x, y,
+                null /* suggestedWordInfo */, FLAG_NONE, null);
     }
 
     public static Event createHardwareKeypressEvent(final int codePoint, final int keyCode,
             final Event next) {
         return new Event(EVENT_INPUT_KEYPRESS, codePoint, keyCode,
                 Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
-                FLAG_NONE, next);
+                null /* suggestedWordInfo */, FLAG_NONE, next);
     }
 
     // This creates an input event for a dead character. @see {@link #FLAG_DEAD}
@@ -117,7 +139,7 @@
         // TODO: add an argument or something if we ever create a software layout with dead keys.
         return new Event(EVENT_INPUT_KEYPRESS, codePoint, keyCode,
                 Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
-                FLAG_DEAD, next);
+                null /* suggestedWordInfo */, FLAG_DEAD, next);
     }
 
     /**
@@ -130,7 +152,8 @@
     public static Event createEventForCodePointFromUnknownSource(final int codePoint) {
         // TODO: should we have a different type of event for this? After all, it's not a key press.
         return new Event(EVENT_INPUT_KEYPRESS, codePoint, NOT_A_KEY_CODE,
-                Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, FLAG_NONE, null /* next */);
+                Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
+                null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
     }
 
     /**
@@ -144,13 +167,24 @@
     public static Event createEventForCodePointFromAlreadyTypedText(final int codePoint,
             final int x, final int y) {
         // TODO: should we have a different type of event for this? After all, it's not a key press.
-        return new Event(EVENT_INPUT_KEYPRESS, codePoint, NOT_A_KEY_CODE, x, y, FLAG_NONE,
-                null /* next */);
+        return new Event(EVENT_INPUT_KEYPRESS, codePoint, NOT_A_KEY_CODE, x, y,
+                null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
+    }
+
+    /**
+     * Creates an input event representing the manual pick of a suggestion.
+     * @return an event for this suggestion pick.
+     */
+    public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) {
+        return new Event(EVENT_SUGGESTION_PICKED, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
+                Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
+                suggestedWordInfo, FLAG_NONE, null);
     }
 
     public static Event createNotHandledEvent() {
         return new Event(EVENT_NOT_HANDLED, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
-                Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, FLAG_NONE, null);
+                Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
+                null /* suggestedWordInfo */, FLAG_NONE, null);
     }
 
     // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index e9ecd1e..d9cb51f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -1669,7 +1669,7 @@
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
-        /* more_keys_for_c */ "\u00E7,\u0107,\u010D",
+        /* more_keys_for_c */ "\u00E7,%,\u0107,\u010D",
         /* label_to_alpha_key ~ */
         null, null, null,
         /* ~ more_keys_for_n */
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 4a18c2b..fc5c7f7 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -169,7 +169,6 @@
 
         private int mDelayUpdateSuggestions;
         private int mDelayUpdateShiftState;
-        private long mDoubleSpacePeriodTimeout;
         private long mDoubleSpacePeriodTimerStart;
 
         public UIHandler(final LatinIME ownerInstance) {
@@ -184,8 +183,6 @@
             final Resources res = latinIme.getResources();
             mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions);
             mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state);
-            mDoubleSpacePeriodTimeout =
-                    res.getInteger(R.integer.config_double_space_period_timeout);
         }
 
         @Override
@@ -327,7 +324,7 @@
 
         public boolean isAcceptingDoubleSpacePeriod() {
             return SystemClock.uptimeMillis() - mDoubleSpacePeriodTimerStart
-                    < mDoubleSpacePeriodTimeout;
+                    < getOwnerInstance().mSettings.getCurrent().mDoubleSpacePeriodTimeout;
         }
 
         // Working variables for the following methods.
@@ -1276,15 +1273,7 @@
         final InputTransaction completeInputTransaction =
                 mInputLogic.onCodeInput(mSettings.getCurrent(), event,
                         mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
-        switch (completeInputTransaction.getRequiredShiftUpdate()) {
-            case InputTransaction.SHIFT_UPDATE_LATER:
-                mHandler.postUpdateShiftState();
-                break;
-            case InputTransaction.SHIFT_UPDATE_NOW:
-                mKeyboardSwitcher.updateShiftState();
-                break;
-            default: // SHIFT_NO_UPDATE
-        }
+        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
         mKeyboardSwitcher.onCodeInput(codePoint);
     }
 
@@ -1500,8 +1489,10 @@
     // interface
     @Override
     public void pickSuggestionManually(final int index, final SuggestedWordInfo suggestionInfo) {
-        mInputLogic.onPickSuggestionManually(mSettings.getCurrent(), index, suggestionInfo,
-                mHandler, mKeyboardSwitcher);
+        final InputTransaction completeInputTransaction = mInputLogic.onPickSuggestionManually(
+                mSettings.getCurrent(), index, suggestionInfo,
+                mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
+        updateShiftModeAfterInputTransaction(completeInputTransaction.getRequiredShiftUpdate());
     }
 
     @Override
@@ -1539,6 +1530,18 @@
         }
     }
 
+    private void updateShiftModeAfterInputTransaction(final int requiredShiftUpdate) {
+        switch (requiredShiftUpdate) {
+        case InputTransaction.SHIFT_UPDATE_LATER:
+            mHandler.postUpdateShiftState();
+            break;
+        case InputTransaction.SHIFT_UPDATE_NOW:
+            mKeyboardSwitcher.updateShiftState();
+            break;
+        default: // SHIFT_NO_UPDATE
+        }
+    }
+
     private void hapticAndAudioFeedback(final int code, final int repeatCount) {
         final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (keyboardView != null && keyboardView.isInDraggingFinger()) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index fa7c4b4..1eff427 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -196,13 +196,16 @@
      * @param settingsValues the current values of the settings.
      * @param index the index of the suggestion.
      * @param suggestionInfo the suggestion info.
+     * @param keyboardShiftState the shift state of the keyboard, as returned by
+     *     {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()}
+     * @return the complete transaction object
      */
     // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
     // interface
-    public void onPickSuggestionManually(final SettingsValues settingsValues,
-            final int index, final SuggestedWordInfo suggestionInfo,
-            // TODO: remove these two arguments
-            final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) {
+    public InputTransaction onPickSuggestionManually(final SettingsValues settingsValues,
+            final int index, final SuggestedWordInfo suggestionInfo, final int keyboardShiftState,
+            // TODO: remove this argument
+            final LatinIME.UIHandler handler) {
         final SuggestedWords suggestedWords = mSuggestedWords;
         final String suggestion = suggestionInfo.mWord;
         // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
@@ -212,16 +215,26 @@
             LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
             // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
             final int primaryCode = suggestion.charAt(0);
-            final Event event = Event.createSoftwareKeypressEvent(primaryCode, Event.NOT_A_KEY_CODE,
-                    Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
-            onCodeInput(settingsValues, event, keyboardSwitcher.getKeyboardShiftMode(), handler);
+            // TODO: we should be using createSuggestionPickedEvent here, but for legacy reasons,
+            // onCodeInput is expected a software keypress event for a suggested punctuation
+            // because the current code is descended from a time where this information used not
+            // to be available. Fix this.
+            final Event event = Event.createSoftwareKeypressEvent(primaryCode,
+                    Event.NOT_A_KEY_CODE /* keyCode*/,
+                    Constants.SUGGESTION_STRIP_COORDINATE /* x */,
+                    Constants.SUGGESTION_STRIP_COORDINATE /* y */);
+            final InputTransaction completeTransaction = onCodeInput(settingsValues, event,
+                    keyboardShiftState, handler);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
                         false /* isBatchMode */, suggestedWords.mIsPrediction);
             }
-            return;
+            return completeTransaction;
         }
 
+        final Event event = Event.createSuggestionPickedEvent(suggestionInfo);
+        final InputTransaction inputTransaction = new InputTransaction(settingsValues,
+                event, SystemClock.uptimeMillis(), mSpaceState, keyboardShiftState);
         mConnection.beginBatchEdit();
         if (SpaceState.PHANTOM == mSpaceState && suggestion.length() > 0
                 // In the batch input mode, a manually picked suggested word should just replace
@@ -241,11 +254,11 @@
         if (SuggestedWordInfo.KIND_APP_DEFINED == suggestionInfo.mKind) {
             mSuggestedWords = SuggestedWords.EMPTY;
             mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
-            keyboardSwitcher.updateShiftState();
+            inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
             resetComposingState(true /* alsoResetLastComposedWord */);
             mConnection.commitCompletion(suggestionInfo.mApplicationSpecifiedCompletionInfo);
             mConnection.endBatchEdit();
-            return;
+            return inputTransaction;
         }
 
         // We need to log before we commit, because the word composer will store away the user
@@ -264,7 +277,7 @@
         mLastComposedWord.deactivate();
         // Space state must be updated before calling updateShiftState
         mSpaceState = SpaceState.PHANTOM;
-        keyboardSwitcher.updateShiftState();
+        inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
 
         // We should show the "Touch again to save" hint if the user pressed the first entry
         // AND it's in none of our current dictionaries (main, user or otherwise).
@@ -290,6 +303,7 @@
             // If we're not showing the "Touch again to save", then update the suggestion strip.
             handler.postUpdateSuggestionStrip();
         }
+        return inputTransaction;
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 5a652a5..d47a61e 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -50,6 +50,7 @@
     // From resources:
     public final SpacingAndPunctuations mSpacingAndPunctuations;
     public final int mDelayUpdateOldSuggestions;
+    public final long mDoubleSpacePeriodTimeout;
 
     // From preferences, in the same order as xml/prefs.xml:
     public final boolean mAutoCap;
@@ -132,6 +133,7 @@
         mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
+        mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
 
         // Compute other readable settings
         mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
new file mode 100644
index 0000000..a4a9701
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The Colemak alphabet keyboard.
+ */
+public final class Colemak extends LayoutBase {
+    private static final String LAYOUT_NAME = "colemak";
+
+    public Colemak(final LayoutCustomizer customizer) {
+        super(customizer, Symbols.class, SymbolsShifted.class);
+    }
+
+    @Override
+    public String getName() { return LAYOUT_NAME; }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+        getCustomizer().setAccentedLetters(builder);
+        builder.replaceKeyOfLabel(ROW1_10, key(";", additionalMoreKey("0"), moreKey(":")));
+        return builder.build();
+    }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+        final ExpectedKeyboardBuilder builder;
+        if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
+                || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+            builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+        } else {
+            builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+            getCustomizer().setAccentedLetters(builder);
+            builder.replaceKeyOfLabel(ROW1_10, key(":", additionalMoreKey("0")));
+        }
+        builder.toUpperCase(getLocale());
+        return builder.build();
+    }
+
+    private static final String ROW1_10 = "ROW1_10";
+
+    private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    key("q", additionalMoreKey("1")),
+                    key("w", additionalMoreKey("2")),
+                    key("f", additionalMoreKey("3")),
+                    key("p", additionalMoreKey("4")),
+                    key("g", additionalMoreKey("5")),
+                    key("j", additionalMoreKey("6")),
+                    key("l", additionalMoreKey("7")),
+                    key("u", additionalMoreKey("8")),
+                    key("y", additionalMoreKey("9")),
+                    ROW1_10)
+            .setKeysOfRow(2, "a", "r", "s", "t", "d", "h", "n", "e", "i", "o")
+            .setKeysOfRow(3, "z", "x", "c", "v", "b", "k", "m")
+            .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
new file mode 100644
index 0000000..99cf6e5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+
+import java.util.Locale;
+
+/**
+ * The QWERTY alphabet keyboard.
+ */
+public final class Dvorak extends LayoutBase {
+    private static final String LAYOUT_NAME = "dvorak";
+
+    public Dvorak(final LayoutCustomizer customizer) {
+        super(customizer, Symbols.class, SymbolsShifted.class);
+    }
+
+    @Override
+    public String getName() { return LAYOUT_NAME; }
+
+    public static class DvorakCustomizer extends LayoutCustomizer {
+        public DvorakCustomizer(final Locale locale) { super(locale); }
+
+        @Override
+        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+            return isPhone ? joinKeys(SHIFT_KEY): joinKeys(SHIFT_KEY, key("q"));
+        }
+
+        @Override
+        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+            return isPhone ? EMPTY_KEYS : joinKeys(key("z"), SHIFT_KEY);
+        }
+
+        @Override
+        public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+            return isPhone ? joinKeys(key("q", SHORTCUT_KEY, SETTINGS_KEY)) : joinKeys(key("/"));
+        }
+
+        @Override
+        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+            final ExpectedAdditionalMoreKey[] punctuationMoreKeys =
+                    convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone));
+            return isPhone
+                    ? joinKeys(key("z", punctuationMoreKeys))
+                    : joinKeys(key("?", moreKey("!")), key("-", moreKey("_")));
+        }
+
+        private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys(
+                final ExpectedKey ... moreKeys) {
+            final ExpectedAdditionalMoreKey[] additionalMoreKeys =
+                    new ExpectedAdditionalMoreKey[moreKeys.length];
+            for (int index = 0; index < moreKeys.length; index++) {
+                additionalMoreKeys[index] = ExpectedAdditionalMoreKey.newInstance(moreKeys[index]);
+            }
+            return additionalMoreKeys;
+        }
+    }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+    @Override
+    public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
+        if (elementId == KeyboardId.ELEMENT_SYMBOLS
+                || elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
+            return super.getLayout(isPhone, elementId);
+        }
+        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+                getCommonAlphabetLayout(isPhone));
+        if (elementId == KeyboardId.ELEMENT_ALPHABET
+                || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+            builder.addKeysOnTheLeftOfRow(1,
+                    key("'", joinMoreKeys(additionalMoreKey("1"), "!", "\"")),
+                    key(",", joinMoreKeys(additionalMoreKey("2"), "?", "<")),
+                    key(".", joinMoreKeys(additionalMoreKey("3"), ">")));
+        } else {
+            builder.addKeysOnTheLeftOfRow(1,
+                    key("\"", additionalMoreKey("1")),
+                    key("<", additionalMoreKey("2")),
+                    key(">", additionalMoreKey("3")));
+        }
+        convertCommonLayoutToKeyboard(builder, isPhone);
+        getCustomizer().setAccentedLetters(builder);
+        if (elementId != KeyboardId.ELEMENT_ALPHABET) {
+            builder.toUpperCase(getLocale());
+            builder.replaceKeysOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY);
+        }
+        return builder.build();
+    }
+
+    private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    key("p", additionalMoreKey("4")),
+                    key("y", additionalMoreKey("5")),
+                    key("f", additionalMoreKey("6")),
+                    key("g", additionalMoreKey("7")),
+                    key("c", additionalMoreKey("8")),
+                    key("r", additionalMoreKey("9")),
+                    key("l", additionalMoreKey("0")))
+            .setKeysOfRow(2, "a", "o", "e", "u", "i", "d", "h", "t", "n", "s")
+            .setKeysOfRow(3, "j", "k", "x", "b", "m", "w", "v")
+            .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
index cce9d5f..a13ec75 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
@@ -109,6 +109,8 @@
     // Icon ids.
     private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_DELETE_KEY);
+    private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SHORTCUT_KEY);
     private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
             KeyboardIconsSet.NAME_SETTINGS_KEY);
     private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
@@ -118,6 +120,7 @@
 
     // Functional keys.
     public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
+    public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
     public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
     public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
     public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI);
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
index b10b368..ab90267 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
@@ -83,6 +83,7 @@
                 // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
                 // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
                 // U+010D: "č" LATIN SMALL LETTER C WITH CARON
-                .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D");
+                .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+                .setAdditionalMoreKeysPositionOf("c", 2);
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
new file mode 100644
index 0000000..9edbcab
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class NoLanguageCustomizer extends LayoutCustomizer {
+    NoLanguageCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+        return builder
+                // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+                .setMoreKeysOf("w", "\u0175")
+                // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+                // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+                // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+                // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+                // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+                // U+0115: "ĕ" LATIN SMALL LETTER E WITH BREVE
+                // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+                // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+                // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+                .setMoreKeysOf("e",
+                        "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113", "\u0115", "\u0117",
+                        "\u0119", "\u011B")
+                // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+                // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+                // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+                .setMoreKeysOf("r", "\u0155", "\u0157", "\u0159")
+                // U+00FE: "þ" LATIN SMALL LETTER THORN
+                // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+                // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+                // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
+                .setMoreKeysOf("t", "\u00FE", "\u0163", "\u0165", "\u0167")
+                // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+                // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
+                // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+                // U+0133: "ij" LATIN SMALL LIGATURE IJ
+                .setMoreKeysOf("y", "\u00FD", "\u0177", "\u00FF", "\u0133")
+                // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+                // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+                // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+                // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+                // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
+                // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+                // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
+                // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+                // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+                // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+                .setMoreKeysOf("u",
+                        "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u0169", "\u016B", "\u016D",
+                        "\u016F", "\u0171", "\u0173")
+                // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+                // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+                // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+                // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+                // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
+                // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+                // U+012D: "ĭ" LATIN SMALL LETTER I WITH BREVE
+                // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+                // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+                // U+0133: "ij" LATIN SMALL LIGATURE IJ
+                .setMoreKeysOf("i",
+                        "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u0129", "\u012B", "\u012D",
+                        "\u012F", "\u0131", "\u0133")
+                // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+                // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+                // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+                // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+                // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+                // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+                // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+                // U+014F: "ŏ" LATIN SMALL LETTER O WITH BREVE
+                // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+                // U+0153: "œ" LATIN SMALL LIGATURE OE
+                // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+                .setMoreKeysOf("o",
+                        "\u00F2", "\u00F3", "\u00F4", "\u00F5", "\u00F6", "\u00F8", "\u014D",
+                        "\u014F", "\u0151", "\u0153", "\u00BA")
+                // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+                // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+                // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+                // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+                // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+                // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+                // U+00E6: "æ" LATIN SMALL LETTER AE
+                // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+                // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+                // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+                // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+                .setMoreKeysOf("a",
+                        "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u00E4", "\u00E5", "\u00E6",
+                        "\u0101", "\u0103", "\u0105", "\u00AA")
+                // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+                // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+                // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
+                // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+                // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+                // U+017F: "ſ" LATIN SMALL LETTER LONG S
+                .setMoreKeysOf("s", "\u00DF", "\u015B", "\u015D", "\u015F", "\u0161", "\u017F")
+                // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+                // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+                // U+00F0: "ð" LATIN SMALL LETTER ETH
+                .setMoreKeysOf("d", "\u010F", "\u0111", "\u00F0")
+                // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
+                // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+                // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
+                // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+                .setMoreKeysOf("g", "\u011D", "\u011F", "\u0121", "\u0123")
+                // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
+                .setMoreKeysOf("h", "\u0125")
+                // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
+                .setMoreKeysOf("j", "\u0135")
+                // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+                // U+0138: "ĸ" LATIN SMALL LETTER KRA
+                .setMoreKeysOf("k", "\u0137", "\u0138")
+                // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+                // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+                // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+                // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
+                // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+                .setMoreKeysOf("l", "\u013A", "\u013C", "\u013E", "\u0140", "\u0142")
+                // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+                // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+                // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+                .setMoreKeysOf("z", "\u017A", "\u017C", "\u017E")
+                // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+                // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+                // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
+                // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
+                // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+                .setMoreKeysOf("c", "\u00E7", "\u0107", "\u0109", "\u010B", "\u010D")
+                // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+                // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+                // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+                // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+                // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+                // U+014B: "ŋ" LATIN SMALL LETTER ENG
+                .setMoreKeysOf("n", "\u00F1", "\u0144", "\u0146", "\u0148", "\u0149", "\u014B");
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
new file mode 100644
index 0000000..a052693
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * en_US: English (United States)/dvorak
+ */
+@SmallTest
+public class TestsEnglishDvorak extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("en", "US");
+    private static final LayoutBase LAYOUT = new Dvorak(new EnglishDvorakCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class EnglishDvorakCustomizer extends DvorakCustomizer {
+        private final EnglishCustomizer mEnglishCustomizer;
+
+        EnglishDvorakCustomizer(final Locale locale) {
+            super(locale);
+            mEnglishCustomizer = new EnglishCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mEnglishCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
new file mode 100644
index 0000000..33d1445
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.tests.TestsFrench.FrenchEuroCustomizer;
+
+import java.util.Locale;
+
+/**
+ * fr: French/dvorak
+ */
+@SmallTest
+public final class TestsFrenchDvorak extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("fr");
+    private static final LayoutBase LAYOUT = new Dvorak(new FrenchDvorakCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class FrenchDvorakCustomizer extends DvorakCustomizer {
+        private final FrenchEuroCustomizer mFrenchEuroCustomizer;
+
+        public FrenchDvorakCustomizer(final Locale locale) {
+            super(locale);
+            mFrenchEuroCustomizer = new FrenchEuroCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKey getCurrencyKey() { return mFrenchEuroCustomizer.getCurrencyKey(); }
+
+        @Override
+        public ExpectedKey[] getOtherCurrencyKeys() {
+            return mFrenchEuroCustomizer.getOtherCurrencyKeys();
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mFrenchEuroCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
new file mode 100644
index 0000000..b28d5cf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * de: German/dvorak
+ */
+@SmallTest
+public final class TestsGermanDvorak extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("de");
+    private static final LayoutBase LAYOUT = new Dvorak(new GermanDvorakCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    static class GermanDvorakCustomizer extends DvorakCustomizer {
+        final GermanCustomizer mGermanCustomizer;
+
+        public GermanDvorakCustomizer(final Locale locale) {
+            super(locale);
+            mGermanCustomizer = new GermanCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
+
+        @Override
+        public ExpectedKey[] getOtherCurrencyKeys() {
+            return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
+        }
+
+        @Override
+        public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+        @Override
+        public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+        @Override
+        public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+        @Override
+        public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mGermanCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
index 480f5f2..3ed6315 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
@@ -19,14 +19,12 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
 /**
- * zz: QWERTY/qwerty
+ * zz: Alphabet/qwerty
  */
 @SmallTest
 public final class TestsNoLanguage extends LayoutTestsBase {
@@ -35,141 +33,4 @@
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
-
-    private static class NoLanguageCustomizer extends LayoutCustomizer {
-        public NoLanguageCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
-            return builder
-                    // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
-                    .setMoreKeysOf("w", "\u0175")
-                    // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
-                    // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
-                    // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
-                    // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
-                    // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
-                    // U+0115: "ĕ" LATIN SMALL LETTER E WITH BREVE
-                    // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
-                    // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
-                    // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
-                    .setMoreKeysOf("e",
-                            "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113", "\u0115", "\u0117",
-                            "\u0119", "\u011B")
-                    // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
-                    // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
-                    // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
-                    .setMoreKeysOf("r", "\u0155", "\u0157", "\u0159")
-                    // U+00FE: "þ" LATIN SMALL LETTER THORN
-                    // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
-                    // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
-                    // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
-                    .setMoreKeysOf("t", "\u00FE", "\u0163", "\u0165", "\u0167")
-                    // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
-                    // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
-                    // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
-                    // U+0133: "ij" LATIN SMALL LIGATURE IJ
-                    .setMoreKeysOf("y", "\u00FD", "\u0177", "\u00FF", "\u0133")
-                    // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
-                    // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
-                    // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
-                    // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
-                    // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
-                    // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
-                    // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
-                    // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
-                    // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
-                    // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
-                    .setMoreKeysOf("u",
-                            "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u0169", "\u016B", "\u016D",
-                            "\u016F", "\u0171", "\u0173")
-                    // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
-                    // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
-                    // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
-                    // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
-                    // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
-                    // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
-                    // U+012D: "ĭ" LATIN SMALL LETTER I WITH BREVE
-                    // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
-                    // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
-                    // U+0133: "ij" LATIN SMALL LIGATURE IJ
-                    .setMoreKeysOf("i",
-                            "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u0129", "\u012B", "\u012D",
-                            "\u012F", "\u0131", "\u0133")
-                    // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
-                    // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
-                    // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
-                    // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
-                    // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
-                    // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
-                    // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
-                    // U+014F: "ŏ" LATIN SMALL LETTER O WITH BREVE
-                    // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
-                    // U+0153: "œ" LATIN SMALL LIGATURE OE
-                    // U+00BA: "º" MASCULINE ORDINAL INDICATOR
-                    .setMoreKeysOf("o",
-                            "\u00F2", "\u00F3", "\u00F4", "\u00F5", "\u00F6", "\u00F8", "\u014D",
-                            "\u014F", "\u0151", "\u0153", "\u00BA")
-                    // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
-                    // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
-                    // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
-                    // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
-                    // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
-                    // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
-                    // U+00E6: "æ" LATIN SMALL LETTER AE
-                    // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
-                    // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
-                    // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
-                    // U+00AA: "ª" FEMININE ORDINAL INDICATOR
-                    .setMoreKeysOf("a",
-                            "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u00E4", "\u00E5", "\u00E6",
-                            "\u0101", "\u0103", "\u0105", "\u00AA")
-                    // U+00DF: "ß" LATIN SMALL LETTER SHARP S
-                    // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
-                    // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
-                    // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
-                    // U+0161: "š" LATIN SMALL LETTER S WITH CARON
-                    // U+017F: "ſ" LATIN SMALL LETTER LONG S
-                    .setMoreKeysOf("s", "\u00DF", "\u015B", "\u015D", "\u015F", "\u0161", "\u017F")
-                    // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
-                    // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
-                    // U+00F0: "ð" LATIN SMALL LETTER ETH
-                    .setMoreKeysOf("d", "\u010F", "\u0111", "\u00F0")
-                    // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
-                    // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
-                    // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
-                    // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
-                    .setMoreKeysOf("g", "\u011D", "\u011F", "\u0121", "\u0123")
-                    // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
-                    .setMoreKeysOf("h", "\u0125")
-                    // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
-                    .setMoreKeysOf("j", "\u0135")
-                    // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
-                    // U+0138: "ĸ" LATIN SMALL LETTER KRA
-                    .setMoreKeysOf("k", "\u0137", "\u0138")
-                    // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
-                    // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
-                    // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
-                    // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
-                    // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
-                    .setMoreKeysOf("l", "\u013A", "\u013C", "\u013E", "\u0140", "\u0142")
-                    // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
-                    // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
-                    // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
-                    .setMoreKeysOf("z", "\u017A", "\u017C", "\u017E")
-                    // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
-                    // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
-                    // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
-                    // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
-                    // U+010D: "č" LATIN SMALL LETTER C WITH CARON
-                    .setMoreKeysOf("c", "\u00E7", "\u0107", "\u0109", "\u010B", "\u010D")
-                    // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
-                    // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
-                    // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
-                    // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
-                    // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
-                    // U+014B: "ŋ" LATIN SMALL LETTER ENG
-                    .setMoreKeysOf("n", "\u00F1", "\u0144", "\u0146", "\u0148", "\u0149", "\u014B");
-        }
-    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
new file mode 100644
index 0000000..8d627e3
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Colemak;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/colemak
+ */
+@SmallTest
+public final class TestsNoLanguageColemak extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("zz");
+    private static final LayoutBase LAYOUT = new Colemak(new NoLanguageColemakCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class NoLanguageColemakCustomizer extends LayoutCustomizer {
+        private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+        public NoLanguageColemakCustomizer(final Locale locale) {
+            super(locale);
+            mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mNoLanguageCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
new file mode 100644
index 0000000..9bf47ed
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/dvorak
+ */
+@SmallTest
+public final class TestsNoLanguageDvorak extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("zz");
+    private static final LayoutBase LAYOUT = new Dvorak(new NoLanguageDvorakCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class NoLanguageDvorakCustomizer extends DvorakCustomizer {
+        private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+        public NoLanguageDvorakCustomizer(final Locale locale) {
+            super(locale);
+            mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mNoLanguageCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml
index 6656776..47b1c9e 100644
--- a/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml
@@ -62,7 +62,7 @@
     <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
          U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
          U+010D: "č" LATIN SMALL LETTER C WITH CARON -->
-    <string name="more_keys_for_c">&#x00E7;,&#x0107;,&#x010D;</string>
+    <string name="more_keys_for_c">&#x00E7;,%,&#x0107;,&#x010D;</string>
     <!-- U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS -->
     <string name="more_keys_for_y">%,&#x00FF;</string>
     <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE -->