@@ -27,17 +27,25 @@ class AdapterAppChooser(
2727 var selected: Boolean = false
2828 }
2929
30+ // ================= Coroutine scope (THAY GlobalScope) =================
31+
32+ private val job = SupervisorJob ()
33+ private val scope = CoroutineScope (Dispatchers .Main + job)
34+
35+ // ======================================================================
36+
3037 private var selectStateListener: SelectStateListener ? = null
3138 private var filter: Filter ? = null
3239
3340 internal var filterApps: ArrayList <AppInfo > = apps
3441 private val mLock = Any ()
3542
36- private class ArrayFilter ( private val adapter : AdapterAppChooser ) : Filter() {
43+ // ================= Filter =================
3744
45+ private class ArrayFilter (private val adapter : AdapterAppChooser ) : Filter() {
3846 override fun performFiltering (constraint : CharSequence? ): FilterResults {
3947 val results = FilterResults ()
40- val prefix = constraint?.toString()?.lowercase() ? : " "
48+ val prefix = constraint?.toString()?.lowercase(Locale .getDefault() ) ? : " "
4149
4250 if (prefix.isEmpty()) {
4351 synchronized(adapter.mLock) {
@@ -56,8 +64,8 @@ class AdapterAppChooser(
5664 continue
5765 }
5866
59- val name = item.appName?.lowercase() ? : " "
60- val pkg = item.packageName?.lowercase() ? : " "
67+ val name = item.appName?.lowercase(Locale .getDefault() ) ? : " "
68+ val pkg = item.packageName?.lowercase(Locale .getDefault() ) ? : " "
6169
6270 if (name.contains(prefix) || pkg.contains(prefix)) {
6371 newValues.add(item)
@@ -83,8 +91,12 @@ class AdapterAppChooser(
8391 return filter!!
8492 }
8593
94+ // ================= Icon cache =================
95+
8696 private val iconCaches = LruCache <String , Drawable >(100 )
8797
98+ // ================= Adapter basic =================
99+
88100 override fun getCount (): Int = filterApps.size
89101
90102 override fun getItem (position : Int ): AppInfo = filterApps[position]
@@ -103,31 +115,37 @@ class AdapterAppChooser(
103115 return convertView
104116 }
105117
118+ // ================= Icon loading =================
119+
106120 private fun loadIcon (app : AppInfo ): Deferred <Drawable ?> {
107- return GlobalScope .async(Dispatchers .IO ) {
121+ return scope .async(Dispatchers .IO ) {
108122 val pkg = app.packageName ? : return @async null
109- val cached = iconCaches.get(pkg)
110- if (cached != null ) return @async cached
123+
124+ iconCaches.get(pkg)?. let { return @async it }
111125
112126 if (! app.notFound) {
113127 try {
114128 val info = context.packageManager.getPackageInfo(pkg, 0 )
115129 val icon = info.applicationInfo?.loadIcon(context.packageManager)
116- if (icon != null ) iconCaches.put(pkg, icon)
130+ if (icon != null ) {
131+ iconCaches.put(pkg, icon)
132+ return @async icon
133+ }
117134 } catch (_: Exception ) {
118135 app.notFound = true
119136 }
120137 }
121- iconCaches.get(pkg)
138+ null
122139 }
123140 }
124141
142+ // ================= Row binding =================
143+
125144 fun updateRow (position : Int , convertView : View ) {
126145 val item = getItem(position)
127146
128- val holder = (convertView.tag as ? ViewHolder ) ? : ViewHolder (convertView).also {
129- convertView.tag = it
130- }
147+ val holder = (convertView.tag as ? ViewHolder )
148+ ? : ViewHolder (convertView).also { convertView.tag = it }
131149
132150 holder.itemTitle.text = item.appName ? : " "
133151 holder.itemDesc.text = item.packageName ? : " "
@@ -148,14 +166,16 @@ class AdapterAppChooser(
148166 val pkg = item.packageName
149167 holder.imgView.tag = pkg
150168
151- GlobalScope .launch( Dispatchers . Main ) {
169+ scope .launch {
152170 val icon = loadIcon(item).await()
153171 if (icon != null && holder.imgView.tag == pkg) {
154172 holder.imgView.setImageDrawable(icon)
155173 }
156174 }
157175 }
158176
177+ // ================= Selection =================
178+
159179 fun setSelectAllState (allSelected : Boolean ) {
160180 apps.forEach { it.selected = allSelected }
161181 notifyDataSetChanged()
@@ -169,6 +189,16 @@ class AdapterAppChooser(
169189 this .selectStateListener = listener
170190 }
171191
192+ // ================= Release (QUAN TRỌNG) =================
193+
194+ fun release () {
195+ job.cancel() // hủy toàn bộ coroutine
196+ iconCaches.evictAll() // clear cache icon
197+ selectStateListener = null
198+ }
199+
200+ // ================= ViewHolder =================
201+
172202 class ViewHolder (view : View ) {
173203 val itemTitle: TextView = view.findViewById(R .id.ItemTitle )
174204 val itemDesc: TextView = view.findViewById(R .id.ItemDesc )
0 commit comments