Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #55 from you-apps/play-all
Browse files Browse the repository at this point in the history
Local Music: Sort order and play all support
  • Loading branch information
Bnyro authored Oct 18, 2023
2 parents 0abd121 + d769265 commit ba9c731
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ data class Song(
val thumbnailUri: Uri? = null,
val albumId: Long? = null,
val artistId: Long? = null,
val isLocal: Boolean = false
val isLocal: Boolean = false,
val creationDate: Long? = null,
val dateAdded: Long? = null,
) {
fun toggleLike(): Song {
return copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import app.suhasdissa.vibeyou.backend.database.dao.RawDao
import app.suhasdissa.vibeyou.backend.database.dao.SearchDao
import app.suhasdissa.vibeyou.backend.database.dao.SongsDao
Expand All @@ -15,7 +16,7 @@ import app.suhasdissa.vibeyou.backend.database.entities.SongEntity
SongEntity::class,
SearchQuery::class
],
version = 1,
version = 2,
exportSchema = false
)
abstract class SongDatabase : RoomDatabase() {
Expand All @@ -28,13 +29,19 @@ abstract class SongDatabase : RoomDatabase() {
@Volatile
private var INSTANCE: SongDatabase? = null

private val MIGRATION_1_2 = Migration(1, 2) { database ->
database.execSQL("ALTER TABLE song ADD COLUMN creationDate INTEGER")
database.execSQL("ALTER TABLE song ADD COLUMN dateAdded INTEGER")
}

fun getDatabase(context: Context): SongDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
SongDatabase::class.java,
"song_database"
)
.addMigrations(MIGRATION_1_2)
.fallbackToDestructiveMigration()
.allowMainThreadQueries().build()
INSTANCE = instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.text.format.DateUtils
import android.util.Log
import androidx.core.net.toUri
import app.suhasdissa.vibeyou.backend.data.Album
import app.suhasdissa.vibeyou.backend.data.Artist
Expand Down Expand Up @@ -44,7 +45,9 @@ class LocalMusicRepository(
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ARTIST_ID
MediaStore.Audio.Media.ARTIST_ID,
MediaStore.Audio.Media.DATE_MODIFIED,
MediaStore.Audio.Media.DATE_ADDED,
)

val sortOrder = "${MediaStore.Audio.Media.TITLE} ASC"
Expand All @@ -66,6 +69,8 @@ class LocalMusicRepository(
val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
val albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)
val artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID)
val creationDateColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_MODIFIED)
val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_ADDED)

