Skip to content

Commit 299aafd

Browse files
committed
Update app links and slogan for HeliboardL branding
1 parent 96aebad commit 299aafd

17 files changed

Lines changed: 29957 additions & 6 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.configuration.updateBuildConfiguration": "automatic"
3+
}

app/src/main/java/helium314/keyboard/latin/common/Constants.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ object Links {
77
const val DICTIONARY_NORMAL_SUFFIX = "dictionaries/"
88
const val DICTIONARY_EXPERIMENTAL_SUFFIX = "dictionaries_experimental/"
99
const val DICTIONARY_EMOJI_CLDR_SUFFIX = "emoji_cldr_signal_dictionaries/"
10-
const val GITHUB = "https://github.com/Helium314/HeliBoard"
10+
// HeliboardL fork repo
11+
const val GITHUB = "https://github.com/LeanBitLab/HeliboardL"
1112
const val LICENSE = "$GITHUB/blob/main/LICENSE"
12-
const val LAYOUT_WIKI_URL = "$GITHUB/wiki/2.-Layouts"
13-
const val WIKI_URL = "$GITHUB/wiki"
14-
const val CUSTOM_LAYOUTS = "$GITHUB/discussions/categories/custom-layout"
15-
const val CUSTOM_COLORS = "$GITHUB/discussions/categories/custom-colors"
13+
// Original HeliBoard wiki and community links
14+
const val ORIGINAL_GITHUB = "https://github.com/Helium314/HeliBoard"
15+
const val LAYOUT_WIKI_URL = "$ORIGINAL_GITHUB/wiki/2.-Layouts"
16+
const val WIKI_URL = "$ORIGINAL_GITHUB/wiki"
17+
const val CUSTOM_LAYOUTS = "$ORIGINAL_GITHUB/discussions/categories/custom-layout"
18+
const val CUSTOM_COLORS = "$ORIGINAL_GITHUB/discussions/categories/custom-colors"
1619
}
1720

1821
val combiningRange = 0x300..0x35b

app/src/main/res/values/donottranslate.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,6 @@
7070

7171
<string name="english_ime_name" translatable="false">HeliboardL</string>
7272
<!-- App slogan-->
73-
<string name="app_slogan" translatable="false">100% FOSS keyboard, based on AOSP.</string>
73+
<string name="app_slogan" translatable="false">Privacy-focused keyboard with Gemini AI enhancements.</string>
7474

