aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xAndroid.mk17
-rwxr-xr-xAndroidManifest.xml24
-rw-r--r--dictionaries/sample.xml16
-rw-r--r--dictionary/Android.mk28
-rw-r--r--dictionary/jni/com_android_inputmethod_latin_BinaryDictionary.cpp207
-rw-r--r--dictionary/src/basechars.h172
-rw-r--r--dictionary/src/dictionary.cpp277
-rw-r--r--dictionary/src/dictionary.h69
-rw-r--r--res/drawable-land/keyboard_suggest_strip_divider.pngbin0 -> 166 bytes
-rw-r--r--res/drawable/candidate_feedback_background.9.pngbin0 -> 1182 bytes
-rwxr-xr-xres/drawable/dialog_bubble_step02.9.pngbin0 -> 1036 bytes
-rwxr-xr-xres/drawable/dialog_bubble_step03.9.pngbin0 -> 1692 bytes
-rwxr-xr-xres/drawable/dialog_bubble_step04.9.pngbin0 -> 1957 bytes
-rw-r--r--res/drawable/highlight_pressed.pngbin0 -> 593 bytes
-rw-r--r--res/drawable/ic_dialog_keyboard.pngbin0 -> 528 bytes
-rw-r--r--res/drawable/ic_suggest_scroll_background.xml25
-rw-r--r--res/drawable/ic_suggest_strip_scroll_left_arrow.pngbin0 -> 445 bytes
-rw-r--r--res/drawable/ic_suggest_strip_scroll_right_arrow.pngbin0 -> 440 bytes
-rw-r--r--res/drawable/keyboard_suggest_strip.9.pngbin0 -> 220 bytes
-rw-r--r--res/drawable/keyboard_suggest_strip_divider.pngbin0 -> 166 bytes
-rw-r--r--res/drawable/sym_keyboard_delete.pngbin0 -> 1366 bytes
-rwxr-xr-xres/drawable/sym_keyboard_done.pngbin0 -> 771 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_delete.pngbin0 -> 524 bytes
-rwxr-xr-xres/drawable/sym_keyboard_feedback_done.pngbin0 -> 498 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_numalt.pngbin0 -> 735 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_numpound.pngbin0 -> 478 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_numstar.pngbin0 -> 464 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_return.pngbin0 -> 381 bytes
-rwxr-xr-xres/drawable/sym_keyboard_feedback_search.pngbin0 -> 501 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_shift.pngbin0 -> 437 bytes
-rwxr-xr-xres/drawable/sym_keyboard_feedback_shift_locked.pngbin0 -> 333 bytes
-rw-r--r--res/drawable/sym_keyboard_feedback_space.pngbin0 -> 223 bytes
-rw-r--r--res/drawable/sym_keyboard_num0.pngbin0 -> 1160 bytes
-rw-r--r--res/drawable/sym_keyboard_num1.pngbin0 -> 506 bytes
-rw-r--r--res/drawable/sym_keyboard_num2.pngbin0 -> 1778 bytes
-rw-r--r--res/drawable/sym_keyboard_num3.pngbin0 -> 1676 bytes
-rw-r--r--res/drawable/sym_keyboard_num4.pngbin0 -> 1540 bytes
-rw-r--r--res/drawable/sym_keyboard_num5.pngbin0 -> 1417 bytes
-rw-r--r--res/drawable/sym_keyboard_num6.pngbin0 -> 1952 bytes
-rw-r--r--res/drawable/sym_keyboard_num7.pngbin0 -> 1997 bytes
-rw-r--r--res/drawable/sym_keyboard_num8.pngbin0 -> 1605 bytes
-rw-r--r--res/drawable/sym_keyboard_num9.pngbin0 -> 2173 bytes
-rw-r--r--res/drawable/sym_keyboard_numalt.pngbin0 -> 1673 bytes
-rw-r--r--res/drawable/sym_keyboard_numpound.pngbin0 -> 963 bytes
-rw-r--r--res/drawable/sym_keyboard_numstar.pngbin0 -> 954 bytes
-rw-r--r--res/drawable/sym_keyboard_return.pngbin0 -> 866 bytes
-rwxr-xr-xres/drawable/sym_keyboard_search.pngbin0 -> 1029 bytes
-rw-r--r--res/drawable/sym_keyboard_shift.pngbin0 -> 1017 bytes
-rwxr-xr-xres/drawable/sym_keyboard_shift_locked.pngbin0 -> 799 bytes
-rw-r--r--res/drawable/sym_keyboard_space.pngbin0 -> 424 bytes
-rw-r--r--res/layout/bubble_text.xml27
-rwxr-xr-xres/layout/candidate_preview.xml29
-rwxr-xr-xres/layout/candidates.xml79
-rwxr-xr-xres/layout/input.xml27
-rwxr-xr-xres/raw/main.dictbin0 -> 42 bytes
-rwxr-xr-xres/raw/type3.oggbin0 -> 4126 bytes
-rw-r--r--res/values-cs/strings.xml101
-rw-r--r--res/values-de/strings.xml101
-rw-r--r--res/values-en/bools.xml22
-rw-r--r--res/values-es/strings.xml101
-rw-r--r--res/values-fr/donottranslate.xml25
-rw-r--r--res/values-fr/strings.xml101
-rw-r--r--res/values-it/donottranslate.xml23
-rw-r--r--res/values-it/strings.xml101
-rw-r--r--res/values-ja/strings.xml101
-rw-r--r--res/values-ko/strings.xml101
-rw-r--r--res/values-land/dimens.xml23
-rw-r--r--res/values-nb/strings.xml92
-rw-r--r--res/values-nl/strings.xml101
-rw-r--r--res/values-pl/strings.xml101
-rw-r--r--res/values-ru/strings.xml101
-rw-r--r--res/values-zh-rCN/strings.xml101
-rw-r--r--res/values-zh-rTW/strings.xml101
-rw-r--r--res/values/colors.xml24
-rw-r--r--res/values/dimens.xml24
-rw-r--r--res/values/donottranslate.xml25
-rw-r--r--res/values/durations.xml25
-rw-r--r--res/values/strings.xml142
-rwxr-xr-xres/xml-de/kbd_qwerty.xml167
-rw-r--r--res/xml-fr/kbd_qwerty.xml168
-rw-r--r--res/xml/azerty.xml114
-rw-r--r--res/xml/kbd_alpha.xml106
-rwxr-xr-xres/xml/kbd_phone.xml67
-rwxr-xr-xres/xml/kbd_phone_symbols.xml68
-rw-r--r--res/xml/kbd_popup_template.xml27
-rwxr-xr-xres/xml/kbd_qwerty.xml166
-rwxr-xr-xres/xml/kbd_symbols.xml138
-rwxr-xr-xres/xml/kbd_symbols_shift.xml93
-rw-r--r--res/xml/method.xml26
-rw-r--r--res/xml/popup_domains.xml34
-rw-r--r--res/xml/popup_punctuation.xml37
-rw-r--r--res/xml/popup_smileys.xml49
-rw-r--r--res/xml/prefs.xml81
-rw-r--r--src/com/android/inputmethod/latin/BinaryDictionary.java127
-rwxr-xr-xsrc/com/android/inputmethod/latin/CandidateView.java478
-rw-r--r--src/com/android/inputmethod/latin/CandidateViewContainer.java83
-rw-r--r--src/com/android/inputmethod/latin/Dictionary.java89
-rw-r--r--src/com/android/inputmethod/latin/KeyboardSwitcher.java241
-rw-r--r--src/com/android/inputmethod/latin/LatinIME.java1091
-rw-r--r--src/com/android/inputmethod/latin/LatinIMESettings.java75
-rw-r--r--src/com/android/inputmethod/latin/LatinKeyboard.java228
-rw-r--r--src/com/android/inputmethod/latin/LatinKeyboardView.java180
-rwxr-xr-xsrc/com/android/inputmethod/latin/Suggest.java278
-rw-r--r--src/com/android/inputmethod/latin/TextEntryState.java232
-rw-r--r--src/com/android/inputmethod/latin/Tutorial.java226
-rw-r--r--src/com/android/inputmethod/latin/UserDictionary.java473
-rw-r--r--src/com/android/inputmethod/latin/WordComposer.java141
107 files changed, 7917 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100755
index 000000000..e3215e822
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LatinIME
+
+LOCAL_CERTIFICATE := shared
+
+LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
+
+LOCAL_AAPT_FLAGS := -0 .dict
+
+include $(BUILD_PACKAGE)
+include $(LOCAL_PATH)/dictionary/Android.mk
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100755
index 000000000..89cdad2ca
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.inputmethod.latin"
+ android:sharedUserId="android.uid.shared">
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
+ <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
+
+ <application android:label="@string/english_ime_name">
+ <service android:name="LatinIME"
+ android:label="@string/english_ime_name"
+ android:permission="android.permission.BIND_INPUT_METHOD">
+ <intent-filter>
+ <action android:name="android.view.InputMethod" />
+ </intent-filter>
+ <meta-data android:name="android.view.im" android:resource="@xml/method" />
+ </service>
+
+ <activity android:name="LatinIMESettings" android:label="@string/english_ime_settings">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/dictionaries/sample.xml b/dictionaries/sample.xml
new file mode 100644
index 000000000..85233b63a
--- /dev/null
+++ b/dictionaries/sample.xml
@@ -0,0 +1,16 @@
+<!-- This is a sample wordlist that can be converted to a binary dictionary
+ for use by the Latin IME.
+ The format of the word list is a flat list of word entries.
+ Each entry has a frequency between 255 and 0.
+ Highest frequency words get more weight in the prediction algorithm.
+ You can capitalize words that must always be capitalized, such as "January".
+ You can have a capitalized and a non-capitalized word as separate entries,
+ such as "robin" and "Robin".
+-->
+<wordlist>
+ <w f="255">this</w>
+ <w f="255">is</w>
+ <w f="128">sample</w>
+ <w f="1">wordlist</w>
+</wordlist>
+
diff --git a/dictionary/Android.mk b/dictionary/Android.mk
new file mode 100644
index 000000000..9ba9f75ec
--- /dev/null
+++ b/dictionary/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+
+LOCAL_SRC_FILES := \
+ jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
+ src/dictionary.cpp
+
+LOCAL_C_INCLUDES += \
+ external/icu4c/common \
+ $(JNI_H_INCLUDE)
+
+LOCAL_LDLIBS := -lm
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libcutils \
+ libutils \
+ libicuuc
+
+LOCAL_MODULE := libjni_latinime
+
+LOCAL_MODULE_TAGS := user
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/dictionary/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/dictionary/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
new file mode 100644
index 000000000..65c640b40
--- /dev/null
+++ b/dictionary/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -0,0 +1,207 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#define LOG_TAG "BinaryDictionary"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <nativehelper/jni.h>
+#include "utils/AssetManager.h"
+#include "utils/Asset.h"
+
+#include "dictionary.h"
+
+// ----------------------------------------------------------------------------
+
+using namespace latinime;
+
+using namespace android;
+
+static jfieldID sDescriptorField;
+static jfieldID sAssetManagerNativeField;
+static jmethodID sAddWordMethod;
+
+//
+// helper function to throw an exception
+//
+static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data)
+{
+ if (jclass cls = env->FindClass(ex)) {
+ char msg[1000];
+ sprintf(msg, fmt, data);
+ env->ThrowNew(cls, msg);
+ env->DeleteLocalRef(cls);
+ }
+}
+
+static jint latinime_BinaryDictionary_open
+ (JNIEnv *env, jobject object, jobject assetManager, jstring resourceString,
+ jint typedLetterMultiplier, jint fullWordMultiplier)
+{
+ // Get the native file descriptor from the FileDescriptor object
+ AssetManager *am = (AssetManager*) env->GetIntField(assetManager, sAssetManagerNativeField);
+ if (!am) {
+ LOGE("DICT: Couldn't get AssetManager native peer\n");
+ return 0;
+ }
+ const char *resourcePath = env->GetStringUTFChars(resourceString, NULL);
+
+ Asset *dictAsset = am->openNonAsset(resourcePath, Asset::ACCESS_BUFFER);
+ if (dictAsset == NULL) {
+ LOGE("DICT: Couldn't get asset %s\n", resourcePath);
+ env->ReleaseStringUTFChars(resourceString, resourcePath);
+ return 0;
+ }
+
+ void *dict = (void*) dictAsset->getBuffer(false);
+ if (dict == NULL) {
+ LOGE("DICT: Dictionary buffer is null\n");
+ env->ReleaseStringUTFChars(resourceString, resourcePath);
+ return 0;
+ }
+ Dictionary *dictionary = new Dictionary(dict, typedLetterMultiplier, fullWordMultiplier);
+ dictionary->setAsset(dictAsset);
+
+ env->ReleaseStringUTFChars(resourceString, resourcePath);
+ return (jint) dictionary;
+}
+
+static int latinime_BinaryDictionary_getSuggestions(
+ JNIEnv *env, jobject object, jint dict, jintArray inputArray, jint arraySize,
+ jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxWords,
+ jint maxAlternatives)
+{
+ Dictionary *dictionary = (Dictionary*) dict;
+ if (dictionary == NULL)
+ return 0;
+
+ int *frequencies = env->GetIntArrayElements(frequencyArray, NULL);
+ int *inputCodes = env->GetIntArrayElements(inputArray, NULL);
+ jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
+
+ int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, frequencies,
+ maxWordLength, maxWords, maxAlternatives);
+
+ env->ReleaseIntArrayElements(frequencyArray, frequencies, JNI_COMMIT);
+ env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
+ env->ReleaseCharArrayElements(outputArray, outputChars, JNI_COMMIT);
+
+ return count;
+}
+
+static jboolean latinime_BinaryDictionary_isValidWord
+ (JNIEnv *env, jobject object, jint dict, jcharArray wordArray, jint wordLength)
+{
+ Dictionary *dictionary = (Dictionary*) dict;
+ if (dictionary == NULL) return (jboolean) false;
+
+ jchar *word = env->GetCharArrayElements(wordArray, NULL);
+ jboolean result = dictionary->isValidWord((unsigned short*) word, wordLength);
+ env->ReleaseCharArrayElements(wordArray, word, JNI_ABORT);
+
+ return result;
+}
+
+static void latinime_BinaryDictionary_close
+ (JNIEnv *env, jobject object, jint dict)
+{
+ Dictionary *dictionary = (Dictionary*) dict;
+ ((Asset*) dictionary->getAsset())->close();
+ delete (Dictionary*) dict;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+ {"openNative", "(Landroid/content/res/AssetManager;Ljava/lang/String;II)I",
+ (void*)latinime_BinaryDictionary_open},
+ {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
+ {"getSuggestionsNative", "(I[II[C[IIII)I", (void*)latinime_BinaryDictionary_getSuggestions},
+ {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}
+};
+
+static int registerNativeMethods(JNIEnv* env, const char* className,
+ JNINativeMethod* gMethods, int numMethods)
+{
+ jclass clazz;
+
+ clazz = env->FindClass(className);
+ if (clazz == NULL) {
+ fprintf(stderr,
+ "Native registration unable to find class '%s'\n", className);
+ return JNI_FALSE;
+ }
+ if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
+ fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+static int registerNatives(JNIEnv *env)
+{
+ const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
+ jclass clazz;
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ if (clazz == NULL) {
+ LOGE("Can't find %s", "java/io/FileDescriptor");
+ return -1;
+ }
+ sDescriptorField = env->GetFieldID(clazz, "descriptor", "I");
+
+ clazz = env->FindClass("android/content/res/AssetManager");
+ if (clazz == NULL) {
+ LOGE("Can't find %s", "java/io/FileDescriptor");
+ return -1;
+ }
+ sAssetManagerNativeField = env->GetFieldID(clazz, "mObject", "I");
+
+ return registerNativeMethods(env,
+ kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
+}
+
+/*
+ * Returns the JNI version on success, -1 on failure.
+ */
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ fprintf(stderr, "ERROR: GetEnv failed\n");
+ goto bail;
+ }
+ assert(env != NULL);
+
+ if (!registerNatives(env)) {
+ fprintf(stderr, "ERROR: BinaryDictionary native registration failed\n");
+ goto bail;
+ }
+
+ /* success -- return valid version number */
+ result = JNI_VERSION_1_4;
+
+bail:
+ return result;
+}
diff --git a/dictionary/src/basechars.h b/dictionary/src/basechars.h
new file mode 100644
index 000000000..5a4406606
--- /dev/null
+++ b/dictionary/src/basechars.h
@@ -0,0 +1,172 @@
+/**
+ * Table mapping most combined Latin, Greek, and Cyrillic characters
+ * to their base characters. If c is in range, BASE_CHARS[c] == c
+ * if c is not a combined character, or the base character if it
+ * is combined.
+ */
+static unsigned short BASE_CHARS[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0020,
+ 0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7,
+ 0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf,
+ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00c6, 0x0043,
+ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049,
+ 0x00d0, 0x004e, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x00d7,
+ 0x004f, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00de, 0x0073, // Manually changed d8 to 4f
+ // Manually changed df to 73
+ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063,
+ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069,
+ 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7,
+ 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, // Manually changed f8 to 6f
+ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063,
+ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064,
+ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065,
+ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067,
+ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127,
+ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069,
+ 0x0049, 0x0131, 0x0049, 0x0069, 0x004a, 0x006a, 0x004b, 0x006b,
+ 0x0138, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c,
+ 0x006c, 0x0141, 0x0142, 0x004e, 0x006e, 0x004e, 0x006e, 0x004e,
+ 0x006e, 0x02bc, 0x014a, 0x014b, 0x004f, 0x006f, 0x004f, 0x006f,
+ 0x004f, 0x006f, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072,
+ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073,
+ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167,
+ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075,
+ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079,
+ 0x0059, 0x005a, 0x007a, 0x005a, 0x007a, 0x005a, 0x007a, 0x0073,
+ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187,
+ 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f,
+ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197,
+ 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f,
+ 0x004f, 0x006f, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7,
+ 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x0055,
+ 0x0075, 0x01b1, 0x01b2, 0x01b3, 0x01b4, 0x01b5, 0x01b6, 0x01b7,
+ 0x01b8, 0x01b9, 0x01ba, 0x01bb, 0x01bc, 0x01bd, 0x01be, 0x01bf,
+ 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0044, 0x0044, 0x0064, 0x004c,
+ 0x004c, 0x006c, 0x004e, 0x004e, 0x006e, 0x0041, 0x0061, 0x0049,
+ 0x0069, 0x004f, 0x006f, 0x0055, 0x0075, 0x00dc, 0x00fc, 0x00dc,
+ 0x00fc, 0x00dc, 0x00fc, 0x00dc, 0x00fc, 0x01dd, 0x00c4, 0x00e4,
+ 0x0226, 0x0227, 0x00c6, 0x00e6, 0x01e4, 0x01e5, 0x0047, 0x0067,
+ 0x004b, 0x006b, 0x004f, 0x006f, 0x01ea, 0x01eb, 0x01b7, 0x0292,
+ 0x006a, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01f6, 0x01f7,
+ 0x004e, 0x006e, 0x00c5, 0x00e5, 0x00c6, 0x00e6, 0x00d8, 0x00f8,
+ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065,
+ 0x0049, 0x0069, 0x0049, 0x0069, 0x004f, 0x006f, 0x004f, 0x006f,
+ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075,
+ 0x0053, 0x0073, 0x0054, 0x0074, 0x021c, 0x021d, 0x0048, 0x0068,
+ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061,
+ 0x0045, 0x0065, 0x00d6, 0x00f6, 0x00d5, 0x00f5, 0x004f, 0x006f,
+ 0x022e, 0x022f, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237,
+ 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f,
+ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,
+ 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f,
+ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
+ 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f,
+ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
+ 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f,
+ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
+ 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f,
+ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
+ 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f,
+ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+ 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
+ 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
+ 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
+ 0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077,
+ 0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
+ 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
+ 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
+ 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df,
+ 0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7,
+ 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
+ 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
+ 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff,
+ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
+ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
+ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
+ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
+ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347,
+ 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
+ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
+ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
+ 0x0370, 0x0371, 0x0372, 0x0373, 0x02b9, 0x0375, 0x0376, 0x0377,
+ 0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00a8, 0x0391, 0x00b7,
+ 0x0395, 0x0397, 0x0399, 0x038b, 0x039f, 0x038d, 0x03a5, 0x03a9,
+ 0x03ca, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+ 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x0399, 0x03a5, 0x03b1, 0x03b5, 0x03b7, 0x03b9,
+ 0x03cb, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03cf,
+ 0x03b2, 0x03b8, 0x03a5, 0x03d2, 0x03d2, 0x03c6, 0x03c0, 0x03d7,
+ 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03dc, 0x03dd, 0x03de, 0x03df,
+ 0x03e0, 0x03e1, 0x03e2, 0x03e3, 0x03e4, 0x03e5, 0x03e6, 0x03e7,
+ 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03ed, 0x03ee, 0x03ef,
+ 0x03ba, 0x03c1, 0x03c2, 0x03f3, 0x0398, 0x03b5, 0x03f6, 0x03f7,
+ 0x03f8, 0x03a3, 0x03fa, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff,
+ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406,
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x041a, 0x0418, 0x0423, 0x040f,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0418, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
+ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f,
+ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467,
+ 0x0468, 0x0469, 0x046a, 0x046b, 0x046c, 0x046d, 0x046e, 0x046f,
+ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475,
+ 0x0478, 0x0479, 0x047a, 0x047b, 0x047c, 0x047d, 0x047e, 0x047f,
+ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f,
+ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497,
+ 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x049f,
+ 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7,
+ 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af,
+ 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7,
+ 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x04be, 0x04bf,
+ 0x04c0, 0x0416, 0x0436, 0x04c3, 0x04c4, 0x04c5, 0x04c6, 0x04c7,
+ 0x04c8, 0x04c9, 0x04ca, 0x04cb, 0x04cc, 0x04cd, 0x04ce, 0x04cf,
+ 0x0410, 0x0430, 0x0410, 0x0430, 0x04d4, 0x04d5, 0x0415, 0x0435,
+ 0x04d8, 0x04d9, 0x04d8, 0x04d9, 0x0416, 0x0436, 0x0417, 0x0437,
+ 0x04e0, 0x04e1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041e, 0x043e,
+ 0x04e8, 0x04e9, 0x04e8, 0x04e9, 0x042d, 0x044d, 0x0423, 0x0443,
+ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04f6, 0x04f7,
+ 0x042b, 0x044b, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff,
+};
+
+// generated with:
+// cat UnicodeData.txt | perl -e 'while (<>) { @foo = split(/;/); $foo[5] =~ s/<.*> //; $base[hex($foo[0])] = hex($foo[5]);} for ($i = 0; $i < 0x500; $i += 8) { for ($j = $i; $j < $i + 8; $j++) { printf("0x%04x, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }'
diff --git a/dictionary/src/dictionary.cpp b/dictionary/src/dictionary.cpp
new file mode 100644
index 000000000..6aecb6374
--- /dev/null
+++ b/dictionary/src/dictionary.cpp
@@ -0,0 +1,277 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <cutils/log.h>
+
+#include <unicode/uchar.h>
+
+//#define USE_ASSET_MANAGER
+
+#ifdef USE_ASSET_MANAGER
+#include <utils/AssetManager.h>
+#include <utils/Asset.h>
+#endif
+
+#include "dictionary.h"
+#include "basechars.h"
+
+#define DEBUG_DICT 0
+
+namespace latinime {
+
+Dictionary::Dictionary(void *dict, int typedLetterMultiplier, int fullWordMultiplier)
+{
+ mDict = (unsigned char*) dict;
+ mTypedLetterMultiplier = typedLetterMultiplier;
+ mFullWordMultiplier = fullWordMultiplier;
+}
+
+Dictionary::~Dictionary()
+{
+}
+
+int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies,
+ int maxWordLength, int maxWords, int maxAlternatives)
+{
+ memset(frequencies, 0, maxWords * sizeof(*frequencies));
+ memset(outWords, 0, maxWords * maxWordLength * sizeof(*outWords));
+
+ mFrequencies = frequencies;
+ mOutputChars = outWords;
+ mInputCodes = codes;
+ mInputLength = codesSize;
+ mMaxAlternatives = maxAlternatives;
+ mMaxWordLength = maxWordLength;
+ mMaxWords = maxWords;
+ mWords = 0;
+
+ getWordsRec(0, 0, mInputLength * 3, false, 1, 0);
+
+ if (DEBUG_DICT) LOGI("Returning %d words", mWords);
+ return mWords;
+}
+
+unsigned short
+Dictionary::getChar(int *pos)
+{
+ unsigned short ch = (unsigned short) (mDict[(*pos)++] & 0xFF);
+ // If the code is 255, then actual 16 bit code follows (in big endian)
+ if (ch == 0xFF) {
+ ch = ((mDict[*pos] & 0xFF) << 8) | (mDict[*pos + 1] & 0xFF);
+ (*pos) += 2;
+ }
+ return ch;
+}
+
+int
+Dictionary::getAddress(int *pos)
+{
+ int address = 0;
+ address += (mDict[*pos] & 0x7F) << 16;
+ address += (mDict[*pos + 1] & 0xFF) << 8;
+ address += (mDict[*pos + 2] & 0xFF);
+ *pos += 3;
+ return address;
+}
+
+int
+Dictionary::wideStrLen(unsigned short *str)
+{
+ if (!str) return 0;
+ unsigned short *end = str;
+ while (*end)
+ end++;
+ return end - str;
+}
+
+bool
+Dictionary::addWord(unsigned short *word, int length, int frequency)
+{
+ word[length] = 0;
+ if (DEBUG_DICT) LOGI("Found word = %s, freq = %d : \n", word, frequency);
+
+ // Find the right insertion point
+ int insertAt = 0;
+ while (insertAt < mMaxWords) {
+ if (frequency > mFrequencies[insertAt]
+ || (mFrequencies[insertAt] == frequency
+ && length < wideStrLen(mOutputChars + insertAt * mMaxWordLength))) {
+ break;
+ }
+ insertAt++;
+ }
+ if (insertAt < mMaxWords) {
+ memmove((char*) mFrequencies + (insertAt + 1) * sizeof(mFrequencies[0]),
+ (char*) mFrequencies + insertAt * sizeof(mFrequencies[0]),
+ (mMaxWords - insertAt - 1) * sizeof(mFrequencies[0]));
+ mFrequencies[insertAt] = frequency;
+ memmove((char*) mOutputChars + (insertAt + 1) * mMaxWordLength * sizeof(short),
+ (char*) mOutputChars + (insertAt ) * mMaxWordLength * sizeof(short),
+ (mMaxWords - insertAt - 1) * sizeof(short) * mMaxWordLength);
+ unsigned short *dest = mOutputChars + (insertAt ) * mMaxWordLength;
+ while (length--) {
+ *dest++ = *word++;
+ }
+ *dest = 0; // NULL terminate
+ // Update the word count
+ if (insertAt + 1 > mWords) mWords = insertAt + 1;
+ if (DEBUG_DICT) LOGI("Added word at %d\n", insertAt);
+ return true;
+ }
+ return false;
+}
+
+unsigned short
+Dictionary::toLowerCase(unsigned short c, const int depth) {
+ if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
+ c = BASE_CHARS[c];
+ }
+ if (depth == 0) {
+ if (c >='A' && c <= 'Z') {
+ c |= 32;
+ } else if (c > 127) {
+ c = u_tolower(c);
+ }
+ }
+ return c;
+}
+
+bool
+Dictionary::sameAsTyped(unsigned short *word, int length)
+{
+ if (length != mInputLength) {
+ return false;
+ }
+ int *inputCodes = mInputCodes;
+ while (length--) {
+ if ((unsigned int) *inputCodes != (unsigned int) *word) {
+ return false;
+ }
+ inputCodes += mMaxAlternatives;
+ word++;
+ }
+ return true;
+}
+
+static char QUOTE = '\'';
+
+void
+Dictionary::getWordsRec(int pos, int depth, int maxDepth, bool completion, int snr, int inputIndex)
+{
+ // Optimization: Prune out words that are too long compared to how much was typed.
+ if (depth > maxDepth) {
+ return;
+ }
+ int count = getCount(&pos);
+ int *currentChars = NULL;
+ if (mInputLength <= inputIndex) {
+ completion = true;
+ } else {
+ currentChars = mInputCodes + (inputIndex * mMaxAlternatives);
+ }
+
+ for (int i = 0; i < count; i++) {
+ unsigned short c = getChar(&pos);
+ unsigned short lowerC = toLowerCase(c, depth);
+ bool terminal = getTerminal(&pos);
+ int childrenAddress = getAddress(&pos);
+ int freq = getFreq(&pos);
+ // If we are only doing completions, no need to look at the typed characters.
+ if (completion) {
+ mWord[depth] = c;
+ if (terminal) {
+ addWord(mWord, depth + 1, freq * snr);
+ }
+ if (childrenAddress != 0) {
+ getWordsRec(childrenAddress, depth + 1, maxDepth,
+ completion, snr, inputIndex);
+ }
+ } else if (c == QUOTE && currentChars[0] != QUOTE) {
+ // Skip the ' and continue deeper
+ mWord[depth] = QUOTE;
+ if (childrenAddress != 0) {
+ getWordsRec(childrenAddress, depth + 1, maxDepth, false, snr, inputIndex);
+ }
+ } else {
+ int j = 0;
+ while (currentChars[j] > 0) {
+ int addedWeight = j == 0 ? mTypedLetterMultiplier : 1;
+ if (currentChars[j] == lowerC || currentChars[j] == c) {
+ mWord[depth] = c;
+ if (mInputLength == inputIndex + 1) {
+ if (terminal) {
+ if (//INCLUDE_TYPED_WORD_IF_VALID ||
+ !sameAsTyped(mWord, depth + 1)) {
+ addWord(mWord, depth + 1,
+ (freq * snr * addedWeight * mFullWordMultiplier));
+ }
+ }
+ if (childrenAddress != 0) {
+ getWordsRec(childrenAddress, depth + 1,
+ maxDepth, true, snr * addedWeight, inputIndex + 1);
+ }
+ } else if (childrenAddress != 0) {
+ getWordsRec(childrenAddress, depth + 1, maxDepth,
+ false, snr * addedWeight, inputIndex + 1);
+ }
+ }
+ j++;
+ }
+ }
+ }
+}
+
+bool
+Dictionary::isValidWord(unsigned short *word, int length)
+{
+ return isValidWordRec(0, word, 0, length);
+}
+
+bool
+Dictionary::isValidWordRec(int pos, unsigned short *word, int offset, int length) {
+ int count = getCount(&pos);
+ unsigned short currentChar = (unsigned short) word[offset];
+ for (int j = 0; j < count; j++) {
+ unsigned short c = getChar(&pos);
+ int terminal = getTerminal(&pos);
+ int childPos = getAddress(&pos);
+ if (c == currentChar) {
+ if (offset == length - 1) {
+ if (terminal) {
+ return true;
+ }
+ } else {
+ if (childPos != 0) {
+ if (isValidWordRec(childPos, word, offset + 1, length)) {
+ return true;
+ }
+ }
+ }
+ }
+ getFreq(&pos);
+ // There could be two instances of each alphabet - upper and lower case. So continue
+ // looking ...
+ }
+ return false;
+}
+
+
+} // namespace latinime
diff --git a/dictionary/src/dictionary.h b/dictionary/src/dictionary.h
new file mode 100644
index 000000000..8574e0736
--- /dev/null
+++ b/dictionary/src/dictionary.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_H
+#define LATINIME_DICTIONARY_H
+
+namespace latinime {
+
+class Dictionary {
+public:
+ Dictionary(void *dict, int typedLetterMultipler, int fullWordMultiplier);
+ int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies,
+ int maxWordLength, int maxWords, int maxAlternatives);
+ bool isValidWord(unsigned short *word, int length);
+ void setAsset(void *asset) { mAsset = asset; }
+ void *getAsset() { return mAsset; }
+ ~Dictionary();
+
+private:
+
+ int getAddress(int *pos);
+ bool getTerminal(int *pos) { return (mDict[*pos] & 0x80) > 0; }
+ int getFreq(int *pos) { return mDict[(*pos)++] & 0xFF; }
+ int getCount(int *pos) { return mDict[(*pos)++] & 0xFF; }
+ unsigned short getChar(int *pos);
+ int wideStrLen(unsigned short *str);
+
+ bool sameAsTyped(unsigned short *word, int length);
+ bool addWord(unsigned short *word, int length, int frequency);
+ unsigned short toLowerCase(unsigned short c, int depth);
+ void getWordsRec(int pos, int depth, int maxDepth, bool completion, int frequency,
+ int inputIndex);
+ bool isValidWordRec(int pos, unsigned short *word, int offset, int length);
+
+ unsigned char *mDict;
+ void *mAsset;
+
+ int *mFrequencies;
+ int mMaxWords;
+ int mMaxWordLength;
+ int mWords;
+ unsigned short *mOutputChars;
+ int *mInputCodes;
+ int mInputLength;
+ int mMaxAlternatives;
+ unsigned short mWord[128];
+
+ int mFullWordMultiplier;
+ int mTypedLetterMultiplier;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace latinime
+
+#endif // LATINIME_DICTIONARY_H
diff --git a/res/drawable-land/keyboard_suggest_strip_divider.png b/res/drawable-land/keyboard_suggest_strip_divider.png
new file mode 100644
index 000000000..e54c5b099
--- /dev/null
+++ b/res/drawable-land/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/res/drawable/candidate_feedback_background.9.png b/res/drawable/candidate_feedback_background.9.png
new file mode 100644
index 000000000..2a80f096d
--- /dev/null
+++ b/res/drawable/candidate_feedback_background.9.png
Binary files differ
diff --git a/res/drawable/dialog_bubble_step02.9.png b/res/drawable/dialog_bubble_step02.9.png
new file mode 100755
index 000000000..d77f85fe2
--- /dev/null
+++ b/res/drawable/dialog_bubble_step02.9.png
Binary files differ
diff --git a/res/drawable/dialog_bubble_step03.9.png b/res/drawable/dialog_bubble_step03.9.png
new file mode 100755
index 000000000..16b4d0226
--- /dev/null
+++ b/res/drawable/dialog_bubble_step03.9.png
Binary files differ
diff --git a/res/drawable/dialog_bubble_step04.9.png b/res/drawable/dialog_bubble_step04.9.png
new file mode 100755
index 000000000..a24012d7c
--- /dev/null
+++ b/res/drawable/dialog_bubble_step04.9.png
Binary files differ
diff --git a/res/drawable/highlight_pressed.png b/res/drawable/highlight_pressed.png
new file mode 100644
index 000000000..d27f1061a
--- /dev/null
+++ b/res/drawable/highlight_pressed.png
Binary files differ
diff --git a/res/drawable/ic_dialog_keyboard.png b/res/drawable/ic_dialog_keyboard.png
new file mode 100644
index 000000000..9a5aada8b
--- /dev/null
+++ b/res/drawable/ic_dialog_keyboard.png
Binary files differ
diff --git a/res/drawable/ic_suggest_scroll_background.xml b/res/drawable/ic_suggest_scroll_background.xml
new file mode 100644
index 000000000..9d246e40e
--- /dev/null
+++ b/res/drawable/ic_suggest_scroll_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="false"
+ android:drawable="@android:color/transparent" />
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/highlight_pressed" />
+
+</selector> \ No newline at end of file
diff --git a/res/drawable/ic_suggest_strip_scroll_left_arrow.png b/res/drawable/ic_suggest_strip_scroll_left_arrow.png
new file mode 100644
index 000000000..a9adef2ba
--- /dev/null
+++ b/res/drawable/ic_suggest_strip_scroll_left_arrow.png
Binary files differ
diff --git a/res/drawable/ic_suggest_strip_scroll_right_arrow.png b/res/drawable/ic_suggest_strip_scroll_right_arrow.png
new file mode 100644
index 000000000..639a28711
--- /dev/null
+++ b/res/drawable/ic_suggest_strip_scroll_right_arrow.png
Binary files differ
diff --git a/res/drawable/keyboard_suggest_strip.9.png b/res/drawable/keyboard_suggest_strip.9.png
new file mode 100644
index 000000000..71bf5e8f4
--- /dev/null
+++ b/res/drawable/keyboard_suggest_strip.9.png
Binary files differ
diff --git a/res/drawable/keyboard_suggest_strip_divider.png b/res/drawable/keyboard_suggest_strip_divider.png
new file mode 100644
index 000000000..e54c5b099
--- /dev/null
+++ b/res/drawable/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_delete.png b/res/drawable/sym_keyboard_delete.png
new file mode 100644
index 000000000..f1f7c58cf
--- /dev/null
+++ b/res/drawable/sym_keyboard_delete.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_done.png b/res/drawable/sym_keyboard_done.png
new file mode 100755
index 000000000..c0d6d1394
--- /dev/null
+++ b/res/drawable/sym_keyboard_done.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_delete.png b/res/drawable/sym_keyboard_feedback_delete.png
new file mode 100644
index 000000000..3c9083972
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_done.png b/res/drawable/sym_keyboard_feedback_done.png
new file mode 100755
index 000000000..0d7ebd4e5
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_done.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_numalt.png b/res/drawable/sym_keyboard_feedback_numalt.png
new file mode 100644
index 000000000..aac737615
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_numalt.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_numpound.png b/res/drawable/sym_keyboard_feedback_numpound.png
new file mode 100644
index 000000000..6b6561e80
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_numpound.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_numstar.png b/res/drawable/sym_keyboard_feedback_numstar.png
new file mode 100644
index 000000000..05f7b4fc9
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_numstar.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_return.png b/res/drawable/sym_keyboard_feedback_return.png
new file mode 100644
index 000000000..03d9c9b2d
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_search.png b/res/drawable/sym_keyboard_feedback_search.png
new file mode 100755
index 000000000..f4af341eb
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_search.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_shift.png b/res/drawable/sym_keyboard_feedback_shift.png
new file mode 100644
index 000000000..97f4661f8
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_shift_locked.png b/res/drawable/sym_keyboard_feedback_shift_locked.png
new file mode 100755
index 000000000..7194b30b0
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_shift_locked.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_feedback_space.png b/res/drawable/sym_keyboard_feedback_space.png
new file mode 100644
index 000000000..739db6879
--- /dev/null
+++ b/res/drawable/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num0.png b/res/drawable/sym_keyboard_num0.png
new file mode 100644
index 000000000..e7007c871
--- /dev/null
+++ b/res/drawable/sym_keyboard_num0.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num1.png b/res/drawable/sym_keyboard_num1.png
new file mode 100644
index 000000000..aaac11b0c
--- /dev/null
+++ b/res/drawable/sym_keyboard_num1.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num2.png b/res/drawable/sym_keyboard_num2.png
new file mode 100644
index 000000000..4372eb8f0
--- /dev/null
+++ b/res/drawable/sym_keyboard_num2.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num3.png b/res/drawable/sym_keyboard_num3.png
new file mode 100644
index 000000000..6f54c850f
--- /dev/null
+++ b/res/drawable/sym_keyboard_num3.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num4.png b/res/drawable/sym_keyboard_num4.png
new file mode 100644
index 000000000..3e50bb957
--- /dev/null
+++ b/res/drawable/sym_keyboard_num4.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num5.png b/res/drawable/sym_keyboard_num5.png
new file mode 100644
index 000000000..c39ef4404
--- /dev/null
+++ b/res/drawable/sym_keyboard_num5.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num6.png b/res/drawable/sym_keyboard_num6.png
new file mode 100644
index 000000000..ea88ceb94
--- /dev/null
+++ b/res/drawable/sym_keyboard_num6.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num7.png b/res/drawable/sym_keyboard_num7.png
new file mode 100644
index 000000000..4d75583af
--- /dev/null
+++ b/res/drawable/sym_keyboard_num7.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num8.png b/res/drawable/sym_keyboard_num8.png
new file mode 100644
index 000000000..1a8ff94bf
--- /dev/null
+++ b/res/drawable/sym_keyboard_num8.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_num9.png b/res/drawable/sym_keyboard_num9.png
new file mode 100644
index 000000000..8b344c0a6
--- /dev/null
+++ b/res/drawable/sym_keyboard_num9.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_numalt.png b/res/drawable/sym_keyboard_numalt.png
new file mode 100644
index 000000000..32a2cf3ca
--- /dev/null
+++ b/res/drawable/sym_keyboard_numalt.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_numpound.png b/res/drawable/sym_keyboard_numpound.png
new file mode 100644
index 000000000..b2419d9ab
--- /dev/null
+++ b/res/drawable/sym_keyboard_numpound.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_numstar.png b/res/drawable/sym_keyboard_numstar.png
new file mode 100644
index 000000000..cb66f968f
--- /dev/null
+++ b/res/drawable/sym_keyboard_numstar.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_return.png b/res/drawable/sym_keyboard_return.png
new file mode 100644
index 000000000..17f257439
--- /dev/null
+++ b/res/drawable/sym_keyboard_return.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_search.png b/res/drawable/sym_keyboard_search.png
new file mode 100755
index 000000000..127755d6b
--- /dev/null
+++ b/res/drawable/sym_keyboard_search.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_shift.png b/res/drawable/sym_keyboard_shift.png
new file mode 100644
index 000000000..0566e5a88
--- /dev/null
+++ b/res/drawable/sym_keyboard_shift.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_shift_locked.png b/res/drawable/sym_keyboard_shift_locked.png
new file mode 100755
index 000000000..ccaf05d3b
--- /dev/null
+++ b/res/drawable/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/res/drawable/sym_keyboard_space.png b/res/drawable/sym_keyboard_space.png
new file mode 100644
index 000000000..4e6273b89
--- /dev/null
+++ b/res/drawable/sym_keyboard_space.png
Binary files differ
diff --git a/res/layout/bubble_text.xml b/res/layout/bubble_text.xml
new file mode 100644
index 000000000..af8abce47
--- /dev/null
+++ b/res/layout/bubble_text.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:minWidth="32dip"
+ />
diff --git a/res/layout/candidate_preview.xml b/res/layout/candidate_preview.xml
new file mode 100755
index 000000000..fe2002d46
--- /dev/null
+++ b/res/layout/candidate_preview.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:minWidth="32dip"
+ android:gravity="center"
+ android:background="@drawable/candidate_feedback_background"
+ />
diff --git a/res/layout/candidates.xml b/res/layout/candidates.xml
new file mode 100755
index 000000000..edd779a81
--- /dev/null
+++ b/res/layout/candidates.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<com.android.inputmethod.latin.CandidateViewContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_suggest_strip"
+ >
+
+ <LinearLayout
+ android:id="@+id/candidate_left_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal">
+ <ImageButton
+ android:id="@+id/candidate_left"
+ android:background="@drawable/ic_suggest_scroll_background"
+ android:src="@drawable/ic_suggest_strip_scroll_left_arrow"
+ android:layout_width="36dp"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ />
+
+ <ImageView
+ android:src="@drawable/keyboard_suggest_strip_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ />
+ </LinearLayout>
+
+ <com.android.inputmethod.latin.CandidateView
+ android:id="@+id/candidates"
+ android:layout_width="wrap_content"
+ android:layout_height="38dp"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:id="@+id/candidate_right_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ android:orientation="horizontal">
+ <ImageView
+ android:src="@drawable/keyboard_suggest_strip_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ />
+
+ <ImageButton
+ android:id="@+id/candidate_right"
+ android:background="@drawable/ic_suggest_scroll_background"
+ android:src="@drawable/ic_suggest_strip_scroll_right_arrow"
+ android:layout_width="36dp"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ />
+ </LinearLayout>
+
+</com.android.inputmethod.latin.CandidateViewContainer> \ No newline at end of file
diff --git a/res/layout/input.xml b/res/layout/input.xml
new file mode 100755
index 000000000..c4bcc91c3
--- /dev/null
+++ b/res/layout/input.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<com.android.inputmethod.latin.LatinKeyboardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/keyboardView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
diff --git a/res/raw/main.dict b/res/raw/main.dict
new file mode 100755
index 000000000..2b473be75
--- /dev/null
+++ b/res/raw/main.dict
Binary files differ
diff --git a/res/raw/type3.ogg b/res/raw/type3.ogg
new file mode 100755
index 000000000..20e670807
--- /dev/null
+++ b/res/raw/type3.ogg
Binary files differ
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644
index 000000000..408038506
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Při stisku klávesy vibrovat"</string>
+ <string name="sound_on_keypress">"Zvuk při stisku klávesy"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Automatická interpunkce"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : Uloženo"</string>
+ <string name="alternates_for_a">"áàâãäåæ"</string>
+ <string name="alternates_for_e">"éěèêë"</string>
+ <string name="alternates_for_i">"íìîï"</string>
+ <string name="alternates_for_o">"óòôõöœø"</string>
+ <string name="alternates_for_u">"ůúùûü"</string>
+ <string name="alternates_for_s">"š§ß"</string>
+ <string name="alternates_for_n">"ňñ"</string>
+ <string name="alternates_for_c">"čç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644
index 000000000..4c9920865
--- /dev/null
+++ b/res/values-de/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Vibrieren auf Tastendruck"</string>
+ <string name="sound_on_keypress">"Sound bei Tastendruck"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Autom. Zeichensetzung"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : Gespeichert"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-en/bools.xml b/res/values-en/bools.xml
new file mode 100644
index 000000000..897f4b3db
--- /dev/null
+++ b/res/values-en/bools.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<resources>
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
new file mode 100644
index 000000000..08815ee7c
--- /dev/null
+++ b/res/values-es/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Vibrar al pulsar tecla"</string>
+ <string name="sound_on_keypress">"Sonido al pulsar una tecla"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Puntuación automática"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : Guardada"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-fr/donottranslate.xml b/res/values-fr/donottranslate.xml
new file mode 100644
index 000000000..527f15e94
--- /dev/null
+++ b/res/values-fr/donottranslate.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Symbols that are commonly considered word separators in this language -->
+ <string name="word_separators">.\u0020,;:!?\'\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+ <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
+ <string name="sentence_separators">.,</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
new file mode 100644
index 000000000..767004d55
--- /dev/null
+++ b/res/values-fr/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Vibrer à chaque touche enfoncée"</string>
+ <string name="sound_on_keypress">"Son à chaque touche enfoncée"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Ponctuation automatique"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-it/donottranslate.xml b/res/values-it/donottranslate.xml
new file mode 100644
index 000000000..5cb72adf5
--- /dev/null
+++ b/res/values-it/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Symbols that are commonly considered word separators in this language -->
+ <string name="word_separators">.\u0020,;:!?\'\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
new file mode 100644
index 000000000..9fd770ac1
--- /dev/null
+++ b/res/values-it/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Vibra quando premi un tasto"</string>
+ <string name="sound_on_keypress">"Suona quando premi un tasto"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Punteggiatura automatica"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644
index 000000000..8a6c76f96
--- /dev/null
+++ b/res/values-ja/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"キーのバイブレーション"</string>
+ <string name="sound_on_keypress">"キーを押したときの音"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"句読点を自動入力"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 000000000..96d92952a
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"키를 누를 때 진동 발생"</string>
+ <string name="sound_on_keypress">"버튼을 누를 때 소리 발생"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"자동 구두점 입력"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : 저장됨"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
new file mode 100644
index 000000000..c5c828ef6
--- /dev/null
+++ b/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<resources>
+ <dimen name="key_height">47dip</dimen>
+</resources> \ No newline at end of file
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 000000000..a01031852
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Vibrer ved tastetrykk"</string>
+ <string name="sound_on_keypress">"Lyd ved tastetrykk"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <string name="auto_correction_summary">"Autokorriger forrige ord ved mellomrom eller linjeskift"</string>
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <string name="auto_cap">"Stor forbokstav"</string>
+ <string name="auto_cap_summary">"Start automatisk setninger med stor bokstav"</string>
+ <string name="auto_punctuate">"Automatisk punktum"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string>
+ <string name="alternates_for_a">"åæáàâãä"</string>
+ <string name="alternates_for_e">"éèêë"</string>
+ <string name="alternates_for_i">"íìîï"</string>
+ <string name="alternates_for_o">"ôóòöõœø"</string>
+ <string name="alternates_for_u">"üùúû"</string>
+ <string name="alternates_for_s">"ß§"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <string name="tip_add_to_dictionary">"Trykk lenge på ordet lengst til venstre for å legge det til i ordlisten"</string>
+ <string name="label_go_key">"Gå"</string>
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <string name="label_symbol_key">"?123"</string>
+ <string name="label_phone_key">"123"</string>
+ <string name="label_alpha_key">"ABC"</string>
+ <string name="label_alt_key">"ALT"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
new file mode 100644
index 000000000..b172defbd
--- /dev/null
+++ b/res/values-nl/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Trillen bij druk op een toets"</string>
+ <string name="sound_on_keypress">"Geluid bij druk op een toets"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Automatische interpunctie"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : Opgeslagen"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
new file mode 100644
index 000000000..eb1a12869
--- /dev/null
+++ b/res/values-pl/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Wibruj przy naciśnięciu klawisza"</string>
+ <string name="sound_on_keypress">"Dźwięk przy naciśnięciu klawisza"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Automatyczna interpunkcja"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
new file mode 100644
index 000000000..7ca302b74
--- /dev/null
+++ b/res/values-ru/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"Вибрация при нажатии клавиш"</string>
+ <string name="sound_on_keypress">"Звук при нажатии клавиш"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"Автоматическая пунктуация"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g> : сохранено"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000..3525a487b
--- /dev/null
+++ b/res/values-zh-rCN/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"按键时振动"</string>
+ <string name="sound_on_keypress">"按键时发出声音"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"自动加标点"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..f880fd7e9
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for english_ime_name (5849054103817924472) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (8408735206364332137) -->
+ <skip />
+ <string name="vibrate_on_keypress">"按下按鍵時震動"</string>
+ <string name="sound_on_keypress">"按下按鍵時播放音效"</string>
+ <!-- no translation found for hit_correction (4855351009261318389) -->
+ <skip />
+ <!-- no translation found for hit_correction_summary (8761701873008070796) -->
+ <skip />
+ <!-- no translation found for hit_correction_land (2567691684825205448) -->
+ <skip />
+ <!-- no translation found for hit_correction_land_summary (4076803842198368328) -->
+ <skip />
+ <!-- no translation found for auto_correction (7911639788808958255) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (6881047311475758267) -->
+ <skip />
+ <!-- no translation found for prediction (466220283138359837) -->
+ <skip />
+ <!-- no translation found for prediction_category (7027100625580696660) -->
+ <skip />
+ <!-- no translation found for prediction_summary (459788228830873110) -->
+ <skip />
+ <!-- no translation found for auto_complete_dialog_title (2172048590607201920) -->
+ <skip />
+ <!-- no translation found for prediction_landscape (4874601565593216183) -->
+ <skip />
+ <!-- no translation found for prediction_landscape_summary (6736551095997839472) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (3260681697600786825) -->
+ <skip />
+ <string name="auto_punctuate">"自動標點"</string>
+ <!-- no translation found for auto_punctuate_summary (7849876837879793721) -->
+ <skip />
+ <!-- no translation found for prediction_modes:0 (4870266572388153286) -->
+ <!-- no translation found for prediction_modes:1 (1669461741568287396) -->
+ <!-- no translation found for prediction_modes:2 (4894328801530136615) -->
+ <!-- no translation found for prediction_none (2472795101338047944) -->
+ <skip />
+ <!-- no translation found for prediction_basic (8407291081834155558) -->
+ <skip />
+ <!-- no translation found for prediction_full (3765102052052510268) -->
+ <skip />
+ <!-- no translation found for prediction_modes_values:0 (1346378763221728910) -->
+ <!-- no translation found for prediction_modes_values:1 (7980848218230433021) -->
+ <!-- no translation found for prediction_modes_values:2 (7444980361469942622) -->
+ <string name="added_word">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
+ <string name="alternates_for_a">"àáâãäåæ"</string>
+ <string name="alternates_for_e">"èéêë"</string>
+ <string name="alternates_for_i">"ìíîï"</string>
+ <string name="alternates_for_o">"òóôõöœø"</string>
+ <string name="alternates_for_u">"ùúûü"</string>
+ <string name="alternates_for_s">"§ß"</string>
+ <string name="alternates_for_n">"ñ"</string>
+ <string name="alternates_for_c">"ç"</string>
+ <string name="alternates_for_y">"ýÿ"</string>
+ <!-- no translation found for alternates_for_z (243837378542028049) -->
+ <skip />
+ <!-- no translation found for tip_long_press (6101270866284343344) -->
+ <skip />
+ <!-- no translation found for tip_dismiss (7585579046862204381) -->
+ <skip />
+ <!-- no translation found for tip_access_symbols (6344098517525531652) -->
+ <skip />
+ <!-- no translation found for tip_add_to_dictionary (1487293888469227817) -->
+ <skip />
+ <!-- no translation found for label_go_key (1635148082137219148) -->
+ <skip />
+ <!-- no translation found for label_next_key (362972844525672568) -->
+ <skip />
+ <!-- no translation found for label_send_key (2815056534433717444) -->
+ <skip />
+ <!-- no translation found for label_symbol_key (6175820506864489453) -->
+ <skip />
+ <!-- no translation found for label_phone_key (4275497665515080551) -->
+ <skip />
+ <!-- no translation found for label_alpha_key (8864943487292437456) -->
+ <skip />
+ <!-- no translation found for label_alt_key (2846315350346694811) -->
+ <skip />
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 000000000..c90d9f6af
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<resources>
+ <color name="candidate_normal">#FF000000</color>
+ <color name="candidate_recommended">#FFE35900</color>
+ <color name="candidate_other">#ff808080</color>
+</resources> \ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 000000000..d757f096d
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<resources>
+ <dimen name="key_height">50dip</dimen>
+ <dimen name="bubble_pointer_offset">22dip</dimen>
+</resources> \ No newline at end of file
diff --git a/res/values/donottranslate.xml b/res/values/donottranslate.xml
new file mode 100644
index 000000000..8a879bdd5
--- /dev/null
+++ b/res/values/donottranslate.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Symbols that are commonly considered word separators in this language -->
+ <string name="word_separators">.\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+ <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
+ <string name="sentence_separators">.,;:!?</string>
+</resources>
diff --git a/res/values/durations.xml b/res/values/durations.xml
new file mode 100644
index 000000000..92af68e39
--- /dev/null
+++ b/res/values/durations.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<resources>
+ <!-- Vibration duration in milliseconds, for key presses in the IME. This can be hardware
+ dependent and may require overriding with a device specific overlay. -->
+ <integer name="vibrate_duration_ms">40</integer>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 000000000..41809c15b
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title for Latin keyboard -->
+ <string name="english_ime_name">Android keyboard</string>
+ <!-- Title for Latin keyboard settings activity / dialog -->
+ <string name="english_ime_settings">Android keyboard settings</string>
+
+ <!-- Option to provide vibrate/haptic feedback on keypress -->
+ <string name="vibrate_on_keypress">Vibrate on keypress</string>
+ <!-- Option to play back sound on keypress in soft keyboard -->
+ <string name="sound_on_keypress">Sound on keypress</string>
+
+ <!-- Option to enable using nearby keys when correcting/predicting -->
+ <string name="hit_correction">Correct typing errors</string>
+
+ <!-- Description for hit_correction -->
+ <string name="hit_correction_summary">Enable input error correction</string>
+
+ <!-- Option to enable using nearby keys when correcting/predicting in landscape-->
+ <string name="hit_correction_land">Landscape input errors</string>
+
+ <!-- Description for hit_correction in landscape -->
+ <string name="hit_correction_land_summary">Enable input error correction</string>
+
+ <!-- Option to automatically correct word on hitting space -->
+ <string name="auto_correction">Word suggestions</string>
+
+ <!-- Description for auto_correction -->
+ <string name="auto_correction_summary">Automatically correct the previous word</string>
+
+ <!-- Option to enable text prediction -->
+ <string name="prediction">Word suggestions</string>
+ <!-- Category title for text prediction -->
+ <string name="prediction_category">Word suggestion settings</string>
+ <!-- Description for text prediction -->
+ <string name="prediction_summary">Enable auto completion while typing</string>
+
+ <!-- Dialog title for auto complete choices -->
+ <string name="auto_complete_dialog_title">Auto completion</string>
+
+ <!-- Option to enable text prediction in landscape -->
+ <string name="prediction_landscape">Increase text field size</string>
+ <!-- Description for text prediction -->
+ <string name="prediction_landscape_summary">Hide word suggestions in landscape view</string>
+
+ <!-- Option to enable auto capitalization of sentences -->
+ <string name="auto_cap">Auto-capitalization</string>
+ <!-- Description for auto cap -->
+ <string name="auto_cap_summary">Capitalize the start of a sentence</string>
+ <!-- Option to enable auto punctuate -->
+ <string name="auto_punctuate">Auto-punctuate</string>
+ <!-- Description for auto punctuate -->
+ <string name="auto_punctuate_summary"></string>
+
+ <!-- Array of prediction modes -->
+ <string-array name="prediction_modes">
+ <item>None</item>
+ <item>Basic</item>
+ <item>Advanced</item>
+ </string-array>
+
+ <!-- Don't translate -->
+ <string name="prediction_none" >0</string>
+ <!-- Don't translate -->
+ <string name="prediction_basic">1</string>
+ <!-- Don't translate -->
+ <string name="prediction_full" >2</string>
+
+ <string-array name="prediction_modes_values">
+ <item>@string/prediction_none</item>
+ <item>@string/prediction_basic</item>
+ <item>@string/prediction_full</item>
+ </string-array>
+
+ <!-- Indicates that a word has been added to the dictionary -->
+ <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
+ <!-- Accented forms of "a" -->
+ <string name="alternates_for_a">àáâãäåæ</string>
+ <!-- Accented forms of "e" -->
+ <string name="alternates_for_e">èéêë</string>
+ <!-- Accented forms of "i" -->
+ <string name="alternates_for_i">ìíîï</string>
+ <!-- Accented forms of "o" -->
+ <string name="alternates_for_o">òóôõöœø</string>
+ <!-- Accented forms of "u" -->
+ <string name="alternates_for_u">ùúûü</string>
+ <!-- Letters associated with "s" -->
+ <string name="alternates_for_s">§ß</string>
+ <!-- Accented forms of "n" -->
+ <string name="alternates_for_n">ñ</string>
+ <!-- Accented forms of "c" -->
+ <string name="alternates_for_c">ç</string>
+ <!-- Accented forms of "y" -->
+ <string name="alternates_for_y">ýÿ</string>
+ <!-- Accented forms of "z" -->
+ <string name="alternates_for_z"></string>
+
+ <!-- Tip to long press on keys -->
+ <string name="tip_long_press">Hold a key down to see accents (ø, ö, etc.)</string>
+ <!-- Tip to dismiss keyboard -->
+ <string name="tip_dismiss">Press the back key \u21B6 to close the keyboard at any point</string>
+ <!-- Tip to press ?123 to access numbers and symbols -->
+ <string name="tip_access_symbols">Access numbers and symbols</string>
+ <!-- Tip to long press on typed word to add to dictionary -->
+ <string name="tip_add_to_dictionary">Press and hold the left-most word to add it to the dictionary
+ </string>
+
+ <!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
+ <string name="label_go_key">Go</string>
+ <!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
+ <string name="label_next_key">Next</string>
+ <!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
+ <string name="label_done_key">Done</string>
+ <!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
+ <string name="label_send_key">Send</string>
+ <!-- Label for "switch to symbols" key. Must be short to fit on key! -->
+ <string name="label_symbol_key">\?123</string>
+ <!-- Label for "switch to numeric" key. Must be short to fit on key! -->
+ <string name="label_phone_key">123</string>
+ <!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
+ <string name="label_alpha_key">ABC</string>
+ <!-- Label for ALT modifier key. Must be short to fit on key! -->
+ <string name="label_alt_key">ALT</string>
+</resources>
diff --git a/res/xml-de/kbd_qwerty.xml b/res/xml-de/kbd_qwerty.xml
new file mode 100755
index 000000000..763492db5
--- /dev/null
+++ b/res/xml-de/kbd_qwerty.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="122" android:keyLabel="z" />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel=".com" android:keyOutputText=".com"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel="/" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:keyLabel="\@" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+
+ </Row>
+</Keyboard>
+ \ No newline at end of file
diff --git a/res/xml-fr/kbd_qwerty.xml b/res/xml-fr/kbd_qwerty.xml
new file mode 100644
index 000000000..573f08a3b
--- /dev/null
+++ b/res/xml-fr/kbd_qwerty.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"/>
+ <Key android:codes="109" android:keyLabel="m" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <!--Key android:codes="233,224,232,234" android:keyLabel="é"/-->
+ <Key android:keyLabel="\'"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel=".com" android:keyOutputText=".com"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel="/" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:keyLabel="\@" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
+ \ No newline at end of file
diff --git a/res/xml/azerty.xml b/res/xml/azerty.xml
new file mode 100644
index 000000000..614aa4936
--- /dev/null
+++ b/res/xml/azerty.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"/>
+ <Key android:codes="109" android:keyLabel="m" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <Key android:codes="233,224,232,234" android:keyLabel="é"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-3" android:keyIcon="@drawable/sym_keyboard_done"
+ android:iconPreview="@drawable/sym_keyboard_feedback_done"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46,44" android:keyLabel=". ,"
+ android:popupKeyboard="@xml/popup_punctuation" android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
+ \ No newline at end of file
diff --git a/res/xml/kbd_alpha.xml b/res/xml/kbd_alpha.xml
new file mode 100644
index 000000000..4e8af3399
--- /dev/null
+++ b/res/xml/kbd_alpha.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left" />
+ <Key android:keyLabel="b" />
+ <Key android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c" />
+ <Key android:keyLabel="d" />
+ <Key android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e" />
+ <Key android:keyLabel="f" />
+ <Key android:keyLabel="g" />
+ <Key android:keyLabel="h" />
+ <Key android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i" />
+ <Key android:keyLabel="j" android:keyEdgeFlags="right" />
+ </Row>
+ <Row>
+ <Key android:keyLabel="k" android:keyEdgeFlags="left" />
+ <Key android:keyLabel="l" />
+ <Key android:keyLabel="m" />
+ <Key android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n" />
+ <Key android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o" />
+ <Key android:keyLabel="p" />
+ <Key android:keyLabel="q" />
+ <Key android:keyLabel="r" />
+ <Key android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s" />
+ <Key android:keyLabel="t" android:keyEdgeFlags="right" />
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u" />
+ <Key android:keyLabel="v"/>
+ <Key android:keyLabel="w"/>
+ <Key android:keyLabel="x"/>
+ <Key android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:keyLabel="z"/>
+ <Key android:keyLabel=","/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-3" android:keyIcon="@drawable/sym_keyboard_done"
+ android:iconPreview="@drawable/sym_keyboard_feedback_done"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="."
+ android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard> \ No newline at end of file
diff --git a/res/xml/kbd_phone.xml b/res/xml/kbd_phone.xml
new file mode 100755
index 000000000..880d9613a
--- /dev/null
+++ b/res/xml/kbd_phone.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="26.67%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1" android:keyEdgeFlags="left"/>
+ <Key android:codes="50" android:keyIcon="@drawable/sym_keyboard_num2"/>
+ <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"/>
+ <Key android:keyLabel="-" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4" android:keyEdgeFlags="left"/>
+ <Key android:codes="53" android:keyIcon="@drawable/sym_keyboard_num5"/>
+ <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"/>
+ <Key android:keyLabel="." android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7" android:keyEdgeFlags="left"/>
+ <Key android:codes="56" android:keyIcon="@drawable/sym_keyboard_num8"/>
+ <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:keyWidth="20%p"
+ android:isRepeatable="true" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyIcon="@drawable/sym_keyboard_numalt"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:iconPreview="@drawable/sym_keyboard_feedback_numalt"/>
+
+ <Key android:codes="48" android:keyIcon="@drawable/sym_keyboard_num0"/>
+
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:keyWidth="20%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard> \ No newline at end of file
diff --git a/res/xml/kbd_phone_symbols.xml b/res/xml/kbd_phone_symbols.xml
new file mode 100755
index 000000000..9ce7a1a1f
--- /dev/null
+++ b/res/xml/kbd_phone_symbols.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="26.67%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="(" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/"/>
+ <Key android:keyLabel=")"/>
+ <Key android:keyLabel="-" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="N" android:keyEdgeFlags="left"/>
+ <!-- Pause is a comma.
+ Check PhoneNumberUtils.java to see if this has changed. -->
+ <Key android:codes="44" android:keyLabel="Pause"/>
+ <Key android:keyLabel=","/>
+ <Key android:keyLabel="." android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="42" android:keyIcon="@drawable/sym_keyboard_numstar"
+ android:keyEdgeFlags="left"/>
+ <!-- Wait is w -->
+ <Key android:codes="w" android:keyLabel="Wait"/>
+ <Key android:codes="35" android:keyIcon="@drawable/sym_keyboard_numpound"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:keyWidth="20%p"
+ android:isRepeatable="true" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_phone_key"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="+"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:keyWidth="20%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard> \ No newline at end of file
diff --git a/res/xml/kbd_popup_template.xml b/res/xml/kbd_popup_template.xml
new file mode 100644
index 000000000..aca46930f
--- /dev/null
+++ b/res/xml/kbd_popup_template.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+</Keyboard>
diff --git a/res/xml/kbd_qwerty.xml b/res/xml/kbd_qwerty.xml
new file mode 100755
index 000000000..34e991239
--- /dev/null
+++ b/res/xml/kbd_qwerty.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel=".com" android:keyOutputText=".com"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel="/" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p" />
+ <Key android:keyLabel="\@" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="15%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
+ \ No newline at end of file
diff --git a/res/xml/kbd_symbols.xml b/res/xml/kbd_symbols.xml
new file mode 100755
index 000000000..2a150397b
--- /dev/null
+++ b/res/xml/kbd_symbols.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¹½⅓¼⅛"
+ />
+ <Key android:codes="50" android:keyLabel="2"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="²⅔"
+ />
+ <Key android:codes="51" android:keyLabel="3"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="³¾⅜"
+ />
+ <Key android:codes="52" android:keyLabel="4"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⁴"
+ />
+ <Key android:codes="53" android:keyLabel="5"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⅝"
+ />
+ <Key android:codes="54" android:keyLabel="6"/>
+ <Key android:codes="55" android:keyLabel="7"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⅞"
+ />
+ <Key android:codes="56" android:keyLabel="8"/>
+ <Key android:codes="57" android:keyLabel="9"/>
+ <Key android:codes="48" android:keyLabel="0"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ⁿ∅"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+ <Key android:codes="35" android:keyLabel="\#"/>
+ <Key android:codes="36" android:keyLabel="$"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¢£€¥₣₤₱"
+ />
+ <Key android:codes="37" android:keyLabel="%"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="‰"
+ />
+ <Key android:codes="38" android:keyLabel="&amp;"/>
+ <Key android:codes="42" android:keyLabel="*"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="†‡"
+ />
+ <Key android:codes="45" android:keyLabel="-"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="–—"
+ />
+ <Key android:keyLabel="+"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="±"
+ />
+ <Key android:codes="40" android:keyLabel="("
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="[{&lt;"
+ />
+ <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="]}&gt;"
+ />
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="33" android:keyLabel="!"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¡"
+ />
+ <Key android:codes="34" android:keyLabel="&quot;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="“”«»˝"
+ />
+ <Key android:codes="39" android:keyLabel="\'"/>
+ <Key android:codes="58" android:keyLabel=":"/>
+ <Key android:codes="59" android:keyLabel=";"/>
+ <Key android:codes="47" android:keyLabel="/" />
+ <Key android:codes="63" android:keyLabel="\?"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¿"
+ />
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_alpha_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," android:keyWidth="15%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="‚„"
+ />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:keyLabel="." android:keyWidth="15%p" />
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ />
+ </Row>
+</Keyboard> \ No newline at end of file
diff --git a/res/xml/kbd_symbols_shift.xml b/res/xml/kbd_symbols_shift.xml
new file mode 100755
index 000000000..6a472a412
--- /dev/null
+++ b/res/xml/kbd_symbols_shift.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="~" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="`"/>
+ <Key android:keyLabel="|"/>
+ <Key android:keyLabel="•"/>
+ <Key android:keyLabel="√"/>
+ <Key android:keyLabel="π"/>
+ <Key android:keyLabel="÷"/>
+ <Key android:keyLabel="×"/>
+ <Key android:keyLabel="{"/>
+ <Key android:keyLabel="}" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="¥" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="£"/>
+ <Key android:keyLabel="¢"/>
+ <Key android:keyLabel="€"/>
+ <Key android:keyLabel="°"/>
+ <Key android:keyLabel="^"/>
+ <Key android:keyLabel="_"/>
+ <Key android:keyLabel="="
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≠≈∞"
+ />
+ <Key android:keyLabel="["/>
+ <Key android:keyLabel="]" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="™"/>
+ <Key android:keyLabel="®"/>
+ <Key android:keyLabel="©"/>
+ <Key android:keyLabel="¶"/>
+ <Key android:keyLabel="\\"/>
+ <Key android:keyLabel="&lt;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≤«‹"
+ />
+ <Key android:keyLabel="&gt;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≥»›"
+ />
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="20%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters=""
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="„" android:keyWidth="15%p" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:keyLabel="…" android:keyWidth="15%p" />
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ />
+ </Row>
+</Keyboard>
diff --git a/res/xml/method.xml b/res/xml/method.xml
new file mode 100644
index 000000000..e5654e96d
--- /dev/null
+++ b/res/xml/method.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2008, 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the Input Method Manager. -->
+
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.android.inputmethod.latin.LatinIMESettings"
+ android:isDefault="@bool/im_is_default"
+/>
diff --git a/res/xml/popup_domains.xml b/res/xml/popup_domains.xml
new file mode 100644
index 000000000..5c86386d5
--- /dev/null
+++ b/res/xml/popup_domains.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="15%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row android:rowEdgeFlags="top|bottom">
+ <Key android:keyLabel=".net" android:keyOutputText=".net" android:keyEdgeFlags="left" />
+ <Key android:keyLabel=".org" android:keyOutputText=".org"/>
+ <Key android:keyLabel=".gov" android:keyOutputText=".gov"/>
+ <Key android:keyLabel=".tv" android:keyOutputText=".tv" android:keyEdgeFlags="right" />
+ </Row>
+</Keyboard>
diff --git a/res/xml/popup_punctuation.xml b/res/xml/popup_punctuation.xml
new file mode 100644
index 000000000..9d4575db6
--- /dev/null
+++ b/res/xml/popup_punctuation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row android:rowEdgeFlags="top|bottom">
+ <Key android:codes="58" android:keyLabel=":" android:keyEdgeFlags="left" />
+ <Key android:codes="47" android:keyLabel="/" />
+ <Key android:codes="64" android:keyLabel="\@" />
+ <Key android:codes="39" android:keyLabel="\'" />
+ <Key android:codes="34" android:keyLabel="&quot;" />
+ <Key android:codes="63" android:keyLabel="\?" />
+ <Key android:codes="33" android:keyLabel="!" android:keyEdgeFlags="right" />
+ </Row>
+</Keyboard>
diff --git a/res/xml/popup_smileys.xml b/res/xml/popup_smileys.xml
new file mode 100644
index 000000000..5663fefc8
--- /dev/null
+++ b/res/xml/popup_smileys.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="15%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row android:rowEdgeFlags="top">
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) " android:keyEdgeFlags="left" />
+ <Key android:keyLabel=":-(" android:keyOutputText=":-( "/>
+ <Key android:keyLabel=";-)" android:keyOutputText=";-) "/>
+ <Key android:keyLabel=":-P" android:keyOutputText=":-P "/>
+ <Key android:keyLabel="=-O" android:keyOutputText="=-O " android:keyEdgeFlags="right" />
+ </Row>
+ <Row>
+ <Key android:keyLabel=":-*" android:keyOutputText=":-* " android:keyEdgeFlags="left" />
+ <Key android:keyLabel=":O" android:keyOutputText=":O "/>
+ <Key android:keyLabel="B-)" android:keyOutputText="B-) "/>
+ <Key android:keyLabel=":-$" android:keyOutputText=":-$ "/>
+ <Key android:keyLabel=":-!" android:keyOutputText=":-! " android:keyEdgeFlags="right" />
+ </Row>
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:keyLabel=":-[" android:keyOutputText=":-[ " android:keyEdgeFlags="left" />
+ <Key android:keyLabel="O:-)" android:keyOutputText="O:-) "/>
+ <Key android:keyLabel=":-\\" android:keyOutputText=":-\\ "/>
+ <Key android:keyLabel=":'(" android:keyOutputText=":'( "/>
+ <Key android:keyLabel=":-D" android:keyOutputText=":-D " android:keyEdgeFlags="right" />
+ </Row>
+</Keyboard>
diff --git a/res/xml/prefs.xml b/res/xml/prefs.xml
new file mode 100644
index 000000000..1721384f1
--- /dev/null
+++ b/res/xml/prefs.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/english_ime_settings"
+ android:key="english_ime_settings">
+
+ <CheckBoxPreference
+ android:key="vibrate_on"
+ android:title="@string/vibrate_on_keypress"
+ android:defaultValue="true"
+ android:persistent="true"
+ />
+
+ <CheckBoxPreference
+ android:key="sound_on"
+ android:title="@string/sound_on_keypress"
+ android:persistent="true"
+ />
+
+ <CheckBoxPreference
+ android:key="auto_cap"
+ android:title="@string/auto_cap"
+ android:persistent="true"
+ android:defaultValue="true"
+ />
+
+ <!--CheckBoxPreference
+ android:key="auto_punctuate"
+ android:title="@string/auto_punctuate"
+ android:persistent="true"
+ android:defaultValue="true"
+ android:visible="false"
+ /-->
+
+ <ListPreference
+ android:key="prediction_mode"
+ android:title="@string/prediction"
+ android:dialogTitle="@string/auto_complete_dialog_title"
+ android:summary="@string/prediction_summary"
+ android:persistent="true"
+ android:entries="@array/prediction_modes"
+ android:entryValues="@array/prediction_modes_values"
+ android:defaultValue="@string/prediction_basic"
+ />
+
+ <PreferenceCategory
+ android:title="@string/prediction_category"
+ android:key="prediction_settings">
+
+ <CheckBoxPreference
+ android:key="hit_correction"
+ android:title="@string/hit_correction"
+ android:summary="@string/hit_correction_summary"
+ android:persistent="true"
+ android:defaultValue="true"
+ />
+
+ <CheckBoxPreference
+ android:key="prediction_landscape"
+ android:title="@string/prediction_landscape"
+ android:summary="@string/prediction_landscape_summary"
+ android:persistent="true"
+ android:defaultValue="false"
+ />
+
+ </PreferenceCategory>
+</PreferenceScreen> \ No newline at end of file
diff --git a/src/com/android/inputmethod/latin/BinaryDictionary.java b/src/com/android/inputmethod/latin/BinaryDictionary.java
new file mode 100644
index 000000000..bb4f1ba46
--- /dev/null
+++ b/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import java.util.Arrays;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+/**
+ * Implements a static, compacted, binary dictionary of standard words.
+ */
+public class BinaryDictionary extends Dictionary {
+
+ public static final int MAX_WORD_LENGTH = 48;
+ private static final int MAX_ALTERNATIVES = 16;
+ private static final int MAX_WORDS = 16;
+
+ private static final int TYPED_LETTER_MULTIPLIER = 2;
+
+ private int mNativeDict;
+ private int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES];
+ private WordCallback mWordCallback;
+ private char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
+ private int[] mFrequencies = new int[MAX_WORDS];
+
+ static {
+ try {
+ System.loadLibrary("jni_latinime");
+ } catch (UnsatisfiedLinkError ule) {
+ Log.e("BinaryDictionary", "Could not load native library jni_latinime");
+ }
+ }
+
+ /**
+ * Create a dictionary from a raw resource file
+ * @param context application context for reading resources
+ * @param resId the resource containing the raw binary dictionary
+ */
+ public BinaryDictionary(Context context, int resId) {
+ if (resId != 0) {
+ loadDictionary(context, resId);
+ }
+ }
+
+ private native int openNative(AssetManager am, String resourcePath, int typedLetterMultiplier,
+ int fullWordMultiplier);
+ private native void closeNative(int dict);
+ private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
+ private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize,
+ char[] outputChars, int[] frequencies,
+ int maxWordLength, int maxWords, int maxAlternatives);
+ private native void setParamsNative(int typedLetterMultiplier,
+ int fullWordMultiplier);
+
+ private final void loadDictionary(Context context, int resId) {
+ AssetManager am = context.getResources().getAssets();
+ String assetName = context.getResources().getString(resId);
+ mNativeDict = openNative(am, assetName, TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER);
+ }
+
+ @Override
+ public void getWords(final WordComposer codes, final WordCallback callback) {
+ mWordCallback = callback;
+ final int codesSize = codes.size();
+ // Wont deal with really long words.
+ if (codesSize > MAX_WORD_LENGTH - 1) return;
+
+ Arrays.fill(mInputCodes, -1);
+ for (int i = 0; i < codesSize; i++) {
+ int[] alternatives = codes.getCodesAt(i);
+ System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES,
+ Math.min(alternatives.length, MAX_ALTERNATIVES));
+ }
+ Arrays.fill(mOutputChars, (char) 0);
+
+ int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, mOutputChars, mFrequencies,
+ MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES);
+
+ for (int j = 0; j < count; j++) {
+ if (mFrequencies[j] < 1) break;
+ int start = j * MAX_WORD_LENGTH;
+ int len = 0;
+ while (mOutputChars[start + len] != 0) {
+ len++;
+ }
+ if (len > 0) {
+ callback.addWord(mOutputChars, start, len, mFrequencies[j]);
+ }
+ }
+ }
+
+ @Override
+ public boolean isValidWord(CharSequence word) {
+ if (word == null) return false;
+ char[] chars = word.toString().toLowerCase().toCharArray();
+ return isValidWordNative(mNativeDict, chars, chars.length);
+ }
+
+ public synchronized void close() {
+ if (mNativeDict != 0) {
+ closeNative(mNativeDict);
+ mNativeDict = 0;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+}
diff --git a/src/com/android/inputmethod/latin/CandidateView.java b/src/com/android/inputmethod/latin/CandidateView.java
new file mode 100755
index 000000000..08c68dc12
--- /dev/null
+++ b/src/com/android/inputmethod/latin/CandidateView.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+public class CandidateView extends View {
+
+ private static final int OUT_OF_BOUNDS = -1;
+ private static final List<CharSequence> EMPTY_LIST = new ArrayList<CharSequence>();
+
+ private LatinIME mService;
+ private List<CharSequence> mSuggestions = EMPTY_LIST;
+ private boolean mShowingCompletions;
+ private CharSequence mSelectedString;
+ private int mSelectedIndex;
+ private int mTouchX = OUT_OF_BOUNDS;
+ private Drawable mSelectionHighlight;
+ private boolean mTypedWordValid;
+
+ private boolean mHaveMinimalSuggestion;
+
+ private Rect mBgPadding;
+
+ private TextView mPreviewText;
+ private PopupWindow mPreviewPopup;
+ private int mCurrentWordIndex;
+ private Drawable mDivider;
+
+ private static final int MAX_SUGGESTIONS = 32;
+ private static final int SCROLL_PIXELS = 20;
+
+ private static final int MSG_REMOVE_PREVIEW = 1;
+ private static final int MSG_REMOVE_THROUGH_PREVIEW = 2;
+
+ private int[] mWordWidth = new int[MAX_SUGGESTIONS];
+ private int[] mWordX = new int[MAX_SUGGESTIONS];
+ private int mPopupPreviewX;
+ private int mPopupPreviewY;
+
+ private static final int X_GAP = 10;
+
+ private int mColorNormal;
+ private int mColorRecommended;
+ private int mColorOther;
+ private Paint mPaint;
+ private int mDescent;
+ private boolean mScrolled;
+ private int mTargetScrollX;
+
+ private int mTotalWidth;
+
+ private GestureDetector mGestureDetector;
+
+ Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REMOVE_PREVIEW:
+ mPreviewText.setVisibility(GONE);
+ break;
+ case MSG_REMOVE_THROUGH_PREVIEW:
+ mPreviewText.setVisibility(GONE);
+ if (mTouchX != OUT_OF_BOUNDS) {
+ removeHighlight();
+ }
+ break;
+ }
+
+ }
+ };
+
+ /**
+ * Construct a CandidateView for showing suggested words for completion.
+ * @param context
+ * @param attrs
+ */
+ public CandidateView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mSelectionHighlight = context.getResources().getDrawable(
+ com.android.internal.R.drawable.list_selector_background_pressed);
+
+ LayoutInflater inflate =
+ (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mPreviewPopup = new PopupWindow(context);
+ mPreviewText = (TextView) inflate.inflate(R.layout.candidate_preview, null);
+ mPreviewPopup.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ mPreviewPopup.setContentView(mPreviewText);
+ mPreviewPopup.setBackgroundDrawable(null);
+ mColorNormal = context.getResources().getColor(R.color.candidate_normal);
+ mColorRecommended = context.getResources().getColor(R.color.candidate_recommended);
+ mColorOther = context.getResources().getColor(R.color.candidate_other);
+ mDivider = context.getResources().getDrawable(R.drawable.keyboard_suggest_strip_divider);
+
+ mPaint = new Paint();
+ mPaint.setColor(mColorNormal);
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(mPreviewText.getTextSize());
+ mPaint.setStrokeWidth(0);
+ mDescent = (int) mPaint.descent();
+
+ mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public void onLongPress(MotionEvent me) {
+ if (mSuggestions.size() > 0) {
+ if (me.getX() + mScrollX < mWordWidth[0] && mScrollX < 10) {
+ longPressFirstWord();
+ }
+ }
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ mScrolled = true;
+ mScrollX += distanceX;
+ if (mScrollX < 0) {
+ mScrollX = 0;
+ }
+ if (mScrollX + getWidth() > mTotalWidth) {
+ mScrollX -= distanceX;
+ }
+ mTargetScrollX = mScrollX;
+ showPreview(OUT_OF_BOUNDS, null);
+ invalidate();
+ return true;
+ }
+ });
+ setHorizontalFadingEdgeEnabled(true);
+ setWillNotDraw(false);
+ setHorizontalScrollBarEnabled(false);
+ setVerticalScrollBarEnabled(false);
+ mScrollX = 0;
+ }
+
+ /**
+ * A connection back to the service to communicate with the text field
+ * @param listener
+ */
+ public void setService(LatinIME listener) {
+ mService = listener;
+ }
+
+ @Override
+ public int computeHorizontalScrollRange() {
+ return mTotalWidth;
+ }
+
+ /**
+ * If the canvas is null, then only touch calculations are performed to pick the target
+ * candidate.
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (canvas != null) {
+ super.onDraw(canvas);
+ }
+ mTotalWidth = 0;
+ if (mSuggestions == null) return;
+
+ final int height = getHeight();
+ if (mBgPadding == null) {
+ mBgPadding = new Rect(0, 0, 0, 0);
+ if (getBackground() != null) {
+ getBackground().getPadding(mBgPadding);
+ }
+ mDivider.setBounds(0, mBgPadding.top, mDivider.getIntrinsicWidth(),
+ mDivider.getIntrinsicHeight());
+ }
+ int x = 0;
+ final int count = mSuggestions.size();
+ final int width = getWidth();
+ final Rect bgPadding = mBgPadding;
+ final Paint paint = mPaint;
+ final int touchX = mTouchX;
+ final int scrollX = mScrollX;
+ final boolean scrolled = mScrolled;
+ final boolean typedWordValid = mTypedWordValid;
+ final int y = (int) (height + mPaint.getTextSize() - mDescent) / 2;
+
+ for (int i = 0; i < count; i++) {
+ CharSequence suggestion = mSuggestions.get(i);
+ if (suggestion == null) continue;
+ paint.setColor(mColorNormal);
+ if (mHaveMinimalSuggestion
+ && ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid))) {
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
+ paint.setColor(mColorRecommended);
+ } else if (i != 0) {
+ paint.setColor(mColorOther);
+ }
+ final int wordWidth;
+ if (mWordWidth[i] != 0) {
+ wordWidth = mWordWidth[i];
+ } else {
+ float textWidth = paint.measureText(suggestion, 0, suggestion.length());
+ wordWidth = (int) textWidth + X_GAP * 2;
+ mWordWidth[i] = wordWidth;
+ }
+
+ mWordX[i] = x;
+
+ if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) {
+ if (canvas != null) {
+ canvas.translate(x, 0);
+ mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
+ mSelectionHighlight.draw(canvas);
+ canvas.translate(-x, 0);
+ showPreview(i, null);
+ }
+ mSelectedString = suggestion;
+ mSelectedIndex = i;
+ }
+
+ if (canvas != null) {
+ canvas.drawText(suggestion, 0, suggestion.length(), x + X_GAP, y, paint);
+ paint.setColor(mColorOther);
+ canvas.translate(x + wordWidth, 0);
+ mDivider.draw(canvas);
+ canvas.translate(-x - wordWidth, 0);
+ }
+ paint.setTypeface(Typeface.DEFAULT);
+ x += wordWidth;
+ }
+ mTotalWidth = x;
+ if (mTargetScrollX != mScrollX) {
+ scrollToTarget();
+ }
+ }
+
+ private void scrollToTarget() {
+ if (mTargetScrollX > mScrollX) {
+ mScrollX += SCROLL_PIXELS;
+ if (mScrollX >= mTargetScrollX) {
+ mScrollX = mTargetScrollX;
+ requestLayout();
+ }
+ } else {
+ mScrollX -= SCROLL_PIXELS;
+ if (mScrollX <= mTargetScrollX) {
+ mScrollX = mTargetScrollX;
+ requestLayout();
+ }
+ }
+ invalidate();
+ }
+
+ public void setSuggestions(List<CharSequence> suggestions, boolean completions,
+ boolean typedWordValid, boolean haveMinimalSuggestion) {
+ clear();
+ if (suggestions != null) {
+ mSuggestions = new ArrayList<CharSequence>(suggestions);
+ }
+ mShowingCompletions = completions;
+ mTypedWordValid = typedWordValid;
+ mScrollX = 0;
+ mTargetScrollX = 0;
+ mHaveMinimalSuggestion = haveMinimalSuggestion;
+ // Compute the total width
+ onDraw(null);
+ invalidate();
+ requestLayout();
+ }
+
+ public void scrollPrev() {
+ int i = 0;
+ final int count = mSuggestions.size();
+ int firstItem = 0; // Actually just before the first item, if at the boundary
+ while (i < count) {
+ if (mWordX[i] < mScrollX
+ && mWordX[i] + mWordWidth[i] >= mScrollX - 1) {
+ firstItem = i;
+ break;
+ }
+ i++;
+ }
+ int leftEdge = mWordX[firstItem] + mWordWidth[firstItem] - getWidth();
+ if (leftEdge < 0) leftEdge = 0;
+ updateScrollPosition(leftEdge);
+ }
+
+ public void scrollNext() {
+ int i = 0;
+ int targetX = mScrollX;
+ final int count = mSuggestions.size();
+ int rightEdge = mScrollX + getWidth();
+ while (i < count) {
+ if (mWordX[i] <= rightEdge &&
+ mWordX[i] + mWordWidth[i] >= rightEdge) {
+ targetX = Math.min(mWordX[i], mTotalWidth - getWidth());
+ break;
+ }
+ i++;
+ }
+ updateScrollPosition(targetX);
+ }
+
+ private void updateScrollPosition(int targetX) {
+ if (targetX != mScrollX) {
+ // TODO: Animate
+ mTargetScrollX = targetX;
+ requestLayout();
+ invalidate();
+ mScrolled = true;
+ }
+ }
+
+ public void clear() {
+ mSuggestions = EMPTY_LIST;
+ mTouchX = OUT_OF_BOUNDS;
+ mSelectedString = null;
+ mSelectedIndex = -1;
+ invalidate();
+ Arrays.fill(mWordWidth, 0);
+ Arrays.fill(mWordX, 0);
+ if (mPreviewPopup.isShowing()) {
+ mPreviewPopup.dismiss();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+
+ if (mGestureDetector.onTouchEvent(me)) {
+ return true;
+ }
+
+ int action = me.getAction();
+ int x = (int) me.getX();
+ int y = (int) me.getY();
+ mTouchX = x;
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mScrolled = false;
+ invalidate();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (y <= 0) {
+ // Fling up!?
+ if (mSelectedString != null) {
+ if (!mShowingCompletions) {
+ TextEntryState.acceptedSuggestion(mSuggestions.get(0),
+ mSelectedString);
+ }
+ mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
+ mSelectedString = null;
+ mSelectedIndex = -1;
+ }
+ }
+ invalidate();
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!mScrolled) {
+ if (mSelectedString != null) {
+ if (!mShowingCompletions) {
+ TextEntryState.acceptedSuggestion(mSuggestions.get(0),
+ mSelectedString);
+ }
+ mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
+ }
+ }
+ mSelectedString = null;
+ mSelectedIndex = -1;
+ removeHighlight();
+ showPreview(OUT_OF_BOUNDS, null);
+ requestLayout();
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * For flick through from keyboard, call this method with the x coordinate of the flick
+ * gesture.
+ * @param x
+ */
+ public void takeSuggestionAt(float x) {
+ mTouchX = (int) x;
+ // To detect candidate
+ onDraw(null);
+ if (mSelectedString != null) {
+ if (!mShowingCompletions) {
+ TextEntryState.acceptedSuggestion(mSuggestions.get(0), mSelectedString);
+ }
+ mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
+ }
+ invalidate();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_REMOVE_THROUGH_PREVIEW), 200);
+ }
+
+ private void showPreview(int wordIndex, String altText) {
+ int oldWordIndex = mCurrentWordIndex;
+ mCurrentWordIndex = wordIndex;
+ // If index changed or changing text
+ if (oldWordIndex != mCurrentWordIndex || altText != null) {
+ if (wordIndex == OUT_OF_BOUNDS) {
+ if (mPreviewPopup.isShowing()) {
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(MSG_REMOVE_PREVIEW), 60);
+ }
+ } else {
+ CharSequence word = altText != null? altText : mSuggestions.get(wordIndex);
+ mPreviewText.setText(word);
+ mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ int wordWidth = (int) (mPaint.measureText(word, 0, word.length()) + X_GAP * 2);
+ final int popupWidth = wordWidth
+ + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight();
+ final int popupHeight = mPreviewText.getMeasuredHeight();
+ //mPreviewText.setVisibility(INVISIBLE);
+ mPopupPreviewX = mWordX[wordIndex] - mPreviewText.getPaddingLeft() - mScrollX;
+ mPopupPreviewY = - popupHeight;
+ mHandler.removeMessages(MSG_REMOVE_PREVIEW);
+ int [] offsetInWindow = new int[2];
+ getLocationInWindow(offsetInWindow);
+ if (mPreviewPopup.isShowing()) {
+ mPreviewPopup.update(mPopupPreviewX, mPopupPreviewY + offsetInWindow[1],
+ popupWidth, popupHeight);
+ } else {
+ mPreviewPopup.setWidth(popupWidth);
+ mPreviewPopup.setHeight(popupHeight);
+ mPreviewPopup.showAtLocation(this, Gravity.NO_GRAVITY, mPopupPreviewX,
+ mPopupPreviewY + offsetInWindow[1]);
+ }
+ mPreviewText.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ private void removeHighlight() {
+ mTouchX = OUT_OF_BOUNDS;
+ invalidate();
+ }
+
+ private void longPressFirstWord() {
+ CharSequence word = mSuggestions.get(0);
+ if (mService.addWordToDictionary(word.toString())) {
+ showPreview(0, getContext().getResources().getString(R.string.added_word, word));
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/CandidateViewContainer.java b/src/com/android/inputmethod/latin/CandidateViewContainer.java
new file mode 100644
index 000000000..e13f2738c
--- /dev/null
+++ b/src/com/android/inputmethod/latin/CandidateViewContainer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.LinearLayout;
+
+public class CandidateViewContainer extends LinearLayout implements OnTouchListener {
+
+ private View mButtonLeft;
+ private View mButtonRight;
+ private View mButtonLeftLayout;
+ private View mButtonRightLayout;
+ private CandidateView mCandidates;
+
+ public CandidateViewContainer(Context screen, AttributeSet attrs) {
+ super(screen, attrs);
+ }
+
+ public void initViews() {
+ if (mCandidates == null) {
+ mButtonLeftLayout = findViewById(R.id.candidate_left_parent);
+ mButtonLeft = findViewById(R.id.candidate_left);
+ if (mButtonLeft != null) {
+ mButtonLeft.setOnTouchListener(this);
+ }
+ mButtonRightLayout = findViewById(R.id.candidate_right_parent);
+ mButtonRight = findViewById(R.id.candidate_right);
+ if (mButtonRight != null) {
+ mButtonRight.setOnTouchListener(this);
+ }
+ mCandidates = (CandidateView) findViewById(R.id.candidates);
+ }
+ }
+
+ @Override
+ public void requestLayout() {
+ if (mCandidates != null) {
+ int availableWidth = mCandidates.getWidth();
+ int neededWidth = mCandidates.computeHorizontalScrollRange();
+ int x = mCandidates.getScrollX();
+ boolean leftVisible = x > 0;
+ boolean rightVisible = x + availableWidth < neededWidth;
+ if (mButtonLeftLayout != null) {
+ mButtonLeftLayout.setVisibility(leftVisible ? VISIBLE : GONE);
+ }
+ if (mButtonRightLayout != null) {
+ mButtonRightLayout.setVisibility(rightVisible ? VISIBLE : GONE);
+ }
+ }
+ super.requestLayout();
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (v == mButtonRight) {
+ mCandidates.scrollNext();
+ } else if (v == mButtonLeft) {
+ mCandidates.scrollPrev();
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/com/android/inputmethod/latin/Dictionary.java b/src/com/android/inputmethod/latin/Dictionary.java
new file mode 100644
index 000000000..fdf34264a
--- /dev/null
+++ b/src/com/android/inputmethod/latin/Dictionary.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+/**
+ * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
+ * strokes.
+ */
+abstract public class Dictionary {
+
+ /**
+ * Whether or not to replicate the typed word in the suggested list, even if it's valid.
+ */
+ protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false;
+
+ /**
+ * The weight to give to a word if it's length is the same as the number of typed characters.
+ */
+ protected static final int FULL_WORD_FREQ_MULTIPLIER = 2;
+
+ /**
+ * Interface to be implemented by classes requesting words to be fetched from the dictionary.
+ * @see #getWords(WordComposer, WordCallback)
+ */
+ public interface WordCallback {
+ /**
+ * Adds a word to a list of suggestions. The word is expected to be ordered based on
+ * the provided frequency.
+ * @param word the character array containing the word
+ * @param wordOffset starting offset of the word in the character array
+ * @param wordLength length of valid characters in the character array
+ * @param frequency the frequency of occurence. This is normalized between 1 and 255, but
+ * can exceed those limits
+ * @return true if the word was added, false if no more words are required
+ */
+ boolean addWord(char[] word, int wordOffset, int wordLength, int frequency);
+ }
+
+ /**
+ * Searches for words in the dictionary that match the characters in the composer. Matched
+ * words are added through the callback object.
+ * @param composer the key sequence to match
+ * @param callback the callback object to send matched words to as possible candidates
+ * @see WordCallback#addWord(char[], int, int)
+ */
+ abstract public void getWords(final WordComposer composer, final WordCallback callback);
+
+ /**
+ * Checks if the given word occurs in the dictionary
+ * @param word the word to search for. The search should be case-insensitive.
+ * @return true if the word exists, false otherwise
+ */
+ abstract public boolean isValidWord(CharSequence word);
+
+ /**
+ * Compares the contents of the character array with the typed word and returns true if they
+ * are the same.
+ * @param word the array of characters that make up the word
+ * @param length the number of valid characters in the character array
+ * @param typedWord the word to compare with
+ * @return true if they are the same, false otherwise.
+ */
+ protected boolean same(final char[] word, final int length, final CharSequence typedWord) {
+ if (typedWord.length() != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (word[i] != typedWord.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/src/com/android/inputmethod/latin/KeyboardSwitcher.java
new file mode 100644
index 000000000..b3fcbda6f
--- /dev/null
+++ b/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.latin;
+
+import android.inputmethodservice.Keyboard;
+
+import java.util.List;
+
+public class KeyboardSwitcher {
+
+ public static final int MODE_TEXT = 1;
+ public static final int MODE_SYMBOLS = 2;
+ public static final int MODE_PHONE = 3;
+ public static final int MODE_URL = 4;
+ public static final int MODE_EMAIL = 5;
+ public static final int MODE_IM = 6;
+
+ public static final int MODE_TEXT_QWERTY = 0;
+ public static final int MODE_TEXT_ALPHA = 1;
+ public static final int MODE_TEXT_COUNT = 2;
+
+ public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal;
+ public static final int KEYBOARDMODE_URL = R.id.mode_url;
+ public static final int KEYBOARDMODE_EMAIL = R.id.mode_email;
+ public static final int KEYBOARDMODE_IM = R.id.mode_im;
+
+
+ LatinKeyboardView mInputView;
+ LatinIME mContext;
+
+ private LatinKeyboard mPhoneKeyboard;
+ private LatinKeyboard mPhoneSymbolsKeyboard;
+ private LatinKeyboard mSymbolsKeyboard;
+ private LatinKeyboard mSymbolsShiftedKeyboard;
+ private LatinKeyboard mQwertyKeyboard;
+ private LatinKeyboard mAlphaKeyboard;
+ private LatinKeyboard mUrlKeyboard;
+ private LatinKeyboard mEmailKeyboard;
+ private LatinKeyboard mIMKeyboard;
+
+ List<Keyboard> mKeyboards;
+
+ private int mMode;
+ private int mImeOptions;
+ private int mTextMode = MODE_TEXT_QWERTY;
+
+ private int mLastDisplayWidth;
+
+ KeyboardSwitcher(LatinIME context) {
+ mContext = context;
+ }
+
+ void setInputView(LatinKeyboardView inputView) {
+ mInputView = inputView;
+ }
+
+ void makeKeyboards() {
+ // Configuration change is coming after the keyboard gets recreated. So don't rely on that.
+ // If keyboards have already been made, check if we have a screen width change and
+ // create the keyboard layouts again at the correct orientation
+ if (mKeyboards != null) {
+ int displayWidth = mContext.getMaxWidth();
+ if (displayWidth == mLastDisplayWidth) return;
+ mLastDisplayWidth = displayWidth;
+ }
+ // Delayed creation when keyboard mode is set.
+ mQwertyKeyboard = null;
+ mAlphaKeyboard = null;
+ mUrlKeyboard = null;
+ mEmailKeyboard = null;
+ mIMKeyboard = null;
+ mPhoneKeyboard = null;
+ mPhoneSymbolsKeyboard = null;
+ mSymbolsKeyboard = null;
+ mSymbolsShiftedKeyboard = null;
+ }
+
+ void setKeyboardMode(int mode, int imeOptions) {
+ mMode = mode;
+ mImeOptions = imeOptions;
+ LatinKeyboard keyboard = (LatinKeyboard) mInputView.getKeyboard();
+ mInputView.setPreviewEnabled(true);
+ switch (mode) {
+ case MODE_TEXT:
+ if (mTextMode == MODE_TEXT_QWERTY) {
+ if (mQwertyKeyboard == null) {
+ mQwertyKeyboard = new LatinKeyboard(mContext, R.xml.kbd_qwerty,
+ KEYBOARDMODE_NORMAL);
+ mQwertyKeyboard.enableShiftLock();
+ }
+ keyboard = mQwertyKeyboard;
+ } else if (mTextMode == MODE_TEXT_ALPHA) {
+ if (mAlphaKeyboard == null) {
+ mAlphaKeyboard = new LatinKeyboard(mContext, R.xml.kbd_alpha,
+ KEYBOARDMODE_NORMAL);
+ mAlphaKeyboard.enableShiftLock();
+ }
+ keyboard = mAlphaKeyboard;
+ }
+ break;
+ case MODE_SYMBOLS:
+ if (mSymbolsKeyboard == null) {
+ mSymbolsKeyboard = new LatinKeyboard(mContext, R.xml.kbd_symbols);
+ }
+ if (mSymbolsShiftedKeyboard == null) {
+ mSymbolsShiftedKeyboard = new LatinKeyboard(mContext, R.xml.kbd_symbols_shift);
+ }
+ keyboard = mSymbolsKeyboard;
+ break;
+ case MODE_PHONE:
+ if (mPhoneKeyboard == null) {
+ mPhoneKeyboard = new LatinKeyboard(mContext, R.xml.kbd_phone);
+ }
+ mInputView.setPhoneKeyboard(mPhoneKeyboard);
+ if (mPhoneSymbolsKeyboard == null) {
+ mPhoneSymbolsKeyboard = new LatinKeyboard(mContext, R.xml.kbd_phone_symbols);
+ }
+ keyboard = mPhoneKeyboard;
+ mInputView.setPreviewEnabled(false);
+ break;
+ case MODE_URL:
+ if (mUrlKeyboard == null) {
+ mUrlKeyboard = new LatinKeyboard(mContext, R.xml.kbd_qwerty, KEYBOARDMODE_URL);
+ mUrlKeyboard.enableShiftLock();
+ }
+ keyboard = mUrlKeyboard;
+ break;
+ case MODE_EMAIL:
+ if (mEmailKeyboard == null) {
+ mEmailKeyboard = new LatinKeyboard(mContext, R.xml.kbd_qwerty, KEYBOARDMODE_EMAIL);
+ mEmailKeyboard.enableShiftLock();
+ }
+ keyboard = mEmailKeyboard;
+ break;
+ case MODE_IM:
+ if (mIMKeyboard == null) {
+ mIMKeyboard = new LatinKeyboard(mContext, R.xml.kbd_qwerty, KEYBOARDMODE_IM);
+ mIMKeyboard.enableShiftLock();
+ }
+ keyboard = mIMKeyboard;
+ break;
+ }
+ mInputView.setKeyboard(keyboard);
+ keyboard.setShifted(false);
+ keyboard.setShiftLocked(keyboard.isShiftLocked());
+ keyboard.setImeOptions(mContext.getResources(), mMode, imeOptions);
+ }
+
+ int getKeyboardMode() {
+ return mMode;
+ }
+
+ boolean isTextMode() {
+ return mMode == MODE_TEXT;
+ }
+
+ int getTextMode() {
+ return mTextMode;
+ }
+
+ void setTextMode(int position) {
+ if (position < MODE_TEXT_COUNT && position >= 0) {
+ mTextMode = position;
+ }
+ if (isTextMode()) {
+ setKeyboardMode(MODE_TEXT, mImeOptions);
+ }
+ }
+
+ int getTextModeCount() {
+ return MODE_TEXT_COUNT;
+ }
+
+ boolean isAlphabetMode() {
+ Keyboard current = mInputView.getKeyboard();
+ if (current == mQwertyKeyboard
+ || current == mAlphaKeyboard
+ || current == mUrlKeyboard
+ || current == mIMKeyboard
+ || current == mEmailKeyboard) {
+ return true;
+ }
+ return false;
+ }
+
+ void toggleShift() {
+ Keyboard currentKeyboard = mInputView.getKeyboard();
+ if (currentKeyboard == mSymbolsKeyboard) {
+ mSymbolsKeyboard.setShifted(true);
+ mInputView.setKeyboard(mSymbolsShiftedKeyboard);
+ mSymbolsShiftedKeyboard.setShifted(true);
+ mSymbolsShiftedKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+ } else if (currentKeyboard == mSymbolsShiftedKeyboard) {
+ mSymbolsShiftedKeyboard.setShifted(false);
+ mInputView.setKeyboard(mSymbolsKeyboard);
+ mSymbolsKeyboard.setShifted(false);
+ mSymbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+ }
+ }
+
+ void toggleSymbols() {
+ Keyboard current = mInputView.getKeyboard();
+ if (mSymbolsKeyboard == null) {
+ mSymbolsKeyboard = new LatinKeyboard(mContext, R.xml.kbd_symbols);
+ }
+ if (mSymbolsShiftedKeyboard == null) {
+ mSymbolsShiftedKeyboard = new LatinKeyboard(mContext, R.xml.kbd_symbols_shift);
+ }
+ if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
+ setKeyboardMode(mMode, mImeOptions); // Could be qwerty, alpha, url, email or im
+ return;
+ } else if (current == mPhoneKeyboard) {
+ current = mPhoneSymbolsKeyboard;
+ mPhoneSymbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+ } else if (current == mPhoneSymbolsKeyboard) {
+ current = mPhoneKeyboard;
+ mPhoneKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+ } else {
+ current = mSymbolsKeyboard;
+ mSymbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+ }
+ mInputView.setKeyboard(current);
+ if (current == mSymbolsKeyboard) {
+ current.setShifted(false);
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/LatinIME.java b/src/com/android/inputmethod/latin/LatinIME.java
new file mode 100644
index 000000000..8671bf2e5
--- /dev/null
+++ b/src/com/android/inputmethod/latin/LatinIME.java
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+import android.text.ClipboardManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Input method implementation for Qwerty'ish keyboard.
+ */
+public class LatinIME extends InputMethodService
+ implements KeyboardView.OnKeyboardActionListener {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ private static final String PREF_VIBRATE_ON = "vibrate_on";
+ private static final String PREF_SOUND_ON = "sound_on";
+ private static final String PREF_PROXIMITY_CORRECTION = "hit_correction";
+ private static final String PREF_PREDICTION = "prediction_mode";
+ private static final String PREF_PREDICTION_LANDSCAPE = "prediction_landscape";
+ private static final String PREF_AUTO_CAP = "auto_cap";
+ static final String PREF_TUTORIAL_RUN = "tutorial_run";
+
+ private static final int MSG_UPDATE_SUGGESTIONS = 0;
+ private static final int MSG_CHECK_TUTORIAL = 1;
+
+ // How many continuous deletes at which to start deleting at a higher speed.
+ private static final int DELETE_ACCELERATE_AT = 20;
+ // Key events coming any faster than this are long-presses.
+ private static final int QUICK_PRESS = 200;
+
+ private static final int KEYCODE_ENTER = 10;
+ private static final int KEYCODE_SPACE = ' ';
+
+ // Contextual menu positions
+ private static final int POS_SETTINGS = 0;
+ private static final int POS_METHOD = 1;
+
+ private LatinKeyboardView mInputView;
+ private CandidateViewContainer mCandidateViewContainer;
+ private CandidateView mCandidateView;
+ private Suggest mSuggest;
+ private CompletionInfo[] mCompletions;
+
+ private AlertDialog mOptionsDialog;
+
+ private KeyboardSwitcher mKeyboardSwitcher;
+
+ private UserDictionary mUserDictionary;
+
+ private String mLocale;
+
+ private StringBuilder mComposing = new StringBuilder();
+ private WordComposer mWord = new WordComposer();
+ private int mCommittedLength;
+ private boolean mPredicting;
+ private CharSequence mBestWord;
+ private boolean mPredictionOn;
+ private boolean mCompletionOn;
+ private boolean mPasswordMode;
+ private boolean mAutoSpace;
+ private boolean mAutoCorrectOn;
+ private boolean mCapsLock;
+ private long mLastShiftTime;
+ private boolean mVibrateOn;
+ private boolean mSoundOn;
+ private boolean mProximityCorrection;
+ private int mCorrectionMode;
+ private boolean mAutoCap;
+ private boolean mAutoPunctuate;
+ private boolean mTutorialShownBefore;
+ // Indicates whether the suggestion strip is to be on in landscape
+ private boolean mShowSuggestInLand;
+ private boolean mJustAccepted;
+ private CharSequence mJustRevertedSeparator;
+ private int mDeleteCount;
+ private long mLastKeyTime;
+
+ private Tutorial mTutorial;
+
+ private Vibrator mVibrator;
+ private long mVibrateDuration;
+
+ private AudioManager mAudioManager;
+ private final float FX_VOLUME = 1.0f;
+ private boolean mSilentMode;
+
+ private String mWordSeparators;
+ private String mSentenceSeparators;
+
+ Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_SUGGESTIONS:
+ updateSuggestions();
+ break;
+ case MSG_CHECK_TUTORIAL:
+ if (!mTutorialShownBefore) {
+ mTutorial = new Tutorial(mInputView);
+ mTutorial.start();
+ }
+ break;
+ }
+ }
+ };
+
+ @Override public void onCreate() {
+ super.onCreate();
+ //setStatusIcon(R.drawable.ime_qwerty);
+ mKeyboardSwitcher = new KeyboardSwitcher(this);
+ initSuggest(getResources().getConfiguration().locale.toString());
+
+ mVibrateDuration = getResources().getInteger(R.integer.vibrate_duration_ms);
+
+ // register to receive ringer mode changes for silent mode
+ IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ registerReceiver(mReceiver, filter);
+ }
+
+ private void initSuggest(String locale) {
+ mLocale = locale;
+ mSuggest = new Suggest(this, R.raw.main);
+ mSuggest.setCorrectionMode(mCorrectionMode);
+ mUserDictionary = new UserDictionary(this);
+ mSuggest.setUserDictionary(mUserDictionary);
+ mWordSeparators = getResources().getString(R.string.word_separators);
+ mSentenceSeparators = getResources().getString(R.string.sentence_separators);
+ }
+
+ @Override public void onDestroy() {
+ mUserDictionary.close();
+ unregisterReceiver(mReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration conf) {
+ if (!TextUtils.equals(conf.locale.toString(), mLocale)) {
+ initSuggest(conf.locale.toString());
+ }
+ if (!mTutorialShownBefore && mTutorial != null) {
+ mTutorial.close(false);
+ }
+ super.onConfigurationChanged(conf);
+ }
+
+ @Override
+ public View onCreateInputView() {
+ mInputView = (LatinKeyboardView) getLayoutInflater().inflate(
+ R.layout.input, null);
+ mKeyboardSwitcher.setInputView(mInputView);
+ mKeyboardSwitcher.makeKeyboards();
+ mInputView.setOnKeyboardActionListener(this);
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT, 0);
+ return mInputView;
+ }
+
+ @Override
+ public View onCreateCandidatesView() {
+ mKeyboardSwitcher.makeKeyboards();
+ mCandidateViewContainer = (CandidateViewContainer) getLayoutInflater().inflate(
+ R.layout.candidates, null);
+ mCandidateViewContainer.initViews();
+ mCandidateView = (CandidateView) mCandidateViewContainer.findViewById(R.id.candidates);
+ mCandidateView.setService(this);
+ setCandidatesViewShown(true);
+ return mCandidateViewContainer;
+ }
+
+ @Override
+ public void onStartInputView(EditorInfo attribute, boolean restarting) {
+ // In landscape mode, this method gets called without the input view being created.
+ if (mInputView == null) {
+ return;
+ }
+
+ mKeyboardSwitcher.makeKeyboards();
+
+ TextEntryState.newSession(this);
+
+ mPredictionOn = false;
+ mCompletionOn = false;
+ mCompletions = null;
+ mCapsLock = false;
+ switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
+ case EditorInfo.TYPE_CLASS_NUMBER:
+ case EditorInfo.TYPE_CLASS_DATETIME:
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
+ attribute.imeOptions);
+ mKeyboardSwitcher.toggleSymbols();
+ break;
+ case EditorInfo.TYPE_CLASS_PHONE:
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_PHONE,
+ attribute.imeOptions);
+ break;
+ case EditorInfo.TYPE_CLASS_TEXT:
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
+ attribute.imeOptions);
+ //startPrediction();
+ mPredictionOn = true;
+ // Make sure that passwords are not displayed in candidate view
+ int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ) {
+ mPasswordMode = true;
+ mPredictionOn = false;
+ } else {
+ mPasswordMode = false;
+ }
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+ || variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) {
+ mAutoSpace = false;
+ } else {
+ mAutoSpace = true;
+ }
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
+ mPredictionOn = false;
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_EMAIL,
+ attribute.imeOptions);
+ } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
+ mPredictionOn = false;
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_URL,
+ attribute.imeOptions);
+ } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_IM,
+ attribute.imeOptions);
+ } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
+ mPredictionOn = false;
+ }
+ if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+ mPredictionOn = false;
+ mCompletionOn = true && isFullscreenMode();
+ }
+ updateShiftKeyState(attribute);
+ break;
+ default:
+ mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
+ attribute.imeOptions);
+ updateShiftKeyState(attribute);
+ }
+ mInputView.closing();
+ mComposing.setLength(0);
+ mPredicting = false;
+ mDeleteCount = 0;
+ setCandidatesViewShown(false);
+ if (mCandidateView != null) mCandidateView.setSuggestions(null, false, false, false);
+ loadSettings();
+ mInputView.setProximityCorrectionEnabled(mProximityCorrection);
+ if (mSuggest != null) {
+ mSuggest.setCorrectionMode(mCorrectionMode);
+ }
+ if (!mTutorialShownBefore && mTutorial == null) {
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_TUTORIAL, 1000);
+ }
+ mPredictionOn = mPredictionOn && mCorrectionMode > 0;
+ if (TRACE) Debug.startMethodTracing("latinime");
+ }
+
+ @Override
+ public void onFinishInput() {
+ super.onFinishInput();
+
+ if (mInputView != null) {
+ mInputView.closing();
+ }
+ if (!mTutorialShownBefore && mTutorial != null) {
+ mTutorial.close(false);
+ }
+ }
+
+ @Override
+ public void onUpdateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd) {
+ super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
+ candidatesStart, candidatesEnd);
+ // If the current selection in the text view changes, we should
+ // clear whatever candidate text we have.
+ if (mComposing.length() > 0 && mPredicting && (newSelStart != candidatesEnd
+ || newSelEnd != candidatesEnd)) {
+ mComposing.setLength(0);
+ mPredicting = false;
+ updateSuggestions();
+ TextEntryState.reset();
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ } else if (!mPredicting && !mJustAccepted
+ && TextEntryState.getState() == TextEntryState.STATE_ACCEPTED_DEFAULT) {
+ TextEntryState.reset();
+ }
+ mJustAccepted = false;
+ }
+
+ @Override
+ public void hideWindow() {
+ if (TRACE) Debug.stopMethodTracing();
+ super.hideWindow();
+ TextEntryState.endSession();
+ }
+
+ @Override
+ public void onDisplayCompletions(CompletionInfo[] completions) {
+ if (false) {
+ Log.i("foo", "Received completions:");
+ for (int i=0; i<(completions != null ? completions.length : 0); i++) {
+ Log.i("foo", " #" + i + ": " + completions[i]);
+ }
+ }
+ if (mCompletionOn) {
+ mCompletions = completions;
+ if (completions == null) {
+ mCandidateView.setSuggestions(null, false, false, false);
+ return;
+ }
+
+ List<CharSequence> stringList = new ArrayList<CharSequence>();
+ for (int i=0; i<(completions != null ? completions.length : 0); i++) {
+ CompletionInfo ci = completions[i];
+ if (ci != null) stringList.add(ci.getText());
+ }
+ //CharSequence typedWord = mWord.getTypedWord();
+ mCandidateView.setSuggestions(stringList, true, true, true);
+ mBestWord = null;
+ setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+ }
+ }
+
+ @Override
+ public void setCandidatesViewShown(boolean shown) {
+ // TODO: Remove this if we support candidates with hard keyboard
+ if (onEvaluateInputViewShown()) {
+ super.setCandidatesViewShown(shown);
+ }
+ }
+
+ @Override
+ public void onComputeInsets(InputMethodService.Insets outInsets) {
+ super.onComputeInsets(outInsets);
+ outInsets.contentTopInsets = outInsets.visibleTopInsets;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ if (event.getRepeatCount() == 0 && mInputView != null) {
+ if (mInputView.handleBack()) {
+ return true;
+ } else if (!mTutorialShownBefore && mTutorial != null) {
+ mTutorial.close(true);
+ }
+ }
+ break;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ // Enable shift key and DPAD to do selections
+ if (mInputView != null && mInputView.isShown() && mInputView.isShifted()) {
+ event = new KeyEvent(event.getDownTime(), event.getEventTime(),
+ event.getAction(), event.getKeyCode(), event.getRepeatCount(),
+ event.getDeviceId(), event.getScanCode(),
+ KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) ic.sendKeyEvent(event);
+ return true;
+ }
+ break;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ private void commitTyped(InputConnection inputConnection) {
+ if (mPredicting) {
+ mPredicting = false;
+ if (mComposing.length() > 0) {
+ if (inputConnection != null) {
+ inputConnection.commitText(mComposing, 1);
+ }
+ mCommittedLength = mComposing.length();
+ TextEntryState.acceptedTyped(mComposing);
+ }
+ updateSuggestions();
+ }
+ }
+
+ public void updateShiftKeyState(EditorInfo attr) {
+ InputConnection ic = getCurrentInputConnection();
+ if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode()
+ && ic != null) {
+ int caps = 0;
+ EditorInfo ei = getCurrentInputEditorInfo();
+ if (mAutoCap && ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
+ caps = ic.getCursorCapsMode(attr.inputType);
+ }
+ mInputView.setShifted(mCapsLock || caps != 0);
+ }
+ }
+
+ private void swapPunctuationAndSpace() {
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+ if (lastTwo != null && lastTwo.length() == 2
+ && lastTwo.charAt(0) == KEYCODE_SPACE && isSentenceSeparator(lastTwo.charAt(1))) {
+ ic.beginBatchEdit();
+ ic.deleteSurroundingText(2, 0);
+ ic.commitText(lastTwo.charAt(1) + " ", 1);
+ ic.endBatchEdit();
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+ }
+
+ private void doubleSpace() {
+ //if (!mAutoPunctuate) return;
+ if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
+ if (lastThree != null && lastThree.length() == 3
+ && Character.isLetterOrDigit(lastThree.charAt(0))
+ && lastThree.charAt(1) == KEYCODE_SPACE && lastThree.charAt(2) == KEYCODE_SPACE) {
+ ic.beginBatchEdit();
+ ic.deleteSurroundingText(2, 0);
+ ic.commitText(". ", 1);
+ ic.endBatchEdit();
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+ }
+
+ public boolean addWordToDictionary(String word) {
+ mUserDictionary.addWord(word, 128);
+ return true;
+ }
+
+ private boolean isAlphabet(int code) {
+ if (Character.isLetter(code)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Implementation of KeyboardViewListener
+
+ public void onKey(int primaryCode, int[] keyCodes) {
+ long when = SystemClock.uptimeMillis();
+ if (primaryCode != Keyboard.KEYCODE_DELETE ||
+ when > mLastKeyTime + QUICK_PRESS) {
+ mDeleteCount = 0;
+ }
+ mLastKeyTime = when;
+ switch (primaryCode) {
+ case Keyboard.KEYCODE_DELETE:
+ handleBackspace();
+ mDeleteCount++;
+ break;
+ case Keyboard.KEYCODE_SHIFT:
+ handleShift();
+ break;
+ case Keyboard.KEYCODE_CANCEL:
+ if (mOptionsDialog == null || !mOptionsDialog.isShowing()) {
+ handleClose();
+ }
+ break;
+ case LatinKeyboardView.KEYCODE_OPTIONS:
+ showOptionsMenu();
+ break;
+ case LatinKeyboardView.KEYCODE_SHIFT_LONGPRESS:
+ if (mCapsLock) {
+ handleShift();
+ } else {
+ toggleCapsLock();
+ }
+ break;
+ case Keyboard.KEYCODE_MODE_CHANGE:
+ changeKeyboardMode();
+ break;
+ default:
+ if (isWordSeparator(primaryCode)) {
+ handleSeparator(primaryCode);
+ } else {
+ handleCharacter(primaryCode, keyCodes);
+ }
+ // Cancel the just reverted state
+ mJustRevertedSeparator = null;
+ }
+ }
+
+ public void onText(CharSequence text) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ ic.beginBatchEdit();
+ if (mPredicting) {
+ commitTyped(ic);
+ }
+ ic.commitText(text, 1);
+ ic.endBatchEdit();
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ mJustRevertedSeparator = null;
+ }
+
+ private void handleBackspace() {
+ boolean deleteChar = false;
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ if (mPredicting) {
+ final int length = mComposing.length();
+ if (length > 0) {
+ mComposing.delete(length - 1, length);
+ mWord.deleteLast();
+ ic.setComposingText(mComposing, 1);
+ if (mComposing.length() == 0) {
+ mPredicting = false;
+ }
+ postUpdateSuggestions();
+ } else {
+ ic.deleteSurroundingText(1, 0);
+ }
+ } else {
+ //getCurrentInputConnection().deleteSurroundingText(1, 0);
+ deleteChar = true;
+ //sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ TextEntryState.backspace();
+ if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
+ revertLastWord(deleteChar);
+ return;
+ } else if (deleteChar) {
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+ if (mDeleteCount > DELETE_ACCELERATE_AT) {
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+ }
+ }
+ mJustRevertedSeparator = null;
+ }
+
+ private void handleShift() {
+ Keyboard currentKeyboard = mInputView.getKeyboard();
+ if (mKeyboardSwitcher.isAlphabetMode()) {
+ // Alphabet keyboard
+ checkToggleCapsLock();
+ mInputView.setShifted(mCapsLock || !mInputView.isShifted());
+ } else {
+ mKeyboardSwitcher.toggleShift();
+ }
+ }
+
+ private void handleCharacter(int primaryCode, int[] keyCodes) {
+ if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) {
+ if (!mPredicting) {
+ mPredicting = true;
+ mComposing.setLength(0);
+ mWord.reset();
+ }
+ }
+ if (mInputView.isShifted()) {
+ primaryCode = Character.toUpperCase(primaryCode);
+ }
+ if (mPredicting) {
+ if (mInputView.isShifted() && mComposing.length() == 0) {
+ mWord.setCapitalized(true);
+ }
+ mComposing.append((char) primaryCode);
+ mWord.add(primaryCode, keyCodes);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.setComposingText(mComposing, 1);
+ }
+ postUpdateSuggestions();
+ } else {
+ sendKeyChar((char)primaryCode);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ measureCps();
+ TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode));
+ }
+
+ private void handleSeparator(int primaryCode) {
+ boolean pickedDefault = false;
+ // Handle separator
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.beginBatchEdit();
+ }
+ if (mPredicting) {
+ // In certain languages where single quote is a separator, it's better
+ // not to auto correct, but accept the typed word. For instance,
+ // in Italian dov' should not be expanded to dove' because the elision
+ // requires the last vowel to be removed.
+ if (mAutoCorrectOn && primaryCode != '\'' &&
+ (mJustRevertedSeparator == null
+ || mJustRevertedSeparator.length() == 0
+ || mJustRevertedSeparator.charAt(0) != primaryCode)) {
+ pickDefaultSuggestion();
+ pickedDefault = true;
+ } else {
+ commitTyped(ic);
+ }
+ }
+ sendKeyChar((char)primaryCode);
+ TextEntryState.typedCharacter((char) primaryCode, true);
+ if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
+ && primaryCode != KEYCODE_ENTER) {
+ swapPunctuationAndSpace();
+ } else if (isPredictionOn() && primaryCode == ' ') {
+ //else if (TextEntryState.STATE_SPACE_AFTER_ACCEPTED) {
+ doubleSpace();
+ }
+ if (pickedDefault && mBestWord != null) {
+ TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ if (ic != null) {
+ ic.endBatchEdit();
+ }
+ }
+
+ private void handleClose() {
+ commitTyped(getCurrentInputConnection());
+ if (!mTutorialShownBefore && mTutorial != null) {
+ mTutorial.close(true);
+ }
+ requestHideSelf(0);
+ mInputView.closing();
+ TextEntryState.endSession();
+ }
+
+ private void checkToggleCapsLock() {
+ if (mInputView.getKeyboard().isShifted()) {
+ toggleCapsLock();
+ }
+ }
+
+ private void toggleCapsLock() {
+ mCapsLock = !mCapsLock;
+ if (mKeyboardSwitcher.isAlphabetMode()) {
+ ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+ }
+ }
+
+ private void postUpdateSuggestions() {
+ mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SUGGESTIONS), 100);
+ }
+
+ private boolean isPredictionOn() {
+ boolean predictionOn = mPredictionOn;
+ //if (isFullscreenMode()) predictionOn &= mPredictionLandscape;
+ return predictionOn;
+ }
+
+ private boolean isCandidateStripVisible() {
+ boolean visible = isPredictionOn() &&
+ (!isFullscreenMode() ||
+ mCorrectionMode == Suggest.CORRECTION_FULL ||
+ mShowSuggestInLand);
+ return visible;
+ }
+
+ private void updateSuggestions() {
+ // Check if we have a suggestion engine attached.
+ if (mSuggest == null || !isPredictionOn()) {
+ return;
+ }
+
+ if (!mPredicting) {
+ mCandidateView.setSuggestions(null, false, false, false);
+ return;
+ }
+
+ List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false);
+ boolean correctionAvailable = mSuggest.hasMinimalCorrection();
+ //|| mCorrectionMode == mSuggest.CORRECTION_FULL;
+ CharSequence typedWord = mWord.getTypedWord();
+ // If we're in basic correct
+ boolean typedWordValid = mSuggest.isValidWord(typedWord);
+ if (mCorrectionMode == Suggest.CORRECTION_FULL) {
+ correctionAvailable |= typedWordValid;
+ }
+
+ mCandidateView.setSuggestions(stringList, false, typedWordValid, correctionAvailable);
+ if (stringList.size() > 0) {
+ if (correctionAvailable && !typedWordValid && stringList.size() > 1) {
+ mBestWord = stringList.get(1);
+ } else {
+ mBestWord = typedWord;
+ }
+ } else {
+ mBestWord = null;
+ }
+ setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+ }
+
+ private void pickDefaultSuggestion() {
+ // Complete any pending candidate query first
+ if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
+ mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
+ updateSuggestions();
+ }
+ if (mBestWord != null) {
+ TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
+ mJustAccepted = true;
+ pickSuggestion(mBestWord);
+ }
+ }
+
+ public void pickSuggestionManually(int index, CharSequence suggestion) {
+ if (mCompletionOn && mCompletions != null && index >= 0
+ && index < mCompletions.length) {
+ CompletionInfo ci = mCompletions[index];
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.commitCompletion(ci);
+ }
+ mCommittedLength = suggestion.length();
+ if (mCandidateView != null) {
+ mCandidateView.clear();
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ return;
+ }
+ pickSuggestion(suggestion);
+ TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
+ // Follow it with a space
+ if (mAutoSpace) {
+ sendSpace();
+ }
+ // Fool the state watcher so that a subsequent backspace will not do a revert
+ TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
+ }
+
+ private void pickSuggestion(CharSequence suggestion) {
+ if (mCapsLock) {
+ suggestion = suggestion.toString().toUpperCase();
+ } else if (preferCapitalization()
+ || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted())) {
+ suggestion = Character.toUpperCase(suggestion.charAt(0))
+ + suggestion.subSequence(1, suggestion.length()).toString();
+ }
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.commitText(suggestion, 1);
+ }
+ mPredicting = false;
+ mCommittedLength = suggestion.length();
+ if (mCandidateView != null) {
+ mCandidateView.setSuggestions(null, false, false, false);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+
+ private boolean isCursorTouchingWord() {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return false;
+ CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
+ CharSequence toRight = ic.getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(toLeft)
+ && !isWordSeparator(toLeft.charAt(0))) {
+ return true;
+ }
+ if (!TextUtils.isEmpty(toRight)
+ && !isWordSeparator(toRight.charAt(0))) {
+ return true;
+ }
+ return false;
+ }
+
+ public void revertLastWord(boolean deleteChar) {
+ final int length = mComposing.length();
+ if (!mPredicting && length > 0) {
+ final InputConnection ic = getCurrentInputConnection();
+ mPredicting = true;
+ ic.beginBatchEdit();
+ mJustRevertedSeparator = ic.getTextBeforeCursor(1, 0);
+ if (deleteChar) ic.deleteSurroundingText(1, 0);
+ int toDelete = mCommittedLength;
+ CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
+ if (toTheLeft != null && toTheLeft.length() > 0
+ && isWordSeparator(toTheLeft.charAt(0))) {
+ toDelete--;
+ }
+ ic.deleteSurroundingText(toDelete, 0);
+ ic.setComposingText(mComposing, 1);
+ TextEntryState.backspace();
+ ic.endBatchEdit();
+ postUpdateSuggestions();
+ } else {
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+ mJustRevertedSeparator = null;
+ }
+ }
+
+ protected String getWordSeparators() {
+ return mWordSeparators;
+ }
+
+ public boolean isWordSeparator(int code) {
+ String separators = getWordSeparators();
+ return separators.contains(String.valueOf((char)code));
+ }
+
+ public boolean isSentenceSeparator(int code) {
+ return mSentenceSeparators.contains(String.valueOf((char)code));
+ }
+
+ private void sendSpace() {
+ sendKeyChar((char)KEYCODE_SPACE);
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ //onKey(KEY_SPACE[0], KEY_SPACE);
+ }
+
+ public boolean preferCapitalization() {
+ return mWord.isCapitalized();
+ }
+
+ public void swipeRight() {
+ if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
+ ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
+ CharSequence text = cm.getText();
+ if (!TextUtils.isEmpty(text)) {
+ mInputView.startPlaying(text.toString());
+ }
+ }
+// if (mAutoCorrectOn) {
+// commitTyped(getCurrentInputConnection());
+// } else if (mPredicting) {
+// pickDefaultSuggestion();
+// }
+// if (mAutoSpace) {
+// sendSpace();
+// }
+ }
+
+ public void swipeLeft() {
+ //handleBackspace();
+ }
+
+ public void swipeDown() {
+ //handleClose();
+ }
+
+ public void swipeUp() {
+ //launchSettings();
+ }
+
+ public void onPress(int primaryCode) {
+ vibrate();
+ playKeyClick(primaryCode);
+ }
+
+ public void onRelease(int primaryCode) {
+ //vibrate();
+ }
+
+ // receive ringer mode changes to detect silent mode
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateRingerMode();
+ }
+ };
+
+ // update flags for silent mode
+ private void updateRingerMode() {
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ }
+ if (mAudioManager != null) {
+ mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
+ }
+ }
+
+ private void playKeyClick(int primaryCode) {
+ // if mAudioManager is null, we don't have the ringer state yet
+ // mAudioManager will be set by updateRingerMode
+ if (mAudioManager == null) {
+ if (mInputView != null) {
+ updateRingerMode();
+ }
+ }
+ if (mSoundOn && !mSilentMode) {
+ // FIXME: Volume and enable should come from UI settings
+ // FIXME: These should be triggered after auto-repeat logic
+ int sound = AudioManager.FX_KEYPRESS_STANDARD;
+ switch (primaryCode) {
+ case Keyboard.KEYCODE_DELETE:
+ sound = AudioManager.FX_KEYPRESS_DELETE;
+ break;
+ case KEYCODE_ENTER:
+ sound = AudioManager.FX_KEYPRESS_RETURN;
+ break;
+ case KEYCODE_SPACE:
+ sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+ break;
+ }
+ mAudioManager.playSoundEffect(sound, FX_VOLUME);
+ }
+ }
+
+ private void vibrate() {
+ if (!mVibrateOn) {
+ return;
+ }
+ if (mVibrator == null) {
+ mVibrator = new Vibrator();
+ }
+ mVibrator.vibrate(mVibrateDuration);
+ }
+
+ private void launchSettings() {
+ handleClose();
+ Intent intent = new Intent();
+ intent.setClass(LatinIME.this, LatinIMESettings.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ private void loadSettings() {
+ // Get the settings preferences
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+ mProximityCorrection = sp.getBoolean(PREF_PROXIMITY_CORRECTION, true);
+ mVibrateOn = sp.getBoolean(PREF_VIBRATE_ON, true);
+ mSoundOn = sp.getBoolean(PREF_SOUND_ON, false);
+ String predictionBasic = getString(R.string.prediction_basic);
+ String mode = sp.getString(PREF_PREDICTION, predictionBasic);
+ if (mode.equals(getString(R.string.prediction_full))) {
+ mCorrectionMode = 2;
+ } else if (mode.equals(predictionBasic)) {
+ mCorrectionMode = 1;
+ } else {
+ mCorrectionMode = 0;
+ }
+ mAutoCorrectOn = mSuggest != null && mCorrectionMode > 0;
+
+ mAutoCap = sp.getBoolean(PREF_AUTO_CAP, true);
+ //mAutoPunctuate = sp.getBoolean(PREF_AUTO_PUNCTUATE, mCorrectionMode > 0);
+ mShowSuggestInLand = !sp.getBoolean(PREF_PREDICTION_LANDSCAPE, false);
+ mTutorialShownBefore = sp.getBoolean(PREF_TUTORIAL_RUN, false);
+ }
+
+ private void showOptionsMenu() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setCancelable(true);
+ builder.setIcon(R.drawable.ic_dialog_keyboard);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ CharSequence itemSettings = getString(R.string.english_ime_settings);
+ CharSequence itemInputMethod = getString(com.android.internal.R.string.inputMethod);
+ builder.setItems(new CharSequence[] {
+ itemSettings, itemInputMethod},
+ new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface di, int position) {
+ di.dismiss();
+ switch (position) {
+ case POS_SETTINGS:
+ launchSettings();
+ break;
+ case POS_METHOD:
+ InputMethodManager.getInstance(LatinIME.this).showInputMethodPicker();
+ break;
+ }
+ }
+ });
+ builder.setTitle(getResources().getString(R.string.english_ime_name));
+ mOptionsDialog = builder.create();
+ Window window = mOptionsDialog.getWindow();
+ WindowManager.LayoutParams lp = window.getAttributes();
+ lp.token = mInputView.getWindowToken();
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ window.setAttributes(lp);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ mOptionsDialog.show();
+ }
+
+ private void changeKeyboardMode() {
+ mKeyboardSwitcher.toggleSymbols();
+ if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
+ ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+ }
+
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ super.dump(fd, fout, args);
+
+ final Printer p = new PrintWriterPrinter(fout);
+ p.println("LatinIME state :");
+ p.println(" Keyboard mode = " + mKeyboardSwitcher.getKeyboardMode());
+ p.println(" mCapsLock=" + mCapsLock);
+ p.println(" mComposing=" + mComposing.toString());
+ p.println(" mPredictionOn=" + mPredictionOn);
+ p.println(" mCorrectionMode=" + mCorrectionMode);
+ p.println(" mPredicting=" + mPredicting);
+ p.println(" mAutoCorrectOn=" + mAutoCorrectOn);
+ p.println(" mAutoSpace=" + mAutoSpace);
+ p.println(" mCompletionOn=" + mCompletionOn);
+ p.println(" TextEntryState.state=" + TextEntryState.getState());
+ p.println(" mSoundOn=" + mSoundOn);
+ p.println(" mVibrateOn=" + mVibrateOn);
+ }
+
+
+ private static final int[] KEY_SPACE = { KEYCODE_SPACE };
+
+
+ // Characters per second measurement
+
+ private static final boolean PERF_DEBUG = false;
+ private long mLastCpsTime;
+ private static final int CPS_BUFFER_SIZE = 16;
+ private long[] mCpsIntervals = new long[CPS_BUFFER_SIZE];
+ private int mCpsIndex;
+
+ private void measureCps() {
+ if (!LatinIME.PERF_DEBUG) return;
+ long now = System.currentTimeMillis();
+ if (mLastCpsTime == 0) mLastCpsTime = now - 100; // Initial
+ mCpsIntervals[mCpsIndex] = now - mLastCpsTime;
+ mLastCpsTime = now;
+ mCpsIndex = (mCpsIndex + 1) % CPS_BUFFER_SIZE;
+ long total = 0;
+ for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i];
+ System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total));
+ }
+
+}
+
+
+
diff --git a/src/com/android/inputmethod/latin/LatinIMESettings.java b/src/com/android/inputmethod/latin/LatinIMESettings.java
new file mode 100644
index 000000000..2c23263ea
--- /dev/null
+++ b/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+
+public class LatinIMESettings extends PreferenceActivity
+ implements OnSharedPreferenceChangeListener{
+
+ private static final String CORRECTION_MODE_KEY = "prediction_mode";
+ private static final String PREDICTION_SETTINGS_KEY = "prediction_settings";
+ private static final String PREDICTION_LANDSCAPE_KEY = "prediction_landscape";
+
+ private ListPreference mCorrectionMode;
+ private PreferenceGroup mPredictionSettings;
+ private Preference mPredictionLandscape;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ addPreferencesFromResource(R.xml.prefs);
+ mCorrectionMode = (ListPreference) findPreference(CORRECTION_MODE_KEY);
+ mPredictionSettings = (PreferenceGroup) findPreference(PREDICTION_SETTINGS_KEY);
+ mPredictionLandscape = findPreference(PREDICTION_LANDSCAPE_KEY);
+ updatePredictionSettings();
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onDestroy() {
+ getPreferenceScreen().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ super.onDestroy();
+ }
+
+ private void updatePredictionSettings() {
+ if (mCorrectionMode != null && mPredictionSettings != null) {
+ String correctionMode = mCorrectionMode.getValue();
+ if (correctionMode.equals(getResources().getString(R.string.prediction_none))) {
+ mPredictionSettings.setEnabled(false);
+ } else {
+ mPredictionSettings.setEnabled(true);
+ boolean suggestionsInLandscape =
+ !correctionMode.equals(getResources().getString(R.string.prediction_full));
+ mPredictionLandscape.setEnabled(suggestionsInLandscape);
+ }
+ }
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ if (key.equals(CORRECTION_MODE_KEY)) {
+ updatePredictionSettings();
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/LatinKeyboard.java b/src/com/android/inputmethod/latin/LatinKeyboard.java
new file mode 100644
index 000000000..94b72b885
--- /dev/null
+++ b/src/com/android/inputmethod/latin/LatinKeyboard.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.Keyboard;
+import android.view.inputmethod.EditorInfo;
+
+public class LatinKeyboard extends Keyboard {
+
+ private Drawable mShiftLockIcon;
+ private Drawable mShiftLockPreviewIcon;
+ private Drawable mOldShiftIcon;
+ private Drawable mOldShiftPreviewIcon;
+ private Key mShiftKey;
+ private Key mEnterKey;
+
+ private static final int SHIFT_OFF = 0;
+ private static final int SHIFT_ON = 1;
+ private static final int SHIFT_LOCKED = 2;
+
+ private int mShiftState = SHIFT_OFF;
+
+ public LatinKeyboard(Context context, int xmlLayoutResId) {
+ this(context, xmlLayoutResId, 0);
+ }
+
+ public LatinKeyboard(Context context, int xmlLayoutResId, int mode) {
+ super(context, xmlLayoutResId, mode);
+ mShiftLockIcon = context.getResources()
+ .getDrawable(R.drawable.sym_keyboard_shift_locked);
+ mShiftLockPreviewIcon = context.getResources()
+ .getDrawable(R.drawable.sym_keyboard_feedback_shift_locked);
+ mShiftLockPreviewIcon.setBounds(0, 0,
+ mShiftLockPreviewIcon.getIntrinsicWidth(),
+ mShiftLockPreviewIcon.getIntrinsicHeight());
+ }
+
+ public LatinKeyboard(Context context, int layoutTemplateResId,
+ CharSequence characters, int columns, int horizontalPadding) {
+ super(context, layoutTemplateResId, characters, columns, horizontalPadding);
+ }
+
+ @Override
+ protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
+ XmlResourceParser parser) {
+ Key key = new LatinKey(res, parent, x, y, parser);
+ if (key.codes[0] == 10) {
+ mEnterKey = key;
+ }
+ return key;
+ }
+
+ void setImeOptions(Resources res, int mode, int options) {
+ if (mEnterKey != null) {
+ // Reset some of the rarely used attributes.
+ mEnterKey.popupCharacters = null;
+ mEnterKey.popupResId = 0;
+ mEnterKey.text = null;
+ switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
+ case EditorInfo.IME_ACTION_GO:
+ mEnterKey.iconPreview = null;
+ mEnterKey.icon = null;
+ mEnterKey.label = res.getText(R.string.label_go_key);
+ break;
+ case EditorInfo.IME_ACTION_NEXT:
+ mEnterKey.iconPreview = null;
+ mEnterKey.icon = null;
+ mEnterKey.label = res.getText(R.string.label_next_key);
+ break;
+ case EditorInfo.IME_ACTION_DONE:
+ mEnterKey.iconPreview = null;
+ mEnterKey.icon = null;
+ mEnterKey.label = res.getText(R.string.label_done_key);
+ break;
+ case EditorInfo.IME_ACTION_SEARCH:
+ mEnterKey.iconPreview = res.getDrawable(
+ R.drawable.sym_keyboard_feedback_search);
+ mEnterKey.icon = res.getDrawable(
+ R.drawable.sym_keyboard_search);
+ mEnterKey.label = null;
+ break;
+ case EditorInfo.IME_ACTION_SEND:
+ mEnterKey.iconPreview = null;
+ mEnterKey.icon = null;
+ mEnterKey.label = res.getText(R.string.label_send_key);
+ break;
+ default:
+ if (mode == KeyboardSwitcher.MODE_IM) {
+ mEnterKey.icon = null;
+ mEnterKey.iconPreview = null;
+ mEnterKey.label = ":-)";
+ mEnterKey.text = ":-) ";
+ mEnterKey.popupResId = R.xml.popup_smileys;
+ } else {
+ mEnterKey.iconPreview = res.getDrawable(
+ R.drawable.sym_keyboard_feedback_return);
+ mEnterKey.icon = res.getDrawable(
+ R.drawable.sym_keyboard_return);
+ mEnterKey.label = null;
+ }
+ break;
+ }
+ // Set the initial size of the preview icon
+ if (mEnterKey.iconPreview != null) {
+ mEnterKey.iconPreview.setBounds(0, 0,
+ mEnterKey.iconPreview.getIntrinsicWidth(),
+ mEnterKey.iconPreview.getIntrinsicHeight());
+ }
+ }
+ }
+
+ void enableShiftLock() {
+ int index = getShiftKeyIndex();
+ if (index >= 0) {
+ mShiftKey = getKeys().get(index);
+ if (mShiftKey instanceof LatinKey) {
+ ((LatinKey)mShiftKey).enableShiftLock();
+ }
+ mOldShiftIcon = mShiftKey.icon;
+ mOldShiftPreviewIcon = mShiftKey.iconPreview;
+ }
+ }
+
+ void setShiftLocked(boolean shiftLocked) {
+ if (mShiftKey != null) {
+ if (shiftLocked) {
+ mShiftKey.on = true;
+ mShiftKey.icon = mShiftLockIcon;
+ mShiftState = SHIFT_LOCKED;
+ } else {
+ mShiftKey.on = false;
+ mShiftKey.icon = mShiftLockIcon;
+ mShiftState = SHIFT_ON;
+ }
+ }
+ }
+
+ boolean isShiftLocked() {
+ return mShiftState == SHIFT_LOCKED;
+ }
+
+ @Override
+ public boolean setShifted(boolean shiftState) {
+ boolean shiftChanged = false;
+ if (mShiftKey != null) {
+ if (shiftState == false) {
+ shiftChanged = mShiftState != SHIFT_OFF;
+ mShiftState = SHIFT_OFF;
+ mShiftKey.on = false;
+ mShiftKey.icon = mOldShiftIcon;
+ } else {
+ if (mShiftState == SHIFT_OFF) {
+ shiftChanged = mShiftState == SHIFT_OFF;
+ mShiftState = SHIFT_ON;
+ mShiftKey.icon = mShiftLockIcon;
+ }
+ }
+ } else {
+ return super.setShifted(shiftState);
+ }
+ return shiftChanged;
+ }
+
+ @Override
+ public boolean isShifted() {
+ if (mShiftKey != null) {
+ return mShiftState != SHIFT_OFF;
+ } else {
+ return super.isShifted();
+ }
+ }
+
+ static class LatinKey extends Keyboard.Key {
+
+ private boolean mShiftLockEnabled;
+
+ public LatinKey(Resources res, Keyboard.Row parent, int x, int y,
+ XmlResourceParser parser) {
+ super(res, parent, x, y, parser);
+ }
+
+ void enableShiftLock() {
+ mShiftLockEnabled = true;
+ }
+
+ @Override
+ public void onReleased(boolean inside) {
+ if (!mShiftLockEnabled) {
+ super.onReleased(inside);
+ } else {
+ pressed = !pressed;
+ }
+ }
+
+ /**
+ * Overriding this method so that we can reduce the target area for certain keys.
+ */
+ @Override
+ public boolean isInside(int x, int y) {
+ if ((edgeFlags & Keyboard.EDGE_BOTTOM) != 0 ||
+ codes[0] == KEYCODE_SHIFT ||
+ codes[0] == KEYCODE_DELETE) {
+ y -= height / 10;
+ }
+ if (codes[0] == KEYCODE_SHIFT) x += width / 6;
+ if (codes[0] == KEYCODE_DELETE) x -= width / 6;
+ return super.isInside(x, y);
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/LatinKeyboardView.java b/src/com/android/inputmethod/latin/LatinKeyboardView.java
new file mode 100644
index 000000000..363dcd0b0
--- /dev/null
+++ b/src/com/android/inputmethod/latin/LatinKeyboardView.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.inputmethodservice.Keyboard.Key;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import java.util.List;
+
+public class LatinKeyboardView extends KeyboardView {
+
+ static final int KEYCODE_OPTIONS = -100;
+ static final int KEYCODE_SHIFT_LONGPRESS = -101;
+
+ private Keyboard mPhoneKeyboard;
+
+ public LatinKeyboardView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setPhoneKeyboard(Keyboard phoneKeyboard) {
+ mPhoneKeyboard = phoneKeyboard;
+ }
+
+ @Override
+ protected boolean onLongPress(Key key) {
+ if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) {
+ getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null);
+ return true;
+ } else if (key.codes[0] == Keyboard.KEYCODE_SHIFT) {
+ getOnKeyboardActionListener().onKey(KEYCODE_SHIFT_LONGPRESS, null);
+ invalidate();
+ return true;
+ } else if (key.codes[0] == '0' && getKeyboard() == mPhoneKeyboard) {
+ // Long pressing on 0 in phone number keypad gives you a '+'.
+ getOnKeyboardActionListener().onKey('+', null);
+ return true;
+ } else {
+ return super.onLongPress(key);
+ }
+ }
+
+
+ /**************************** INSTRUMENTATION *******************************/
+
+ static final boolean DEBUG_AUTO_PLAY = false;
+ private static final int MSG_TOUCH_DOWN = 1;
+ private static final int MSG_TOUCH_UP = 2;
+
+ Handler mHandler2;
+
+ private String mStringToPlay;
+ private int mStringIndex;
+ private boolean mDownDelivered;
+ private Key[] mAsciiKeys = new Key[256];
+ private boolean mPlaying;
+
+ @Override
+ public void setKeyboard(Keyboard k) {
+ super.setKeyboard(k);
+ if (DEBUG_AUTO_PLAY) {
+ findKeys();
+ if (mHandler2 == null) {
+ mHandler2 = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ removeMessages(MSG_TOUCH_DOWN);
+ removeMessages(MSG_TOUCH_UP);
+ if (mPlaying == false) return;
+
+ switch (msg.what) {
+ case MSG_TOUCH_DOWN:
+ if (mStringIndex >= mStringToPlay.length()) {
+ mPlaying = false;
+ return;
+ }
+ char c = mStringToPlay.charAt(mStringIndex);
+ while (c > 255 || mAsciiKeys[(int) c] == null) {
+ mStringIndex++;
+ if (mStringIndex >= mStringToPlay.length()) {
+ mPlaying = false;
+ return;
+ }
+ c = mStringToPlay.charAt(mStringIndex);
+ }
+ int x = mAsciiKeys[c].x + 10;
+ int y = mAsciiKeys[c].y + 26;
+ MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_DOWN, x, y, 0);
+ LatinKeyboardView.this.dispatchTouchEvent(me);
+ me.recycle();
+ sendEmptyMessageDelayed(MSG_TOUCH_UP, 500); // Deliver up in 500ms if nothing else
+ // happens
+ mDownDelivered = true;
+ break;
+ case MSG_TOUCH_UP:
+ char cUp = mStringToPlay.charAt(mStringIndex);
+ int x2 = mAsciiKeys[cUp].x + 10;
+ int y2 = mAsciiKeys[cUp].y + 26;
+ mStringIndex++;
+
+ MotionEvent me2 = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_UP, x2, y2, 0);
+ LatinKeyboardView.this.dispatchTouchEvent(me2);
+ me2.recycle();
+ sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 500); // Deliver up in 500ms if nothing else
+ // happens
+ mDownDelivered = false;
+ break;
+ }
+ }
+ };
+
+ }
+ }
+ }
+
+ private void findKeys() {
+ List<Key> keys = getKeyboard().getKeys();
+ // Get the keys on this keyboard
+ for (int i = 0; i < keys.size(); i++) {
+ int code = keys.get(i).codes[0];
+ if (code >= 0 && code <= 255) {
+ mAsciiKeys[code] = keys.get(i);
+ }
+ }
+ }
+
+ void startPlaying(String s) {
+ if (!DEBUG_AUTO_PLAY) return;
+ if (s == null) return;
+ mStringToPlay = s.toLowerCase();
+ mPlaying = true;
+ mDownDelivered = false;
+ mStringIndex = 0;
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10);
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ super.draw(c);
+ if (DEBUG_AUTO_PLAY && mPlaying) {
+ mHandler2.removeMessages(MSG_TOUCH_DOWN);
+ mHandler2.removeMessages(MSG_TOUCH_UP);
+ if (mDownDelivered) {
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20);
+ } else {
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
+ }
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/Suggest.java b/src/com/android/inputmethod/latin/Suggest.java
new file mode 100755
index 000000000..91decd66a
--- /dev/null
+++ b/src/com/android/inputmethod/latin/Suggest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.text.AutoText;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This class loads a dictionary and provides a list of suggestions for a given sequence of
+ * characters. This includes corrections and completions.
+ * @hide pending API Council Approval
+ */
+public class Suggest implements Dictionary.WordCallback {
+
+ public static final int CORRECTION_NONE = 0;
+ public static final int CORRECTION_BASIC = 1;
+ public static final int CORRECTION_FULL = 2;
+
+ private Dictionary mMainDict;
+
+ private Dictionary mUserDictionary;
+
+ private int mPrefMaxSuggestions = 12;
+
+ private int[] mPriorities = new int[mPrefMaxSuggestions];
+ private List<CharSequence> mSuggestions = new ArrayList<CharSequence>();
+ private boolean mIncludeTypedWordIfValid;
+ private List<CharSequence> mStringPool = new ArrayList<CharSequence>();
+ private Context mContext;
+ private boolean mHaveCorrection;
+ private CharSequence mOriginalWord;
+ private String mLowerOriginalWord;
+
+ private int mCorrectionMode = CORRECTION_BASIC;
+
+
+ public Suggest(Context context, int dictionaryResId) {
+ mContext = context;
+ mMainDict = new BinaryDictionary(context, dictionaryResId);
+ for (int i = 0; i < mPrefMaxSuggestions; i++) {
+ StringBuilder sb = new StringBuilder(32);
+ mStringPool.add(sb);
+ }
+ }
+
+ public int getCorrectionMode() {
+ return mCorrectionMode;
+ }
+
+ public void setCorrectionMode(int mode) {
+ mCorrectionMode = mode;
+ }
+
+ /**
+ * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
+ * before the main dictionary, if set.
+ */
+ public void setUserDictionary(Dictionary userDictionary) {
+ mUserDictionary = userDictionary;
+ }
+
+ /**
+ * Number of suggestions to generate from the input key sequence. This has
+ * to be a number between 1 and 100 (inclusive).
+ * @param maxSuggestions
+ * @throws IllegalArgumentException if the number is out of range
+ */
+ public void setMaxSuggestions(int maxSuggestions) {
+ if (maxSuggestions < 1 || maxSuggestions > 100) {
+ throw new IllegalArgumentException("maxSuggestions must be between 1 and 100");
+ }
+ mPrefMaxSuggestions = maxSuggestions;
+ mPriorities = new int[mPrefMaxSuggestions];
+ collectGarbage();
+ while (mStringPool.size() < mPrefMaxSuggestions) {
+ StringBuilder sb = new StringBuilder(32);
+ mStringPool.add(sb);
+ }
+ }
+
+ private boolean haveSufficientCommonality(String original, CharSequence suggestion) {
+ final int len = Math.min(original.length(), suggestion.length());
+ if (len <= 2) return true;
+ int matching = 0;
+ for (int i = 0; i < len; i++) {
+ if (UserDictionary.toLowerCase(original.charAt(i))
+ == UserDictionary.toLowerCase(suggestion.charAt(i))) {
+ matching++;
+ }
+ }
+ if (len <= 4) {
+ return matching >= 2;
+ } else {
+ return matching > len / 2;
+ }
+ }
+
+ /**
+ * Returns a list of words that match the list of character codes passed in.
+ * This list will be overwritten the next time this function is called.
+ * @param a view for retrieving the context for AutoText
+ * @param codes the list of codes. Each list item contains an array of character codes
+ * in order of probability where the character at index 0 in the array has the highest
+ * probability.
+ * @return list of suggestions.
+ */
+ public List<CharSequence> getSuggestions(View view, WordComposer wordComposer,
+ boolean includeTypedWordIfValid) {
+ mHaveCorrection = false;
+ collectGarbage();
+ Arrays.fill(mPriorities, 0);
+ mIncludeTypedWordIfValid = includeTypedWordIfValid;
+
+ // Save a lowercase version of the original word
+ mOriginalWord = wordComposer.getTypedWord();
+ if (mOriginalWord != null) {
+ mOriginalWord = mOriginalWord.toString();
+ mLowerOriginalWord = mOriginalWord.toString().toLowerCase();
+ } else {
+ mLowerOriginalWord = "";
+ }
+ // Search the dictionary only if there are at least 2 characters
+ if (wordComposer.size() > 1) {
+ if (mUserDictionary != null) {
+ mUserDictionary.getWords(wordComposer, this);
+ if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)) {
+ mHaveCorrection = true;
+ }
+ }
+ mMainDict.getWords(wordComposer, this);
+ if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 0) {
+ mHaveCorrection = true;
+ }
+ }
+ if (mOriginalWord != null) {
+ mSuggestions.add(0, mOriginalWord.toString());
+ }
+
+ // Check if the first suggestion has a minimum number of characters in common
+ if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 1) {
+ if (!haveSufficientCommonality(mLowerOriginalWord, mSuggestions.get(1))) {
+ mHaveCorrection = false;
+ }
+ }
+
+ int i = 0;
+ int max = 6;
+ // Don't autotext the suggestions from the dictionaries
+ if (mCorrectionMode == CORRECTION_BASIC) max = 1;
+ while (i < mSuggestions.size() && i < max) {
+ String suggestedWord = mSuggestions.get(i).toString().toLowerCase();
+ CharSequence autoText =
+ AutoText.get(suggestedWord, 0, suggestedWord.length(), view);
+ // Is there an AutoText correction?
+ boolean canAdd = autoText != null;
+ // Is that correction already the current prediction (or original word)?
+ canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i));
+ // Is that correction already the next predicted word?
+ if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) {
+ canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1));
+ }
+ if (canAdd) {
+ mHaveCorrection = true;
+ mSuggestions.add(i + 1, autoText);
+ i++;
+ }
+ i++;
+ }
+
+ return mSuggestions;
+ }
+
+ public boolean hasMinimalCorrection() {
+ return mHaveCorrection;
+ }
+
+ private boolean compareCaseInsensitive(final String mLowerOriginalWord,
+ final char[] word, final int offset, final int length) {
+ final int originalLength = mLowerOriginalWord.length();
+ if (originalLength == length && Character.isUpperCase(word[offset])) {
+ for (int i = 0; i < originalLength; i++) {
+ if (mLowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addWord(final char[] word, final int offset, final int length, final int freq) {
+ int pos = 0;
+ final int[] priorities = mPriorities;
+ final int prefMaxSuggestions = mPrefMaxSuggestions;
+ // Check if it's the same word, only caps are different
+ if (compareCaseInsensitive(mLowerOriginalWord, word, offset, length)) {
+ pos = 0;
+ } else {
+ // Check the last one's priority and bail
+ if (priorities[prefMaxSuggestions - 1] >= freq) return true;
+ while (pos < prefMaxSuggestions) {
+ if (priorities[pos] < freq
+ || (priorities[pos] == freq && length < mSuggestions
+ .get(pos).length())) {
+ break;
+ }
+ pos++;
+ }
+ }
+
+ if (pos >= prefMaxSuggestions) {
+ return true;
+ }
+ System.arraycopy(priorities, pos, priorities, pos + 1,
+ prefMaxSuggestions - pos - 1);
+ priorities[pos] = freq;
+ int poolSize = mStringPool.size();
+ StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
+ : new StringBuilder(32);
+ sb.setLength(0);
+ sb.append(word, offset, length);
+ mSuggestions.add(pos, sb);
+ if (mSuggestions.size() > prefMaxSuggestions) {
+ CharSequence garbage = mSuggestions.remove(prefMaxSuggestions);
+ if (garbage instanceof StringBuilder) {
+ mStringPool.add(garbage);
+ }
+ }
+ return true;
+ }
+
+ public boolean isValidWord(final CharSequence word) {
+ if (word == null || word.length() == 0) {
+ return false;
+ }
+ return (mCorrectionMode == CORRECTION_FULL && mMainDict.isValidWord(word))
+ || (mCorrectionMode > CORRECTION_NONE &&
+ (mUserDictionary != null && mUserDictionary.isValidWord(word)));
+ }
+
+ private void collectGarbage() {
+ int poolSize = mStringPool.size();
+ int garbageSize = mSuggestions.size();
+ while (poolSize < mPrefMaxSuggestions && garbageSize > 0) {
+ CharSequence garbage = mSuggestions.get(garbageSize - 1);
+ if (garbage != null && garbage instanceof StringBuilder) {
+ mStringPool.add(garbage);
+ poolSize++;
+ }
+ garbageSize--;
+ }
+ if (poolSize == mPrefMaxSuggestions + 1) {
+ Log.w("Suggest", "String pool got too big: " + poolSize);
+ }
+ mSuggestions.clear();
+ }
+}
diff --git a/src/com/android/inputmethod/latin/TextEntryState.java b/src/com/android/inputmethod/latin/TextEntryState.java
new file mode 100644
index 000000000..90c364a1c
--- /dev/null
+++ b/src/com/android/inputmethod/latin/TextEntryState.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import android.inputmethodservice.Keyboard.Key;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+
+public class TextEntryState {
+
+ private static boolean LOGGING = false;
+
+ private static int sBackspaceCount = 0;
+
+ private static int sAutoSuggestCount = 0;
+
+ private static int sAutoSuggestUndoneCount = 0;
+
+ private static int sManualSuggestCount = 0;
+
+ private static int sWordNotInDictionaryCount = 0;
+
+ private static int sSessionCount = 0;
+
+ private static int sTypedChars;
+
+ private static int sActualChars;
+
+ private static final String[] STATES = {
+ "Unknown",
+ "Start",
+ "In word",
+ "Accepted default",
+ "Picked suggestion",
+ "Punc. after word",
+ "Punc. after accepted",
+ "Space after accepted",
+ "Space after picked",
+ "Undo commit"
+ };
+
+ public static final int STATE_UNKNOWN = 0;
+ public static final int STATE_START = 1;
+ public static final int STATE_IN_WORD = 2;
+ public static final int STATE_ACCEPTED_DEFAULT = 3;
+ public static final int STATE_PICKED_SUGGESTION = 4;
+ public static final int STATE_PUNCTUATION_AFTER_WORD = 5;
+ public static final int STATE_PUNCTUATION_AFTER_ACCEPTED = 6;
+ public static final int STATE_SPACE_AFTER_ACCEPTED = 7;
+ public static final int STATE_SPACE_AFTER_PICKED = 8;
+ public static final int STATE_UNDO_COMMIT = 9;
+
+ private static int sState = STATE_UNKNOWN;
+
+ private static FileOutputStream sKeyLocationFile;
+ private static FileOutputStream sUserActionFile;
+
+ public static void newSession(Context context) {
+ sSessionCount++;
+ sAutoSuggestCount = 0;
+ sBackspaceCount = 0;
+ sAutoSuggestUndoneCount = 0;
+ sManualSuggestCount = 0;
+ sWordNotInDictionaryCount = 0;
+ sTypedChars = 0;
+ sActualChars = 0;
+ sState = STATE_START;
+
+ if (LOGGING) {
+ try {
+ sKeyLocationFile = context.openFileOutput("key.txt", Context.MODE_APPEND);
+ sUserActionFile = context.openFileOutput("action.txt", Context.MODE_APPEND);
+ } catch (IOException ioe) {
+ Log.e("TextEntryState", "Couldn't open file for output: " + ioe);
+ }
+ }
+ }
+
+ public static void endSession() {
+ if (sKeyLocationFile == null) {
+ return;
+ }
+ try {
+ sKeyLocationFile.close();
+ // Write to log file
+ // Write timestamp, settings,
+ String out = DateFormat.format("MM:dd hh:mm:ss", Calendar.getInstance().getTime())
+ .toString()
+ + " BS: " + sBackspaceCount
+ + " auto: " + sAutoSuggestCount
+ + " manual: " + sManualSuggestCount
+ + " typed: " + sWordNotInDictionaryCount
+ + " undone: " + sAutoSuggestUndoneCount
+ + " saved: " + ((float) (sActualChars - sTypedChars) / sActualChars)
+ + "\n";
+ sUserActionFile.write(out.getBytes());
+ sUserActionFile.close();
+ sKeyLocationFile = null;
+ sUserActionFile = null;
+ } catch (IOException ioe) {
+
+ }
+ }
+
+ public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord) {
+ if (!typedWord.equals(actualWord)) {
+ sAutoSuggestCount++;
+ }
+ sTypedChars += typedWord.length();
+ sActualChars += actualWord.length();
+ sState = STATE_ACCEPTED_DEFAULT;
+ }
+
+ public static void acceptedTyped(CharSequence typedWord) {
+ sWordNotInDictionaryCount++;
+ sState = STATE_PICKED_SUGGESTION;
+ }
+
+ public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) {
+ sManualSuggestCount++;
+ if (typedWord.equals(actualWord)) {
+ acceptedTyped(typedWord);
+ }
+ sState = STATE_PICKED_SUGGESTION;
+ }
+
+ public static void typedCharacter(char c, boolean isSeparator) {
+ boolean isSpace = c == ' ';
+ switch (sState) {
+ case STATE_IN_WORD:
+ if (isSpace || isSeparator) {
+ sState = STATE_START;
+ } else {
+ // State hasn't changed.
+ }
+ break;
+ case STATE_ACCEPTED_DEFAULT:
+ case STATE_SPACE_AFTER_PICKED:
+ if (isSpace) {
+ sState = STATE_SPACE_AFTER_ACCEPTED;
+ } else if (isSeparator) {
+ sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
+ } else {
+ sState = STATE_IN_WORD;
+ }
+ break;
+ case STATE_PICKED_SUGGESTION:
+ if (isSpace) {
+ sState = STATE_SPACE_AFTER_PICKED;
+ } else if (isSeparator) {
+ // Swap
+ sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
+ } else {
+ sState = STATE_IN_WORD;
+ }
+ break;
+ case STATE_START:
+ case STATE_UNKNOWN:
+ case STATE_SPACE_AFTER_ACCEPTED:
+ case STATE_PUNCTUATION_AFTER_ACCEPTED:
+ case STATE_PUNCTUATION_AFTER_WORD:
+ if (!isSpace && !isSeparator) {
+ sState = STATE_IN_WORD;
+ } else {
+ sState = STATE_START;
+ }
+ break;
+ case STATE_UNDO_COMMIT:
+ if (isSpace || isSeparator) {
+ sState = STATE_ACCEPTED_DEFAULT;
+ } else {
+ sState = STATE_IN_WORD;
+ }
+ }
+ }
+
+ public static void backspace() {
+ if (sState == STATE_ACCEPTED_DEFAULT) {
+ sState = STATE_UNDO_COMMIT;
+ sAutoSuggestUndoneCount++;
+ } else if (sState == STATE_UNDO_COMMIT) {
+ sState = STATE_IN_WORD;
+ }
+ sBackspaceCount++;
+ }
+
+ public static void reset() {
+ sState = STATE_START;
+ }
+
+ public static int getState() {
+ return sState;
+ }
+
+ public static void keyPressedAt(Key key, int x, int y) {
+ if (LOGGING && sKeyLocationFile != null && key.codes[0] >= 32) {
+ String out =
+ "KEY: " + (char) key.codes[0]
+ + " X: " + x
+ + " Y: " + y
+ + " MX: " + (key.x + key.width / 2)
+ + " MY: " + (key.y + key.height / 2)
+ + "\n";
+ try {
+ sKeyLocationFile.write(out.getBytes());
+ } catch (IOException ioe) {
+ // TODO: May run out of space
+ }
+ }
+ }
+}
+
diff --git a/src/com/android/inputmethod/latin/Tutorial.java b/src/com/android/inputmethod/latin/Tutorial.java
new file mode 100644
index 000000000..2b3138bf9
--- /dev/null
+++ b/src/com/android/inputmethod/latin/Tutorial.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.graphics.drawable.Drawable;
+import android.opengl.Visibility;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Tutorial {
+
+ private List<Bubble> mBubbles = new ArrayList<Bubble>();
+ private long mStartTime;
+ private static final long MINIMUM_TIME = 6000;
+ private static final long MAXIMUM_TIME = 20000;
+ private View mInputView;
+ private int[] mLocation = new int[2];
+ private int mBubblePointerOffset;
+
+ private static final int MSG_SHOW_BUBBLE = 0;
+ private static final int MSG_HIDE_ALL = 1;
+
+ Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SHOW_BUBBLE:
+ Bubble bubba = (Bubble) msg.obj;
+ bubba.show(mLocation[0], mLocation[1]);
+ break;
+ case MSG_HIDE_ALL:
+ close(true);
+ }
+ }
+ };
+
+ class Bubble {
+ Drawable bubbleBackground;
+ int x;
+ int y;
+ int width;
+ int gravity;
+ String text;
+ boolean dismissOnTouch;
+ boolean dismissOnClose;
+ PopupWindow window;
+ TextView textView;
+ View inputView;
+
+ Bubble(Context context, View inputView,
+ int backgroundResource, int bx, int by, int bw, int gravity, int textResource,
+ boolean dismissOnTouch, boolean dismissOnClose) {
+ bubbleBackground = context.getResources().getDrawable(backgroundResource);
+ x = bx;
+ y = by;
+ width = bw;
+ this.gravity = gravity;
+ text = context.getResources().getString(textResource);
+ this.dismissOnTouch = dismissOnTouch;
+ this.dismissOnClose = dismissOnClose;
+ this.inputView = inputView;
+ window = new PopupWindow(context);
+ window.setBackgroundDrawable(null);
+ LayoutInflater inflate =
+ (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ textView = (TextView) inflate.inflate(R.layout.bubble_text, null);
+ textView.setBackgroundDrawable(bubbleBackground);
+ textView.setText(text);
+ window.setContentView(textView);
+ window.setFocusable(false);
+ window.setTouchable(true);
+ window.setOutsideTouchable(false);
+ textView.setOnTouchListener(new View.OnTouchListener() {
+ public boolean onTouch(View view, MotionEvent me) {
+ Tutorial.this.touched();
+ return true;
+ }
+ });
+ }
+
+ private void chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv) {
+ int wid = tv.getPaddingLeft() + tv.getPaddingRight();
+ int ht = tv.getPaddingTop() + tv.getPaddingBottom();
+
+ /*
+ * Figure out how big the text would be if we laid it out to the
+ * full width of this view minus the border.
+ */
+ int cap = width - wid;
+
+ Layout l = new StaticLayout(text, tv.getPaint(), cap,
+ Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
+ float max = 0;
+ for (int i = 0; i < l.getLineCount(); i++) {
+ max = Math.max(max, l.getLineWidth(i));
+ }
+
+ /*
+ * Now set the popup size to be big enough for the text plus the border.
+ */
+ pop.setWidth(width);
+ pop.setHeight(ht + l.getHeight());
+ }
+
+ void show(int offx, int offy) {
+ chooseSize(window, inputView, text, textView);
+ if (inputView.getVisibility() == View.VISIBLE
+ && inputView.getWindowVisibility() == View.VISIBLE) {
+ try {
+ if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
+ if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
+ window.showAtLocation(inputView, Gravity.NO_GRAVITY, x + offx, y + offy);
+ } catch (Exception e) {
+ // Input view is not valid
+ }
+ }
+ }
+
+ void hide() {
+ textView.setOnTouchListener(null);
+ if (window.isShowing()) {
+ window.dismiss();
+ }
+ }
+ }
+
+ public Tutorial(LatinKeyboardView inputView) {
+ Context context = inputView.getContext();
+ int inputHeight = inputView.getHeight();
+ int inputWidth = inputView.getWidth();
+ mBubblePointerOffset = inputView.getContext().getResources()
+ .getDimensionPixelOffset(R.dimen.bubble_pointer_offset);
+ Bubble b0 = new Bubble(context, inputView,
+ R.drawable.dialog_bubble_step02, 0, 0,
+ inputWidth,
+ Gravity.BOTTOM | Gravity.LEFT,
+ R.string.tip_dismiss,
+ false, true);
+ mBubbles.add(b0);
+ Bubble b1 = new Bubble(context, inputView,
+ R.drawable.dialog_bubble_step03,
+ (int) (inputWidth * 0.85) + mBubblePointerOffset, inputHeight / 5,
+ (int) (inputWidth * 0.45),
+ Gravity.TOP | Gravity.RIGHT,
+ R.string.tip_long_press,
+ true, false);
+ mBubbles.add(b1);
+ Bubble b2 = new Bubble(inputView.getContext(), inputView,
+ R.drawable.dialog_bubble_step04,
+ inputWidth / 10 - mBubblePointerOffset, inputHeight - inputHeight / 5,
+ (int) (inputWidth * 0.45),
+ Gravity.BOTTOM | Gravity.LEFT,
+ R.string.tip_access_symbols,
+ true, false);
+ mBubbles.add(b2);
+ mInputView = inputView;
+ }
+
+ void start() {
+ mInputView.getLocationInWindow(mLocation);
+ long delayMillis = 0;
+ for (int i = 0; i < mBubbles.size(); i++) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_BUBBLE, mBubbles.get(i)), delayMillis);
+ delayMillis += 2000;
+ }
+ //mHandler.sendEmptyMessageDelayed(MSG_HIDE_ALL, MAXIMUM_TIME);
+ mStartTime = SystemClock.uptimeMillis();
+ }
+
+ void touched() {
+ if (SystemClock.uptimeMillis() - mStartTime < MINIMUM_TIME) {
+ return;
+ }
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubba = mBubbles.get(i);
+ if (bubba.dismissOnTouch) {
+ bubba.hide();
+ }
+ }
+ }
+
+ void close(boolean completed) {
+ mHandler.removeMessages(MSG_SHOW_BUBBLE);
+ for (int i = 0; i < mBubbles.size(); i++) {
+ mBubbles.get(i).hide();
+ }
+ if (completed) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ mInputView.getContext());
+ Editor editor = sp.edit();
+ editor.putBoolean(LatinIME.PREF_TUTORIAL_RUN, true);
+ editor.commit();
+ }
+ }
+}
diff --git a/src/com/android/inputmethod/latin/UserDictionary.java b/src/com/android/inputmethod/latin/UserDictionary.java
new file mode 100644
index 000000000..09549bf8c
--- /dev/null
+++ b/src/com/android/inputmethod/latin/UserDictionary.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2008 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.latin;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.provider.UserDictionary.Words;
+
+public class UserDictionary extends Dictionary {
+
+ private static final String[] PROJECTION = {
+ Words._ID,
+ Words.WORD,
+ Words.FREQUENCY
+ };
+
+ private static final int INDEX_WORD = 1;
+ private static final int INDEX_FREQUENCY = 2;
+
+ private static final char QUOTE = '\'';
+
+ private Context mContext;
+
+ List<Node> mRoots;
+ private int mMaxDepth;
+ private int mInputLength;
+
+ public static final int MAX_WORD_LENGTH = 32;
+
+ private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
+
+ private ContentObserver mObserver;
+
+ static class Node {
+ char code;
+ int frequency;
+ boolean terminal;
+ List<Node> children;
+ }
+
+ private boolean mRequiresReload;
+
+ public UserDictionary(Context context) {
+ mContext = context;
+ // Perform a managed query. The Activity will handle closing and requerying the cursor
+ // when needed.
+ ContentResolver cres = context.getContentResolver();
+
+ cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean self) {
+ mRequiresReload = true;
+ }
+ });
+
+ loadDictionary();
+ }
+
+ public synchronized void close() {
+ if (mObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+ }
+
+ private synchronized void loadDictionary() {
+ Cursor cursor = mContext.getContentResolver()
+ .query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)",
+ new String[] { Locale.getDefault().toString() }, null);
+ addWords(cursor);
+ mRequiresReload = false;
+ }
+
+ /**
+ * Adds a word to the dictionary and makes it persistent.
+ * @param word the word to add. If the word is capitalized, then the dictionary will
+ * recognize it as a capitalized word when searched.
+ * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
+ * the highest.
+ * @TODO use a higher or float range for frequency
+ */
+ public synchronized void addWord(String word, int frequency) {
+ if (mRequiresReload) loadDictionary();
+ // Safeguard against adding long words. Can cause stack overflow.
+ if (word.length() >= MAX_WORD_LENGTH) return;
+ addWordRec(mRoots, word, 0, frequency);
+ Words.addWord(mContext, word, frequency, Words.LOCALE_TYPE_CURRENT);
+ // In case the above does a synchronous callback of the change observer
+ mRequiresReload = false;
+ }
+
+ @Override
+ public synchronized void getWords(final WordComposer codes, final WordCallback callback) {
+ if (mRequiresReload) loadDictionary();
+ mInputLength = codes.size();
+ mMaxDepth = mInputLength * 3;
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1.0f, 0, callback);
+ }
+
+ @Override
+ public synchronized boolean isValidWord(CharSequence word) {
+ if (mRequiresReload) loadDictionary();
+ return isValidWordRec(mRoots, word, 0, word.length());
+ }
+
+ private boolean isValidWordRec(final List<Node> children, final CharSequence word,
+ final int offset, final int length) {
+ final int count = children.size();
+ char currentChar = word.charAt(offset);
+ for (int j = 0; j < count; j++) {
+ final Node node = children.get(j);
+ if (node.code == currentChar) {
+ if (offset == length - 1) {
+ if (node.terminal) {
+ return true;
+ }
+ } else {
+ if (node.children != null) {
+ if (isValidWordRec(node.children, word, offset + 1, length)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ static char toLowerCase(char c) {
+ if (c < BASE_CHARS.length) {
+ c = BASE_CHARS[c];
+ }
+ c = Character.toLowerCase(c);
+ return c;
+ }
+
+ /**
+ * Recursively traverse the tree for words that match the input. Input consists of
+ * a list of arrays. Each item in the list is one input character position. An input
+ * character is actually an array of multiple possible candidates. This function is not
+ * optimized for speed, assuming that the user dictionary will only be a few hundred words in
+ * size.
+ * @param roots node whose children have to be search for matches
+ * @param codes the input character codes
+ * @param word the word being composed as a possible match
+ * @param depth the depth of traversal - the length of the word being composed thus far
+ * @param completion whether the traversal is now in completion mode - meaning that we've
+ * exhausted the input and we're looking for all possible suffixes.
+ * @param snr current weight of the word being formed
+ * @param inputIndex position in the input characters. This can be off from the depth in
+ * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type
+ * "wouldve", it could be matching "would've", so the depth will be one more than the
+ * inputIndex
+ * @param callback the callback class for adding a word
+ */
+ private void getWordsRec(List<Node> roots, final WordComposer codes, final char[] word,
+ final int depth, boolean completion, float snr, int inputIndex,
+ WordCallback callback) {
+ final int count = roots.size();
+ final int codeSize = mInputLength;
+ // Optimization: Prune out words that are too long compared to how much was typed.
+ if (depth > mMaxDepth) {
+ return;
+ }
+ int[] currentChars = null;
+ if (codeSize <= inputIndex) {
+ completion = true;
+ } else {
+ currentChars = codes.getCodesAt(inputIndex);
+ }
+
+ for (int i = 0; i < count; i++) {
+ final Node node = roots.get(i);
+ final char c = node.code;
+ final char lowerC = toLowerCase(c);
+ boolean terminal = node.terminal;
+ List<Node> children = node.children;
+ int freq = node.frequency;
+ if (completion) {
+ word[depth] = c;
+ if (terminal) {
+ if (!callback.addWord(word, 0, depth + 1, (int) (freq * snr))) {
+ return;
+ }
+ }
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
+ callback);
+ }
+ } else if (c == QUOTE && currentChars[0] != QUOTE) {
+ // Skip the ' and continue deeper
+ word[depth] = QUOTE;
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
+ callback);
+ }
+ } else {
+ for (int j = 0; j < currentChars.length; j++) {
+ float addedAttenuation = (j > 0 ? 1f : 3f);
+ if (currentChars[j] == -1) {
+ break;
+ }
+ if (currentChars[j] == lowerC || currentChars[j] == c) {
+ word[depth] = c;
+
+ if (codes.size() == depth + 1) {
+ if (terminal) {
+ if (INCLUDE_TYPED_WORD_IF_VALID
+ || !same(word, depth + 1, codes.getTypedWord())) {
+ callback.addWord(word, 0, depth + 1,
+ (int) (freq * snr * addedAttenuation
+ * FULL_WORD_FREQ_MULTIPLIER));
+ }
+ }
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1,
+ true, snr * addedAttenuation, inputIndex + 1, callback);
+ }
+ } else if (children != null) {
+ getWordsRec(children, codes, word, depth + 1,
+ false, snr * addedAttenuation, inputIndex + 1, callback);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void addWords(Cursor cursor) {
+ mRoots = new ArrayList<Node>();
+
+ if (cursor.moveToFirst()) {
+ while (!cursor.isAfterLast()) {
+ String word = cursor.getString(INDEX_WORD);
+ int frequency = cursor.getInt(INDEX_FREQUENCY);
+ // Safeguard against adding really long words. Stack may overflow due
+ // to recursion
+ if (word.length() < MAX_WORD_LENGTH) {
+ addWordRec(mRoots, word, 0, frequency);
+ }
+ cursor.moveToNext();
+ }
+ }
+ cursor.close();
+ }
+
+ private void addWordRec(List<Node> children, final String word,
+ final int depth, final int frequency) {
+
+ final int wordLength = word.length();
+ final char c = word.charAt(depth);
+ // Does children have the current character?
+ final int childrenLength = children.size();
+ Node childNode = null;
+ boolean found = false;
+ for (int i = 0; i < childrenLength; i++) {
+ childNode = children.get(i);
+ if (childNode.code == c) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ childNode = new Node();
+ childNode.code = c;
+ children.add(childNode);
+ }
+ if (wordLength == depth + 1) {
+ // Terminate this word
+ childNode.terminal = true;
+ childNode.frequency += frequency; // If there are multiple similar words
+ return;
+ }
+ if (childNode.children == null) {
+ childNode.children = new ArrayList<Node>();
+ }
+ addWordRec(childNode.children, word, depth + 1, frequency);
+ }
+
+ /**
+ * Table mapping most combined Latin, Greek, and Cyrillic characters
+ * to their base characters. If c is in range, BASE_CHARS[c] == c
+ * if c is not a combined character, or the base character if it
+ * is combined.
+ */
+ static final char BASE_CHARS[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0020,
+ 0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7,
+ 0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf,
+ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00c6, 0x0043,
+ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049,
+ 0x00d0, 0x004e, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x00d7,
+ 0x004f, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00de, 0x0073, // Manually changed d8 to 4f
+ // Manually changed df to 73
+ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063,
+ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069,
+ 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7,
+ 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, // Manually changed f8 to 6f
+ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063,
+ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064,
+ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065,
+ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067,
+ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127,
+ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069,
+ 0x0049, 0x0131, 0x0049, 0x0069, 0x004a, 0x006a, 0x004b, 0x006b,
+ 0x0138, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c,
+ 0x006c, 0x0141, 0x0142, 0x004e, 0x006e, 0x004e, 0x006e, 0x004e,
+ 0x006e, 0x02bc, 0x014a, 0x014b, 0x004f, 0x006f, 0x004f, 0x006f,
+ 0x004f, 0x006f, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072,
+ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073,
+ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167,
+ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075,
+ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079,
+ 0x0059, 0x005a, 0x007a, 0x005a, 0x007a, 0x005a, 0x007a, 0x0073,
+ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187,
+ 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f,
+ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197,
+ 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f,
+ 0x004f, 0x006f, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7,
+ 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x0055,
+ 0x0075, 0x01b1, 0x01b2, 0x01b3, 0x01b4, 0x01b5, 0x01b6, 0x01b7,
+ 0x01b8, 0x01b9, 0x01ba, 0x01bb, 0x01bc, 0x01bd, 0x01be, 0x01bf,
+ 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0044, 0x0044, 0x0064, 0x004c,
+ 0x004c, 0x006c, 0x004e, 0x004e, 0x006e, 0x0041, 0x0061, 0x0049,
+ 0x0069, 0x004f, 0x006f, 0x0055, 0x0075, 0x00dc, 0x00fc, 0x00dc,
+ 0x00fc, 0x00dc, 0x00fc, 0x00dc, 0x00fc, 0x01dd, 0x00c4, 0x00e4,
+ 0x0226, 0x0227, 0x00c6, 0x00e6, 0x01e4, 0x01e5, 0x0047, 0x0067,
+ 0x004b, 0x006b, 0x004f, 0x006f, 0x01ea, 0x01eb, 0x01b7, 0x0292,
+ 0x006a, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01f6, 0x01f7,
+ 0x004e, 0x006e, 0x00c5, 0x00e5, 0x00c6, 0x00e6, 0x00d8, 0x00f8,
+ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065,
+ 0x0049, 0x0069, 0x0049, 0x0069, 0x004f, 0x006f, 0x004f, 0x006f,
+ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075,
+ 0x0053, 0x0073, 0x0054, 0x0074, 0x021c, 0x021d, 0x0048, 0x0068,
+ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061,
+ 0x0045, 0x0065, 0x00d6, 0x00f6, 0x00d5, 0x00f5, 0x004f, 0x006f,
+ 0x022e, 0x022f, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237,
+ 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f,
+ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,
+ 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f,
+ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
+ 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f,
+ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
+ 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f,
+ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
+ 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f,
+ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
+ 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f,
+ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+ 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
+ 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
+ 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
+ 0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077,
+ 0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
+ 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
+ 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
+ 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
+ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df,
+ 0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7,
+ 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
+ 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
+ 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff,
+ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
+ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
+ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
+ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
+ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347,
+ 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
+ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
+ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
+ 0x0370, 0x0371, 0x0372, 0x0373, 0x02b9, 0x0375, 0x0376, 0x0377,
+ 0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00a8, 0x0391, 0x00b7,
+ 0x0395, 0x0397, 0x0399, 0x038b, 0x039f, 0x038d, 0x03a5, 0x03a9,
+ 0x03ca, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+ 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x0399, 0x03a5, 0x03b1, 0x03b5, 0x03b7, 0x03b9,
+ 0x03cb, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03cf,
+ 0x03b2, 0x03b8, 0x03a5, 0x03d2, 0x03d2, 0x03c6, 0x03c0, 0x03d7,
+ 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03dc, 0x03dd, 0x03de, 0x03df,
+ 0x03e0, 0x03e1, 0x03e2, 0x03e3, 0x03e4, 0x03e5, 0x03e6, 0x03e7,
+ 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03ed, 0x03ee, 0x03ef,
+ 0x03ba, 0x03c1, 0x03c2, 0x03f3, 0x0398, 0x03b5, 0x03f6, 0x03f7,
+ 0x03f8, 0x03a3, 0x03fa, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff,
+ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406,
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x041a, 0x0418, 0x0423, 0x040f,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0418, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
+ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f,
+ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467,
+ 0x0468, 0x0469, 0x046a, 0x046b, 0x046c, 0x046d, 0x046e, 0x046f,
+ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475,
+ 0x0478, 0x0479, 0x047a, 0x047b, 0x047c, 0x047d, 0x047e, 0x047f,
+ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f,
+ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497,
+ 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x049f,
+ 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7,
+ 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af,
+ 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7,
+ 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x04be, 0x04bf,
+ 0x04c0, 0x0416, 0x0436, 0x04c3, 0x04c4, 0x04c5, 0x04c6, 0x04c7,
+ 0x04c8, 0x04c9, 0x04ca, 0x04cb, 0x04cc, 0x04cd, 0x04ce, 0x04cf,
+ 0x0410, 0x0430, 0x0410, 0x0430, 0x04d4, 0x04d5, 0x0415, 0x0435,
+ 0x04d8, 0x04d9, 0x04d8, 0x04d9, 0x0416, 0x0436, 0x0417, 0x0437,
+ 0x04e0, 0x04e1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041e, 0x043e,
+ 0x04e8, 0x04e9, 0x04e8, 0x04e9, 0x042d, 0x044d, 0x0423, 0x0443,
+ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04f6, 0x04f7,
+ 0x042b, 0x044b, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff,
+ };
+
+ // generated with:
+ // cat UnicodeData.txt | perl -e 'while (<>) { @foo = split(/;/); $foo[5] =~ s/<.*> //; $base[hex($foo[0])] = hex($foo[5]);} for ($i = 0; $i < 0x500; $i += 8) { for ($j = $i; $j < $i + 8; $j++) { printf("0x%04x, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }'
+
+}
diff --git a/src/com/android/inputmethod/latin/WordComposer.java b/src/com/android/inputmethod/latin/WordComposer.java
new file mode 100644
index 000000000..c950a7f18
--- /dev/null
+++ b/src/com/android/inputmethod/latin/WordComposer.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2008-2009 Google Inc.
+ *
+ * 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.latin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A place to store the currently composing word with information such as adjacent key codes as well
+ */
+public class WordComposer {
+ /**
+ * The list of unicode values for each keystroke (including surrounding keys)
+ */
+ private List<int[]> mCodes;
+
+ /**
+ * The word chosen from the candidate list, until it is committed.
+ */
+ private String mPreferredWord;
+
+ private StringBuilder mTypedWord;
+
+ /**
+ * Whether the user chose to capitalize the word.
+ */
+ private boolean mIsCapitalized;
+
+ WordComposer() {
+ mCodes = new ArrayList<int[]>(12);
+ mTypedWord = new StringBuilder(20);
+ }
+
+ /**
+ * Clear out the keys registered so far.
+ */
+ public void reset() {
+ mCodes.clear();
+ mIsCapitalized = false;
+ mPreferredWord = null;
+ mTypedWord.setLength(0);
+ }
+
+ /**
+ * Number of keystrokes in the composing word.
+ * @return the number of keystrokes
+ */
+ public int size() {
+ return mCodes.size();
+ }
+
+ /**
+ * Returns the codes at a particular position in the word.
+ * @param index the position in the word
+ * @return the unicode for the pressed and surrounding keys
+ */
+ public int[] getCodesAt(int index) {
+ return mCodes.get(index);
+ }
+
+ /**
+ * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of
+ * the array containing unicode for adjacent keys, sorted by reducing probability/proximity.
+ * @param codes the array of unicode values
+ */
+ public void add(int primaryCode, int[] codes) {
+ mTypedWord.append((char) primaryCode);
+ mCodes.add(codes);
+ }
+
+ /**
+ * Delete the last keystroke as a result of hitting backspace.
+ */
+ public void deleteLast() {
+ mCodes.remove(mCodes.size() - 1);
+ mTypedWord.deleteCharAt(mTypedWord.length() - 1);
+ }
+
+ /**
+ * Returns the word as it was typed, without any correction applied.
+ * @return the word that was typed so far
+ */
+ public CharSequence getTypedWord() {
+ int wordSize = mCodes.size();
+ if (wordSize == 0) {
+ return null;
+ }
+// StringBuffer sb = new StringBuffer(wordSize);
+// for (int i = 0; i < wordSize; i++) {
+// char c = (char) mCodes.get(i)[0];
+// if (i == 0 && mIsCapitalized) {
+// c = Character.toUpperCase(c);
+// }
+// sb.append(c);
+// }
+// return sb;
+ return mTypedWord;
+ }
+
+ public void setCapitalized(boolean capitalized) {
+ mIsCapitalized = capitalized;
+ }
+
+ /**
+ * Whether or not the user typed a capital letter as the first letter in the word
+ * @return capitalization preference
+ */
+ public boolean isCapitalized() {
+ return mIsCapitalized;
+ }
+
+ /**
+ * Stores the user's selected word, before it is actually committed to the text field.
+ * @param preferred
+ */
+ public void setPreferredWord(String preferred) {
+ mPreferredWord = preferred;
+ }
+
+ /**
+ * Return the word chosen by the user, or the typed word if no other word was chosen.
+ * @return the preferred word
+ */
+ public CharSequence getPreferredWord() {
+ return mPreferredWord != null ? mPreferredWord : getTypedWord();
+ }
+}