Skip to content

Commit 98ea6f6

Browse files
ADFA-4446: Invalidate Glide's cache when a plugin icon file changes (#1633)
* ADFA-4446: Invalidate Glide's cache when a plugin icon file changes * attend to Pr comment --------- Co-authored-by: Daniel Alome <astrocoder007@gmail.com>
1 parent df78fea commit 98ea6f6

2 files changed

Lines changed: 153 additions & 130 deletions

File tree

‎app/src/main/java/com/itsaky/androidide/adapters/PluginListAdapter.kt‎

Lines changed: 148 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
package com.itsaky.androidide.adapters
33

44
import android.view.LayoutInflater
5+
import android.view.Menu
56
import android.view.View
67
import android.view.ViewGroup
78
import android.widget.PopupMenu
9+
import androidx.annotation.StringRes
810
import androidx.recyclerview.widget.DiffUtil
911
import androidx.recyclerview.widget.ListAdapter
1012
import androidx.recyclerview.widget.RecyclerView
1113
import com.bumptech.glide.Glide
14+
import com.bumptech.glide.signature.ObjectKey
1215
import com.itsaky.androidide.R
1316
import com.itsaky.androidide.databinding.ItemPluginBinding
1417
import com.itsaky.androidide.idetooltips.TooltipManager
@@ -18,137 +21,152 @@ import com.itsaky.androidide.utils.isSystemInDarkMode
1821
import java.io.File
1922

