diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..412eeda
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs diff=csharp
+*.sln merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc diff=astextplain
+*.DOC diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot diff=astextplain
+*.DOT diff=astextplain
+*.pdf diff=astextplain
+*.PDF diff=astextplain
+*.rtf diff=astextplain
+*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b9d6bd9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,215 @@
+#################
+## Eclipse
+#################
+
+*.pydevproject
+.project
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+#################
+## Visual Studio
+#################
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+build/
+[Bb]in/
+[Oo]bj/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+*.pubxml
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+#############
+## Windows detritus
+#############
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+
+#############
+## Python
+#############
+
+*.py[co]
+
+# Packages
+*.egg
+*.egg-info
+dist/
+build/
+eggs/
+parts/
+var/
+sdist/
+develop-eggs/
+.installed.cfg
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+
+#Translations
+*.mo
+
+#Mr Developer
+.mr.developer.cfg
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..9aea6e4
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gen/com/halbae87/koreanbasicime/BuildConfig.java b/gen/com/halbae87/koreanbasicime/BuildConfig.java
new file mode 100644
index 0000000..4afbd36
--- /dev/null
+++ b/gen/com/halbae87/koreanbasicime/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package com.halbae87.koreanbasicime;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
new file mode 100644
index 0000000..428bdbc
Binary files /dev/null and b/libs/android-support-v4.jar differ
diff --git a/proguard-project.txt b/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/project.properties b/project.properties
new file mode 100644
index 0000000..a3ee5ab
--- /dev/null
+++ b/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_delete.png b/res/drawable-hdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..5139c71
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_delete.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_done.png b/res/drawable-hdpi/sym_keyboard_done.png
new file mode 100644
index 0000000..471c502
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_done.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_return.png b/res/drawable-hdpi/sym_keyboard_return.png
new file mode 100644
index 0000000..5a5670c
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_return.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_search.png b/res/drawable-hdpi/sym_keyboard_search.png
new file mode 100644
index 0000000..e72cde3
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_search.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_shift.png b/res/drawable-hdpi/sym_keyboard_shift.png
new file mode 100644
index 0000000..2757696
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_shift.png differ
diff --git a/res/drawable-hdpi/sym_keyboard_space.png b/res/drawable-hdpi/sym_keyboard_space.png
new file mode 100644
index 0000000..cef2daa
Binary files /dev/null and b/res/drawable-hdpi/sym_keyboard_space.png differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_delete.png b/res/drawable-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..6cee596
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_delete.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_done.png b/res/drawable-mdpi/sym_keyboard_done.png
new file mode 100644
index 0000000..c0d6d13
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_done.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_return.png b/res/drawable-mdpi/sym_keyboard_return.png
new file mode 100644
index 0000000..cbe2b15
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_return.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_search.png b/res/drawable-mdpi/sym_keyboard_search.png
new file mode 100644
index 0000000..127755d
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_search.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_shift.png b/res/drawable-mdpi/sym_keyboard_shift.png
new file mode 100644
index 0000000..d059628
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_shift.png differ
diff --git a/res/drawable-mdpi/sym_keyboard_space.png b/res/drawable-mdpi/sym_keyboard_space.png
new file mode 100644
index 0000000..09b94d9
Binary files /dev/null and b/res/drawable-mdpi/sym_keyboard_space.png differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/res/layout/input.xml b/res/layout/input.xml
new file mode 100644
index 0000000..e47d734
--- /dev/null
+++ b/res/layout/input.xml
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
new file mode 100644
index 0000000..b5f3bc1
--- /dev/null
+++ b/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ 46dip
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..d6932a9
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,28 @@
+
+
+
+ #FF000000
+ #FFE35900
+ #ff808080
+ #bbffffff
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..a21ffd7
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ 50dip
+ 16sp
+ 6sp
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..9a8be34
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ 한글 기본 입력기
+
+
+ \u0020.,;:!?\n()[]*&@{}/<>_+=|"
+
+
+ Go
+ Next
+ Send
+
diff --git a/res/xml/korean.xml b/res/xml/korean.xml
new file mode 100644
index 0000000..d5613f0
--- /dev/null
+++ b/res/xml/korean.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/korean_shifted.xml b/res/xml/korean_shifted.xml
new file mode 100644
index 0000000..490d113
--- /dev/null
+++ b/res/xml/korean_shifted.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/method.xml b/res/xml/method.xml
new file mode 100644
index 0000000..96dee8b
--- /dev/null
+++ b/res/xml/method.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/res/xml/qwerty.xml b/res/xml/qwerty.xml
new file mode 100644
index 0000000..7530feb
--- /dev/null
+++ b/res/xml/qwerty.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/symbols.xml b/res/xml/symbols.xml
new file mode 100644
index 0000000..aac63fe
--- /dev/null
+++ b/res/xml/symbols.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/symbols_shift.xml b/res/xml/symbols_shift.xml
new file mode 100644
index 0000000..e6774a7
--- /dev/null
+++ b/res/xml/symbols_shift.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/halbae87/koreanbasicime/CandidateView.java b/src/com/halbae87/koreanbasicime/CandidateView.java
new file mode 100644
index 0000000..48cc684
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/CandidateView.java
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ */
+
+package com.halbae87.koreanbasicime;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CandidateView extends View {
+
+ private static final int OUT_OF_BOUNDS = -1;
+
+ private SoftKeyboard mService;
+ private List mSuggestions;
+ private int mSelectedIndex;
+ private int mTouchX = OUT_OF_BOUNDS;
+ private Drawable mSelectionHighlight;
+ private boolean mTypedWordValid;
+
+ private Rect mBgPadding;
+
+ private static final int MAX_SUGGESTIONS = 32;
+ private static final int SCROLL_PIXELS = 20;
+
+ private int[] mWordWidth = new int[MAX_SUGGESTIONS];
+ private int[] mWordX = new int[MAX_SUGGESTIONS];
+
+ private static final int X_GAP = 10;
+
+ private static final List EMPTY_LIST = new ArrayList();
+
+ private int mColorNormal;
+ private int mColorRecommended;
+ private int mColorOther;
+ private int mVerticalPadding;
+ private Paint mPaint;
+ private boolean mScrolled;
+ private int mTargetScrollX;
+
+ private int mTotalWidth;
+
+ private GestureDetector mGestureDetector;
+
+ /**
+ * Construct a CandidateView for showing suggested words for completion.
+ * @param context
+ * @param attrs
+ */
+ @SuppressWarnings("deprecation")
+ public CandidateView(Context context) {
+ super(context);
+ mSelectionHighlight = context.getResources().getDrawable(
+ android.R.drawable.list_selector_background);
+ mSelectionHighlight.setState(new int[] {
+ android.R.attr.state_enabled,
+ android.R.attr.state_focused,
+ android.R.attr.state_window_focused,
+ android.R.attr.state_pressed
+ });
+
+ Resources r = context.getResources();
+
+ setBackgroundColor(r.getColor(R.color.candidate_background));
+
+ mColorNormal = r.getColor(R.color.candidate_normal);
+ mColorRecommended = r.getColor(R.color.candidate_recommended);
+ mColorOther = r.getColor(R.color.candidate_other);
+ mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding);
+
+ mPaint = new Paint();
+ mPaint.setColor(mColorNormal);
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
+ mPaint.setStrokeWidth(0);
+
+ mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ mScrolled = true;
+ int sx = getScrollX();
+ sx += distanceX;
+ if (sx < 0) {
+ sx = 0;
+ }
+ if (sx + getWidth() > mTotalWidth) {
+ sx -= distanceX;
+ }
+ mTargetScrollX = sx;
+ scrollTo(sx, getScrollY());
+ invalidate();
+ return true;
+ }
+ });
+ setHorizontalFadingEdgeEnabled(true);
+ setWillNotDraw(false);
+ setHorizontalScrollBarEnabled(false);
+ setVerticalScrollBarEnabled(false);
+ }
+
+ /**
+ * A connection back to the service to communicate with the text field
+ * @param listener
+ */
+ public void setService(SoftKeyboard listener) {
+ mService = listener;
+ }
+
+ @Override
+ public int computeHorizontalScrollRange() {
+ return mTotalWidth;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = resolveSize(50, widthMeasureSpec);
+
+ // Get the desired height of the icon menu view (last row of items does
+ // not have a divider below)
+ Rect padding = new Rect();
+ mSelectionHighlight.getPadding(padding);
+ final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding
+ + padding.top + padding.bottom;
+
+ // Maximum possible width and desired height
+ setMeasuredDimension(measuredWidth,
+ resolveSize(desiredHeight, heightMeasureSpec));
+ }
+
+ /**
+ * 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;
+
+ if (mBgPadding == null) {
+ mBgPadding = new Rect(0, 0, 0, 0);
+ if (getBackground() != null) {
+ getBackground().getPadding(mBgPadding);
+ }
+ }
+ int x = 0;
+ final int count = mSuggestions.size();
+ final int height = getHeight();
+ final Rect bgPadding = mBgPadding;
+ final Paint paint = mPaint;
+ final int touchX = mTouchX;
+ final int scrollX = getScrollX();
+ final boolean scrolled = mScrolled;
+ final boolean typedWordValid = mTypedWordValid;
+ final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent());
+
+ for (int i = 0; i < count; i++) {
+ String suggestion = mSuggestions.get(i);
+ float textWidth = paint.measureText(suggestion);
+ final int wordWidth = (int) textWidth + X_GAP * 2;
+
+ mWordX[i] = x;
+ mWordWidth[i] = wordWidth;
+ paint.setColor(mColorNormal);
+ 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);
+ }
+ mSelectedIndex = i;
+ }
+
+ if (canvas != null) {
+ if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) {
+ paint.setFakeBoldText(true);
+ paint.setColor(mColorRecommended);
+ } else if (i != 0) {
+ paint.setColor(mColorOther);
+ }
+ canvas.drawText(suggestion, x + X_GAP, y, paint);
+ paint.setColor(mColorOther);
+ canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top,
+ x + wordWidth + 0.5f, height + 1, paint);
+ paint.setFakeBoldText(false);
+ }
+ x += wordWidth;
+ }
+ mTotalWidth = x;
+ if (mTargetScrollX != getScrollX()) {
+ scrollToTarget();
+ }
+ }
+
+ private void scrollToTarget() {
+ int sx = getScrollX();
+ if (mTargetScrollX > sx) {
+ sx += SCROLL_PIXELS;
+ if (sx >= mTargetScrollX) {
+ sx = mTargetScrollX;
+ requestLayout();
+ }
+ } else {
+ sx -= SCROLL_PIXELS;
+ if (sx <= mTargetScrollX) {
+ sx = mTargetScrollX;
+ requestLayout();
+ }
+ }
+ scrollTo(sx, getScrollY());
+ invalidate();
+ }
+
+ public void setSuggestions(List suggestions, boolean completions,
+ boolean typedWordValid) {
+ clear();
+ if (suggestions != null) {
+ mSuggestions = new ArrayList(suggestions);
+ }
+ mTypedWordValid = typedWordValid;
+ scrollTo(0, 0);
+ mTargetScrollX = 0;
+ // Compute the total width
+ onDraw(null);
+ invalidate();
+ requestLayout();
+ }
+
+ public void clear() {
+ mSuggestions = EMPTY_LIST;
+ mTouchX = OUT_OF_BOUNDS;
+ mSelectedIndex = -1;
+ invalidate();
+ }
+
+ @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 (mSelectedIndex >= 0) {
+ mService.pickSuggestionManually(mSelectedIndex);
+ mSelectedIndex = -1;
+ }
+ }
+ invalidate();
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!mScrolled) {
+ if (mSelectedIndex >= 0) {
+ mService.pickSuggestionManually(mSelectedIndex);
+ }
+ }
+ mSelectedIndex = -1;
+ removeHighlight();
+ 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 (mSelectedIndex >= 0) {
+ mService.pickSuggestionManually(mSelectedIndex);
+ }
+ invalidate();
+ }
+
+ private void removeHighlight() {
+ mTouchX = OUT_OF_BOUNDS;
+ invalidate();
+ }
+}
diff --git a/src/com/halbae87/koreanbasicime/InputTables.java b/src/com/halbae87/koreanbasicime/InputTables.java
new file mode 100644
index 0000000..7b5bede
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/InputTables.java
@@ -0,0 +1,74 @@
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ * but this part is my original source
+ */
+
+package com.halbae87.koreanbasicime;
+
+public final class InputTables {
+ public static final int NUM_OF_FIRST = 19;
+ public static final int NUM_OF_MIDDLE = 21;
+ public static final int NUM_OF_LAST = 27;
+ public static final int NUM_OF_LAST_INDEX = NUM_OF_LAST + 1; // add 1 for non-last consonant added characters
+
+ public static final int KEYSTATE_NONE = 0;
+
+ public static final int KEYSTATE_SHIFT = 1;
+ public static final int KEYSTATE_SHIFT_LEFT = 1;
+ public static final int KEYSTATE_SHIFT_RIGHT = 2;
+ public static final int KEYSTATE_SHIFT_MASK = 3;
+
+ public static final int KEYSTATE_ALT = 4;
+ public static final int KEYSTATE_ALT_LEFT = 4;
+ public static final int KEYSTATE_ALT_RIGHT = 8;
+ public static final int KEYSTATE_ALT_MASK = 12;
+
+ public static final int KEYSTATE_CTRL = 16;
+ public static final int KEYSTATE_CTRL_LEFT = 16;
+ public static final int KEYSTATE_CTRL_RIGHT = 32;
+ public static final int KEYSTATE_CTRL_MASK = 48;
+
+ public static final int KEYSTATE_FN = 64; // just for future usage...
+
+ public static final char BACK_SPACE = 0x8;
+
+ // formula to get HANGUL_CODE by composing consonants and vowel indexes
+ // HANGUL_CODE = HANGUL_START + iFirst*NUM_OF_MIDDLE*NUM_OF_LAST_INDEX + iMiddle*NUM_OF_LAST_INDEX + iLast
+
+ // getting the first consonant index from code
+ // iFirst = (vCode - HANGUL_START) / (NUM_OF_MIDDLE * NUM_OF_LAST_INDEX)
+
+ // getting the vowel index from code
+ // iMiddle = ((vCode - HANGUL_START) % (NUM_OF_MIDDLE * NUM_OF_LAST_INDEX)) / NUM_OF_LAST_INDEX
+
+ // getting the last consonant index from code
+ // iLast = (vCode - HANGUL_START) % NUM_OF_LAST_INDEX
+
+
+ public static final class NormalKeyMap {
+ public static final char Code[] = {0x3141, 0x3160, 0x314A, 0x3147, 0x3137, 0x3139, 0x314E, 0x3157, 0x3151, 0x3153, 0x314F, 0x3163, 0x3161, 0x315C, 0x3150, 0x3154, 0x3142, 0x3131, 0x3134, 0x3145, 0x3155, 0x314D, 0x3148, 0x314C, 0x315B, 0x314B};
+ public static final int FirstIndex[] = {6, -1, 14, 11, 3, 5, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, 0, 2, 9, -1, 17, 12, 16, -1, 15};
+ public static final int MiddleIndex[] = {-1, 17, -1, -1, -1, -1, -1, 8, 2, 4, 0, 20, 18, 13, 1, 5, -1, -1, -1, -1, 6, -1, -1, -1, 12, -1};
+ public static final int LastIndex[] = {16, -1, 23, 21, 7, 8, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 1, 4, 19, -1, 26, 22, 25, -1, 24};
+ }
+
+ public static final class ShiftedKeyMap {
+ public static final char Code[] = {0x3141, 0x3160, 0x314A, 0x3147, 0x3138, 0x3139, 0x314E, 0x3157, 0x3151, 0x3153, 0x314F, 0x3163, 0x3161, 0x315C, 0x3152, 0x3156, 0x3143, 0x3132, 0x3134, 0x3146, 0x3155, 0x314D, 0x3149, 0x314C, 0x315B, 0x314B};
+ public static final int FirstIndex[] = {6, -1, 14, 11, 4, 5, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 1, 2, 10, -1, 17, 13, 16, -1, 15};
+ public static final int MiddleIndex[] = {-1, 17, -1, -1, -1, -1, -1, 8, 2, 4, 0, 20, 18, 13, 3, 7, -1, -1, -1, -1, 6, -1, -1, -1, 12, -1};
+ public static final int LastIndex[] = {16, -1, 23, 21, -1, 8, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 4, 20, -1, 26, -1, 25, -1, 24};
+ }
+
+ public static final char FirstConsonantCodes[] = {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E };
+
+ public static final class LastConsonants {
+ public static final char Code[] = {0x0, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3139, 0x313A, 0x313B, 0x313C, 0x313D, 0x313E, 0x313F, 0x3140, 0x3141, 0x3142, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E};
+ public static final int iLast[] = {-1, -1, -1, 1, -1, 4, 4, -1, -1, 8, 8, 8, 8, 8, 8, 8, -1, -1, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ public static final int iFirst[] = {-1, -1, -1, 9, -1, 12, 18, -1, -1, 0, 6, 7, 9, 16, 17, 18, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ }
+
+ public static final class Vowels {
+ public static final char Code[] = {0x314F, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315A, 0x315B, 0x315C, 0x315D, 0x315E, 0x315F, 0x3160, 0x3161, 0x3162, 0x3163};
+ public static final int iMiddle[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 8, 8, -1, -1, 13, 13, 13, -1, -1, 18, -1};
+ }
+}
diff --git a/src/com/halbae87/koreanbasicime/KoreanAutomata.java b/src/com/halbae87/koreanbasicime/KoreanAutomata.java
new file mode 100644
index 0000000..e7a9302
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/KoreanAutomata.java
@@ -0,0 +1,1142 @@
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ * but this part is my original source.
+ */
+
+package com.halbae87.koreanbasicime;
+
+import com.halbae87.koreanbasicime.InputTables;
+import android.util.Log;
+
+public class KoreanAutomata {
+ public static int HANGUL_START = 0xAC00;
+ public static int HANGUL_END = 0xD7A3;
+ public static int HANGUL_JAMO_START = 0x3131;
+ public static int HANGUL_MO_START = 0x314F;
+ public static int HANGUL_JAMO_END = 0x3163;
+ private static final String TAG = "KoreanAutomata";
+
+ // Action Codes
+ public static final int ACTION_NONE = 0;
+ public static final int ACTION_UPDATE_COMPOSITIONSTR = 1;
+ public static final int ACTION_UPDATE_COMPLETESTR = 2;
+ public static final int ACTION_USE_INPUT_AS_RESULT = 4;
+ // public static final int ACTION_BACKSPACE = 8; not used.
+ public static final int ACTION_ERROR = -1;
+
+ private int mState = 0;
+ private String mCompositionString = "";
+ private String mCompleteString = "";
+ private boolean mKoreanMode = false;
+
+ public KoreanAutomata()
+ {
+ mState = 0;
+ mCompositionString = "";
+ mCompleteString = "";
+ mKoreanMode = false;
+ }
+ public int GetState()
+ {
+ return mState;
+ };
+ public String GetCompositionString()
+ {
+ return mCompositionString;
+ };
+ public String GetCompleteString()
+ {
+ return mCompleteString;
+ };
+
+ public void ToggleMode()
+ {
+ mKoreanMode = !mKoreanMode;
+ }
+ public boolean IsKoreanMode()
+ {
+ return mKoreanMode;
+ }
+ public boolean IsHangul(char code)
+ {
+ if ((code >= HANGUL_START) && (code <= HANGUL_END))
+ return true;
+ if ((code >= HANGUL_JAMO_START) && (code <= HANGUL_JAMO_END))
+ return true;
+ return false;
+ }
+
+ public boolean IsJAMO(char code)
+ {
+ if ((code >= HANGUL_JAMO_START) && (code <= HANGUL_JAMO_END))
+ return true;
+ return false;
+ };
+
+ public boolean IsConsonant(char code)
+ {
+ if ((code >= HANGUL_JAMO_START) && (code < HANGUL_MO_START))
+ return true;
+ return false;
+ };
+
+ public boolean IsVowel(char code)
+ {
+ if ((code >= HANGUL_MO_START) && (code <= HANGUL_JAMO_END))
+ return true;
+ return false;
+ };
+
+ /** not used.
+ public boolean IsLastConsonanted(char code)
+ {
+ if (IsHangul(code))
+ {
+ if (IsJAMO(code)) // <- need to fix, if this routine is to be used...
+ return true;
+ int offset = code - HANGUL_START;
+ if (offset % InputTables.NUM_OF_LAST_INDEX == 0)
+ return false;
+ else
+ return true;
+ }
+ else
+ {
+ // wrong input
+ return false;
+ }
+ }
+ **/
+
+ public int GetLastConsonantIndex(char code)
+ {
+ int lcIndex= -1;
+ if (IsHangul(code))
+ {
+ if (IsJAMO(code))
+ {
+ if (IsConsonant(code))
+ {
+ for (lcIndex = 0; lcIndex < InputTables.NUM_OF_LAST_INDEX; lcIndex++)
+ {
+ if (code == InputTables.LastConsonants.Code[lcIndex])
+ break;
+ }
+ if (lcIndex >= InputTables.NUM_OF_LAST_INDEX)
+ lcIndex = -1;
+ }
+ else
+ lcIndex = -1;
+ }
+ else
+ {
+ int offset = code - HANGUL_START;
+ lcIndex = (offset % InputTables.NUM_OF_LAST_INDEX);
+ }
+ }
+ return lcIndex;
+ }
+
+ public char GetLastConsonant(char code)
+ {
+ char lcCode;
+ int lcIndex = GetLastConsonantIndex(code);
+ if (lcIndex < 0)
+ lcCode = (char) 0;
+ else
+ lcCode = InputTables.LastConsonants.Code[lcIndex];
+ return lcCode;
+ }
+
+ public int GetFirstConsonantIndex(char code)
+ {
+ int fcIndex = -1;
+ if (IsHangul(code))
+ {
+ if (IsConsonant(code))
+ {
+ for (fcIndex = 0; fcIndex < InputTables.NUM_OF_FIRST; fcIndex++)
+ if (code == InputTables.FirstConsonantCodes[fcIndex])
+ break;
+ if (fcIndex >= InputTables.NUM_OF_FIRST)
+ fcIndex = -1;
+ }
+ else if (IsVowel(code))
+ {
+ fcIndex = -1;
+ }
+ else
+ {
+ int offset = code - HANGUL_START;
+ fcIndex = (offset / (InputTables.NUM_OF_MIDDLE * InputTables.NUM_OF_LAST_INDEX));
+ }
+ }
+
+ return fcIndex;
+ }
+
+ public char GetFirstConsonant(char code)
+ {
+ char fcCode;
+ int fcIndex = GetFirstConsonantIndex(code);
+ if (fcIndex < 0)
+ fcCode = (char) 0;
+ else
+ fcCode = InputTables.FirstConsonantCodes[fcIndex];
+ return fcCode;
+ }
+
+ public int GetVowelIndex(char code)
+ {
+ int vIndex = -1;
+ if (IsHangul(code))
+ {
+ if (IsVowel(code)) // vowel only character..
+ {
+ vIndex = ConvertVowelCodeToIndex(code);
+ }
+ else
+ {
+ int offset = code - HANGUL_START;
+ vIndex = (offset % (InputTables.NUM_OF_MIDDLE * InputTables.NUM_OF_LAST_INDEX))/InputTables.NUM_OF_LAST_INDEX;
+ }
+ }
+ return vIndex;
+ }
+
+ public char GetVowel(char code)
+ {
+ char vCode;
+ int vIndex = GetVowelIndex(code);
+ if (vIndex < 0)
+ vCode = (char) 0;
+ else
+ vCode = InputTables.Vowels.Code[vIndex];
+ return vCode;
+ }
+
+ public int ConvertFirstConsonantCodeToIndex(char fcCode) // fcCode should be one of "First Consonants" otherwise return -1
+ {
+ int fcIndex = 0;
+ while (fcIndex < InputTables.NUM_OF_FIRST)
+ {
+ if (fcCode == InputTables.FirstConsonantCodes[fcIndex])
+ break;
+ fcIndex++;
+ }
+ if (fcIndex == InputTables.NUM_OF_FIRST)
+ fcIndex = -1;
+ return fcIndex;
+ }
+
+ public int ConvertLastConsonantCodeToIndex(char lcCode) // fcCode should be one of "Last Consonants", otherwise return -1
+ {
+ int lcIndex = 0;
+ while (lcIndex < InputTables.NUM_OF_LAST_INDEX)
+ {
+ if (lcCode == InputTables.LastConsonants.Code[lcIndex])
+ break;
+ lcIndex++;
+ }
+ if (lcIndex == InputTables.NUM_OF_LAST_INDEX)
+ lcIndex = -1;
+ return lcIndex;
+ }
+
+ public int ConvertVowelCodeToIndex(char vCode)
+ {
+ if (vCode < InputTables.Vowels.Code[0])
+ return -1;
+ int vIndex = vCode - InputTables.Vowels.Code[0];
+ if (vIndex >= InputTables.NUM_OF_MIDDLE)
+ return -1;
+ return vIndex;
+ }
+
+ public int CombineLastConsonantWithIndex(int cIndex1, int cIndex2)
+ {
+ int newIndex = 0;
+ char newCode = (char) 0;
+
+ if (InputTables.LastConsonants.Code[cIndex1] == 0x3131 && InputTables.LastConsonants.Code[cIndex2] == 0x3145)
+ newCode = 0x3133; // ㄳ
+
+ if (InputTables.LastConsonants.Code[cIndex1] == 0x3142 && InputTables.LastConsonants.Code[cIndex2] == 0x3145)
+ newCode = 0x3144; // ã…„
+
+ if (InputTables.LastConsonants.Code[cIndex1] == 0x3134)
+ {
+ if (InputTables.LastConsonants.Code[cIndex2] == 0x3148)
+ newCode = 0x3135; // ㄵ
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x314E)
+ newCode = 0x3136; // ㄶ
+ }
+
+ if (InputTables.LastConsonants.Code[cIndex1] == 0x3139)
+ {
+ if (InputTables.LastConsonants.Code[cIndex2] == 0x3131)
+ newCode = 0x313A; // ㄺ
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x3141)
+ newCode = 0x313B; // ã„»
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x3142)
+ newCode = 0x313C; // ㄼ
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x3145)
+ newCode = 0x313D; // ㄽ
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x314C)
+ newCode = 0x313E; // ㄾ
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x314D)
+ newCode = 0x313F; // ã„¿
+ else if (InputTables.LastConsonants.Code[cIndex2] == 0x314E)
+ newCode = 0x3140; // ã…€
+ }
+
+ if (newCode == (char) 0)
+ newIndex = -1;
+ else
+ newIndex = ConvertLastConsonantCodeToIndex(newCode);
+
+ return newIndex;
+ }
+
+ public char CombineLastConsonantWithCode(char lcCode1, char lcCode2)
+ {
+ char newCode = (char) 0;
+
+ if (lcCode1 == 0x3131 && lcCode2 == 0x3145)
+ newCode = 0x3133; // ㄳ
+
+ else if (lcCode1 == 0x3142 && lcCode2 == 0x3145)
+ newCode = 0x3144; // ã…„
+
+ else if (lcCode1 == 0x3134)
+ {
+ if (lcCode2 == 0x3148)
+ newCode = 0x3135; // ㄵ
+ else if (lcCode2 == 0x314E)
+ newCode = 0x3136; // ㄶ
+ }
+
+ else if (lcCode1 == 0x3139)
+ {
+ if (lcCode2 == 0x3131)
+ newCode = 0x313A; // ㄺ
+ else if (lcCode2 == 0x3141)
+ newCode = 0x313B; // ã„»
+ else if (lcCode2 == 0x3142)
+ newCode = 0x313C; // ㄼ
+ else if (lcCode2 == 0x3145)
+ newCode = 0x313D; // ㄽ
+ else if (lcCode2 == 0x314C)
+ newCode = 0x313E; // ㄾ
+ else if (lcCode2 == 0x314D)
+ newCode = 0x313F; // ã„¿
+ else if (lcCode2 == 0x314E)
+ newCode = 0x3140; // ã…€
+ }
+
+ return newCode;
+ }
+
+ public char CombineVowelWithCode(char vCode1, char vCode2)
+ {
+ char newCode = (char) 0;
+ if (vCode1 == 0x3157) // ã…—
+ {
+ if (vCode2 == 0x314F) // ã…�
+ newCode = 0x3158; // ã…˜
+ else if (vCode2 == 0x3150) // ã…�
+ newCode = 0x3159; // ã…™
+ else if (vCode2 == 0x3163) // ã…£
+ newCode = 0x315A; // ã…š
+ }
+ else if (vCode1 == 0x315C) // ㅜ
+ {
+ if (vCode2 == 0x3153) // ã…“
+ newCode = 0x315D; // ã…�
+ else if (vCode2 == 0x3154) // ã…”
+ newCode = 0x315E; // ã…ž
+ else if (vCode2 == 0x3163) // ã…£
+ newCode = 0x315F; // ã…Ÿ
+ }
+ else if (vCode1 == 0x3161) // ã…¡
+ {
+ if (vCode2 == 0x3163) // ã…£
+ newCode = 0x3162; // ã…¢
+ }
+ return newCode;
+ }
+
+ public int CombineVowelWithIndex(int vIndex1, int vIndex2)
+ {
+ int newIndex = -1;
+ char vCode1 = InputTables.Vowels.Code[vIndex1];
+ char vCode2 = InputTables.Vowels.Code[vIndex2];
+
+ char newCode = CombineVowelWithCode(vCode1, vCode2);
+ if (newCode != (char) 0)
+ {
+ newIndex = ConvertVowelCodeToIndex(newCode);
+ }
+ return newIndex;
+ }
+
+ public char ComposeCharWithIndexs(int fcIndex, int vIndex, int lcIndex)
+ {
+ char Code = (char) 0;
+ if ((fcIndex >= 0) && (fcIndex < InputTables.NUM_OF_FIRST))
+ {
+ if ((vIndex >= 0) && (vIndex < InputTables.NUM_OF_MIDDLE))
+ {
+ if ((lcIndex >= 0) && (lcIndex < InputTables.NUM_OF_LAST))
+ {
+ int offset = fcIndex*InputTables.NUM_OF_MIDDLE*InputTables.NUM_OF_LAST_INDEX + vIndex*InputTables.NUM_OF_LAST_INDEX + lcIndex;
+ Code = (char) (offset + HANGUL_START);
+ }
+ }
+ }
+ return Code;
+ }
+
+ public int GetAlphabetIndex(char code)
+ {
+ if (code >= 'a' && code <= 'z')
+ return (int) (code - 'a');
+ if (code >= 'A' && code <= 'Z')
+ return (int) (code - 'A');
+ return -1;
+ }
+
+ /* not used...
+ public boolean IsToggleKey(char code, int KeyState)
+ {
+ boolean bRet = false;
+ if ((code == ' ') && ((KeyState & InputTables.KEYSTATE_SHIFT_MASK) != 0)) // SHIFT-SPACE
+ bRet = true;
+ return bRet;
+ }
+ */
+
+ public int DoBackSpace()
+ {
+ int ret = ACTION_NONE;
+ char code;
+
+ if (mCompositionString != "")
+ code = mCompositionString.charAt(0);
+ else
+ code = (char) 0;
+
+ if (mState != 0 && code == (char) 0)
+ {
+ // Log.v(TAG, "DoBackSpace -- Error. CompositionString is NULL. mState = " + mState);
+ return ACTION_ERROR;
+ }
+
+ switch (mState)
+ {
+ case 0: // current composition string: NULL
+ ret = ACTION_USE_INPUT_AS_RESULT;
+ break;
+ case 1: // current composition string: single consonant only
+ case 4: // current composition string: single vowel
+ mCompositionString = "";
+ mState = 0;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+
+ case 2: // current composition string: single consonant + single vowel
+ // iFirst = (vCode - HANGUL_START) / (NUM_OF_MIDDLE * NUM_OF_LAST_INDEX)
+ {
+ int fcIndex = GetFirstConsonantIndex(code);
+ code = InputTables.FirstConsonantCodes[fcIndex];
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 3: // current composition string: single consonant + single vowel + single consonant
+ // iLast = (vCode - HANGUL_START) % NUM_OF_LAST_INDEX
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ code = (char)((int)code - lcIndex);
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 2;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 5: // current composition string: a combined vowel
+ {
+ int vIndex = GetVowelIndex(code);
+ if (vIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ int newIndex = InputTables.Vowels.iMiddle[vIndex];
+ if (newIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ code = InputTables.Vowels.Code[newIndex];
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 4;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 10: // current composition string: a combined consonant
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ if (lcIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ int newIndex = InputTables.LastConsonants.iLast[lcIndex];
+ if (newIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ code = InputTables.LastConsonants.Code[newIndex];
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 11: // current composition string: single consonant + single vowel + a combined consonant
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ if (lcIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ int newIndex = InputTables.LastConsonants.iLast[lcIndex];
+ if (newIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ code = (char)((int) code - lcIndex + newIndex);
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 3;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 20: // current composition string: single consonant + a combined vowel
+ {
+ int fcIndex = GetFirstConsonantIndex(code);
+ int vIndex = GetVowelIndex(code);
+ int newIndex = InputTables.Vowels.iMiddle[vIndex];
+ if (newIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ code = ComposeCharWithIndexs(fcIndex, newIndex, 0);
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 2;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 21: // current composition string: single consonant + a combined vowel + single consonant
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ code = (char) ((int) code - lcIndex);
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 20;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ case 22: // current composition string: single consonant + a combined vowel + a combined consonant
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ if (lcIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ int newIndex = InputTables.LastConsonants.iLast[lcIndex];
+ if (newIndex < 0)
+ {
+ ret = ACTION_ERROR;
+ break;
+ }
+ code = (char)((int) code - lcIndex + newIndex);
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 21;
+ }
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ break;
+ default: ret = ACTION_ERROR; // error. should not be here in any circumstance.
+ }
+ return ret;
+ }
+
+ public int FinishAutomataWithoutInput() // Input is ended by external causes
+ {
+ int ret = ACTION_NONE;
+ if (mKoreanMode) // && mState > 0)
+ {
+ mCompleteString = "";
+ mCompositionString = "";
+ mState = 0;
+ //ret |= ACTION_UPDATE_COMPOSITIONSTR;
+ //ret |= ACTION_UPDATE_COMPLETESTR;
+ }
+ return ret;
+ }
+
+ public int DoAutomata(char code, int KeyState) // , String CurrentCompositionString)
+ {
+ // Log.v(TAG, "DoAutomata Entered - code = "+ code + " KeyState = " + KeyState + " mState = " + mState);
+
+ int result = ACTION_NONE;
+ int AlphaIndex = GetAlphabetIndex(code);
+ char hcode;
+
+ /* remove toggle key check and backspace check.
+ // check toggle key first
+ if (IsToggleKey(code, KeyState)) // SHIFT-SPACE
+ {
+ // toggle Korean/English
+ if (mState != 0) // flushing..
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mState = 0;
+ result = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ mKoreanMode = !mKoreanMode; // input mode toggle
+ }
+ else if (code == InputTables.BACK_SPACE)
+ {
+ // do back space
+ }
+ else */
+ if (AlphaIndex < 0) // white spaces...
+ {
+ if (mKoreanMode)
+ {
+ // flush Korean characters first.
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mState = 0;
+ result = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ // process the code as English
+ if ((KeyState & (InputTables.KEYSTATE_ALT_MASK | InputTables.KEYSTATE_CTRL_MASK | InputTables.KEYSTATE_FN)) == 0)
+ {
+ result |= ACTION_USE_INPUT_AS_RESULT;
+ }
+ }
+ else if (!mKoreanMode){
+ // process the code as English
+ result = ACTION_USE_INPUT_AS_RESULT;
+ }
+ else {
+ if ((KeyState & InputTables.KEYSTATE_SHIFT_MASK) == 0)
+ {
+ hcode = InputTables.NormalKeyMap.Code[AlphaIndex];
+ }
+ else
+ {
+ hcode = InputTables.ShiftedKeyMap.Code[AlphaIndex];
+ }
+ // Log.v(TAG, "--DoAutomata() - hcode = " + hcode);
+
+ switch (mState)
+ {
+ case 0: result = DoState00(hcode); break; // current composition string: NULL
+ case 1: result = DoState01(hcode); break; // current composition string: single consonant only
+ case 2: result = DoState02(hcode); break; // current composition string: single consonant + single vowel
+ case 3: result = DoState03(hcode); break; // current composition string: single consonant + single vowel + single consonant
+ case 4: result = DoState04(hcode); break; // current composition string: single vowel
+ case 5: result = DoState05(hcode); break; // current composition string: a combined vowel
+ case 10: result = DoState10(hcode); break; // current composition string: a combined consonant
+ case 11: result = DoState11(hcode); break; // current composition string: single consonant + single vowel + a combined consonant
+ case 20: result = DoState20(hcode); break; // current composition string: single consonant + a combined vowel
+ case 21: result = DoState21(hcode); break; // current composition string: single consonant + a combined vowel + single consonant
+ case 22: result = DoState22(hcode); break; // current composition string: single consonant + a combined vowel + a combined consonant
+ default: result = ACTION_ERROR; // error. should not be here in any circumstance.
+ }
+ }
+ return result;
+ };
+
+ private int DoState00(char code) // current composition string: NULL
+ {
+ // Log.v(TAG, "State 0 Entered - code = "+ code );
+ if (IsConsonant(code))
+ {
+ mState = 1;
+ }
+ else
+ {
+ mState = 4;
+ }
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += code;
+ return ACTION_UPDATE_COMPOSITIONSTR;
+ };
+
+ private int DoState01(char code) // current composition string: single consonant only
+ {
+ // Log.v(TAG, "State 1 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 01 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ char newCode = CombineLastConsonantWithCode(mCompositionString.charAt(0), code);
+ if (newCode == (char)0) // cannot combine last consonants
+ {
+ mCompleteString = mCompositionString; // flush
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else // can combine last consonants
+ {
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newCode;
+ mState = 10;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ else
+ {
+ int fcIndex = ConvertFirstConsonantCodeToIndex(mCompositionString.charAt(0));
+ int vIndex = ConvertVowelCodeToIndex(code);
+ char newCode = ComposeCharWithIndexs(fcIndex, vIndex, 0);
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newCode;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState02(char code) // current composition string: single consonant + single vowel
+ {
+ // Log.v(TAG, "State 2 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState-02 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ int lcIndex = GetLastConsonantIndex(code);
+ if (lcIndex != -1) // code can be last consonant..
+ {
+ char newCode = (char)((int) mCompositionString.charAt(0) + lcIndex);
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newCode;
+ mState = 3;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = ""; // flush
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ else // vowel
+ {
+ char vCode = GetVowel(mCompositionString.charAt(0));
+ char newCode = CombineVowelWithCode(vCode, code);
+ if (newCode != (char) 0)
+ {
+ int fcIndex = GetFirstConsonantIndex(mCompositionString.charAt(0));
+ int vIndex = ConvertVowelCodeToIndex(newCode);
+ char newChar = ComposeCharWithIndexs(fcIndex, vIndex, 0);
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 20;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 4;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ return ret;
+ };
+
+ private int DoState03(char code) // current composition string: single consonant + single vowel + single consonant
+ {
+ // Log.v(TAG, "State 3 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 03 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ int lcIndex = GetLastConsonantIndex(mCompositionString.charAt(0));
+ if (lcIndex < 0)
+ {
+ // Log.v(TAG, " -- Error. consonant, lcIndex = " + lcIndex);
+ return ACTION_ERROR;
+ }
+ char newCode = CombineLastConsonantWithCode(InputTables.LastConsonants.Code[lcIndex], code);
+ if (newCode != (char)0) // Last Consonants can be combined
+ {
+ char newChar = (char) ((int) mCompositionString.charAt(0) - lcIndex + GetLastConsonantIndex(newCode));
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 11;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ else // vowel
+ {
+ int lcIndex = GetLastConsonantIndex(mCompositionString.charAt(0));
+ if (lcIndex < 0)
+ {
+ // Log.v(TAG, " -- complete Error. vowel, lcIndex = " + lcIndex);
+ return ACTION_ERROR;
+ }
+ char newChar = (char) ((int) mCompositionString.charAt(0) - lcIndex); // remove last consonant and flush it.
+ mCompleteString = "";
+ mCompleteString += newChar;
+ int fcIndex = GetFirstConsonantIndex(InputTables.LastConsonants.Code[lcIndex]);
+ if (fcIndex < 0)
+ {
+ // Log.v(TAG, " -- composition Error, vowel, lcIndex = " + lcIndex);
+ return ACTION_ERROR;
+ }
+ int vIndex = GetVowelIndex(code);
+ char newCode = ComposeCharWithIndexs(fcIndex, vIndex, 0); // compose new composition string
+ mCompositionString = "";
+ mCompositionString += newCode;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState04(char code) // current composition string: single vowel
+ {
+ // Log.v(TAG, "State 4 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 04 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ char newCode = CombineVowelWithCode(mCompositionString.charAt(0), code);
+ if (newCode != (char) 0)
+ {
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newCode;
+ mState = 5;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 4;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ return ret;
+ };
+
+ private int DoState05(char code) // current composition string: a combined vowel
+ {
+ // Log.v(TAG, "State 5 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 05 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 4;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState10(char code) // current composition string: a combined consonant
+ {
+ // Log.v(TAG, "State 10 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 10 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ int lcIndex0 = GetLastConsonantIndex(mCompositionString.charAt(0));
+ int lcIndex1 = InputTables.LastConsonants.iLast[lcIndex0];
+ int fcIndex = InputTables.LastConsonants.iFirst[lcIndex0];
+ mCompleteString = "";
+ mCompleteString += InputTables.LastConsonants.Code[lcIndex1];
+ int vIndex = GetVowelIndex(code);
+ char newChar = ComposeCharWithIndexs(fcIndex, vIndex, 0);
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState11(char code) // current composition string: single consonant + single vowel + a combined consonant
+ {
+ // Log.v(TAG, "State 11 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 11 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ int lcIndexOrg = GetLastConsonantIndex(mCompositionString.charAt(0));
+ int fcIndexOrg = GetFirstConsonantIndex(mCompositionString.charAt(0));
+ int vIndexOrg = GetVowelIndex(mCompositionString.charAt(0));
+ int lcIndexNew = InputTables.LastConsonants.iLast[lcIndexOrg];
+ char newChar = ComposeCharWithIndexs(fcIndexOrg, vIndexOrg, lcIndexNew);
+ int fcIndexNew = InputTables.LastConsonants.iFirst[lcIndexOrg];
+ int vIndexNew = ConvertVowelCodeToIndex(code);
+ mCompleteString = "";
+ mCompleteString += newChar;
+ newChar = ComposeCharWithIndexs(fcIndexNew, vIndexNew, 0);
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState20(char code) // current composition string: single consonant + a combined vowel
+ {
+ // Log.v(TAG, "State 20 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 20 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ int lcIndex = ConvertLastConsonantCodeToIndex(code);
+ if (lcIndex < 0) // cannot compose the code with composition string. flush it.
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else // compose..
+ {
+ char newChar = mCompositionString.charAt(0);
+ newChar = (char)((int)newChar + lcIndex);
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 21;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+ else
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 4;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState21(char code) // current composition string: single consonant + a combined vowel + single consonant
+ {
+ // Log.v(TAG, "State 21 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 20 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ int lcIndex = GetLastConsonantIndex(mCompositionString.charAt(0));
+ int lcIndexTemp = ConvertLastConsonantCodeToIndex(code);
+ if (lcIndexTemp < 0)
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else {
+ int lcIndexNew = CombineLastConsonantWithIndex(lcIndex, lcIndexTemp);
+ if (lcIndexNew < 0)
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ char newChar = mCompositionString.charAt(0);
+ newChar = (char)((int)newChar - lcIndex + lcIndexNew);
+ mCompleteString = "";
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 22;
+ ret = ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ }
+
+ }
+ else
+ {
+ char newChar = mCompositionString.charAt(0);
+ int lcIndex = GetLastConsonantIndex(newChar);
+ newChar = (char)((int)newChar - lcIndex);
+ mCompleteString = "";
+ mCompleteString += newChar;
+ int fcIndex = ConvertFirstConsonantCodeToIndex(InputTables.LastConsonants.Code[lcIndex]);
+ int vIndex = ConvertVowelCodeToIndex(code);
+ newChar = ComposeCharWithIndexs(fcIndex, vIndex, 0);
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+
+ private int DoState22(char code) // current composition string: single consonant + a combined vowel + a combined consonant
+ {
+ // Log.v(TAG, "State 22 Entered - code = "+ code );
+ if (mCompositionString == "")
+ {
+ // Log.v(TAG, "DoState 22 -- Error. CompositionString is NULL");
+ return ACTION_ERROR;
+ }
+
+ int ret = ACTION_NONE;
+ if (IsConsonant(code))
+ {
+ mCompleteString = mCompositionString;
+ mCompositionString = "";
+ mCompositionString += code;
+ mState = 1;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ else
+ {
+ int lcIndex0 = GetLastConsonantIndex(mCompositionString.charAt(0));
+ int lcIndex1 = InputTables.LastConsonants.iLast[lcIndex0];
+ int fcIndex = InputTables.LastConsonants.iFirst[lcIndex0];
+ mCompleteString = "";
+ mCompleteString += InputTables.LastConsonants.Code[lcIndex1];
+ int vIndex = GetVowelIndex(code);
+ char newChar = ComposeCharWithIndexs(fcIndex, vIndex, 0);
+ mCompositionString = "";
+ mCompositionString += newChar;
+ mState = 2;
+ ret = ACTION_UPDATE_COMPLETESTR | ACTION_UPDATE_COMPOSITIONSTR;
+ }
+ return ret;
+ };
+}
diff --git a/src/com/halbae87/koreanbasicime/LatinKeyboard.java b/src/com/halbae87/koreanbasicime/LatinKeyboard.java
new file mode 100644
index 0000000..b6efce1
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/LatinKeyboard.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ */
+package com.halbae87.koreanbasicime;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.inputmethodservice.Keyboard.Row;
+import android.view.inputmethod.EditorInfo;
+
+@SuppressLint("InlinedApi")
+@SuppressWarnings("unused")
+public class LatinKeyboard extends Keyboard {
+
+ private Key mEnterKey;
+
+ public LatinKeyboard(Context context, int xmlLayoutResId) {
+ super(context, xmlLayoutResId);
+ }
+
+ 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;
+ }
+
+ /**
+ * This looks at the ime options given by the current editor, to set the
+ * appropriate label on the keyboard's enter key (if it has one).
+ */
+ void setImeOptions(Resources res, int options) {
+ if (mEnterKey == null) {
+ return;
+ }
+
+ 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_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:
+ mEnterKey.icon = res.getDrawable(
+ R.drawable.sym_keyboard_return);
+ mEnterKey.label = null;
+ break;
+ }
+ }
+
+ static class LatinKey extends Keyboard.Key {
+
+ public LatinKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
+ super(res, parent, x, y, parser);
+ }
+
+ /**
+ * Overriding this method so that we can reduce the target area for the key that
+ * closes the keyboard.
+ */
+ @Override
+ public boolean isInside(int x, int y) {
+ return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);
+ }
+ }
+
+}
diff --git a/src/com/halbae87/koreanbasicime/LatinKeyboardView.java b/src/com/halbae87/koreanbasicime/LatinKeyboardView.java
new file mode 100644
index 0000000..77a7c0e
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/LatinKeyboardView.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ */
+
+package com.halbae87.koreanbasicime;
+
+import android.content.Context;
+// import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.inputmethodservice.Keyboard.Key;
+import android.util.AttributeSet;
+
+public class LatinKeyboardView extends KeyboardView {
+
+ static final int KEYCODE_OPTIONS = -100;
+
+ public LatinKeyboardView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected boolean onLongPress(Key key) {
+ /*
+ if (key.codes[0] == Keyboard.KEYCODE_ALT) {
+ getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null);
+ return true;
+ } else {
+ return super.onLongPress(key);
+ }
+ */
+ return super.onLongPress(key);
+ }
+}
diff --git a/src/com/halbae87/koreanbasicime/SoftKeyboard.java b/src/com/halbae87/koreanbasicime/SoftKeyboard.java
new file mode 100644
index 0000000..7e58840
--- /dev/null
+++ b/src/com/halbae87/koreanbasicime/SoftKeyboard.java
@@ -0,0 +1,1099 @@
+/*
+ * 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.
+ */
+
+/*
+ * halbae87: this project is created from Soft Keyboard Sample source
+ */
+
+package com.halbae87.koreanbasicime;
+
+import android.annotation.SuppressLint;
+import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.text.method.MetaKeyKeyListener;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/*
+ just for reference
+ Hangul/English: KEYCODE_KANA (218)
+ Hanja: KEYCODE_EISU (212)
+ Win (Left): KEYCODE_META_LEFT (117)
+ Menu: KEYCODE_MENU (82)
+ sys. req./prtscr: KEYCODE_SYSRQ (120)
+ */
+/**
+ * Example of writing an input method for a soft keyboard. This code is
+ * focused on simplicity over completeness, so it should in no way be considered
+ * to be a complete soft keyboard implementation. Its purpose is to provide
+ * a basic example for how you would get started writing an input method, to
+ * be fleshed out as appropriate.
+ */
+
+
+
+@SuppressLint("InlinedApi")
+@SuppressWarnings("unused")
+public class SoftKeyboard extends InputMethodService
+ implements KeyboardView.OnKeyboardActionListener {
+ static final boolean DEBUG = false;
+
+ /**
+ * This boolean indicates the optional example code for performing
+ * processing of hard keys in addition to regular text generation
+ * from on-screen interaction. It would be used for input methods that
+ * perform language translations (such as converting text entered on
+ * a QWERTY keyboard to Chinese), but may not be used for input methods
+ * that are primarily intended to be used for on-screen text entry.
+ */
+ static final boolean PROCESS_HARD_KEYS = true;
+ private final String TAG = "SoftKeyboard";
+
+ private KeyboardView mInputView;
+ // no suggestion for Korean
+ // private CandidateView mCandidateView;
+ // private CompletionInfo[] mCompletions;
+
+ private StringBuilder mComposing = new StringBuilder();
+ // private boolean mPredictionOn;
+ // private boolean mCompletionOn;
+ private int mLastDisplayWidth;
+ private boolean mCapsLock;
+ private long mLastShiftTime;
+ private long mMetaState;
+ private boolean mHwShift = false;
+
+ private LatinKeyboard mSymbolsKeyboard;
+ private LatinKeyboard mSymbolsShiftedKeyboard;
+ private LatinKeyboard mQwertyKeyboard;
+
+ // special key definitions.
+ static final int KEYCODE_HANGUL = 218; // KeyEvent.KEYCODE_KANA is available from API 16
+ static final int KEYCODE_HANJA = 212; // KeyEvent.KEYCODE_EISU is available from API 16
+ static final int KEYCODE_WIN_LEFT = 117; // KeyEvent.KEYCODE_META_LEFT is available from API 11
+ static final int KEYCODE_SYSREQ = 120; // KeyEvent.KEYCODE_SYSREQ is available from API 11
+
+ private LatinKeyboard mKoreanKeyboard;
+ private LatinKeyboard mKoreanShiftedKeyboard;
+ private LatinKeyboard mBackupKeyboard;
+
+ private LatinKeyboard mCurKeyboard;
+
+ private String mWordSeparators;
+
+ private KoreanAutomata kauto;
+ private boolean mNoKorean = false;
+
+ /**
+ * Main initialization of the input method component. Be sure to call
+ * to super class.
+ */
+
+ @Override public void onCreate() {
+ super.onCreate();
+ mWordSeparators = getResources().getString(R.string.word_separators);
+
+ // enable for debug purpose only. otherwise, it will stuck here.
+ // android.os.Debug.waitForDebugger();
+ }
+
+ /**
+ * This is the point where you can do all of your UI initialization. It
+ * is called after creation and any configuration change.
+ */
+ @Override public void onInitializeInterface() {
+ if (mQwertyKeyboard != null) {
+ // Configuration changes can happen after the keyboard gets recreated,
+ // so we need to be able to re-build the keyboards if the available
+ // space has changed.
+ int displayWidth = getMaxWidth();
+ if (displayWidth == mLastDisplayWidth) return;
+ mLastDisplayWidth = displayWidth;
+ }
+ kauto = new KoreanAutomata();
+ mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
+ mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
+ mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
+ mKoreanKeyboard = new LatinKeyboard(this, R.xml.korean);
+ mKoreanShiftedKeyboard = new LatinKeyboard(this, R.xml.korean_shifted);
+ mBackupKeyboard = null;
+ }
+
+ /**
+ * Called by the framework when your view for creating input needs to
+ * be generated. This will be called the first time your input method
+ * is displayed, and every time it needs to be re-created such as due to
+ * a configuration change.
+ */
+ @Override public View onCreateInputView() {
+ // Log.v(TAG,"onCreateInputView ---- enter");
+ mInputView = (KeyboardView) getLayoutInflater().inflate(
+ R.layout.input, null);
+ mInputView.setOnKeyboardActionListener(this);
+ mInputView.setKeyboard(mQwertyKeyboard);
+ // Log.v(TAG,"onCreateInputView ---- leave");
+ return mInputView;
+ }
+
+ /**
+ * Called by the framework when your view for showing candidates needs to
+ * be generated, like {@link #onCreateInputView}.
+ */
+ /* no candidate window for Korean
+ @Override public View onCreateCandidatesView() {
+ mCandidateView = new CandidateView(this);
+ mCandidateView.setService(this);
+ return mCandidateView;
+ }
+ */
+
+ // add this. no need full screen IME mode for this IME. ---- KGS need to check...
+
+ @Override
+ public void onUpdateExtractingVisibility(EditorInfo ei) {
+ ei.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
+ super.onUpdateExtractingVisibility(ei);
+ }
+
+
+ /**
+ * This is the main point where we do our initialization of the input method
+ * to begin operating on an application. At this point we have been
+ * bound to the client, and are now receiving all of the detailed information
+ * about the target of our edits.
+ */
+ @Override public void onStartInput(EditorInfo attribute, boolean restarting) {
+ super.onStartInput(attribute, restarting);
+ // Log.v(TAG,"onStartInput ---- enter");
+
+ // Reset our state. We want to do this even if restarting, because
+ // the underlying state of the text editor could have changed in any way.
+ mComposing.setLength(0);
+ // updateCandidates();
+
+ if (!restarting) {
+ // Clear shift states.
+ mMetaState = 0;
+ }
+
+ // mPredictionOn = false;
+ // mCompletionOn = false;
+ // mCompletions = null;
+ kauto.FinishAutomataWithoutInput();
+
+ // We are now going to initialize our state based on the type of
+ // text being edited.
+ switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
+ case EditorInfo.TYPE_CLASS_NUMBER:
+ case EditorInfo.TYPE_CLASS_DATETIME:
+ // Numbers and dates default to the symbols keyboard, with
+ // no extra features.
+ mCurKeyboard = mSymbolsKeyboard;
+ mNoKorean = true;
+ if (kauto.IsKoreanMode())
+ kauto.ToggleMode();
+ break;
+
+ case EditorInfo.TYPE_CLASS_PHONE:
+ // Phones will also default to the symbols keyboard, though
+ // often you will want to have a dedicated phone keyboard.
+ mCurKeyboard = mSymbolsKeyboard;
+ mNoKorean = true;
+ if (kauto.IsKoreanMode())
+ kauto.ToggleMode();
+ break;
+
+ case EditorInfo.TYPE_CLASS_TEXT:
+ // This is general text editing. We will default to the
+ // normal alphabetic keyboard, and assume that we should
+ // be doing predictive text (showing candidates as the
+ // user types).
+
+ // mPredictionOn = false;
+
+ // We now look for a few special variations of text that will
+ // modify our behavior.
+ int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
+ // Do not display predictions / what the user is typing
+ // when they are entering a password.
+ mNoKorean = true;
+ // mPredictionOn = false;
+ }
+
+ if (mNoKorean || !kauto.IsKoreanMode())
+ mCurKeyboard = mQwertyKeyboard;
+ else
+ mCurKeyboard = mKoreanKeyboard;
+ //if (kauto.IsKoreanMode())
+ // kauto.ToggleMode();
+
+ /* KGS, do I need these?
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+ || variation == EditorInfo.TYPE_TEXT_VARIATION_URI
+ || variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
+ // Our predictions are not useful for e-mail addresses
+ // or URIs.
+ mPredictionOn = false;
+ }
+
+ if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+ // If this is an auto-complete text view, then our predictions
+ // will not be shown and instead we will allow the editor
+ // to supply their own. We only show the editor's
+ // candidates when in fullscreen mode, otherwise relying
+ // own it displaying its own UI.
+ mPredictionOn = false;
+ mCompletionOn = isFullscreenMode();
+ }
+ */
+
+ // We also want to look at the current state of the editor
+ // to decide whether our alphabetic keyboard should start out
+ // shifted.
+ updateShiftKeyState(attribute);
+ break;
+
+ default:
+ // For all unknown input types, default to the alphabetic
+ // keyboard with no special features.
+ // mCurKeyboard = mQwertyKeyboard;
+
+ if (kauto.IsKoreanMode())
+ mCurKeyboard = mKoreanKeyboard;
+ else
+ mCurKeyboard = mQwertyKeyboard;
+ updateShiftKeyState(attribute);
+ }
+
+ // Update the label on the enter key, depending on what the application
+ // says it will do.
+ mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
+ // Log.v(TAG,"onStartInput ---- leave");
+}
+
+ @Override public void onFinishInputView(boolean finishingInput) {
+ super.onFinishInputView(finishingInput);
+ // Log.v(TAG,"onFinishInputView ---- enter");
+ if (kauto.IsKoreanMode())
+ kauto.FinishAutomataWithoutInput();
+ mKoreanKeyboard.setShifted(false);
+ // Log.v(TAG,"onFinishInputView ---- leave");
+ }
+
+ /**
+ * This is called when the user is done editing a field. We can use
+ * this to reset our state.
+ */
+
+ @Override public void onFinishInput() {
+ super.onFinishInput();
+ // Log.v(TAG,"onFinishInput ---- enter");
+
+ // Clear current composing text and candidates.
+ kauto.FinishAutomataWithoutInput();
+ mNoKorean = false;
+ /*
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null)
+ commitTyped(ic);
+ */
+ mComposing.setLength(0);
+ // updateCandidates();
+
+ // We only hide the candidates window when finishing input on
+ // a particular editor, to avoid popping the underlying application
+ // up and down if the user is entering text into the bottom of
+ // its window.
+ setCandidatesViewShown(false);
+
+ mCurKeyboard = mQwertyKeyboard;
+ if (mInputView != null) {
+ // let's reset Korean input mode in soft keyboard mode. H/W keyboard will not be affected with this.
+ // if (kauto.IsKoreanMode())
+ // kauto.ToggleMode();
+ mInputView.closing();
+ }
+ // Log.v(TAG,"onFinishInput ---- leave");
+ }
+
+ @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
+ super.onStartInputView(attribute, restarting);
+ // Log.v(TAG,"onStartInputView ---- enter, restarting = " + restarting);
+ // Apply the selected keyboard to the input view.
+ if (kauto.IsKoreanMode())
+ mCurKeyboard = mKoreanKeyboard;
+ else
+ mCurKeyboard = mQwertyKeyboard;
+ mInputView.setKeyboard(mCurKeyboard);
+ mInputView.closing();
+ // Log.v(TAG,"onStartInputView ---- leave");
+ }
+
+ /**
+ * Deal with the editor reporting movement of its cursor.
+ */
+ @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 && (newSelStart != candidatesEnd
+ || newSelEnd != candidatesEnd)) {
+ mComposing.setLength(0);
+ // Log.v(TAG, "onUpdateSelection --- called");
+ kauto.FinishAutomataWithoutInput();
+ // updateCandidates();
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+ }
+
+ /**
+ * This tells us about completions that the editor has determined based
+ * on the current text in it. We want to use this in fullscreen mode
+ * to show the completions ourself, since the editor can not be seen
+ * in that situation.
+ */
+ /* do nothing
+ @Override public void onDisplayCompletions(CompletionInfo[] completions) {
+ if (mCompletionOn) {
+ mCompletions = completions;
+ if (completions == null) {
+ setSuggestions(null, false, false);
+ return;
+ }
+
+ List stringList = new ArrayList();
+ for (int i=0; i<(completions != null ? completions.length : 0); i++) {
+ CompletionInfo ci = completions[i];
+ if (ci != null) stringList.add(ci.getText().toString());
+ }
+ setSuggestions(stringList, true, true);
+ }
+ }
+ */
+ /**
+ * This translates incoming hard key events in to edit operations on an
+ * InputConnection. It is only needed when using the
+ * PROCESS_HARD_KEYS option.
+ */
+ private boolean translateKeyDown(int keyCode, KeyEvent event) {
+ // // Log.v(TAG, "translateKeyDown() called");
+ mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState,
+ keyCode, event);
+ int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState));
+ mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
+ InputConnection ic = getCurrentInputConnection();
+ if (c == 0 || ic == null) {
+ return false;
+ }
+
+ boolean dead = false;
+
+ if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ dead = true;
+ c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
+ }
+
+ if (mComposing.length() > 0) {
+ char accent = mComposing.charAt(mComposing.length() -1 );
+ int composed = KeyEvent.getDeadChar(accent, c);
+
+ if (composed != 0) {
+ c = composed;
+ mComposing.setLength(mComposing.length()-1);
+ }
+ }
+
+ onKey(c, null);
+
+ return true;
+ }
+
+ /**
+ * Use this to monitor key events being delivered to the application.
+ * We get first crack at them, and can either resume them or let them
+ * continue to the app.
+ */
+ @SuppressWarnings({ })
+ @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // // Log.v(TAG, "onKeyDown - keyCode = " + event.keyCodeToString(keyCode) + " ("+ keyCode +")");
+ if (event.isShiftPressed())
+ mHwShift = true;
+ // if ALT or CTRL meta keys are using, the key event should not be touched here and be passed through to.
+ if ((event.getMetaState() & (KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK)) == 0)
+ {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ // The InputMethodService already takes care of the back
+ // key for us, to dismiss the input method if it is shown.
+ // However, our keyboard could be showing a pop-up window
+ // that back should dismiss, so we first allow it to do that.
+ if (event.getRepeatCount() == 0 && mInputView != null) {
+ if (mInputView.handleBack()) {
+ return true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ if ( /* (event.getFlags() & event.FLAG_SOFT_KEYBOARD) != 0 && */!mNoKorean && event.isShiftPressed())
+ {
+ if (mComposing.length() > 0)
+ {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null)
+ commitTyped(ic);
+ }
+ if (kauto.IsKoreanMode())
+ {
+ kauto.FinishAutomataWithoutInput();
+ }
+ kauto.ToggleMode();
+ // // Log.v(TAG, "onKeyDown -- SHIFT SPACE. Korean Mode = " + kauto.IsKoreanMode());
+ return true;
+ }
+ else
+ {
+ translateKeyDown(keyCode, event);
+ return true;
+ }
+ // break;
+
+ // for Korean Keyboard only.
+ case KEYCODE_HANGUL:
+ if (!mNoKorean)
+ {
+ // // Log.v(TAG, "onKeyDown -- KEYCODE_HANGUL. Korean Mode = " + kauto.IsKoreanMode() +" Let's toggle it.");
+ if (mComposing.length() > 0)
+ {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null)
+ commitTyped(ic);
+ }
+ if (kauto.IsKoreanMode())
+ {
+ kauto.FinishAutomataWithoutInput();
+ }
+ kauto.ToggleMode();
+ // consume this event.
+ return true;
+ }
+ else
+ {
+ // // Log.v(TAG, "onKeyDown -- KEYCODE_HANGUL. but Korean is not allowed in this context. ignore this.");
+ }
+ break;
+
+ case KeyEvent.KEYCODE_DEL:
+ // Special handling of the delete key: if we currently are
+ // composing text for the user, we want to modify that instead
+ // of let the application to the delete itself.
+ if (mComposing.length() > 0) {
+ onKey(Keyboard.KEYCODE_DELETE, null);
+ return true;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_ENTER:
+ // Let the underlying text editor always handle these.
+ if (kauto.IsKoreanMode())
+ {
+ kauto.FinishAutomataWithoutInput();
+ }
+ return false;
+
+ default:
+ // For all other keys, if we want to do transformations on
+ // text being entered with a hard keyboard, we need to process
+ // it and do the appropriate action.
+ if (PROCESS_HARD_KEYS) {
+ if (keyCode == KeyEvent.KEYCODE_SPACE
+ && (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) {
+ // A silly example: in our input method, Alt+Space
+ // is a shortcut for 'android' in lower case.
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ // First, tell the editor that it is no longer in the
+ // shift state, since we are consuming this.
+ ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
+ keyDownUp(KeyEvent.KEYCODE_A);
+ keyDownUp(KeyEvent.KEYCODE_N);
+ keyDownUp(KeyEvent.KEYCODE_D);
+ keyDownUp(KeyEvent.KEYCODE_R);
+ keyDownUp(KeyEvent.KEYCODE_O);
+ keyDownUp(KeyEvent.KEYCODE_I);
+ keyDownUp(KeyEvent.KEYCODE_D);
+ // And we consume this event.
+ return true;
+ }
+ }
+ if ( /* mPredictionOn && */ translateKeyDown(keyCode, event)) {
+ return true;
+ }
+ }
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Use this to monitor key events being delivered to the application.
+ * We get first crack at them, and can either resume them or let them
+ * continue to the app.
+ */
+ @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // If we want to do transformations on text being entered with a hard
+ // keyboard, we need to process the up events to update the meta key
+ // state we are tracking.
+ // // Log.v(TAG, "onKeyUp - keyCode = " + event.keyCodeToString(keyCode));
+ if (PROCESS_HARD_KEYS) {
+ // if (mPredictionOn) {
+ mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
+ keyCode, event);
+ // }
+ }
+ mHwShift = false;
+
+ return super.onKeyUp(keyCode, event);
+ }
+
+ /**
+ * Helper function to commit any text being composed in to the editor.
+ */
+ private void commitTyped(InputConnection inputConnection) {
+ // Log.v(TAG, "commitTyped ------- mComposing = [" + mComposing +"] length = " + mComposing.length());
+ if (mComposing.length() > 0) {
+ inputConnection.commitText(mComposing, 1);
+ mComposing.setLength(0);
+ // updateCandidates();
+ }
+ }
+
+ /**
+ * Helper to update the shift state of our keyboard based on the initial
+ * editor state.
+ */
+ // KGS need to fix..
+ private void updateShiftKeyState(EditorInfo attr) {
+ if (attr != null && mInputView != null)
+ {
+ if (mQwertyKeyboard == mInputView.getKeyboard())
+ {
+ int caps = 0;
+ EditorInfo ei = getCurrentInputEditorInfo();
+ if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
+ caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
+ }
+ // // Log.v(TAG, "updateShiftKeyState - mCapsLock = " + mCapsLock + ", caps = " + caps);
+ mInputView.setShifted(mCapsLock || caps != 0);
+ }
+ else if (mKoreanShiftedKeyboard == mInputView.getKeyboard())
+ {
+ mKoreanShiftedKeyboard.setShifted(false);
+ mInputView.setKeyboard(mKoreanKeyboard);
+ mKoreanKeyboard.setShifted(false);
+ }
+ }
+ }
+
+ /**
+ * Helper to determine if a given character code is alphabetic.
+ */
+ private boolean isAlphabet(int code) {
+ if (Character.isLetter(code)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Helper to send a key down / key up pair to the current editor.
+ */
+ private void keyDownUp(int keyEventCode) {
+ getCurrentInputConnection().sendKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
+ getCurrentInputConnection().sendKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
+ }
+
+ /**
+ * Helper to send a character to the editor as raw key events.
+ */
+ private void sendKey(int keyCode) {
+ // Log.v(TAG, "sendKey ------- keyCode = " + keyCode);
+ switch (keyCode) {
+ case '\n':
+ keyDownUp(KeyEvent.KEYCODE_ENTER);
+ // Log.v(TAG, "sendKey ------- ENTER");
+ break;
+ default:
+ if (keyCode >= '0' && keyCode <= '9') {
+ keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
+ // Log.v(TAG, "sendKey ------- DIGIT [" + (keyCode - '0') + "]");
+ } else {
+ getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
+ // Log.v(TAG, "sendKey ------- maybe white space. valueOf [" + (String.valueOf((char) keyCode)) + "]");
+ }
+ break;
+ }
+ }
+
+ // Implementation of KeyboardViewListener
+
+ public void onKey(int primaryCode, int[] keyCodes) {
+ // Log.v(TAG, "onKey - primaryCode = " + primaryCode);
+ if (isWordSeparator(primaryCode)) {
+ // Log.v(TAG, " -- separator = [" + (char) primaryCode + "]");
+ // Handle separator
+ if (mComposing.length() > 0) {
+ commitTyped(getCurrentInputConnection());
+ }
+ if (kauto.IsKoreanMode())
+ kauto.FinishAutomataWithoutInput();
+ sendKey(primaryCode);
+ if (mInputView != null)
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
+ handleBackspace();
+ } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
+ handleShift();
+ } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
+ handleClose();
+ return;
+ } else if (primaryCode == Keyboard.KEYCODE_ALT) {
+ // Show a menu or somethin'
+ // Log.v(TAG,"onKey ---------- KEYCODE_ALT. let's use it as input mode change");
+ if (mInputView != null)
+ {
+ if (!mNoKorean)
+ {
+ Keyboard current;
+ if (kauto.IsKoreanMode())
+ {
+ // Log.v(TAG, " change keyboard view to English");
+ current = mQwertyKeyboard;
+ }
+ else
+ {
+ // Log.v(TAG, " change keyboard view to Korean");
+ current = mKoreanKeyboard;
+ }
+ mInputView.setKeyboard(current);
+ if (mComposing.length() > 0)
+ {
+ commitTyped(getCurrentInputConnection());
+ }
+ kauto.ToggleMode();
+ if (current == mQwertyKeyboard || current == mKoreanKeyboard)
+ {
+ current.setShifted(false);
+ }
+ }
+ else
+ {
+ // Log.v(TAG, " mNoKorean is ture. ignore the toggle key");
+ }
+ }
+ } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
+ && mInputView != null) {
+ Keyboard current = mInputView.getKeyboard();
+ if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
+ if (mBackupKeyboard != null)
+ current = mBackupKeyboard;
+ else
+ current = mQwertyKeyboard;
+ mBackupKeyboard = null;
+ // reset Korean input mode.
+ if (current == mQwertyKeyboard && kauto.IsKoreanMode())
+ kauto.ToggleMode();
+ } else {
+ mBackupKeyboard = (LatinKeyboard) current;
+ current = mSymbolsKeyboard;
+ // need to submit current composition string
+ if (mComposing.length() > 0)
+ {
+ commitTyped(getCurrentInputConnection());
+ }
+ if (kauto.IsKoreanMode())
+ kauto.FinishAutomataWithoutInput();
+ }
+ mInputView.setKeyboard(current);
+ if (current == mSymbolsKeyboard) {
+ current.setShifted(false);
+ }
+ } else {
+ handleCharacter(primaryCode, keyCodes);
+ }
+ }
+
+ public void onText(CharSequence text) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ ic.beginBatchEdit();
+ if (mComposing.length() > 0) {
+ commitTyped(ic);
+ }
+ ic.commitText(text, 0);
+ ic.endBatchEdit();
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+
+ /**
+ * Update the list of available candidates from the current composing
+ * text. This will need to be filled in by however you are determining
+ * candidates.
+ */
+ /* no candidate window
+ private void updateCandidates() {
+ if (!mCompletionOn) {
+ if (mComposing.length() > 0) {
+ ArrayList list = new ArrayList();
+ list.add(mComposing.toString());
+ setSuggestions(list, true, true);
+ } else {
+ setSuggestions(null, false, false);
+ }
+ }
+ }
+
+ public void setSuggestions(List suggestions, boolean completions,
+ boolean typedWordValid) {
+ if (suggestions != null && suggestions.size() > 0) {
+ setCandidatesViewShown(true);
+ } else if (isExtractViewShown()) {
+ setCandidatesViewShown(true);
+ }
+ if (mCandidateView != null) {
+ mCandidateView.setSuggestions(suggestions, completions, typedWordValid);
+ }
+ }
+ */
+ private void handleBackspace() {
+ // boolean cont = false;
+ if (kauto.IsKoreanMode())
+ {
+ int ret = kauto.DoBackSpace();
+ if (ret == KoreanAutomata.ACTION_ERROR)
+ {
+ // // Log.v(TAG, "handleBackspace() - calling DoBackSpace() failed.");
+ //// Log.v(TAG, " mCompositionString = [" + kauto.GetCompositionString()+"]");
+ // // Log.v(TAG, " mState = " + kauto.GetState());
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ return;
+ }
+ if ((ret & KoreanAutomata.ACTION_UPDATE_COMPOSITIONSTR) != 0)
+ {
+ if (kauto.GetCompositionString() != "")
+ {
+ // mComposing.setLength(0);
+ if (mComposing.length() > 0)
+ {
+ mComposing.replace(mComposing.length() -1, mComposing.length(), kauto.GetCompositionString());
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ }
+ // mComposing.append(kauto.GetCompositionString());
+
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ return;
+ }
+ /*
+ else
+ {
+ cont = true;
+ // mComposing.setLength(0);
+ // getCurrentInputConnection().commitText("", 0);
+ }
+ */
+ }
+ /*
+ if ((ret & KoreanAutomata.ACTION_USE_INPUT_AS_RESULT) != 0)
+ {
+ // // Log.v(TAG, "handleBackspace() - something unexpected behavior happened during DoBackSpace()..");
+ // // Log.v(TAG, " - Let it go through.");
+ cont = true;
+ }
+ if (!cont)
+ return;
+ */
+ // otherwise, just leave it.
+ }
+ final int length = mComposing.length();
+ if (length > 1) {
+ mComposing.delete(length - 1, length);
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ // updateCandidates();
+ } else if (length > 0) {
+ mComposing.setLength(0);
+ getCurrentInputConnection().commitText("", 0);
+ // updateCandidates();
+ } else {
+ keyDownUp(KeyEvent.KEYCODE_DEL);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ }
+
+ private void handleShift() {
+ if (mInputView == null) {
+ return;
+ }
+
+ Keyboard currentKeyboard = mInputView.getKeyboard();
+ if (mQwertyKeyboard == currentKeyboard) {
+ // Alphabet keyboard
+ checkToggleCapsLock();
+ mInputView.setShifted(mCapsLock || !mInputView.isShifted());
+ }
+ // add Korean Keyboards
+ else if (currentKeyboard == mKoreanKeyboard)
+ {
+ // // Log.v(TAG,"handleShift -- shift on Korean Keyboard");
+ mKoreanKeyboard.setShifted(true);
+ mInputView.setKeyboard(mKoreanShiftedKeyboard);
+ mKoreanShiftedKeyboard.setShifted(true);
+ }
+ else if (currentKeyboard == mKoreanShiftedKeyboard)
+ {
+ // // Log.v(TAG,"handleShift -- shift off Korean Keyboard");
+ mKoreanShiftedKeyboard.setShifted(false);
+ mInputView.setKeyboard(mKoreanKeyboard);
+ mKoreanKeyboard.setShifted(false);
+ }
+ // end of Korean keyboard care..
+ else if (currentKeyboard == mSymbolsKeyboard) {
+ mSymbolsKeyboard.setShifted(true);
+ mInputView.setKeyboard(mSymbolsShiftedKeyboard);
+ mSymbolsShiftedKeyboard.setShifted(true);
+ } else if (currentKeyboard == mSymbolsShiftedKeyboard) {
+ mSymbolsShiftedKeyboard.setShifted(false);
+ mInputView.setKeyboard(mSymbolsKeyboard);
+ mSymbolsKeyboard.setShifted(false);
+ }
+ }
+
+ private void handleCharacter(int primaryCode, int[] keyCodes) {
+ int keyState = InputTables.KEYSTATE_NONE;
+
+ if (isInputViewShown()) {
+ if (mInputView.isShifted()) {
+ primaryCode = Character.toUpperCase(primaryCode);
+ keyState |= InputTables.KEYSTATE_SHIFT;
+ }
+ }
+ // for h/w keyboard....
+ if (mHwShift)
+ keyState |= InputTables.KEYSTATE_SHIFT;
+ // Log.v(TAG, "handleCharacter() - POS 1");
+
+// if (isAlphabet(primaryCode) && mPredictionOn ) {
+ if (isAlphabet(primaryCode)) {
+ int ret = kauto.DoAutomata((char )primaryCode, keyState);
+ if (ret < 0)
+ {
+ // Log.v(TAG,"handleCharacter() - DoAutomata() call failed. primaryCode = " + primaryCode + " keyStete = " + keyState);
+ if (kauto.IsKoreanMode())
+ kauto.ToggleMode();
+ }
+ else
+ {
+ // debug block..
+ // Log.v(TAG, "handleCharacter - After calling DoAutomata()");
+ // Log.v(TAG, " KoreanMode = [" + (kauto.IsKoreanMode()? "true" : "false") + "]");
+ // Log.v(TAG, " CompleteString = [" + kauto.GetCompleteString() + "]");
+ // Log.v(TAG, " CompositionString = [" + kauto.GetCompositionString() + "]");
+ // Log.v(TAG, " State = [" + kauto.GetState() + "]");
+ // Log.v(TAG, " ret = [" + ret + "]");
+
+ if ((ret & KoreanAutomata.ACTION_UPDATE_COMPLETESTR) != 0)
+ {
+ // Log.v(TAG, "--- UPDATE_COMPLETESTR");
+ // mComposing.setLength(0);
+ if (mComposing.length() > 0)
+ mComposing.replace(mComposing.length()-1, mComposing.length(), kauto.GetCompleteString());
+ else
+ mComposing.append(kauto.GetCompleteString());
+ if (mComposing.length() > 0) {
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ // commitTyped(getCurrentInputConnection());
+ }
+ }
+ if ((ret & KoreanAutomata.ACTION_UPDATE_COMPOSITIONSTR) != 0)
+ {
+ // Log.v(TAG, "--- UPDATE_COMPOSITIONSTR");
+ // mComposing.setLength(0);
+ if ((mComposing.length() > 0) && ((ret & KoreanAutomata.ACTION_UPDATE_COMPLETESTR) == 0))
+ mComposing.replace(mComposing.length()-1, mComposing.length(), kauto.GetCompositionString());
+ else
+ mComposing.append(kauto.GetCompositionString());
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ }
+ }
+ if ((ret & KoreanAutomata.ACTION_USE_INPUT_AS_RESULT) != 0)
+ {
+ // Log.v(TAG, "--- USE_INPUT_AS_RESULT");
+ mComposing.append((char) primaryCode);
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ // updateCandidates();
+ } else {
+ // Log.v(TAG, "handleCharacter() - POS 2");
+ int ret = kauto.DoAutomata((char )primaryCode, keyState);
+ if (ret < 0)
+ {
+ // Log.v(TAG,"handleCharacter() - DoAutomata() call failed. primaryCode = " + primaryCode + " keyStete = " + keyState);
+ if (kauto.IsKoreanMode())
+ kauto.ToggleMode();
+ }
+ else
+ {
+ // Log.v(TAG, "handleCharacter - After calling DoAutomata()");
+ // Log.v(TAG, " KoreanMode = [" + (kauto.IsKoreanMode()? "true" : "false") + "]");
+ // Log.v(TAG, " CompleteString = [" + kauto.GetCompleteString() + "]");
+ // Log.v(TAG, " CompositionString = [" + kauto.GetCompositionString() + "]");
+ // Log.v(TAG, " State = [" + kauto.GetState() + "]");
+ // Log.v(TAG, " ret = [" + ret + "]");
+ if ((ret & KoreanAutomata.ACTION_UPDATE_COMPLETESTR) != 0)
+ {
+ // // Log.v(TAG, " update complete string - " + kauto.GetCompleteString());
+ // mComposing.setLength(0);
+ if (mComposing.length() > 0)
+ mComposing.replace(mComposing.length()-1, mComposing.length(), kauto.GetCompleteString());
+ else
+ mComposing.append(kauto.GetCompleteString());
+ // mComposing.append(kauto.GetCompleteString());
+ if (mComposing.length() > 0) {
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ // commitTyped(getCurrentInputConnection());
+ }
+ }
+ if ((ret & KoreanAutomata.ACTION_UPDATE_COMPOSITIONSTR) != 0)
+ {
+ // // Log.v(TAG, " update composition string - " + kauto.GetCompositionString());
+ // mComposing.setLength(0);
+ if ((mComposing.length() > 0) && ((ret & KoreanAutomata.ACTION_UPDATE_COMPLETESTR) == 0))
+ mComposing.replace(mComposing.length()-1, mComposing.length(), kauto.GetCompositionString());
+ else
+ mComposing.append(kauto.GetCompositionString());
+ // mComposing.append(kauto.GetCompositionString());
+ if (mComposing.length() > 0) {
+ getCurrentInputConnection().setComposingText(mComposing, 1);
+ }
+ }
+ }
+ if ((ret & KoreanAutomata.ACTION_USE_INPUT_AS_RESULT) != 0)
+ {
+ getCurrentInputConnection().commitText(
+ String.valueOf((char) primaryCode), 1);
+ }
+ // updateShiftKeyState(getCurrentInputEditorInfo());
+// getCurrentInputConnection().commitText(
+// String.valueOf((char) primaryCode), 1);
+ }
+ }
+
+ private void handleClose() {
+ commitTyped(getCurrentInputConnection());
+ requestHideSelf(0);
+ mInputView.closing();
+ }
+
+ private void checkToggleCapsLock() {
+ long now = System.currentTimeMillis();
+ if (mLastShiftTime + 800 > now) {
+ mCapsLock = !mCapsLock;
+ mLastShiftTime = 0;
+ } else {
+ mLastShiftTime = now;
+ }
+ }
+
+ private String getWordSeparators() {
+ return mWordSeparators;
+ }
+
+ public boolean isWordSeparator(int code) {
+ String separators = getWordSeparators();
+ return separators.contains(String.valueOf((char)code));
+ }
+
+ /* no candidate view for Korean. at least, not now.
+ public void pickDefaultCandidate() {
+ pickSuggestionManually(0);
+ }
+
+ public void pickSuggestionManually(int index) {
+ if (mCompletionOn && mCompletions != null && index >= 0
+ && index < mCompletions.length) {
+ CompletionInfo ci = mCompletions[index];
+ getCurrentInputConnection().commitCompletion(ci);
+ if (mCandidateView != null) {
+ mCandidateView.clear();
+ }
+ updateShiftKeyState(getCurrentInputEditorInfo());
+ } else if (mComposing.length() > 0) {
+ // If we were generating candidate suggestions for the current
+ // text, we would commit one of them here. But for this sample,
+ // we will just commit the current text.
+ commitTyped(getCurrentInputConnection());
+ }
+ }
+ */
+ public void pickSuggestionManually(int index) {} // just fake it to build.
+
+
+ public void swipeRight() {
+// if (mCompletionOn) {
+// pickDefaultCandidate();
+// }
+ }
+
+ public void swipeLeft() {
+ handleBackspace();
+ }
+
+ public void swipeDown() {
+ handleClose();
+ }
+
+ public void swipeUp() {
+ }
+
+ public void onPress(int primaryCode) {
+ }
+
+ public void onRelease(int primaryCode) {
+ }
+}