Hangul update (#160)

Add Hangul layout from https://github.com/Lee0701/openboard/tree/hangul

Further changes:
* merge current state and make it compile
* some formatting
* remove Korean dictionary (to be downloaded from original repo)
* add file in make-keyboard-text for auto-generating KeyboardTextsTable
* disable Hangul combiner when not using Hangul (otherwise symbols are seen as parts of a word)

---------

Co-authored-by: Lee0701 <hgl1002@naver.com>
This commit is contained in:
Helium314 2023-09-16 11:09:15 +02:00 committed by GitHub
parent 5f29248855
commit eb45495d13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 2683 additions and 300 deletions

View file

@ -9,16 +9,17 @@ Might end up on F-Droid...
* Allow loading Glide typing library * Allow loading Glide typing library
* not included in the app, as there is no compatible open source library * not included in the app, as there is no compatible open source library
* can be extracted from GApps packages (_swypelibs_), or downloaded [here](https://github.com/erkserkserks/openboard/tree/master/app/src/main/jniLibs) * can be extracted from GApps packages (_swypelibs_), or downloaded [here](https://github.com/erkserkserks/openboard/tree/master/app/src/main/jniLibs)
* Multilingual typing * Multilingual typing
* Load external dictionaries * Load external dictionaries
* get them [here](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/dictionaries), or in the [experimental](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/dictionaries_experimental) section (quality may vary) * get them [here](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/dictionaries), or in the [experimental](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/dictionaries_experimental) section (quality may vary)
* additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search") * additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search")
* Adjust keyboard themes (style and colors) * note that for Korean layouts, suggestions only work using [this dictionary](https://github.com/openboard-team/openboard/commit/83fca9533c03b9fecc009fc632577226bbd6301f), the tools in the dictionary repository are not able to create working dictionaries
* can follow the system's day/night setting on Android 10+ (and on some versions of Android 9) * Adjust keyboard themes (style and colors)
* Split keyboard (if the screen is large enough) * can follow the system's day/night setting on Android 10+ (and on some versions of Android 9)
* Number row * Split keyboard (if the screen is large enough)
* Number pad * Number row
* Show all available extra characters on long pressing a key * Number pad
* Show all available extra characters on long pressing a key
## Hidden functionality ## Hidden functionality
Features that may go unnoticed Features that may go unnoticed

View file

@ -17,7 +17,7 @@ interface Combiner {
* @param event the event to combine with the existing state. * @param event the event to combine with the existing state.
* @return the resulting event. * @return the resulting event.
*/ */
fun processEvent(previousEvents: ArrayList<Event>?, event: Event?): Event fun processEvent(previousEvents: ArrayList<Event>?, event: Event): Event
/** /**
* Get the feedback that should be shown to the user for the current state of this combiner. * Get the feedback that should be shown to the user for the current state of this combiner.

View file

@ -17,12 +17,42 @@ import java.util.*
* feedback on the composing state and will typically be shown with different styling such as * feedback on the composing state and will typically be shown with different styling such as
* a colored background. * a colored background.
*/ */
class CombinerChain(initialText: String?) { class CombinerChain(initialText: String) {
// The already combined text, as described above // The already combined text, as described above
private val mCombinedText: StringBuilder private val mCombinedText = StringBuilder(initialText)
// The feedback on the composing state, as described above // The feedback on the composing state, as described above
private val mStateFeedback: SpannableStringBuilder private val mStateFeedback = SpannableStringBuilder()
private val mCombiners: ArrayList<Combiner> private val mCombiners = ArrayList<Combiner>()
// Hangul combiner affects other scripts, e.g. period is seen as port of a word for latin,
// so we need to remove the combiner when not writing in hangul script.
// Maybe it would be better to always have the Hangul combiner, but make sure it doesn't affect
// events for other scripts, but how?
// todo: this really should be done properly, hangul combiner should do nothing when it's not needed
var isHangul = false
set(value) {
if (field == value) return
field = value
if (!value)
mCombiners.removeAll { it is HangulCombiner }
else if (mCombiners.none { it is HangulCombiner })
mCombiners.add(HangulCombiner())
}
/**
* Create an combiner chain.
*
* The combiner chain takes events as inputs and outputs code points and combining state.
* For example, if the input language is Japanese, the combining chain will typically perform
* kana conversion. This takes a string for initial text, taken to be present before the
* cursor: we'll start after this.
*
* @param initialText The text that has already been combined so far.
*/
init {
// The dead key combiner is always active, and always first
mCombiners.add(DeadKeyCombiner())
}
fun reset() { fun reset() {
mCombinedText.setLength(0) mCombinedText.setLength(0)
mStateFeedback.clear() mStateFeedback.clear()
@ -45,15 +75,16 @@ class CombinerChain(initialText: String?) {
* @return the processed event. It may be the same event, or a consumed event, or a completely * @return the processed event. It may be the same event, or a consumed event, or a completely
* new event. However it may never be null. * new event. However it may never be null.
*/ */
fun processEvent(previousEvents: ArrayList<Event>?, fun processEvent(previousEvents: ArrayList<Event>?, newEvent: Event): Event {
newEvent: Event): Event {
val modifiablePreviousEvents = ArrayList(previousEvents!!) val modifiablePreviousEvents = ArrayList(previousEvents!!)
var event = newEvent var event = newEvent
for (combiner in mCombiners) { // A combiner can never return more than one event; it can return several for (combiner in mCombiners) {
// code points, but they should be encapsulated within one event. // A combiner can never return more than one event; it can return several
// code points, but they should be encapsulated within one event.
event = combiner.processEvent(modifiablePreviousEvents, event) event = combiner.processEvent(modifiablePreviousEvents, event)
if (event.isConsumed) { // If the event is consumed, then we don't pass it to subsequent combiners: if (event.isConsumed) {
// they should not see it at all. // If the event is consumed, then we don't pass it to subsequent combiners:
// they should not see it at all.
break break
} }
} }
@ -93,21 +124,4 @@ class CombinerChain(initialText: String?) {
return s.append(mStateFeedback) return s.append(mStateFeedback)
} }
/** }
* Create an combiner chain.
*
* The combiner chain takes events as inputs and outputs code points and combining state.
* For example, if the input language is Japanese, the combining chain will typically perform
* kana conversion. This takes a string for initial text, taken to be present before the
* cursor: we'll start after this.
*
* @param initialText The text that has already been combined so far.
*/
init {
mCombiners = ArrayList()
// The dead key combiner is always active, and always first
mCombiners.add(DeadKeyCombiner())
mCombinedText = StringBuilder(initialText!!)
mStateFeedback = SpannableStringBuilder()
}
}

View file

@ -188,18 +188,18 @@ class DeadKeyCombiner : Combiner {
// TODO: make this a list of events instead // TODO: make this a list of events instead
val mDeadSequence = StringBuilder() val mDeadSequence = StringBuilder()
override fun processEvent(previousEvents: ArrayList<Event>?, event: Event?): Event { override fun processEvent(previousEvents: ArrayList<Event>?, event: Event): Event {
if (TextUtils.isEmpty(mDeadSequence)) { // No dead char is currently being tracked: this is the most common case. if (TextUtils.isEmpty(mDeadSequence)) { // No dead char is currently being tracked: this is the most common case.
if (event!!.isDead) { // The event was a dead key. Start tracking it. if (event.isDead) { // The event was a dead key. Start tracking it.
mDeadSequence.appendCodePoint(event.mCodePoint) mDeadSequence.appendCodePoint(event.mCodePoint)
return Event.createConsumedEvent(event) return Event.createConsumedEvent(event)
} }
// Regular keystroke when not keeping track of a dead key. Simply said, there are // Regular keystroke when not keeping track of a dead key. Simply said, there are
// no dead keys at all in the current input, so this combiner has nothing to do and // no dead keys at all in the current input, so this combiner has nothing to do and
// simply returns the event as is. The majority of events will go through this path. // simply returns the event as is. The majority of events will go through this path.
return event return event
} }
if (Character.isWhitespace(event!!.mCodePoint) if (Character.isWhitespace(event.mCodePoint)
|| event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length)) { // When whitespace or twice the same dead key, we should output the dead sequence as is. || event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length)) { // When whitespace or twice the same dead key, we should output the dead sequence as is.
val resultEvent = createEventChainFromSequence(mDeadSequence.toString(), event) val resultEvent = createEventChainFromSequence(mDeadSequence.toString(), event)
mDeadSequence.setLength(0) mDeadSequence.setLength(0)

View file

@ -17,59 +17,59 @@ import org.dslul.openboard.inputmethod.latin.common.StringUtils
* for example. * for example.
* The combiner should figure out what to do with this. * The combiner should figure out what to do with this.
*/ */
class Event private constructor(// The type of event - one of the constants above class Event private constructor(
private val mEventType: Int, // If applicable, this contains the string that should be input. // The type of event - one of the constants above
val mText: CharSequence?, // The code point associated with the event, if relevant. This is a unicode code point, and private val mEventType: Int,
// has nothing to do with other representations of the key. It is only relevant if this event // If applicable, this contains the string that should be input.
// is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point val mText: CharSequence?,
// associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when // The code point associated with the event, if relevant. This is a unicode code point, and
// it's not relevant. // has nothing to do with other representations of the key. It is only relevant if this event
val mCodePoint: Int, // The key code associated with the event, if relevant. This is relevant whenever this event // is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point
// has been triggered by a key press, but not for a gesture for example. This has conceptually // associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when
// no link to the code point, although keys that enter a straight code point may often set // it's not relevant.
// this to be equal to mCodePoint for convenience. If this is not a key, this must contain val mCodePoint: Int,
// NOT_A_KEY_CODE. // The key code associated with the event, if relevant. This is relevant whenever this event
// has been triggered by a key press, but not for a gesture for example. This has conceptually
// no link to the code point, although keys that enter a straight code point may often set
// this to be equal to mCodePoint for convenience. If this is not a key, this must contain
// NOT_A_KEY_CODE.
val mKeyCode: Int, val mKeyCode: Int,
// Coordinates of the touch event, if relevant. If useful, we may want to replace this with // Coordinates of the touch event, if relevant. If useful, we may want to replace this with
// a MotionEvent or something in the future. This is only relevant when the keypress is from // a MotionEvent or something in the future. This is only relevant when the keypress is from
// a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the // a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the
// future or some other awesome sauce. // future or some other awesome sauce.
val mX: Int, val mY: Int, val mX: Int, val mY: Int,
// If this is of type EVENT_TYPE_SUGGESTION_PICKED, this must not be null (and must be null in // If this is of type EVENT_TYPE_SUGGESTION_PICKED, this must not be null (and must be null in
// other cases). // other cases).
val mSuggestedWordInfo: SuggestedWordInfo?, val mSuggestedWordInfo: SuggestedWordInfo?,
// Some flags that can't go into the key code. It's a bit field of FLAG_* // Some flags that can't go into the key code. It's a bit field of FLAG_*
private val mFlags: Int, private val mFlags: Int,
// The next event, if any. Null if there is no next event yet. // The next event, if any. Null if there is no next event yet.
val mNextEvent: Event?// This logic may need to be refined in the future val mNextEvent: Event?
// This logic may need to be refined in the future
) { ) {
// Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys
// that result in input like letters or space. // that result in input like letters or space.
val isFunctionalKeyEvent: Boolean val isFunctionalKeyEvent: Boolean
get() =// This logic may need to be refined in the future get() = NOT_A_CODE_POINT == mCodePoint // This logic may need to be refined in the future
NOT_A_CODE_POINT == mCodePoint
// Returns whether this event is for a dead character. @see {@link #FLAG_DEAD} // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD}
val isDead: Boolean val isDead: Boolean get() = 0 != FLAG_DEAD and mFlags
get() = 0 != FLAG_DEAD and mFlags
val isKeyRepeat: Boolean val isKeyRepeat: Boolean get() = 0 != FLAG_REPEAT and mFlags
get() = 0 != FLAG_REPEAT and mFlags
val isConsumed: Boolean val isConsumed: Boolean get() = 0 != FLAG_CONSUMED and mFlags
get() = 0 != FLAG_CONSUMED and mFlags
val isGesture: Boolean val isCombining: Boolean get() = 0 != FLAG_COMBINING and mFlags
get() = EVENT_TYPE_GESTURE == mEventType
val isGesture: Boolean get() = EVENT_TYPE_GESTURE == mEventType
// Returns whether this is a fake key press from the suggestion strip. This happens with // Returns whether this is a fake key press from the suggestion strip. This happens with
// punctuation signs selected from the suggestion strip. // punctuation signs selected from the suggestion strip.
val isSuggestionStripPress: Boolean val isSuggestionStripPress: Boolean get() = EVENT_TYPE_SUGGESTION_PICKED == mEventType
get() = EVENT_TYPE_SUGGESTION_PICKED == mEventType
val isHandled: Boolean val isHandled: Boolean get() = EVENT_TYPE_NOT_HANDLED != mEventType
get() = EVENT_TYPE_NOT_HANDLED != mEventType
// A consumed event should input no text. // A consumed event should input no text.
val textToCommit: CharSequence? val textToCommit: CharSequence?
@ -87,20 +87,20 @@ class Event private constructor(// The type of event - one of the constants abov
companion object { companion object {
// Should the types below be represented by separate classes instead? It would be cleaner // Should the types below be represented by separate classes instead? It would be cleaner
// but probably a bit too much // but probably a bit too much
// An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard. // An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard.
const val EVENT_TYPE_NOT_HANDLED = 0 const val EVENT_TYPE_NOT_HANDLED = 0
// A key press that is part of input, for example pressing an alphabetic character on a // A key press that is part of input, for example pressing an alphabetic character on a
// hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later // hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later
// through combination. // through combination.
const val EVENT_TYPE_INPUT_KEYPRESS = 1 const val EVENT_TYPE_INPUT_KEYPRESS = 1
// A toggle event is triggered by a key that affects the previous character. An example would // A toggle event is triggered by a key that affects the previous character. An example would
// be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with // be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with
// repeated presses. // repeated presses.
const val EVENT_TYPE_TOGGLE = 2 const val EVENT_TYPE_TOGGLE = 2
// A mode event instructs the combiner to change modes. The canonical example would be the // A mode event instructs the combiner to change modes. The canonical example would be the
// hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard // hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard
// if handled at the combiner level. // if handled at the combiner level.
const val EVENT_TYPE_MODE_KEY = 3 const val EVENT_TYPE_MODE_KEY = 3
// An event corresponding to a gesture. // An event corresponding to a gesture.
const val EVENT_TYPE_GESTURE = 4 const val EVENT_TYPE_GESTURE = 4
@ -116,12 +116,14 @@ class Event private constructor(// The type of event - one of the constants abov
const val NOT_A_KEY_CODE = 0 const val NOT_A_KEY_CODE = 0
private const val FLAG_NONE = 0 private const val FLAG_NONE = 0
// This event is a dead character, usually input by a dead key. Examples include dead-acute // This event is a dead character, usually input by a dead key. Examples include dead-acute
// or dead-abovering. // or dead-abovering.
private const val FLAG_DEAD = 0x1 private const val FLAG_DEAD = 0x1
// This event is coming from a key repeat, software or hardware. // This event is coming from a key repeat, software or hardware.
private const val FLAG_REPEAT = 0x2 private const val FLAG_REPEAT = 0x2
// This event has already been consumed. // This event has already been consumed.
private const val FLAG_CONSUMED = 0x4 private const val FLAG_CONSUMED = 0x4
// This event is a combining character, usually a hangul input.
private const val FLAG_COMBINING = 0x8
@JvmStatic @JvmStatic
fun createSoftwareKeypressEvent(codePoint: Int, keyCode: Int, fun createSoftwareKeypressEvent(codePoint: Int, keyCode: Int,
@ -192,15 +194,20 @@ class Event private constructor(// The type of event - one of the constants abov
* or combination that outputs a string. * or combination that outputs a string.
* @param text the CharSequence associated with this event. * @param text the CharSequence associated with this event.
* @param keyCode the key code, or NOT_A_KEYCODE if not applicable. * @param keyCode the key code, or NOT_A_KEYCODE if not applicable.
* @param next the next event, or null if not applicable.
* @return an event for this text. * @return an event for this text.
*/ */
@JvmStatic @JvmStatic
fun createSoftwareTextEvent(text: CharSequence?, keyCode: Int): Event { fun createSoftwareTextEvent(text: CharSequence?, keyCode: Int, next: Event?): Event {
return Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, return Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
null /* suggestedWordInfo */, FLAG_NONE, null /* next */) null /* suggestedWordInfo */, FLAG_NONE, next)
} }
@JvmStatic
fun createSoftwareTextEvent(text: CharSequence?, keyCode: Int) =
createSoftwareTextEvent(text, keyCode, null)
/** /**
* Creates an input event representing the manual pick of a punctuation suggestion. * Creates an input event representing the manual pick of a punctuation suggestion.
* @return an event for this suggestion pick. * @return an event for this suggestion pick.
@ -208,7 +215,7 @@ class Event private constructor(// The type of event - one of the constants abov
@JvmStatic @JvmStatic
fun createPunctuationSuggestionPickedEvent( fun createPunctuationSuggestionPickedEvent(
suggestedWordInfo: SuggestedWordInfo): Event { suggestedWordInfo: SuggestedWordInfo): Event {
val primaryCode = suggestedWordInfo.mWord[0].toInt() val primaryCode = suggestedWordInfo.mWord[0].code
return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode, return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode,
NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE, NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE,
Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE, Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE,
@ -232,12 +239,18 @@ class Event private constructor(// The type of event - one of the constants abov
* @param source the event to copy the properties of. * @param source the event to copy the properties of.
* @return an identical event marked as consumed. * @return an identical event marked as consumed.
*/ */
fun createConsumedEvent(source: Event?): Event { // A consumed event should not input any text at all, so we pass the empty string as text. fun createConsumedEvent(source: Event): Event { // A consumed event should not input any text at all, so we pass the empty string as text.
return Event(source!!.mEventType, source.mText, source.mCodePoint, source.mKeyCode, return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode,
source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_CONSUMED, source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_CONSUMED,
source.mNextEvent) source.mNextEvent)
} }
fun createCombiningEvent(source: Event): Event {
return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode,
source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING,
source.mNextEvent)
}
fun createNotHandledEvent(): Event { fun createNotHandledEvent(): Event {
return Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, return Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
@ -248,7 +261,7 @@ class Event private constructor(// The type of event - one of the constants abov
// This method is private - to create a new event, use one of the create* utility methods. // This method is private - to create a new event, use one of the create* utility methods.
init { init {
// Sanity checks // Sanity checks
// mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED // mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED
if (EVENT_TYPE_SUGGESTION_PICKED == mEventType) { if (EVENT_TYPE_SUGGESTION_PICKED == mEventType) {
if (null == mSuggestedWordInfo) { if (null == mSuggestedWordInfo) {
throw RuntimeException("Wrong event: SUGGESTION_PICKED event must have a " throw RuntimeException("Wrong event: SUGGESTION_PICKED event must have a "
@ -261,4 +274,4 @@ class Event private constructor(// The type of event - one of the constants abov
} }
} }
} }
} }

View file

@ -0,0 +1,332 @@
package org.dslul.openboard.inputmethod.event
import org.dslul.openboard.inputmethod.latin.common.Constants
import java.lang.StringBuilder
import java.util.ArrayList
class HangulCombiner : Combiner {
private val composingWord = StringBuilder()
val history: MutableList<HangulSyllable> = mutableListOf()
private val syllable: HangulSyllable? get() = history.lastOrNull()
override fun processEvent(previousEvents: ArrayList<Event>?, event: Event): Event {
if (event.mKeyCode == Constants.CODE_SHIFT) return event
if (Character.isWhitespace(event.mCodePoint)) {
val text = combiningStateFeedback
reset()
return createEventChainFromSequence(text, event)
} else if (event.isFunctionalKeyEvent) {
if(event.mKeyCode == Constants.CODE_DELETE) {
return when {
history.size == 1 && composingWord.isEmpty() || history.isEmpty() && composingWord.length == 1 -> {
reset()
Event.createHardwareKeypressEvent(0x20, Constants.CODE_SPACE, event, event.isKeyRepeat)
}
history.isNotEmpty() -> {
history.removeAt(history.lastIndex)
Event.createConsumedEvent(event)
}
composingWord.isNotEmpty() -> {
composingWord.deleteCharAt(composingWord.lastIndex)
Event.createConsumedEvent(event)
}
else -> event
}
}
val text = combiningStateFeedback
reset()
return createEventChainFromSequence(text, event)
} else {
val currentSyllable = syllable ?: HangulSyllable()
val jamo = HangulJamo.of(event.mCodePoint)
if (!event.isCombining || jamo is HangulJamo.NonHangul) {
composingWord.append(currentSyllable.string)
composingWord.append(jamo.string)
history.clear()
} else {
when (jamo) {
is HangulJamo.Consonant -> {
val initial = jamo.toInitial()
val final = jamo.toFinal()
if (currentSyllable.initial != null && currentSyllable.medial != null) {
if (currentSyllable.final == null) {
val combination = COMBINATION_TABLE_DUBEOLSIK[currentSyllable.initial.codePoint to (initial?.codePoint ?: -1)]
history +=
if (combination != null) {
currentSyllable.copy(initial = HangulJamo.Initial(combination))
} else {
if (final != null) {
currentSyllable.copy(final = final)
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(initial = initial)
}
}
} else {
val pair = currentSyllable.final.codePoint to (final?.codePoint ?: -1)
val combination = COMBINATION_TABLE_DUBEOLSIK[pair]
history += if (combination != null) {
currentSyllable.copy(final = HangulJamo.Final(combination, combinationPair = pair))
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(initial = initial)
}
}
} else {
composingWord.append(currentSyllable.string)
history.clear()
history += HangulSyllable(initial = initial)
}
}
is HangulJamo.Vowel -> {
val medial = jamo.toMedial()
if (currentSyllable.final == null) {
history +=
if (currentSyllable.medial != null) {
val combination = COMBINATION_TABLE_DUBEOLSIK[currentSyllable.medial.codePoint to (medial?.codePoint ?: -1)]
if (combination != null) {
currentSyllable.copy(medial = HangulJamo.Medial(combination))
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(medial = medial)
}
} else {
currentSyllable.copy(medial = medial)
}
} else if (currentSyllable.final.combinationPair != null) {
val pair = currentSyllable.final.combinationPair
history.removeAt(history.lastIndex)
val final = HangulJamo.Final(pair.first)
history += currentSyllable.copy(final = final)
composingWord.append(syllable?.string ?: "")
history.clear()
val initial = HangulJamo.Final(pair.second).toConsonant()?.toInitial()
val newSyllable = HangulSyllable(initial = initial)
history += newSyllable
history += newSyllable.copy(medial = medial)
} else {
history.removeAt(history.lastIndex)
composingWord.append(syllable?.string ?: "")
history.clear()
val initial = currentSyllable.final.toConsonant()?.toInitial()
val newSyllable = HangulSyllable(initial = initial)
history += newSyllable
history += newSyllable.copy(medial = medial)
}
}
is HangulJamo.Initial -> {
history +=
if (currentSyllable.initial != null) {
val combination = COMBINATION_TABLE_SEBEOLSIK[currentSyllable.initial.codePoint to jamo.codePoint]
if (combination != null && currentSyllable.medial == null && currentSyllable.final == null) {
currentSyllable.copy(initial = HangulJamo.Initial(combination))
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(initial = jamo)
}
} else {
currentSyllable.copy(initial = jamo)
}
}
is HangulJamo.Medial -> {
history +=
if (currentSyllable.medial != null) {
val combination = COMBINATION_TABLE_SEBEOLSIK[currentSyllable.medial.codePoint to jamo.codePoint]
if (combination != null) {
currentSyllable.copy(medial = HangulJamo.Medial(combination))
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(medial = jamo)
}
} else {
currentSyllable.copy(medial = jamo)
}
}
is HangulJamo.Final -> {
history +=
if (currentSyllable.final != null) {
val combination = COMBINATION_TABLE_SEBEOLSIK[currentSyllable.final.codePoint to jamo.codePoint]
if (combination != null) {
currentSyllable.copy(final = HangulJamo.Final(combination))
} else {
composingWord.append(currentSyllable.string)
history.clear()
HangulSyllable(final = jamo)
}
} else {
currentSyllable.copy(final = jamo)
}
}
// compiler bug? when it's not added, compiler complains that it's missing
// but when added, linter (correctly) states it's unreachable anyway
is HangulJamo.NonHangul -> Unit
}
}
}
return Event.createConsumedEvent(event)
}
override val combiningStateFeedback: CharSequence
get() = composingWord.toString() + (syllable?.string ?: "")
override fun reset() {
composingWord.setLength(0)
history.clear()
}
sealed class HangulJamo {
abstract val codePoint: Int
abstract val modern: Boolean
val string: String get() = codePoint.toChar().toString()
data class NonHangul(override val codePoint: Int) : HangulJamo() {
override val modern: Boolean get() = false
}
data class Initial(override val codePoint: Int) : HangulJamo() {
override val modern: Boolean get() = codePoint in 0x1100 .. 0x1112
val ordinal: Int get() = codePoint - 0x1100
fun toConsonant(): Consonant? {
val codePoint = COMPAT_CONSONANTS.getOrNull(CONVERT_INITIALS.indexOf(codePoint.toChar())) ?: return null
if(codePoint.code == 0) return null
return Consonant(codePoint.code)
}
}
data class Medial(override val codePoint: Int) : HangulJamo() {
override val modern: Boolean get() = codePoint in 1161 .. 0x1175
val ordinal: Int get() = codePoint - 0x1161
fun toVowel(): Vowel? {
val codePoint = COMPAT_VOWELS.getOrNull(CONVERT_MEDIALS.indexOf(codePoint.toChar())) ?: return null
return Vowel(codePoint.code)
}
}
data class Final(override val codePoint: Int, val combinationPair: Pair<Int, Int>? = null) : HangulJamo() {
override val modern: Boolean get() = codePoint in 0x11a8 .. 0x11c2
val ordinal: Int get() = codePoint - 0x11a7
fun toConsonant(): Consonant? {
val codePoint = COMPAT_CONSONANTS.getOrNull(CONVERT_FINALS.indexOf(codePoint.toChar())) ?: return null
if(codePoint.code == 0) return null
return Consonant(codePoint.code)
}
}
data class Consonant(override val codePoint: Int) : HangulJamo() {
override val modern: Boolean get() = codePoint in 0x3131 .. 0x314e
val ordinal: Int get() = codePoint - 0x3131
fun toInitial(): Initial? {
val codePoint = CONVERT_INITIALS.getOrNull(COMPAT_CONSONANTS.indexOf(codePoint.toChar())) ?: return null
if(codePoint.code == 0) return null
return Initial(codePoint.code)
}
fun toFinal(): Final? {
val codePoint = CONVERT_FINALS.getOrNull(COMPAT_CONSONANTS.indexOf(codePoint.toChar())) ?: return null
if(codePoint.code == 0) return null
return Final(codePoint.code)
}
}
data class Vowel(override val codePoint: Int) : HangulJamo() {
override val modern: Boolean get() = codePoint in 0x314f .. 0x3163
val ordinal: Int get() = codePoint - 0x314f1
fun toMedial(): Medial? {
val codePoint = CONVERT_MEDIALS.getOrNull(COMPAT_VOWELS.indexOf(codePoint.toChar())) ?: return null
if(codePoint.code == 0) return null
return Medial(codePoint.code)
}
}
companion object {
const val COMPAT_CONSONANTS = "ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ"
const val COMPAT_VOWELS = "ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ"
const val CONVERT_INITIALS = "ᄀᄁ\u0000\u0000\u0000ᄃᄄᄅ\u0000\u0000\u0000\u0000\u0000\u0000\u0000ᄆᄇᄈ\u0000ᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒ"
const val CONVERT_MEDIALS = "ᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ"
const val CONVERT_FINALS = "ᆨᆩᆪᆫᆬᆭᆮ\u0000ᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸ\u0000ᆹᆺᆻᆼᆽ\u0000ᆾᆿᇀᇁᇂ"
fun of(codePoint: Int): HangulJamo {
return when(codePoint) {
in 0x3131 .. 0x314e -> Consonant(codePoint)
in 0x314f .. 0x3163 -> Vowel(codePoint)
in 0x1100 .. 0x115f -> Initial(codePoint)
in 0x1160 .. 0x11a7 -> Medial(codePoint)
in 0x11a8 .. 0x11ff -> Final(codePoint)
else -> NonHangul(codePoint)
}
}
}
}
data class HangulSyllable(
val initial: HangulJamo.Initial? = null,
val medial: HangulJamo.Medial? = null,
val final: HangulJamo.Final? = null
) {
val combinable: Boolean get() = (initial?.modern ?: false) && (medial?.modern ?: false) && (final?.modern ?: true)
val combined: String get() = (0xac00 + (initial?.ordinal ?: 0) * 21 * 28
+ (medial?.ordinal ?: 0) * 28
+ (final?.ordinal ?: 0)).toChar().toString()
val uncombined: String get() = (initial?.string ?: "") + (medial?.string ?: "") + (final?.string ?: "")
val uncombinedCompat: String get() = (initial?.toConsonant()?.string ?: "") +
(medial?.toVowel()?.string ?: "") + (final?.toConsonant()?.string ?: "")
val string: String get() = if (this.combinable) this.combined else this.uncombinedCompat
}
companion object {
val COMBINATION_TABLE_DUBEOLSIK = mapOf<Pair<Int, Int>, Int>(
0x1169 to 0x1161 to 0x116a,
0x1169 to 0x1162 to 0x116b,
0x1169 to 0x1175 to 0x116c,
0x116e to 0x1165 to 0x116f,
0x116e to 0x1166 to 0x1170,
0x116e to 0x1175 to 0x1171,
0x1173 to 0x1175 to 0x1174,
0x11a8 to 0x11ba to 0x11aa,
0x11ab to 0x11bd to 0x11ac,
0x11ab to 0x11c2 to 0x11ad,
0x11af to 0x11a8 to 0x11b0,
0x11af to 0x11b7 to 0x11b1,
0x11af to 0x11b8 to 0x11b2,
0x11af to 0x11ba to 0x11b3,
0x11af to 0x11c0 to 0x11b4,
0x11af to 0x11c1 to 0x11b5,
0x11af to 0x11c2 to 0x11b6,
0x11b8 to 0x11ba to 0x11b9
)
val COMBINATION_TABLE_SEBEOLSIK = mapOf<Pair<Int, Int>, Int>(
0x1100 to 0x1100 to 0x1101, // ㄲ
0x1103 to 0x1103 to 0x1104, // ㄸ
0x1107 to 0x1107 to 0x1108, // ㅃ
0x1109 to 0x1109 to 0x110a, // ㅆ
0x110c to 0x110c to 0x110d, // ㅉ
0x1169 to 0x1161 to 0x116a, // ㅘ
0x1169 to 0x1162 to 0x116b, // ㅙ
0x1169 to 0x1175 to 0x116c, // ㅚ
0x116e to 0x1165 to 0x116f, // ㅝ
0x116e to 0x1166 to 0x1170, // ㅞ
0x116e to 0x1175 to 0x1171, // ㅟ
0x1173 to 0x1175 to 0x1174, // ㅢ
0x11a8 to 0x11a8 to 0x11a9, // ㄲ
0x11a8 to 0x11ba to 0x11aa, // ㄳ
0x11ab to 0x11bd to 0x11ac, // ㄵ
0x11ab to 0x11c2 to 0x11ad, // ㄶ
0x11af to 0x11a8 to 0x11b0, // ㄺ
0x11af to 0x11b7 to 0x11b1, // ㄻ
0x11af to 0x11b8 to 0x11b2, // ㄼ
0x11af to 0x11ba to 0x11b3, // ㄽ
0x11af to 0x11c0 to 0x11b4, // ㄾ
0x11af to 0x11c1 to 0x11b5, // ㄿ
0x11af to 0x11c2 to 0x11b6, // ㅀ
0x11b8 to 0x11ba to 0x11b9, // ㅄ
0x11ba to 0x11ba to 0x11bb // ㅆ
)
private fun createEventChainFromSequence(text: CharSequence, originalEvent: Event): Event {
return Event.createSoftwareTextEvent(text, Constants.CODE_OUTPUT_TEXT, originalEvent)
}
}
}

View file

@ -0,0 +1,163 @@
package org.dslul.openboard.inputmethod.event
import android.view.KeyEvent
import org.dslul.openboard.inputmethod.latin.RichInputMethodSubtype
import org.dslul.openboard.inputmethod.event.HangulCombiner.HangulJamo
object HangulEventDecoder {
@JvmStatic
fun decodeHardwareKeyEvent(subtype: RichInputMethodSubtype, event: KeyEvent, defaultEvent: () -> Event): Event {
val layout = LAYOUTS[subtype.keyboardLayoutSetName] ?: return defaultEvent()
val codePoint = layout[event.keyCode]?.let { if (event.isShiftPressed) it.second else it.first } ?: return defaultEvent()
val hardwareEvent = Event.createHardwareKeypressEvent(codePoint, event.keyCode, null, event.repeatCount != 0)
return decodeSoftwareKeyEvent(hardwareEvent)
}
@JvmStatic
fun decodeSoftwareKeyEvent(event: Event): Event {
if (event.isCombining) return event
return if (HangulJamo.of(event.mCodePoint) is HangulJamo.NonHangul) event
else Event.createCombiningEvent(event)
}
private val LAYOUT_DUBEOLSIK_STANDARD = mapOf<Int, Pair<Int, Int>>(
45 to (0x3142 to 0x3143),
51 to (0x3148 to 0x3149),
33 to (0x3137 to 0x3138),
46 to (0x3131 to 0x3132),
48 to (0x3145 to 0x3146),
53 to (0x315b to 0x315b),
49 to (0x3155 to 0x3155),
37 to (0x3151 to 0x3151),
43 to (0x3150 to 0x3152),
44 to (0x3154 to 0x3156),
29 to (0x3141 to 0x3141),
47 to (0x3134 to 0x3134),
32 to (0x3147 to 0x3147),
34 to (0x3139 to 0x3139),
35 to (0x314e to 0x314e),
36 to (0x3157 to 0x3157),
38 to (0x3153 to 0x3153),
39 to (0x314f to 0x314f),
40 to (0x3163 to 0x3163),
54 to (0x314b to 0x314b),
52 to (0x314c to 0x314c),
31 to (0x314a to 0x314a),
50 to (0x314d to 0x314d),
30 to (0x3160 to 0x3160),
42 to (0x315c to 0x315c),
41 to (0x3161 to 0x3161)
)
private val LAYOUT_SEBEOLSIK_390 = mapOf<Int, Pair<Int, Int>>(
8 to (0x11c2 to 0x11bd),
9 to (0x11bb to 0x0040),
10 to (0x11b8 to 0x0023),
11 to (0x116d to 0x0024),
12 to (0x1172 to 0x0025),
13 to (0x1163 to 0x005e),
14 to (0x1168 to 0x0026),
15 to (0x1174 to 0x002a),
16 to (0x116e to 0x0028),
7 to (0x110f to 0x0029),
45 to (0x11ba to 0x11c1),
51 to (0x11af to 0x11c0),
33 to (0x1167 to 0x11bf),
46 to (0x1162 to 0x1164),
48 to (0x1165 to 0x003b),
53 to (0x1105 to 0x003c),
49 to (0x1103 to 0x0037),
37 to (0x1106 to 0x0038),
43 to (0x110e to 0x0039),
44 to (0x1111 to 0x003e),
29 to (0x11bc to 0x11ae),
47 to (0x11ab to 0x11ad),
32 to (0x1175 to 0x11b0),
34 to (0x1161 to 0x11a9),
35 to (0x1173 to 0x002f),
36 to (0x1102 to 0x0027),
38 to (0x110b to 0x0034),
39 to (0x1100 to 0x0035),
40 to (0x110c to 0x0036),
74 to (0x1107 to 0x003a),
75 to (0x1110 to 0x0022),
54 to (0x11b7 to 0x11be),
52 to (0x11a8 to 0x11b9),
31 to (0x1166 to 0x11b1),
50 to (0x1169 to 0x11b6),
30 to (0x116e to 0x0021),
42 to (0x1109 to 0x0030),
41 to (0x1112 to 0x0031),
55 to (0x002c to 0x0032),
56 to (0x002e to 0x0033),
76 to (0x1169 to 0x003f)
)
private val LAYOUT_SEBEOLSIK_FINAL = mapOf<Int, Pair<Int, Int>>(
68 to (0x002a to 0x203b),
8 to (0x11c2 to 0x11a9),
9 to (0x11bb to 0x11b0),
10 to (0x11b8 to 0x11bd),
11 to (0x116d to 0x11b5),
12 to (0x1172 to 0x11b4),
13 to (0x1163 to 0x003d),
14 to (0x1168 to 0x201c),
15 to (0x1174 to 0x201d),
16 to (0x116e to 0x0027),
7 to (0x110f to 0x007e),
69 to (0x0029 to 0x003b),
70 to (0x003e to 0x002b),
45 to (0x11ba to 0x11c1),
51 to (0x11af to 0x11c0),
33 to (0x1167 to 0x11ac),
46 to (0x1162 to 0x11b6),
48 to (0x1165 to 0x11b3),
53 to (0x1105 to 0x0035),
49 to (0x1103 to 0x0036),
37 to (0x1106 to 0x0037),
43 to (0x110e to 0x0038),
44 to (0x1111 to 0x0039),
71 to (0x0028 to 0x0025),
72 to (0x003c to 0x002f),
73 to (0x003a to 0x005c),
29 to (0x11bc to 0x11ae),
47 to (0x11ab to 0x11ad),
32 to (0x1175 to 0x11b2),
34 to (0x1161 to 0x11b1),
35 to (0x1173 to 0x1164),
36 to (0x1102 to 0x0030),
38 to (0x110b to 0x0031),
39 to (0x1100 to 0x0032),
40 to (0x110c to 0x0033),
74 to (0x1107 to 0x0034),
75 to (0x1110 to 0x00b7),
54 to (0x11b7 to 0x11be),
52 to (0x11a8 to 0x11b9),
31 to (0x1166 to 0x11bf),
50 to (0x1169 to 0x11aa),
30 to (0x116e to 0x003f),
42 to (0x1109 to 0x002d),
41 to (0x1112 to 0x0022),
55 to (0x002c to 0x002c),
56 to (0x002e to 0x002e),
76 to (0x1169 to 0x0021)
)
private val LAYOUTS = mapOf<String, Map<Int, Pair<Int, Int>>>(
"korean" to LAYOUT_DUBEOLSIK_STANDARD,
"korean_sebeolsik_390" to LAYOUT_SEBEOLSIK_390,
"korean_sebeolsik_final" to LAYOUT_SEBEOLSIK_FINAL
)
}

View file

@ -26,42 +26,41 @@ import org.dslul.openboard.inputmethod.latin.common.Constants
* can be dead keys, they can be meta keys like shift or ctrl... This does not deal with * can be dead keys, they can be meta keys like shift or ctrl... This does not deal with
* 10-key like keyboards; a different decoder is used for this. * 10-key like keyboards; a different decoder is used for this.
*/ */
class HardwareKeyboardEventDecoder // TODO: get the layout for this hardware keyboard // TODO: get the layout for this hardware keyboard
(val mDeviceId: Int) : HardwareEventDecoder { class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder {
override fun decodeHardwareKey(keyEvent: KeyEvent): Event { // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value override fun decodeHardwareKey(keyEvent: KeyEvent): Event {
// that includes both the unicode char in the lower 21 bits and flags in the upper bits, // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value
// hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info. // that includes both the unicode char in the lower 21 bits and flags in the upper bits,
// hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
val codePointAndFlags = keyEvent.unicodeChar val codePointAndFlags = keyEvent.unicodeChar
// The keyCode is the abstraction used by the KeyEvent to represent different keys that // The keyCode is the abstraction used by the KeyEvent to represent different keys that
// do not necessarily map to a unicode character. This represents a physical key, like // do not necessarily map to a unicode character. This represents a physical key, like
// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock. // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
val keyCode = keyEvent.keyCode val keyCode = keyEvent.keyCode
val isKeyRepeat = 0 != keyEvent.repeatCount val isKeyRepeat = 0 != keyEvent.repeatCount
if (KeyEvent.KEYCODE_DEL == keyCode) { if (KeyEvent.KEYCODE_DEL == keyCode) {
return Event.Companion.createHardwareKeypressEvent(Event.Companion.NOT_A_CODE_POINT, Constants.CODE_DELETE, return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_DELETE, null /* next */, isKeyRepeat)
null /* next */, isKeyRepeat)
} }
if (keyEvent.isPrintingKey || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) { if (keyEvent.isPrintingKey || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) {
if (0 != codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT) { // A dead key. if (0 != codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT) { // A dead key.
return Event.Companion.createDeadEvent( return Event.createDeadEvent(
codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, null /* next */)
null /* next */)
} }
return if (KeyEvent.KEYCODE_ENTER == keyCode) { // The Enter key. If the Shift key is not being pressed, this should send a return if (KeyEvent.KEYCODE_ENTER == keyCode) {
// CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the // The Enter key. If the Shift key is not being pressed, this should send a
// Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let // CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the
// Latin IME decide what to do with it. // Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let
// Latin IME decide what to do with it.
if (keyEvent.isShiftPressed) { if (keyEvent.isShiftPressed) {
Event.Companion.createHardwareKeypressEvent(Event.Companion.NOT_A_CODE_POINT, Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT,
Constants.CODE_SHIFT_ENTER, null /* next */, isKeyRepeat) Constants.CODE_SHIFT_ENTER, null /* next */, isKeyRepeat)
} else Event.Companion.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, } else Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode,
null /* next */, isKeyRepeat) null /* next */, isKeyRepeat)
} else Event.Companion.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, } else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, isKeyRepeat)
isKeyRepeat)
// If not Enter, then this is just a regular keypress event for a normal character // If not Enter, then this is just a regular keypress event for a normal character
// that can be committed right away, taking into account the current state. // that can be committed right away, taking into account the current state.
} }
return Event.Companion.createNotHandledEvent() return Event.createNotHandledEvent()
} }
} }

View file

@ -27,6 +27,7 @@ import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader;
import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils; import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils;
import java.io.File; import java.io.File;
import java.text.Normalizer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Locale; import java.util.Locale;
@ -79,7 +80,12 @@ public final class DictionaryFactory {
new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength, new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength,
false /* useFullEditDistance */, locale, dictType); false /* useFullEditDistance */, locale, dictType);
if (readOnlyBinaryDictionary.isValidDictionary()) { if (readOnlyBinaryDictionary.isValidDictionary()) {
dictList.add(readOnlyBinaryDictionary); if(locale.getLanguage().equals("ko")) {
// Use KoreanDictionary for Korean locale
dictList.add(new KoreanDictionary(readOnlyBinaryDictionary));
} else {
dictList.add(readOnlyBinaryDictionary);
}
} else { } else {
readOnlyBinaryDictionary.close(); readOnlyBinaryDictionary.close();
// Prevent this dictionary to do any further harm. // Prevent this dictionary to do any further harm.

View file

@ -0,0 +1,104 @@
package org.dslul.openboard.inputmethod.latin;
import org.dslul.openboard.inputmethod.event.HangulCombiner;
import org.dslul.openboard.inputmethod.latin.common.ComposedData;
import org.dslul.openboard.inputmethod.latin.settings.SettingsValuesForSuggestion;
import java.text.Normalizer;
import java.util.ArrayList;
/*
* For Korean dictionary, there are too many cases of characters to store on dictionary, which makes it slow.
* To solve that, Unicode normalization is used to decompose Hangul syllables into Hangul jamos.
*/
public class KoreanDictionary extends Dictionary {
private static final String COMPAT_JAMO = HangulCombiner.HangulJamo.COMPAT_CONSONANTS + HangulCombiner.HangulJamo.COMPAT_VOWELS;
private static final String STANDARD_JAMO = HangulCombiner.HangulJamo.CONVERT_INITIALS + HangulCombiner.HangulJamo.CONVERT_MEDIALS;
private final Dictionary mDictionary;
public KoreanDictionary(Dictionary dictionary) {
super(dictionary.mDictType, dictionary.mLocale);
mDictionary = dictionary;
}
private String processInput(String input) {
String normalized = Normalizer.normalize(input, Normalizer.Form.NFD);
StringBuilder result = new StringBuilder();
for (char c : normalized.toCharArray()) {
int index = COMPAT_JAMO.indexOf(c);
if (index == -1) result.append(c);
else result.append(STANDARD_JAMO.charAt(index));
}
return result.toString();
}
private String processOutput(String output) {
return Normalizer.normalize(output, Normalizer.Form.NFC);
}
@Override
public ArrayList<SuggestedWords.SuggestedWordInfo> getSuggestions(ComposedData composedData,
NgramContext ngramContext, long proximityInfoHandle, SettingsValuesForSuggestion settingsValuesForSuggestion,
int sessionId, float weightForLocale, float[] inOutWeightOfLangModelVsSpatialModel) {
composedData = new ComposedData(composedData.mInputPointers,
composedData.mIsBatchMode, processInput(composedData.mTypedWord));
ArrayList<SuggestedWords.SuggestedWordInfo> suggestions = mDictionary.getSuggestions(composedData,
ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId,
weightForLocale, inOutWeightOfLangModelVsSpatialModel);
ArrayList<SuggestedWords.SuggestedWordInfo> result = new ArrayList<>();
for (SuggestedWords.SuggestedWordInfo info : suggestions) {
result.add(new SuggestedWords.SuggestedWordInfo(processOutput(info.mWord), info.mPrevWordsContext,
info.mScore, info.mKindAndFlags, info.mSourceDict, info.mIndexOfTouchPointOfSecondWord, info.mAutoCommitFirstWordConfidence));
}
return result;
}
@Override
public boolean isInDictionary(String word) {
return mDictionary.isInDictionary(processInput(word));
}
@Override
public int getFrequency(String word) {
return mDictionary.getFrequency(processInput(word));
}
@Override
public int getMaxFrequencyOfExactMatches(String word) {
return mDictionary.getMaxFrequencyOfExactMatches(processInput(word));
}
@Override
protected boolean same(char[] word, int length, String typedWord) {
word = processInput(new String(word)).toCharArray();
typedWord = processInput(typedWord);
return mDictionary.same(word, length, typedWord);
}
@Override
public void close() {
mDictionary.close();
}
@Override
public void onFinishInput() {
mDictionary.onFinishInput();
}
@Override
public boolean isInitialized() {
return mDictionary.isInitialized();
}
@Override
public boolean shouldAutoCommit(SuggestedWords.SuggestedWordInfo candidate) {
return mDictionary.shouldAutoCommit(candidate);
}
@Override
public boolean isUserSpecific() {
return mDictionary.isUserSpecific();
}
}

View file

@ -52,6 +52,7 @@ import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils;
import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants; import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants;
import org.dslul.openboard.inputmethod.event.Event; import org.dslul.openboard.inputmethod.event.Event;
import org.dslul.openboard.inputmethod.event.HangulEventDecoder;
import org.dslul.openboard.inputmethod.event.HardwareEventDecoder; import org.dslul.openboard.inputmethod.event.HardwareEventDecoder;
import org.dslul.openboard.inputmethod.event.HardwareKeyboardEventDecoder; import org.dslul.openboard.inputmethod.event.HardwareKeyboardEventDecoder;
import org.dslul.openboard.inputmethod.event.InputTransaction; import org.dslul.openboard.inputmethod.event.InputTransaction;
@ -1885,8 +1886,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
return super.onKeyDown(keyCode, keyEvent); return super.onKeyDown(keyCode, keyEvent);
} }
final Event event = getHardwareKeyEventDecoder( final Event event;
keyEvent.getDeviceId()).decodeHardwareKey(keyEvent); if (mRichImm.getCurrentSubtypeLocale().getLanguage().equals("ko")) {
final RichInputMethodSubtype subtype = mKeyboardSwitcher.getKeyboard() == null
? mRichImm.getCurrentSubtype()
: mKeyboardSwitcher.getKeyboard().mId.mSubtype;
event = HangulEventDecoder.decodeHardwareKeyEvent(subtype, keyEvent,
() -> getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent));
} else {
event = getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent);
}
// If the event is not handled by LatinIME, we just pass it to the parent implementation. // If the event is not handled by LatinIME, we just pass it to the parent implementation.
// If it's handled, we return true because we did handle it. // If it's handled, we return true because we did handle it.
if (event.isHandled()) { if (event.isHandled()) {

View file

@ -21,6 +21,8 @@ import androidx.annotation.NonNull;
import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
import org.dslul.openboard.inputmethod.event.CombinerChain; import org.dslul.openboard.inputmethod.event.CombinerChain;
import org.dslul.openboard.inputmethod.event.Event; import org.dslul.openboard.inputmethod.event.Event;
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher;
import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import org.dslul.openboard.inputmethod.latin.common.ComposedData; import org.dslul.openboard.inputmethod.latin.common.ComposedData;
import org.dslul.openboard.inputmethod.latin.common.Constants; import org.dslul.openboard.inputmethod.latin.common.Constants;
@ -91,6 +93,11 @@ public final class WordComposer {
mCursorPositionWithinWord = 0; mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null; mRejectedBatchModeSuggestion = null;
refreshTypedWordCache(); refreshTypedWordCache();
final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
if (keyboard != null)
// initializing with the right state is important for the spell checker,
// which creates a new WordComposer when receiving suggestions
mCombinerChain.setHangul(keyboard.mId.mSubtype.getLocale().getLanguage().equals("ko"));
} }
public ComposedData getComposedDataSnapshot() { public ComposedData getComposedDataSnapshot() {
@ -104,12 +111,14 @@ public final class WordComposer {
public void restartCombining(final String combiningSpec) { public void restartCombining(final String combiningSpec) {
final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec; final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
if (!nonNullCombiningSpec.equals(mCombiningSpec)) { if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
mCombinerChain = new CombinerChain( mCombinerChain = new CombinerChain(mCombinerChain.getComposingWordWithCombiningFeedback().toString());
mCombinerChain.getComposingWordWithCombiningFeedback().toString());
mCombiningSpec = nonNullCombiningSpec; mCombiningSpec = nonNullCombiningSpec;
} }
} }
/** Forwards the state to CombinerChain, which disables or enables the Hangul combiner */
public void setHangul(final boolean enabled) { mCombinerChain.setHangul(enabled); }
/** /**
* Clear out the keys registered so far. * Clear out the keys registered so far.
*/ */
@ -275,8 +284,7 @@ public final class WordComposer {
final int codePoint = Character.codePointAt(word, i); final int codePoint = Character.codePointAt(word, i);
// We don't want to override the batch input points that are held in mInputPointers // We don't want to override the batch input points that are held in mInputPointers
// (See {@link #add(int,int,int)}). // (See {@link #add(int,int,int)}).
final Event processedEvent = final Event processedEvent = processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
applyProcessedEvent(processedEvent); applyProcessedEvent(processedEvent);
} }
} }
@ -293,8 +301,9 @@ public final class WordComposer {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
final Event processedEvent = final Event processedEvent =
processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
CoordinateUtils.xFromArray(coordinates, i), CoordinateUtils.xFromArray(coordinates, i),
CoordinateUtils.yFromArray(coordinates, i))); CoordinateUtils.yFromArray(coordinates, i))
);
applyProcessedEvent(processedEvent); applyProcessedEvent(processedEvent);
} }
mIsResumed = true; mIsResumed = true;

