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

Commit ef002248 authored by Cosmin Băieș's avatar Cosmin Băieș Committed by Android (Google) Code Review
Browse files

Merge "Refactor SimpleKeyboard into SimpleKeyboardView" into main

parents 8bd38e91 3158d159
Loading
Loading
Loading
Loading
+0 −22
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2022 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.
  -->

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/input"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
 No newline at end of file
+46 −39
Original line number Diff line number Diff line
@@ -18,51 +18,58 @@ package com.android.apps.inputmethod.simpleime;

import android.view.KeyEvent;

import java.util.HashMap;
import androidx.annotation.NonNull;

/** Holder of key codes and their name. */
public final class KeyCodeConstants {
    private KeyCodeConstants() {}
final class KeyCodeConstants {

    static final HashMap<String, Integer> KEY_NAME_TO_CODE_MAP = new HashMap<>();
    private KeyCodeConstants() {
    }

    static {
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_A", KeyEvent.KEYCODE_A);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_B", KeyEvent.KEYCODE_B);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_C", KeyEvent.KEYCODE_C);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_D", KeyEvent.KEYCODE_D);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_E", KeyEvent.KEYCODE_E);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_F", KeyEvent.KEYCODE_F);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_G", KeyEvent.KEYCODE_G);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_H", KeyEvent.KEYCODE_H);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_I", KeyEvent.KEYCODE_I);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_J", KeyEvent.KEYCODE_J);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_K", KeyEvent.KEYCODE_K);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_L", KeyEvent.KEYCODE_L);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_M", KeyEvent.KEYCODE_M);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_N", KeyEvent.KEYCODE_N);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_O", KeyEvent.KEYCODE_O);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_P", KeyEvent.KEYCODE_P);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_Q", KeyEvent.KEYCODE_Q);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_R", KeyEvent.KEYCODE_R);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_S", KeyEvent.KEYCODE_S);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_T", KeyEvent.KEYCODE_T);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_U", KeyEvent.KEYCODE_U);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_V", KeyEvent.KEYCODE_V);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_W", KeyEvent.KEYCODE_W);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_X", KeyEvent.KEYCODE_X);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_Y", KeyEvent.KEYCODE_Y);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_Z", KeyEvent.KEYCODE_Z);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_SHIFT", KeyEvent.KEYCODE_SHIFT_LEFT);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_DEL", KeyEvent.KEYCODE_DEL);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_SPACE", KeyEvent.KEYCODE_SPACE);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_ENTER", KeyEvent.KEYCODE_ENTER);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_COMMA", KeyEvent.KEYCODE_COMMA);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_PERIOD", KeyEvent.KEYCODE_PERIOD);
        KEY_NAME_TO_CODE_MAP.put("KEYCODE_TAB", KeyEvent.KEYCODE_TAB);
    /**
     * Returns the keyCode value corresponding to the given keyCode name.
     *
     * @param keyCodeName the keyCode name.
     */
    static int getKeyCode(@NonNull String keyCodeName) {
        return switch (keyCodeName) {
            case "KEYCODE_A" -> KeyEvent.KEYCODE_A;
            case "KEYCODE_B" -> KeyEvent.KEYCODE_B;
            case "KEYCODE_C" -> KeyEvent.KEYCODE_C;
            case "KEYCODE_D" -> KeyEvent.KEYCODE_D;
            case "KEYCODE_E" -> KeyEvent.KEYCODE_E;
            case "KEYCODE_F" -> KeyEvent.KEYCODE_F;
            case "KEYCODE_G" -> KeyEvent.KEYCODE_G;
            case "KEYCODE_H" -> KeyEvent.KEYCODE_H;
            case "KEYCODE_I" -> KeyEvent.KEYCODE_I;
            case "KEYCODE_J" -> KeyEvent.KEYCODE_J;
            case "KEYCODE_K" -> KeyEvent.KEYCODE_K;
            case "KEYCODE_L" -> KeyEvent.KEYCODE_L;
            case "KEYCODE_M" -> KeyEvent.KEYCODE_M;
            case "KEYCODE_N" -> KeyEvent.KEYCODE_N;
            case "KEYCODE_O" -> KeyEvent.KEYCODE_O;
            case "KEYCODE_P" -> KeyEvent.KEYCODE_P;
            case "KEYCODE_Q" -> KeyEvent.KEYCODE_Q;
            case "KEYCODE_R" -> KeyEvent.KEYCODE_R;
            case "KEYCODE_S" -> KeyEvent.KEYCODE_S;
            case "KEYCODE_T" -> KeyEvent.KEYCODE_T;
            case "KEYCODE_U" -> KeyEvent.KEYCODE_U;
            case "KEYCODE_V" -> KeyEvent.KEYCODE_V;
            case "KEYCODE_W" -> KeyEvent.KEYCODE_W;
            case "KEYCODE_X" -> KeyEvent.KEYCODE_X;
            case "KEYCODE_Y" -> KeyEvent.KEYCODE_Y;
            case "KEYCODE_Z" -> KeyEvent.KEYCODE_Z;
            case "KEYCODE_SHIFT" -> KeyEvent.KEYCODE_SHIFT_LEFT;
            case "KEYCODE_DEL" -> KeyEvent.KEYCODE_DEL;
            case "KEYCODE_SPACE" -> KeyEvent.KEYCODE_SPACE;
            case "KEYCODE_ENTER" -> KeyEvent.KEYCODE_ENTER;
            case "KEYCODE_COMMA" -> KeyEvent.KEYCODE_COMMA;
            case "KEYCODE_PERIOD" -> KeyEvent.KEYCODE_PERIOD;
            default -> KeyEvent.KEYCODE_UNKNOWN;
        };
    }

    public static boolean isAlphaKeyCode(int keyCode) {
    static boolean isAlphaKeyCode(int keyCode) {
        return keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z;
    }
}
+20 −24
Original line number Diff line number Diff line
@@ -20,45 +20,41 @@ import android.inputmethodservice.InputMethodService;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper;

