Skip to content

Commit

Permalink
Add album view (#214)
Browse files Browse the repository at this point in the history
  • Loading branch information
chvp authored Jul 20, 2021
1 parent f689d94 commit 467c09f
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 163 deletions.
18 changes: 18 additions & 0 deletions app/src/main/java/me/vanpetegem/accentor/data/tracks/Track.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ data class Track(
const val ALBUMARTIST = "me.vanpetegem.accentor.data.tracks.Track.ALBUMARTIST"
const val ARTIST = "me.vanpetegem.accentor.data.tracks.Track.ARTIST"
const val YEAR = "me.vanpetegem.accentor.data.tracks.Track.YEAR"

fun fromDbTrack(t: DbTrack, trackArtists: SparseArray<MutableList<TrackArtist>>, trackGenres: SparseArray<MutableList<Int>>) =
Track(
t.id,
t.title,
t.normalizedTitle,
t.number,
t.albumId,
t.reviewComment,
t.createdAt,
t.updatedAt,
trackGenres.get(t.id, ArrayList()),
trackArtists.get(t.id, ArrayList()),
t.codecId,
t.length,
t.bitrate,
t.locationId
)
}
}

Expand Down
97 changes: 28 additions & 69 deletions app/src/main/java/me/vanpetegem/accentor/data/tracks/TrackDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,73 +20,37 @@ abstract class TrackDao {
val ids = tracks.map { it.id }
val trackGenres = getTrackGenresByTrackIdWhereTrackIds(ids)
val trackArtists = getTrackArtistsByTrackIdWhereTrackIds(ids)
return tracks.map { t ->
Track(
t.id,
t.title,
t.normalizedTitle,
t.number,
t.albumId,
t.reviewComment,
t.createdAt,
t.updatedAt,
trackGenres.get(t.id, ArrayList()),
trackArtists.get(t.id, ArrayList()),
t.codecId,
t.length,
t.bitrate,
t.locationId
)
}
return tracks.map { t -> Track.fromDbTrack(t, trackArtists, trackGenres) }
}

open fun findByIds(ids: List<Int>): LiveData<List<Track>> = switchMap(findDbTracksByIds(ids)) { tracks ->
switchMap(findTrackArtistsByTrackIdWhereTrackIds(ids)) { trackArtists ->
map(findTrackGenresByTrackIdWhereTrackIds(ids)) { trackGenres ->
tracks.map { t ->
Track(
t.id,
t.title,
t.normalizedTitle,
t.number,
t.albumId,
t.reviewComment,
t.createdAt,
t.updatedAt,
trackGenres.get(t.id, ArrayList()),
trackArtists.get(t.id, ArrayList()),
t.codecId,
t.length,
t.bitrate,
t.locationId
)
}
tracks.map { t -> Track.fromDbTrack(t, trackArtists, trackGenres) }
}
}
}

