aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/event/EventInterpreter.java
blob: 726b9206bdc966b4f5f76bafed937f96b274cb2b (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Copyright (C) 2012 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.event;

import android.util.SparseArray;
import android.view.KeyEvent;

import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.utils.CollectionUtils;

import java.util.ArrayList;

/**
 * This class implements the logic between receiving events and generating code points.
 *
 * Event sources are multiple. It may be a hardware keyboard, a D-PAD, a software keyboard,
 * or any exotic input source.
 * This class will orchestrate the decoding chain that starts with an event and ends up with
 * a stream of code points + decoding state.
 */
public class EventInterpreter {
    // TODO: Implement an object pool for events, as we'll create a lot of them
    // TODO: Create a combiner
    // TODO: Create an object type to represent input material + visual feedback + decoding state
    // TODO: Create an interface to call back to Latin IME through the above object

    final EventDecoderSpec mDecoderSpec;
    final SparseArray<HardwareEventDecoder> mHardwareEventDecoders;
    final SoftwareEventDecoder mSoftwareEventDecoder;
    final LatinIME mLatinIme;
    final ArrayList<Combiner> mCombiners;

    /**
     * Create a default interpreter.
     *
     * This creates a default interpreter that does nothing. A default interpreter should normally
     * only be used for fallback purposes, when we really don't know what we want to do with input.
     *
     * @param latinIme a reference to the ime.
     */
    public EventInterpreter(final LatinIME latinIme) {
        this(null, latinIme);
    }

    /**
     * Create an event interpreter according to a specification.
     *
     * The specification contains information about what to do with events. Typically, it will
     * contain information about the type of keyboards - for example, if hardware keyboard(s) is/are
     * attached, their type will be included here so that the decoder knows what to do with each
     * keypress (a 10-key keyboard is not handled like a qwerty-ish keyboard).
     * It also contains information for combining characters. For example, if the input language
     * is Japanese, the specification will typically request kana conversion.
     * Also note that the specification can be null. This means that we need to create a default
     * interpreter that does no specific combining, and assumes the most common cases.
     *
     * @param specification the specification for event interpretation. null for default.
     * @param latinIme a reference to the ime.
     */
    public EventInterpreter(final EventDecoderSpec specification, final LatinIME latinIme) {
        mDecoderSpec = null != specification ? specification : new EventDecoderSpec();
        // For both, we expect to have only one decoder in almost all cases, hence the default
        // capacity of 1.
        mHardwareEventDecoders = new SparseArray<HardwareEventDecoder>(1);
        mSoftwareEventDecoder = new SoftwareKeyboardEventDecoder();
        mCombiners = CollectionUtils.newArrayList();
        mCombiners.add(new DeadKeyCombiner());
        mLatinIme = latinIme;
    }

    // Helper method to decode a hardware key event into a generic event, and execute any
    // necessary action.
    public boolean onHardwareKeyEvent(final KeyEvent hardwareKeyEvent) {
        final Event decodedEvent = getHardwareKeyEventDecoder(hardwareKeyEvent.getDeviceId())
                .decodeHardwareKey(hardwareKeyEvent);
        return onEvent(decodedEvent);
    }

    public boolean onSoftwareEvent() {
        final Event decodedEvent = getSoftwareEventDecoder().decodeSoftwareEvent();
        return onEvent(decodedEvent);
    }

    private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
        final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId);
        if (null != decoder) return decoder;
        // TODO: create the decoder according to the specification
        final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId);
        mHardwareEventDecoders.put(deviceId, newDecoder);
        return newDecoder;
    }

    private SoftwareEventDecoder getSoftwareEventDecoder() {
        // Within the context of Latin IME, since we never present several software interfaces
        // at the time, we should never need multiple software event decoders at a time.
        return mSoftwareEventDecoder;
    }

    private boolean onEvent(final Event event) {
        Event currentlyProcessingEvent = event;
        boolean processed = false;
        for (int i = 0; i < mCombiners.size(); ++i) {
            currentlyProcessingEvent = mCombiners.get(i).combine(event);
        }
        while (null != currentlyProcessingEvent) {
            if (currentlyProcessingEvent.isCommittable()) {
                mLatinIme.onCodeInput(currentlyProcessingEvent.mCodePoint,
                        Constants.EXTERNAL_KEYBOARD_COORDINATE,
                        Constants.EXTERNAL_KEYBOARD_COORDINATE);
                processed = true;
            } else if (event.isDead()) {
                processed = true;
            }
            currentlyProcessingEvent = currentlyProcessingEvent.mNextEvent;
        }
        return processed;
    }
}