22package com.itsaky.androidide.adapters
33
44import android.view.LayoutInflater
5+ import android.view.Menu
56import android.view.View
67import android.view.ViewGroup
78import android.widget.PopupMenu
9+ import androidx.annotation.StringRes
810import androidx.recyclerview.widget.DiffUtil
911import androidx.recyclerview.widget.ListAdapter
1012import androidx.recyclerview.widget.RecyclerView
1113import com.bumptech.glide.Glide
14+ import com.bumptech.glide.signature.ObjectKey
1215import com.itsaky.androidide.R
1316import com.itsaky.androidide.databinding.ItemPluginBinding
1417import com.itsaky.androidide.idetooltips.TooltipManager
@@ -18,137 +21,152 @@ import com.itsaky.androidide.utils.isSystemInDarkMode
1821import java.io.File
1922
2023class 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
146162class 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+ }
0 commit comments