@@ -11,158 +11,176 @@ import com.omarea.common.ui.AdapterAppChooser
1111import com.omarea.common.ui.DialogAppChooser
1212import com.omarea.krscript.R
1313import com.omarea.krscript.model.ActionParamInfo
14- import java.util.Locale
1514import 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