2023
class PluginListAdapter(
21-
private val onActionClick: (PluginInfo, Action) -> Unit
24+
private val onActionClick: (PluginInfo, Action) -> Unit,
2225
) : ListAdapter<PluginInfo, PluginListAdapter.PluginViewHolder>(PluginDiffCallback()) {
23-
24-
enum class Action {
25-
ENABLE,
26-
DISABLE,
27-
UNINSTALL,
28-
DETAILS
29-
}
30-
31-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PluginViewHolder {
32-
val binding = ItemPluginBinding.inflate(
33-
LayoutInflater.from(parent.context),
34-
parent,
35-
false
36-
)
37-
return PluginViewHolder(binding)
38-
}
39-
40-
override fun onBindViewHolder(holder: PluginViewHolder, position: Int) {
41-
holder.bind(getItem(position))
42-
}
43-
44-
inner class PluginViewHolder(
45-
private val binding: ItemPluginBinding
46-
) : RecyclerView.ViewHolder(binding.root) {
47-
48-
fun bind(plugin: PluginInfo) {
49-
binding.apply {
50-
pluginName.text = plugin.metadata.name
51-
pluginDescription.text = plugin.metadata.description
52-
val version = plugin.metadata.version
53-
val segments = version.split('.')
54-
pluginVersion.text = if (segments.size > 3) {
55-
"v${segments.take(3).joinToString(".")}..."
56-
} else {
57-
"v$version"
58-
}
59-
pluginAuthor.text = "by ${plugin.metadata.author}"
60-
61-
val iconPath = if (itemView.context.isSystemInDarkMode()) {
62-
plugin.metadata.iconNightPath
63-
} else {
64-
plugin.metadata.iconDayPath
65-
}
66-
67-
pluginIcon.background = null
68-
pluginIcon.imageTintList = null
69-
val iconFile = iconPath?.let(::File)?.takeIf { it.exists() }
70-
if (iconFile != null) {
71-
Glide.with(pluginIcon)
72-
.load(iconFile)
73-
.placeholder(R.drawable.ic_extension)
74-
.error(R.drawable.ic_extension)
75-
.into(pluginIcon)
76-
} else {
77-
Glide.with(pluginIcon).clear(pluginIcon)
78-
pluginIcon.setImageResource(R.drawable.ic_extension)
79-
}
80-
81-
// Set status
82-
val statusText = when {
83-
!plugin.isLoaded -> "Not Loaded"
84-
!plugin.isEnabled -> "Disabled"
85-
else -> "Enabled"
86-
}
87-
pluginStatus.text = statusText
88-
89-
// Set status color
90-
val statusColor = when {
91-
!plugin.isLoaded -> R.color.error
92-
!plugin.isEnabled -> R.color.warning
93-
else -> R.color.success
94-
}
95-
pluginStatus.setTextColor(
96-
itemView.context.getColor(statusColor)
97-
)
98-
99-
// Setup menu button
100-
btnMenu.setOnClickListener { view ->
101-
showPopupMenu(view, plugin)
102-
}
103-
104-
// Setup item click for details
105-
root.setOnClickListener {
106-
onActionClick(plugin, Action.DETAILS)
107-
}
108-
109-
// Long-press for Plugin Manager tooltip
110-
root.setOnLongClickListener {
111-
TooltipManager.showIdeCategoryTooltip(it.context, it, TooltipTag.PLUGIN_MANAGER)
112-
true
113-
}
114-
}
115-
}
116-
117-
private fun showPopupMenu(view: View, plugin: PluginInfo) {
118-
val popup = PopupMenu(view.context, view)
119-
120-
// Add menu items based on plugin state
121-
if (plugin.isLoaded) {
122-
if (plugin.isEnabled) {
123-
popup.menu.add(0, 1, 0, "Disable")
124-
} else {
125-
popup.menu.add(0, 2, 0, "Enable")
126-
}
127-
popup.menu.add(0, 3, 0, "Uninstall")
128-
}
129-
popup.menu.add(0, 4, 0, "Details")
130-
131-
popup.setOnMenuItemClickListener { menuItem ->
132-
when (menuItem.itemId) {
133-
1 -> onActionClick(plugin, Action.DISABLE)
134-
2 -> onActionClick(plugin, Action.ENABLE)
135-
3 -> onActionClick(plugin, Action.UNINSTALL)
136-
4 -> onActionClick(plugin, Action.DETAILS)
137-
}
138-
true
139-
}
140-
141-
popup.show()
142-
}
143-
}
26+
enum class Action(
27+
@StringRes val labelRes: Int,
28+
) {
29+
ENABLE(R.string.enable_plugin),
30+
DISABLE(R.string.disable_plugin),
31+
UNINSTALL(R.string.uninstall_plugin),
32+
DETAILS(R.string.plugin_action_details),
33+
}
34+
35+
override fun onCreateViewHolder(
36+
parent: ViewGroup,
37+
viewType: Int,
38+
): PluginViewHolder {
39+
val binding =
40+
ItemPluginBinding.inflate(
41+
LayoutInflater.from(parent.context),
42+
parent,
43+
false,
44+
)
45+
return PluginViewHolder(binding)
46+
}
47+
48+
override fun onBindViewHolder(
49+
holder: PluginViewHolder,
50+
position: Int,
51+
) {
52+
holder.bind(getItem(position))
53+
}
54+
55+
inner class PluginViewHolder(
56+
private val binding: ItemPluginBinding,
57+
) : RecyclerView.ViewHolder(binding.root) {
58+
fun bind(plugin: PluginInfo) {
59+
binding.apply {
60+
pluginName.text = plugin.metadata.name
61+
pluginDescription.text = plugin.metadata.description
62+
val version = plugin.metadata.version
63+
val segments = version.split('.')
64+
pluginVersion.text =
65+
if (segments.size > 3) {
66+
"v${segments.take(3).joinToString(".")}..."
67+
} else {
68+
"v$version"
69+
}
70+
pluginAuthor.text =
71+
itemView.context.getString(R.string.plugin_author_by, plugin.metadata.author)
72+
73+
val iconPath =
74+
if (itemView.context.isSystemInDarkMode()) {
75+
plugin.metadata.iconNightPath
76+
} else {
77+
plugin.metadata.iconDayPath
78+
}
79+
80+
pluginIcon.background = null
81+
pluginIcon.imageTintList = null
82+
val iconFile = iconPath?.let(::File)?.takeIf { it.exists() }
83+
if (iconFile != null) {
84+
Glide
85+
.with(pluginIcon)
86+
.load(iconFile)
87+
.signature(ObjectKey(iconFile.lastModified()))
88+
.placeholder(R.drawable.ic_extension)
89+
.error(R.drawable.ic_extension)
90+
.into(pluginIcon)
91+
} else {
92+
Glide.with(pluginIcon).clear(pluginIcon)
93+
pluginIcon.setImageResource(R.drawable.ic_extension)
94+
}
95+
96+
val statusText =
97+
when {
98+
!plugin.isLoaded -> R.string.plugin_status_not_loaded
99+
!plugin.isEnabled -> R.string.plugin_status_disabled
100+
else -> R.string.plugin_status_enabled
101+
}
102+
pluginStatus.setText(statusText)
103+
104+
val statusColor =
105+
when {
106+
!plugin.isLoaded -> R.color.error
107+
!plugin.isEnabled -> R.color.warning
108+
else -> R.color.success
109+
}
110+
pluginStatus.setTextColor(
111+
itemView.context.getColor(statusColor),
112+
)
113+
114+
// Setup menu button
115+
btnMenu.setOnClickListener { view ->
116+
showPopupMenu(view, plugin)
117+
}
118+
119+
// Setup item click for details
120+
root.setOnClickListener {
121+
onActionClick(plugin, Action.DETAILS)
122+
}
123+
124+
// Long-press for Plugin Manager tooltip
125+
root.setOnLongClickListener {
126+
TooltipManager.showIdeCategoryTooltip(it.context, it, TooltipTag.PLUGIN_MANAGER)
127+
true
128+
}
129+
}
130+
}
131+
132+
private fun showPopupMenu(
133+
view: View,
134+
plugin: PluginInfo,
135+
) {
136+
val popup = PopupMenu(view.context, view)
137+
val actions = menuActionsFor(plugin)
138+
139+
actions.forEachIndexed { index, action ->
140+
popup.menu.add(Menu.NONE, index, index, action.labelRes)
141+
}
142+
143+
popup.setOnMenuItemClickListener { menuItem ->
144+
onActionClick(plugin, actions[menuItem.itemId])
145+
true
146+
}
147+
148+
popup.show()
149+
}
150+
151+
private fun menuActionsFor(plugin: PluginInfo): List<Action> =
152+
buildList {
153+
if (plugin.isLoaded) {
154+
add(if (plugin.isEnabled) Action.DISABLE else Action.ENABLE)
155+
add(Action.UNINSTALL)
156+
}
157+
add(Action.DETAILS)
158+
}
159+
}
144160
}
145161

