1/*
2 * Copyright 2006, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
17#include <ui/KeyCharacterMap.h>
18#include <ui/Input.h>
19
20#include <android_runtime/AndroidRuntime.h>
21#include <nativehelper/jni.h>
22#include <nativehelper/JNIHelp.h>
23
24#include "android_view_KeyEvent.h"
25
26namespace android {
27
28static struct {
29    jclass clazz;
30} gKeyEventClassInfo;
31
32static struct {
33    jfieldID keyCode;
34    jfieldID metaState;
35} gFallbackActionClassInfo;
36
37
38static jint nativeLoad(JNIEnv *env, jobject clazz, jstring fileStr) {
39    const char* file = env->GetStringUTFChars(fileStr, NULL);
40
41    KeyCharacterMap* map;
42    status_t status = KeyCharacterMap::load(String8(file), &map);
43    jint result;
44    if (status) {
45        String8 msg;
46        msg.appendFormat("Could not load key character map '%s' due to error %d.  "
47                "Refer to the log for details.", file, status);
48        jniThrowException(env, "android/view/KeyCharacterMap$KeyCharacterMapUnavailableException",
49                msg.string());
50        result = 0;
51    } else {
52        result = reinterpret_cast<jint>(map);
53    }
54
55    env->ReleaseStringUTFChars(fileStr, file);
56    return result;
57}
58
59static void nativeDispose(JNIEnv *env, jobject clazz, jint ptr) {
60    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
61    delete map;
62}
63
64static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jint ptr,
65        jint keyCode, jint metaState) {
66    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
67    return map->getCharacter(keyCode, metaState);
68}
69
70static jboolean nativeGetFallbackAction(JNIEnv *env, jobject clazz, jint ptr, jint keyCode,
71        jint metaState, jobject fallbackActionObj) {
72    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
73    KeyCharacterMap::FallbackAction fallbackAction;
74
75    bool result = map->getFallbackAction(keyCode, metaState, &fallbackAction);
76    if (result) {
77        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.keyCode,
78                fallbackAction.keyCode);
79        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.metaState,
80                fallbackAction.metaState);
81    }
82    return result;
83}
84
85static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) {
86    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
87    return map->getNumber(keyCode);
88}
89
90static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jint ptr, jint keyCode,
91        jcharArray charsArray, jint metaState) {
92    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
93
94    jsize numChars = env->GetArrayLength(charsArray);
95    jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL));
96    if (!chars) {
97        return 0;
98    }
99
100    char16_t result = map->getMatch(keyCode, chars, size_t(numChars), metaState);
101
102    env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
103    return result;
104}
105
106static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) {
107    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
108    return map->getDisplayLabel(keyCode);
109}
110
111static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jint ptr) {
112    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
113    return map->getKeyboardType();
114}
115
116static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jint ptr, jint deviceId,
117        jcharArray charsArray) {
118    KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
119
120    jchar* chars = env->GetCharArrayElements(charsArray, NULL);
121    if (!chars) {
122        return NULL;
123    }
124    jsize numChars = env->GetArrayLength(charsArray);
125
126    Vector<KeyEvent> events;
127    jobjectArray result = NULL;
128    if (map->getEvents(deviceId, chars, size_t(numChars), events)) {
129        result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
130        if (result) {
131            for (size_t i = 0; i < events.size(); i++) {
132                jobject keyEventObj = android_view_KeyEvent_fromNative(env, &events.itemAt(i));
133                if (!keyEventObj) break; // threw OOM exception
134                env->SetObjectArrayElement(result, jsize(i), keyEventObj);
135                env->DeleteLocalRef(keyEventObj);
136            }
137        }
138    }
139
140    env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT);
141    return result;
142}
143
144
145/*
146 * JNI registration.
147 */
148
149static JNINativeMethod g_methods[] = {
150    /* name, signature, funcPtr */
151    { "nativeLoad", "(Ljava/lang/String;)I",
152            (void*)nativeLoad },
153    { "nativeDispose", "(I)V",
154            (void*)nativeDispose },
155    { "nativeGetCharacter", "(III)C",
156            (void*)nativeGetCharacter },
157    { "nativeGetFallbackAction", "(IIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
158            (void*)nativeGetFallbackAction },
159    { "nativeGetNumber", "(II)C",
160            (void*)nativeGetNumber },
161    { "nativeGetMatch", "(II[CI)C",
162            (void*)nativeGetMatch },
163    { "nativeGetDisplayLabel", "(II)C",
164            (void*)nativeGetDisplayLabel },
165    { "nativeGetKeyboardType", "(I)I",
166            (void*)nativeGetKeyboardType },
167    { "nativeGetEvents", "(II[C)[Landroid/view/KeyEvent;",
168            (void*)nativeGetEvents },
169};
170
171#define FIND_CLASS(var, className) \
172        var = env->FindClass(className); \
173        LOG_FATAL_IF(! var, "Unable to find class " className);
174
175#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
176        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
177        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
178
179int register_android_text_KeyCharacterMap(JNIEnv* env)
180{
181    FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
182    gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz));
183
184    jclass clazz;
185    FIND_CLASS(clazz, "android/view/KeyCharacterMap$FallbackAction");
186
187    GET_FIELD_ID(gFallbackActionClassInfo.keyCode, clazz,
188            "keyCode", "I");
189
190    GET_FIELD_ID(gFallbackActionClassInfo.metaState, clazz,
191            "metaState", "I");
192
193    return AndroidRuntime::registerNativeMethods(env,
194            "android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
195}
196
197}; // namespace android
198