Add RecyclerView to LibraryFragment

Add a basic RecyclerView to LibraryFragment that shows every album.
This commit is contained in:
OxygenCobalt 2020-08-24 14:53:13 -06:00
parent 37b071de0d
commit c04a90c3fa
8 changed files with 126 additions and 20 deletions

View file

@ -7,9 +7,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentLibraryBinding import org.oxycblt.auxio.databinding.FragmentLibraryBinding
import org.oxycblt.auxio.library.recycler.AlbumDataAdapter
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
class LibraryFragment : Fragment() { class LibraryFragment : Fragment() {
@ -27,11 +29,16 @@ class LibraryFragment : Fragment() {
inflater, R.layout.fragment_library, container, false inflater, R.layout.fragment_library, container, false
) )
libraryModel val adapter = AlbumDataAdapter()
binding.libraryRecycler.adapter = adapter
val albums = MusicRepository.getInstance().albums.value val repo = MusicRepository.getInstance()
repo.albums.observe(
binding.testAlbum.setImageBitmap(albums?.get((0..albums.size).random())?.cover) viewLifecycleOwner,
Observer {
adapter.data = it
}
)
Log.d(this::class.simpleName, "Fragment created.") Log.d(this::class.simpleName, "Fragment created.")

View file

@ -0,0 +1,39 @@
package org.oxycblt.auxio.library.recycler
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import org.oxycblt.auxio.databinding.LibraryItemBinding
import org.oxycblt.auxio.music.models.Album
class AlbumDataAdapter : ListAdapter<Album, LibraryViewHolder>(DiffCallback) {
var data = listOf<Album>()
set(newData) {
field = newData
submitList(data)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryViewHolder {
return LibraryViewHolder(
LibraryItemBinding.inflate(LayoutInflater.from(parent.context))
)
}
override fun onBindViewHolder(holder: LibraryViewHolder, position: Int) {
val album = getItem(position)
holder.bindAlbum(album)
}
companion object DiffCallback : DiffUtil.ItemCallback<Album>() {
override fun areItemsTheSame(oldItem: Album, newItem: Album): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Album, newItem: Album): Boolean {
return oldItem.id == newItem.id
}
}
}

View file

@ -0,0 +1,21 @@
package org.oxycblt.auxio.library.recycler
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.LibraryItemBinding
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
class LibraryViewHolder(
private var binding: LibraryItemBinding
) : RecyclerView.ViewHolder(binding.root) {
// Bind the view w/new data
fun bindAlbum(album: Album) {
binding.album = album
binding.executePendingBindings()
}
fun bindArtist(artist: Artist) {
// TODO: Not implemented.
}
}

View file

@ -32,9 +32,9 @@ private val ID3_GENRES = arrayOf<String>(
const val PAREN_FILTER = "()" const val PAREN_FILTER = "()"
// Convert legacy ID3 genres to a named genre // Convert legacy ID3 genres to a named genre
fun intToNamedGenre(genre: String): String { fun String.toNamedGenre(): String {
// Strip the genres of any parentheses, and convert it to an int // Strip the genres of any parentheses, and convert it to an int
val intGenre = genre.filterNot { val intGenre = this.filterNot {
PAREN_FILTER.indexOf(it) > -1 PAREN_FILTER.indexOf(it) > -1
}.toInt() }.toInt()

View file

@ -12,12 +12,13 @@ import android.provider.MediaStore.Audio.Artists
import android.provider.MediaStore.Audio.Genres import android.provider.MediaStore.Audio.Genres
import android.provider.MediaStore.Audio.Media import android.provider.MediaStore.Audio.Media
import android.util.Log import android.util.Log
import org.oxycblt.auxio.music.intToNamedGenre
import org.oxycblt.auxio.music.models.Album import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Genre import org.oxycblt.auxio.music.models.Genre
import org.oxycblt.auxio.music.models.Song import org.oxycblt.auxio.music.models.Song
import org.oxycblt.auxio.music.toAlbumArtURI import org.oxycblt.auxio.music.toAlbumArtURI
import org.oxycblt.auxio.music.toNamedGenre
import java.io.FileNotFoundException
enum class MusicLoaderResponse { enum class MusicLoaderResponse {
DONE, FAILURE, NO_MUSIC DONE, FAILURE, NO_MUSIC
@ -87,7 +88,7 @@ class MusicLoader(private val app: Application) {
// convert that to the corresponding ID3 genre. Really hope anyone doesn't have // convert that to the corresponding ID3 genre. Really hope anyone doesn't have
// a genre that contains parentheses. // a genre that contains parentheses.
if (name.contains(Regex("[()]"))) { if (name.contains(Regex("[()]"))) {
name = intToNamedGenre(name) name = name.toNamedGenre()
} }
genres.add( genres.add(
@ -193,17 +194,24 @@ class MusicLoader(private val app: Application) {
val year = cursor.getInt(yearIndex) val year = cursor.getInt(yearIndex)
val numSongs = cursor.getInt(numIndex) val numSongs = cursor.getInt(numIndex)
// TODO:
// Album art loading during the initial load isn't really practical for a large amount of albums
// Use glide or something
val artUri = id.toAlbumArtURI() val artUri = id.toAlbumArtURI()
var cover: Bitmap? = null var cover: Bitmap? = null
// Get the album art through either ImageDecoder or MediaStore depending on the // Get the album art through either ImageDecoder or MediaStore depending on the
// version. // version.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { try {
cover = ImageDecoder.decodeBitmap( cover = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.createSource(resolver, artUri) ImageDecoder.decodeBitmap(
) ImageDecoder.createSource(resolver, artUri)
} else { )
cover = MediaStore.Images.Media.getBitmap(resolver, artUri) } else {
MediaStore.Images.Media.getBitmap(resolver, artUri)
}
} catch (noFound: FileNotFoundException) {
cover = null
} }
albums.add( albums.add(

View file

@ -17,14 +17,15 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:title="@string/title_library_fragment" /> app:title="@string/title_library_fragment" />
<ImageView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/testAlbum" android:id="@+id/library_recycler"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" /> app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="album"
type="org.oxycblt.auxio.music.models.Album" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/albumNameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{album.title}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Album Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -12,7 +12,9 @@
tools:layout="@layout/fragment_loading"> tools:layout="@layout/fragment_loading">
<action <action
android:id="@+id/action_to_library" android:id="@+id/action_to_library"
app:destination="@id/libraryFragment" /> app:destination="@id/libraryFragment"
app:popUpTo="@id/loadingFragment"
app:popUpToInclusive="true" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/libraryFragment" android:id="@+id/libraryFragment"