Skip to content

Commit d11cd47

Browse files
committed
Update
1 parent f531c5b commit d11cd47

1 file changed

Lines changed: 134 additions & 71 deletions

File tree

app/src/main/java/com/tool/tree/ActionPage.kt

Lines changed: 134 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,35 @@ import com.omarea.krscript.ui.ParamsFileChooserRender
2929
import com.omarea.krscript.ui.PageMenuLoader
3030
import com.tool.tree.databinding.ActivityActionPageBinding
3131
import kotlinx.coroutines.Dispatchers
32+
import kotlinx.coroutines.Job
33+
import kotlinx.coroutines.async
34+
import kotlinx.coroutines.awaitAll
35+
import kotlinx.coroutines.coroutineScope
3236
import kotlinx.coroutines.isActive
3337
import kotlinx.coroutines.launch
3438
import kotlinx.coroutines.withContext
35-
import kotlinx.coroutines.async
36-
import kotlinx.coroutines.awaitAll
3739

3840
class ActionPage : AppCompatActivity() {
3941
private val progressBarDialog by lazy { ProgressBarDialog(this) }
4042
private var actionsLoaded = false
4143
private val handler = Handler(Looper.getMainLooper())
42-
44+
4345
private var currentPageConfig: PageNode? = null
4446
private var autoRunItemId = ""
4547
private lateinit var binding: ActivityActionPageBinding
4648
private var openedSubPage = false
4749

48-
// Sử dụng HashCode của String ID để khóa trạng thái tạm thời, không sợ lệch index
50+
// Khóa theo ID thật của item để không lệ thuộc vào vị trí trong mảng
4951
private val justClickedItemIds = HashSet<Int>()
5052

53+
private var fileSelectedInterface: ParamsFileChooserRender.FileSelectedInterface? = null
54+
private val ACTION_FILE_PATH_CHOOSER = 65400
55+
private val ACTION_FILE_PATH_CHOOSER_INNER = 65300
56+
57+
private var menuOptions: ArrayList<PageMenuOption>? = null
58+
private var menuCheckboxRefreshing = false
59+
private var checkboxRefreshJob: Job? = null
60+
5161
override fun onCreate(savedInstanceState: Bundle?) {
5262
super.onCreate(savedInstanceState)
5363

@@ -83,7 +93,7 @@ class ActionPage : AppCompatActivity() {
8393
} else if (extras.containsKey("shortcutId")) {
8494
ActionShortcutManager(this).getShortcutTarget(extras.getString("shortcutId") ?: "")
8595
} else null
86-
96+
8797
autoRunItemId = extras.getString("autoRunItemId", "")
8898
}
8999

@@ -149,13 +159,6 @@ class ActionPage : AppCompatActivity() {
149159
}
150160
}
151161

