2020-11-16 19:14:44 +01:00
|
|
|
/*
|
|
|
|
This file is part of Subsonic.
|
|
|
|
|
|
|
|
Subsonic is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Subsonic is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Copyright 2020 (C) Jozsef Varga
|
|
|
|
*/
|
|
|
|
package org.moire.ultrasonic.activity
|
|
|
|
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.MenuInflater
|
|
|
|
import android.view.MenuItem
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2020-11-18 21:46:46 +01:00
|
|
|
import android.widget.ImageView
|
2020-11-16 19:14:44 +01:00
|
|
|
import android.widget.LinearLayout
|
|
|
|
import android.widget.PopupMenu
|
|
|
|
import android.widget.RelativeLayout
|
|
|
|
import android.widget.TextView
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2020-11-23 13:55:28 +01:00
|
|
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.SectionedAdapter
|
2020-11-23 14:02:18 +01:00
|
|
|
import java.text.Collator
|
2020-11-16 19:14:44 +01:00
|
|
|
import org.moire.ultrasonic.R
|
|
|
|
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
|
|
|
import org.moire.ultrasonic.domain.Artist
|
2020-11-18 21:46:46 +01:00
|
|
|
import org.moire.ultrasonic.domain.MusicDirectory
|
|
|
|
import org.moire.ultrasonic.util.ImageLoader
|
2020-11-16 19:14:44 +01:00
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
/**
|
|
|
|
* Creates a Row in a RecyclerView which contains the details of an Artist
|
|
|
|
*/
|
2020-11-16 19:14:44 +01:00
|
|
|
class ArtistRowAdapter(
|
|
|
|
private var artistList: List<Artist>,
|
|
|
|
private var folderName: String,
|
|
|
|
private var shouldShowHeader: Boolean,
|
|
|
|
val onArtistClick: (Artist) -> Unit,
|
|
|
|
val onContextMenuClick: (MenuItem, Artist) -> Boolean,
|
2020-11-18 21:46:46 +01:00
|
|
|
val onFolderClick: (view: View) -> Unit,
|
2020-11-23 13:55:28 +01:00
|
|
|
private val imageLoader: ImageLoader
|
|
|
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), SectionedAdapter {
|
2020-11-16 19:14:44 +01:00
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
/**
|
|
|
|
* Sets the data to be displayed in the RecyclerView
|
|
|
|
*/
|
2020-11-16 19:14:44 +01:00
|
|
|
fun setData(data: List<Artist>) {
|
2020-11-23 14:02:18 +01:00
|
|
|
artistList = data.sortedWith(compareBy(Collator.getInstance()) { t -> t.name })
|
2020-11-16 19:14:44 +01:00
|
|
|
notifyDataSetChanged()
|
|
|
|
}
|
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
/**
|
|
|
|
* Sets the name of the folder to be displayed n the Header (first) row
|
|
|
|
*/
|
2020-11-16 19:14:44 +01:00
|
|
|
fun setFolderName(name: String) {
|
|
|
|
folderName = name
|
|
|
|
notifyDataSetChanged()
|
|
|
|
}
|
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
/**
|
|
|
|
* Holds the view properties of an Artist row
|
|
|
|
*/
|
2020-11-16 19:14:44 +01:00
|
|
|
class ArtistViewHolder(
|
|
|
|
itemView: View
|
|
|
|
) : RecyclerView.ViewHolder(itemView) {
|
|
|
|
var section: TextView = itemView.findViewById(R.id.row_section)
|
|
|
|
var textView: TextView = itemView.findViewById(R.id.row_artist_name)
|
|
|
|
var layout: RelativeLayout = itemView.findViewById(R.id.row_artist_layout)
|
2020-11-18 21:46:46 +01:00
|
|
|
var coverArt: ImageView = itemView.findViewById(R.id.artist_coverart)
|
|
|
|
var coverArtId: String? = null
|
2020-11-16 19:14:44 +01:00
|
|
|
}
|
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
/**
|
|
|
|
* Holds the view properties of the Header row
|
|
|
|
*/
|
2020-11-16 19:14:44 +01:00
|
|
|
class HeaderViewHolder(
|
|
|
|
itemView: View
|
|
|
|
) : RecyclerView.ViewHolder(itemView) {
|
|
|
|
var folderName: TextView = itemView.findViewById(R.id.select_artist_folder_2)
|
|
|
|
var layout: LinearLayout = itemView.findViewById(R.id.select_artist_folder)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreateViewHolder(
|
|
|
|
parent: ViewGroup,
|
|
|
|
viewType: Int
|
|
|
|
): RecyclerView.ViewHolder {
|
|
|
|
if (viewType == TYPE_ITEM) {
|
|
|
|
val row = LayoutInflater.from(parent.context)
|
|
|
|
.inflate(R.layout.artist_list_item, parent, false)
|
|
|
|
return ArtistViewHolder(row)
|
|
|
|
}
|
|
|
|
val header = LayoutInflater.from(parent.context)
|
|
|
|
.inflate(R.layout.select_artist_header, parent, false)
|
|
|
|
return HeaderViewHolder(header)
|
|
|
|
}
|
|
|
|
|
2020-11-18 21:46:46 +01:00
|
|
|
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
|
|
|
if ((holder is ArtistViewHolder) && (holder.coverArtId != null)) {
|
|
|
|
imageLoader.cancel(holder.coverArtId)
|
|
|
|
}
|
|
|
|
super.onViewRecycled(holder)
|
|
|
|
}
|
|
|
|
|
2020-11-16 19:14:44 +01:00
|
|
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
|
|
if (holder is ArtistViewHolder) {
|
|
|
|
val listPosition = if (shouldShowHeader) position - 1 else position
|
|
|
|
holder.textView.text = artistList[listPosition].name
|
|
|
|
holder.section.text = getSectionForArtist(listPosition)
|
|
|
|
holder.layout.setOnClickListener { onArtistClick(artistList[listPosition]) }
|
|
|
|
holder.layout.setOnLongClickListener { view -> createPopupMenu(view, listPosition) }
|
2020-11-18 21:46:46 +01:00
|
|
|
holder.coverArtId = artistList[listPosition].coverArt
|
|
|
|
imageLoader.loadImage(
|
|
|
|
holder.coverArt,
|
|
|
|
MusicDirectory.Entry().apply { coverArt = holder.coverArtId },
|
2020-11-24 18:19:58 +01:00
|
|
|
false, 0, false, true, R.drawable.ic_contact_picture
|
2020-11-18 21:46:46 +01:00
|
|
|
)
|
2020-11-16 19:14:44 +01:00
|
|
|
} else if (holder is HeaderViewHolder) {
|
|
|
|
holder.folderName.text = folderName
|
|
|
|
holder.layout.setOnClickListener { onFolderClick(holder.layout) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun getItemCount() = if (shouldShowHeader) artistList.size + 1 else artistList.size
|
|
|
|
|
|
|
|
override fun getItemViewType(position: Int): Int {
|
|
|
|
return if (position == 0 && shouldShowHeader) TYPE_HEADER else TYPE_ITEM
|
|
|
|
}
|
|
|
|
|
2020-11-23 13:55:28 +01:00
|
|
|
override fun getSectionName(position: Int): String {
|
|
|
|
val listPosition = if (shouldShowHeader) position - 1 else position
|
|
|
|
return getSectionFromName(artistList[listPosition].name ?: " ")
|
|
|
|
}
|
|
|
|
|
2020-11-16 19:14:44 +01:00
|
|
|
private fun getSectionForArtist(artistPosition: Int): String {
|
|
|
|
if (artistPosition == 0)
|
|
|
|
return getSectionFromName(artistList[artistPosition].name ?: " ")
|
|
|
|
|
|
|
|
val previousArtistSection = getSectionFromName(
|
|
|
|
artistList[artistPosition - 1].name ?: " "
|
|
|
|
)
|
|
|
|
val currentArtistSection = getSectionFromName(
|
|
|
|
artistList[artistPosition].name ?: " "
|
|
|
|
)
|
|
|
|
|
|
|
|
return if (previousArtistSection == currentArtistSection) "" else currentArtistSection
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getSectionFromName(name: String): String {
|
|
|
|
var section = name.first().toUpperCase()
|
|
|
|
if (!section.isLetter()) section = '#'
|
|
|
|
return section.toString()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun createPopupMenu(view: View, position: Int): Boolean {
|
|
|
|
val popup = PopupMenu(view.context, view)
|
|
|
|
val inflater: MenuInflater = popup.menuInflater
|
|
|
|
inflater.inflate(R.menu.select_artist_context, popup.menu)
|
|
|
|
|
|
|
|
val downloadMenuItem = popup.menu.findItem(R.id.artist_menu_download)
|
|
|
|
downloadMenuItem?.isVisible = !isOffline(view.context)
|
|
|
|
|
|
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
|
|
onContextMenuClick(menuItem, artistList[position])
|
|
|
|
}
|
|
|
|
popup.show()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val TYPE_HEADER = 0
|
|
|
|
private const val TYPE_ITEM = 1
|
|
|
|
}
|
|
|
|
}
|