open fun findById(id: Int): LiveData<Track?> = switchMap(findDbTrackById(id)) { dbTrack ->
switchMap(findDbTrackArtistsById(id)) { trackArtists ->
map(findDbTrackGenresById(id)) { trackGenres ->
if (dbTrack != null) {
dbTrack?.let {
Track(
dbTrack.id,
dbTrack.title,
dbTrack.normalizedTitle,
dbTrack.number,
dbTrack.albumId,
dbTrack.reviewComment,
dbTrack.createdAt,
dbTrack.updatedAt,
it.id,
it.title,
it.normalizedTitle,
it.number,
it.albumId,
it.reviewComment,
it.createdAt,
it.updatedAt,
trackGenres.map { it.genreId },
trackArtists.map { TrackArtist(it.artistId, it.name, it.normalizedName, it.role, it.order) },
dbTrack.codecId,
dbTrack.length,
dbTrack.bitrate,
dbTrack.locationId
it.codecId,
it.length,
it.bitrate,
it.locationId
)
} else {
null
}
}
}
Expand All @@ -96,24 +60,16 @@ abstract class TrackDao {
val ids = tracks.map { it.id }
switchMap(findTrackArtistsByTrackIdWhereTrackIds(ids)) { trackArtists ->
map(findTrackGenresByTrackIdWhereTrackIds(ids)) { trackGenres ->
tracks.map { t ->
Track(
t.id,
t.title,
t.normalizedTitle,
t.number,
t.albumId,
t.reviewComment,
t.createdAt,
t.updatedAt,
trackGenres.get(t.id, ArrayList()),
trackArtists.get(t.id, ArrayList()),
t.codecId,
t.length,
t.bitrate,
t.locationId
)
}
tracks.map { Track.fromDbTrack(it, trackArtists, trackGenres) }
}
}
}

open fun findByAlbum(album: Album): LiveData<List<Track>> = switchMap(findDbTracksByAlbumId(album.id)) { tracks ->
val ids = tracks.map { it.id }
switchMap(findTrackArtistsByTrackIdWhereTrackIds(ids)) { trackArtists ->
map(findTrackGenresByTrackIdWhereTrackIds(ids)) { trackGenres ->
tracks.map { Track.fromDbTrack(it, trackArtists, trackGenres) }
}
}
}
Expand Down Expand Up @@ -155,6 +111,9 @@ abstract class TrackDao {
@Query("SELECT * FROM tracks WHERE id IN (:ids)")
protected abstract fun findDbTracksByIds(ids: List<Int>): LiveData<List<DbTrack>>

@Query("SELECT * FROM tracks WHERE album_id = :id")
protected abstract fun findDbTracksByAlbumId(id: Int): LiveData<List<DbTrack>>

@Query("SELECT * FROM tracks WHERE id IN (SELECT track_id FROM track_artists WHERE artist_id = :id)")
protected abstract fun findDbTracksByArtistId(id: Int): LiveData<List<DbTrack>>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TrackRepository(
fun findById(id: Int): LiveData<Track?> = trackDao.findById(id)
fun findByIds(ids: List<Int>): LiveData<List<Track>> = trackDao.findByIds(ids)
fun findByArtist(artist: Artist): LiveData<List<Track>> = trackDao.findByArtist(artist)
fun findByAlbum(album: Album): LiveData<List<Track>> = trackDao.findByAlbum(album)
fun getByAlbum(album: Album): List<Track> = trackDao.getByAlbum(album)

suspend fun refresh(handler: suspend (Result<Unit>) -> Unit) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.vanpetegem.accentor.ui.albums

import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -35,6 +36,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.rememberImagePainter
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
Expand All @@ -43,9 +45,9 @@ import me.vanpetegem.accentor.data.albums.Album
import me.vanpetegem.accentor.media.MediaSessionConnection

@Composable
public fun AlbumCard(album: Album, mediaSessionConnection: MediaSessionConnection = viewModel()) {
public fun AlbumCard(album: Album, navController: NavController, mediaSessionConnection: MediaSessionConnection = viewModel()) {
val scope = rememberCoroutineScope()
Card(modifier = Modifier.padding(8.dp)) {
Card(modifier = Modifier.padding(8.dp).clickable { navController.navigate("albums/${album.id}") }) {
Column {
Image(
painter = if (album.image500 != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import me.vanpetegem.accentor.ui.util.FastScrollableGrid

@Composable
fun AlbumGrid(albumsViewModel: AlbumsViewModel = viewModel()) {
fun AlbumGrid(navController: NavController, albumsViewModel: AlbumsViewModel = viewModel()) {
val albums by albumsViewModel.allAlbums.observeAsState()
if (albums != null) {
FastScrollableGrid(albums!!, { it.firstCharacter().uppercase() }) { album -> AlbumCard(album) }
FastScrollableGrid(albums!!, { it.firstCharacter().uppercase() }) { album -> AlbumCard(album, navController) }
}
}
80 changes: 80 additions & 0 deletions app/src/main/java/me/vanpetegem/accentor/ui/albums/AlbumView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package me.vanpetegem.accentor.ui.albums

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.rememberImagePainter
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import me.vanpetegem.accentor.R
import me.vanpetegem.accentor.media.MediaSessionConnection
import me.vanpetegem.accentor.ui.tracks.TrackRow

@Composable
fun AlbumView(id: Int, albumViewModel: AlbumViewModel = viewModel(), mediaSessionConnection: MediaSessionConnection = viewModel()) {
val scope = rememberCoroutineScope()
val albumState by albumViewModel.getAlbum(id).observeAsState()
if (albumState != null) {
val album = albumState!!
val tracks by albumViewModel.tracksForAlbum(album).observeAsState()
LazyColumn(modifier = Modifier.fillMaxSize()) {
item {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 8.dp)) {
Image(
painter = if (album.image500 != null) {
rememberImagePainter(album.image500) {
placeholder(R.drawable.ic_album)
}
} else {
painterResource(R.drawable.ic_album)
},
contentDescription = stringResource(R.string.artist_image),
modifier = Modifier.width(128.dp).aspectRatio(1f),
)
Column {
Text(album.title, style = MaterialTheme.typography.h4, modifier = Modifier.padding(start = 8.dp))
Text(
album.stringifyAlbumArtists(),
style = MaterialTheme.typography.subtitle1,
color = LocalContentColor.current.copy(alpha = ContentAlpha.medium),
modifier = Modifier.padding(start = 8.dp),
)
Row(modifier = Modifier.padding(8.dp)) {
IconButton(onClick = { scope.launch(IO) { mediaSessionConnection.play(album) } }) {
Icon(painterResource(R.drawable.ic_play), contentDescription = stringResource(R.string.play_now))
}
IconButton(onClick = { scope.launch(IO) { mediaSessionConnection.addTracksToQueue(album) } }) {
Icon(painterResource(R.drawable.ic_queue_add), contentDescription = stringResource(R.string.play_last))
}
}
}
}
}
if (tracks != null && tracks!!.size > 0) {
items(tracks!!.size) { i -> TrackRow(tracks!![i]) }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.vanpetegem.accentor.ui.albums

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations.map
import me.vanpetegem.accentor.data.AccentorDatabase
import me.vanpetegem.accentor.data.albums.Album
import me.vanpetegem.accentor.data.albums.AlbumRepository
import me.vanpetegem.accentor.data.authentication.AuthenticationDataSource
import me.vanpetegem.accentor.data.authentication.AuthenticationRepository
import me.vanpetegem.accentor.data.tracks.Track
import me.vanpetegem.accentor.data.tracks.TrackRepository

class AlbumViewModel(application: Application) : AndroidViewModel(application) {
private val database = AccentorDatabase.getDatabase(application)
private val authenticationRepository = AuthenticationRepository(AuthenticationDataSource(application))
private val albumRepository = AlbumRepository(database.albumDao(), authenticationRepository)
private val trackRepository = TrackRepository(database.trackDao(), authenticationRepository)

fun getAlbum(id: Int): LiveData<Album> = map(albumRepository.allAlbumsById) { albums ->
albums[id]
}

fun tracksForAlbum(album: Album): LiveData<List<Track>> = map(trackRepository.findByAlbum(album)) { tracks ->
val copy = tracks.toMutableList()
copy.sortWith({ t1, t2 -> t1.number.compareTo(t2.number) })
copy
}
}
Loading

0 comments on commit 467c09f

Please sign in to comment.