Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51fc864e authored by Dan Zivkovic's avatar Dan Zivkovic Committed by Android Git Automerger
Browse files

am 1194ad63: am 1fdb8f31: Define shortcuts for toggling IME layouts.

* commit '1194ad63':
  Define shortcuts for toggling IME layouts.
parents 8aed171c 1194ad63
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -91,4 +91,22 @@
    <fraction name="config_emoji_keyboard_row_height">33%p</fraction>
    <fraction name="config_emoji_keyboard_key_letter_size">68%p</fraction>
    <integer name="config_emoji_keyboard_max_page_key_count">21</integer>

    <!-- Key codes of hardware keys that can be used to toggle the Emoji layout.
         Each array defines a comma-separated tuple containing:
         1. Key code constant from android.view.KeyEvent
         2. Meta mask (if any) from android.view.KeyEvent
         Used in EmojiAltPhysicalKeyDetector and KeyboardSwitcher. -->
    <string-array name="keyboard_switcher_emoji" translatable="false">
        <item>57,16</item> <!-- KeyEvent.KEYCODE_ALT_LEFT , KeyEvent.META_ALT_LEFT_ON -->
    </string-array>

    <!-- Key codes of hardware keys that can be used to toggle the Symbols (Shifted) layout.
         Each array defines a comma-separated tuple containing:
         1. Key code constant from android.view.KeyEvent
         2. Meta mask (if any) from android.view.KeyEvent
         Used in EmojiAltPhysicalKeyDetector and KeyboardSwitcher. -->
    <string-array name="keyboard_switcher_symbols_shifted" translatable="false">
        <item>58,32</item> <!-- KeyEvent.KEYCODE_ALT_RIGHT , KeyEvent.META_ALT_RIGHT_ON -->
    </string-array>
</resources>
+95 −28
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import com.android.inputmethod.latin.utils.RecapitalizeStatus;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;

import javax.annotation.Nonnull;

public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
    private static final String TAG = KeyboardSwitcher.class.getSimpleName();

