diff --git a/app/src/main/assets/emoji/FLAGS.txt b/app/src/main/assets/emoji/FLAGS.txt index c4bd834609..c95f7c77cc 100644 --- a/app/src/main/assets/emoji/FLAGS.txt +++ b/app/src/main/assets/emoji/FLAGS.txt @@ -267,4 +267,30 @@ 🇿🇼 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🏴󠁧󠁢󠁳󠁣󠁴󠁿 -🏴󠁧󠁢󠁷󠁬󠁳󠁿 \ No newline at end of file +🏴󠁧󠁢󠁷󠁬󠁳󠁿 +🇦 +🇧 +🇨 +🇩 +🇪 +🇫 +🇬 +🇭 +🇮 +🇯 +🇰 +🇱 +🇲 +🇳 +🇴 +🇵 +🇶 +🇷 +🇸 +🇹 +🇺 +🇻 +🇼 +🇽 +🇾 +🇿 \ No newline at end of file diff --git a/app/src/main/java/helium314/keyboard/latin/common/StringUtils.kt b/app/src/main/java/helium314/keyboard/latin/common/StringUtils.kt index 8268c4b961..48442d2c27 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/StringUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/StringUtils.kt @@ -246,7 +246,14 @@ fun mightBeEmoji(text: CharSequence): Boolean { fun isEmoji(c: Int): Boolean = mightBeEmoji(c) && isEmoji(newSingleCodePointString(c)) /** returns whether the text is a single emoji */ -fun isEmoji(text: CharSequence): Boolean = text.toString().isSingleGrapheme && mightBeEmoji(text) && text.matches(singleEmojiRegex) +// the standalone regional indicator check comes first because the RGI-based singleEmojiRegex only +// matches indicator pairs (country flags), so the second clause can't recognise the standalone +// indicator letters that the flags category also offers +fun isEmoji(text: CharSequence): Boolean = text.isSingleRegionalIndicator + || (text.toString().isSingleGrapheme && mightBeEmoji(text) && text.matches(singleEmojiRegex)) + +private val CharSequence.isSingleRegionalIndicator: Boolean + get() = length == 2 && Character.codePointAt(this, 0) in 0x1F1E6..0x1F1FF // from https://github.com/chattymin/Pebble/blob/main/pebble/src/main/java/com/chattymin/pebble/LocalBreakIterator.kt, Apache-2.0 license // there is more potentially useful code like String.graphemeLength (should be graphemeCount though) @@ -272,7 +279,8 @@ val String.isSingleGrapheme: Boolean get() { iterator.next() if (iterator.next() != BreakIterator.DONE) return false // we have a single grapheme, but " 🏼" is detected as single grapheme which we don't want - return if ('\uD83C' !in this) true // does not contain skin tone + return if (isSingleRegionalIndicator) true // standalone regional indicator (🇦–🇿): one code point + else if ('\uD83C' !in this) true // does not contain skin tone else singleEmojiRegex.matches(this) // single grapheme only if it's a single emoji } // got IllegalArgumentException: Invalid index on iterator.next() diff --git a/app/src/test/java/helium314/keyboard/latin/StringUtilsTest.kt b/app/src/test/java/helium314/keyboard/latin/StringUtilsTest.kt index 672c7e739d..7cb7614fb1 100644 --- a/app/src/test/java/helium314/keyboard/latin/StringUtilsTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/StringUtilsTest.kt @@ -133,6 +133,9 @@ class StringUtilsTest { assert(!"a🏼".isSingleGrapheme) assert(!"🏼🏼".isSingleGrapheme) assert("🏼".isSingleGrapheme) + assert("🇦".isSingleGrapheme) // 🇦 standalone regional indicator is a single grapheme + assert("🇩🇪".isSingleGrapheme) // 🇩🇪 flag (indicator pair) is still a single grapheme + assert(!"🇦🇧🇨".isSingleGrapheme) // 🇦🇧🇨 three indicators are not one grapheme } @Test fun detectEmojisAtEnd() { diff --git a/tools/make-emoji-keys/build.gradle b/tools/make-emoji-keys/build.gradle index f4ec920c81..c2e8c4689c 100644 --- a/tools/make-emoji-keys/build.gradle +++ b/tools/make-emoji-keys/build.gradle @@ -17,7 +17,7 @@ jar { } tasks.register('makeEmoji', JavaExec) { - dependsOn['jar'] + dependsOn jar main = '-jar' args jar.archiveFile.get() args '-assets' diff --git a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/MakeEmojiKeys.kt b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/MakeEmojiKeys.kt index ce11e9c505..0ae333b7f0 100644 --- a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/MakeEmojiKeys.kt +++ b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/MakeEmojiKeys.kt @@ -49,6 +49,7 @@ class MakeEmojiKeys { val parser = EmojiUCDTestFileParser() parser.parse(JarUtils.getLatestEmojiTestResource(jar)) val emojis = parser.getParsedData() + emojis.appendRegionalIndicators() val parser2 = AndroidEmojiSupportFileParser() parser2.parse(JarUtils.getEmojiSupportResource(jar)) diff --git a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt index d4f73733a4..5919cc38df 100644 --- a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt +++ b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt @@ -21,6 +21,17 @@ class EmojiData { fun emojiGroupCount(group: EmojiGroup) = emojiGroups[group]?.size ?: 0 + /** Unicode's emoji-test.txt only lists the composed country flags, but Android also supports + * the standalone regional indicator letters (two of them in a row combine into a flag). + * They were removed from the symbols group in #1680 with the promise to restore them if + * requested (#2627), so append them to the end of the flags group as suggested there. */ + fun appendRegionalIndicators() { + (CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A..CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z).forEach { cp -> + val letter = 'a' + (cp - CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A) + insertEmoji(EmojiGroup.FLAGS, intArrayOf(cp), 2.0f, "regional indicator symbol letter $letter") + } + } + fun insertEmoji(group: EmojiGroup, codes: IntArray, unicodeVer: Float, name: String): EmojiSpec { return EmojiSpec(codes, unicodeVer, name).also { emoji -> val baseEmoji = findBaseEmoji(group, emoji) @@ -103,5 +114,8 @@ class EmojiData { private const val CP_WHITE_HAIR = 0x1F9B3 private const val CP_BARLD = 0x1F9B2 private const val CP_VARIANT_SELECTOR = 0xFE0F + + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A = 0x1F1E6 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z = 0x1F1FF } } diff --git a/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt b/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt index 01fee14143..2538cf0f07 100644 --- a/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt +++ b/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt @@ -3912,5 +3912,31 @@ U+1F532 # black square button U+1F3C1 # chequered flag U+1F6A9 # triangular flag U+1F38C # crossed flags +U+1F1E6 # regional indicator symbol letter a +U+1F1E7 # regional indicator symbol letter b +U+1F1E8 # regional indicator symbol letter c +U+1F1E9 # regional indicator symbol letter d +U+1F1EA # regional indicator symbol letter e +U+1F1EB # regional indicator symbol letter f +U+1F1EC # regional indicator symbol letter g +U+1F1ED # regional indicator symbol letter h +U+1F1EE # regional indicator symbol letter i +U+1F1EF # regional indicator symbol letter j +U+1F1F0 # regional indicator symbol letter k +U+1F1F1 # regional indicator symbol letter l +U+1F1F2 # regional indicator symbol letter m +U+1F1F3 # regional indicator symbol letter n +U+1F1F4 # regional indicator symbol letter o +U+1F1F5 # regional indicator symbol letter p +U+1F1F6 # regional indicator symbol letter q +U+1F1F7 # regional indicator symbol letter r +U+1F1F8 # regional indicator symbol letter s +U+1F1F9 # regional indicator symbol letter t +U+1F1FA # regional indicator symbol letter u +U+1F1FB # regional indicator symbol letter v +U+1F1FC # regional indicator symbol letter w +U+1F1FD # regional indicator symbol letter x +U+1F1FE # regional indicator symbol letter y +U+1F1FF # regional indicator symbol letter z # Above emojis are supported from Android 4.4 (API level 19)