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

Commit fdb2cf49 authored by David Riley's avatar David Riley
Browse files

Merge remote-tracking branch 'goog/mnc-dr-ryu-dev'

parents a78b3807 a2a057a9
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -196,6 +196,8 @@ final class EmojiCategory {
                    addShownCategoryId(EmojiCategory.ID_FLAGS);
                }
            }
        } else {
            addShownCategoryId(EmojiCategory.ID_SYMBOLS);
        }
        addShownCategoryId(EmojiCategory.ID_EMOTICONS);

@@ -204,9 +206,14 @@ final class EmojiCategory {
        recentsKbd.loadRecentKeys(mCategoryKeyboardMap.values());

        mCurrentCategoryId = Settings.readLastShownEmojiCategoryId(mPrefs, defaultCategoryId);
        if (mCurrentCategoryId == EmojiCategory.ID_RECENTS &&
        Log.i(TAG, "Last Emoji category id is " + mCurrentCategoryId);
        if (!isShownCategoryId(mCurrentCategoryId)) {
            Log.i(TAG, "Last emoji category " + mCurrentCategoryId +
                    " is invalid, starting in " + defaultCategoryId);
            mCurrentCategoryId = defaultCategoryId;
        } else if (mCurrentCategoryId == EmojiCategory.ID_RECENTS &&
                recentsKbd.getSortedKeys().isEmpty()) {
            Log.i(TAG, "No recent emojis found, starting in category " + mCurrentCategoryId);
            Log.i(TAG, "No recent emojis found, starting in category " + defaultCategoryId);
            mCurrentCategoryId = defaultCategoryId;
        }
    }
@@ -219,6 +226,15 @@ final class EmojiCategory {
        mShownCategories.add(properties);
    }

    private boolean isShownCategoryId(final int categoryId) {
        for (final CategoryProperties prop : mShownCategories) {
            if (prop.mCategoryId == categoryId) {
                return true;
            }
        }
        return false;
    }

    public static String getCategoryName(final int categoryId, final int categoryPageId) {
        return sCategoryName[categoryId] + "-" + categoryPageId;
    }
+149 −97
Original line number Diff line number Diff line
@@ -18,14 +18,15 @@ package com.android.inputmethod.latin;

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

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

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

import javax.annotation.Nonnull;
@@ -35,121 +36,172 @@ import javax.annotation.Nonnull;
 */
final class EmojiAltPhysicalKeyDetector {
    private static final String TAG = "EmojiAltPhysicalKeyDetector";
    private static final boolean DEBUG = false;

    private final Map<Integer, Integer> mEmojiSwitcherMap;
    private final Map<Integer, Integer> mSymbolsShiftedSwitcherMap;
    private final Map<Integer, Integer> mCombinedSwitcherMap;
    private List<EmojiHotKeys> mHotKeysList;

    // Set of keys codes that have been used as modifiers.
    private Set<Integer> mActiveModifiers;
    private static class HotKeySet extends HashSet<Pair<Integer, Integer>> { };

    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<>();
    private abstract class EmojiHotKeys {
        private final String mName;
        private final HotKeySet mKeySet;

        boolean mCanFire;
        int mMetaState;

        public EmojiHotKeys(final String name, HotKeySet keySet) {
            mName = name;
            mKeySet = keySet;
            mCanFire = 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]);
        public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
            if (DEBUG) {
                Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - considering " + keyEvent);
            }
            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);

            final Pair<Integer, Integer> key =
                    Pair.create(keyEvent.getKeyCode(), keyEvent.getMetaState());
            if (mKeySet.contains(key)) {
                if (DEBUG) {
                   Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - enabling action");
                }
                mCanFire = true;
                mMetaState = keyEvent.getMetaState();
            } else if (mCanFire) {
                if (DEBUG) {
                   Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - disabling action");
                }
                mCanFire = false;
            }
        return definition;
        }

    /**
     * Determine whether an up key event came from a mapped modifier key.
     *
     * @param keyEvent an up key event.
     */
        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;
        }
        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 (DEBUG) {
                Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - considering " + keyEvent);
            }

            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;
            int metaState = keyEvent.getMetaState();
            if (KeyEvent.isModifierKey(keyCode)) {
                 // Try restoring meta stat in case the released key was a modifier.
                 // I am sure one can come up with scenarios to break this, but it
                 // seems to work well in practice.
                 metaState |= mMetaState;
            }

            final Pair<Integer, Integer> key = Pair.create(keyCode, metaState);
            if (mKeySet.contains(key)) {
                if (mCanFire) {
                    if (!keyEvent.isCanceled()) {
                        if (DEBUG) {
                            Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - firing action");
                        }
                        action();
                    } else {
                        // This key up event was a part of key combinations and
                        // should be ignored.
                        if (DEBUG) {
                            Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - canceled, ignoring action");
                        }
                    }
                    mCanFire = false;
                }
            }
        if (!isMappedKeyCode(keyEvent)) {
            // Nothing special about this key.
            Log.d(TAG, "onKeyUp() : Not Mapped: " + keyCode);
            return;

            if (mCanFire) {
                if (DEBUG) {
                    Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - disabling action");
                }
                mCanFire = false;
            }
        }

        protected abstract void action();
    }

    public EmojiAltPhysicalKeyDetector(@Nonnull final Resources resources) {
        mHotKeysList = new ArrayList<EmojiHotKeys>();

        final HotKeySet emojiSwitchSet = parseHotKeys(
                resources, R.array.keyboard_switcher_emoji);
        final EmojiHotKeys emojiHotKeys = new EmojiHotKeys("emoji", emojiSwitchSet) {
            @Override
            protected void action() {
                final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
        if (mEmojiSwitcherMap.keySet().contains(keyCode)) {
                switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.EMOJI);
        } else if (mSymbolsShiftedSwitcherMap.keySet().contains(keyCode)) {
            }
        };
        mHotKeysList.add(emojiHotKeys);

        final HotKeySet symbolsSwitchSet = parseHotKeys(
                resources, R.array.keyboard_switcher_symbols_shifted);
        final EmojiHotKeys symbolsHotKeys = new EmojiHotKeys("symbols", symbolsSwitchSet) {
            @Override
            protected void action() {
                final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
                switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.SYMBOLS_SHIFTED);
        } else {
            Log.w(TAG, "Cannot toggle on keyCode: " + keyCode);
            }
        };
        mHotKeysList.add(symbolsHotKeys);
    }

    /**
     * @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;
    public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
        if (DEBUG) {
            Log.d(TAG, "onKeyDown(): " + 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();
        final int metaState = keyEvent.getMetaState();
        for (int mappedKeyCode : mCombinedSwitcherMap.keySet()) {
            if (keyCode == mappedKeyCode) {
                Log.d(TAG, "getMappedModifier() : KeyCode = MappedKeyCode = " + mappedKeyCode);
                continue;
        if (shouldProcessEvent(keyEvent)) {
            for (EmojiHotKeys hotKeys : mHotKeysList) {
                hotKeys.onKeyDown(keyEvent);
            }
        }
    }

    public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
        if (DEBUG) {
            Log.d(TAG, "onKeyUp(): " + keyEvent);
        }

        if (shouldProcessEvent(keyEvent)) {
            for (EmojiHotKeys hotKeys : mHotKeysList) {
                hotKeys.onKeyUp(keyEvent);
            }
        }
            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;

    private static boolean shouldProcessEvent(@Nonnull final KeyEvent keyEvent) {
        if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
            // The feature is disabled.
            if (DEBUG) {
                Log.d(TAG, "shouldProcessEvent(): Disabled");
            }
            return false;
        }

        return true;
    }

    private static HotKeySet parseHotKeys(
            @Nonnull final Resources resources, final int resourceId) {
        final HotKeySet keySet = new HotKeySet();
        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 {
                final Integer keyCode = Integer.parseInt(valuePair[0]);
                final Integer metaState = Integer.parseInt(valuePair[1]);
                final Pair<Integer, Integer> key = Pair.create(
                        keyCode, KeyEvent.normalizeMetaState(metaState));
                keySet.add(key);
            } catch (NumberFormatException e) {
                Log.w(TAG, "Failed to parse " + name + "[" + i + "] : " + values[i], e);
            }
        }
        return null;
        return keySet;
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -1658,6 +1658,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    // Hooks for hardware keyboard
    @Override
    public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
        if (mEmojiAltPhysicalKeyDetector == null) {
            mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
                    getApplicationContext().getResources());
        }
        mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent);
        if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
            return super.onKeyDown(keyCode, keyEvent);
        }