146162
class PluginDiffCallback : DiffUtil.ItemCallback<PluginInfo>() {
147-
override fun areItemsTheSame(oldItem: PluginInfo, newItem: PluginInfo): Boolean {
148-
return oldItem.metadata.id == newItem.metadata.id
149-
}
150-
151-
override fun areContentsTheSame(oldItem: PluginInfo, newItem: PluginInfo): Boolean {
152-
return oldItem == newItem
153-
}
154-
}
163+
override fun areItemsTheSame(
164+
oldItem: PluginInfo,
165+
newItem: PluginInfo,
166+
): Boolean = oldItem.metadata.id == newItem.metadata.id
167+
168+
override fun areContentsTheSame(
169+
oldItem: PluginInfo,
170+
newItem: PluginInfo,
171+
): Boolean = oldItem == newItem
172+
}

‎resources/src/main/res/values/strings.xml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,12 @@
912912
<string name="enable_plugin">Enable</string>
913913
<string name="disable_plugin">Disable</string>
914914
<string name="uninstall_plugin">Uninstall</string>
915+
<string name="plugin_action_details">Details</string>
915916
<string name="plugin_details">Plugin Details</string>
917+
<string name="plugin_author_by">by %1$s</string>
918+
<string name="plugin_status_enabled">Enabled</string>
919+
<string name="plugin_status_disabled">Disabled</string>
920+
<string name="plugin_status_not_loaded">Not Loaded</string>
916921
<string name="plugin_permissions">Permissions</string>
917922
<string name="plugin_dependencies">Dependencies</string>
918923

0 commit comments

Comments
 (0)