Skip to content

feat(Bixby): support custom wakeup options#40

Open
Mzdyl wants to merge 1 commit into
SoClear:mainfrom
Mzdyl:feat/bixby-custom-wakeup
Open

feat(Bixby): support custom wakeup options#40
Mzdyl wants to merge 1 commit into
SoClear:mainfrom
Mzdyl:feat/bixby-custom-wakeup

Conversation

@Mzdyl

@Mzdyl Mzdyl commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add Bixby Agent and Bixby Wakeup to the module scope.
  • Add options for Bixby offline processing, custom wake words, and wake word restriction bypass.
  • Add English, Chinese, French, and Russian strings plus README entries.

Test

  • sh ./gradlew :app:assembleDebug --no-daemon

Note

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment on lines +318 to +337
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) {
""
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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) {
            ""
        }
    }

Comment on lines +208 to +218
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
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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
                        }

Comment on lines +289 to +291
if (keyword.any { it in '\u4E00'..'\u9FFF' }) {
param.result = 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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
                                }

@Mzdyl Mzdyl force-pushed the feat/bixby-custom-wakeup branch from 9fdc27c to d92fc4d Compare May 27, 2026 16:19
@Mzdyl

Mzdyl commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback:

  • Cached custom wakeup text reads with a short TTL to avoid repeated disk I/O in hooks.
  • Cached the MatrixCursor columnNames field lookup instead of resolving it on every addRow call.
  • Expanded keyword detection to cover CJK ideographs, Korean Hangul, and Japanese Kana.

Verified with sh ./gradlew :app:assembleDebug --no-daemon.

@SoClear

SoClear commented May 30, 2026

Copy link
Copy Markdown
Owner
Screenshot_20260530_225458_Bixby PUS unknown error:-1 请问你有没有遇到这个问题

@Mzdyl

Mzdyl commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

PUS unknown error:-1
是自定义唤醒词资源下载有问题

我没有遇到这个问题,暂时也没能复现,近期比较忙,晚点我再检查一下

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants