File tree Expand file tree Collapse file tree
main/java/ai/openclaw/app
test/java/ai/openclaw/app/ui/chat Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ package ai.openclaw.app
2+
3+ internal fun String.takeUtf16Safe (maxChars : Int ): String {
4+ if (length <= maxChars) return this
5+ // Keep the code-unit cap without leaving a high surrogate at its boundary.
6+ val endsOnHighSurrogate = maxChars > 0 && Character .isHighSurrogate(this [maxChars - 1 ])
7+ return take(if (endsOnHighSurrogate) maxChars - 1 else maxChars)
8+ }
Original file line number Diff line number Diff line change @@ -4,6 +4,7 @@ import ai.openclaw.app.NotificationBurstLimiter
44import ai.openclaw.app.SecurePrefs
55import ai.openclaw.app.allowsPackage
66import ai.openclaw.app.isWithinQuietHours
7+ import ai.openclaw.app.takeUtf16Safe
78import android.app.Notification
89import android.app.NotificationManager
910import android.app.RemoteInput
@@ -30,17 +31,6 @@ internal fun sanitizeNotificationText(value: CharSequence?): String? {
3031 return normalized.takeUtf16Safe(MAX_NOTIFICATION_TEXT_CHARS ).ifEmpty { null }
3132}
3233
33- private fun String.takeUtf16Safe (maxChars : Int ): String {
34- if (length <= maxChars) return this
35- val end =
36- if (maxChars > 0 && Character .isHighSurrogate(this [maxChars - 1 ])) {
37- maxChars - 1
38- } else {
39- maxChars
40- }
41- return take(end)
42- }
43-
4434/* *
4535 * Stable notification snapshot entry exposed through the Android notifications command.
4636 */
Original file line number Diff line number Diff line change 11package ai.openclaw.app.ui.chat
22
3+ import ai.openclaw.app.takeUtf16Safe
34import android.graphics.Bitmap
45import android.graphics.BitmapFactory
56import android.util.LruCache
@@ -527,17 +528,6 @@ private fun sanitizeMetadataText(
527528 return sanitized.takeUtf16Safe(maxChars).takeIf (String ::isNotEmpty)
528529}
529530
530- private fun String.takeUtf16Safe (maxChars : Int ): String {
531- if (length <= maxChars) return this
532- val end =
533- if (maxChars > 0 && Character .isHighSurrogate(this [maxChars - 1 ])) {
534- maxChars - 1
535- } else {
536- maxChars
537- }
538- return take(end)
539- }
540-
541531private fun findTitle (html : String ): String? =
542532 Regex (" (?is)<title(?:\\ s[^>]*)?>(.*?)</title\\ s*>" )
543533 .find(html)
Original file line number Diff line number Diff line change @@ -167,7 +167,7 @@ class ChatLinkPreviewTest {
167167 @Test
168168 fun metadataTruncationPreservesUtf16Boundaries () {
169169 val titlePrefix = " t" .repeat(LINK_PREVIEW_TITLE_MAX_CHARS - 1 )
170- val descriptionPrefix = " d" .repeat(LINK_PREVIEW_DESCRIPTION_MAX_CHARS - 1 )
170+ val descriptionPrefix = " d" .repeat(LINK_PREVIEW_DESCRIPTION_MAX_CHARS - 2 )
171171 val result =
172172 parseOpenGraph(
173173 " <meta property='og:title' content='$titlePrefix \uD83D\uDE80 trailing'>" +
@@ -176,7 +176,7 @@ class ChatLinkPreviewTest {
176176 ) as LinkPreviewResult .Loaded
177177
178178 assertEquals(titlePrefix, result.metadata.title)
179- assertEquals(descriptionPrefix, result.metadata.description)
179+ assertEquals(" $ descriptionPrefix\uD83D\uDE80 " , result.metadata.description)
180180 }
181181
182182 @Test
You can’t perform that action at this time.
0 commit comments