7575
</resources>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
3+
package com.majeur.inputmethod.tools.emoji
4+
5+
6+
class AndroidEmojiSupportFileParser : TextFileParser<Map<Int, Int>>() {
7+
8+
private val map = mutableMapOf<Int, Int>()
9+
private var currentApiLevel = 0
10+
11+
override fun getParseResult() = map
12+
13+
override fun parseLine(content: String) {
14+
ifStartsWith(content,
15+
API_LEVEL_MARK to ::parseApiLevel,
16+
UNICODE_MARK to ::parseCodePoints)
17+
}
18+
19+
private fun parseApiLevel(content: String) {
20+
currentApiLevel = content
21+
.substringBefore("#")
22+
.trim()
23+
.toInt()
24+
}
25+
26+
private fun parseCodePoints(content: String) {
27+
val codePointsHash = content
28+
.substringBefore("#")
29+
.trim()
30+
.split(" ")
31+
.map { it
32+
.trim()
33+
.removePrefix("U+")
34+
.toInt(radix = 16) }
35+
.joinToString(separator = "")
36+
.hashCode()
37+
map[codePointsHash] = currentApiLevel
38+
}
39+
40+
companion object {
41+
42+
private const val API_LEVEL_MARK = "@"
43+
private const val UNICODE_MARK = "U"
44+
}
45+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
3+
package com.majeur.inputmethod.tools.emoji
4+
5+
import com.majeur.inputmethod.tools.emoji.model.EmojiData
6+
import com.majeur.inputmethod.tools.emoji.model.EmojiGroup
7+
8+
class EmojiUCDTestFileParser: TextFileParser<EmojiData>() {
9+
10+
private var count = 0
11+
private var emojiData = EmojiData()
12+
13+
private var currentGroup = EmojiGroup.SMILEYS_AND_EMOTION
14+
15+
override fun getParseResult() = emojiData
16+
17+
override fun parseLine(content: String) {
18+
ifStartsWith(content,
19+
"#" to ::parseComment,
20+
"" to ::parseEmojiSpec
21+
)
22+
}
23+
24+
private fun parseComment(content: String) {
25+
ifStartsWith(content,
26+
PROP_DATE to { emojiData.dataDate = it},
27+
PROP_UNICODE_VER to {
28+
emojiData.unicodeVersion = it
29+
println("Parsing emoji table from Unicode $it")
30+
},
31+
PROP_GROUP to ::parseGroup,
32+
PROP_SUBGROUP to { },
33+
"${currentGroup.rawName} subtotal:" to ::parseGroupSubtotal,
34+
EOF to { println("Parsed a total of $count emojis") }
35+
)
36+
}
37+
38+
private fun parseGroup(content: String) {
39+
currentGroup = EmojiGroup.get(content)
40+
}
41+
42+
private fun parseGroupSubtotal(content: String) {
43+
if (content.contains("w/o modifiers")) return
44+
val expected = content.toInt()
45+
val count = emojiData.emojiGroupCount(currentGroup)
46+
println(" - $count/$expected emojis for group ${currentGroup.rawName}")
47+
}
48+
49+
private fun parseEmojiSpec(content: String) {
50+
if (content.isEmpty()) return
51+
52+
val codePoints = content
53+
.substringBefore(';')
54+
.trim()
55+
val status = content
56+
.substringAfter(';')
57+
.substringBefore('#')
58+
.trim()
59+
val extras = content.substringAfter('#')
60+
61+
if (status != "fully-qualified") return
62+
63+
val rawVersion = EMOJI_VERSION_REGEX.find(extras)?.value ?: "O.0"
64+
val version = rawVersion.toFloat()
65+
val name = extras
66+
.substringAfter(rawVersion)
67+
.trim()
68+
69+
val cps = codePoints
70+
.split(" ")
71+
.map { it.toInt(radix = 16) }
72+
.toIntArray()
73+
74+
emojiData.insertEmoji(currentGroup, cps, version, name)
75+
count++
76+
}
77+
78+
companion object {
79+
80+
private const val PROP_UNICODE_VER = "Version:"
81+
private const val PROP_DATE = "Date:"
82+
private const val PROP_GROUP = "group:"
83+
private const val PROP_SUBGROUP = "subgroup:"
84+
private const val EOF = "EOF"
85+
86+
private val EMOJI_VERSION_REGEX = "[0-9]*[.]?[0-9]+".toRegex()
87+
}
88+
89+
90+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
* modified
4+
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
5+
*/
6+
7+
package com.majeur.inputmethod.tools.emoji
8+
9+
import java.io.Closeable
10+
import java.io.IOException
11+
import java.io.InputStream
12+
import java.io.UnsupportedEncodingException
13+
import java.net.URLDecoder
14+
import java.util.jar.JarFile
15+
import kotlin.RuntimeException
16+
17+
object JarUtils {
18+
19+
fun getJarFile(mainClass: Class<*>): JarFile {
20+
val mainClassPath = "/${mainClass.name.replace('.', '/')}.class"
21+
val resUrl = mainClass.getResource(mainClassPath)
22+
if (resUrl?.protocol != "jar") {
23+
throw RuntimeException("Should run as jar and not as " + resUrl?.protocol)
24+
}
25+
val path = resUrl.path
26+
if (!path.startsWith("file:")) {
27+
throw RuntimeException("Unknown jar path: $path")
28+
}
29+
val jarPath = path.substring("file:".length, path.indexOf('!'))
30+
try {
31+
return JarFile(URLDecoder.decode(jarPath, "UTF-8"))
32+
} catch (e: UnsupportedEncodingException) {
33+
throw RuntimeException(e)
34+
} catch (e: IOException) {
35+
throw RuntimeException(e)
36+
}
37+
}
38+
39+
fun openResource(name: String): InputStream {
40+
return javaClass.getResourceAsStream("/$name")
41+
}
42+
43+
fun getLatestEmojiTestResource(jar: JarFile) : String {
44+
var latestUnicodeVersion = 0.0
45+
var name = ""
46+
jar.entries().iterator().forEach {
47+
if (it.name.endsWith("emoji-test.txt")) {
48+
val ver = it.name
49+
.removeSuffix("/emoji-test.txt")
50+
.substringAfterLast("/")
51+
.toDouble()
52+
if (ver > latestUnicodeVersion) {
53+
latestUnicodeVersion = ver
54+
name = it.name
55+
}
56+
}
57+
}
58+
if (name.isEmpty())
59+
throw RuntimeException("No emoji specs provided in resources")
60+
return name
61+
}
62+
63+
fun getAndroidResTemplateResource(jar: JarFile) : String {
64+
jar.entries().iterator().forEach {
65+
if (it.name.endsWith("emoji-categories.tmpl")) {
66+
return it.name
67+
}
68+
}
69+
throw RuntimeException("No template provided in resources")
70+
}
71+
72+
fun close(stream: Closeable?) {
73+
try {
74+
stream?.close()
75+
} catch (_: IOException) {
76+
}
77+
}
78+
79+
fun getEmojiSupportResource(jar: JarFile): String {
80+
jar.entries().iterator().forEach {
81+
if (it.name.endsWith("android-emoji-support.txt")) {
82+
return it.name
83+
}
84+
}
85+
throw RuntimeException("No emoji support file provided in resources")
86+
}
87+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
3+
package com.majeur.inputmethod.tools.emoji
4+
5+
import com.majeur.inputmethod.tools.emoji.model.EmojiData
6+
import com.majeur.inputmethod.tools.emoji.model.EmojiGroup
7+
import com.majeur.inputmethod.tools.emoji.model.EmojiSpec
8+
import java.io.File
9+
import java.nio.charset.Charset
10+
import java.util.*
11+
import kotlin.system.exitProcess
12+
13+
class MakeEmojiKeys {
14+
15+
class Options(argsArray: Array<String>) {
16+
17+
var assetPath: String? = null
18+
19+
init {
20+
val args = listOf(*argsArray).toMutableList()
21+
var arg: String? = null
22+
try {
23+
while (args.isNotEmpty()) {
24+
arg = args.removeFirst()
25+
if (arg == OPTION_ASSETS) {
26+
assetPath = args.removeFirst()
27+
} else {
28+
usage("Unknown option: $arg")
29+
}
30+
}
31+
} catch (_: NoSuchElementException) {
32+
usage("Option $arg needs argument")
33+
}
34+
}
35+
36+
fun usage(message: String?) {
37+
message?.let { System.err.println(it) }
38+
System.err.println("usage: make-emoji-keys $OPTION_ASSETS <res_output_dir>")
39+
exitProcess(1)
40+
}
41+
}
42+
43+
companion object {
44+
45+
@JvmStatic fun main(args: Array<String>) {
46+
val options = Options(args)
47+
val jar = JarUtils.getJarFile(Companion::class.java)
48+
49+
val parser = EmojiUCDTestFileParser()
50+
parser.parse(JarUtils.getLatestEmojiTestResource(jar))
51+
val emojis = parser.getParsedData()
52+
53+
val parser2 = AndroidEmojiSupportFileParser()
54+
parser2.parse(JarUtils.getEmojiSupportResource(jar))
55+
val supportData = parser2.getParsedData()
56+
57+
if (options.assetPath != null) {
58+
writeMinApiLevels(options.assetPath!!, emojis, supportData)
59+
writeEmojis(options.assetPath!!, emojis)
60+
}
61+
}
62+
63+
private fun writeMinApiLevels(outDir: String, emojiData: EmojiData, supportData: Map<Int, Int>) {
64+
val minApiLevels = mutableMapOf<Int, MutableSet<String>>()
65+
fun addMinLevel(emoji: EmojiSpec) {
66+
val minApi = getMinApi(emoji.codes, supportData)
67+
if (minApi < 0)
68+
throw Exception("unknown min SDK for ${emoji.name}")
69+
if (minApi > 21)
70+
minApiLevels.getOrPut(minApi) { mutableSetOf() }.add(emoji.text)
71+
}
72+
73+
EmojiGroup.entries.filterNot { it == EmojiGroup.COMPONENT }.forEach { group ->
74+
emojiData[group].forEach { emoji ->
75+
addMinLevel(emoji)
76+
emoji.variants.forEach { addMinLevel(it) }
77+
}
78+
}
79+
if (minApiLevels.any { it.value.any { it.contains(" ") } })
80+
throw Exception("emoji contains space")
81+
val text = minApiLevels.map { "${it.key} ${it.value.joinToString(" ")}" }
82+
.sorted().joinToString("\n")
83+
File(outDir, "minApi.txt").writeText(text, Charset.forName("UTF-8"))
84+
}
85+
86+
private fun writeEmojis(outDir: String, emojiData: EmojiData) {
87+
// each category gets a file, one main emoji per line, followed by popups
88+
EmojiGroup.entries.filterNot { it == EmojiGroup.COMPONENT }
89+
.forEach { writeEmojiGroup(File(outDir, it.name + ".txt"), emojiData[it]) }
90+
}
91+
92+
private fun writeEmojiGroup(outFile: File, emojis: List<EmojiSpec>) {
93+
val text = emojis.joinToString("\n") { emoji ->
94+
if (emoji.variants.isEmpty()) emoji.text
95+
else "${emoji.text} ${emoji.variants.joinToString(" ") { it.text }}"
96+
}
97+
outFile.writeText(text, Charset.forName("UTF-8"))
98+
}
99+
100+
private fun getMinApi(codes: IntArray, supportData: Map<Int, Int>): Int {
101+
val hash = codes.joinToString("").hashCode()
102+
return supportData[hash] ?: -1
103+
}
104+
}
105+
}
106+
107+
private const val OPTION_ASSETS = "-assets"

0 commit comments

Comments
 (0)