/** The {@link InputMethodService} implementation for SimpleTestIme app. */
/** A simple implementation of an {@link InputMethodService}. */
public final class SimpleInputMethodService extends InputMethodServiceWrapper {

    private static final String TAG = "SimpleIMS";

    private FrameLayout mInputView;

    @Override
    public View onCreateInputView() {
        Log.i(TAG, "onCreateInputView()");
        mInputView = (FrameLayout) LayoutInflater.from(this).inflate(R.layout.input_view, null);
        return mInputView;
    }

    @Override
    public void onStartInputView(EditorInfo info, boolean restarting) {
        super.onStartInputView(info, restarting);
        mInputView.removeAllViews();
        final var keyboard = new SimpleKeyboard(this, R.layout.qwerty_10_9_9);
        mInputView.addView(keyboard.inflateKeyboardView(LayoutInflater.from(this), mInputView));
        final var simpleKeyboard = new SimpleKeyboardView(this);
        simpleKeyboard.setKeyPressListener(this::onKeyPress);
        return simpleKeyboard;
    }

    void handleKeyPress(@NonNull String keyCodeName, int keyboardState) {
        final Integer keyCode = KeyCodeConstants.KEY_NAME_TO_CODE_MAP.get(keyCodeName);
        Log.v(TAG, "keyCode: " + keyCode);
        if (keyCode != null) {
    /**
     * Called when a key is pressed.
     *
     * @param keyCodeName the keycode of the key, as a string.
     * @param metaState   the flags indicating which meta keys are currently pressed.
     */
    private void onKeyPress(@NonNull String keyCodeName, int metaState) {
        final int keyCode = KeyCodeConstants.getKeyCode(keyCodeName);
        Log.v(TAG, "onKeyPress: " + keyCode);
        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
            final var ic = getCurrentInputConnection();
            if (ic != null) {
                final var downTime = SystemClock.uptimeMillis();
            getCurrentInputConnection().sendKeyEvent(new KeyEvent(downTime, downTime,
                    KeyEvent.ACTION_DOWN, keyCode, 0 /* repeat */,
                    KeyCodeConstants.isAlphaKeyCode(keyCode) ? keyboardState : 0) /* metaState */);
                ic.sendKeyEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode,
                        0 /* repeat */, KeyCodeConstants.isAlphaKeyCode(keyCode) ? metaState : 0));
            }
        }
    }
}
+181 −0
Original line number Diff line number Diff line
@@ -16,20 +16,22 @@

package com.android.apps.inputmethod.simpleime;

import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;

import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/** Controls the visible virtual keyboard view. */
final class SimpleKeyboard {
/** A simple implementation of a software keyboard view. */
final class SimpleKeyboardView extends FrameLayout {

    private static final String TAG = "SimpleKeyboard";

@@ -69,63 +71,110 @@ final class SimpleKeyboard {
            R.id.key_pos_enter,
    };

    @NonNull
    private final SimpleInputMethodService mSimpleInputMethodService;
    private final int mViewResId;
    private final SparseArray<TextView> mSoftKeyViews = new SparseArray<>();
    private View mKeyboardView;
    private int mKeyboardState;

