6.6.1 - Alpha2 - 支持 Node.js 拼音模块及结巴分词

This commit is contained in:
SuperMonster003
2024-12-24 20:24:32 +08:00
parent 7c62902429
commit 9a4b581f07
53 changed files with 38238 additions and 212 deletions

View File

@@ -1,5 +1,6 @@
package org.autojs.autojs.engine
import android.annotation.SuppressLint
import android.util.Log
import android.view.View
import org.autojs.autojs.core.ui.ViewExtras
@@ -179,7 +180,16 @@ open class RhinoJavaScriptEngine(private val scriptRuntime: ScriptRuntime, priva
initStandardObjects(context, false)
}
fun enterContext(): Context = RhinoAndroidHelper(androidContext).enterContext().also { setupContext(it) }
@SuppressLint("VisibleForTests")
fun enterContext(): Context {
val rhinoAndroidHelper = RhinoAndroidHelper(androidContext)
val enterContext = try {
rhinoAndroidHelper.enterContext()
} catch (e: SecurityException) {
rhinoAndroidHelper.contextFactory.enterContext()
}
return enterContext.also { setupContext(it) }
}
fun setupContext(context: Context) = context.apply {
if (this is AutoJsContext) {

View File

@@ -72,6 +72,7 @@ import org.autojs.autojs.runtime.api.augment.ocr.OcrPaddle
import org.autojs.autojs.runtime.api.augment.ocr.OcrRapid
import org.autojs.autojs.runtime.api.augment.opencc.OpenCC
import org.autojs.autojs.runtime.api.augment.pinyin.Pinyin
import org.autojs.autojs.runtime.api.augment.pinyin4j.Pinyin4j
import org.autojs.autojs.runtime.api.augment.plugins.Plugins
import org.autojs.autojs.runtime.api.augment.recorder.Recorder
import org.autojs.autojs.runtime.api.augment.s13n.S13n
@@ -763,6 +764,7 @@ class ScriptRuntime private constructor(builder: Builder) {
SQLite(this).augment(target, true)
NanoID.augment(target, true)
Pinyin.augment(target, true)
Pinyin4j.augment(target, true)
augmentedApp.defineProp(Autojs::class.java.simpleName.lowercase(), augmentedAutojs)
}

View File

@@ -24,7 +24,7 @@ class RootAutomatorNativeObject(scriptRuntime: ScriptRuntime, waitForReady: Any?
init {
super.exportAsJSClass(MAX_PROTOTYPE_ID, this, false)
defineProperty("__ra__", mRootAutomatorObject, DONTENUM or READONLY or PERMANENT)
defineProperty("__ra__", mRootAutomatorObject, READONLY or DONTENUM or PERMANENT)
}
override fun has(name: String?): Boolean {

View File

@@ -0,0 +1,517 @@
package org.autojs.autojs.runtime.api.augment.pinyin
object Dict {
/* 带声调字符. */
val PHONETIC_SYMBOL = mapOf(
"ā" to "a1",
"á" to "a2",
"ǎ" to "a3",
"à" to "a4",
"ē" to "e1",
"é" to "e2",
"ě" to "e3",
"è" to "e4",
"ō" to "o1",
"ó" to "o2",
"ǒ" to "o3",
"ò" to "o4",
"ī" to "i1",
"í" to "i2",
"ǐ" to "i3",
"ì" to "i4",
"ū" to "u1",
"ú" to "u2",
"ǔ" to "u3",
"ù" to "u4",
"ü" to "v0",
"ǘ" to "v2",
"ǚ" to "v3",
"ǜ" to "v4",
"ń" to "n2",
"ň" to "n3",
"" to "m2",
)
/* 百家姓. */
/* https://zh.wikipedia.org/wiki/%E7%99%BE%E5%AE%B6%E5%A7%93 */
val SURNAME = mapOf(
"赵" to listOf(listOf("zhào")),
"钱" to listOf(listOf("qián")),
"孙" to listOf(listOf("sūn")),
"李" to listOf(listOf("lǐ")),
"周" to listOf(listOf("zhōu")),
"吴" to listOf(listOf("wú")),
"郑" to listOf(listOf("zhèng")),
"王" to listOf(listOf("wáng")),
"冯" to listOf(listOf("féng")),
"陈" to listOf(listOf("chén")),
"褚" to listOf(listOf("chǔ")),
"卫" to listOf(listOf("wèi")),
"蒋" to listOf(listOf("jiǎng")),
"沈" to listOf(listOf("shěn")),
"韩" to listOf(listOf("hán")),
"杨" to listOf(listOf("yáng")),
"朱" to listOf(listOf("zhū")),
"秦" to listOf(listOf("qín")),
"尤" to listOf(listOf("yóu")),
"许" to listOf(listOf("xǔ")),
"何" to listOf(listOf("hé")),
"吕" to listOf(listOf("lǚ")),
"施" to listOf(listOf("shī")),
"张" to listOf(listOf("zhāng")),
"孔" to listOf(listOf("kǒng")),
"曹" to listOf(listOf("cáo")),
"严" to listOf(listOf("yán")),
"华" to listOf(listOf("huà")),
"金" to listOf(listOf("jīn")),
"魏" to listOf(listOf("wèi")),
"陶" to listOf(listOf("táo")),
"姜" to listOf(listOf("jiāng")),
"戚" to listOf(listOf("qī")),
"谢" to listOf(listOf("xiè")),
"邹" to listOf(listOf("zōu")),
"喻" to listOf(listOf("yù")),
"柏" to listOf(listOf("bǎi")),
"水" to listOf(listOf("shuǐ")),
"窦" to listOf(listOf("dòu")),
"章" to listOf(listOf("zhāng")),
"云" to listOf(listOf("yún")),
"苏" to listOf(listOf("sū")),
"潘" to listOf(listOf("pān")),
"葛" to listOf(listOf("gě")),
"奚" to listOf(listOf("xī")),
"范" to listOf(listOf("fàn")),
"彭" to listOf(listOf("péng")),
"郎" to listOf(listOf("láng")),
"鲁" to listOf(listOf("lǔ")),
"韦" to listOf(listOf("wéi")),
"昌" to listOf(listOf("chāng")),
"马" to listOf(listOf("mǎ")),
"苗" to listOf(listOf("miáo")),
"凤" to listOf(listOf("fèng")),
"花" to listOf(listOf("huā")),
"方" to listOf(listOf("fāng")),
"俞" to listOf(listOf("yú")),
"任" to listOf(listOf("rèn")),
"袁" to listOf(listOf("yuán")),
"柳" to listOf(listOf("liǔ")),
"酆" to listOf(listOf("fēng")),
"鲍" to listOf(listOf("bào")),
"史" to listOf(listOf("shǐ")),
"唐" to listOf(listOf("táng")),
"费" to listOf(listOf("fèi")),
"廉" to listOf(listOf("lián")),
"岑" to listOf(listOf("cén")),
"薛" to listOf(listOf("xuē")),
"雷" to listOf(listOf("léi")),
"贺" to listOf(listOf("hè")),
"倪" to listOf(listOf("ní")),
"汤" to listOf(listOf("tāng")),
"滕" to listOf(listOf("téng")),
"殷" to listOf(listOf("yīn")),
"罗" to listOf(listOf("luó")),
"毕" to listOf(listOf("bì")),
"郝" to listOf(listOf("hǎo")),
"邬" to listOf(listOf("wū")),
"安" to listOf(listOf("ān")),
"常" to listOf(listOf("cháng")),
"乐" to listOf(listOf("yuè")),
"于" to listOf(listOf("yú")),
"时" to listOf(listOf("shí")),
"傅" to listOf(listOf("fù")),
"皮" to listOf(listOf("pí")),
"卞" to listOf(listOf("biàn")),
"齐" to listOf(listOf("qí")),
"康" to listOf(listOf("kāng")),
"伍" to listOf(listOf("wǔ")),
"余" to listOf(listOf("yú")),
"元" to listOf(listOf("yuán")),
"卜" to listOf(listOf("bǔ")),
"顾" to listOf(listOf("gù")),
"孟" to listOf(listOf("mèng")),
"平" to listOf(listOf("píng")),
"黄" to listOf(listOf("huáng")),
"和" to listOf(listOf("hé")),
"穆" to listOf(listOf("mù")),
"萧" to listOf(listOf("xiāo")),
"尹" to listOf(listOf("yǐn")),
"姚" to listOf(listOf("yáo")),
"邵" to listOf(listOf("shào")),
"湛" to listOf(listOf("zhàn")),
"汪" to listOf(listOf("wāng")),
"祁" to listOf(listOf("qí")),
"毛" to listOf(listOf("máo")),
"禹" to listOf(listOf("yǔ")),
"狄" to listOf(listOf("dí")),
"米" to listOf(listOf("mǐ")),
"贝" to listOf(listOf("bèi")),
"明" to listOf(listOf("míng")),
"臧" to listOf(listOf("zāng")),
"计" to listOf(listOf("jì")),
"伏" to listOf(listOf("fú")),
"成" to listOf(listOf("chéng")),
"戴" to listOf(listOf("dài")),
"谈" to listOf(listOf("tán")),
"宋" to listOf(listOf("sòng")),
"茅" to listOf(listOf("máo")),
"庞" to listOf(listOf("páng")),
"熊" to listOf(listOf("xióng")),
"纪" to listOf(listOf("jì")),
"舒" to listOf(listOf("shū")),
"屈" to listOf(listOf("qū")),
"项" to listOf(listOf("xiàng")),
"祝" to listOf(listOf("zhù")),
"董" to listOf(listOf("dǒng")),
"梁" to listOf(listOf("liáng")),
"杜" to listOf(listOf("dù")),
"阮" to listOf(listOf("ruǎn")),
"蓝" to listOf(listOf("lán")),
"闵" to listOf(listOf("mǐn")),
"席" to listOf(listOf("xí")),
"季" to listOf(listOf("jì")),
"麻" to listOf(listOf("má")),
"强" to listOf(listOf("qiáng")),
"贾" to listOf(listOf("jiǎ")),
"路" to listOf(listOf("lù")),
"娄" to listOf(listOf("lóu")),
"危" to listOf(listOf("wēi")),
"江" to listOf(listOf("jiāng")),
"童" to listOf(listOf("tóng")),
"颜" to listOf(listOf("yán")),
"郭" to listOf(listOf("guō")),
"梅" to listOf(listOf("méi")),
"盛" to listOf(listOf("shèng")),
"林" to listOf(listOf("lín")),
"刁" to listOf(listOf("diāo")),
"钟" to listOf(listOf("zhōng")),
"徐" to listOf(listOf("xú")),
"邱" to listOf(listOf("qiū")),
"骆" to listOf(listOf("luò")),
"高" to listOf(listOf("gāo")),
"夏" to listOf(listOf("xià")),
"蔡" to listOf(listOf("cài")),
"田" to listOf(listOf("tián")),
"樊" to listOf(listOf("fán")),
"胡" to listOf(listOf("hú")),
"凌" to listOf(listOf("líng")),
"霍" to listOf(listOf("huò")),
"虞" to listOf(listOf("yú")),
"万" to listOf(listOf("wàn")),
"支" to listOf(listOf("zhī")),
"柯" to listOf(listOf("kē")),
"昝" to listOf(listOf("zǎn")),
"管" to listOf(listOf("guǎn")),
"卢" to listOf(listOf("lú")),
"莫" to listOf(listOf("mò")),
"经" to listOf(listOf("jīng")),
"房" to listOf(listOf("fáng")),
"裘" to listOf(listOf("qiú")),
"缪" to listOf(listOf("miào")),
"干" to listOf(listOf("gān")),
"解" to listOf(listOf("xiè")),
"应" to listOf(listOf("yīng")),
"宗" to listOf(listOf("zōng")),
"丁" to listOf(listOf("dīng")),
"宣" to listOf(listOf("xuān")),
"贲" to listOf(listOf("bēn")),
"邓" to listOf(listOf("dèng")),
"郁" to listOf(listOf("yù")),
"单" to listOf(listOf("shàn")),
"杭" to listOf(listOf("háng")),
"洪" to listOf(listOf("hóng")),
"包" to listOf(listOf("bāo")),
"诸" to listOf(listOf("zhū")),
"左" to listOf(listOf("zuǒ")),
"石" to listOf(listOf("shí")),
"崔" to listOf(listOf("cuī")),
"吉" to listOf(listOf("jí")),
"钮" to listOf(listOf("niǔ")),
"龚" to listOf(listOf("gōng")),
"程" to listOf(listOf("chéng")),
"嵇" to listOf(listOf("jī")),
"邢" to listOf(listOf("xíng")),
"滑" to listOf(listOf("huá")),
"裴" to listOf(listOf("péi")),
"陆" to listOf(listOf("lù")),
"荣" to listOf(listOf("róng")),
"翁" to listOf(listOf("wēng")),
"荀" to listOf(listOf("xún")),
"羊" to listOf(listOf("yáng")),
"於" to listOf(listOf("yū")),
"惠" to listOf(listOf("huì")),
"甄" to listOf(listOf("zhēn")),
"曲" to listOf(listOf("qū")),
"家" to listOf(listOf("jiā")),
"封" to listOf(listOf("fēng")),
"芮" to listOf(listOf("ruì")),
"羿" to listOf(listOf("yì")),
"储" to listOf(listOf("chǔ")),
"靳" to listOf(listOf("jìn")),
"汲" to listOf(listOf("jí")),
"邴" to listOf(listOf("bǐng")),
"糜" to listOf(listOf("mí")),
"松" to listOf(listOf("sōng")),
"井" to listOf(listOf("jǐng")),
"段" to listOf(listOf("duàn")),
"富" to listOf(listOf("fù")),
"巫" to listOf(listOf("wū")),
"乌" to listOf(listOf("wū")),
"焦" to listOf(listOf("jiāo")),
"巴" to listOf(listOf("bā")),
"弓" to listOf(listOf("gōng")),
"牧" to listOf(listOf("mù")),
"隗" to listOf(listOf("kuí")),
"山" to listOf(listOf("shān")),
"谷" to listOf(listOf("gǔ")),
"车" to listOf(listOf("chē")),
"侯" to listOf(listOf("hóu")),
"宓" to listOf(listOf("mì")),
"蓬" to listOf(listOf("péng")),
"全" to listOf(listOf("quán")),
"郗" to listOf(listOf("xī")),
"班" to listOf(listOf("bān")),
"仰" to listOf(listOf("yǎng")),
"秋" to listOf(listOf("qiū")),
"仲" to listOf(listOf("zhòng")),
"伊" to listOf(listOf("yī")),
"宫" to listOf(listOf("gōng")),
"宁" to listOf(listOf("nìng")),
"仇" to listOf(listOf("qiú")),
"栾" to listOf(listOf("luán")),
"暴" to listOf(listOf("bào")),
"甘" to listOf(listOf("gān")),
"钭" to listOf(listOf("tǒu")),
"厉" to listOf(listOf("lì")),
"戎" to listOf(listOf("róng")),
"祖" to listOf(listOf("zǔ")),
"武" to listOf(listOf("wǔ")),
"符" to listOf(listOf("fú")),
"刘" to listOf(listOf("liú")),
"景" to listOf(listOf("jǐng")),
"詹" to listOf(listOf("zhān")),
"束" to listOf(listOf("shù")),
"龙" to listOf(listOf("lóng")),
"叶" to listOf(listOf("yè")),
"幸" to listOf(listOf("xìng")),
"司" to listOf(listOf("sī")),
"韶" to listOf(listOf("sháo")),
"郜" to listOf(listOf("gào")),
"黎" to listOf(listOf("lí")),
"蓟" to listOf(listOf("jì")),
"薄" to listOf(listOf("bó")),
"印" to listOf(listOf("yìn")),
"宿" to listOf(listOf("sù")),
"白" to listOf(listOf("bái")),
"怀" to listOf(listOf("huái")),
"蒲" to listOf(listOf("pú")),
"邰" to listOf(listOf("tái")),
"从" to listOf(listOf("cóng")),
"鄂" to listOf(listOf("è")),
"索" to listOf(listOf("suǒ")),
"咸" to listOf(listOf("xián")),
"籍" to listOf(listOf("jí")),
"赖" to listOf(listOf("lài")),
"卓" to listOf(listOf("zhuó")),
"蔺" to listOf(listOf("lìn")),
"屠" to listOf(listOf("tú")),
"蒙" to listOf(listOf("méng")),
"池" to listOf(listOf("chí")),
"乔" to listOf(listOf("qiáo")),
"阴" to listOf(listOf("yīn")),
"鬱" to listOf(listOf("yù")),
"胥" to listOf(listOf("xū")),
"能" to listOf(listOf("nài")),
"苍" to listOf(listOf("cāng")),
"双" to listOf(listOf("shuāng")),
"闻" to listOf(listOf("wén")),
"莘" to listOf(listOf("shēn")),
"党" to listOf(listOf("dǎng")),
"翟" to listOf(listOf("zhái")),
"谭" to listOf(listOf("tán")),
"贡" to listOf(listOf("gòng")),
"劳" to listOf(listOf("láo")),
"逄" to listOf(listOf("páng")),
"姬" to listOf(listOf("jī")),
"申" to listOf(listOf("shēn")),
"扶" to listOf(listOf("fú")),
"堵" to listOf(listOf("dǔ")),
"冉" to listOf(listOf("rǎn")),
"宰" to listOf(listOf("zǎi")),
"郦" to listOf(listOf("lì")),
"雍" to listOf(listOf("yōng")),
"郤" to listOf(listOf("xì")),
"璩" to listOf(listOf("qú")),
"桑" to listOf(listOf("sāng")),
"桂" to listOf(listOf("guì")),
"濮" to listOf(listOf("pú")),
"牛" to listOf(listOf("niú")),
"寿" to listOf(listOf("shòu")),
"通" to listOf(listOf("tōng")),
"边" to listOf(listOf("biān")),
"扈" to listOf(listOf("hù")),
"燕" to listOf(listOf("yān")),
"冀" to listOf(listOf("jì")),
"郏" to listOf(listOf("jiá")),
"浦" to listOf(listOf("pǔ")),
"尚" to listOf(listOf("shàng")),
"农" to listOf(listOf("nóng")),
"温" to listOf(listOf("wēn")),
"别" to listOf(listOf("bié")),
"庄" to listOf(listOf("zhuāng")),
"晏" to listOf(listOf("yàn")),
"柴" to listOf(listOf("chái")),
"瞿" to listOf(listOf("qú")),
"阎" to listOf(listOf("yán")),
"充" to listOf(listOf("chōng")),
"慕" to listOf(listOf("mù")),
"连" to listOf(listOf("lián")),
"茹" to listOf(listOf("rú")),
"习" to listOf(listOf("xí")),
"宦" to listOf(listOf("huàn")),
"艾" to listOf(listOf("ài")),
"鱼" to listOf(listOf("yú")),
"容" to listOf(listOf("róng")),
"向" to listOf(listOf("xiàng")),
"古" to listOf(listOf("gǔ")),
"易" to listOf(listOf("yì")),
"慎" to listOf(listOf("shèn")),
"戈" to listOf(listOf("gē")),
"廖" to listOf(listOf("liào")),
"庾" to listOf(listOf("yǔ")),
"终" to listOf(listOf("zhōng")),
"暨" to listOf(listOf("jì")),
"居" to listOf(listOf("jū")),
"衡" to listOf(listOf("héng")),
"步" to listOf(listOf("bù")),
"都" to listOf(listOf("dū")),
"耿" to listOf(listOf("gěng")),
"满" to listOf(listOf("mǎn")),
"弘" to listOf(listOf("hóng")),
"匡" to listOf(listOf("kuāng")),
"国" to listOf(listOf("guó")),
"文" to listOf(listOf("wén")),
"寇" to listOf(listOf("kòu")),
"广" to listOf(listOf("guǎng")),
"禄" to listOf(listOf("lù")),
"阙" to listOf(listOf("quē")),
"东" to listOf(listOf("dōng")),
"欧" to listOf(listOf("ōu")),
"殳" to listOf(listOf("shū")),
"沃" to listOf(listOf("wò")),
"利" to listOf(listOf("lì")),
"蔚" to listOf(listOf("wèi")),
"越" to listOf(listOf("yuè")),
"夔" to listOf(listOf("kuí")),
"隆" to listOf(listOf("lóng")),
"师" to listOf(listOf("shī")),
"巩" to listOf(listOf("gǒng")),
"厍" to listOf(listOf("shè")),
"聂" to listOf(listOf("niè")),
"晁" to listOf(listOf("cháo")),
"勾" to listOf(listOf("gōu")),
"敖" to listOf(listOf("áo")),
"融" to listOf(listOf("róng")),
"冷" to listOf(listOf("lěng")),
"訾" to listOf(listOf("zǐ")),
"辛" to listOf(listOf("xīn")),
"阚" to listOf(listOf("kàn")),
"那" to listOf(listOf("nā")),
"简" to listOf(listOf("jiǎn")),
"饶" to listOf(listOf("ráo")),
"空" to listOf(listOf("kōng")),
"曾" to listOf(listOf("zēng")),
"母" to listOf(listOf("mǔ")),
"沙" to listOf(listOf("shā")),
"乜" to listOf(listOf("niè")),
"养" to listOf(listOf("yǎng")),
"鞠" to listOf(listOf("jū")),
"须" to listOf(listOf("xū")),
"丰" to listOf(listOf("fēng")),
"巢" to listOf(listOf("cháo")),
"关" to listOf(listOf("guān")),
"蒯" to listOf(listOf("kuǎi")),
"相" to listOf(listOf("xiàng")),
"查" to listOf(listOf("zhā")),
"后" to listOf(listOf("hòu")),
"荆" to listOf(listOf("jīng")),
"红" to listOf(listOf("hóng")),
"游" to listOf(listOf("yóu")),
"竺" to listOf(listOf("zhú")),
"权" to listOf(listOf("quán")),
"逯" to listOf(listOf("lù")),
"盖" to listOf(listOf("gài")),
"益" to listOf(listOf("yì")),
"桓" to listOf(listOf("huán")),
"公" to listOf(listOf("gōng")),
"牟" to listOf(listOf("móu")),
"哈" to listOf(listOf("hǎ")),
"言" to listOf(listOf("yán")),
"福" to listOf(listOf("fú")),
)
/* 复姓. */
val COMPOUND_SURNAME = mapOf(
"万俟" to listOf(listOf("mò"), listOf("qí")),
"上官" to listOf(listOf("shàng"), listOf("guān")),
"东方" to listOf(listOf("dōng"), listOf("fāng")),
"东郭" to listOf(listOf("dōng"), listOf("guō")),
"东门" to listOf(listOf("dōng"), listOf("mén")),
"乐正" to listOf(listOf("yuè"), listOf("zhèng")),
"亓官" to listOf(listOf("qí"), listOf("guān")),
"仉督" to listOf(listOf("zhǎng"), listOf("dū")),
"令狐" to listOf(listOf("líng"), listOf("hú")),
"仲孙" to listOf(listOf("zhòng"), listOf("sūn")),
"公冶" to listOf(listOf("gōng"), listOf("yě")),
"公孙" to listOf(listOf("gōng"), listOf("sūn")),
"公羊" to listOf(listOf("gōng"), listOf("yáng")),
"公良" to listOf(listOf("gōng"), listOf("liáng")),
"公西" to listOf(listOf("gōng"), listOf("xī")),
"单于" to listOf(listOf("chán"), listOf("yú")),
"南宫" to listOf(listOf("nán"), listOf("gōng")),
"南门" to listOf(listOf("nán"), listOf("mén")),
"司寇" to listOf(listOf("sī"), listOf("kòu")),
"司徒" to listOf(listOf("sī"), listOf("tú")),
"司空" to listOf(listOf("sī"), listOf("kōng")),
"司马" to listOf(listOf("sī"), listOf("mǎ")),
"呼延" to listOf(listOf("hū"), listOf("yán")),
"壤驷" to listOf(listOf("rǎng"), listOf("sì")),
"夏侯" to listOf(listOf("xià"), listOf("hóu")),
"太叔" to listOf(listOf("tài"), listOf("shū")),
"夹谷" to listOf(listOf("jiá"), listOf("gǔ")),
"子车" to listOf(listOf("zǐ"), listOf("jū")),
"宇文" to listOf(listOf("yǔ"), listOf("wén")),
"宗政" to listOf(listOf("zōng"), listOf("zhèng")),
"宰父" to listOf(listOf("zǎi"), listOf("fǔ")),
"尉迟" to listOf(listOf("yù"), listOf("chí")),
"左丘" to listOf(listOf("zuǒ"), listOf("qiū")),
"巫马" to listOf(listOf("wū"), listOf("mǎ")),
"慕容" to listOf(listOf("mù"), listOf("róng")),
"拓跋" to listOf(listOf("tuò"), listOf("bá")),
"梁丘" to listOf(listOf("liáng"), listOf("qiū")),
"榖梁" to listOf(listOf("gǔ"), listOf("liáng")),
"欧阳" to listOf(listOf("ōu"), listOf("yáng")),
"段干" to listOf(listOf("duàn"), listOf("gān")),
"淳于" to listOf(listOf("chún"), listOf("yú")),
"漆雕" to listOf(listOf("qī"), listOf("diāo")),
"澹台" to listOf(listOf("tán"), listOf("tái")),
"濮阳" to listOf(listOf("pú"), listOf("yáng")),
"申屠" to listOf(listOf("shēn"), listOf("tú")),
"百里" to listOf(listOf("bǎi"), listOf("lǐ")),
"皇甫" to listOf(listOf("huáng"), listOf("pǔ")),
"端木" to listOf(listOf("duān"), listOf("mù")),
"第五" to listOf(listOf("dì"), listOf("wǔ")),
"羊舌" to listOf(listOf("yáng"), listOf("shé")),
"西门" to listOf(listOf("xī"), listOf("mén")),
"诸葛" to listOf(listOf("zhū"), listOf("gě")),
"赫连" to listOf(listOf("hè"), listOf("lián")),
"轩辕" to listOf(listOf("xuān"), listOf("yuán")),
"钟离" to listOf(listOf("zhōng"), listOf("lí")),
"长孙" to listOf(listOf("zhǎng"), listOf("sūn")),
"闻人" to listOf(listOf("wén"), listOf("rén")),
"闾丘" to listOf(listOf("lǘ"), listOf("qiū")),
"颛孙" to listOf(listOf("zhuān"), listOf("sūn")),
"鲜于" to listOf(listOf("xiān"), listOf("yú")),
)
}

View File

@@ -1,178 +1,263 @@
package org.autojs.autojs.runtime.api.augment.pinyin
import net.sourceforge.pinyin4j.PinyinHelper
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.LOWERCASE
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.UPPERCASE
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITHOUT_TONE
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_MARK
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_NUMBER
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_AND_COLON
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_UNICODE
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_V
import com.huaban.analysis.jieba.CharsDictionaryDatabase
import com.huaban.analysis.jieba.JiebaSegmenter
import com.huaban.analysis.jieba.PhrasesDictionaryDatabase
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
import org.autojs.autojs.extension.ArrayExtensions.toNativeArray
import org.autojs.autojs.extension.ScriptableExtensions.defineProp
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
import org.autojs.autojs.runtime.api.augment.Augmentable
import org.autojs.autojs.runtime.api.augment.Invokable
import org.autojs.autojs.runtime.api.augment.pinyin.PinyinCore.DefaultOptions
import org.autojs.autojs.runtime.api.augment.pinyin.PinyinCore.PinyinMode
import org.autojs.autojs.runtime.api.augment.pinyin.PinyinCore.PinyinStyle
import org.autojs.autojs.runtime.api.augment.pinyin.PinyinCore.parseStyle
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
import org.autojs.autojs.util.RhinoUtils.coerceIntNumber
import org.autojs.autojs.util.RhinoUtils.coerceString
import org.autojs.autojs.util.RhinoUtils.coerceStringUppercase
import org.autojs.autojs.util.RhinoUtils.newNativeObject
import org.autojs.autojs.util.RhinoUtils.withRhinoContext
import org.mozilla.javascript.BoundFunction
import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeArray
import org.mozilla.javascript.NativeJavaMethod
import org.mozilla.javascript.NativeObject
import org.mozilla.javascript.Scriptable
import org.mozilla.javascript.ScriptableObject.DONTENUM
import org.mozilla.javascript.ScriptableObject.PERMANENT
import org.mozilla.javascript.ScriptableObject.READONLY
object Pinyin : Augmentable(), Invokable {
private const val DEFAULT_SEPARATOR = ""
private val charsDictDatabase by lazy { CharsDictionaryDatabase.getInstance(globalContext).database }
private val phrasesDictDatabase by lazy { PhrasesDictionaryDatabase.getInstance(globalContext).database }
private const val DEFAULT_CASE_TYPE_STRING = "LOWERCASE"
private val DEFAULT_CASE_TYPE = LOWERCASE
private const val DEFAULT_TONE_TYPE_STRING = "WITHOUT_TONE"
private val DEFAULT_TONE_TYPE = WITHOUT_TONE
private const val DEFAULT_V_CHAR_TYPE_STRING = "WITH_V"
private val DEFAULT_V_CHAR_TYPE = WITH_V
private val pinyinToneMap = mapOf(
// 单韵母
"a1" to "ā", "a2" to "á", "a3" to "ǎ", "a4" to "à", "a0" to "a",
"o1" to "ō", "o2" to "ó", "o3" to "ǒ", "o4" to "ò", "o0" to "o",
"e1" to "ē", "e2" to "é", "e3" to "ě", "e4" to "è", "e0" to "e",
"i1" to "ī", "i2" to "í", "i3" to "ǐ", "i4" to "ì", "i0" to "i",
"u1" to "ū", "u2" to "ú", "u3" to "ǔ", "u4" to "ù", "u0" to "u",
"ü1" to "ǖ", "ü2" to "ǘ", "ü3" to "ǚ", "ü4" to "ǜ", "ü0" to "ü",
"v1" to "ǖ", "v2" to "ǘ", "v3" to "ǚ", "v4" to "ǜ", "v0" to "ü",
"u:1" to "ǖ", "u:2" to "ǘ", "u:3" to "ǚ", "u:4" to "ǜ", "u:0" to "ü",
// 复韵母 - 普通
"ai1" to "āi", "ai2" to "ái", "ai3" to "ǎi", "ai4" to "ài", "ai0" to "ai",
"ei1" to "ēi", "ei2" to "éi", "ei3" to "ěi", "ei4" to "èi", "ei0" to "ei",
"ui1" to "uī", "ui2" to "uí", "ui3" to "uǐ", "ui4" to "uì", "ui0" to "ui",
"ao1" to "āo", "ao2" to "áo", "ao3" to "ǎo", "ao4" to "ào", "ao0" to "ao",
"ou1" to "ōu", "ou2" to "óu", "ou3" to "ǒu", "ou4" to "òu", "ou0" to "ou",
"iu1" to "iū", "iu2" to "iú", "iu3" to "iǔ", "iu4" to "iù", "iu0" to "iu",
"ie1" to "iē", "ie2" to "ié", "ie3" to "iě", "ie4" to "iè", "ie0" to "ie",
"üe1" to "üē", "üe2" to "üé", "üe3" to "üě", "üe4" to "üè", "üe0" to "üe",
"u:e1" to "üē", "u:e2" to "üé", "u:e3" to "üě", "u:e4" to "üè", "u:e0" to "üe",
"ve1" to "üē", "ve2" to "üé", "ve3" to "üě", "ve4" to "üè", "ve0" to "üe",
// 复韵母 - 特殊
"er1" to "ēr", "er2" to "ér", "er3" to "ěr", "er4" to "èr", "er0" to "er",
// 复韵母 - 前鼻韵母
"an1" to "ān", "an2" to "án", "an3" to "ǎn", "an4" to "àn", "an0" to "an",
"en1" to "ēn", "en2" to "én", "en3" to "ěn", "en4" to "èn", "en0" to "en",
"in1" to "īn", "in2" to "ín", "in3" to "ǐn", "in4" to "ìn", "in0" to "in",
"un1" to "ūn", "un2" to "ún", "un3" to "ǔn", "un4" to "ùn", "un0" to "un",
"ün1" to "ǖn", "ün2" to "ǘn", "ün3" to "ǚn", "ün4" to "ǜn", "ün0" to "ün",
"u:n1" to "ǖn", "u:n2" to "ǘn", "u:n3" to "ǚn", "u:n4" to "ǜn", "u:n0" to "ün",
"vn1" to "ǖn", "vn2" to "ǘn", "vn3" to "ǚn", "vn4" to "ǜn", "vn0" to "ün",
// 复韵母 - 后鼻韵母
"ang1" to "āng", "ang2" to "áng", "ang3" to "ǎng", "ang4" to "àng", "ang0" to "ang",
"eng1" to "ēng", "eng2" to "éng", "eng3" to "ěng", "eng4" to "èng", "eng0" to "eng",
"ing1" to "īng", "ing2" to "íng", "ing3" to "ǐng", "ing4" to "ìng", "ing0" to "ing",
"ong1" to "ōng", "ong2" to "óng", "ong3" to "ǒng", "ong4" to "òng", "ong0" to "ong"
@Suppress("SpellCheckingInspection")
override val selfAssignmentProperties = listOf(
"STYLE_NORMAL" to PinyinStyle.NORMAL,
"STYLE_TONE" to PinyinStyle.TONE,
"STYLE_TONE2" to PinyinStyle.TONE2,
"STYLE_TO3NE" to PinyinStyle.TO3NE,
"STYLE_INITIALS" to PinyinStyle.INITIALS,
"STYLE_FIRST_LETTER" to PinyinStyle.FIRST_LETTER,
"MODE_NORMAL" to PinyinMode.NORMAL,
"MODE_SURNAME" to PinyinMode.SURNAME,
"MODE_PLACENAME" to PinyinMode.PLACE_NAME,
"MODE_PLACE_NAME" to PinyinMode.PLACE_NAME,
)
override val selfAssignmentFunctions = listOf(
::`as`.name,
::of.name,
::convert.name,
::simple.name,
::compare.name,
::compact.name,
::fromCodePoint.name,
::fromPhrase.name,
)
@RhinoSingletonFunctionInterface
override fun invoke(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
of(argList)
override fun invoke(args: Array<out Any?>): NativeArray = ensureArgumentsLengthInRange(args, 1..2) { argList ->
convert(argList)
}
@RhinoSingletonFunctionInterface
fun of(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
val (string, options) = argList
val str = coerceString(string, "").takeIf { it.isNotEmpty() } ?: return@ensureArgumentsLengthInRange ""
var separator = DEFAULT_SEPARATOR
val opt = when (options) {
is String -> newNativeObject().also { separator = options }
is NativeObject -> options
else -> newNativeObject()
}
separator = opt.inquire(listOf("separator", "sep"), ::coerceString, separator)
val caseType = when (val case = opt.inquire(listOf("case", "caseType"))) {
is HanyuPinyinCaseType -> case
else -> when (coerceStringUppercase(case, DEFAULT_CASE_TYPE_STRING)) {
"LOWERCASE", "LOW", "L", "0" -> LOWERCASE
"UPPERCASE", "UP", "U", "1" -> UPPERCASE
else -> throw IllegalArgumentException("Invalid case type: ${Context.toString(case)}")
fun convert(args: Array<out Any?>): NativeArray = ensureArgumentsLengthInRange(args, 1..2) { argList ->
val (hansArg, options) = argList
val hans = coerceString(hansArg, "")
val opt = options as? NativeObject ?: newNativeObject()
convertRhino(hans, opt).map { it.toNativeArray() }.toNativeArray().also { result ->
val funcName = "compact"
withRhinoContext { cx ->
val thisObj = Context.javaToJS(Util, result) as Scriptable
val boundFunction = BoundFunction(
/* cx = */ cx,
/* scope = */ result,
/* targetFunction = */ NativeJavaMethod(Util::class.java.methods.find { it.name == funcName }, funcName),
/* boundThis = */ thisObj,
/* boundArgs = */ arrayOf(result),
)
result.defineProp(funcName, boundFunction, READONLY or DONTENUM or PERMANENT)
}
}
val toneType = when (val tone = opt.inquire(listOf("tone", "toneType"))) {
is HanyuPinyinToneType -> tone
else -> when (coerceStringUppercase(tone, DEFAULT_TONE_TYPE_STRING)) {
"WITH_TONE_NUMBER", "WITH_NUMBER", "NUMBER", "NUM" -> WITH_TONE_NUMBER
"WITHOUT_TONE", "NO_TONE", "NO", "FALSE", "0" -> WITHOUT_TONE
"WITH_TONE_MARK", "WITH_MARK", "MARK", "TRUE", "1" -> WITH_TONE_MARK
else -> throw IllegalArgumentException("Invalid tone type: ${Context.toString(tone)}")
}
}
val vCharType = when (val vChar = opt.inquire(listOf("v", "vChar", "vCharType"))) {
is HanyuPinyinVCharType -> vChar
else -> when (coerceStringUppercase(vChar, if (toneType == WITH_TONE_MARK) "WITH_U_UNICODE" else DEFAULT_V_CHAR_TYPE_STRING)) {
"WITH_U_AND_COLON", "U_AND_COLON", "U_COLON", "U:" -> WITH_U_AND_COLON
"WITH_V", "V" -> WITH_V
"WITH_U_UNICODE", "U_UNICODE", "UNICODE", "Ü" -> WITH_U_UNICODE
else -> throw IllegalArgumentException("Invalid v char type: ${Context.toString(vChar)}")
}
}
ofRhino(str, separator, caseType, toneType, vCharType)
}
@JvmStatic
@JvmOverloads
fun ofRhino(
str: String,
separator: String = DEFAULT_SEPARATOR,
caseType: HanyuPinyinCaseType = DEFAULT_CASE_TYPE,
toneType: HanyuPinyinToneType = DEFAULT_TONE_TYPE,
vCharType: HanyuPinyinVCharType = DEFAULT_V_CHAR_TYPE,
): String {
val format = HanyuPinyinOutputFormat().also {
it.caseType = caseType
it.toneType = toneType
it.vCharType = vCharType
}
val pinyinResult = StringBuilder()
str.forEach { ch ->
val pinyin = when (ch) {
in '\u4E00'..'\u9FA5' -> {
PinyinHelper.toHanyuPinyinStringArray(ch, format)?.firstOrNull() ?: ""
fun convertRhino(hans: String, opt: NativeObject): List<List<String>> = when {
hans.isEmpty() -> listOf()
else -> {
val phrases: List<String> = when {
opt.inquire("segment", ::coerceBoolean, DefaultOptions.SEGMENT) -> {
segment(hans)
}
else -> ch.toString()
else -> groupChineseAndNonChinese(hans)
}
pinyinResult.append(pinyin).append(separator)
}
var nonHans = ""
return pinyinResult.toString().trim()
when (PinyinMode.SURNAME.value) {
opt.inquire("mode", { o, def -> PinyinCore.parseMode(o) ?: def }, DefaultOptions.MODE.value) -> {
PinyinCore.surnamePinyin(hans, opt)
}
else -> {
val pinyinList = mutableListOf<List<String>>()
for (words in phrases) {
val firstCharCode = words.codePointAt(0)
if (fromCodePointInternal(firstCharCode) != null) {
if (nonHans.isNotEmpty()) {
pinyinList.add(listOf(nonHans))
nonHans = ""
}
val newPinyinList = when (words.length) {
1 -> PinyinCore.convert(words, opt)
else -> phrasePinyin(words, opt)
}
if (opt.inquire("group", ::coerceBoolean, false)) {
pinyinList.add(groupPhrases(newPinyinList))
} else {
pinyinList.addAll(newPinyinList)
}
} else {
nonHans += words
}
}
if (nonHans.isNotEmpty()) {
pinyinList.add(listOf(nonHans))
}
pinyinList
}
}
}
}
@RhinoSingletonFunctionInterface
fun `as`(args: Array<out Any?>): String = ensureArgumentsOnlyOne(args) { o ->
convertToTonePinyin(coerceString(o, ""))
fun compare(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
""
}
private fun convertToTonePinyin(input: String): String {
val regex = Regex("(üe|u:e|ve|ue|ang|eng|ing|ong|ai|ei|ui|ao|ou|iu|ie|er|an|en|in|un|ün|u:n|vn|[aoeiuüv])([0-4])")
return regex.replace(input) { matchResult ->
val pinyinBase = matchResult.groupValues[1]
val toneNumber = matchResult.groupValues[2]
pinyinToneMap[pinyinBase + toneNumber] ?: matchResult.value
@RhinoSingletonFunctionInterface
fun compact(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
""
}
@RhinoSingletonFunctionInterface
fun simple(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..3) { argList ->
val (str, enableNumericTone, enableSegment) = argList
simpleRhino(coerceString(str, ""), coerceBoolean(enableNumericTone, false), coerceBoolean(enableSegment, false))
}
@JvmOverloads
fun simpleRhino(s: String, enableNumericTone: Boolean = false, enableSegment: Boolean = false): String {
return convertRhino(s, newNativeObject().also {
it.defineProp("style", if (enableNumericTone) PinyinStyle.TONE2 else PinyinStyle.NORMAL)
it.defineProp("segment", enableSegment)
}).map { it.firstOrNull() ?: emptyList<String>() }.joinToString("")
}
@RhinoSingletonFunctionInterface
fun fromCodePoint(args: Array<out Any?>): String? = ensureArgumentsOnlyOne(args) { o ->
fromCodePointInternal(coerceIntNumber(o))
}
@RhinoSingletonFunctionInterface
fun fromPhrase(args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) { o ->
fromPhraseInternal(coerceString(o)).map { it.toNativeArray() }.toNativeArray()
}
internal fun fromCodePointInternal(codePoint: Int): String? = charsDictDatabase.let { db ->
db.query(
"Dict",
arrayOf("Pinyin"),
"CodePoint = ?",
arrayOf(codePoint.toString()),
null,
null,
null
).use { cursor ->
when {
cursor.moveToFirst() -> cursor.getString(cursor.getColumnIndexOrThrow("Pinyin"))
else -> null
}
}
}
private fun fromPhraseInternal(phrase: String): List<List<String>> = phrasesDictDatabase.let { db ->
val cursor = db.query(
"PhrasesDict",
arrayOf("Pinyin", "OrderIndex"),
"Word = ?",
arrayOf(phrase),
null,
null,
"OrderIndex ASC",
)
val result = mutableListOf<MutableList<String>>()
cursor.use {
while (it.moveToNext()) {
val pinyin = it.getString(it.getColumnIndexOrThrow("Pinyin"))
val orderIndex = it.getInt(it.getColumnIndexOrThrow("OrderIndex"))
while (result.size < orderIndex) {
result.add(mutableListOf())
}
result[orderIndex - 1].add(pinyin)
}
}
result
}
private fun segment(hans: String): List<String> {
return JiebaSegmenter(globalContext).cutSmall(hans, 4)
}
private fun phrasePinyin(phrase: String, options: NativeObject): List<List<String>> {
val phrasePinyinList = fromPhraseInternal(phrase)
return when {
phrasePinyinList.isNotEmpty() -> {
val style = options.inquire("style", { o, def -> parseStyle(o) ?: def }, DefaultOptions.STYLE.value)
phrasePinyinList.map { item ->
if (options.inquire("heteronym", ::coerceBoolean, DefaultOptions.HETERONYM)) {
item.map { pyItem -> PinyinCore.toFixed(pyItem, style) }.toMutableList()
} else {
mutableListOf(PinyinCore.toFixed(item[0], style))
}
}.toMutableList()
}
else -> PinyinCore.convert(phrase, options)
}
}
private fun groupPhrases(phrases: List<List<String>>): List<String> {
return when (phrases.size) {
1 -> phrases[0]
else -> Util.combo(phrases)
}
}
private fun groupChineseAndNonChinese(input: String): List<String> {
if (input.isEmpty()) return emptyList()
val result = mutableListOf<String>()
val chineseRegex = "\\p{IsHan}".toRegex() // 匹配中文字符的正则
var tempGroup = StringBuilder()
var isLastChinese = chineseRegex.matches(input[0].toString()) // 判断第一个字符是否中文
for (char in input) {
val isChinese = chineseRegex.matches(char.toString())
// 如果字符类型(中文或非中文)发生切换,保存当前分组
if (isChinese != isLastChinese) {
result.add(tempGroup.toString()) // 添加当前分组
tempGroup = StringBuilder() // 重置分组
}
tempGroup.append(char) // 将字符添加到当前分组
isLastChinese = isChinese // 更新当前字符类型
}
// 添加最后一个分组
if (tempGroup.isNotEmpty()) {
result.add(tempGroup.toString())
}
return result
}
}

View File

@@ -0,0 +1,282 @@
package org.autojs.autojs.runtime.api.augment.pinyin
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.extension.AnyExtensions.jsUnwrapped
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
import org.autojs.autojs.util.RhinoUtils.coerceString
import org.autojs.autojs.util.RhinoUtils.newNativeObject
import org.mozilla.javascript.NativeObject
import java.util.regex.Pattern
object PinyinCore {
private val SURNAME = Dict.SURNAME
private val COMPOUND_SURNAME = Dict.COMPOUND_SURNAME
// 声母表
private val INITIALS = arrayOf(
"b", "p", "m", "f", "d", "t", "n", "l",
"g", "k", "h", "j", "q", "x",
"zh", "ch", "sh", "r", "z", "c", "s",
)
// 韵母表
private val FINALS = arrayOf(
"a", "o", "e", "i", "u", "v",
"ai", "ei", "ui", "ao", "ou", "iu", "ie", "ve", "er",
"an", "en", "in", "un", "vn",
"ang", "eng", "ing", "ong",
)
// 带声调字符
private val PHONETIC_SYMBOL = Dict.PHONETIC_SYMBOL
private val RE_PHONETIC_SYMBOL = Pattern.compile("([${PHONETIC_SYMBOL.keys.joinToString("")}])").toRegex()
private val RE_TONE2 = Regex("([aeoiuvnm])([0-4])$")
/**
* @param hansArg 要转为拼音的目标字符串 (汉字).
* @param options 可选, 用于指定拼音风格, 是否启用多音字.
* @return 返回的拼音列表.
*/
fun convert(hansArg: String, options: NativeObject = newNativeObject()): MutableList<List<String>> {
val pinyinList = mutableListOf<List<String>>()
val hans = coerceString(hansArg, "").trim()
if (hans.isNotEmpty()) {
var nonHans = ""
when (PinyinMode.SURNAME.value) {
options.inquire("mode", { o, def -> parseMode(o) ?: def }, DefaultOptions.MODE.value) -> {
pinyinList.addAll(surnamePinyin(hans, options))
}
else -> hans.forEach { char ->
when {
Pinyin.fromCodePointInternal(char.code) != null -> {
// 拼音转换
if (nonHans.isNotEmpty()) {
pinyinList.add(listOf(nonHans))
nonHans = ""
}
pinyinList.add(singlePinyin(char.toString(), options))
}
else -> nonHans += char
}
}
}
if (nonHans.isNotEmpty()) pinyinList.add(listOf(nonHans))
}
return pinyinList
}
/**
* 比较两个汉字转成拼音后的排序顺序, 可以用作默认的拼音排序算法.
*
* @param hanA 汉字字符串 A.
* @param hanB 汉字字符串 B.
* @return -1, 0 或 1.
*/
fun compare(hanA: String, hanB: String): Int {
return coerceString(convert(hanA)).compareTo(coerceString(convert(hanB)))
}
fun compact(arr: List<List<String>>) = Util.compact(arr)
/**
* 单字拼音转换.
*
* @param han 单个汉字.
* @param options 选项.
* @return 返回拼音列表, 多音字会有多个拼音项.
*/
private fun singlePinyin(han: String, options: NativeObject): List<String> {
require(han.isNotEmpty()) { "Argument han must not be empty" }
val hanCode = han.first().code
val consulted = Pinyin.fromCodePointInternal(hanCode)
val pinyinList = consulted?.split(",") ?: return listOf(han.first().toString())
val style = options.inquire("style", { o, def -> parseStyle(o) ?: def }, DefaultOptions.STYLE.value)
return when {
options.inquire("heteronym", ::coerceBoolean, DefaultOptions.HETERONYM) -> {
pinyinList.map { toFixed(it, style) }.distinct()
}
else -> listOf(toFixed(pinyinList.first(), style))
}
}
/**
* 单姓处理, 将单个姓氏转换为拼音.
*
* @param hans 姓氏字符串.
* @param options 转换选项 (包括风格、多音字选项等).
* @return 转换后的拼音列表.
*/
private fun singleSurname(hans: String, options: NativeObject): List<List<String>> {
val result = mutableListOf<List<String>>()
// 遍历所有汉字
hans.forEach { char ->
val word = char.toString()
when {
// 如果是已知的姓氏, 从预定义数据中获取拼音, 并格式化
SURNAME.containsKey(word) -> SURNAME[word]?.map { toFixedList(it, options) }?.let { surnamePinyin ->
result.addAll(surnamePinyin)
}
// 如果是未知姓氏, 则按一般规则转换为拼音
else -> result.add(singlePinyin(word, options))
}
}
return result
}
/**
* 姓名转换为拼音.
*/
fun surnamePinyin(hans: String, options: NativeObject): List<List<String>> {
return compoundSurname(hans, options)
}
/**
* 复姓处理, 将复姓转换为拼音.
*
* @param hans 包含复姓的字符串.
* @param options 转换选项 (包含拼音风格等).
* @return 转换后的拼音列表.
*/
private fun compoundSurname(hans: String, options: NativeObject): List<List<String>> {
val len = hans.length
var prefixIndex = 0
val result = mutableListOf<List<String>>()
var i = 0
while (i < len) {
// 获取两个连续字符, 检测是否是复姓
val twoWords = hans.substring(i, (i + 2).coerceAtMost(len))
if (!COMPOUND_SURNAME.containsKey(twoWords)) {
i++
continue
}
// 如果前缀内容存在 (非复姓部分), 处理非复姓部分
if (prefixIndex < i) {
val nonCompoundPart = hans.substring(prefixIndex, i)
result.addAll(singleSurname(nonCompoundPart, options)) // 调用单姓处理函数
}
// 处理复姓部分, 并格式化为指定风格
COMPOUND_SURNAME[twoWords]?.map { toFixedList(it, options) }?.let { compoundPart ->
result.addAll(compoundPart)
}
// 跳过复姓的两个字
i += 2
prefixIndex = i
}
// 处理复姓最后剩下的部分 (即非复姓部分)
if (prefixIndex < len) {
val remaining = hans.substring(prefixIndex, len)
result.addAll(singleSurname(remaining, options))
}
return result
}
/**
* 格式化拼音风格.
*
* @param pinyin TONE 风格的拼音.
* @param style 目标转换的拼音风格.
* @return 转换后的拼音.
*/
internal fun toFixed(pinyin: String, style: Int): String = when (style) {
PinyinStyle.NORMAL.value -> pinyin.replace(RE_PHONETIC_SYMBOL) { matchResult ->
PHONETIC_SYMBOL[matchResult.groupValues[1]]?.replace(RE_TONE2, "$1") ?: matchResult.value
}
PinyinStyle.INITIALS.value -> initials(pinyin)
PinyinStyle.FIRST_LETTER.value -> pinyin.first().toString()
PinyinStyle.TONE.value -> pinyin
PinyinStyle.TONE2.value -> {
var tone = ""
val py = pinyin.replace(RE_PHONETIC_SYMBOL) { matchResult ->
PHONETIC_SYMBOL[matchResult.groupValues[1]]?.let { s ->
s.replace(RE_TONE2, "$2").also { tone = it }
s.replace(RE_TONE2, "$1")
} ?: matchResult.value
}
py + tone
}
PinyinStyle.TO3NE.value -> pinyin.replace(RE_PHONETIC_SYMBOL) { matchResult ->
PHONETIC_SYMBOL[matchResult.groupValues[1]] ?: matchResult.value
}
else -> pinyin
}
private fun toFixedList(items: List<String>, options: NativeObject): List<String> {
val style = options.inquire("style", { o, def -> parseStyle(o) ?: def }, DefaultOptions.STYLE.value)
return items.map { toFixed(it, style) }
}
/**
* 格式化拼音为声母 (Initials) 形式.
*/
private fun initials(pinyin: String) = INITIALS.find { pinyin.startsWith(it) } ?: ""
internal fun parseMode(o: Any): Int? {
val obj = o.jsUnwrapped()
return when {
obj.isJsNullish() -> null
obj is PinyinMode -> obj.value
obj is Int -> {
require(PinyinMode.entries.any { it.value == obj }) { "Invalid mode constant: $obj" }
obj
}
else -> coerceString(obj, "").uppercase().let { name ->
PinyinMode.entries.find { it.name == name }?.value ?: throw IllegalArgumentException("Invalid mode name: $obj")
}
}
}
internal fun parseStyle(o: Any): Int? {
val obj = o.jsUnwrapped()
return when {
obj.isJsNullish() -> null
obj is PinyinStyle -> obj.value
obj is Int -> {
require(PinyinStyle.entries.any { it.value == obj }) { "Invalid style constant: $obj" }
obj
}
else -> coerceString(obj, "").uppercase().let { name ->
PinyinStyle.entries.find { it.name == name }?.value ?: throw IllegalArgumentException("Invalid style name: $obj")
}
}
}
enum class PinyinStyle(val value: Int) {
NORMAL(0), // 普通风格, 不带声调
TONE(1), // 标准风格, 声调在韵母上
TONE2(2), // 数字形式标记声调
TO3NE(5), // 数字方式, 声母后加声调
INITIALS(3), // 仅保留声母
FIRST_LETTER(4), // 仅保留首字母
}
@Suppress("SpellCheckingInspection")
enum class PinyinMode(val value: Int) {
NORMAL(0), // 普通模式
SURNAME(1), // 姓氏模式
PLACE_NAME(2), // 地名模式
PLACENAME(2), // 地名模式 (兼容)
}
object DefaultOptions {
val MODE = PinyinMode.NORMAL // 默认模式
val STYLE = PinyinStyle.TONE // 默认风格
const val SEGMENT: Boolean = false // 是否分词
const val HETERONYM: Boolean = false // 是否允许多音字
}
}

View File

@@ -0,0 +1,89 @@
package org.autojs.autojs.runtime.api.augment.pinyin
import org.autojs.autojs.util.RhinoUtils.coerceString
/**
* 工具类 Util, 用于实现拼音转换时辅助功能.
*/
@Suppress("SpellCheckingInspection")
object Util {
/**
* 合并二维字符串数组为一维字符串数组.
*
* @param arr 二维数组, 如 [["zhāo", "cháo"], ["yáng"], ["dōng"], ["shēng"]]
* @return 合并后的字符串数组, 一维结果, 如 ["zhāoyáng", "cháoyáng", "dōng", "shēng"]
*/
fun combo(arr: List<List<String>>): List<String> = when {
arr.size <= 1 -> arr.firstOrNull() ?: emptyList()
else -> {
var result = combo2array(arr[0], arr[1])
for (i in 2 until arr.size) {
result = combo2array(result, arr[i])
}
result
}
}
/**
* 组合两个拼音数组为一维字符串数组.
*
* @param a1 第一个拼音数组, 如 ["zhāo", "cháo"]
* @param a2 第二个拼音数组, 如 ["yáng"]
* @return 组合后的一维字符串数组, 如 ["zhāoyáng", "cháoyáng"]
*/
private fun combo2array(a1: List<String>, a2: List<String>): List<String> {
return when {
a1.isEmpty() -> a2
a2.isEmpty() -> a1
else -> a1.flatMap { item1 -> a2.map { item2 -> item1 + item2 } }
}
}
/**
* 合并多个拼音数组, 形成一个新的二维数组.
* 每两个拼音元素形成一个元组数组.
*
* @param arr 二维数组(每组拼音数组)
* @return 合并后的二维拼音数组, 如 [[hai, qian], [huan, qian]]
*/
fun compact(arr: List<List<String>>): List<List<String>> = when {
arr.isEmpty() -> emptyList()
arr.size == 1 -> arr.map { listOf(it).flatten() }
else -> {
var result = compact2array(arr[0], arr[1])
for (i in 2 until arr.size) {
result = compact2array(result, arr[i])
}
result
}
}
/**
* 组合两个拼音数组, 形成一个新的二维数组.
*
* @param a1 第一个拼音数组 eg: ["hai", "huan"] / [["hai"], ["huan"]]
* @param a2 第二个拼音数组 eg: ["qian"]
* @return 组合后的二维数组, 如 [["hai", "qian"], ["huan", "qian"]]
*/
private fun compact2array(a1: List<Any>, a2: List<String>): List<List<String>> = when {
a1.isEmpty() -> listOf(a2)
a2.isEmpty() -> a1.map { item1 ->
when (item1) {
is List<*> -> item1.map { coerceString(it) }
else -> listOf(item1.toString())
}
}
else -> {
val result = mutableListOf<List<String>>()
a1.forEach { item1 ->
val list1 = when (item1) {
is List<*> -> item1.map { coerceString(it) }
else -> listOf(coerceString(item1))
}
a2.mapTo(result) { list1 + it }
}
result
}
}
}

View File

@@ -0,0 +1,178 @@
package org.autojs.autojs.runtime.api.augment.pinyin4j
import net.sourceforge.pinyin4j.PinyinHelper
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.LOWERCASE
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.UPPERCASE
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITHOUT_TONE
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_MARK
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_NUMBER
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_AND_COLON
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_UNICODE
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_V
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
import org.autojs.autojs.runtime.api.augment.Augmentable
import org.autojs.autojs.runtime.api.augment.Invokable
import org.autojs.autojs.util.RhinoUtils.coerceString
import org.autojs.autojs.util.RhinoUtils.coerceStringUppercase
import org.autojs.autojs.util.RhinoUtils.newNativeObject
import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeObject
object Pinyin4j : Augmentable(), Invokable {
private const val DEFAULT_SEPARATOR = ""
private const val DEFAULT_CASE_TYPE_STRING = "LOWERCASE"
private val DEFAULT_CASE_TYPE = LOWERCASE
private const val DEFAULT_TONE_TYPE_STRING = "WITHOUT_TONE"
private val DEFAULT_TONE_TYPE = WITHOUT_TONE
private const val DEFAULT_V_CHAR_TYPE_STRING = "WITH_V"
private val DEFAULT_V_CHAR_TYPE = WITH_V
private val pinyinToneMap = mapOf(
// 单韵母
"a1" to "ā", "a2" to "á", "a3" to "ǎ", "a4" to "à", "a0" to "a",
"o1" to "ō", "o2" to "ó", "o3" to "ǒ", "o4" to "ò", "o0" to "o",
"e1" to "ē", "e2" to "é", "e3" to "ě", "e4" to "è", "e0" to "e",
"i1" to "ī", "i2" to "í", "i3" to "ǐ", "i4" to "ì", "i0" to "i",
"u1" to "ū", "u2" to "ú", "u3" to "ǔ", "u4" to "ù", "u0" to "u",
"ü1" to "ǖ", "ü2" to "ǘ", "ü3" to "ǚ", "ü4" to "ǜ", "ü0" to "ü",
"v1" to "ǖ", "v2" to "ǘ", "v3" to "ǚ", "v4" to "ǜ", "v0" to "ü",
"u:1" to "ǖ", "u:2" to "ǘ", "u:3" to "ǚ", "u:4" to "ǜ", "u:0" to "ü",
// 复韵母 - 普通
"ai1" to "āi", "ai2" to "ái", "ai3" to "ǎi", "ai4" to "ài", "ai0" to "ai",
"ei1" to "ēi", "ei2" to "éi", "ei3" to "ěi", "ei4" to "èi", "ei0" to "ei",
"ui1" to "uī", "ui2" to "uí", "ui3" to "uǐ", "ui4" to "uì", "ui0" to "ui",
"ao1" to "āo", "ao2" to "áo", "ao3" to "ǎo", "ao4" to "ào", "ao0" to "ao",
"ou1" to "ōu", "ou2" to "óu", "ou3" to "ǒu", "ou4" to "òu", "ou0" to "ou",
"iu1" to "iū", "iu2" to "iú", "iu3" to "iǔ", "iu4" to "iù", "iu0" to "iu",
"ie1" to "iē", "ie2" to "ié", "ie3" to "iě", "ie4" to "iè", "ie0" to "ie",
"üe1" to "üē", "üe2" to "üé", "üe3" to "üě", "üe4" to "üè", "üe0" to "üe",
"u:e1" to "üē", "u:e2" to "üé", "u:e3" to "üě", "u:e4" to "üè", "u:e0" to "üe",
"ve1" to "üē", "ve2" to "üé", "ve3" to "üě", "ve4" to "üè", "ve0" to "üe",
// 复韵母 - 特殊
"er1" to "ēr", "er2" to "ér", "er3" to "ěr", "er4" to "èr", "er0" to "er",
// 复韵母 - 前鼻韵母
"an1" to "ān", "an2" to "án", "an3" to "ǎn", "an4" to "àn", "an0" to "an",
"en1" to "ēn", "en2" to "én", "en3" to "ěn", "en4" to "èn", "en0" to "en",
"in1" to "īn", "in2" to "ín", "in3" to "ǐn", "in4" to "ìn", "in0" to "in",
"un1" to "ūn", "un2" to "ún", "un3" to "ǔn", "un4" to "ùn", "un0" to "un",
"ün1" to "ǖn", "ün2" to "ǘn", "ün3" to "ǚn", "ün4" to "ǜn", "ün0" to "ün",
"u:n1" to "ǖn", "u:n2" to "ǘn", "u:n3" to "ǚn", "u:n4" to "ǜn", "u:n0" to "ün",
"vn1" to "ǖn", "vn2" to "ǘn", "vn3" to "ǚn", "vn4" to "ǜn", "vn0" to "ün",
// 复韵母 - 后鼻韵母
"ang1" to "āng", "ang2" to "áng", "ang3" to "ǎng", "ang4" to "àng", "ang0" to "ang",
"eng1" to "ēng", "eng2" to "éng", "eng3" to "ěng", "eng4" to "èng", "eng0" to "eng",
"ing1" to "īng", "ing2" to "íng", "ing3" to "ǐng", "ing4" to "ìng", "ing0" to "ing",
"ong1" to "ōng", "ong2" to "óng", "ong3" to "ǒng", "ong4" to "òng", "ong0" to "ong"
)
override val selfAssignmentFunctions = listOf(
::`as`.name,
::of.name,
)
@RhinoSingletonFunctionInterface
override fun invoke(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
of(argList)
}
@RhinoSingletonFunctionInterface
fun of(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..2) { argList ->
val (string, options) = argList
val str = coerceString(string, "").takeIf { it.isNotEmpty() } ?: return@ensureArgumentsLengthInRange ""
var separator = DEFAULT_SEPARATOR
val opt = when (options) {
is String -> newNativeObject().also { separator = options }
is NativeObject -> options
else -> newNativeObject()
}
separator = opt.inquire(listOf("separator", "sep"), ::coerceString, separator)
val caseType = when (val case = opt.inquire(listOf("case", "caseType"))) {
is HanyuPinyinCaseType -> case
else -> when (coerceStringUppercase(case, DEFAULT_CASE_TYPE_STRING)) {
"LOWERCASE", "LOW", "L", "0" -> LOWERCASE
"UPPERCASE", "UP", "U", "1" -> UPPERCASE
else -> throw IllegalArgumentException("Invalid case type: ${Context.toString(case)}")
}
}
val toneType = when (val tone = opt.inquire(listOf("tone", "toneType"))) {
is HanyuPinyinToneType -> tone
else -> when (coerceStringUppercase(tone, DEFAULT_TONE_TYPE_STRING)) {
"WITH_TONE_NUMBER", "WITH_NUMBER", "NUMBER", "NUM" -> WITH_TONE_NUMBER
"WITHOUT_TONE", "NO_TONE", "NO", "FALSE", "0" -> WITHOUT_TONE
"WITH_TONE_MARK", "WITH_MARK", "MARK", "TRUE", "1" -> WITH_TONE_MARK
else -> throw IllegalArgumentException("Invalid tone type: ${Context.toString(tone)}")
}
}
val vCharType = when (val vChar = opt.inquire(listOf("v", "vChar", "vCharType"))) {
is HanyuPinyinVCharType -> vChar
else -> when (coerceStringUppercase(vChar, if (toneType == WITH_TONE_MARK) "WITH_U_UNICODE" else DEFAULT_V_CHAR_TYPE_STRING)) {
"WITH_U_AND_COLON", "U_AND_COLON", "U_COLON", "U:" -> WITH_U_AND_COLON
"WITH_V", "V" -> WITH_V
"WITH_U_UNICODE", "U_UNICODE", "UNICODE", "Ü" -> WITH_U_UNICODE
else -> throw IllegalArgumentException("Invalid v char type: ${Context.toString(vChar)}")
}
}
ofRhino(str, separator, caseType, toneType, vCharType)
}
@JvmStatic
@JvmOverloads
fun ofRhino(
str: String,
separator: String = DEFAULT_SEPARATOR,
caseType: HanyuPinyinCaseType = DEFAULT_CASE_TYPE,
toneType: HanyuPinyinToneType = DEFAULT_TONE_TYPE,
vCharType: HanyuPinyinVCharType = DEFAULT_V_CHAR_TYPE,
): String {
val format = HanyuPinyinOutputFormat().also {
it.caseType = caseType
it.toneType = toneType
it.vCharType = vCharType
}
val pinyinResult = StringBuilder()
str.forEach { ch ->
val pinyin4j = when (ch) {
in '\u4E00'..'\u9FA5' -> {
PinyinHelper.toHanyuPinyinStringArray(ch, format)?.firstOrNull() ?: ""
}
else -> ch.toString()
}
pinyinResult.append(pinyin4j).append(separator)
}
return pinyinResult.toString().trim()
}
@RhinoSingletonFunctionInterface
fun `as`(args: Array<out Any?>): String = ensureArgumentsOnlyOne(args) { o ->
convertToTonePinyin(coerceString(o, ""))
}
private fun convertToTonePinyin(input: String): String {
val regex = Regex("(üe|u:e|ve|ue|ang|eng|ing|ong|ai|ei|ui|ao|ou|iu|ie|er|an|en|in|un|ün|u:n|vn|[aoeiuüv])([0-4])")
return regex.replace(input) { matchResult ->
val pinyinBase = matchResult.groupValues[1]
val toneNumber = matchResult.groupValues[2]
pinyinToneMap[pinyinBase + toneNumber] ?: matchResult.value
}
}
}

View File

@@ -161,7 +161,7 @@ class UI(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRunt
val key = coerceString(keyArg)
value?.let { mProperties[key] = it } ?: mProperties.remove(key)
}, NOT_CONSTRUCTABLE))
}, PERMANENT or READONLY)
}, READONLY or PERMANENT)
}
@Suppress("SameReturnValue")
@@ -599,11 +599,11 @@ class UI(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRunt
private fun initWidgetConstructor(ctor: BaseFunction) {
val prototype = ctor.prop("prototype") as? ScriptableObject ?: newNativeObject()
assignWidgetProperties(prototype)
ctor.defineProp("prototype", prototype, PERMANENT or DONTENUM)
ctor.defineProp("prototype", prototype, DONTENUM or PERMANENT)
}
private fun assignWidgetProperties(o: ScriptableObject) {
o.defineProperty("__attrs__", newNativeObject(), PERMANENT or DONTENUM)
o.defineProperty("__attrs__", newNativeObject(), DONTENUM or PERMANENT)
o.defineFunctionProperties(
arrayOf(
"renderInternal",

View File

@@ -277,7 +277,7 @@ object Util : Augmentable() {
else -> withRhinoContext { context ->
val tmp = object : BaseFunction() {
override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable?, args: Array<out Any?>) = newNativeObject().also {
it.defineProperty("constructor", d, READONLY or PERMANENT or DONTENUM)
it.defineProperty("constructor", d, READONLY or DONTENUM or PERMANENT)
}
}
tmp.construct(context, ImporterTopLevel(context), arrayOf()).also { instance ->
@@ -718,7 +718,7 @@ object Util : Augmentable() {
it.defineFunctionProperties(
arrayOf("toString"),
RegularFunction::class.java,
READONLY or PERMANENT or DONTENUM,
READONLY or DONTENUM or PERMANENT,
)
}
}

View File

@@ -19,6 +19,9 @@ import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.huaban.analysis.jieba.CharsDictionaryDatabase
import com.huaban.analysis.jieba.PhrasesDictionaryDatabase
import com.huaban.analysis.jieba.WordDictionaryDatabase
import org.autojs.autojs.AutoJs
import org.autojs.autojs.app.FragmentPagerAdapterBuilder
import org.autojs.autojs.app.FragmentPagerAdapterBuilder.StoredFragmentPagerAdapter
@@ -230,12 +233,20 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
fun exitCompletely() {
FloatyWindowManger.hideCircularMenuAndSaveState()
stopService(Intent(this, FloatyService::class.java))
stopService(Intent(this, ScreenCapturerForegroundService::class.java))
AutoJs.instance.scriptEngineService.stopAll()
WordDictionaryDatabase.getInstance(this).close()
CharsDictionaryDatabase.getInstance(this).close()
PhrasesDictionaryDatabase.getInstance(this).close()
mA11yTool.stopService(false)
mForeGroundService.stopIfNeeded()
WrappedShizuku.onDestroy()
Process.killProcess(Process.myPid())
}

View File

@@ -234,8 +234,8 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView {
mTask = task;
firstCharIconBinding.firstChar
.setIcon(AutoFileSource.ENGINE.equals(mTask.getEngineName()) ? FileUtils.TYPE.AUTO.icon : FileUtils.TYPE.JAVASCRIPT.icon)
// .setIconTextThemeColor()
// .setStrokeThemeColor()
.setIconTextThemeColor()
.setStrokeThemeColor()
.setFillTransparent();
itemBinding.name.setText(task.getName());
itemBinding.taskListFilePath.setText(task.getDesc());

View File

@@ -56,7 +56,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -115,8 +114,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
EditText mOutputPath;
EditText mAppName;
EditText mPackageName;
TextInputLayout mPackageNameParent;
EditText mVersionName;
TextInputLayout mVersionNameParent;
EditText mVersionCode;
TextInputLayout mVersionCodeParent;
ImageView mIcon;
LinearLayout mAppConfig;
@@ -181,11 +183,15 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
return false;
});
mPackageNameParent = binding.packageNameParent;
mVersionName = binding.versionName;
mVersionNameParent = binding.versionNameParent;
mVersionCode = binding.versionCode;
mVersionCodeParent = binding.versionCodeParent;
mIcon = binding.appIcon;
mIcon.setVisibility(View.INVISIBLE);
mIcon.setOnClickListener(v -> selectIcon());
mAppConfig = binding.appConfig;
@@ -261,11 +267,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
NotAskAgainDialog.Builder builder = new NotAskAgainDialog.Builder(context, key);
builder.title(R.string.text_prompt);
builder.content(getString(R.string.text_unable_to_build_apk_as_autojs6_does_not_include_selected_abi, abiText) + "\n\n" +
getString(R.string.text_the_following_solutions_can_be_referred_to) + ":\n\n" +
"- " + getString(R.string.text_download_and_install_autojs6_including_above_abi, abiText) + "\n" +
"- " + getString(R.string.text_download_and_install_autojs6_including_all_abis) + " [" + getString(R.string.text_recommended) + "]\n\n" +
getString(R.string.text_download_link_for_autojs6) + ":\n" +
getString(R.string.uri_autojs6_download_link));
getString(R.string.text_the_following_solutions_can_be_referred_to) + ":\n\n" +
"- " + getString(R.string.text_download_and_install_autojs6_including_above_abi, abiText) + "\n" +
"- " + getString(R.string.text_download_and_install_autojs6_including_all_abis) + " [" + getString(R.string.text_recommended) + "]\n\n" +
getString(R.string.text_download_link_for_autojs6) + ":\n" +
getString(R.string.uri_autojs6_download_link));
builder.positiveText(R.string.dialog_button_dismiss);
MaterialDialog dialog = builder.show();
if (dialog != null) {
@@ -360,6 +366,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
return list.stream().anyMatch(item -> item.equalsIgnoreCase(s));
}
@SuppressLint("CheckResult")
private void setupWithSourceFile(ScriptFile file) {
String dir = file.getParent();
if (dir != null && dir.startsWith(getFilesDir().getPath())) {
@@ -368,20 +375,42 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
mOutputPath.setText(dir);
mAppName.setText(file.getSimplifiedName());
String packageName = getString(R.string.format_default_package_name, generatePackageNameSuffix(file));
mPackageName.setText(packageName);
Observable.fromCallable(() -> {
String packageNameSuffix = generatePackageNameSuffix(file);
return getString(R.string.format_default_package_name, packageNameSuffix);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(packageName -> {
mPackageName.setText(packageName);
mPackageNameParent.setHint(R.string.text_package_name);
SimpleVersionInfo nextVersionInfo = AppUtils.generateNextVersionInfo(packageName);
if (nextVersionInfo != null) {
mVersionName.setText(nextVersionInfo.versionName);
mVersionCode.setText(nextVersionInfo.versionCodeString);
}
SimpleVersionInfo nextVersionInfo = AppUtils.generateNextVersionInfo(packageName);
if (nextVersionInfo != null) {
mVersionName.setText(nextVersionInfo.versionName);
mVersionCode.setText(nextVersionInfo.versionCodeString);
} else {
mVersionName.setText(R.string.default_build_apk_version_name);
mVersionCode.setText(R.string.default_build_apk_version_code);
}
mVersionNameParent.setHint(R.string.text_version_name);
mVersionCodeParent.setHint(R.string.text_version_code);
Drawable iconDrawable = AppUtils.getInstalledAppIcon(packageName);
if (iconDrawable != null) {
mIcon.setImageDrawable(iconDrawable);
mIsDefaultIcon = false;
}
Drawable iconDrawable = AppUtils.getInstalledAppIcon(packageName);
if (iconDrawable != null) {
mIcon.setImageDrawable(iconDrawable);
mIsDefaultIcon = false;
}
mIcon.setVisibility(View.VISIBLE);
}, throwable -> {
mPackageName.setText(getString(R.string.format_default_package_name, file.getSimplifiedName().toLowerCase(Language.getPrefLanguage().getLocale())));
mVersionName.setText(R.string.default_build_apk_version_name);
mVersionCode.setText(R.string.default_build_apk_version_code);
mPackageNameParent.setHint(R.string.text_package_name);
mVersionNameParent.setHint(R.string.text_version_name);
mVersionCodeParent.setHint(R.string.text_version_code);
mIcon.setVisibility(View.VISIBLE);
});
setSource(file);
}
@@ -389,7 +418,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private static String generatePackageNameSuffix(ScriptFile file) {
String name = file.getSimplifiedName();
if (name.matches(".*[\\u4e00-\\u9fff].*")) {
name = Pinyin.ofRhino(name);
// name = Pinyin4j.ofRhino(name);
name = Pinyin.INSTANCE.simpleRhino(name, false, true);
}
name = name.replaceAll("\\W+", "_");
if (name.matches("^\\d.*")) {
@@ -461,11 +491,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
return checkNotEmpty(mOutputPath);
}
return checkNotEmpty(mSourcePath)
& checkNotEmpty(mOutputPath)
& checkNotEmpty(mAppName)
& checkNotEmpty(mVersionCode)
& checkNotEmpty(mVersionName)
& checkPackageNameValid(mPackageName);
& checkNotEmpty(mOutputPath)
& checkNotEmpty(mAppName)
& checkNotEmpty(mVersionCode)
& checkNotEmpty(mVersionName)
& checkPackageNameValid(mPackageName);
}
private boolean checkAbis() {
@@ -482,9 +512,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private boolean checkPackageNameValid(EditText editText) {
Editable text = editText.getText();
String hint = Objects.requireNonNull(((TextInputLayout) editText.getParent().getParent()).getHint()).toString();
if (TextUtils.isEmpty(text)) {
editText.setError(hint + getString(R.string.text_should_not_be_empty));
editText.setError(getString(R.string.text_should_not_be_empty));
return false;
}
if (!REGEX_PACKAGE_NAME.matcher(text).matches()) {
@@ -495,15 +524,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
private boolean checkNotEmpty(EditText editText) {
if (!TextUtils.isEmpty(editText.getText()) || !editText.isShown())
return true;
// TODO by Stardust on Dec 8, 2017.
// ! More beautiful ways?
// ! zh-CN (translated by SuperMonster003 on Jul 29, 2024):
// ! 更优雅的方式?
String hint = Objects.requireNonNull(((TextInputLayout) editText.getParent().getParent()).getHint()).toString();
editText.setError(hint + getString(R.string.text_should_not_be_empty));
return false;
if (TextUtils.isEmpty(editText.getText()) && editText.isShown()) {
editText.setError(getString(R.string.text_should_not_be_empty));
return false;
}
return true;
}
private void showHintDialogIfNeeded() {
@@ -586,18 +611,18 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private boolean isWideCharacter(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS ||
block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A ||
block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT ||
block == Character.UnicodeBlock.HIRAGANA ||
block == Character.UnicodeBlock.KATAKANA ||
block == Character.UnicodeBlock.HANGUL_SYLLABLES ||
block == Character.UnicodeBlock.HANGUL_JAMO ||
block == Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO ||
block == Character.UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS ||
block == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A ||
block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT ||
block == Character.UnicodeBlock.HIRAGANA ||
block == Character.UnicodeBlock.KATAKANA ||
block == Character.UnicodeBlock.HANGUL_SYLLABLES ||
block == Character.UnicodeBlock.HANGUL_JAMO ||
block == Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO ||
block == Character.UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS ||
block == Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS ||
block == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
}
@SuppressWarnings("ResultOfMethodCallIgnored")

View File

@@ -2,6 +2,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -156,10 +157,11 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/package_name_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/text_package_name"
android:hint="@string/text_package_name_with_computing"
app:expandedHintEnabled="false">
<!-- @Hint by SuperMonster003 on Nov 18, 2024. -->
@@ -188,10 +190,11 @@
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/version_name_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/text_version_name"
android:hint="@string/text_version_name_with_computing"
app:expandedHintEnabled="false">
<androidx.appcompat.widget.AppCompatEditText
@@ -199,15 +202,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:text="1.0.0" />
tools:text="1.0.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/version_code_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/text_version_code"
android:hint="@string/text_version_code_with_computing"
app:expandedHintEnabled="false">
<androidx.appcompat.widget.AppCompatEditText
@@ -215,7 +219,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:text="1" />
tools:text="1" />
</com.google.android.material.textfield.TextInputLayout>

View File

@@ -811,5 +811,8 @@
<string name="text_write_secure_settings_description">إعدادات النظام الآمنة ، التي تحتوي على تفضيلات النظام التي يمكن أن تقرأها التطبيقات ولكن لا يُسمح لها بالكتابة.\nهذه هي لتفضيلات يجب على المستخدم تعديلها بشكل صريح من خلال واجهة المستخدم لتطبيق النظام.\nمع إذن إعدادات النظام الآمن ، يمكن للتطبيقات العادية تعديل الإعدادات الآمنة مباشرة (مثل خدمة إمكانية الوصول).</string>
<string name="text_write_system_settings">كتابة إعدادات النظام</string>
<string name="theme_color_default">تقصير</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">اسم الحزمة (جارٍ الحساب...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">اسم الإصدار (جارٍ الحساب...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">رمز الإصدار (جارٍ الحساب...)</string>
</resources>

View File

@@ -807,5 +807,8 @@
<string name="text_write_secure_settings_description">Secure system settings, containing system preferences that applications can read but are not allowed to write.\nThese are for preferences that the user must explicitly modify through the UI of a system app.\nWith secure system settings permission, normal applications can directly modify the secure settings (such as accessibility service).</string>
<string name="text_write_system_settings">Write system settings</string>
<string name="theme_color_default">Default</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">Package name (computing...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">Version name (computing...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">Version code (computing...)</string>
</resources>

View File

@@ -810,5 +810,8 @@
<string name="text_write_secure_settings_description">Ajustes de seguridad del sistema, que contienen preferencias del sistema que las aplicaciones pueden leer pero no pueden escribir.\nSe trata de preferencias que el usuario debe modificar explícitamente a través de la interfaz de usuario de una aplicación del sistema.\nCon el permiso de configuración segura del sistema, las aplicaciones normales pueden modificar directamente la configuración segura (como el servicio de accesibilidad).</string>
<string name="text_write_system_settings">Escribir la configuración del sistema</string>
<string name="theme_color_default">Por defecto</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">Nombre del paquete (Calculando...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">Nombre de la versión (Calculando...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">Código de la versión (Calculando...)</string>
</resources>

View File

@@ -810,5 +810,8 @@
<string name="text_write_secure_settings_description">Paramètres de sécurité du système, contenant les préférences du système que les applications peuvent lire mais ne sont pas autorisées à écrire.\nIl s\'agit des préférences que l\'utilisateur doit explicitement modifier par le biais de l\'interface utilisateur d\'une application système.\nAvec l\'autorisation de paramètres de sécurité du système, les applications normales peuvent directement modifier les paramètres de sécurité (comme le service d\'accessibilité).</string>
<string name="text_write_system_settings">Écrire les paramètres système</string>
<string name="theme_color_default">Default</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">Nom du paquet (en cours de calcul...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">Nom de la version (en cours de calcul...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">Code de version (en cours de calcul...)</string>
</resources>

View File

@@ -810,5 +810,8 @@
<string name="text_write_secure_settings_description">アプリケーションが読み取ることはできるが, 書き込むことはできないシステム環境設定を含む, 安全なシステム設定です.\nこれは, ユーザーがシステムアプリのUIを通じて明示的に変更する必要がある環境設定のためのものです.\nセキュアなシステム設定を許可すると, 通常のアプリケーションはセキュアな設定 (アクセシビリティサービスなど) を直接変更できるようになります</string>
<string name="text_write_system_settings">システム設定の書き込み</string>
<string name="theme_color_default">デフォルト</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">パッケージ名 (計算中...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">バージョン名 (計算中...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">バージョンコード (計算中...)</string>
</resources>

View File

@@ -811,5 +811,8 @@
<string name="text_write_secure_settings_description">애플리케이션이 읽을 수 있지만 쓸 수없는 시스템 환경 설정을 포함하는 보안 시스템 설정.\n이들은 사용자가 시스템 앱의 UI를 통해 명시 적으로 수정 해야하는 선호도입니다.\n보안 시스템 설정 권한을 사용하면 일반 애플리케이션이 보안 설정 (예: 접근성 서비스)을 직접 수정할 수 있습니다.</string>
<string name="text_write_system_settings">시스템 설정을 작성하십시오</string>
<string name="theme_color_default">기본</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">패키지 이름 (계산 중...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">버전 이름 (계산 중...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">버전 코드 (계산 중...)</string>
</resources>

View File

@@ -810,5 +810,8 @@
<string name="text_write_secure_settings_description">Настройки безопасности системы, содержащие системные предпочтения, которые приложения могут читать, но не имеют права записывать.\nОни предназначены для параметров, которые пользователь должен явно изменить через пользовательский интерфейс системного приложения.\nПри наличии разрешения на безопасные системные настройки обычные приложения могут напрямую изменять безопасные настройки (например, служба доступности).</string>
<string name="text_write_system_settings">Запись системных настроек</string>
<string name="theme_color_default">По умолчанию</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">Имя пакета (Вычисление...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">Название версии (Вычисление...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">Код версии (Вычисление...)</string>
</resources>

View File

@@ -809,5 +809,8 @@
<string name="text_write_secure_settings_description">安全設置包含應用程序可讀但不可寫入的設置選項, 這些選項只能由 UI 或系統級別應用修改.\n被授予 \"修改安全設置權限\" 後, 普通應用可直接修改上述安全設置 (例如無障礙服務).</string>
<string name="text_write_system_settings">修改系統設置</string>
<string name="theme_color_default">默認</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">包名 (計算中...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">版本名稱 (計算中...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">版本號 (計算中...)</string>
</resources>

View File

@@ -809,5 +809,8 @@
<string name="text_write_secure_settings_description">安全設定包含應用程式可讀但不可寫入的設定選項, 這些選項只能由 UI 或系統級別應用修改.\n被授予 \"修改安全設定許可權\" 後, 普通應用可直接修改上述安全設定 (例如無障礙服務).</string>
<string name="text_write_system_settings">修改系統設定</string>
<string name="theme_color_default">預設</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">包名 (計算中...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">版本名稱 (計算中...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">版本號 (計算中...)</string>
</resources>

View File

@@ -805,5 +805,8 @@
<string name="text_write_secure_settings_description">安全设置包含应用程序可读但不可写入的设置选项, 这些选项只能由 UI 或系统级别应用修改.\n被授予 \"修改安全设置权限\" 后, 普通应用可直接修改上述安全设置 (例如无障碍服务).</string>
<string name="text_write_system_settings">修改系统设置</string>
<string name="theme_color_default">默认</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">包名 (计算中...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">版本名称 (计算中...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">版本号 (计算中...)</string>
</resources>

View File

@@ -1030,5 +1030,10 @@
<string name="value_app_language_english" translatable="false">EN</string>
<string name="value_app_language_follow_system" translatable="false">Auto</string>
<string name="value_app_language_simplified_chinese" translatable="false">ZH_CN</string>
<string name="text_package_name_with_computing" tools:ignore="TypographyEllipsis">Package name (computing...)</string>
<string name="text_version_name_with_computing" tools:ignore="TypographyEllipsis">Version name (computing...)</string>
<string name="text_version_code_with_computing" tools:ignore="TypographyEllipsis">Version code (computing...)</string>
<string name="default_build_apk_version_name" translatable="false">1.0.0</string>
<string name="default_build_apk_version_code" translatable="false">1</string>
</resources>