feat(Bixby): support custom wakeup options#40
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Bixby customization features, including unlocking offline processing, supporting custom wake words, and bypassing wake word restrictions. These features are implemented via Xposed hooks in a new Bixby.kt class and integrated into the settings UI with multi-language localization. The review feedback highlights critical performance issues, specifically recommending caching for synchronous disk I/O in readCustomWakeupText and caching the reflected columnNames field in addRow to prevent UI stutters. Additionally, it is recommended to expand the CJK keyword detection to support Korean and Japanese character ranges.
| private fun readCustomWakeupText(): String { | ||
| return try { | ||
| val directory = File(BIXBY_WAKEUP_SHARED_PREFERENCES_PATH) | ||
| if (!directory.isDirectory) return "" | ||
|
|
||
| directory.listFiles() | ||
| ?.asSequence() | ||
| ?.filter { it.name.endsWith(".xml") } | ||
| ?.mapNotNull { file -> | ||
| Regex("<string name=\"$CUSTOM_WAKEUP_TEXT_KEY\">(.*?)</string>") | ||
| .find(file.readText()) | ||
| ?.groupValues | ||
| ?.get(1) | ||
| } | ||
| ?.firstOrNull() | ||
| .orEmpty() | ||
| } catch (_: Throwable) { | ||
| "" | ||
| } | ||
| } |
There was a problem hiding this comment.
The readCustomWakeupText function performs synchronous disk I/O (listing files, reading file contents, and running regex matching) on every invocation. Since this function is called inside frequently executed hooks like SharedPreferencesImpl.getString and MatrixCursor.addRow, it can cause severe performance degradation, UI stutters, or even ANRs.
Consider caching the result in memory with a short TTL (e.g., 5 seconds) to avoid repeated disk access while still allowing updates to be picked up.
private var cachedCustomWakeupText: String? = null
private var lastCacheTime: Long = 0
private val customWakeupTextRegex by lazy {
Regex("<string name=\"$CUSTOM_WAKEUP_TEXT_KEY\">(.*?)</string>")
}
private fun readCustomWakeupText(): String {
val now = System.currentTimeMillis()
if (cachedCustomWakeupText != null && now - lastCacheTime < 5000) {
return cachedCustomWakeupText!!
}
return try {
val directory = File(BIXBY_WAKEUP_SHARED_PREFERENCES_PATH)
if (!directory.isDirectory) return ""
val text = directory.listFiles()
?.asSequence()
?.filter { it.name.endsWith(".xml") }
?.mapNotNull { file ->
customWakeupTextRegex
.find(file.readText())
?.groupValues
?.get(1)
}
?.firstOrNull()
.orEmpty()
cachedCustomWakeupText = text
lastCacheTime = now
text
} catch (_: Throwable) {
""
}
}| object : XC_MethodHook() { | ||
| override fun beforeHookedMethod(param: MethodHookParam) { | ||
| val row = param.args[0] as? Array<*> ?: return | ||
| val columnNames = try { | ||
| param.thisObject.javaClass | ||
| .getDeclaredField("columnNames") | ||
| .apply { isAccessible = true } | ||
| .get(param.thisObject) as? Array<*> ?: return | ||
| } catch (_: Throwable) { | ||
| return | ||
| } |
There was a problem hiding this comment.
Retrieving the columnNames field via reflection (getDeclaredField) on every single invocation of addRow is highly inefficient and can introduce significant overhead.
Since MatrixCursor is a standard Android framework class, we can resolve and cache this field once at the class level to avoid repeated reflection lookups.
object : XC_MethodHook() {
private val columnNamesField = try {
android.database.MatrixCursor::class.java
.getDeclaredField("columnNames")
.apply { isAccessible = true }
} catch (_: Throwable) {
null
}
override fun beforeHookedMethod(param: MethodHookParam) {
val row = param.args[0] as? Array<*> ?: return
val columnNames = try {
columnNamesField?.get(param.thisObject) as? Array<*> ?: return
} catch (_: Throwable) {
return
}| if (keyword.any { it in '\u4E00'..'\u9FFF' }) { | ||
| param.result = 1 | ||
| } |
There was a problem hiding this comment.
The current check keyword.any { it in '\u4E00'..'鿿' } only covers the main block of Chinese characters (CJK Unified Ideographs). It misses Hangul (Korean) and Kana (Japanese Hiragana/Katakana).
Since Bixby is a Samsung product with a large Korean and Japanese user base, we should expand this check to support Korean and Japanese custom wakeup words as well.
if (keyword.any {
it in '\u4E00'..'\u9FFF' || // Chinese
it in '\uAC00'..'\uD7AF' || // Korean Hangul
it in '\u3040'..'\u30FF' // Japanese Hiragana/Katakana
}) {
param.result = 1
}9fdc27c to
d92fc4d
Compare
|
Addressed the review feedback:
Verified with |
|
PUS unknown error:-1 我没有遇到这个问题,暂时也没能复现,近期比较忙,晚点我再检查一下 |

Summary
Test
Note
之前那个 PR 代码有点问题,所以直接删除了新开提交。