View file

@ -16,8 +16,6 @@
package org.dslul.openboard.inputmethod.latin.common; package org.dslul.openboard.inputmethod.latin.common;
import android.renderscript.Script;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;

View file

@ -1,7 +1,9 @@
package org.dslul.openboard.inputmethod.latin.define package org.dslul.openboard.inputmethod.latin.define
object ProductionFlags { object ProductionFlags {
const val IS_HARDWARE_KEYBOARD_SUPPORTED = false const val IS_HARDWARE_KEYBOARD_SUPPORTED = true // was set to true in hangul branch
// todo: test whether there are issues
// hangul dev apparently did use it at least the hangul hardware event decoder in latinIme suggests it
/** /**
* Include all suggestions from all dictionaries in * Include all suggestions from all dictionaries in

View file

@ -34,6 +34,7 @@ import androidx.annotation.NonNull;
import org.dslul.openboard.inputmethod.compat.SuggestionSpanUtils; import org.dslul.openboard.inputmethod.compat.SuggestionSpanUtils;
import org.dslul.openboard.inputmethod.event.Event; import org.dslul.openboard.inputmethod.event.Event;
import org.dslul.openboard.inputmethod.event.HangulEventDecoder;
import org.dslul.openboard.inputmethod.event.InputTransaction; import org.dslul.openboard.inputmethod.event.InputTransaction;
import org.dslul.openboard.inputmethod.keyboard.Keyboard; import org.dslul.openboard.inputmethod.keyboard.Keyboard;
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher; import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher;
@ -58,6 +59,7 @@ import org.dslul.openboard.inputmethod.latin.suggestions.SuggestionStripViewAcce
import org.dslul.openboard.inputmethod.latin.utils.AsyncResultHolder; import org.dslul.openboard.inputmethod.latin.utils.AsyncResultHolder;
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils; import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils;
import org.dslul.openboard.inputmethod.latin.utils.RecapitalizeStatus; import org.dslul.openboard.inputmethod.latin.utils.RecapitalizeStatus;
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
import org.dslul.openboard.inputmethod.latin.utils.StatsUtils; import org.dslul.openboard.inputmethod.latin.utils.StatsUtils;
import org.dslul.openboard.inputmethod.latin.utils.TextRange; import org.dslul.openboard.inputmethod.latin.utils.TextRange;
@ -445,7 +447,18 @@ public final class InputLogic {
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) { final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
mWordBeingCorrectedByCursor = null; mWordBeingCorrectedByCursor = null;
mJustRevertedACommit = false; mJustRevertedACommit = false;
final Event processedEvent = mWordComposer.processEvent(event); final Event processedEvent;
if (currentKeyboardScriptId == ScriptUtils.SCRIPT_HANGUL
// only use the Hangul chain if codepoint may actually be Hangul
// todo: this whole hangul-related logic should probably be somewhere else
&& event.getMCodePoint() >= 0x1100) {
mWordComposer.setHangul(true);
final Event hangulDecodedEvent = HangulEventDecoder.decodeSoftwareKeyEvent(event);
processedEvent = mWordComposer.processEvent(hangulDecodedEvent);
} else {
mWordComposer.setHangul(false);
processedEvent = mWordComposer.processEvent(event);
}
final InputTransaction inputTransaction = new InputTransaction(settingsValues, final InputTransaction inputTransaction = new InputTransaction(settingsValues,
processedEvent, SystemClock.uptimeMillis(), mSpaceState, processedEvent, SystemClock.uptimeMillis(), mSpaceState,
getActualCapsMode(settingsValues, keyboardShiftMode)); getActualCapsMode(settingsValues, keyboardShiftMode));
@ -736,6 +749,11 @@ public final class InputLogic {
// line, so that does affect the contents of the editor. // line, so that does affect the contents of the editor.
inputTransaction.setDidAffectContents(); inputTransaction.setDidAffectContents();
break; break;
case Constants.CODE_OUTPUT_TEXT:
// added in the hangul branch, but without this a space after a period crashes
// -> where is the change?
mWordComposer.applyProcessedEvent(event);
break;
case Constants.CODE_START_ONE_HANDED_MODE: case Constants.CODE_START_ONE_HANDED_MODE:
case Constants.CODE_STOP_ONE_HANDED_MODE: case Constants.CODE_STOP_ONE_HANDED_MODE:
// Note: One-handed mode activation is being // Note: One-handed mode activation is being

View file

@ -50,6 +50,7 @@ public class ScriptUtils {
public static final int SCRIPT_TELUGU = 16; public static final int SCRIPT_TELUGU = 16;
public static final int SCRIPT_THAI = 17; public static final int SCRIPT_THAI = 17;
public static final int SCRIPT_BULGARIAN = 18; public static final int SCRIPT_BULGARIAN = 18;
public static final int SCRIPT_HANGUL = 19;
private static final TreeMap<String, Integer> mLanguageCodeToScriptCode; private static final TreeMap<String, Integer> mLanguageCodeToScriptCode;
private final static ArraySet<Integer> UPPERCASE_SCRIPTS = new ArraySet<>(); private final static ArraySet<Integer> UPPERCASE_SCRIPTS = new ArraySet<>();
@ -76,6 +77,7 @@ public class ScriptUtils {
mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU); mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU);
mLanguageCodeToScriptCode.put("th", SCRIPT_THAI); mLanguageCodeToScriptCode.put("th", SCRIPT_THAI);
mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC); mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC);
mLanguageCodeToScriptCode.put("ko", SCRIPT_HANGUL);
// only Latin, Cyrillic, Greek and Armenian have upper/lower case // only Latin, Cyrillic, Greek and Armenian have upper/lower case
// https://unicode.org/faq/casemap_charprop.html#3 // https://unicode.org/faq/casemap_charprop.html#3
@ -193,6 +195,13 @@ public class ScriptUtils {
case SCRIPT_THAI: case SCRIPT_THAI:
// Thai unicode block is U+0E00..U+0E7F // Thai unicode block is U+0E00..U+0E7F
return (codePoint >= 0xE00 && codePoint <= 0xE7F); return (codePoint >= 0xE00 && codePoint <= 0xE7F);
case SCRIPT_HANGUL:
return (codePoint >= 0xAC00 && codePoint <= 0xD7A3
|| codePoint >= 0x3131 && codePoint <= 0x318E
|| codePoint >= 0x1100 && codePoint <= 0x11FF
|| codePoint >= 0xA960 && codePoint <= 0xA97C
|| codePoint >= 0xD7B0 && codePoint <= 0xD7C6
|| codePoint >= 0xD7CB && codePoint <= 0xD7FB);
case SCRIPT_UNKNOWN: case SCRIPT_UNKNOWN:
return true; return true;
default: default:

View file

@ -610,6 +610,7 @@
<enum name="telugu" value="16" /> <enum name="telugu" value="16" />
<enum name="thai" value="17" /> <enum name="thai" value="17" />
<enum name="bulgarian" value="18" /> <enum name="bulgarian" value="18" />
<enum name="hangul" value="19" />
</attr> </attr>
</declare-styleable> </declare-styleable>

View file

@ -272,6 +272,12 @@
<!-- Description for "LANGUAGE_NAME" (Compact) keyboard subtype [CHAR LIMIT=25] <!-- Description for "LANGUAGE_NAME" (Compact) keyboard subtype [CHAR LIMIT=25]
(Compact) can be an abbreviation to fit in the CHAR LIMIT. --> (Compact) can be an abbreviation to fit in the CHAR LIMIT. -->
<string name="subtype_generic_compact"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string> <string name="subtype_generic_compact"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string>
<!-- Description for "LANGUAGE_NAME" (Sebeolsik 390) keyboard subtype [CHAR LIMIT=25]
(Sebeolsik 390) can be an abbreviation to fit in the CHAR LIMIT. -->
<string name="subtype_generic_sebeolsik_390"><xliff:g id="LANGUAGE_NAME" example="Korean">%s</xliff:g> (Sebeolsik 390)</string>
<!-- Description for "LANGUAGE_NAME" (Sebeolsik Final) keyboard subtype [CHAR LIMIT=25]
(Sebeolsik Final) can be an abbreviation to fit in the CHAR LIMIT. -->
<string name="subtype_generic_sebeolsik_final"><xliff:g id="LANGUAGE_NAME" example="Korean">%s</xliff:g> (Sebeolsik Final)</string>
<!-- This string is displayed in a language list that allows to choose a language for <!-- This string is displayed in a language list that allows to choose a language for
suggestions in a software keyboard. This setting won't give suggestions in any particular suggestions in a software keyboard. This setting won't give suggestions in any particular
language, hence "No language". language, hence "No language".

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include latin:keyboardLayout="@xml/key_styles_common" />
<Row latin:keyWidth="9%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_2"
latin:keyXPos="4.5%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p" >
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10.0%p" />
<include latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_3" />
<include latin:keyboardLayout="@xml/keys_exclamation_question"
latin:keyWidth="9.0%p" />
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<include latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include latin:keyboardLayout="@xml/key_styles_common" />
<switch>
<case
latin:numberRowEnabled="true"
>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_0" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_2"
latin:keyXPos="4%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10.0%p" />
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_3" />
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x003f;" />
</case>
<default>
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x003f;"
latin:additionalMoreKeys="&#x003f;" />
</default>
</switch>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="fillRight" />
</Row>
</case>
<default>
<Row
latin:keyWidth="10%p"
latin:rowHeight="15%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_0" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_2"
latin:keyXPos="4%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10.0%p" />
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_3" />
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x003f;" />
</case>
<default>
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x003f;"
latin:additionalMoreKeys="&#x003f;" />
</default>
</switch>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="fillRight" />
</Row>
</default>
</switch>
<include latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include latin:keyboardLayout="@xml/key_styles_common" />
<switch>
<case
latin:numberRowEnabled="true"
>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_0" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_2"
latin:keyXPos="4%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="25%p" >
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10.0%p" />
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_3" />
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x0021;" />
</case>
<default>
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x0021;"
latin:additionalMoreKeys="&#x0021;" />
</default>
</switch>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="fillRight" />
</Row>
</case>
<default>
<Row
latin:keyWidth="10%p"
latin:rowHeight="15%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_0" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_2"
latin:keyXPos="4%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
</Row>
<Row latin:keyWidth="9%p"
latin:rowHeight="20%p" >
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10.0%p" />
<include latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_3" />
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x0021;" />
</case>
<default>
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x0021;"
latin:additionalMoreKeys="&#x0021;" />
</default>
</switch>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="fillRight" />
</Row>
</default>
</switch>
<include latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/rows_korean_dubeolsik_standard" />
</Keyboard>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/rows_korean_sebeolsik_390" />
</Keyboard>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/rows_korean_sebeolsik_final" />
</Keyboard>

View file

@ -110,7 +110,7 @@
th: Thai (Baht) th: Thai (Baht)
uk: Ukrainian (Hryvnia) uk: Ukrainian (Hryvnia)
vi: Vietnamese (Dong) --> vi: Vietnamese (Dong) -->
<case latin:languageCode="fa|hi|iw|lo|mn|ne|th|uk|vi"> <case latin:languageCode="fa|hi|iw|ko|lo|mn|ne|th|uk|vi">
<include latin:keyboardLayout="@xml/key_styles_currency_generic" /> <include latin:keyboardLayout="@xml/key_styles_currency_generic" />
</case> </case>
<!-- hy: Armenian (Armenian Dram) --> <!-- hy: Armenian (Armenian Dram) -->

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<KeyboardLayoutSet
xmlns:latin="http://schemas.android.com/apk/res-auto">
<Feature
latin:supportedScript="hangul" />
<Element
latin:elementName="alphabet"
latin:elementKeyboard="@xml/kbd_korean_dubeolsik_standard"
latin:enableProximityCharsCorrection="true" />
<Element
latin:elementName="symbols"
latin:elementKeyboard="@xml/kbd_symbols" />
<Element
latin:elementName="symbolsShifted"
latin:elementKeyboard="@xml/kbd_symbols_shift" />
<Element
latin:elementName="phone"
latin:elementKeyboard="@xml/kbd_phone" />
<Element
latin:elementName="phoneSymbols"
latin:elementKeyboard="@xml/kbd_phone_symbols" />
<Element
latin:elementName="number"
latin:elementKeyboard="@xml/kbd_number" />
<Element
latin:elementName="numpad"
latin:elementKeyboard="@xml/kbd_numpad" />
</KeyboardLayoutSet>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<KeyboardLayoutSet
xmlns:latin="http://schemas.android.com/apk/res-auto">
<Feature
latin:supportedScript="hangul" />
<Element
latin:elementName="alphabet"
latin:elementKeyboard="@xml/kbd_korean_sebeolsik_390"
latin:enableProximityCharsCorrection="true" />
<Element
latin:elementName="symbols"
latin:elementKeyboard="@xml/kbd_symbols" />
<Element
latin:elementName="symbolsShifted"
latin:elementKeyboard="@xml/kbd_symbols_shift" />
<Element
latin:elementName="phone"
latin:elementKeyboard="@xml/kbd_phone" />
<Element
latin:elementName="phoneSymbols"
latin:elementKeyboard="@xml/kbd_phone_symbols" />
<Element
latin:elementName="number"
latin:elementKeyboard="@xml/kbd_number" />
<Element
latin:elementName="numpad"
latin:elementKeyboard="@xml/kbd_numpad" />
</KeyboardLayoutSet>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<KeyboardLayoutSet
xmlns:latin="http://schemas.android.com/apk/res-auto">
<Feature
latin:supportedScript="hangul" />
<Element
latin:elementName="alphabet"
latin:elementKeyboard="@xml/kbd_korean_sebeolsik_final"
latin:enableProximityCharsCorrection="true" />
<Element
latin:elementName="symbols"
latin:elementKeyboard="@xml/kbd_symbols" />
<Element
latin:elementName="symbolsShifted"
latin:elementKeyboard="@xml/kbd_symbols_shift" />
<Element
latin:elementName="phone"
latin:elementKeyboard="@xml/kbd_phone" />
<Element
latin:elementName="phoneSymbols"
latin:elementKeyboard="@xml/kbd_phone_symbols" />
<Element
latin:elementName="number"
latin:elementKeyboard="@xml/kbd_number" />
<Element
latin:elementName="numpad"
latin:elementKeyboard="@xml/kbd_numpad" />
</KeyboardLayoutSet>

View file

@ -525,6 +525,30 @@
android:imeSubtypeExtraValue="KeyboardLayoutSet=kannada,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=kannada,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x456d04f2"
android:imeSubtypeLocale="ko"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=korean,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic_sebeolsik_390"
android:subtypeId="0xb0bbe261"
android:imeSubtypeLocale="ko"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=korean_sebeolsik_390,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic_sebeolsik_final"
android:subtypeId="0xb6774038"
android:imeSubtypeLocale="ko"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=korean_sebeolsik_final,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark" <subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:subtypeId="0x2e391c04" android:subtypeId="0x2e391c04"

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x3143;" />
<Key
latin:keySpec="&#x3149;" />
<Key
latin:keySpec="&#x3138;" />
<Key
latin:keySpec="&#x3132;" />
<Key
latin:keySpec="&#x3146;" />
</case>
<default>
<Key
latin:keySpec="&#x3142;"
latin:keyHintLabel="&#x3143;"
latin:additionalMoreKeys="&#x3143;" />
<Key
latin:keySpec="&#x3148;"
latin:keyHintLabel="&#x3149;"
latin:additionalMoreKeys="&#x3149;" />
<Key
latin:keySpec="&#x3137;"
latin:keyHintLabel="&#x3138;"
latin:additionalMoreKeys="&#x3138;" />
<Key
latin:keySpec="&#x3131;"
latin:keyHintLabel="&#x3132;"
latin:additionalMoreKeys="&#x3132;" />
<Key
latin:keySpec="&#x3145;"
latin:keyHintLabel="&#x3146;"
latin:additionalMoreKeys="&#x3146;" />
</default>
</switch>
<Key
latin:keySpec="&#x315b;" />
<Key
latin:keySpec="&#x3155;" />
<Key
latin:keySpec="&#x3151;" />
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x3152;" />
<Key
latin:keySpec="&#x3156;" />
</case>
<default>
<Key
latin:keySpec="&#x3150;"
latin:keyHintLabel="&#x3152;"
latin:additionalMoreKeys="&#x3152;" />
<Key
latin:keySpec="&#x3154;"
latin:keyHintLabel="&#x3156;"
latin:additionalMoreKeys="&#x3156;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<Key
latin:keySpec="&#x3141;" />
<Key
latin:keySpec="&#x3134;" />
<Key
latin:keySpec="&#x3147;" />
<Key
latin:keySpec="&#x3139;" />
<Key
latin:keySpec="&#x314e;" />
<Key
latin:keySpec="&#x3157;" />
<Key
latin:keySpec="&#x3153;" />
<Key
latin:keySpec="&#x314f;" />
<Key
latin:keySpec="&#x3163;" />
</merge>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<Key
latin:keySpec="&#x314b;" />
<Key
latin:keySpec="&#x314c;" />
<Key
latin:keySpec="&#x314a;" />
<Key
latin:keySpec="&#x314d;" />
<Key
latin:keySpec="&#x3160;" />
<Key
latin:keySpec="&#x315c;" />
<Key
latin:keySpec="&#x3161;" />
</merge>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11bd;" />
<Key
latin:keySpec="&#x0040;" />
<Key
latin:keySpec="&#x0023;" />
<Key
latin:keySpec="&#x0024;" />
<Key
latin:keySpec="&#x0025;" />
<Key
latin:keySpec="&#x005e;" />
<Key
latin:keySpec="&#x0026;" />
<Key
latin:keySpec="&#x002a;" />
<Key
latin:keySpec="&#x0028;" />
<Key
latin:keySpec="&#x0029;" />
</case>
<default>
<Key
latin:keySpec="&#x11c2;"
latin:keyHintLabel="&#x11bd;"
latin:additionalMoreKeys="&#x11bd;" />
<Key
latin:keySpec="&#x11bb;"
latin:keyHintLabel="&#x0040;"
latin:additionalMoreKeys="&#x0040;" />
<Key
latin:keySpec="&#x11b8;"
latin:keyHintLabel="&#x0023;"
latin:additionalMoreKeys="&#x0023;" />
<Key
latin:keySpec="&#x116d;"
latin:keyHintLabel="&#x0024;"
latin:additionalMoreKeys="&#x0024;" />
<Key
latin:keySpec="&#x1172;"
latin:keyHintLabel="&#x0025;"
latin:additionalMoreKeys="&#x0025;" />
<Key
latin:keySpec="&#x1163;"
latin:keyHintLabel="&#x005e;"
latin:additionalMoreKeys="&#x005e;" />
<Key
latin:keySpec="&#x1168;"
latin:keyHintLabel="&#x0026;"
latin:additionalMoreKeys="&#x0026;" />
<Key
latin:keySpec="&#x1174;"
latin:keyHintLabel="&#x002a;"
latin:additionalMoreKeys="&#x002a;" />
<Key
latin:keySpec="&#x116e;"
latin:keyHintLabel="&#x0028;"
latin:additionalMoreKeys="&#x0028;" />
<Key
latin:keySpec="&#x110f;"
latin:keyHintLabel="&#x0029;"
latin:additionalMoreKeys="&#x0029;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11c1;" />
<Key
latin:keySpec="&#x11c0;" />
<Key
latin:keySpec="&#x11ac;" />
<Key
latin:keySpec="&#x11b6;" />
<Key
latin:keySpec="&#x11b3;" />
<Key
latin:keySpec="&#x003c;" />
<Key
latin:keySpec="&#x0037;" />
<Key
latin:keySpec="&#x0038;" />
<Key
latin:keySpec="&#x0039;" />
<Key
latin:keySpec="&#x003e;" />
</case>
<default>
<Key
latin:keySpec="&#x11ba;"
latin:keyHintLabel="&#x11c1;"
latin:additionalMoreKeys="&#x11c1;" />
<Key
latin:keySpec="&#x11af;"
latin:keyHintLabel="&#x11c0;"
latin:additionalMoreKeys="&#x11c0;" />
<Key
latin:keySpec="&#x1167;"
latin:keyHintLabel="&#x11ac;"
latin:additionalMoreKeys="&#x11ac;" />
<Key
latin:keySpec="&#x1162;"
latin:keyHintLabel="&#x11b6;"
latin:additionalMoreKeys="&#x11b6;" />
<Key
latin:keySpec="&#x1165;"
latin:keyHintLabel="&#x11b3;"
latin:additionalMoreKeys="&#x11b3;" />
<Key
latin:keySpec="&#x1105;"
latin:keyHintLabel="&#x003c;"
latin:additionalMoreKeys="&#x003c;" />
<Key
latin:keySpec="&#x1103;"
latin:keyHintLabel="&#x0037;"
latin:additionalMoreKeys="&#x0037;" />
<Key
latin:keySpec="&#x1106;"
latin:keyHintLabel="&#x0038;"
latin:additionalMoreKeys="&#x0038;" />
<Key
latin:keySpec="&#x110e;"
latin:keyHintLabel="&#x0039;"
latin:additionalMoreKeys="&#x0039;" />
<Key
latin:keySpec="&#x1111;"
latin:keyHintLabel="&#x003e;"
latin:additionalMoreKeys="&#x003e;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11ae;" />
<Key
latin:keySpec="&#x11ad;" />
<Key
latin:keySpec="&#x11b0;" />
<Key
latin:keySpec="&#x11a9;" />
<Key
latin:keySpec="&#x002f;" />
<Key
latin:keySpec="&#x0027;" />
<Key
latin:keySpec="&#x0034;" />
<Key
latin:keySpec="&#x0035;" />
<Key
latin:keySpec="&#x0036;" />
<Key
latin:keySpec="&#x003a;" />
</case>
<default>
<Key
latin:keySpec="&#x11bc;"
latin:keyHintLabel="&#x11ae;"
latin:additionalMoreKeys="&#x11ae;" />
<Key
latin:keySpec="&#x11ab;"
latin:keyHintLabel="&#x11ad;"
latin:additionalMoreKeys="&#x11ad;" />
<Key
latin:keySpec="&#x1175;"
latin:keyHintLabel="&#x11b0;"
latin:additionalMoreKeys="&#x11b0;" />
<Key
latin:keySpec="&#x1161;"
latin:keyHintLabel="&#x11a9;"
latin:additionalMoreKeys="&#x11a9;" />
<Key
latin:keySpec="&#x1173;"
latin:keyHintLabel="&#x002f;"
latin:additionalMoreKeys="&#x002f;" />
<Key
latin:keySpec="&#x1102;"
latin:keyHintLabel="&#x0027;"
latin:additionalMoreKeys="&#x0027;" />
<Key
latin:keySpec="&#x110b;"
latin:keyHintLabel="&#x0034;"
latin:additionalMoreKeys="&#x0034;" />
<Key
latin:keySpec="&#x1100;"
latin:keyHintLabel="&#x0035;"
latin:additionalMoreKeys="&#x0035;" />
<Key
latin:keySpec="&#x110c;"
latin:keyHintLabel="&#x0036;"
latin:additionalMoreKeys="&#x0036;" />
<Key
latin:keySpec="&#x1107;"
latin:keyHintLabel="&#x003a;"
latin:additionalMoreKeys="&#x003a;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11be;" />
<Key
latin:keySpec="&#x11b9;" />
<Key
latin:keySpec="&#x11b1;" />
<Key
latin:keySpec="&#x11b6;" />
<Key
latin:keySpec="&#x0030;"
latin:keyHintLabel="&#x0021;"
latin:additionalMoreKeys="&#x0021;" />
<Key
latin:keySpec="&#x0031;" />
<Key
latin:keySpec="&#x0032;" />
<Key
latin:keySpec="&#x0033;"
latin:keyHintLabel="&#x0022;"
latin:additionalMoreKeys="&#x0022;" />
</case>
<default>
<Key
latin:keySpec="&#x11b7;"
latin:keyHintLabel="&#x11be;"
latin:additionalMoreKeys="&#x11be;" />
<Key
latin:keySpec="&#x11a8;"
latin:keyHintLabel="&#x11b9;"
latin:additionalMoreKeys="&#x11b9;" />
<Key
latin:keySpec="&#x1166;"
latin:keyHintLabel="&#x11b1;"
latin:additionalMoreKeys="&#x11b1;" />
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x11b6;"
latin:additionalMoreKeys="&#x11b6;" />
<Key
latin:keySpec="&#x116e;"
latin:keyHintLabel="&#x0030;"
latin:additionalMoreKeys="&#x0030;,&#x0021;" />
<Key
latin:keySpec="&#x1109;"
latin:keyHintLabel="&#x0031;"
latin:additionalMoreKeys="&#x0031;" />
<Key
latin:keySpec="&#x1112;"
latin:keyHintLabel="&#x0032;"
latin:additionalMoreKeys="&#x0032;" />
<Key
latin:keySpec="&#x1110;"
latin:keyHintLabel="&#x0033;"
latin:additionalMoreKeys="&#x0033;,&#x0022;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11a9;" />
<Key
latin:keySpec="&#x11b0;" />
<Key
latin:keySpec="&#x11bd;" />
<Key
latin:keySpec="&#x11b5;" />
<Key
latin:keySpec="&#x11b4;" />
<Key
latin:keySpec="&#x003d;" />
<Key
latin:keySpec="&#x201c;" />
<Key
latin:keySpec="&#x201d;" />
<Key
latin:keySpec="&#x0027;" />
<Key
latin:keySpec="&#x007e;" />
</case>
<default>
<Key
latin:keySpec="&#x11c2;"
latin:keyHintLabel="&#x11a9;"
latin:additionalMoreKeys="&#x11a9;" />
<Key
latin:keySpec="&#x11bb;"
latin:keyHintLabel="&#x11b0;"
latin:additionalMoreKeys="&#x11b0;" />
<Key
latin:keySpec="&#x11b8;"
latin:keyHintLabel="&#x11bd;"
latin:additionalMoreKeys="&#x11bd;" />
<Key
latin:keySpec="&#x116d;"
latin:keyHintLabel="&#x11b5;"
latin:additionalMoreKeys="&#x11b5;" />
<Key
latin:keySpec="&#x1172;"
latin:keyHintLabel="&#x11b4;"
latin:additionalMoreKeys="&#x11b4;" />
<Key
latin:keySpec="&#x1163;"
latin:keyHintLabel="&#x003d;"
latin:additionalMoreKeys="&#x003d;" />
<Key
latin:keySpec="&#x1168;"
latin:keyHintLabel="&#x201c;"
latin:additionalMoreKeys="&#x201c;" />
<Key
latin:keySpec="&#x1174;"
latin:keyHintLabel="&#x201d;"
latin:additionalMoreKeys="&#x201d;" />
<Key
latin:keySpec="&#x116e;"
latin:keyHintLabel="&#x0027;"
latin:additionalMoreKeys="&#x0027;" />
<Key
latin:keySpec="&#x110f;"
latin:keyHintLabel="&#x007e;"
latin:additionalMoreKeys="&#x007e;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11c1;" />
<Key
latin:keySpec="&#x11c0;" />
<Key
latin:keySpec="&#x11ac;" />
<Key
latin:keySpec="&#x11b6;" />
<Key
latin:keySpec="&#x11b3;" />
<Key
latin:keySpec="&#x0035;" />
<Key
latin:keySpec="&#x0036;" />
<Key
latin:keySpec="&#x0037;" />
<Key
latin:keySpec="&#x0038;" />
<Key
latin:keySpec="&#x0039;" />
</case>
<default>
<Key
latin:keySpec="&#x11ba;"
latin:keyHintLabel="&#x11c1;"
latin:additionalMoreKeys="&#x11c1;" />
<Key
latin:keySpec="&#x11af;"
latin:keyHintLabel="&#x11c0;"
latin:additionalMoreKeys="&#x11c0;" />
<Key
latin:keySpec="&#x1167;"
latin:keyHintLabel="&#x11ac;"
latin:additionalMoreKeys="&#x11ac;" />
<Key
latin:keySpec="&#x1162;"
latin:keyHintLabel="&#x11b6;"
latin:additionalMoreKeys="&#x11b6;" />
<Key
latin:keySpec="&#x1165;"
latin:keyHintLabel="&#x11b3;"
latin:additionalMoreKeys="&#x11b3;" />
<Key
latin:keySpec="&#x1105;"
latin:keyHintLabel="&#x0035;"
latin:additionalMoreKeys="&#x0035;" />
<Key
latin:keySpec="&#x1103;"
latin:keyHintLabel="&#x0036;"
latin:additionalMoreKeys="&#x0036;" />
<Key
latin:keySpec="&#x1106;"
latin:keyHintLabel="&#x0037;"
latin:additionalMoreKeys="&#x0037;" />
<Key
latin:keySpec="&#x110e;"
latin:keyHintLabel="&#x0038;"
latin:additionalMoreKeys="&#x0038;" />
<Key
latin:keySpec="&#x1111;"
latin:keyHintLabel="&#x0039;"
latin:additionalMoreKeys="&#x0039;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11ae;" />
<Key
latin:keySpec="&#x11ad;" />
<Key
latin:keySpec="&#x11b2;" />
<Key
latin:keySpec="&#x11b1;" />
<Key
latin:keySpec="&#x1164;" />
<Key
latin:keySpec="&#x0030;" />
<Key
latin:keySpec="&#x0031;" />
<Key
latin:keySpec="&#x0032;" />
<Key
latin:keySpec="&#x0033;" />
<Key
latin:keySpec="&#x0034;" />
</case>
<default>
<Key
latin:keySpec="&#x11bc;"
latin:keyHintLabel="&#x11ae;"
latin:additionalMoreKeys="&#x11ae;" />
<Key
latin:keySpec="&#x11ab;"
latin:keyHintLabel="&#x11ad;"
latin:additionalMoreKeys="&#x11ad;" />
<Key
latin:keySpec="&#x1175;"
latin:keyHintLabel="&#x11b2;"
latin:additionalMoreKeys="&#x11b2;" />
<Key
latin:keySpec="&#x1161;"
latin:keyHintLabel="&#x11b1;"
latin:additionalMoreKeys="&#x11b1;" />
<Key
latin:keySpec="&#x1173;"
latin:keyHintLabel="&#x1164;"
latin:additionalMoreKeys="&#x1164;" />
<Key
latin:keySpec="&#x1102;"
latin:keyHintLabel="&#x0030;"
latin:additionalMoreKeys="&#x0030;" />
<Key
latin:keySpec="&#x110b;"
latin:keyHintLabel="&#x0031;"
latin:additionalMoreKeys="&#x0031;" />
<Key
latin:keySpec="&#x1100;"
latin:keyHintLabel="&#x0032;"
latin:additionalMoreKeys="&#x0032;" />
<Key
latin:keySpec="&#x110c;"
latin:keyHintLabel="&#x0033;"
latin:additionalMoreKeys="&#x0033;" />
<Key
latin:keySpec="&#x1107;"
latin:keyHintLabel="&#x0034;"
latin:additionalMoreKeys="&#x0034;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:latin="http://schemas.android.com/apk/res-auto">
<switch>
<case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
<Key
latin:keySpec="&#x11be;" />
<Key
latin:keySpec="&#x11b9;" />
<Key
latin:keySpec="&#x11bf;" />
<Key
latin:keySpec="&#x11aa;" />
<Key
latin:keySpec="&#x003f;" />
<Key
latin:keySpec="&#x002d;" />
<Key
latin:keySpec="&#x0022;" />
<Key
latin:keySpec="&#x00b7;" />
</case>
<default>
<Key
latin:keySpec="&#x11b7;"
latin:keyHintLabel="&#x11be;"
latin:additionalMoreKeys="&#x11be;" />
<Key
latin:keySpec="&#x11a8;"
latin:keyHintLabel="&#x11b9;"
latin:additionalMoreKeys="&#x11b9;" />
<Key
latin:keySpec="&#x1166;"
latin:keyHintLabel="&#x11bf;"
latin:additionalMoreKeys="&#x11bf;" />
<Key
latin:keySpec="&#x1169;"
latin:keyHintLabel="&#x11aa;"
latin:additionalMoreKeys="&#x11aa;" />
<Key
latin:keySpec="&#x116e;"
latin:keyHintLabel="&#x003f;"
latin:additionalMoreKeys="&#x003f;" />
<Key
latin:keySpec="&#x1109;"
latin:keyHintLabel="&#x002d;"
latin:additionalMoreKeys="&#x002d;" />
<Key
latin:keySpec="&#x1112;"
latin:keyHintLabel="&#x0022;"
latin:additionalMoreKeys="&#x0022;" />
<Key
latin:keySpec="&#x1110;"
latin:keyHintLabel="&#x00b7;"
latin:additionalMoreKeys="&#x00b7;" />
</default>
</switch>
</merge>

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/key_styles_common" />
<switch>
<case
latin:numberRowEnabled="true"
>
<Row
latin:keyWidth="10%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_symbols1" />
</Row>
</case>
</switch>
<Row
latin:keyWidth="10%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_1" />
</Row>
<Row
latin:keyWidth="10%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_2"
latin:keyXPos="5%p" />
</Row>
<Row
latin:keyWidth="10%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="15%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_korean_dubeolsik_standard_3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
<include
latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/key_styles_common" />
<switch>
<case
latin:numberRowEnabled="true"
>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_0" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_1" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_2" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
</case>
<default>
<Row
latin:keyWidth="10%p"
latin:rowHeight="15%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_0" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_1" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_2" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_390_3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
</default>
</switch>
<include
latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/key_styles_common" />
<switch>
<case
latin:numberRowEnabled="true"
>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_0" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_1" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_2" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="25%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
</case>
<default>
<Row
latin:keyWidth="10%p"
latin:rowHeight="15%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_0" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_1" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_2" />
</Row>
<Row
latin:keyWidth="10%p"
latin:rowHeight="20%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="10%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_korean_sebeolsik_final_3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
</default>
</switch>
<include
latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- U+20B9: "₩" FULLWIDTH WON SIGN -->
<string name="keyspec_currency">&#xFFE6;</string>
<!-- Label for "switch to alphabetic" key.
U+0995: "ᄀ" HANGUL LETTER KIYEOK
U+0996: "ㄴ" HANGUL LETTER NIEUN
U+0997: "ㄷ" HANGUL LETTER TIKEUT -->
<string name="keylabel_to_alpha">&#x3131;&#x3134;&#x3137;</string>
</resources>