@@ -233,7 +233,7 @@ class DialogLogFragment : DialogFragment() {
233233 private var pendingOverwrite = false
234234
235235 init {
236- logView?.setText(logBuffer, TextView . BufferType . EDITABLE )
236+ logView?.text = Editable . Factory .getInstance().newEditable( " " )
237237 }
238238
239239 private fun getColor (resId : Int ): Int {
@@ -297,7 +297,7 @@ class DialogLogFragment : DialogFragment() {
297297 lineStart = 0
298298 pendingOverwrite = false
299299 logBuffer.clear()
300- logViewRef.get()?.text = logBuffer
300+ logViewRef.get()?.editableText?.clear()
301301 }
302302
303303 override fun onStart (forceStop : Runnable ? ) {
@@ -380,96 +380,77 @@ class DialogLogFragment : DialogFragment() {
380380 */
381381 private fun dispatchLogUpdate (formattedText : CharSequence ) {
382382 val logView = logViewRef.get() ? : return
383-
383+
384384 logView.post {
385+ val editable = logView.editableText ? : return @post
386+
387+ var start = 0
385388 var i = 0
386389 val len = formattedText.length
387-
390+
388391 while (i < len) {
389- // Tìm ký tự điều khiển (\r hoặc \n) gần nhất kể từ i
390- var j = i
391- while (j < len && formattedText[j] != ' \r ' && formattedText[j] != ' \n ' ) j++
392-
393- // Phần nội dung thuần trước ký tự điều khiển (hoặc tới hết chuỗi)
394- if (j > i) {
395- val segment = formattedText.subSequence(i, j)
396- if (pendingOverwrite) {
397- if (lineStart in 0 .. logBuffer.length) {
398- logBuffer.delete(lineStart, logBuffer.length)
392+ when (formattedText[i]) {
393+ ' \r ' , ' \n ' -> {
394+
395+ if (i > start) {
396+ if (pendingOverwrite) {
397+ if (lineStart <= editable.length) {
398+ editable.delete(lineStart, editable.length)
399+ }
400+ pendingOverwrite = false
401+ }
402+
403+ editable.append(formattedText, start, i)
399404 }
400- pendingOverwrite = false
401- }
402- logBuffer.append(segment)
403- }
404-
405- if (j < len) {
406- when (formattedText[j]) {
407- ' \r ' -> {
408- // \r\n (CRLF chuẩn) chỉ là xuống dòng bình thường, bỏ qua \r
409- val isCRLF = j + 1 < len && formattedText[j + 1 ] == ' \n '
405+
406+ if (formattedText[i] == ' \r ' ) {
407+ val isCRLF =
408+ (i + 1 < len && formattedText[i + 1 ] == ' \n ' )
409+
410410 if (! isCRLF) {
411- // Đánh dấu: nội dung tiếp theo sẽ ghi đè từ đầu dòng hiện tại
412411 pendingOverwrite = true
413412 }
414- }
415- ' \n ' -> {
416- // Nếu đang chờ ghi đè mà gặp \n ngay (không có nội dung mới sau \r)
417- // thì coi như dòng đó bị xoá trước khi xuống dòng thật
418- if (pendingOverwrite && lineStart in 0 .. logBuffer.length) {
419- logBuffer.delete(lineStart, logBuffer.length)
413+ } else {
414+ if (pendingOverwrite) {
415+ if (lineStart <= editable.length) {
416+ editable.delete(lineStart, editable.length)
417+ }
420418 pendingOverwrite = false
421419 }
422- logBuffer.append(' \n ' )
420+
421+ editable.append(' \n ' )
423422 lineCount++
424- lineStart = logBuffer .length
423+ lineStart = editable .length
425424 }
425+
426+ start = i + 1
426427 }
427- j++
428428 }
429- i = j
429+
430+ i++
430431 }
431-
432- // Giới hạn log tối đa 5000 dòng để tránh tràn bộ nhớ
433- if (lineCount > 5000 ) {
434- var deleteEndIndex = 0
435- var linesToTrim = lineCount - 5000
436- val currentCharSequence = logBuffer.toString()
437-
438- for (k in currentCharSequence.indices) {
439- if (currentCharSequence[k] == ' \n ' ) {
440- linesToTrim--
441- if (linesToTrim <= 0 ) {
442- deleteEndIndex = k + 1
443- break
444- }
432+
433+ if (start < len) {
434+ if (pendingOverwrite) {
435+ if (lineStart <= editable.length) {
436+ editable.delete(lineStart, editable.length)
445437 }
438+ pendingOverwrite = false
446439 }
447- if (deleteEndIndex > 0 ) {
448- logBuffer.delete(0 , deleteEndIndex)
449- lineCount = 5000
450- lineStart = (lineStart - deleteEndIndex).coerceAtLeast(0 )
451- }
440+
441+ editable.append(formattedText, start, len)
452442 }
453-
454- (logView.editableText ? : return @post).replace(
455- 0 ,
456- logView.editableText.length,
457- logBuffer
458- )
459-
460- // Thực hiện cuộn và giữ focus cho ô nhập liệu
461- (logView.parent as ? ScrollView )?.let { scrollView ->
443+
444+ val scrollView = logView.parent as ? ScrollView
445+ if (scrollView != null ) {
462446 scrollView.post {
463- // 1. Cuộn ScrollView xuống cuối cùng
464- scrollView.fullScroll(ScrollView .FOCUS_DOWN )
465-
466- // 2. Nếu ô nhập liệu đang hiện, ép hệ thống giữ con trỏ tại đây
447+ scrollView.scrollTo(0 , scrollView.getChildAt(0 ).bottom)
448+
467449 val inputRow = inputRowRef.get()
468450 val input = shellInputRef.get()
469- if (inputRow != null && inputRow.visibility == View .VISIBLE && input != null ) {
470- input.post {
471- input.requestFocus()
472- }
451+
452+ if (inputRow?.visibility == View .VISIBLE && input != null ) {
453+ input.requestFocus()
473454 }
474455 }
475456 }
0 commit comments