Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion app/src/main/assets/emoji/FLAGS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,30 @@
🇿🇼
🏴󠁧󠁢󠁥󠁮󠁧󠁿
🏴󠁧󠁢󠁳󠁣󠁴󠁿
🏴󠁧󠁢󠁷󠁬󠁳󠁿
🏴󠁧󠁢󠁷󠁬󠁳󠁿
🇦
🇧
🇨
🇩
🇪
🇫
🇬
🇭
🇮
🇯
🇰
🇱
🇲
🇳
🇴
🇵
🇶
🇷
🇸
🇹
🇺
🇻
🇼
🇽
🇾
🇿
12 changes: 10 additions & 2 deletions app/src/main/java/helium314/keyboard/latin/common/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have the isSingleRegionalIndicator check separate from isSingleGrapheme? Should the letters really be an emoji, but not a single grapheme?

|| (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)
Expand All @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions app/src/test/java/helium314/keyboard/latin/StringUtilsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion tools/make-emoji-keys/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jar {
}

tasks.register('makeEmoji', JavaExec) {
dependsOn['jar']
dependsOn jar
main = '-jar'
args jar.archiveFile.get()
args '-assets'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)