    SimpleKeyboard(@NonNull SimpleInputMethodService simpleInputMethodService, int viewResId) {
        mSimpleInputMethodService = simpleInputMethodService;
        mViewResId = viewResId;
        mKeyboardState = 0;
    @FunctionalInterface
    interface KeyPressListener {

        /**
         * Called when a key is pressed.
         *
         * @param keyCodeName the keycode of the key, as a string.
         * @param metaState   the flags indicating which meta keys are currently pressed.
         */
        void onKeyPress(@NonNull String keyCodeName, int metaState);
    }

    @NonNull
    View inflateKeyboardView(@NonNull LayoutInflater inflater, @NonNull ViewGroup inputView) {
        mKeyboardView = inflater.inflate(mViewResId, inputView, false);
    /** A listener to react to key presses. */
    @Nullable
    private KeyPressListener mKeyPressListener;

    /** The flags indicating which meta keys are currently pressed. */
    private int mMetaState;

    SimpleKeyboardView(@NonNull Context context) {
        this(context, null);
    }

    SimpleKeyboardView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0 /* defStyleAttr */);
    }

    SimpleKeyboardView(@NonNull Context context, @Nullable AttributeSet attrs,
            @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr, 0 /* defStyleRes */);
        LayoutInflater.from(context).inflate(R.layout.qwerty_10_9_9, this, true);
        mapSoftKeys();
        return mKeyboardView;
    }

    /**
     * Sets the key press listener.
     *
     * @param listener the listener to set.
     */
    void setKeyPressListener(@NonNull KeyPressListener listener) {
        mKeyPressListener = listener;
    }

    /** Maps the soft key ids to their corresponding views, and sets their on click listener. */
    private void mapSoftKeys() {
        for (int id : SOFT_KEY_IDS) {
            final TextView softKeyView = mKeyboardView.requireViewById(id);
        for (final int id : SOFT_KEY_IDS) {
            final TextView softKeyView = requireViewById(id);
            mSoftKeyViews.put(id, softKeyView);
            final var keyCodeName = softKeyView.getTag() != null
                    ? softKeyView.getTag().toString() : null;
            softKeyView.setOnClickListener(v -> handleKeyPress(keyCodeName));
            softKeyView.setOnClickListener(v -> onKeyPress(keyCodeName));
        }
    }

    private void handleKeyPress(@Nullable String keyCodeName) {
        Log.i(TAG, "handle(): " + keyCodeName);
    /**
     * Called when a key is pressed.
     *
     * @param keyCodeName the keycode of the key, as a string.
     */
    private void onKeyPress(@Nullable String keyCodeName) {
        Log.i(TAG, "onKeyPress: " + keyCodeName);
        if (TextUtils.isEmpty(keyCodeName)) {
            return;
        }
        if ("KEYCODE_SHIFT".equals(keyCodeName)) {
            handleShift();
            onShiftPress();
            return;
        }

        mSimpleInputMethodService.handleKeyPress(keyCodeName, mKeyboardState);
        if (mKeyPressListener != null) {
            mKeyPressListener.onKeyPress(keyCodeName, mMetaState);
        }
    }

    private void handleShift() {
        mKeyboardState = toggleShiftState(mKeyboardState);
        Log.v(TAG, "currentKeyboardState: " + mKeyboardState);
        final boolean isShiftOn = isShiftOn(mKeyboardState);
    /**
     * Called when the shift key is pressed. This will toggle the capitalization of all the keys.
     */
    private void onShiftPress() {
        mMetaState = toggleShiftState(mMetaState);
        Log.v(TAG, "onShiftPress, new metaState: " + mMetaState);
        final boolean isShiftOn = isShiftOn(mMetaState);
        for (int i = 0; i < mSoftKeyViews.size(); i++) {
            TextView softKeyView = mSoftKeyViews.valueAt(i);
            final TextView softKeyView = mSoftKeyViews.valueAt(i);
            softKeyView.setAllCaps(isShiftOn);
        }
    }

    /**
     * Checks whether the shift meta key is pressed.
     *
     * @param state the flags indicating which meta keys are currently pressed.
     */
    private static boolean isShiftOn(int state) {
        return (state & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
    }

    /**
     * Toggles the value of the shift meta key being pressed.
     *
     * @param state the flags indicating which meta keys are currently pressed.
     * @return a new flag state, with the shift meta key value flipped.
     */
    private static int toggleShiftState(int state) {
        return state ^ KeyEvent.META_SHIFT_ON;
    }