@@ -139,15 +141,18 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        }
    }

    private void setKeyboard(final Keyboard keyboard) {
    private void setKeyboard(
            @Nonnull final int keyboardId,
            @Nonnull final KeyboardSwitchState toggleState) {
        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
        final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
        setMainKeyboardFrame(currentSettingsValues);
        setMainKeyboardFrame(currentSettingsValues, toggleState);
        // TODO: pass this object to setKeyboard instead of getting the current values.
        final MainKeyboardView keyboardView = mKeyboardView;
        final Keyboard oldKeyboard = keyboardView.getKeyboard();
        keyboardView.setKeyboard(keyboard);
        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
        final Keyboard newKeyboard = mKeyboardLayoutSet.getKeyboard(keyboardId);
        keyboardView.setKeyboard(newKeyboard);
        mCurrentInputView.setKeyboardTopPadding(newKeyboard.mTopPadding);
        keyboardView.setKeyPreviewPopupEnabled(
                currentSettingsValues.mKeyPreviewPopupOn,
                currentSettingsValues.mKeyPreviewPopupDismissDelay);
@@ -161,9 +166,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
                currentSettingsValues.mKeyPreviewDismissDuration);
        keyboardView.updateShortcutKey(mRichImm.isShortcutImeReady());
        final boolean subtypeChanged = (oldKeyboard == null)
                || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
                || !newKeyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
        final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils
                .getLanguageOnSpacebarFormatType(keyboard.mId.mSubtype);
                .getLanguageOnSpacebarFormatType(newKeyboard.mId.mSubtype);
        final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm
                .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
@@ -205,7 +210,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setAlphabetKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
        setKeyboard(KeyboardId.ELEMENT_ALPHABET, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
@@ -214,7 +219,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setAlphabetManualShiftedKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
        setKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
@@ -223,7 +228,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
        setKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
@@ -232,7 +237,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setAlphabetShiftLockedKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
        setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
@@ -241,7 +246,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
        setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
@@ -250,11 +255,29 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setSymbolsKeyboard");
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
        setKeyboard(KeyboardId.ELEMENT_SYMBOLS, KeyboardSwitchState.OTHER);
    }

    // Implements {@link KeyboardState.SwitchActions}.
    @Override
    public void setSymbolsShiftedKeyboard() {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setSymbolsShiftedKeyboard");
        }
        setKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, KeyboardSwitchState.SYMBOLS_SHIFTED);
    }

    public boolean isImeSuppressedByHardwareKeyboard(
            @Nonnull final SettingsValues settingsValues,
            @Nonnull final KeyboardSwitchState toggleState) {
        return settingsValues.mHasHardwareKeyboard && toggleState == KeyboardSwitchState.HIDDEN;
    }

    private void setMainKeyboardFrame(final SettingsValues settingsValues) {
        final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE;
    private void setMainKeyboardFrame(
            @Nonnull final SettingsValues settingsValues,
            @Nonnull final KeyboardSwitchState toggleState) {
        final int visibility =  isImeSuppressedByHardwareKeyboard(settingsValues, toggleState)
                ? View.GONE : View.VISIBLE;
        mKeyboardView.setVisibility(visibility);
        // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
        // @see #getVisibleKeyboardView() and
@@ -282,24 +305,55 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        mEmojiPalettesView.setVisibility(View.VISIBLE);
    }

    public void onToggleEmojiKeyboard() {
        final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null);
        if (needsToLoadKeyboard || !isShowingEmojiPalettes()) {
            mLatinIME.startShowingInputView(needsToLoadKeyboard);
            setEmojiKeyboard();
        } else {
            mLatinIME.stopShowingInputView();
            setAlphabetKeyboard();
    public enum KeyboardSwitchState {
        HIDDEN(-1),
        SYMBOLS_SHIFTED(KeyboardId.ELEMENT_SYMBOLS_SHIFTED),
        EMOJI(KeyboardId.ELEMENT_EMOJI_RECENTS),
        OTHER(-1);

        final int mKeyboardId;

        KeyboardSwitchState(int keyboardId) {
            mKeyboardId = keyboardId;
        }
    }

    // Implements {@link KeyboardState.SwitchActions}.
    @Override
    public void setSymbolsShiftedKeyboard() {
        if (DEBUG_ACTION) {
            Log.d(TAG, "setSymbolsShiftedKeyboard");
    public KeyboardSwitchState getKeyboardSwitchState() {
        boolean hidden = !isShowingEmojiPalettes()
                && (mKeyboardLayoutSet == null
                || mKeyboardView == null
                || !mKeyboardView.isShown());
        KeyboardSwitchState state;
        if (hidden) {
            return KeyboardSwitchState.HIDDEN;
        } else if (isShowingEmojiPalettes()) {
            return KeyboardSwitchState.EMOJI;
        } else if (isShowingKeyboardId(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)) {
            return KeyboardSwitchState.SYMBOLS_SHIFTED;
        }
        return KeyboardSwitchState.OTHER;
    }

    public void onToggleKeyboard(@Nonnull final KeyboardSwitchState toggleState) {
        KeyboardSwitchState currentState = getKeyboardSwitchState();
        Log.w(TAG, "onToggleKeyboard() : Current = " + currentState + " : Toggle = " + toggleState);
        if (currentState == toggleState) {
            mLatinIME.stopShowingInputView();
            mLatinIME.hideWindow();
            setAlphabetKeyboard();
        } else {
            mLatinIME.startShowingInputView(true);
            if (toggleState == KeyboardSwitchState.EMOJI) {
                setEmojiKeyboard();
            } else {
                mEmojiPalettesView.stopEmojiPalettes();
                mEmojiPalettesView.setVisibility(View.GONE);

                mMainKeyboardFrame.setVisibility(View.VISIBLE);
                mKeyboardView.setVisibility(View.VISIBLE);
                setKeyboard(toggleState.mKeyboardId, toggleState);
            }
        }
        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
    }

    // Future method for requesting an updating to the shift state.
@@ -355,6 +409,19 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
        mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState);
    }

    public boolean isShowingKeyboardId(@Nonnull int... keyboardIds) {
        if (mKeyboardView == null || !mKeyboardView.isShown()) {
            return false;
        }
        int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId;
        for (int keyboardId : keyboardIds) {
            if (activeKeyboardId == keyboardId) {
                return true;
            }
        }
        return false;
    }

    public boolean isShowingEmojiPalettes() {
        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
    }
+95 −43
Original line number Diff line number Diff line
@@ -16,88 +16,140 @@

package com.android.inputmethod.latin;

import android.content.res.Resources;
import android.util.Log;
import android.view.KeyEvent;

import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.settings.Settings;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;

/**
 * A class for detecting Emoji-Alt physical key.
 */
final class EmojiAltPhysicalKeyDetector {
    private static final String TAG = "EmojiAltPhysicalKeyDetector";

    private final RichInputConnection mRichInputConnection;
    private final Map<Integer, Integer> mEmojiSwitcherMap;
    private final Map<Integer, Integer> mSymbolsShiftedSwitcherMap;
    private final Map<Integer, Integer> mCombinedSwitcherMap;

    // True if the Alt key has been used as a modifier. In this case the Alt key up isn't
    // recognized as an emoji key.
    private boolean mAltHasBeenUsedAsAModifier;
    // Set of keys codes that have been used as modifiers.
    private Set<Integer> mActiveModifiers;

    public EmojiAltPhysicalKeyDetector(final RichInputConnection richInputConnection) {
        mRichInputConnection = richInputConnection;
    public EmojiAltPhysicalKeyDetector(@Nonnull final Resources resources) {
        mEmojiSwitcherMap = parseSwitchDefinition(resources, R.array.keyboard_switcher_emoji);
        mSymbolsShiftedSwitcherMap = parseSwitchDefinition(
                resources, R.array.keyboard_switcher_symbols_shifted);
        mCombinedSwitcherMap = new HashMap<>();
        mCombinedSwitcherMap.putAll(mEmojiSwitcherMap);
        mCombinedSwitcherMap.putAll(mSymbolsShiftedSwitcherMap);
        mActiveModifiers = new HashSet<>();
    }

    /**
     * Record a down key event.
     * @param keyEvent a down key event.
     */
    public void onKeyDown(final KeyEvent keyEvent) {
        if (isAltKey(keyEvent)) {
            mAltHasBeenUsedAsAModifier = false;
    private static Map<Integer, Integer> parseSwitchDefinition(
            @Nonnull final Resources resources,
            final int resourceId) {
        final Map<Integer, Integer> definition = new HashMap<>();
        final String name = resources.getResourceEntryName(resourceId);
        final String[] values = resources.getStringArray(resourceId);
        for (int i = 0; values != null && i < values.length; i++) {
            String[] valuePair = values[i].split(",");
            if (valuePair.length != 2) {
                Log.w(TAG, "Expected 2 integers in " + name + "[" + i + "] : " + values[i]);
            }
            try {
                definition.put(Integer.parseInt(valuePair[0]), Integer.parseInt(valuePair[1]));
            } catch (NumberFormatException e) {
                Log.w(TAG, "Failed to parse " + name + "[" + i + "] : " + values[i], e);
            }
        if (containsAltModifier(keyEvent)) {
            mAltHasBeenUsedAsAModifier = true;
        }
        return definition;
    }

    /**
     * Determine whether an up key event is a special key up or not.
     * Determine whether an up key event came from a mapped modifier key.
     *
     * @param keyEvent an up key event.
     */
    public void onKeyUp(final KeyEvent keyEvent) {
    public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
        Log.d(TAG, "onKeyUp() : " + keyEvent);
        if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
            // The feature is disabled.
            Log.d(TAG, "onKeyUp() : Disabled");
            return;
        }
        if (keyEvent.isCanceled()) {
            // This key up event was a part of key combinations and should be ignored.
            Log.d(TAG, "onKeyUp() : Canceled");
            return;
        }
        if (!isAltKey(keyEvent)) {
            mAltHasBeenUsedAsAModifier |= containsAltModifier(keyEvent);
        final Integer mappedModifier = getMappedModifier(keyEvent);
        if (mappedModifier != null) {
            // If the key was modified by a mapped key, then ignore the next time
            // the same modifier key comes up.
            Log.d(TAG, "onKeyUp() : Using Modifier: " + mappedModifier);
            mActiveModifiers.add(mappedModifier);
            return;
        }
        if (containsAltModifier(keyEvent)) {
            mAltHasBeenUsedAsAModifier = true;
        final int keyCode = keyEvent.getKeyCode();
        if (mActiveModifiers.contains(keyCode)) {
            // Used as a modifier, not a standalone key press.
            Log.d(TAG, "onKeyUp() : Used as Modifier: " + keyCode);
            mActiveModifiers.remove(keyCode);
            return;
        }
        if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
        if (!isMappedKeyCode(keyEvent)) {
            // Nothing special about this key.
            Log.d(TAG, "onKeyUp() : Not Mapped: " + keyCode);
            return;
        }
        if (mAltHasBeenUsedAsAModifier) {
            return;
        final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
        if (mEmojiSwitcherMap.keySet().contains(keyCode)) {
            switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.EMOJI);
        } else if (mSymbolsShiftedSwitcherMap.keySet().contains(keyCode)) {
            switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.SYMBOLS_SHIFTED);
        } else {
            Log.w(TAG, "Cannot toggle on keyCode: " + keyCode);
        }
        onEmojiAltKeyDetected();
    }

    private static void onEmojiAltKeyDetected() {
        KeyboardSwitcher.getInstance().onToggleEmojiKeyboard();
    /**
     * @param keyEvent pressed key event
     * @return true iff the user pressed a mapped modifier key.
     */
    private boolean isMappedKeyCode(@Nonnull final KeyEvent keyEvent) {
        return mCombinedSwitcherMap.get(keyEvent.getKeyCode()) != null;
    }

    private static boolean isAltKey(final KeyEvent keyEvent) {
    /**
     * @param keyEvent pressed key event
     * @return the mapped modifier used with this key opress, if any.
     */
    private Integer getMappedModifier(@Nonnull final KeyEvent keyEvent) {
        final int keyCode = keyEvent.getKeyCode();
        return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT;
    }

    private static boolean containsAltModifier(final KeyEvent keyEvent) {
        final int metaState = keyEvent.getMetaState();
        // TODO: Support multiple keyboards. Take device id into account.
        switch (keyEvent.getKeyCode()) {
        case KeyEvent.KEYCODE_ALT_LEFT:
            // Return true if Left-Alt is pressed with Right-Alt pressed.
            return (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
        case KeyEvent.KEYCODE_ALT_RIGHT:
            // Return true if Right-Alt is pressed with Left-Alt pressed.
            return (metaState & KeyEvent.META_ALT_LEFT_ON) != 0;
        default:
            return (metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0;
        for (int mappedKeyCode : mCombinedSwitcherMap.keySet()) {
            if (keyCode == mappedKeyCode) {
                Log.d(TAG, "getMappedModifier() : KeyCode = MappedKeyCode = " + mappedKeyCode);
                continue;
            }
            final Integer mappedMeta = mCombinedSwitcherMap.get(mappedKeyCode);
            if (mappedMeta == null || mappedMeta.intValue() == -1) {
                continue;
            }
            if ((metaState & mappedMeta) != 0) {
                Log.d(TAG, "getMappedModifier() : MetaState(" + metaState
                        + ") contains MappedMeta(" + mappedMeta + ")");
                return mappedKeyCode;
            }
        }
        return null;
    }
}
+16 −11
Original line number Diff line number Diff line
@@ -142,8 +142,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    private RichInputMethodManager mRichImm;
    @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
    private final SubtypeState mSubtypeState = new SubtypeState();
    private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector =
            new EmojiAltPhysicalKeyDetector(mInputLogic.mConnection);
    private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
    private StatsUtilsManager mStatsUtilsManager;
    // Working variable for {@link #startShowingInputView()} and
    // {@link #onEvaluateInputViewShown()}.
@@ -702,6 +701,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
        mInputLogic.recycle();
    }

    private boolean isImeSuppressedByHardwareKeyboard() {
        final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
        return switcher.isImeSuppressedByHardwareKeyboard(
                mSettings.getCurrent(), switcher.getKeyboardSwitchState());
    }

    @Override
    public void onConfigurationChanged(final Configuration conf) {
        SettingsValues settingsValues = mSettings.getCurrent();
@@ -716,7 +721,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
            // have a change in hardware keyboard configuration.
            loadSettings();
            settingsValues = mSettings.getCurrent();
            if (settingsValues.mHasHardwareKeyboard) {
            if (isImeSuppressedByHardwareKeyboard()) {
                // We call cleanupInternalStateForFinishInput() because it's the right thing to do;
                // however, it seems at the moment the framework is passing us a seemingly valid
                // but actually non-functional InputConnection object. So if this bug ever gets
@@ -874,7 +879,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
        // can go into the correct mode, so we need to do some housekeeping here.
        final boolean needToCallLoadKeyboardLater;
        final Suggest suggest = mInputLogic.mSuggest;
        if (!currentSettingsValues.mHasHardwareKeyboard) {
        if (!isImeSuppressedByHardwareKeyboard()) {
            // The app calling setText() has the effect of clearing the composing
            // span, so we should reset our state unconditionally, even if restarting is true.
            // We also tell the input logic about the combining rules for the current subtype, so
@@ -1118,8 +1123,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
            return;
        }
        final int inputHeight = mInputView.getHeight();
        final boolean hasHardwareKeyboard = settingsValues.mHasHardwareKeyboard;
        if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) {
        if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
            // If there is a hardware keyboard and a visible software keyboard view has been hidden,
            // no visual element will be shown on the screen.
            outInsets.contentTopInsets = inputHeight;
@@ -1165,7 +1169,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen

    @Override
    public boolean onShowInputRequested(final int flags, final boolean configChange) {
        if (Settings.getInstance().getCurrent().mHasHardwareKeyboard) {
        if (isImeSuppressedByHardwareKeyboard()) {
            return true;
        }
        return super.onShowInputRequested(flags, configChange);
@@ -1182,7 +1186,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    @Override
    public boolean onEvaluateFullscreenMode() {
        final SettingsValues settingsValues = mSettings.getCurrent();
        if (settingsValues.mHasHardwareKeyboard) {
        if (isImeSuppressedByHardwareKeyboard()) {
            // If there is a hardware keyboard, disable full screen mode.
            return false;
        }
@@ -1646,8 +1650,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    // Hooks for hardware keyboard
    @Override
    public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
        // TODO: This should be processed in {@link InputLogic}.
        mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent);
        if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
            return super.onKeyDown(keyCode, keyEvent);
        }
@@ -1668,7 +1670,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen

    @Override
    public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
        // TODO: This should be processed in {@link InputLogic}.
        if (mEmojiAltPhysicalKeyDetector == null) {
            mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
                    getApplicationContext().getResources());
        }
        mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent);
        if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
            return super.onKeyUp(keyCode, keyEvent);