@@ -30,14 +30,17 @@ class ParamsAppChooserRender(
3030 private lateinit var nameView: TextView
3131 private lateinit var packages: ArrayList <AdapterAppChooser .AppInfo >
3232
33+ // collator dùng chung cho toàn bộ quá trình sort
34+ private val collator: Collator = Collator .getInstance(Locale .getDefault())
35+
3336 fun render (): View {
3437 val layout = LayoutInflater .from(context)
3538 .inflate(R .layout.kr_param_app, null )
3639
3740 valueView = layout.findViewById(R .id.kr_param_app_package)
3841 nameView = layout.findViewById(R .id.kr_param_app_name)
3942
40- // ✅ GIỮ HÀNH VI CŨ: KHÔNG LOAD PACKAGE Ở ĐÂY
43+ // giữ hành vi cũ: không load package ở đây
4144 setTextView()
4245
4346 layout.findViewById<View >(R .id.kr_param_app_btn).setOnClickListener {
@@ -52,7 +55,7 @@ class ParamsAppChooserRender(
5255 }
5356
5457 // =======================
55- // OPEN DIALOG (KHÔNG BLOCK UI)
58+ // OPEN DIALOG
5659 // =======================
5760 private fun openAppChooser () {
5861 packages = ArrayList ()
@@ -64,17 +67,32 @@ class ParamsAppChooserRender(
6467 this
6568 )
6669
67- // ✅ SHOW NGAY
6870 dialog.show(context.supportFragmentManager, " app-chooser" )
69-
7071 dialog.showLoading(true )
7172
72- // ✅ LOAD SAU KHI DIALOG ĐÃ HIỆN
7373 loadPackagesAsync(dialog, actionParamInfo.type == " packages" )
7474 }
7575
7676 // =======================
77- // LOAD PACKAGE ASYNC + BATCH
77+ // SORTED INSERT
78+ // =======================
79+ private fun insertSorted (
80+ list : MutableList <AdapterAppChooser .AppInfo >,
81+ item : AdapterAppChooser .AppInfo
82+ ) {
83+ val index = list.binarySearch(item) { a, b ->
84+ collator.compare(a.appName ? : " " , b.appName ? : " " )
85+ }
86+
87+ if (index < 0 ) {
88+ list.add(- index - 1 , item)
89+ } else {
90+ list.add(index, item)
91+ }
92+ }
93+
94+ // =======================
95+ // LOAD PACKAGE ASYNC + SORT TRONG LÚC LOAD
7896 // =======================
7997 private fun loadPackagesAsync (
8098 dialog : DialogAppChooser ,
@@ -97,27 +115,29 @@ class ParamsAppChooserRender(
97115
98116 if (filterSet == null || filterSet.contains(pkg)) {
99117 val info = AdapterAppChooser .AppInfo ().apply {
100- packageName = pkg // GIỮ NULL
101- appName = app.loadLabel(pm)?.toString() // GIỮ NULL
118+ packageName = pkg
119+ appName = app.loadLabel(pm)?.toString()
102120 }
103121 result[pkg] = info
104122 batch.add(info)
105123 }
106124
107- // đổ dần
125+ // đổ batch
108126 if (batch.size == 10 || index == apps.lastIndex) {
109127 val copy = ArrayList (batch)
110128 batch.clear()
111129
112130 withContext(Dispatchers .Main ) {
113- packages.addAll(copy)
131+ for (info in copy) {
132+ insertSorted(packages, info)
133+ }
114134 setSelectStatus()
115135 dialog.notifyDataChanged()
116136 }
117137 }
118138 }
119139
120- // thêm app bị thiếu (giữ hành vi cũ)
140+ // thêm app thiếu (giữ hành vi cũ)
121141 if (includeMissing && actionParamInfo.optionsFromShell != null ) {
122142 val missing = ArrayList <AdapterAppChooser .AppInfo >()
123143 for (item in actionParamInfo.optionsFromShell!! ) {
@@ -126,22 +146,22 @@ class ParamsAppChooserRender(
126146 missing.add(
127147 AdapterAppChooser .AppInfo ().apply {
128148 packageName = pkg
129- appName = item.title // có thể null
149+ appName = item.title
130150 }
131151 )
132152 }
133153 }
154+
134155 withContext(Dispatchers .Main ) {
135- packages.addAll(missing)
156+ for (info in missing) {
157+ insertSorted(packages, info)
158+ }
159+ dialog.notifyDataChanged()
136160 }
137161 }
138162
139- // sort cuối (giữ hành vi cũ)
163+ // kết thúc load
140164 withContext(Dispatchers .Main ) {
141- val collator = Collator .getInstance(Locale .getDefault())
142- packages.sortWith { a, b ->
143- collator.compare(a.appName ? : " " , b.appName ? : " " )
144- }
145165 setSelectStatus()
146166 dialog.notifyDataChanged()
147167 dialog.showLoading(false )
@@ -150,7 +170,7 @@ class ParamsAppChooserRender(
150170 }
151171
152172 // =======================
153- // SELECTION LOGIC (GIỮ NGUYÊN)
173+ // SELECTION LOGIC
154174 // =======================
155175 private fun setSelectStatus () {
156176 packages.forEach { it.selected = false }
@@ -169,7 +189,7 @@ class ParamsAppChooserRender(
169189 }
170190
171191 // =======================
172- // INIT UI VALUE (GIỮ NGUYÊN HÀNH VI CŨ )
192+ // INIT UI VALUE (GIỮ NGUYÊN)
173193 // =======================
174194 private fun setTextView () {
175195 if (actionParamInfo.multiple) {
@@ -185,35 +205,12 @@ class ParamsAppChooserRender(
185205 .getParamValues(actionParamInfo)
186206 ?.firstOrNull()
187207 ? : " "
188-
208+
189209 valueView.text = value
190210 nameView.text = value
191211 }
192212 }
193213
194- // =======================
195- // HÀM CŨ – KHÔNG DÙNG (TRÁNH BLOCK UI)
196- // =======================
197- @Deprecated(
198- message = " DO NOT USE - blocks UI thread. Use loadPackagesAsync instead" ,
199- level = DeprecationLevel .WARNING
200- )
201- private fun loadPackages (includeMissing : Boolean ): ArrayList <AdapterAppChooser .AppInfo > {
202- val pm = context.packageManager
203- val list = ArrayList <AdapterAppChooser .AppInfo >()
204-
205- val apps = pm.getInstalledApplications(PackageManager .MATCH_ALL )
206- for (app in apps) {
207- list.add(
208- AdapterAppChooser .AppInfo ().apply {
209- packageName = app.packageName
210- appName = app.loadLabel(pm)?.toString()
211- }
212- )
213- }
214- return list
215- }
216-
217214 // =======================
218215 // CALLBACK
219216 // =======================
0 commit comments