Skip to content

Commit a7a5c3e

Browse files
committed
Upload file
1 parent cf72d41 commit a7a5c3e

1 file changed

Lines changed: 113 additions & 95 deletions

File tree

krscript/src/main/java/com/omarea/krscript/ui/ParamsAppChooserRender.kt

Lines changed: 113 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -11,158 +11,176 @@ import com.omarea.common.ui.AdapterAppChooser
1111
import com.omarea.common.ui.DialogAppChooser
1212
import com.omarea.krscript.R
1313
import com.omarea.krscript.model.ActionParamInfo
14-
import java.util.Locale
1514
import java.text.Collator
15+
import java.util.Locale
16+
import java.util.HashMap
17+
import java.util.HashSet
18+
import java.util.ArrayList
19+
20+
class ParamsAppChooserRender(
21+
private var actionParamInfo: ActionParamInfo,
22+
private var context: FragmentActivity
23+
) : DialogAppChooser.Callback {
24+
25+
companion object {
26+
// Cache danh sách app để tránh load lại nhiều lần
27+
private var cachedApps: List<AdapterAppChooser.AppInfo>? = null
28+
private var cachedAppsWithMissing: List<AdapterAppChooser.AppInfo>? = null
29+
}
1630

17-
class ParamsAppChooserRender(private var actionParamInfo: ActionParamInfo, private var context: FragmentActivity) : DialogAppChooser.Callback {
18-
// Sử dụng Configuration để xác định chế độ sáng/tối
1931
private val uiMode = context.resources.configuration.uiMode
20-
private var darkMode: Boolean = (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
32+
private val darkMode =
33+
(uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
2134

2235
private lateinit var valueView: TextView
2336
private lateinit var nameView: TextView
2437
private lateinit var packages: ArrayList<AdapterAppChooser.AppInfo>
2538

2639
fun render(): View {
27-
val layout = LayoutInflater.from(context).inflate(R.layout.kr_param_app, null)
40+
val layout = LayoutInflater.from(context)
41+
.inflate(R.layout.kr_param_app, null)
42+
2843
valueView = layout.findViewById(R.id.kr_param_app_package)
2944
nameView = layout.findViewById(R.id.kr_param_app_name)
3045

3146
setTextView()
3247

33-
layout.findViewById<View>(R.id.kr_param_app_btn).setOnClickListener {
34-
openAppChooser()
35-
}
36-
nameView.setOnClickListener {
37-
openAppChooser()
38-
}
48+
layout.findViewById<View>(R.id.kr_param_app_btn)
49+
.setOnClickListener { openAppChooser() }
3950

40-
valueView.tag = actionParamInfo.name
51+
nameView.setOnClickListener { openAppChooser() }
4152

53+
valueView.tag = actionParamInfo.name
4254
return layout
4355
}
4456

4557
private fun openAppChooser() {
4658
setSelectStatus()
47-
48-
// Gọi DialogAppChooser với chế độ tối/sáng
49-
DialogAppChooser(darkMode, packages, actionParamInfo.multiple, this).show(context.supportFragmentManager, "app-chooser")
59+
DialogAppChooser(
60+
darkMode,
61+
packages,
62+
actionParamInfo.multiple,
63+
this
64+
).show(context.supportFragmentManager, "app-chooser")
5065
}
5166

52-
private fun loadPackages(includeMissing: Boolean = false): List<AdapterAppChooser.AppInfo> {
67+
/**
68+
* Load & cache danh sách app (tối ưu PM + tránh O(n²))
69+
*/
70+
private fun loadPackages(includeMissing: Boolean): List<AdapterAppChooser.AppInfo> {
71+
if (!includeMissing && cachedApps != null) return cachedApps!!
72+
if (includeMissing && cachedAppsWithMissing != null) return cachedAppsWithMissing!!
73+
5374
val pm = context.packageManager
54-
val filter = actionParamInfo.optionsFromShell?.map {
55-
it.value
56-
}
75+
val filterSet = actionParamInfo.optionsFromShell
76+
?.mapTo(HashSet()) { it.value }
5777

58-
val packages = pm.getInstalledPackages(0).filter {
59-
filter == null || filter.contains(it.packageName)
60-
}
78+
val appMap = HashMap<String, AdapterAppChooser.AppInfo>(128)
6179

62-
val options = ArrayList(packages.map {
63-
AdapterAppChooser.AppInfo().apply {
64-
appName = "" + it.applicationInfo?.loadLabel(pm)
65-
packageName = it.packageName
80+
pm.getInstalledApplications(PackageManager.MATCH_ALL).forEach { app ->
81+
val pkg = app.packageName
82+
if (filterSet == null || filterSet.contains(pkg)) {
83+
appMap[pkg] = AdapterAppChooser.AppInfo().apply {
84+
packageName = pkg
85+
appName = app.loadLabel(pm).toString()
86+
}
6687
}
67-
})
88+
}
6889

69-
// 是否包含丢失的应用程序
90+
// include missing packages
7091
if (includeMissing && actionParamInfo.optionsFromShell != null) {
7192
for (item in actionParamInfo.optionsFromShell!!) {
72-
if (options.none { it.packageName == item.value }) {
73-
options.add(AdapterAppChooser.AppInfo().apply {
74-
appName = "" + item.title
75-
packageName = "" + item.value
76-
})
93+
if (!appMap.containsKey(item.value)) {
94+
appMap[item.value] = AdapterAppChooser.AppInfo().apply {
95+
packageName = item.value
96+
appName = item.title
97+
}
7798
}
7899
}
79100
}
80101

81-
return options
102+
val collator = Collator.getInstance(Locale.getDefault())
103+
val result = appMap.values.sortedWith { a, b ->
104+
collator.compare(a.appName, b.appName)
105+
}
106+
107+
if (includeMissing) {
108+
cachedAppsWithMissing = result
109+
} else {
110+
cachedApps = result
111+
}
112+
return result
82113
}
83114

115+
/**
116+
* Đặt trạng thái selected (O(n))
117+
*/
84118
private fun setSelectStatus() {
85-
packages.forEach {
86-
it.selected = false
87-
}
88-
val currentValue = valueView.text
119+
val map = packages.associateBy { it.packageName }
120+
packages.forEach { it.selected = false }
121+
89122
if (actionParamInfo.multiple) {
90-
currentValue.split(actionParamInfo.separator).run {
91-
this.forEach {
92-
val value = it
93-
val app = packages.find { it.packageName == value }
94-
if (app != null) {
95-
app.selected = true
96-
}
97-
}
98-
}
123+
valueView.text
124+
.split(actionParamInfo.separator)
125+
.forEach { map[it]?.selected = true }
99126
} else {
100-
val current = packages.find { it.packageName == currentValue }
101-
val currentIndex = if (current != null) packages.indexOf(current) else -1
102-
if (currentIndex > -1) {
103-
packages[currentIndex].selected = true
104-
}
127+
map[valueView.text]?.selected = true
105128
}
106129
}
107130

108-
// 设置界面显示和元素赋值
131+
/**
132+
* Gán dữ liệu hiển thị ban đầu
133+
*/
109134
private fun setTextView() {
110135
packages = ArrayList(loadPackages(actionParamInfo.type == "packages"))
111-
// Sắp xếp tên app chuẩn theo locale (đa ngôn ngữ)
112-
val collator = Collator.getInstance(Locale.getDefault())
113-
packages.sortWith { a, b -> collator.compare(a.appName, b.appName) }
114-
115-
packages.run {
116-
val labels = map { it.appName }.toTypedArray()
117-
val values = map { it.packageName }.toTypedArray()
118-
if (actionParamInfo.multiple) {
119-
ActionParamsLayoutRender.getParamValues(actionParamInfo)?.run {
120-
this.forEach {
121-
val value = it
122-
val app = packages.find { it.packageName == value }
123-
if (app != null) {
124-
app.selected = true
125-
}
126-
}
127-
}
128136

129-
onConfirm((packages.filter { it.selected }))
130-
} else {
131-
// TODO: 这里有过多的数据包装盒解包,需要进行优化
132-
val validOptions = ArrayList(packages.map {
137+
if (actionParamInfo.multiple) {
138+
ActionParamsLayoutRender.getParamValues(actionParamInfo)
139+
?.forEach { value ->
140+
packages.firstOrNull { it.packageName == value }?.selected = true
141+
}
142+
onConfirm(packages.filter { it.selected })
143+
} else {
144+
val validOptions = ArrayList<SelectItem>(packages.size)
145+
packages.forEach {
146+
validOptions.add(
133147
SelectItem().apply {
134148
title = it.appName
135149
value = it.packageName
136150
}
137-
}.toList())
138-
139-
val currentIndex = ActionParamsLayoutRender.getParamOptionsCurrentIndex(actionParamInfo, validOptions)
140-
if (currentIndex > -1) {
141-
valueView.text = values[currentIndex]
142-
nameView.text = labels[currentIndex]
143-
} else {
144-
valueView.text = ""
145-
nameView.text = ""
146-
}
151+
)
152+
}
153+
154+
val currentIndex =
155+
ActionParamsLayoutRender.getParamOptionsCurrentIndex(
156+
actionParamInfo,
157+
validOptions
158+
)
159+
160+
if (currentIndex >= 0) {
161+
val item = packages[currentIndex]
162+
valueView.text = item.packageName
163+
nameView.text = item.appName
164+
} else {
165+
valueView.text = ""
166+
nameView.text = ""
147167
}
148168
}
149169
}
150170

171+
/**
172+
* Callback từ DialogAppChooser
173+
*/
151174
override fun onConfirm(apps: List<AdapterAppChooser.AppInfo>) {
152175
if (actionParamInfo.multiple) {
153-
val values = apps.joinToString(actionParamInfo.separator) { it.packageName }
154-
val labels = apps.joinToString("") { it.appName }
155-
valueView.text = values
156-
nameView.text = labels
176+
valueView.text =
177+
apps.joinToString(actionParamInfo.separator) { it.packageName }
178+
nameView.text =
179+
apps.joinToString("") { it.appName }
157180
} else {
158181
val item = apps.firstOrNull()
159-
if (item == null) {
160-
valueView.text = ""
161-
nameView.text = ""
162-
} else {
163-
valueView.text = item.packageName
164-
nameView.text = item.appName
165-
}
182+
valueView.text = item?.packageName.orEmpty()
183+
nameView.text = item?.appName.orEmpty()
166184
}
167185
}
168186
}

0 commit comments

Comments
 (0)