152-
private var fileSelectedInterface: ParamsFileChooserRender.FileSelectedInterface? = null
153-
private val ACTION_FILE_PATH_CHOOSER = 65400
154-
private val ACTION_FILE_PATH_CHOOSER_INNER = 65300
155-
156-
private var menuOptions: ArrayList<PageMenuOption>? = null
157-
private var menuCheckboxRefreshing = false
158-
159162
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
160163
val config = currentPageConfig ?: return false
161164
if (menuOptions == null) {
@@ -168,7 +171,6 @@ class ActionPage : AppCompatActivity() {
168171
if (option.isFab) {
169172
addFab(option)
170173
} else {
171-
// ĐỔI: Sử dụng hashCode của chuỗi ID duy nhất (option.key hoặc option.id) làm itemId cho Menu
172174
val uniqueItemId = option.key.hashCode()
173175
val menuItem = menu?.add(Menu.NONE, uniqueItemId, Menu.NONE, option.title)
174176
if (option.type == "checkbox") {
@@ -180,58 +182,72 @@ class ActionPage : AppCompatActivity() {
180182
}
181183

182184
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
183-
// Tìm và gán chính xác trạng thái checkbox theo uniqueId
185+
// Chỉ cập nhật trạng thái hiển thị từ dữ liệu hiện có, không chạy shell ở đây
184186
menuOptions?.forEach { option ->
185187
if (!option.isFab && option.type == "checkbox") {
186188
val uniqueItemId = option.key.hashCode()
187189
val menuItem = menu.findItem(uniqueItemId)
188190
menuItem?.isChecked = option.checked
189191
}
190192
}
193+
194+
// Vẫn giữ hành vi cũ: menu sẵn sàng thì refresh checkbox
191195
refreshCheckboxMenuStates()
192196
return super.onPrepareOptionsMenu(menu)
193197
}
194198

195199
private fun refreshCheckboxMenuStates() {
196200
val config = currentPageConfig ?: return
197-
198-
// Lọc danh sách các checkbox hợp lệ
199-
val checkboxOptions = menuOptions?.filter { option ->
201+
202+
val checkboxOptions = menuOptions?.filter { option ->
200203
val uniqueItemId = option.key.hashCode()
201-
option.type == "checkbox" && option.checkedSh.isNotEmpty() && !justClickedItemIds.contains(uniqueItemId)
202-
}
203-
204-
if (checkboxOptions.isNullOrEmpty() || menuCheckboxRefreshing) return
204+
option.type == "checkbox" &&
205+
option.checkedSh.isNotEmpty() &&
206+
!justClickedItemIds.contains(uniqueItemId)
207+
}.orEmpty()
208+
209+
if (checkboxOptions.isEmpty() || menuCheckboxRefreshing) return
210+
205211
menuCheckboxRefreshing = true
206-
207-
lifecycleScope.launch(Dispatchers.IO) {
208-
// TỐI ƯU SONG SONG: Sử dụng async để phát TẤT CẢ các lệnh getprop đi CÙNG MỘT LÚC
209-
val deferredResults = checkboxOptions.map { option ->
210-
async {
211-
val result = ScriptEnvironmen.executeResultRoot(this@ActionPage, option.checkedSh, config)?.trim()
212-
option to result
212+
checkboxRefreshJob?.cancel()
213+
214+
checkboxRefreshJob = lifecycleScope.launch(Dispatchers.IO) {
215+
try {
216+
// Chạy song song thay vì chạy nối tiếp từng item
217+
val results = coroutineScope {
218+
checkboxOptions.map { option ->
219+
async {
220+
val result = ScriptEnvironmen.executeResultRoot(
221+
this@ActionPage,
222+
option.checkedSh,
223+
config
224+
)?.trim()
225+
option to result
226+
}
227+
}.awaitAll()
213228
}
214-
}
215-
216-
// Đợi tất cả các ô trả về kết quả đầy đủ rồi mới xử lý tiếp
217-
val results = deferredResults.awaitAll()
218-
219-
withContext(Dispatchers.Main) {
220-
menuCheckboxRefreshing = false
221-
var changed = false
222-
223-
results.forEach { (option, result) ->
224-
val uniqueItemId = option.key.hashCode()
225-
if (!justClickedItemIds.contains(uniqueItemId)) {
226-
val newChecked = result == "1" || result?.lowercase() == "true"
227-
if (option.checked != newChecked) changed = true
228-
option.checked = newChecked
229+
230+
withContext(Dispatchers.Main) {
231+
if (isFinishing || isDestroyed) return@withContext
232+
233+
var changed = false
234+
results.forEach { (option, result) ->
235+
val uniqueItemId = option.key.hashCode()
236+
237+
if (!justClickedItemIds.contains(uniqueItemId)) {
238+
val newChecked = result == "1" || result.equals("true", ignoreCase = true)
239+
if (option.checked != newChecked) changed = true
240+
option.checked = newChecked
241+
}
242+
}
243+
244+
if (changed) {
245+
invalidateOptionsMenu()
229246
}
230247
}
231-
232-
// Chỉ vẽ lại và kích hoạt luồng reload khi TẤT CẢ dữ liệu đã sẵn sàng
233-
if (changed && !isFinishing) {
234-
invalidateOptionsMenu()
248+
} finally {
249+
withContext(Dispatchers.Main) {
250+
menuCheckboxRefreshing = false
235251
}
236252
}
237253
}
@@ -242,26 +258,29 @@ class ActionPage : AppCompatActivity() {
242258
visibility = View.VISIBLE
243259
setOnClickListener { onMenuItemClick(menuOption) }
244260

245-
val iconRes = if (menuOption.type == "file" && menuOption.iconPath.isEmpty()) R.drawable.kr_folder else R.drawable.kr_fab
246-
val customIcon = if (menuOption.iconPath.isNotEmpty()) IconPathAnalysis().loadLogo(context, menuOption, false) else null
247-
261+
val iconRes = if (menuOption.type == "file" && menuOption.iconPath.isEmpty()) {
262+
R.drawable.kr_folder
263+
} else {
264+
R.drawable.kr_fab
265+
}
266+
val customIcon = if (menuOption.iconPath.isNotEmpty()) {
267+
IconPathAnalysis().loadLogo(context, menuOption, false)
268+
} else null
269+
248270
setImageDrawable(customIcon ?: ContextCompat.getDrawable(context, iconRes))
249271
}
250272
}
251273

252274
override fun onOptionsItemSelected(item: MenuItem): Boolean {
253275
val options = menuOptions ?: return false
254-
val targetItemId = item.itemId // Đây chính là hashCode của checkbox vừa click
255-
256-
// ĐỔI: Tìm option khớp với hashCode của ID thay vì dùng vị trí mảng index
276+
val targetItemId = item.itemId
257277
val option = options.find { it.key.hashCode() == targetItemId }
258-
278+
259279
if (option != null) {
260280
if (option.type == "checkbox") {
261281
option.checked = !option.checked
262282
item.isChecked = option.checked
263283

264-
// Khóa đồng bộ theo ID cụ thể
265284
justClickedItemIds.add(targetItemId)
266285
handler.postDelayed({
267286
justClickedItemIds.remove(targetItemId)
@@ -277,12 +296,13 @@ class ActionPage : AppCompatActivity() {
277296
private fun onMenuItemClick(menuOption: PageMenuOption) {
278297
if (menuOption.link.isNotEmpty() || menuOption.activity.isNotEmpty() ||
279298
menuOption.onlineHtmlPage.isNotEmpty() || menuOption.pageConfigSh.isNotEmpty() ||
280-
menuOption.pageConfigPath.isNotEmpty()) {
299+
menuOption.pageConfigPath.isNotEmpty()
300+
) {
281301
openMenuOptionAsPage(menuOption)
282302
return
283303
}
284304

285-
when(menuOption.type) {
305+
when (menuOption.type) {
286306
"refresh", "reload" -> recreate()
287307
"restart" -> restartApp()
288308
"exit", "finish", "close" -> finish()
@@ -337,7 +357,7 @@ class ActionPage : AppCompatActivity() {
337357
if (!isActive) return@launch
338358

339359
withContext(Dispatchers.Main) {
340-
if (isFinishing) return@withContext
360+
if (isFinishing || isDestroyed) return@withContext
341361
when {
342362
menuOption.autoFinish -> finish()
343363
menuOption.reloadPage -> recreate()
@@ -356,12 +376,16 @@ class ActionPage : AppCompatActivity() {
356376

357377
lifecycleScope.launch(Dispatchers.IO) {
358378
if (config.beforeRead.isNotEmpty()) {
359-
withContext(Dispatchers.Main) { progressBarDialog.showDialog(getString(R.string.kr_page_before_load)) }
379+
withContext(Dispatchers.Main) {
380+
progressBarDialog.showDialog(getString(R.string.kr_page_before_load))
381+
}
360382
ScriptEnvironmen.executeResultRoot(this@ActionPage, config.beforeRead, config)
361383
}
362384

363385
if (showLoading) {
364-
withContext(Dispatchers.Main) { progressBarDialog.showDialog(getString(R.string.kr_page_loading)) }
386+
withContext(Dispatchers.Main) {
387+
progressBarDialog.showDialog(getString(R.string.kr_page_loading))
388+
}
365389
}
366390

367391
var items: ArrayList<NodeInfoBase>? = null
@@ -378,12 +402,13 @@ class ActionPage : AppCompatActivity() {
378402

379403
withContext(Dispatchers.Main) {
380404
if (!isActive || isFinishing) return@withContext
381-
405+
382406
if (items != null && items.isNotEmpty()) {
383407
if (config.loadSuccess.isNotEmpty()) {
384408
ScriptEnvironmen.executeResultRoot(this@ActionPage, config.loadSuccess, config)
385409
}
386410
updateActionList(items, showLoading)
411+
refreshCheckboxMenuStates()
387412
} else {
388413
handleLoadError(config)
389414
}
@@ -432,7 +457,9 @@ class ActionPage : AppCompatActivity() {
432457
}
433458

434459
private fun killApp() {
435-
startService(Intent(this, WakeLockService::class.java).apply { action = WakeLockService.ACTION_END_WAKELOCK })
460+
startService(Intent(this, WakeLockService::class.java).apply {
461+
action = WakeLockService.ACTION_END_WAKELOCK
462+
})
436463
finishAffinity()
437464
System.exit(0)
438465
}
@@ -445,9 +472,21 @@ class ActionPage : AppCompatActivity() {
445472
menuOption.autoKill -> killApp()
446473
menuOption.autoRestart -> restartApp()
447474
}
475+
476+
if (menuOption.type == "checkbox") {
477+
refreshCheckboxMenuStates()
478+
}
448479
}
480+
449481
val config = currentPageConfig ?: return
450-
val dialog = DialogLogFragment.create(menuOption, {}, onDismiss, config.pageHandlerSh, params, ThemeModeState.getThemeMode().isDarkMode)
482+
val dialog = DialogLogFragment.create(
483+
menuOption,
484+
{},
485+
onDismiss,
486+
config.pageHandlerSh,
487+
params,
488+
ThemeModeState.getThemeMode().isDarkMode
489+
)
451490
dialog.show(supportFragmentManager, "")
452491
dialog.isCancelable = false
453492
}
@@ -457,27 +496,48 @@ class ActionPage : AppCompatActivity() {
457496
override fun onFileSelected(path: String?) {
458497
path?.let {
459498
handler.post {
460-
menuItemExecute(menuOption, hashMapOf("state" to menuOption.key, "menu_id" to menuOption.key, "file" to it, "folder" to it))
499+
menuItemExecute(
500+
menuOption,
501+
hashMapOf(
502+
"state" to menuOption.key,
503+
"menu_id" to menuOption.key,
504+
"file" to it,
505+
"folder" to it
506+
)
507+
)
461508
}
462509
}
463510
}
511+
464512
override fun mimeType() = menuOption.mime.ifEmpty { null }
465513
override fun suffix() = menuOption.suffix.ifEmpty { null }
466-
override fun type() = if (menuOption.type == "folder") ParamsFileChooserRender.FileSelectedInterface.TYPE_FOLDER else ParamsFileChooserRender.FileSelectedInterface.TYPE_FILE
514+
override fun type() = if (menuOption.type == "folder") {
515+
ParamsFileChooserRender.FileSelectedInterface.TYPE_FOLDER
516+
} else {
517+
ParamsFileChooserRender.FileSelectedInterface.TYPE_FILE
518+
}
467519
})
468520
}
469521

470522
private fun chooseFilePath(fileSelectedInterface: ParamsFileChooserRender.FileSelectedInterface): Boolean {
471523
return try {
472524
if (fileSelectedInterface.type() == ParamsFileChooserRender.FileSelectedInterface.TYPE_FOLDER) {
473-
startActivityForResult(Intent(this, ActivityFileSelector::class.java).apply { putExtra("mode", ActivityFileSelector.MODE_FOLDER) }, ACTION_FILE_PATH_CHOOSER_INNER)
525+
startActivityForResult(
526+
Intent(this, ActivityFileSelector::class.java).apply {
527+
putExtra("mode", ActivityFileSelector.MODE_FOLDER)
528+
},
529+
ACTION_FILE_PATH_CHOOSER_INNER
530+
)
474531
} else {
475532
val suffix = fileSelectedInterface.suffix()
476533
if (!suffix.isNullOrEmpty()) {
477-
startActivityForResult(Intent(this, ActivityFileSelector::class.java).apply {
478-
putExtra("extension", suffix)
479-
putExtra("mode", ActivityFileSelector.MODE_FILE)
480-
}, ACTION_FILE_PATH_CHOOSER_INNER)
534+
startActivityForResult(
535+
Intent(this, ActivityFileSelector::class.java).apply {
536+
putExtra("extension", suffix)
537+
putExtra("mode", ActivityFileSelector.MODE_FILE)
538+
},
539+
ACTION_FILE_PATH_CHOOSER_INNER
540+
)
481541
} else {
482542
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
483543
type = fileSelectedInterface.mimeType() ?: "*/*"
@@ -488,7 +548,9 @@ class ActionPage : AppCompatActivity() {
488548
}
489549
this.fileSelectedInterface = fileSelectedInterface
490550
true
491-
} catch (_: Exception) { false }
551+
} catch (_: Exception) {
552+
false
553+
}
492554
}
493555

494556
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -524,6 +586,7 @@ class ActionPage : AppCompatActivity() {
524586
}
525587

526588
override fun onDestroy() {
589+
checkboxRefreshJob?.cancel()
527590
handler.removeCallbacksAndMessages(null)
528591
setExcludeFromRecents()
529592
super.onDestroy()

0 commit comments

Comments
 (0)