diff --git a/README.md b/README.md index ffd91a37e..297d534a4 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,17 @@ Might end up on F-Droid... * Allow loading Glide typing 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) - * Multilingual typing - * 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) - * additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search") - * Adjust keyboard themes (style and colors) - * can follow the system's day/night setting on Android 10+ (and on some versions of Android 9) - * Split keyboard (if the screen is large enough) - * Number row - * Number pad - * Show all available extra characters on long pressing a key +* Multilingual typing +* 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) + * additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search") + * 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 +* Adjust keyboard themes (style and colors) + * can follow the system's day/night setting on Android 10+ (and on some versions of Android 9) +* Split keyboard (if the screen is large enough) +* Number row +* Number pad +* Show all available extra characters on long pressing a key ## Hidden functionality Features that may go unnoticed diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/Combiner.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/Combiner.kt index 5d1886c73..8b2d91fd5 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/event/Combiner.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/Combiner.kt @@ -17,7 +17,7 @@ interface Combiner { * @param event the event to combine with the existing state. * @return the resulting event. */ - fun processEvent(previousEvents: ArrayList?, event: Event?): Event + fun processEvent(previousEvents: ArrayList?, event: Event): Event /** * Get the feedback that should be shown to the user for the current state of this combiner. diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/CombinerChain.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/CombinerChain.kt index c05934a76..687e57ec5 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/event/CombinerChain.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/CombinerChain.kt @@ -17,12 +17,42 @@ import java.util.* * feedback on the composing state and will typically be shown with different styling such as * a colored background. */ -class CombinerChain(initialText: String?) { +class CombinerChain(initialText: String) { // 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 - private val mStateFeedback: SpannableStringBuilder - private val mCombiners: ArrayList + private val mStateFeedback = SpannableStringBuilder() + private val mCombiners = ArrayList() + // 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() { mCombinedText.setLength(0) 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 * new event. However it may never be null. */ - fun processEvent(previousEvents: ArrayList?, - newEvent: Event): Event { + fun processEvent(previousEvents: ArrayList?, newEvent: Event): Event { val modifiablePreviousEvents = ArrayList(previousEvents!!) var event = newEvent - for (combiner in mCombiners) { // A combiner can never return more than one event; it can return several -// code points, but they should be encapsulated within one event. + for (combiner in mCombiners) { + // 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) - if (event.isConsumed) { // If the event is consumed, then we don't pass it to subsequent combiners: -// they should not see it at all. + if (event.isConsumed) { + // If the event is consumed, then we don't pass it to subsequent combiners: + // they should not see it at all. break } } @@ -93,21 +124,4 @@ class CombinerChain(initialText: String?) { 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() - } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/DeadKeyCombiner.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/DeadKeyCombiner.kt index 4081fed7a..678c91c6e 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/event/DeadKeyCombiner.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/DeadKeyCombiner.kt @@ -188,18 +188,18 @@ class DeadKeyCombiner : Combiner { // TODO: make this a list of events instead val mDeadSequence = StringBuilder() - override fun processEvent(previousEvents: ArrayList?, event: Event?): Event { + override fun processEvent(previousEvents: ArrayList?, event: Event): Event { 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) return Event.createConsumedEvent(event) } // 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 -// simply returns the event as is. The majority of events will go through this path. + // 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. 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. val resultEvent = createEventChainFromSequence(mDeadSequence.toString(), event) mDeadSequence.setLength(0) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/Event.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/Event.kt index ba0563794..469615093 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/event/Event.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/Event.kt @@ -17,59 +17,59 @@ import org.dslul.openboard.inputmethod.latin.common.StringUtils * for example. * The combiner should figure out what to do with this. */ -class Event private constructor(// The type of event - one of the constants above - private val mEventType: Int, // If applicable, this contains the string that should be input. - val mText: CharSequence?, // The code point associated with the event, if relevant. This is a unicode code point, and -// has nothing to do with other representations of the key. It is only relevant if this event -// is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point -// associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when -// it's not relevant. - val mCodePoint: Int, // 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. +class Event private constructor( + // The type of event - one of the constants above + private val mEventType: Int, + // If applicable, this contains the string that should be input. + val mText: CharSequence?, + // The code point associated with the event, if relevant. This is a unicode code point, and + // has nothing to do with other representations of the key. It is only relevant if this event + // is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point + // associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when + // it's not relevant. + val mCodePoint: Int, + // 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, // 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 software keyboard obviously, unless there are touch-sensitive hardware keyboards in the -// future or some other awesome sauce. + // 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 + // future or some other awesome sauce. 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 -// other cases). + // other cases). val mSuggestedWordInfo: SuggestedWordInfo?, // Some flags that can't go into the key code. It's a bit field of FLAG_* private val mFlags: Int, // 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 -// that result in input like letters or space. + // that result in input like letters or space. val isFunctionalKeyEvent: Boolean - get() =// This logic may need to be refined in the future - NOT_A_CODE_POINT == mCodePoint + get() = NOT_A_CODE_POINT == mCodePoint // This logic may need to be refined in the future // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD} - val isDead: Boolean - get() = 0 != FLAG_DEAD and mFlags + val isDead: Boolean get() = 0 != FLAG_DEAD and mFlags - val isKeyRepeat: Boolean - get() = 0 != FLAG_REPEAT and mFlags + val isKeyRepeat: Boolean get() = 0 != FLAG_REPEAT and mFlags - val isConsumed: Boolean - get() = 0 != FLAG_CONSUMED and mFlags + val isConsumed: Boolean get() = 0 != FLAG_CONSUMED and mFlags - val isGesture: Boolean - get() = EVENT_TYPE_GESTURE == mEventType + val isCombining: Boolean get() = 0 != FLAG_COMBINING and mFlags + + val isGesture: Boolean get() = EVENT_TYPE_GESTURE == mEventType // Returns whether this is a fake key press from the suggestion strip. This happens with -// punctuation signs selected from the suggestion strip. - val isSuggestionStripPress: Boolean - get() = EVENT_TYPE_SUGGESTION_PICKED == mEventType + // punctuation signs selected from the suggestion strip. + val isSuggestionStripPress: Boolean get() = EVENT_TYPE_SUGGESTION_PICKED == mEventType - val isHandled: Boolean - get() = EVENT_TYPE_NOT_HANDLED != mEventType + val isHandled: Boolean get() = EVENT_TYPE_NOT_HANDLED != mEventType // A consumed event should input no text. val textToCommit: CharSequence? @@ -87,20 +87,20 @@ class Event private constructor(// The type of event - one of the constants abov companion object { // Should the types below be represented by separate classes instead? It would be cleaner -// but probably a bit too much -// An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard. + // but probably a bit too much + // An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard. const val EVENT_TYPE_NOT_HANDLED = 0 // 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 -// through combination. + // hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later + // through combination. const val EVENT_TYPE_INPUT_KEYPRESS = 1 // 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 -// repeated presses. + // be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with + // repeated presses. const val EVENT_TYPE_TOGGLE = 2 // 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 -// if handled at the combiner level. + // hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard + // if handled at the combiner level. const val EVENT_TYPE_MODE_KEY = 3 // An event corresponding to a gesture. 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 private const val FLAG_NONE = 0 // 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 // This event is coming from a key repeat, software or hardware. private const val FLAG_REPEAT = 0x2 // This event has already been consumed. private const val FLAG_CONSUMED = 0x4 + // This event is a combining character, usually a hangul input. + private const val FLAG_COMBINING = 0x8 @JvmStatic 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. * @param text the CharSequence associated with this event. * @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. */ @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, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - null /* suggestedWordInfo */, FLAG_NONE, null /* next */) + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, + 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. * @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 fun createPunctuationSuggestionPickedEvent( suggestedWordInfo: SuggestedWordInfo): Event { - val primaryCode = suggestedWordInfo.mWord[0].toInt() + val primaryCode = suggestedWordInfo.mWord[0].code return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode, NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE, 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. * @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. - return Event(source!!.mEventType, source.mText, source.mCodePoint, source.mKeyCode, + 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, source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_CONSUMED, 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 { return Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 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. init { // 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 (null == mSuggestedWordInfo) { 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 } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulCombiner.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulCombiner.kt new file mode 100644 index 000000000..29f58e33c --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulCombiner.kt @@ -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 = mutableListOf() + private val syllable: HangulSyllable? get() = history.lastOrNull() + + override fun processEvent(previousEvents: ArrayList?, 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? = 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, 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, 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) + } + } + +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulEventDecoder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulEventDecoder.kt new file mode 100644 index 000000000..42f88e6bf --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/HangulEventDecoder.kt @@ -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>( + 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>( + 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>( + 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>>( + "korean" to LAYOUT_DUBEOLSIK_STANDARD, + "korean_sebeolsik_390" to LAYOUT_SEBEOLSIK_390, + "korean_sebeolsik_final" to LAYOUT_SEBEOLSIK_FINAL + ) + +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/event/HardwareKeyboardEventDecoder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/event/HardwareKeyboardEventDecoder.kt index 91991e8d9..cd0d15c66 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/event/HardwareKeyboardEventDecoder.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/event/HardwareKeyboardEventDecoder.kt @@ -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 * 10-key like keyboards; a different decoder is used for this. */ -class HardwareKeyboardEventDecoder // TODO: get the layout for this hardware keyboard -(val mDeviceId: Int) : HardwareEventDecoder { - override fun decodeHardwareKey(keyEvent: KeyEvent): Event { // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value -// 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. +// TODO: get the layout for this hardware keyboard +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 + // 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 // 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 -// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock. + // 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. val keyCode = keyEvent.keyCode val isKeyRepeat = 0 != keyEvent.repeatCount if (KeyEvent.KEYCODE_DEL == keyCode) { - return Event.Companion.createHardwareKeypressEvent(Event.Companion.NOT_A_CODE_POINT, Constants.CODE_DELETE, - null /* next */, isKeyRepeat) + return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_DELETE, null /* next */, isKeyRepeat) } if (keyEvent.isPrintingKey || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) { if (0 != codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT) { // A dead key. - return Event.Companion.createDeadEvent( - codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, - null /* next */) + return Event.createDeadEvent( + codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, null /* next */) } - return if (KeyEvent.KEYCODE_ENTER == keyCode) { // The Enter key. If the Shift key is not being pressed, this should send a -// CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the -// Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let -// Latin IME decide what to do with it. + return if (KeyEvent.KEYCODE_ENTER == keyCode) { + // The Enter key. If the Shift key is not being pressed, this should send a + // CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the + // 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) { - Event.Companion.createHardwareKeypressEvent(Event.Companion.NOT_A_CODE_POINT, + Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, 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) - } else Event.Companion.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, - isKeyRepeat) + } else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, isKeyRepeat) // 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() } } \ No newline at end of file diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsTable.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsTable.java index f2efaeb12..d7059f87b 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -84,15 +84,15 @@ public final class KeyboardTextsTable { /* 0:34 */ "morekeys_a", /* 1:34 */ "morekeys_o", /* 2:33 */ "morekeys_e", - /* 3:32 */ "morekeys_u", - /* 4:32 */ "keylabel_to_alpha", + /* 3:33 */ "keylabel_to_alpha", + /* 4:32 */ "morekeys_u", /* 5:30 */ "morekeys_i", /* 6:26 */ "morekeys_n", /* 7:25 */ "morekeys_c", /* 8:24 */ "double_quotes", /* 9:23 */ "morekeys_s", /* 10:23 */ "single_quotes", - /* 11:19 */ "keyspec_currency", + /* 11:20 */ "keyspec_currency", /* 12:17 */ "morekeys_y", /* 13:16 */ "morekeys_z", /* 14:14 */ "morekeys_d", @@ -270,12 +270,12 @@ public final class KeyboardTextsTable { /* Default texts */ private static final String[] TEXTS_DEFAULT = { /* morekeys_a ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_u */ + EMPTY, EMPTY, EMPTY, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. /* keylabel_to_alpha */ "ABC", - /* morekeys_i ~ */ - EMPTY, EMPTY, EMPTY, + /* morekeys_u ~ */ + EMPTY, EMPTY, EMPTY, EMPTY, /* ~ morekeys_c */ /* double_quotes */ "!text/double_lqm_rqm", /* morekeys_s */ EMPTY, @@ -521,13 +521,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -550,17 +550,17 @@ public final class KeyboardTextsTable { /* Locale ar: Arabic */ private static final String[] TEXTS_ar = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "ج" ARABIC LETTER JEEM /* keylabel_to_alpha */ "\u0623\u200C\u0628\u200C\u062C", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, + null, null, /* ~ morekeys_r */ // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK @@ -700,13 +700,13 @@ public final class KeyboardTextsTable { // U+0259: "ə" LATIN SMALL LETTER SCHWA // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE /* morekeys_e */ "\u0259,\u00E9", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -744,15 +744,15 @@ public final class KeyboardTextsTable { /* Locale be: Belarusian */ private static final String[] TEXTS_be = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -782,15 +782,15 @@ public final class KeyboardTextsTable { /* Locale bg: Bulgarian */ private static final String[] TEXTS_bg = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ // single_quotes of Bulgarian is default single_quotes_right_left. /* double_quotes */ "!text/double_9qm_lqm", @@ -799,15 +799,15 @@ public final class KeyboardTextsTable { /* Locale bn_BD: Bangla (Bangladesh) */ private static final String[] TEXTS_bn_BD = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0995: "क" BENGALI LETTER KA // U+0996: "ख" BENGALI LETTER KHA // U+0997: "ग" BENGALI LETTER GA /* keylabel_to_alpha */ "\u0995\u0996\u0997", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+09F3: "৳" BENGALI RUPEE SIGN /* keyspec_currency */ "\u09F3", @@ -861,15 +861,15 @@ public final class KeyboardTextsTable { /* Locale bn_IN: Bangla (India) */ private static final String[] TEXTS_bn_IN = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0995: "क" BENGALI LETTER KA // U+0996: "ख" BENGALI LETTER KHA // U+0997: "ग" BENGALI LETTER GA /* keylabel_to_alpha */ "\u0995\u0996\u0997", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -906,13 +906,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E8,\u00E9,\u00EB,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -980,6 +980,7 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u011B,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX @@ -987,7 +988,6 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -1052,13 +1052,13 @@ public final class KeyboardTextsTable { // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS /* morekeys_e */ "\u00E9,\u00EB", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS /* morekeys_i */ "\u00ED,\u00EF", @@ -1130,13 +1130,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0117", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,%,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, /* morekeys_i */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1199,13 +1199,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0117", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, /* morekeys_i */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1245,8 +1245,8 @@ public final class KeyboardTextsTable { /* Locale el: Greek */ private static final String[] TEXTS_el = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA @@ -1280,13 +1280,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -1336,6 +1336,7 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u011B,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX @@ -1347,7 +1348,6 @@ public final class KeyboardTextsTable { // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK // U+00B5: "µ" MICRO SIGN /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B,\u0169,\u0171,\u0173,\u00B5", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -1484,13 +1484,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -1546,6 +1546,7 @@ public final class KeyboardTextsTable { // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK // U+011B: "ě" LATIN SMALL LETTER E WITH CARON /* morekeys_e */ "\u0113,\u00E8,\u0117,\u00E9,\u00EA,\u00EB,\u0119,\u011B", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK @@ -1555,7 +1556,6 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u00FC,\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u016F,\u0171", - /* keylabel_to_alpha */ null, // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK @@ -1655,13 +1655,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -1681,16 +1681,16 @@ public final class KeyboardTextsTable { /* Locale fa: Persian */ private static final String[] TEXTS_fa = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+FDFC: "﷼" RIAL SIGN /* keyspec_currency */ "\uFDFC", @@ -1839,10 +1839,11 @@ public final class KeyboardTextsTable { // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON /* morekeys_o */ "\u00F6,\u00F8,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u014D", /* morekeys_e */ null, + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* morekeys_u */ "\u00FC", - /* keylabel_to_alpha ~ */ - null, null, null, null, null, + /* morekeys_i ~ */ + null, null, null, null, /* ~ double_quotes */ // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -1905,13 +1906,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,%,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00F9,\u00FB,%,\u00FC,\u00FA,\u016B", - /* keylabel_to_alpha */ null, // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -1979,13 +1980,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -2005,15 +2006,15 @@ public final class KeyboardTextsTable { /* Locale he: Hebrew */ private static final String[] TEXTS_he = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL /* keylabel_to_alpha */ "\u05D0\u05D1\u05D2", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_rqm_9qm", /* morekeys_s */ null, @@ -2064,15 +2065,15 @@ public final class KeyboardTextsTable { /* Locale hi: Hindi */ private static final String[] TEXTS_hi = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2192,11 +2193,11 @@ public final class KeyboardTextsTable { /* morekeys_o */ "\u00F3,\u00F6,\u0151", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE /* morekeys_e */ "\u00E9", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u00FA,\u00FC,\u0171", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE /* morekeys_i */ "\u00ED", /* morekeys_n */ null, @@ -2214,17 +2215,17 @@ public final class KeyboardTextsTable { /* Locale hy: Armenian */ private static final String[] TEXTS_hy = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM /* keylabel_to_alpha */ "\u0531\u0532\u0533", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_symbols_0 */ // U+0589: "։" ARMENIAN FULL STOP /* keyspec_period */ "\u0589", @@ -2310,13 +2311,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00EB,\u00E8,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX @@ -2371,13 +2372,13 @@ public final class KeyboardTextsTable { // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON // U+0259: "ə" LATIN SMALL LETTER SCHWA /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0119,\u0117,\u0113,\u0259", + /* keylabel_to_alpha */ null, // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u016B", - /* keylabel_to_alpha */ null, // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX @@ -2408,15 +2409,15 @@ public final class KeyboardTextsTable { /* Locale ka: Georgian */ private static final String[] TEXTS_ka = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -2426,16 +2427,16 @@ public final class KeyboardTextsTable { /* Locale kk: Kazakh */ private static final String[] TEXTS_kk = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2484,14 +2485,14 @@ public final class KeyboardTextsTable { /* Locale km: Khmer */ private static final String[] TEXTS_km = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+1780: "ក" KHMER LETTER KA // U+1781: "ខ" KHMER LETTER KHA // U+1782: "គ" KHMER LETTER KO /* keylabel_to_alpha */ "\u1780\u1781\u1782", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2500,7 +2501,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ morekeys_cyrillic_a */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", @@ -2509,33 +2510,50 @@ public final class KeyboardTextsTable { /* Locale kn: Kannada */ private static final String[] TEXTS_kn = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0C85: "ಅ" KANNADA LETTER A // U+0C86: "ಆ" KANNADA LETTER AA // U+0C87: "ಇ" KANNADA LETTER I /* keylabel_to_alpha */ "\u0C85\u0C86\u0C87", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", }; + /* Locale ko: Korean */ + private static final String[] TEXTS_ko = { + /* morekeys_a ~ */ + null, null, null, + /* ~ morekeys_e */ + // Label for "switch to alphabetic" key. + // U+0995: "ᄀ" HANGUL LETTER KIYEOK + // U+0996: "ㄴ" HANGUL LETTER NIEUN + // U+0997: "ㄷ" HANGUL LETTER TIKEUT + /* keylabel_to_alpha */ "\u3131\u3134\u3137", + /* morekeys_u ~ */ + null, null, null, null, null, null, null, + /* ~ single_quotes */ + // U+20B9: "₩" FULLWIDTH WON SIGN + /* keyspec_currency */ "\uFFE6", + }; + /* Locale ky: Kyrgyz */ private static final String[] TEXTS_ky = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2570,15 +2588,15 @@ public final class KeyboardTextsTable { /* Locale lo: Lao */ private static final String[] TEXTS_lo = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0E81: "ກ" LAO LETTER KO // U+0E82: "ຂ" LAO LETTER KHO SUNG // U+0E84: "ຄ" LAO LETTER KHO TAM /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20AD: "₭" KIP SIGN /* keyspec_currency */ "\u20AD", @@ -2614,6 +2632,7 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+011B: "ě" LATIN SMALL LETTER E WITH CARON /* morekeys_e */ "\u0117,\u0119,\u0113,\u00E8,\u00E9,\u00EA,\u00EB,\u011B", + /* keylabel_to_alpha */ null, // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS @@ -2624,7 +2643,6 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u016B,\u0173,\u00FC,\u016B,\u00F9,\u00FA,\u00FB,\u016F,\u0171", - /* keylabel_to_alpha */ null, // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -2712,6 +2730,7 @@ public final class KeyboardTextsTable { // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK // U+011B: "ě" LATIN SMALL LETTER E WITH CARON /* morekeys_e */ "\u0113,\u0117,\u00E8,\u00E9,\u00EA,\u00EB,\u0119,\u011B", + /* keylabel_to_alpha */ null, // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE @@ -2721,7 +2740,6 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u00FC,\u016F,\u0171", - /* keylabel_to_alpha */ null, // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -2782,15 +2800,15 @@ public final class KeyboardTextsTable { /* Locale mk: Macedonian */ private static final String[] TEXTS_mk = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -2823,13 +2841,13 @@ public final class KeyboardTextsTable { /* Locale ml: Malayalam */ private static final String[] TEXTS_ml = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0D05: "അ" MALAYALAM LETTER A /* keylabel_to_alpha */ "\u0D05", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2838,15 +2856,15 @@ public final class KeyboardTextsTable { /* Locale mn: Mongolian */ private static final String[] TEXTS_mn = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20AE: "₮" TUGRIK SIGN /* keyspec_currency */ "\u20AE", @@ -2855,15 +2873,15 @@ public final class KeyboardTextsTable { /* Locale mr: Marathi */ private static final String[] TEXTS_mr = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2909,17 +2927,17 @@ public final class KeyboardTextsTable { /* Locale my: Burmese */ private static final String[] TEXTS_my = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+1000: "က" MYANMAR LETTER KA // U+1001: "ခ" MYANMAR LETTER KHA // U+1002: "ဂ" MYANMAR LETTER GA /* keylabel_to_alpha */ "\u1000\u1001\u1002", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_symbols_0 */ /* keyspec_period */ "\u104B", /* morekeys_period ~ */ @@ -2976,14 +2994,15 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha ~ */ - null, null, null, null, + /* morekeys_i ~ */ + null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_s */ null, @@ -3011,15 +3030,15 @@ public final class KeyboardTextsTable { /* Locale ne: Nepali */ private static final String[] TEXTS_ne = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN /* keyspec_currency */ "\u0930\u0941.", @@ -3098,13 +3117,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00EB,\u00EA,\u00E8,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -3154,7 +3173,7 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u0119,\u00E8,\u00E9,\u00EA,\u00EB,\u0117,\u0113", - /* morekeys_u ~ */ + /* keylabel_to_alpha ~ */ null, null, null, /* ~ morekeys_i */ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -3211,13 +3230,13 @@ public final class KeyboardTextsTable { // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS /* morekeys_e */ "\u00E9,\u00EA,\u00E8,\u0119,\u0117,\u0113,\u00EB", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -3259,7 +3278,7 @@ public final class KeyboardTextsTable { /* morekeys_a */ "\u0103,\u00E2,\u00E3,\u00E0,\u00E1,\u00E4,\u00E6,\u00E5,\u0101", /* morekeys_o ~ */ null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ morekeys_u */ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -3286,15 +3305,15 @@ public final class KeyboardTextsTable { /* Locale ru: Russian */ private static final String[] TEXTS_ru = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -3324,14 +3343,14 @@ public final class KeyboardTextsTable { /* Locale si: Sinhala */ private static final String[] TEXTS_si = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0D85: "අ" SINHALA LETTER AYANNA // U+0D86: "ආ" SINHALA LETTER AAYANNA /* keylabel_to_alpha */ "\u0D85,\u0D86", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA /* keyspec_currency */ "\u0DBB\u0DD4", @@ -3367,6 +3386,7 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK /* morekeys_e */ "\u00E9,\u011B,\u0113,\u0117,\u00E8,\u00EA,\u00EB,\u0119", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS @@ -3376,7 +3396,6 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u00FA,\u016F,\u00FC,\u016B,\u0173,\u00F9,\u00FB,\u0171", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK @@ -3463,16 +3482,16 @@ public final class KeyboardTextsTable { /* Locale sr: Serbian */ private static final String[] TEXTS_sr = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -3530,8 +3549,8 @@ public final class KeyboardTextsTable { /* morekeys_o */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE /* morekeys_e */ "\u00E8", - /* morekeys_u */ null, /* keylabel_to_alpha */ null, + /* morekeys_u */ null, // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE /* morekeys_i */ "\u00EC", /* morekeys_n */ null, @@ -3593,13 +3612,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FA,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX @@ -3688,13 +3707,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113", + /* keylabel_to_alpha */ null, // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE @@ -3717,15 +3736,15 @@ public final class KeyboardTextsTable { /* Locale ta_IN: Tamil (India) */ private static final String[] TEXTS_ta_IN = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0BA4: "த" TAMIL LETTER TA // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0BF9: "௹" TAMIL RUPEE SIGN /* keyspec_currency */ "\u0BF9", @@ -3734,15 +3753,15 @@ public final class KeyboardTextsTable { /* Locale ta_LK: Tamil (Sri Lanka) */ private static final String[] TEXTS_ta_LK = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0BA4: "த" TAMIL LETTER TA // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA /* keyspec_currency */ "\u0DBB\u0DD4", @@ -3751,8 +3770,8 @@ public final class KeyboardTextsTable { /* Locale ta_SG: Tamil (Singapore) */ private static final String[] TEXTS_ta_SG = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0BA4: "த" TAMIL LETTER TA // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I @@ -3763,15 +3782,15 @@ public final class KeyboardTextsTable { /* Locale te: Telugu */ private static final String[] TEXTS_te = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0C05: "అ" TELUGU LETTER A // U+0C06: "ఆ" TELUGU LETTER AA // U+0C07: "ఇ" TELUGU LETTER I /* keylabel_to_alpha */ "\u0C05\u0C06\u0C07", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -3780,15 +3799,15 @@ public final class KeyboardTextsTable { /* Locale th: Thai */ private static final String[] TEXTS_th = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04", - /* morekeys_i ~ */ - null, null, null, null, null, null, + /* morekeys_u ~ */ + null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT /* keyspec_currency */ "\u0E3F", @@ -3825,13 +3844,13 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE @@ -3866,13 +3885,13 @@ public final class KeyboardTextsTable { // U+0259: "ə" LATIN SMALL LETTER SCHWA // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE /* morekeys_e */ "\u0259,\u00E9", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -3910,15 +3929,15 @@ public final class KeyboardTextsTable { /* Locale uk: Ukrainian */ private static final String[] TEXTS_uk = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_i ~ */ - null, null, null, + /* morekeys_u ~ */ + null, null, null, null, /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_s */ null, @@ -3956,17 +3975,17 @@ public final class KeyboardTextsTable { /* Locale ur: Urdu */ private static final String[] TEXTS_ur = { /* morekeys_a ~ */ - null, null, null, null, - /* ~ morekeys_u */ + null, null, null, + /* ~ morekeys_e */ // Label for "switch to alphabetic" key. // U+0623: "ا" ARABIC LETTER ALEF WITH HAMZA ABOVE // U+0628: "ب" ARABIC LETTER BEH // U+200C: ZERO WIDTH NON-JOINER // U+062C: "پ" URDU LETTER PEH /* keylabel_to_alpha */ "\u0627\u0628\u200C\u067E", - /* morekeys_i ~ */ + /* morekeys_u ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, + null, null, /* ~ morekeys_r */ // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK @@ -4062,13 +4081,13 @@ public final class KeyboardTextsTable { // U+0259: "ə" LATIN SMALL LETTER SCHWA // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE /* morekeys_e */ "\u0259,\u00E9", + /* keylabel_to_alpha */ null, // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", - /* keylabel_to_alpha */ null, // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -4153,6 +4172,7 @@ public final class KeyboardTextsTable { // U+1EC5: "ễ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE // U+1EC7: "ệ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW /* morekeys_e */ "\u00E8,\u00E9,\u1EBB,\u1EBD,\u1EB9,\u00EA,\u1EC1,\u1EBF,\u1EC3,\u1EC5,\u1EC7", + /* keylabel_to_alpha */ null, // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+1EE7: "ủ" LATIN SMALL LETTER U WITH HOOK ABOVE @@ -4165,7 +4185,6 @@ public final class KeyboardTextsTable { // U+1EEF: "ữ" LATIN SMALL LETTER U WITH HORN AND TILDE // U+1EF1: "ự" LATIN SMALL LETTER U WITH HORN AND DOT BELOW /* morekeys_u */ "\u00F9,\u00FA,\u1EE7,\u0169,\u1EE5,\u01B0,\u1EEB,\u1EE9,\u1EED,\u1EEF,\u1EF1", - /* keylabel_to_alpha */ null, // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+1EC9: "ỉ" LATIN SMALL LETTER I WITH HOOK ABOVE @@ -4215,13 +4234,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113", + /* keylabel_to_alpha */ null, // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", - /* keylabel_to_alpha */ null, // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -4273,6 +4292,7 @@ public final class KeyboardTextsTable { // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK // U+011B: "ě" LATIN SMALL LETTER E WITH CARON /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113,\u0115,\u0117,\u0119,\u011B", + /* keylabel_to_alpha */ null, // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX @@ -4284,7 +4304,6 @@ public final class KeyboardTextsTable { // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u0169,\u016B,\u016D,\u016F,\u0171,\u0173", - /* keylabel_to_alpha */ null, // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX @@ -4393,7 +4412,7 @@ public final class KeyboardTextsTable { "da" , TEXTS_da, /* 19/ 58 Danish */ "de" , TEXTS_de, /* 16/ 64 German */ "de_DE" , TEXTS_de_DE, /* 16/ 64 German (Germany) */ - "el" , TEXTS_el, /* 1/ 5 Greek */ + "el" , TEXTS_el, /* 1/ 4 Greek */ "en" , TEXTS_en, /* 8/ 10 English */ "eo" , TEXTS_eo, /* 26/128 Esperanto */ "es" , TEXTS_es, /* 8/ 65 Spanish */ @@ -4416,6 +4435,7 @@ public final class KeyboardTextsTable { "kk" , TEXTS_kk, /* 15/131 Kazakh */ "km" , TEXTS_km, /* 2/132 Khmer */ "kn" , TEXTS_kn, /* 2/ 12 Kannada */ + "ko" , TEXTS_ko, /* 2/ 12 Korean */ "ky" , TEXTS_ky, /* 10/ 98 Kyrgyz */ "lo" , TEXTS_lo, /* 2/ 12 Lao */ "lt" , TEXTS_lt, /* 18/ 33 Lithuanian */ @@ -4442,7 +4462,7 @@ public final class KeyboardTextsTable { "sw" , TEXTS_sw, /* 9/ 18 Swahili */ "ta_IN" , TEXTS_ta_IN, /* 2/ 12 Tamil (India) */ "ta_LK" , TEXTS_ta_LK, /* 2/ 12 Tamil (Sri Lanka) */ - "ta_SG" , TEXTS_ta_SG, /* 1/ 5 Tamil (Singapore) */ + "ta_SG" , TEXTS_ta_SG, /* 1/ 4 Tamil (Singapore) */ "te" , TEXTS_te, /* 2/ 12 Telugu */ "th" , TEXTS_th, /* 2/ 12 Thai */ "tl" , TEXTS_tl, /* 7/ 8 Tagalog */ diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFactory.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFactory.java index 8182c54b5..eaf4d11c5 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFactory.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFactory.java @@ -27,6 +27,7 @@ import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader; import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils; import java.io.File; +import java.text.Normalizer; import java.util.ArrayList; import java.util.LinkedList; import java.util.Locale; @@ -79,7 +80,12 @@ public final class DictionaryFactory { new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength, false /* useFullEditDistance */, locale, dictType); 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 { readOnlyBinaryDictionary.close(); // Prevent this dictionary to do any further harm. diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/KoreanDictionary.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KoreanDictionary.java new file mode 100644 index 000000000..90118b546 --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KoreanDictionary.java @@ -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 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 suggestions = mDictionary.getSuggestions(composedData, + ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); + ArrayList 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(); + } +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java index 0a62f90d3..402eaa21e 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java @@ -52,6 +52,7 @@ import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils; import org.dslul.openboard.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants; 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.HardwareKeyboardEventDecoder; import org.dslul.openboard.inputmethod.event.InputTransaction; @@ -1885,8 +1886,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } - final Event event = getHardwareKeyEventDecoder( - keyEvent.getDeviceId()).decodeHardwareKey(keyEvent); + final Event event; + 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 it's handled, we return true because we did handle it. if (event.isHandled()) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/WordComposer.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/WordComposer.java index c9989d6e9..5f9ce3785 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/WordComposer.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/WordComposer.java @@ -21,6 +21,8 @@ import androidx.annotation.NonNull; import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.event.CombinerChain; 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.common.ComposedData; import org.dslul.openboard.inputmethod.latin.common.Constants; @@ -91,6 +93,11 @@ public final class WordComposer { mCursorPositionWithinWord = 0; mRejectedBatchModeSuggestion = null; 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() { @@ -104,12 +111,14 @@ public final class WordComposer { public void restartCombining(final String combiningSpec) { final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec; if (!nonNullCombiningSpec.equals(mCombiningSpec)) { - mCombinerChain = new CombinerChain( - mCombinerChain.getComposingWordWithCombiningFeedback().toString()); + mCombinerChain = new CombinerChain(mCombinerChain.getComposingWordWithCombiningFeedback().toString()); 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. */ @@ -275,8 +284,7 @@ public final class WordComposer { final int codePoint = Character.codePointAt(word, i); // We don't want to override the batch input points that are held in mInputPointers // (See {@link #add(int,int,int)}). - final Event processedEvent = - processEvent(Event.createEventForCodePointFromUnknownSource(codePoint)); + final Event processedEvent = processEvent(Event.createEventForCodePointFromUnknownSource(codePoint)); applyProcessedEvent(processedEvent); } } @@ -293,8 +301,9 @@ public final class WordComposer { for (int i = 0; i < length; ++i) { final Event processedEvent = processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], - CoordinateUtils.xFromArray(coordinates, i), - CoordinateUtils.yFromArray(coordinates, i))); + CoordinateUtils.xFromArray(coordinates, i), + CoordinateUtils.yFromArray(coordinates, i)) + ); applyProcessedEvent(processedEvent); } mIsResumed = true; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java index 0ca9080c5..b90a7a930 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java @@ -16,8 +16,6 @@ package org.dslul.openboard.inputmethod.latin.common; -import android.renderscript.Script; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/define/ProductionFlags.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/define/ProductionFlags.kt index 23d904f9b..8350fe2e8 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/define/ProductionFlags.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/define/ProductionFlags.kt @@ -1,7 +1,9 @@ package org.dslul.openboard.inputmethod.latin.define 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 diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java index 9c09a3315..1f22aedbd 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/inputlogic/InputLogic.java @@ -34,6 +34,7 @@ import androidx.annotation.NonNull; import org.dslul.openboard.inputmethod.compat.SuggestionSpanUtils; 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.keyboard.Keyboard; 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.InputTypeUtils; 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.TextRange; @@ -445,7 +447,18 @@ public final class InputLogic { final int currentKeyboardScriptId, final LatinIME.UIHandler handler) { mWordBeingCorrectedByCursor = null; 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, processedEvent, SystemClock.uptimeMillis(), mSpaceState, getActualCapsMode(settingsValues, keyboardShiftMode)); @@ -736,6 +749,11 @@ public final class InputLogic { // line, so that does affect the contents of the editor. inputTransaction.setDidAffectContents(); 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_STOP_ONE_HANDED_MODE: // Note: One-handed mode activation is being diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java index f5ae867b6..8da471e91 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java @@ -50,6 +50,7 @@ public class ScriptUtils { public static final int SCRIPT_TELUGU = 16; public static final int SCRIPT_THAI = 17; public static final int SCRIPT_BULGARIAN = 18; + public static final int SCRIPT_HANGUL = 19; private static final TreeMap mLanguageCodeToScriptCode; private final static ArraySet UPPERCASE_SCRIPTS = new ArraySet<>(); @@ -76,6 +77,7 @@ public class ScriptUtils { mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU); mLanguageCodeToScriptCode.put("th", SCRIPT_THAI); mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC); + mLanguageCodeToScriptCode.put("ko", SCRIPT_HANGUL); // only Latin, Cyrillic, Greek and Armenian have upper/lower case // https://unicode.org/faq/casemap_charprop.html#3 @@ -193,6 +195,13 @@ public class ScriptUtils { case SCRIPT_THAI: // Thai unicode block is U+0E00..U+0E7F 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: return true; default: diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index f36e213ec..6b2944f87 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -610,6 +610,7 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7260e320..8dfd33bea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -272,6 +272,12 @@ %s (Compact) + + %s (Sebeolsik 390) + + %s (Sebeolsik Final) + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_390.xml b/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_390.xml new file mode 100644 index 000000000..4d35382a7 --- /dev/null +++ b/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_390.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_final.xml b/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_final.xml new file mode 100644 index 000000000..2f7dc9eaa --- /dev/null +++ b/app/src/main/res/xml-sw600dp/rows_korean_sebeolsik_final.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/kbd_korean_dubeolsik_standard.xml b/app/src/main/res/xml/kbd_korean_dubeolsik_standard.xml new file mode 100644 index 000000000..2f668a3e5 --- /dev/null +++ b/app/src/main/res/xml/kbd_korean_dubeolsik_standard.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/xml/kbd_korean_sebeolsik_390.xml b/app/src/main/res/xml/kbd_korean_sebeolsik_390.xml new file mode 100644 index 000000000..2bed7f2f1 --- /dev/null +++ b/app/src/main/res/xml/kbd_korean_sebeolsik_390.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/xml/kbd_korean_sebeolsik_final.xml b/app/src/main/res/xml/kbd_korean_sebeolsik_final.xml new file mode 100644 index 000000000..070fbb8b7 --- /dev/null +++ b/app/src/main/res/xml/kbd_korean_sebeolsik_final.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/xml/key_styles_currency.xml b/app/src/main/res/xml/key_styles_currency.xml index ba7ca070a..8554a95b5 100644 --- a/app/src/main/res/xml/key_styles_currency.xml +++ b/app/src/main/res/xml/key_styles_currency.xml @@ -110,7 +110,7 @@ th: Thai (Baht) uk: Ukrainian (Hryvnia) vi: Vietnamese (Dong) --> - + diff --git a/app/src/main/res/xml/keyboard_layout_set_korean.xml b/app/src/main/res/xml/keyboard_layout_set_korean.xml new file mode 100644 index 000000000..901a9805f --- /dev/null +++ b/app/src/main/res/xml/keyboard_layout_set_korean.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_390.xml b/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_390.xml new file mode 100644 index 000000000..3bac69e92 --- /dev/null +++ b/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_390.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_final.xml b/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_final.xml new file mode 100644 index 000000000..7030734c0 --- /dev/null +++ b/app/src/main/res/xml/keyboard_layout_set_korean_sebeolsik_final.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml index 8977b7195..52d70473a 100644 --- a/app/src/main/res/xml/method.xml +++ b/app/src/main/res/xml/method.xml @@ -525,6 +525,30 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=kannada,EmojiCapable" android:isAsciiCapable="false" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_2.xml b/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_2.xml new file mode 100644 index 000000000..8d50ea37f --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_2.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_3.xml b/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_3.xml new file mode 100644 index 000000000..b2ac6c90c --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_dubeolsik_standard_3.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_0.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_0.xml new file mode 100644 index 000000000..21253656e --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_0.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_1.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_1.xml new file mode 100644 index 000000000..0b9d32c3b --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_1.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_2.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_2.xml new file mode 100644 index 000000000..5cf61d85d --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_2.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_3.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_3.xml new file mode 100644 index 000000000..31cb9c1fb --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_390_3.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_0.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_0.xml new file mode 100644 index 000000000..05c6f06f9 --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_0.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_1.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_1.xml new file mode 100644 index 000000000..af6e16cb7 --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_1.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_2.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_2.xml new file mode 100644 index 000000000..67cd2c6a0 --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_2.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_3.xml b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_3.xml new file mode 100644 index 000000000..b7124b0b1 --- /dev/null +++ b/app/src/main/res/xml/rowkeys_korean_sebeolsik_final_3.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rows_korean_dubeolsik_standard.xml b/app/src/main/res/xml/rows_korean_dubeolsik_standard.xml new file mode 100644 index 000000000..20933c304 --- /dev/null +++ b/app/src/main/res/xml/rows_korean_dubeolsik_standard.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rows_korean_sebeolsik_390.xml b/app/src/main/res/xml/rows_korean_sebeolsik_390.xml new file mode 100644 index 000000000..9f4f2f280 --- /dev/null +++ b/app/src/main/res/xml/rows_korean_sebeolsik_390.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/rows_korean_sebeolsik_final.xml b/app/src/main/res/xml/rows_korean_sebeolsik_final.xml new file mode 100644 index 000000000..b7e4f5631 --- /dev/null +++ b/app/src/main/res/xml/rows_korean_sebeolsik_final.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/make-keyboard-text/src/main/resources/values-ko/donottranslate-more-keys.xml b/tools/make-keyboard-text/src/main/resources/values-ko/donottranslate-more-keys.xml new file mode 100644 index 000000000..43416e10e --- /dev/null +++ b/tools/make-keyboard-text/src/main/resources/values-ko/donottranslate-more-keys.xml @@ -0,0 +1,10 @@ + + + + + + ㄱㄴㄷ +