while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
Expand Down Expand Up @@ -99,7 +104,9 @@ class LocalMusicRepository(
artistsText = cursor.getString(artistColumn),
albumId = albumId,
artistId = cursor.getLong(artistIdColumn),
isLocal = true
isLocal = true,
creationDate = cursor.getLong(creationDateColumn),
dateAdded = cursor.getLong(dateAddedColumn)
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ import app.suhasdissa.vibeyou.backend.data.Album
import app.suhasdissa.vibeyou.backend.data.Artist
import app.suhasdissa.vibeyou.backend.data.Song
import app.suhasdissa.vibeyou.backend.repository.LocalMusicRepository
import app.suhasdissa.vibeyou.ui.dialogs.SortOrder
import kotlinx.coroutines.launch

class LocalSongViewModel(private val musicRepository: LocalMusicRepository) : ViewModel() {
var songs by mutableStateOf(listOf<Song>())
var albums by mutableStateOf(listOf<Album>())
var artists by mutableStateOf(listOf<Artist>())

var songsSortOrder = SortOrder.Alphabetic
var reverseSongs = false

init {
viewModelScope.launch {
try {
Expand All @@ -45,6 +49,16 @@ class LocalSongViewModel(private val musicRepository: LocalMusicRepository) : Vi
}
}

fun updateSongsSortOrder() {
val sortedSongs = when (songsSortOrder) {
SortOrder.Alphabetic -> songs.sortedBy { it.title.lowercase() }
SortOrder.Creation_Date -> songs.sortedBy { it.creationDate }
SortOrder.Date_Added -> songs.sortedBy { it.dateAdded }
SortOrder.Artist_Name -> songs.sortedBy { it.artistsText.orEmpty().lowercase() }
}
songs = if (reverseSongs) sortedSongs.reversed() else sortedSongs
}

companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,15 @@ class PlayerViewModel(
controller!!.seekToPrevious()
}

fun shuffleSongs(songs: List<Song>) {
fun playSongs(songs: List<Song>, shuffle: Boolean = false) {
viewModelScope.launch {
val shuffleQueue = songs.shuffled().map { it.asMediaItem }
playAll(shuffleQueue)
val queue = if (shuffle) {
songs.shuffled()
} else {
songs
}
.map { it.asMediaItem }
playAll(queue)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ inline fun <reified T : Enum<T>> ChipSelector(
view.playSoundEffect(SoundEffectConstants.CLICK)
selectedOption = it
onItemSelected(it)
}, label = { Text(it.name) })
}, label = { Text(it.name.replace("_", " ")) })
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package app.suhasdissa.vibeyou.ui.dialogs

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.suhasdissa.vibeyou.R
import app.suhasdissa.vibeyou.ui.components.ChipSelector

enum class SortOrder {
Alphabetic,
Creation_Date,
Date_Added,
Artist_Name
}

@Composable
fun SortOrderDialog(
onDismissRequest: () -> Unit,
onSortOrderChange: (order: SortOrder, reverse: Boolean) -> Unit,
defaultSortOrder: SortOrder,
defaultReverse: Boolean,
) {
var sortOrder by remember {
mutableStateOf(defaultSortOrder)
}
var reverse by remember {
mutableStateOf(defaultReverse)
}

AlertDialog(
onDismissRequest = onDismissRequest,
title = {
Text(stringResource(R.string.sort_order))
},
text = {
Column {
ChipSelector(
onItemSelected = {
sortOrder = it
},
defaultValue = sortOrder
)
val interactionSource = remember { MutableInteractionSource() }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(interactionSource = interactionSource, indication = null) {
reverse = !reverse
}
) {
Checkbox(checked = reverse, onCheckedChange = { reverse = it })
Text(text = stringResource(R.string.reversed))
}
}
},
confirmButton = {
TextButton(
onClick = {
onSortOrderChange(sortOrder, reverse)
onDismissRequest()
}
) {
Text(stringResource(R.string.ok))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(stringResource(R.string.cancel))
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ package app.suhasdissa.vibeyou.ui.screens.music

import android.view.SoundEffectConstants
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Shuffle
import androidx.compose.material.icons.filled.Sort
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
Expand All @@ -18,8 +25,14 @@ import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand All @@ -31,6 +44,7 @@ import app.suhasdissa.vibeyou.backend.viewmodel.LocalSongViewModel
import app.suhasdissa.vibeyou.backend.viewmodel.PlayerViewModel
import app.suhasdissa.vibeyou.ui.components.AlbumList
import app.suhasdissa.vibeyou.ui.components.ArtistList
import app.suhasdissa.vibeyou.ui.dialogs.SortOrderDialog
import app.suhasdissa.vibeyou.ui.screens.songs.SongListView
import kotlinx.coroutines.launch

Expand All @@ -44,6 +58,11 @@ fun LocalMusicScreen(
) {
val pagerState = rememberPagerState { 3 }
val scope = rememberCoroutineScope()

var showSortDialog by remember {
mutableStateOf(false)
}

Column {
TabRow(selectedTabIndex = pagerState.currentPage, Modifier.fillMaxWidth()) {
val view = LocalView.current
Expand Down Expand Up @@ -98,22 +117,50 @@ fun LocalMusicScreen(
0 -> {
val view = LocalView.current
Scaffold(floatingActionButton = {
FloatingActionButton(onClick = {
view.playSoundEffect(SoundEffectConstants.CLICK)
playerViewModel.shuffleSongs(localSongViewModel.songs)
}) {
Icon(
imageVector = Icons.Default.Shuffle,
contentDescription = stringResource(R.string.shuffle)
)
Row {
FloatingActionButton(onClick = {
view.playSoundEffect(SoundEffectConstants.CLICK)
playerViewModel.playSongs(localSongViewModel.songs)
}) {
Icon(
imageVector = Icons.Default.PlayArrow,
contentDescription = stringResource(R.string.play_all)
)
}

Spacer(modifier = Modifier.width(12.dp))

FloatingActionButton(onClick = {
view.playSoundEffect(SoundEffectConstants.CLICK)
playerViewModel.playSongs(localSongViewModel.songs, shuffle = true)
}) {
Icon(
imageVector = Icons.Default.Shuffle,
contentDescription = stringResource(R.string.shuffle)
)
}
}
}) { innerPadding ->
Column(
Modifier
.fillMaxSize()
.padding(innerPadding)
) {
SongListView(songs = localSongViewModel.songs)
Row(
modifier = Modifier
.align(Alignment.End)
.padding(top = 4.dp)
.clip(RoundedCornerShape(12.dp))
.padding(horizontal = 10.dp, vertical = 6.dp)
.clickable {
showSortDialog = true
}
) {
Text(text = stringResource(R.string.sort_order))
Spacer(modifier = Modifier.width(6.dp))
Icon(imageVector = Icons.Default.Sort, contentDescription = null)
}
SongListView(songs = localSongViewModel.songs, sortOrder = localSongViewModel.songsSortOrder)
}
}
}
Expand All @@ -134,4 +181,17 @@ fun LocalMusicScreen(
}
}
}

if (showSortDialog) {
SortOrderDialog(
onDismissRequest = { showSortDialog = false },
defaultSortOrder = localSongViewModel.songsSortOrder,
defaultReverse = localSongViewModel.reverseSongs,
onSortOrderChange = { sortOrder, reverse ->
localSongViewModel.songsSortOrder = sortOrder
localSongViewModel.reverseSongs = reverse
localSongViewModel.updateSongsSortOrder()
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fun AlbumScreen(
MiniPlayerScaffold(fab = {
if (state is AlbumInfoState.Success) {
FloatingActionButton(onClick = {
playerViewModel.shuffleSongs(state.songs)
playerViewModel.playSongs(state.songs, shuffle = true)
}) {
Icon(
imageVector = Icons.Default.Shuffle,
Expand Down
Loading

0 comments on commit ba9c731

Please sign in to comment.