@@ -4,6 +4,7 @@ import android.app.Dialog
44import android.content.ClipData
55import android.content.ClipboardManager
66import android.content.Context
7+ import android.content.DialogInterface
78import android.os.Build
89import android.os.Bundle
910import android.os.Message
@@ -12,19 +13,15 @@ import android.view.KeyEvent
1213import android.view.LayoutInflater
1314import android.view.View
1415import android.view.ViewGroup
15- import android.widget.ProgressBar
16- import android.widget.ScrollView
17- import android.widget.TextView
18- import android.widget.Toast
1916import android.view.WindowManager
17+ import android.widget.*
2018import androidx.fragment.app.DialogFragment
2119import com.omarea.common.ui.DialogHelper
22- import com.tool.tree.databinding.KrDialogLogBinding
2320import com.omarea.krscript.executor.ShellExecutor
2421import com.omarea.krscript.model.RunnableNode
2522import com.omarea.krscript.model.ShellHandlerBase
26- import android.content.DialogInterface
2723import com.tool.tree.R
24+ import com.tool.tree.databinding.KrDialogLogBinding
2825import java.lang.ref.WeakReference
2926
3027class DialogLogFragment : DialogFragment () {
@@ -41,14 +38,9 @@ class DialogLogFragment : DialogFragment() {
4138 private var themeResId: Int = 0
4239 private var onDismissRunnable: Runnable ? = null
4340
44- // Lưu tham chiếu Handler để giải phóng khi hủy View
4541 private var currentHandler: MyShellHandler ? = null
4642
47- override fun onCreateView (
48- inflater : LayoutInflater ,
49- container : ViewGroup ? ,
50- savedInstanceState : Bundle ?
51- ): View {
43+ override fun onCreateView (inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ): View {
5244 _binding = KrDialogLogBinding .inflate(inflater, container, false )
5345 return binding.root
5446 }
@@ -110,7 +102,9 @@ class DialogLogFragment : DialogFragment() {
110102 binding.btnCopy.setOnClickListener {
111103 try {
112104 val clipboard = requireContext().getSystemService(Context .CLIPBOARD_SERVICE ) as ClipboardManager
113- val clip = ClipData .newPlainText(" text" , binding.shellOutput.text.toString())
105+ // Lấy toàn bộ text từ StringBuilder của handler (không bị giới hạn bởi ListView)
106+ val fullLog = currentHandler?.getAllLogText() ? : " "
107+ val clip = ClipData .newPlainText(" shell_log" , fullLog)
114108 clipboard.setPrimaryClip(clip)
115109 Toast .makeText(requireContext(), getString(R .string.copy_success), Toast .LENGTH_SHORT ).show()
116110 } catch (e: Exception ) {
@@ -126,20 +120,12 @@ class DialogLogFragment : DialogFragment() {
126120 binding.btnCancel.visibility = View .GONE
127121 }
128122
129- if (nodeInfo.title.isNotEmpty()) {
130- binding.title.text = nodeInfo.title
131- } else {
132- binding.title.visibility = View .GONE
133- }
134-
135- if (nodeInfo.desc.isNotEmpty()) {
136- binding.desc.text = nodeInfo.desc
137- } else {
138- binding.desc.visibility = View .GONE
139- }
123+ binding.title.text = if (nodeInfo.title.isNotEmpty()) nodeInfo.title else { binding.title.visibility = View .GONE ; " " }
124+ binding.desc.text = if (nodeInfo.desc.isNotEmpty()) nodeInfo.desc else { binding.desc.visibility = View .GONE ; " " }
140125
141126 binding.actionProgress.isIndeterminate = true
142127
128+ // Khởi tạo Handler với ListView
143129 val handler = MyShellHandler (requireContext().applicationContext, object : IActionEventHandler {
144130 override fun onCompleted () {
145131 running = false
@@ -162,18 +148,11 @@ class DialogLogFragment : DialogFragment() {
162148 running = true
163149 canceled = false
164150 forceStopRunnable = forceStop
165-
166151 dialog?.window?.addFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
167-
168- if (nodeInfo.interruptable && forceStop != null ) {
169- binding.btnCancel.visibility = View .VISIBLE
170- binding.btnExit.visibility = View .GONE
171- } else {
172- binding.btnExit.visibility = View .GONE
173- binding.btnCancel.visibility = View .GONE
174- }
152+ binding.btnExit.visibility = View .GONE
153+ binding.btnCancel.visibility = if (nodeInfo.interruptable && forceStop != null ) View .VISIBLE else View .GONE
175154 }
176- }, binding.shellOutput , binding.actionProgress)
155+ }, binding.shellOutputList , binding.actionProgress)
177156
178157 this .currentHandler = handler
179158 return handler
@@ -189,33 +168,42 @@ class DialogLogFragment : DialogFragment() {
189168 class MyShellHandler (
190169 context : Context ,
191170 private var actionEventHandler : IActionEventHandler ? ,
192- logView : TextView ? ,
171+ listView : ListView ? ,
193172 shellProgress : ProgressBar ?
194173 ) : ShellHandlerBase(context) {
195174
196- private val logViewRef = WeakReference (logView )
175+ private val listViewRef = WeakReference (listView )
197176 private val progressRef = WeakReference (shellProgress)
198177
178+ // Dữ liệu cho hiển thị (Giới hạn 5k dòng)
179+ private val logData = mutableListOf<SpannableString >()
180+ private val adapter = ArrayAdapter <SpannableString >(context, android.R .layout.simple_list_item_1, logData)
181+
182+ // Dữ liệu cho Copy (Không giới hạn)
183+ private val fullLogBuilder = StringBuilder ()
184+
199185 private val errorColor = getColor(R .color.kr_shell_log_error)
200186 private val basicColor = getColor(R .color.kr_shell_log_basic)
201187 private val scriptColor = getColor(R .color.kr_shell_log_script)
202188 private val endColor = getColor(R .color.kr_shell_log_end)
203189 private var hasError = false
204190
191+ init {
192+ listView?.adapter = adapter
193+ }
194+
205195 private fun getColor (resId : Int ): Int {
206- return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
207- context.getColor(resId)
208- } else {
209- context.resources.getColor(resId)
210- }
196+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) context.getColor(resId) else context.resources.getColor(resId)
211197 }
212198
213199 fun release () {
214- logViewRef .clear()
200+ listViewRef .clear()
215201 progressRef.clear()
216202 actionEventHandler = null
217203 }
218204
205+ fun getAllLogText (): String = fullLogBuilder.toString()
206+
219207 override fun handleMessage (msg : Message ) {
220208 when (msg.what) {
221209 EVENT_EXIT -> onExit(msg.obj)
@@ -241,45 +229,45 @@ class DialogLogFragment : DialogFragment() {
241229 val shellProgress = progressRef.get() ? : return
242230 shellProgress.post {
243231 when {
244- current < 0 -> shellProgress.apply {
245- visibility = View .VISIBLE
246- isIndeterminate = true
247- }
232+ current < 0 -> { shellProgress.visibility = View .VISIBLE ; shellProgress.isIndeterminate = true }
248233 current >= total -> shellProgress.visibility = View .GONE
249- else -> shellProgress.apply {
250- visibility = View .VISIBLE
251- isIndeterminate = false
252- max = total
253- progress = current
254- (layoutParams as ? ViewGroup .MarginLayoutParams )?.let { params ->
255- params.height = 4
256- params.topMargin = 44
257- layoutParams = params
258- }
234+ else -> {
235+ shellProgress.visibility = View .VISIBLE
236+ shellProgress.isIndeterminate = false
237+ shellProgress.max = total
238+ shellProgress.progress = current
259239 }
260240 }
261241 }
262242 }
263243
264244 override fun onStart (msg : Any? ) {
265- logViewRef.get()?.text = " "
245+ logData.clear()
246+ fullLogBuilder.setLength(0 )
247+ adapter.notifyDataSetChanged()
266248 }
267249
268250 override fun onExit (msg : Any? ) {
269251 val code = (msg as ? Int ) ? : - 1
270- if (! hasError && code == 0 ) {
271- actionEventHandler?.onSuccess()
272- }
252+ if (! hasError && code == 0 ) actionEventHandler?.onSuccess()
273253 updateLog(context.getString(R .string.kr_shell_completed), endColor)
274254 actionEventHandler?.onCompleted()
275255 }
276256
277257 override fun updateLog (msg : SpannableString ? ) {
278- val logView = logViewRef .get() ? : return
258+ val listView = listViewRef .get() ? : return
279259 msg?.let {
280- logView.post {
281- logView.append(it)
282- (logView.parent as ? ScrollView )?.fullScroll(ScrollView .FOCUS_DOWN )
260+ // Thêm vào Builder cho việc Copy (Không giới hạn)
261+ fullLogBuilder.append(it.toString()).append(" \n " )
262+
263+ // Cập nhật giao diện (Giới hạn 5000 dòng cuối)
264+ listView.post {
265+ logData.add(it)
266+ if (logData.size > 5000 ) {
267+ logData.subList(0 , logData.size - 5000 ).clear()
268+ }
269+ adapter.notifyDataSetChanged()
270+ listView.setSelection(logData.size - 1 )
283271 }
284272 }
285273 }
@@ -289,25 +277,17 @@ class DialogLogFragment : DialogFragment() {
289277 super .onResume()
290278 dialog?.setOnKeyListener { _, keyCode, event ->
291279 if (! uiVisible || ! running) return @setOnKeyListener false
292- event.action == KeyEvent .ACTION_DOWN &&
293- (keyCode == KeyEvent .KEYCODE_VOLUME_UP || keyCode == KeyEvent .KEYCODE_VOLUME_DOWN )
280+ event.action == KeyEvent .ACTION_DOWN && (keyCode == KeyEvent .KEYCODE_VOLUME_UP || keyCode == KeyEvent .KEYCODE_VOLUME_DOWN )
294281 }
295282 }
296283
297- private fun offScreen () {
298- dialog?.window?.clearFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
299- }
284+ private fun offScreen () = dialog?.window?.clearFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
300285
301- private fun closeView () {
302- try {
303- dismissAllowingStateLoss()
304- } catch (ex: Exception ) {}
305- }
286+ private fun closeView () { try { dismissAllowingStateLoss() } catch (ex: Exception ) {} }
306287
307288 override fun onDismiss (dialog : DialogInterface ) {
308289 super .onDismiss(dialog)
309290 onDismissRunnable?.run ()
310- onDismissRunnable = null
311291 }
312292
313293 override fun onDestroyView () {
@@ -323,14 +303,7 @@ class DialogLogFragment : DialogFragment() {
323303 }
324304
325305 companion object {
326- fun create (
327- nodeInfo : RunnableNode ,
328- onExit : Runnable ,
329- onDismiss : Runnable ,
330- script : String ,
331- params : HashMap <String , String >? ,
332- darkMode : Boolean = false
333- ): DialogLogFragment {
306+ fun create (nodeInfo : RunnableNode , onExit : Runnable , onDismiss : Runnable , script : String , params : HashMap <String , String >? , darkMode : Boolean = false): DialogLogFragment {
334307 val fragment = DialogLogFragment ()
335308 fragment.nodeInfo = nodeInfo
336309 fragment.onExit = onExit
0 commit comments