diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index c19de999..7a345ede 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -9,14 +9,14 @@
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/common/Extensions.kt b/app/src/main/java/luci/sixsixsix/powerampache2/common/Extensions.kt
index e76ba6dd..2aa5e282 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/common/Extensions.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/common/Extensions.kt
@@ -62,7 +62,6 @@ import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.Constants.PLAY_STORE_URL
import luci.sixsixsix.powerampache2.domain.common.Constants.PLUGIN_CHROMECAST_ACTIVITY_ID
import luci.sixsixsix.powerampache2.domain.common.Constants.PLUGIN_CHROMECAST_ID
-import luci.sixsixsix.powerampache2.domain.models.Song
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@@ -102,7 +101,7 @@ fun Context.openLinkInBrowser(link: String) =
}
)
-fun Context.exportSong(song: Song, offlineUri: String) {
+fun Context.exportSong(mimeType: String?, offlineUri: String) {
val fileWithinAppDir = File(offlineUri)
val fileUri = FileProvider.getUriForFile(this,
getString(R.string.sharing_provider_authority),
@@ -113,7 +112,7 @@ fun Context.exportSong(song: Song, offlineUri: String) {
startActivity(
Intent.createChooser(
Intent(Intent.ACTION_SEND).apply {
- type = song.mime
+ type = mimeType
setDataAndType(fileUri, contentResolver.getType(fileUri))
putExtra(Intent.EXTRA_STREAM, fileUri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/common/Mappers.kt b/app/src/main/java/luci/sixsixsix/powerampache2/common/Mappers.kt
index a987a981..2f6089da 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/common/Mappers.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/common/Mappers.kt
@@ -4,9 +4,9 @@ import android.net.Uri
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.StarRating
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
-fun Song.toMediaItem(songUri: String) = MediaItem.Builder()
+fun SongUI.toMediaItem(songUri: String) = MediaItem.Builder()
.setMediaId(mediaId)
.setUri(songUri)
.setMimeType(mime)
@@ -25,8 +25,12 @@ fun Song.toMediaItem(songUri: String) = MediaItem.Builder()
.setGenre(if (genre.isNotEmpty()) { genre[0].name } else null)
.setComposer(composer)
.setAlbumArtist(albumArtist.name)
- .setOverallRating(StarRating(5, if (averageRating in 0f..5f) averageRating.toFloat() else 0f))
+ .setOverallRating(StarRating(
+ 5,
+ if (averageRating in 0f..5f) averageRating else 0f))
.setReleaseYear(year)
- .setUserRating(StarRating(5, if (rating in 0f..5f) rating.toFloat() else 0f))
- .build()
+ .setUserRating(StarRating(
+ 5,
+ if (rating in 0f..5f) rating else 0f)
+ ).build()
).build()
\ No newline at end of file
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/common/ShareManagerImpl.kt b/app/src/main/java/luci/sixsixsix/powerampache2/common/ShareManagerImpl.kt
index 818b317c..47a4a9ee 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/common/ShareManagerImpl.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/common/ShareManagerImpl.kt
@@ -46,6 +46,7 @@ class ShareManagerImpl @Inject constructor(
.append("/share")
.append("/song")
.append("/${song.id}")
+ // TODO: possibly blocking call in non-blocking context warnings
.append("/${URLEncoder.encode(song.title, "UTF-8")}")
.append("/${URLEncoder.encode(song.album.name,"UTF-8")}")
.append("/${URLEncoder.encode(song.artist.name,"UTF-8")}")
@@ -77,8 +78,8 @@ class ShareManagerImpl @Inject constructor(
id: String,
title: String,
artist: String,
- songCallback: (song: Song) -> Unit,
- songsCallback: (songs: List) -> Unit,
+ songCallback: suspend (song: Song) -> Unit,
+ songsCallback: suspend (songs: List) -> Unit,
errorCallback: () -> Unit
) {
val song = getSongFromIdUseCase(id)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandler.kt b/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandler.kt
index 77521ad1..19772d85 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandler.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandler.kt
@@ -1,13 +1,13 @@
package luci.sixsixsix.powerampache2.common.delegates
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
interface FetchArtistSongsHandler {
suspend fun getSongsFromArtist(
artistId: String,
isOfflineMode: Boolean,
fetchRemote: Boolean = true,
- songsCallback: (List) -> Unit,
+ songsCallback: (List) -> Unit,
loadingCallback: (Boolean) -> Unit = { },
errorCallback: (Throwable?) -> Unit = { }
)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandlerImpl.kt b/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandlerImpl.kt
index d0877554..53559a5f 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandlerImpl.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/common/delegates/FetchArtistSongsHandlerImpl.kt
@@ -1,17 +1,20 @@
package luci.sixsixsix.powerampache2.common.delegates
import luci.sixsixsix.powerampache2.common.Resource
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.usecase.artists.SongsFromArtistUseCase
+import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
class FetchArtistSongsHandlerImpl(
private val songsFromArtistUseCase: SongsFromArtistUseCase,
+ private val isSongAvailableOfflineUseCase: IsSongAvailableOfflineUseCase,
): FetchArtistSongsHandler {
override suspend fun getSongsFromArtist(
artistId: String,
isOfflineMode: Boolean,
fetchRemote: Boolean,
- songsCallback: (List) -> Unit,
+ songsCallback: (List) -> Unit,
loadingCallback: (Boolean) -> Unit,
errorCallback: (Throwable?) -> Unit
) {
@@ -24,7 +27,9 @@ class FetchArtistSongsHandlerImpl(
// check against network data but use db data.
// OR if in offline mode
result.data?.let { songs ->
- songsCallback(songs)
+ songsCallback(songs.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ })
}
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/di/AppModule.kt b/app/src/main/java/luci/sixsixsix/powerampache2/di/AppModule.kt
index e0e7b41b..43b1015f 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/di/AppModule.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/di/AppModule.kt
@@ -21,7 +21,6 @@
*/
package luci.sixsixsix.powerampache2.di
-import android.app.Application
import android.content.Context
import android.util.Log
import androidx.annotation.OptIn
@@ -38,12 +37,9 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.CoroutineScope
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.ConfigProviderImpl
import luci.sixsixsix.powerampache2.common.DataStringsProviderImpl
-import luci.sixsixsix.powerampache2.alarm.PingScheduler
-import luci.sixsixsix.powerampache2.domain.utils.AlarmScheduler
import luci.sixsixsix.powerampache2.domain.utils.ConfigProvider
import luci.sixsixsix.powerampache2.domain.utils.DataStringsProvider
import luci.sixsixsix.powerampache2.domain.utils.SharedPreferencesManager
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/di/BindModule.kt b/app/src/main/java/luci/sixsixsix/powerampache2/di/BindModule.kt
index 92deae01..1d7d78f5 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/di/BindModule.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/di/BindModule.kt
@@ -21,19 +21,15 @@
*/
package luci.sixsixsix.powerampache2.di
-import android.app.Application
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.CoroutineScope
import luci.sixsixsix.powerampache2.alarm.PingScheduler
import luci.sixsixsix.powerampache2.common.ShareManagerImpl
import luci.sixsixsix.powerampache2.common.WorkerHelperImpl
import luci.sixsixsix.powerampache2.domain.utils.AlarmScheduler
import luci.sixsixsix.powerampache2.domain.utils.ShareManager
-import luci.sixsixsix.powerampache2.domain.utils.SharedPreferencesManager
import luci.sixsixsix.powerampache2.domain.utils.WorkerHelper
import javax.inject.Singleton
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicController.kt b/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicController.kt
index 0b53462c..7516b736 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicController.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicController.kt
@@ -78,7 +78,7 @@ class MusicController @Inject constructor(
initController(context)
}
- // There are 2 ways to stop the music using the timer, if the setting"sleepTimerWaitSongEnd"
+ // There are 2 ways to stop the music using the timer, if the setting "sleepTimerWaitSongEnd"
// is not enabled, stop the music right away, to do so, listen to the alarm event sent
// through sleepTimerEventBus.sleepTimerEvents.
// If the setting"sleepTimerWaitSongEnd" is enabled, ignore the callback from the alarm
@@ -100,6 +100,7 @@ class MusicController @Inject constructor(
// Callback triggered every time a new song is being played
applicationCoroutineScope.launch {
+ // TODO: unused named lambda parameter, check if needed
playlistManager.currentSongState.filterNotNull().collectLatest { newSong ->
val sleepTimerEndTimestamp = sleepTimerEndTimestampFlow().value
if (
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicPlaylistManager.kt b/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicPlaylistManager.kt
index e93134bc..1f9e3791 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicPlaylistManager.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/player/MusicPlaylistManager.kt
@@ -24,26 +24,27 @@ package luci.sixsixsix.powerampache2.player
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import luci.sixsixsix.mrlog.L
-import luci.sixsixsix.powerampache2.domain.common.reduceList
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.reduceList
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MusicPlaylistManager @Inject constructor() {
- private val _currentSongState = MutableStateFlow(null)
- val currentSongState: StateFlow = _currentSongState //val currentSong = _currentSong.asStateFlow()
+ private val _currentSongState = MutableStateFlow(null)
+ val currentSongState: StateFlow = _currentSongState //val currentSong = _currentSong.asStateFlow()
private val _currentSearchQuery = MutableStateFlow("")
val currentSearchQuery: StateFlow = _currentSearchQuery
- private val _currentQueueState = MutableStateFlow(listOf())
- val currentQueueState: StateFlow> = _currentQueueState
+ private val _currentQueueState = MutableStateFlow(listOf())
+ val currentQueueState: StateFlow> = _currentQueueState
- private val _downloadedSongFlow = MutableStateFlow(null)
- val downloadedSongFlow: StateFlow = _downloadedSongFlow
+ private val _downloadedSongFlow = MutableStateFlow(null)
+ // TODO: is this needed?
+ val downloadedSongFlow: StateFlow = _downloadedSongFlow
- fun updateDownloadedSong(song: Song?) {
+ fun updateDownloadedSong(song: SongUI?) {
_downloadedSongFlow.value = song
}
@@ -57,7 +58,7 @@ class MusicPlaylistManager @Inject constructor() {
* one that is currently playing. Add a list of song to the queue state,if no song is currently
* set as state, automatically set the first song of the queue
*/
- fun addToCurrentQueueUpdateTopSong(newSong: Song, newQueue: List) {
+ fun addToCurrentQueueUpdateTopSong(newSong: SongUI, newQueue: List) {
// add the current song on top of the queue
val updatedQueue = ArrayList(_currentQueueState.value).apply {
remove(newSong)
@@ -77,7 +78,7 @@ class MusicPlaylistManager @Inject constructor() {
/**
* used in the callback when music player goes to the next song in the playlist
*/
- fun updateCurrentSong(newSong: Song?) {
+ fun updateCurrentSong(newSong: SongUI?) {
L( "MusicPlaylistManager updateCurrentSong", newSong)
_currentSongState.value = newSong
}
@@ -86,19 +87,19 @@ class MusicPlaylistManager @Inject constructor() {
* same as updateCurrentSong but also provides current queue
* TODO unused function
*/
- fun moveToSongInQueue(newSong: Song?, queue: List) = newSong?.let {
- L( "MusicPlaylistManager moveToSongInQueue", newSong)
- _currentSongState.value = newSong
- }
+ //fun moveToSongInQueue(newSong: SongUI?, queue: List) = newSong?.let {
+ // L( "MusicPlaylistManager moveToSongInQueue", newSong)
+ // _currentSongState.value = newSong
+ //}
- fun replaceCurrentQueue(newQueue: List) {
+ fun replaceCurrentQueue(newQueue: List) {
L( "MusicPlaylistManager replaceCurrentQueue", newQueue.size)
- _currentQueueState.value = newQueue.filterNotNull().reduceList()
+ _currentQueueState.value = newQueue.reduceList()
checkCurrentSong()
}
- fun replaceQueuePlaySong(newQueue: List, songToPlay: Song) {
- _currentQueueState.value = newQueue.filterNotNull().reduceList()
+ fun replaceQueuePlaySong(newQueue: List, songToPlay: SongUI) {
+ _currentQueueState.value = newQueue.reduceList()
_currentSongState.value = songToPlay
}
@@ -106,7 +107,7 @@ class MusicPlaylistManager @Inject constructor() {
* add a list of song to the queue state
* if no song is currently set as state, automatically set the first song of the queue
*/
- fun addToCurrentQueue(newQueue: List) {
+ fun addToCurrentQueue(newQueue: List) {
L( "MusicPlaylistManager addToCurrentQueue", newQueue.size)
_currentQueueState.value = LinkedHashSet(_currentQueueState.value)
.apply { addAll(newQueue) }
@@ -118,7 +119,7 @@ class MusicPlaylistManager @Inject constructor() {
/**
* adds the song to the current queue if the song is not null
*/
- fun addToCurrentQueue(newSong: Song?) = newSong?.let {
+ fun addToCurrentQueue(newSong: SongUI?) = newSong?.let {
L( "MusicPlaylistManager addToCurrentQueue", newSong)
addToCurrentQueue(listOf(newSong))
}
@@ -126,9 +127,9 @@ class MusicPlaylistManager @Inject constructor() {
/**
* removes a list of songs from the current queue
*/
- fun removeFromCurrentQueue(songsToRemove: List) {
+ fun removeFromCurrentQueue(songsToRemove: List) {
_currentQueueState.value = LinkedHashSet(_currentQueueState.value)
- .apply { removeAll(songsToRemove.filterNotNull().toSet()) }
+ .apply { removeAll(songsToRemove.toSet()) }
.toList()
// if the queue is empty after this operation also remove the current song
if (_currentQueueState.value.isEmpty()) {
@@ -140,13 +141,13 @@ class MusicPlaylistManager @Inject constructor() {
/**
* remove a single song from queue
*/
- fun removeFromCurrentQueue(songToRemove: Song) =
+ fun removeFromCurrentQueue(songToRemove: SongUI) =
removeFromCurrentQueue(listOf(songToRemove))
/**
* add items to the current queue as next in queue
*/
- fun addToCurrentQueueNext(list: List) {
+ fun addToCurrentQueueNext(list: List) {
L( "MusicPlaylistManager addToCurrentQueueNext", list.size)
val queue = ArrayList(_currentQueueState.value)
.apply {
@@ -167,9 +168,9 @@ class MusicPlaylistManager @Inject constructor() {
replaceCurrentQueue(queue)
}
- fun addToCurrentQueueTop(list: List) {
+ fun addToCurrentQueueTop(list: List) {
L( "MusicPlaylistManager addToCurrentQueueTop", list.size)
- val queue = ArrayList(currentQueueState.value).apply {
+ val queue = ArrayList(currentQueueState.value).apply {
addAll(0, list)
}
replaceCurrentQueue(queue)
@@ -187,7 +188,7 @@ class MusicPlaylistManager @Inject constructor() {
/**
* assign the new song state, remove the song from the queue if exists and re-add it on top
*/
- fun updateTopSong(newSong: Song) {
+ fun updateTopSong(newSong: SongUI) {
L("MusicPlaylistManager updateTopSong", newSong)
_currentSongState.value = newSong
// add the current song on top of the queue
@@ -197,7 +198,7 @@ class MusicPlaylistManager @Inject constructor() {
}
}
- fun addToCurrentQueueNext(song: Song?) = song?.let {
+ fun addToCurrentQueueNext(song: SongUI?) = song?.let {
L( "MusicPlaylistManager addToCurrentQueueNext", song)
addToCurrentQueueNext(listOf(song))
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/player/PlayerManager.kt b/app/src/main/java/luci/sixsixsix/powerampache2/player/PlayerManager.kt
index af2b528a..d6c31f15 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/player/PlayerManager.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/player/PlayerManager.kt
@@ -1,11 +1,8 @@
package luci.sixsixsix.powerampache2.player
import android.content.Context
-import android.os.Handler
-import android.os.Looper
import androidx.annotation.OptIn
import androidx.media3.common.AudioAttributes
-import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.cache.CacheDataSource
@@ -16,13 +13,8 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import dagger.hilt.android.qualifiers.ApplicationContext
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
-import luci.sixsixsix.powerampache2.BuildConfig
import luci.sixsixsix.powerampache2.domain.utils.SharedPreferencesManager
import javax.inject.Inject
import javax.inject.Singleton
@@ -112,5 +104,6 @@ class PlayerManager @OptIn(UnstableApi::class)
_playerState.value = null
}
+ // TODO: unused?
fun isPlayerInitialized(): Boolean = _player != null
}
\ No newline at end of file
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/player/SimpleMediaServiceHandler.kt b/app/src/main/java/luci/sixsixsix/powerampache2/player/SimpleMediaServiceHandler.kt
index f2d725c8..f5601019 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/player/SimpleMediaServiceHandler.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/player/SimpleMediaServiceHandler.kt
@@ -22,10 +22,8 @@
package luci.sixsixsix.powerampache2.player
import android.content.Context
-import android.content.Intent
import android.media.session.PlaybackState
import androidx.annotation.OptIn
-import androidx.lifecycle.viewModelScope
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED
@@ -58,6 +56,8 @@ import luci.sixsixsix.powerampache2.domain.errors.UserNotEnabledException
import javax.inject.Inject
import javax.inject.Singleton
+// TODO: there's a bunch of unused stuff in here, worth investigation and cleaning
+
@Singleton
class SimpleMediaServiceHandler @Inject constructor(
private val playerManager: PlayerManager,
@@ -106,7 +106,7 @@ class SimpleMediaServiceHandler @Inject constructor(
fun getMediaItemCount() = player().mediaItemCount
fun addMediaItemList(mediaItems: List) {
- if(mediaItems.isNullOrEmpty() && player().mediaItemCount == 0) return
+ if(mediaItems.isEmpty() && player().mediaItemCount == 0) return
if (player().mediaItemCount > 0 &&
playlistManager.currentSongState.value?.mediaId == player().currentMediaItem?.mediaId) {
// if the current song of the playlist (if playlist is not empty) corresponds to the current
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/TestScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/TestScreen.kt
index 59c06444..7ada2e0d 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/TestScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/TestScreen.kt
@@ -34,7 +34,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/AmpacheListItem.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/AmpacheListItem.kt
index d7323293..d33f72e1 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/AmpacheListItem.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/AmpacheListItem.kt
@@ -85,6 +85,8 @@ import luci.sixsixsix.powerampache2.domain.models.isOwnerAdmin
import luci.sixsixsix.powerampache2.domain.models.isOwnerSystem
import luci.sixsixsix.powerampache2.domain.models.isSmartPlaylist
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItemEvent
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.isAvailableOffline
data class InfoViewItemText(
val firstRowText: String,
@@ -97,7 +99,6 @@ fun AmpacheListItem(
item: T,
songItemEventListener: (songItemEvent: SongItemEvent) -> Unit,
modifier: Modifier = Modifier,
- isSongDownloaded: Boolean = false,
showDownloadedSongMarker: Boolean = false,
enableSwipeToRemove: Boolean = false,
onRemove: (AmpacheModel) -> Unit = {},
@@ -111,7 +112,7 @@ fun AmpacheListItem(
var showPublicBadge = false
when(item) {
- is Song -> {
+ is SongUI -> {
imageUrl = item.imageUrl
infoViewItemText = InfoViewItemText(
item.title,
@@ -141,9 +142,9 @@ fun AmpacheListItem(
} else stringResource(R.string.item_title_playlist)
} ?: stringResource(R.string.item_title_playlist)
- val items = item.items?.let {
+ val items = item.items.let {
if (it > 0) stringResource(id = R.string.playlistItem_songCount, it) else " "
- } ?: run { " " }
+ }
infoViewItemText = InfoViewItemText(item.name, items, ownerText)
isFavourite = item.flag == 1
rating = item.rating
@@ -162,9 +163,9 @@ fun AmpacheListItem(
imageUrl = imageUrl,
textInfo = infoViewItemText,
songItemEventListener = songItemEventListener,
- isSongDownloaded = isSongDownloaded,
+ isSongDownloaded = if (item is SongUI) { item.isAvailableOffline() } else false,
showDownloadedSongMarker = showDownloadedSongMarker,
- hideSongMenu = item !is Song,
+ hideSongMenu = item !is SongUI,
isFavourite = isFavourite,
rating = rating,
showAmpacheBadge = showAmpacheBadge,
@@ -292,7 +293,7 @@ fun AmpacheListItemMain(
SongDropDownMenu(
isContextMenuVisible = isContextMenuVisible,
pressOffset = pressOffset,
- isSongDownloaded = isSongDownloaded,
+ isSongAvailableOffline = isSongDownloaded,
songItemEventListener = {
isContextMenuVisible = false
songItemEventListener(it)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/SongDropDownMenu.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/SongDropDownMenu.kt
index 1075f1f6..d663d405 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/SongDropDownMenu.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/SongDropDownMenu.kt
@@ -59,7 +59,7 @@ fun SongDropDownMenu(
modifier: Modifier = Modifier,
isContextMenuVisible: Boolean,
pressOffset: DpOffset,
- isSongDownloaded: Boolean,
+ isSongAvailableOffline: Boolean,
songItemEventListener: (songItemEvent: SongItemEvent) -> Unit,
onDismissRequest:() -> Unit
) {
@@ -114,7 +114,7 @@ fun SongDropDownMenu(
) {
songItemEventListener(SongItemEvent.SHOW_SONG_INFO)
}
- if (isSongDownloaded) {
+ if (isSongAvailableOffline) {
SongDropDownMenuItem(
text = R.string.dropdownMenu_item_export,
iconImageVector = Icons.Default.SaveAlt
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/InfoTextSectionSongItem.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/InfoTextSectionSongItem.kt
index d20f0c62..69df5a21 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/InfoTextSectionSongItem.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/InfoTextSectionSongItem.kt
@@ -33,14 +33,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.fontDimensionResource
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.domain.models.totalTime
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.totalTime
@Composable
fun InfoTextSectionSongItem(
modifier: Modifier,
- song: Song,
+ song: SongUI,
subtitleString: SubtitleString,
songInfoThirdRow: SongInfoThirdRow = SongInfoThirdRow.AlbumTitle
) {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItem.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItem.kt
index 75ab3201..7076c530 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItem.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItem.kt
@@ -61,7 +61,8 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.isAvailableOffline
import luci.sixsixsix.powerampache2.presentation.common.SongDropDownMenu
import luci.sixsixsix.powerampache2.presentation.common.SwipeToDismissItem
@@ -71,23 +72,22 @@ enum class SubtitleString { NOTHING, ARTIST, ALBUM }
@Composable
fun SongItem(
- song: Song,
+ song: SongUI,
songItemEventListener: (songItemEvent: SongItemEvent) -> Unit,
modifier: Modifier = Modifier,
isEditMode: Boolean = false,
isEditEnabled: Boolean = true,
isLandscape: Boolean = false,
- isSongDownloaded: Boolean = false,
showDownloadedSongMarker: Boolean = true,
subtitleString: SubtitleString = SubtitleString.ARTIST,
songInfoThirdRow: SongInfoThirdRow = SongInfoThirdRow.AlbumTitle,
enableSwipeToRemove: Boolean = false,
isEditSongSelected: Boolean = false,
- onRemove: (Song) -> Unit = {},
- onRightToLeftSwipe: (Song) -> Unit = {},
- onEditMoveUp: (Song) -> Unit = { _ -> },
- onEditMoveDown: (Song) -> Unit = { _ -> },
- onEditSelected: (Boolean, Song) -> Unit = { _, _ -> }
+ onRemove: (SongUI) -> Unit = {},
+ onRightToLeftSwipe: (SongUI) -> Unit = {},
+ onEditMoveUp: (SongUI) -> Unit = { _ -> },
+ onEditMoveDown: (SongUI) -> Unit = { _ -> },
+ onEditSelected: (Boolean, SongUI) -> Unit = { _, _ -> }
) {
SwipeToDismissItem(
item = song,
@@ -98,7 +98,6 @@ fun SongItem(
songItemEventListener = songItemEventListener,
modifier = modifier,
isLandscape = isLandscape,
- isSongDownloaded = isSongDownloaded,
showDownloadedSongMarker = showDownloadedSongMarker,
subtitleString = subtitleString,
songInfoThirdRow = songInfoThirdRow
@@ -107,9 +106,8 @@ fun SongItem(
SongItemForegroundEdit(
song = song,
modifier = modifier,
- isSongDownloaded = isSongDownloaded,
isEditEnabled = isEditEnabled,
- showDownloadedSongMarker = showDownloadedSongMarker,
+ //showDownloadedSongMarker = showDownloadedSongMarker,
subtitleString = subtitleString,
songInfoThirdRow = songInfoThirdRow,
checked = isEditSongSelected,
@@ -128,11 +126,10 @@ fun SongItem(
@Composable
fun SongItemMain(
- song: Song,
+ song: SongUI,
songItemEventListener: (songItemEvent: SongItemEvent) -> Unit,
modifier: Modifier = Modifier,
isLandscape: Boolean,
- isSongDownloaded: Boolean,
showDownloadedSongMarker: Boolean,
subtitleString: SubtitleString = SubtitleString.ARTIST,
songInfoThirdRow: SongInfoThirdRow = SongInfoThirdRow.AlbumTitle
@@ -157,7 +154,6 @@ fun SongItemMain(
.background(Color.Transparent)
.align(Alignment.CenterVertically),
song = song,
- isSongDownloaded = isSongDownloaded,
showDownloadedSongMarker = showDownloadedSongMarker
)
}
@@ -204,7 +200,7 @@ fun SongItemMain(
SongDropDownMenu(
isContextMenuVisible = isContextMenuVisible,
pressOffset = pressOffset,
- isSongDownloaded = isSongDownloaded,
+ isSongAvailableOffline = song.isAvailableOffline(),
songItemEventListener = {
isContextMenuVisible = false
songItemEventListener(it)
@@ -218,8 +214,7 @@ fun SongItemMain(
@Composable
private fun SongAlbumCover(
modifier: Modifier,
- song: Song,
- isSongDownloaded: Boolean,
+ song: SongUI,
showDownloadedSongMarker: Boolean
) {
Card(
@@ -242,7 +237,7 @@ private fun SongAlbumCover(
error = painterResource(id = R.drawable.placeholder_album),
contentDescription = song.title,
)
- if(isSongDownloaded && showDownloadedSongMarker) {
+ if(song.isAvailableOffline() && showDownloadedSongMarker) {
Card(modifier = Modifier.size(20.dp)) {
Box(
modifier = Modifier
@@ -264,9 +259,8 @@ private fun SongAlbumCover(
@Composable
fun SongItemPreview() {
SongItem(
- song = Song.mockSong,
+ song = SongUI.mockSongUI,
songItemEventListener = {},
subtitleString = SubtitleString.NOTHING,
- isSongDownloaded = true
)
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItemForegroundEdit.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItemForegroundEdit.kt
index a6693cfb..13167025 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItemForegroundEdit.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongItemForegroundEdit.kt
@@ -43,19 +43,18 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
@Composable
fun SongItemForegroundEdit(
- song: Song,
+ song: SongUI,
modifier: Modifier = Modifier,
- isSongDownloaded: Boolean,
isEditEnabled: Boolean,
- showDownloadedSongMarker: Boolean,
+ //showDownloadedSongMarker: Boolean,
subtitleString: SubtitleString = SubtitleString.ARTIST,
songInfoThirdRow: SongInfoThirdRow = SongInfoThirdRow.AlbumTitle,
checked: Boolean,
- onCheckedChange: (Boolean, Song) -> Unit,
+ onCheckedChange: (Boolean, SongUI) -> Unit,
onMoveUp: () -> Unit,
onMoveDown: () -> Unit,
) {
@@ -148,9 +147,8 @@ fun SongItemForegroundEdit(
@Preview
fun SongItemForegroundEditPreview() {
SongItemForegroundEdit(
- song = Song.mockSong,
- isSongDownloaded = true,
- showDownloadedSongMarker = true,
+ song = SongUI.mockSongUI,
+ //showDownloadedSongMarker = true,
checked = true,
onMoveDown = {},
isEditEnabled = true,
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongWrapper.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongWrapper.kt
deleted file mode 100644
index 069c0fc6..00000000
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/common/songitem/SongWrapper.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (C) 2024 Antonio Tari
- *
- * This file is a part of Power Ampache 2
- * Ampache Android client application
- * @author Antonio Tari
- *
- * This program 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.
- *
- * This program 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 this program. If not, see .
- *
- */
-package luci.sixsixsix.powerampache2.presentation.common.songitem
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-import luci.sixsixsix.powerampache2.domain.models.Song
-
-@Parcelize
-data class SongWrapper(
- val song: Song,
- val isOffline: Boolean
-): Parcelable
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialog.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialog.kt
index bd2f26b4..8b694a44 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialog.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialog.kt
@@ -64,6 +64,7 @@ import luci.sixsixsix.powerampache2.common.RandomThemeBackgroundColour
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.PlaylistType
import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -74,7 +75,7 @@ val textPaddingVertical = 10.dp
data class AddToPlaylistOrQueueDialogOpen(
val isOpen: Boolean,
- val songs: List = listOf()
+ val songs: List = listOf()
)
/**
@@ -82,7 +83,7 @@ data class AddToPlaylistOrQueueDialogOpen(
*/
@Composable
fun AddToPlaylistOrQueueDialog(
- songs: List,
+ songs: List,
onDismissRequest: () -> Unit,
onCreatePlaylistRequest: (success: Boolean) -> Unit = {},
mainViewModel: MainViewModel,
@@ -194,7 +195,7 @@ fun AddToPlaylistOrQueueDialog(
private fun addToPlaylist(
context: Context,
viewModel: AddToPlaylistOrQueueDialogViewModel,
- songs: List,
+ songs: List,
playlist: Playlist
) {
when (songs.size) {
@@ -222,7 +223,7 @@ private fun addToPlaylist(
private fun addToQueue(
mainViewModel: MainViewModel,
viewModel: AddToPlaylistOrQueueDialogViewModel,
- songs: List
+ songs: List
) {
when (songs.size) {
1 -> mainViewModel.onEvent(MainEvent.OnAddSongToQueue(songs[0]))
@@ -236,7 +237,7 @@ private fun addToQueue(
private fun CreateAddToPlaylist(
context: Context,
viewModel: AddToPlaylistOrQueueDialogViewModel,
- songs: List,
+ songs: List,
onConfirm: (playlistName: String, playlistType: PlaylistType) -> Unit,
onCancel: () -> Unit
) {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialogViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialogViewModel.kt
index 0ee63b3f..dbe99be5 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialogViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/AddToPlaylistOrQueueDialogViewModel.kt
@@ -40,13 +40,14 @@ import luci.sixsixsix.powerampache2.domain.PlaylistsRepository
import luci.sixsixsix.powerampache2.domain.errors.ErrorHandler
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.PlaylistType
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.models.USER_SYSTEM
import luci.sixsixsix.powerampache2.domain.models.User
import luci.sixsixsix.powerampache2.domain.usecase.UserFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsFlow
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsUseCase
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.player.MusicPlaylistManager
+import luci.sixsixsix.powerampache2.presentation.models.toSong
import java.util.UUID
import javax.inject.Inject
@@ -151,10 +152,14 @@ class AddToPlaylistOrQueueDialogViewModel @Inject constructor(
private fun createPlaylistAndAddSongs(
playlistName: String,
playlistType: PlaylistType,
- songsToAdd: List
+ songsToAdd: List
) = viewModelScope.launch {
playlistsRepository
- .createNewPlaylistAddSongs(playlistName, playlistType, songsToAdd).collect { result ->
+ .createNewPlaylistAddSongs(
+ name = playlistName,
+ playlistType = playlistType,
+ songsToAdd = songsToAdd.map { it.toSong() },
+ ).collect { result ->
when (result) {
is Resource.Success -> {
result.data?.let {
@@ -188,10 +193,10 @@ class AddToPlaylistOrQueueDialogViewModel @Inject constructor(
}
}
- private fun addSongsToPlaylist(playlist: Playlist, songs: List) = viewModelScope.launch {
+ private fun addSongsToPlaylist(playlist: Playlist, songs: List) = viewModelScope.launch {
playlistsRepository.addSongsToPlaylist(
playlist = playlist,
- songsToAdd = songs
+ songsToAdd = songs.map { it.toSong() },
).collect { result ->
when (result) {
is Resource.Success -> {
@@ -215,9 +220,17 @@ data class AddToPlaylistOrQueueDialogState (
)
sealed class AddToPlaylistOrQueueDialogEvent {
- data class OnAddAlbumToQueue(val songs: List): AddToPlaylistOrQueueDialogEvent()
- data class AddSongsToPlaylist(val songs: List, val playlist: Playlist): AddToPlaylistOrQueueDialogEvent()
- data class CreatePlaylistAndAddSongs(val songs: List, val playlistName: String, val playlistType: PlaylistType): AddToPlaylistOrQueueDialogEvent()
- data class AddSongToPlaylist(val song: Song, val playlistId: String): AddToPlaylistOrQueueDialogEvent()
- data class CreatePlaylistAndAddSong(val song: Song, val playlistName: String, val playlistType: PlaylistType): AddToPlaylistOrQueueDialogEvent()
+ data class OnAddAlbumToQueue(val songs: List): AddToPlaylistOrQueueDialogEvent()
+ data class AddSongsToPlaylist(
+ val songs: List, val playlist: Playlist
+ ): AddToPlaylistOrQueueDialogEvent()
+ data class CreatePlaylistAndAddSongs(
+ val songs: List, val playlistName: String, val playlistType: PlaylistType
+ ): AddToPlaylistOrQueueDialogEvent()
+ data class AddSongToPlaylist(
+ val song: SongUI, val playlistId: String
+ ): AddToPlaylistOrQueueDialogEvent()
+ data class CreatePlaylistAndAddSong(
+ val song: SongUI, val playlistName: String, val playlistType: PlaylistType
+ ): AddToPlaylistOrQueueDialogEvent()
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/EraseConfirmDialog.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/EraseConfirmDialog.kt
index 0a06bdc9..e13637c2 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/EraseConfirmDialog.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/EraseConfirmDialog.kt
@@ -44,10 +44,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.common.RoundedCornerButton
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
data class ShowEraseConfirmDialog(
val isOpen: Boolean,
- val song: Song? = null,
+ val song: SongUI? = null,
)
@Composable
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogAlbum.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogAlbum.kt
index 7426b944..16bd1f10 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogAlbum.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogAlbum.kt
@@ -44,6 +44,7 @@ import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.capitalizeWords
import luci.sixsixsix.powerampache2.domain.models.Album
import luci.sixsixsix.powerampache2.domain.models.totalTime
+import luci.sixsixsix.powerampache2.presentation.models.totalTime
import luci.sixsixsix.powerampache2.domain.plugin.info.PluginAlbumData
import luci.sixsixsix.powerampache2.domain.plugin.info.totalTime
import luci.sixsixsix.powerampache2.presentation.common.MusicChips
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogSong.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogSong.kt
index 8031a200..f0de7b1b 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogSong.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/dialogs/info/InfoDialogSong.kt
@@ -43,23 +43,23 @@ import coil.compose.AsyncImage
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.capitalizeWords
import luci.sixsixsix.powerampache2.domain.common.toDebugMap
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.domain.models.totalTime
+import luci.sixsixsix.powerampache2.presentation.models.totalTime
import luci.sixsixsix.powerampache2.domain.plugin.info.PluginSongData
import luci.sixsixsix.powerampache2.domain.plugin.info.totalTime
import luci.sixsixsix.powerampache2.presentation.common.MusicChips
import luci.sixsixsix.powerampache2.presentation.dialogs.info.components.InfoDialogText
import luci.sixsixsix.powerampache2.presentation.dialogs.info.components.InfoDialogTextHorizontal
import luci.sixsixsix.powerampache2.presentation.dialogs.info.components.InfoDialogTitleText
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
data class ShowSongInfoDialogOpen(
val isOpen: Boolean,
- val song: Song? = null,
+ val song: SongUI? = null,
val songPlugin: PluginSongData? = null,
)
@Composable
-fun InfoDialogSong(song: Song, songPlugin: PluginSongData?, onDismissRequest: () -> Unit) {
+fun InfoDialogSong(song: SongUI, songPlugin: PluginSongData?, onDismissRequest: () -> Unit) {
InfoDialogBase(onDismissRequest) {
Card(
//border = BorderStroke((0.5).dp, MaterialTheme.colorScheme.background),
@@ -103,7 +103,6 @@ fun InfoDialogSong(song: Song, songPlugin: PluginSongData?, onDismissRequest: ()
if (dur > 0) songPlugin.totalTime() else song.totalTime()
} ?: song.totalTime()
-
val year = try { (songPlugin?.year ?: "0").toInt() } catch (e: Exception) { 0 }
InfoDialogTextHorizontal(name, "")
@@ -112,7 +111,7 @@ fun InfoDialogSong(song: Song, songPlugin: PluginSongData?, onDismissRequest: ()
if (artistName.isNotBlank()) InfoDialogTextHorizontal("Artist", artistName)
if (playCount > 0) InfoDialogTextHorizontal("Play Count", playCount.toString())
if (listeners > 0) InfoDialogTextHorizontal("Listeners", listeners.toString())
- InfoDialogTextHorizontal("Duration", duration.toString())
+ InfoDialogTextHorizontal("Duration", duration)
if (year > 0) InfoDialogTextHorizontal("Year", year.toString())
if (songPlugin?.language?.isNotBlank() == true) InfoDialogText("Language", songPlugin.language)
if (artistAlbum.isNotBlank()) InfoDialogTextHorizontal("Album Artist", artistAlbum)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/models/SongUI.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/models/SongUI.kt
new file mode 100644
index 00000000..d86eb3a5
--- /dev/null
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/models/SongUI.kt
@@ -0,0 +1,221 @@
+package luci.sixsixsix.powerampache2.presentation.models
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+import luci.sixsixsix.powerampache2.domain.common.Constants
+import luci.sixsixsix.powerampache2.domain.models.AmpacheModel
+import luci.sixsixsix.powerampache2.domain.models.MusicAttribute
+import luci.sixsixsix.powerampache2.domain.models.Song
+
+@Parcelize
+data class SongUI(
+ val mediaId: String,
+ override val id: String = mediaId,
+ val title: String,
+ val album: MusicAttribute,
+ val artist: MusicAttribute,
+ val albumArtist: MusicAttribute,
+ val songUrl: String,
+ val imageUrl: String,
+ val bitrate: Int,
+ val streamBitrate: Int,
+ val catalog: Int,
+ val channels: Int,
+ val composer: String,
+ val filename: String,
+ val genre: List,
+ val mime: String?,
+ val playCount: Int,
+ val playlistTrackNumber: Int,
+ val rateHz: Int,
+ val size: Int,
+ val time: Int,
+ val trackNumber: Int,
+ val year: Int,
+ val name: String,
+ val mode: String?,
+ val artists: List,
+ val flag: Int,
+ val streamFormat: String?,
+ val format: String?,
+ val streamMime: String?,
+ val publisher: String?,
+ val replayGainTrackGain: Float?,
+ val replayGainTrackPeak: Float?,
+ val disk: Int,
+ val diskSubtitle: String,
+ val mbId: String,
+ val comment: String,
+ val language: String,
+ val lyrics: String,
+ val albumMbId: String,
+ val artistMbId: String,
+ val albumArtistMbId: String,
+ val averageRating: Float,
+ val preciseRating: Float,
+ val rating: Float,
+ val isDownloaded: Boolean,
+): Comparable, Parcelable, AmpacheModel {
+ override fun compareTo(other: SongUI): Int = mediaId.compareTo(other.mediaId)
+
+ companion object {
+ val mockSongUI = Song(
+ mediaId = "12345",
+ title = "Stabwound",
+ artist = MusicAttribute("666", "Necrophagist"),
+ album = MusicAttribute("2004", "Epitaph"),
+ albumArtist = MusicAttribute.emptyInstance(),
+ genre = listOf(MusicAttribute.randomInstance(),
+ MusicAttribute.randomInstance(),
+ MusicAttribute.randomInstance()
+ ),
+ songUrl = "http://192.168.200.200/ampache/public/play/index.php?ssid=bd15d8f22785f5176aa2f783f88616f3&type=song&oid=12345&uid=1&player=api&name=Necrophagist%20-%20Stabwound.mp3",
+ imageUrl = "http://192.168.200.200/ampache/public/image.php?object_id=1986&object_type=album&auth=bd15d8f22785f5176aa2f783f88616f3&name=art.jpg",
+ averageRating = Constants.ERROR_FLOAT,
+ preciseRating = Constants.ERROR_FLOAT,
+ rating = Constants.ERROR_FLOAT,
+ ).toSongUI(true)
+
+ fun mapSongs(songs: List) = LinkedHashMap().apply {
+ songs.forEach {
+ put(it.mediaId, it)
+ }
+ }
+ }
+}
+
+fun SongUI.isAvailableOffline() = isDownloaded
+
+fun SongUI.hasLyrics() = lyrics.isNotBlank()
+
+fun SongUI.isFavourite() = flag != 0
+
+fun SongUI.totalTime(): String {
+ val minutes = time / 60
+ val seconds = time % 60
+ return "$minutes:${if (seconds < 10) { "0" } else { "" } }${seconds}"
+}
+
+fun List.reduceList() = if (size > Constants.config.queueSizeLimit) {
+ subList(0, Constants.config.queueSizeLimit) } else this
+
+/** Returns a "presentation" level SongUI model of the Song**/
+fun Song.toSongUI(isDownloaded: Boolean = false) = SongUI(
+ mediaId = mediaId,
+ title = title,
+ artist = artist,
+ album = album,
+ albumArtist = albumArtist,
+ songUrl = songUrl,
+ imageUrl = imageUrl,
+ bitrate = bitrate,
+ streamBitrate = streamBitrate,
+ catalog = catalog,
+ channels = channels,
+ composer = composer,
+ filename = filename,
+ genre = genre,
+ mime = mime,
+ name = name,
+ playCount = playCount,
+ playlistTrackNumber = playlistTrackNumber,
+ rateHz = rateHz,
+ size = size,
+ time = time,
+ trackNumber = trackNumber,
+ year = year,
+ mode = mode,
+ artists = artists,
+ flag = flag,
+ streamFormat = streamFormat,
+ format = format,
+ streamMime = streamMime,
+ publisher = publisher,
+ replayGainTrackGain = replayGainTrackGain,
+ replayGainTrackPeak = replayGainTrackPeak,
+ lyrics = lyrics,
+ comment = comment,
+ language = language,
+ disk = disk,
+ diskSubtitle = diskSubtitle,
+ mbId = mbId,
+ albumMbId = albumMbId,
+ artistMbId = artistMbId,
+ albumArtistMbId = albumArtistMbId,
+ rating = rating,
+ preciseRating = preciseRating,
+ averageRating = averageRating,
+ isDownloaded = isDownloaded,
+)
+
+/** Returns a domain level Song model version of the SongUI **/
+fun SongUI.toSong() = Song(
+ mediaId = mediaId,
+ title = title,
+ artist = artist,
+ album = album,
+ albumArtist = albumArtist,
+ songUrl = songUrl,
+ imageUrl = imageUrl,
+ bitrate = bitrate,
+ streamBitrate = streamBitrate,
+ catalog = catalog,
+ channels = channels,
+ composer = composer,
+ filename = filename,
+ genre = genre,
+ mime = mime,
+ name = name,
+ playCount = playCount,
+ playlistTrackNumber = playlistTrackNumber,
+ rateHz = rateHz,
+ size = size,
+ time = time,
+ trackNumber = trackNumber,
+ year = year,
+ mode = mode,
+ artists = artists,
+ flag = flag,
+ streamFormat = streamFormat,
+ format = format,
+ streamMime = streamMime,
+ publisher = publisher,
+ replayGainTrackGain = replayGainTrackGain,
+ replayGainTrackPeak = replayGainTrackPeak,
+ lyrics = lyrics,
+ comment = comment,
+ language = language,
+ disk = disk,
+ diskSubtitle = diskSubtitle,
+ mbId = mbId,
+ albumMbId = albumMbId,
+ artistMbId = artistMbId,
+ albumArtistMbId = albumArtistMbId,
+ rating = rating,
+ preciseRating = preciseRating,
+ averageRating = averageRating,
+)
+
+/** Returns a list of "presentation" level Song models, mapped from the list of Song **/
+suspend fun List.toSongUI(isAvailableOfflineCallback: suspend (Song) -> Boolean): List {
+ val songUiList = mutableListOf()
+ this.forEach {
+ songUiList.add(
+ it.toSongUI(
+ isAvailableOfflineCallback(it)
+ )
+ )
+ }
+ return songUiList
+}
+
+/** Returns a list of domain level Song models, mapped from the list of SongUI **/
+fun List.toSong(): List {
+ val songList = mutableListOf()
+ this.forEach {
+ songList.add(
+ it.toSong()
+ )
+ }
+ return songList
+}
\ No newline at end of file
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsScreen.kt
index dacb44f7..6483aa07 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsScreen.kt
@@ -59,10 +59,10 @@ const val GRID_ITEMS_ROW_LAND = 6
@Destination
@Composable
fun ArtistsScreen(
+ modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
gridPerRow: Int = GRID_ITEMS_ROW,
viewModel: ArtistsViewModel = hiltViewModel(),
- modifier: Modifier = Modifier
) {
ArtistsScreenContent(
artists = viewModel.state.artists,
@@ -78,14 +78,14 @@ fun ArtistsScreen(
@Destination
@Composable
fun ArtistsScreenContent(
+ modifier: Modifier = Modifier,
artists: List,
isLoading: Boolean,
isRefreshing: Boolean,
gridPerRow: Int = GRID_ITEMS_ROW,
- modifier: Modifier = Modifier,
swipeToRefreshEnabled: Boolean = true,
onEvent: (ArtistEvent) -> Unit,
- navigateToArtist: (artistId: String, artist: Artist) -> Unit
+ navigateToArtist: (artistId: String, artist: Artist) -> Unit,
) {
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing)
var orientation by remember { mutableIntStateOf(Configuration.ORIENTATION_PORTRAIT) }
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsViewModel.kt
index cc30cefa..cc82bc26 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/artists/ArtistsViewModel.kt
@@ -62,6 +62,7 @@ class ArtistsViewModel @Inject constructor(
getArtists(fetchRemote = true)
}
is ArtistEvent.OnSearchQueryChange -> {
+ // TODO: invert check to remove if empty body
if (event.query.isBlank() && state.searchQuery.isBlank()) {
} else {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreen.kt
index fd41ff06..664e6d46 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreen.kt
@@ -67,7 +67,7 @@ sealed class HomeScreenRowItems(@StringRes val title: Int, val items: List,
randomAlbums: List
) =
- frequentAlbums.isNullOrEmpty() &&
- recentAlbums.isNullOrEmpty() &&
- randomAlbums.isNullOrEmpty() &&
- state.newestAlbums.isNullOrEmpty() &&
- playlists.isNullOrEmpty()
+ frequentAlbums.isEmpty() &&
+ recentAlbums.isEmpty() &&
+ randomAlbums.isEmpty() &&
+ state.newestAlbums.isEmpty() &&
+ playlists.isEmpty()
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreenViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreenViewModel.kt
index 68cc27c1..bd2eccb0 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreenViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/HomeScreenViewModel.kt
@@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -47,7 +46,6 @@ import luci.sixsixsix.powerampache2.common.Resource
import luci.sixsixsix.powerampache2.common.delegates.FetchArtistSongsHandler
import luci.sixsixsix.powerampache2.common.delegates.FetchArtistSongsHandlerImpl
import luci.sixsixsix.powerampache2.domain.AlbumsRepository
-import luci.sixsixsix.powerampache2.domain.PlaylistsRepository
import luci.sixsixsix.powerampache2.domain.models.Album
import luci.sixsixsix.powerampache2.domain.models.AmpacheModel
import luci.sixsixsix.powerampache2.domain.models.Artist
@@ -56,7 +54,6 @@ import luci.sixsixsix.powerampache2.domain.models.FrequentPlaylist
import luci.sixsixsix.powerampache2.domain.models.HighestPlaylist
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.RecentPlaylist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.usecase.albums.RecommendedAlbumsFlow
import luci.sixsixsix.powerampache2.domain.usecase.artists.MostPlayedArtistsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.artists.RecommendedArtistsFlow
@@ -64,6 +61,8 @@ import luci.sixsixsix.powerampache2.domain.usecase.artists.SongsFromArtistUseCas
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsFlow
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.settings.OfflineModeFlowUseCase
+import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import javax.inject.Inject
@HiltViewModel
@@ -75,8 +74,12 @@ class HomeScreenViewModel @Inject constructor(
recommendedArtistsFlow: RecommendedArtistsFlow,
recommendedAlbumsFlow: RecommendedAlbumsFlow,
offlineModeFlowUseCase: OfflineModeFlowUseCase,
- songsFromArtistUseCase: SongsFromArtistUseCase
-) : ViewModel(), FetchArtistSongsHandler by FetchArtistSongsHandlerImpl(songsFromArtistUseCase) {
+ songsFromArtistUseCase: SongsFromArtistUseCase,
+ isSongAvailableOfflineUseCase: IsSongAvailableOfflineUseCase,
+) : ViewModel(), FetchArtistSongsHandler by FetchArtistSongsHandlerImpl(
+ songsFromArtistUseCase,
+ isSongAvailableOfflineUseCase,
+ ) {
var state by mutableStateOf(HomeScreenState())
private var _recentNetwork: MutableStateFlow> = MutableStateFlow(listOf())
@@ -186,7 +189,7 @@ class HomeScreenViewModel @Inject constructor(
init {
viewModelScope.launch {
// playlists can change or be edited, make sure to always listen to the latest version
- offlineModeStateFlow.collectLatest { isOfflineMode ->
+ offlineModeStateFlow.collectLatest {
fetchAllAsync()
}
}
@@ -314,6 +317,7 @@ class HomeScreenViewModel @Inject constructor(
}
// ---- NEWEST
+ // TODO: fetchRemote isn't used?
private suspend fun getNewest(fetchRemote: Boolean = true) {
albumsRepository
.getNewestAlbums()
@@ -398,6 +402,7 @@ class HomeScreenViewModel @Inject constructor(
// ---- RANDOM
private suspend fun getRandom(fetchRemote: Boolean = true) {
+ // TODO: empty callback?
getRandom(fetchRemote = fetchRemote, injectArtists = true) { albums ->
//state = state.copy(randomAlbums = albums)
}
@@ -436,7 +441,7 @@ class HomeScreenViewModel @Inject constructor(
fetchRemote: Boolean = false,
callback: (albums: List) -> Unit
) {
- if (albums.isNullOrEmpty()) {
+ if (albums.isEmpty()) {
getRandom(fetchRemote = fetchRemote, injectArtists = true) { albums ->
callback(albums)
}
@@ -505,7 +510,7 @@ class HomeScreenViewModel @Inject constructor(
fun fetchSongsFromArtist(
artist: Artist,
fetchRemote: Boolean = true,
- songsCallback: (List) -> Unit
+ songsCallback: (List) -> Unit
) {
fetchSongsArtistJob?.cancel()
fetchSongsArtistJob = viewModelScope.launch {
@@ -515,7 +520,7 @@ class HomeScreenViewModel @Inject constructor(
isOfflineMode = offlineModeStateFlow.value,
songsCallback = songsCallback,
loadingCallback = { state = state.copy(currentArtistPlayLoading = if (it) artist else null) },
- errorCallback = { state.copy(currentArtistPlayLoading = null) }
+ errorCallback = { state = state.copy(currentArtistPlayLoading = null) }
)
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/components/ArtistItemSquare.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/components/ArtistItemSquare.kt
index cfd50e76..ef03a381 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/components/ArtistItemSquare.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/home/components/ArtistItemSquare.kt
@@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.sharp.PlayArrow
-import androidx.compose.material.icons.sharp.PlayCircleOutline
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoggedInScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoggedInScreen.kt
index 6f8d9401..d9ddbfa8 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoggedInScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoggedInScreen.kt
@@ -53,9 +53,9 @@ import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.navigation.dependency
import luci.sixsixsix.mrlog.L
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.NavGraphs
import luci.sixsixsix.powerampache2.presentation.dialogs.IntroDialog
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.AuthViewModel
import luci.sixsixsix.powerampache2.presentation.screens.main.screens.components.CheckCustomStoragePermissionDialog
import luci.sixsixsix.powerampache2.presentation.screens.main.screens.components.SheetDragHandle
@@ -166,5 +166,5 @@ fun LoggedInScreen(
}
@Composable
-fun getPeakHeight(song: Song?): Dp = //TODO find a way to animate this (low-priority)
+fun getPeakHeight(song: SongUI?): Dp = //TODO find a way to animate this (low-priority)
if (song == null) { 0.dp } else { dimensionResource(id = R.dimen.miniPlayer_height) }
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoginScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoginScreen.kt
index 3a2e3343..c2690870 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoginScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/LoginScreen.kt
@@ -182,7 +182,7 @@ fun LoginScreenContent(
contentDescription = "Power Ampache Title"
)
- if (!error.isNullOrBlank()) {
+ if (error.isNotBlank()) {
ErrorView(
errorString = error,
modifier = Modifier
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/MainContentScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/MainContentScreen.kt
index 1efb4754..7080cbe6 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/MainContentScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/MainContentScreen.kt
@@ -21,7 +21,6 @@
*/
package luci.sixsixsix.powerampache2.presentation.screens.main.screens
-import android.content.Intent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
@@ -74,7 +73,6 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/CheckCustomStoragePermissionDialog.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/CheckCustomStoragePermissionDialog.kt
index 987b619f..6426a30e 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/CheckCustomStoragePermissionDialog.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/CheckCustomStoragePermissionDialog.kt
@@ -33,7 +33,8 @@ import luci.sixsixsix.powerampache2.presentation.screens.settings.SettingsViewMo
true -> {
val rootUri = settingsViewModel.playerSettingsStateFlow
.collectAsStateWithLifecycle().value.customDownloadLocation
- checkCustomStoragePermission(rootUri) {
+
+ CheckCustomStoragePermission(rootUri) {
settingsViewModel.onEvent(SettingsEvent.OnChooseCustomDirDownloads(it))
}
}
@@ -44,7 +45,7 @@ import luci.sixsixsix.powerampache2.presentation.screens.settings.SettingsViewMo
}
@Composable
-private fun checkCustomStoragePermission(rootUri: Uri?, onSelectCustomDir: (Uri) -> Unit) {
+private fun CheckCustomStoragePermission(rootUri: Uri?, onSelectCustomDir: (Uri) -> Unit) {
rootUri?.let { uri ->
val context = LocalContext.current
if(!context.hasPersistedWritePermission(uri)) {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/Drawer.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/Drawer.kt
index a8ccaea6..9c1bbd84 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/Drawer.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/Drawer.kt
@@ -96,13 +96,13 @@ val drawerItems = listOf(
@Composable
fun MainDrawer(
+ modifier: Modifier = Modifier,
user: User,
versionInfo: String,
hideDonationButtons: Boolean,
currentItem: MainContentMenuItem,
items: List = drawerItems,
onItemClick: (MainContentMenuItem) -> Unit,
- modifier: Modifier = Modifier,
donateButton: @Composable () -> Unit = { DonateButton(
isTransparent = true,
modifier = Modifier.padding(horizontal = 16.dp)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoadingShimmerScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoadingShimmerScreen.kt
index 643864bd..c2ae7ca0 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoadingShimmerScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoadingShimmerScreen.kt
@@ -77,7 +77,7 @@ fun LoadingShimmerScreen() {
@Composable
fun LoadingShimmerScreenSection() {
- Column() {
+ Column {
Text(
modifier = Modifier
.width(200.dp)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginSheet.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginSheet.kt
index 1cf6559f..4cb2d778 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginSheet.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginSheet.kt
@@ -46,7 +46,6 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextField.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextField.kt
index 479a7c63..b68b1933 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextField.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextField.kt
@@ -25,7 +25,6 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextFields.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextFields.kt
index 0ff2bf18..a9fa1fc7 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextFields.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/LoginTextFields.kt
@@ -38,7 +38,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/MainContentTopAppBar.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/MainContentTopAppBar.kt
index bd61a364..9fb70bde 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/MainContentTopAppBar.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/MainContentTopAppBar.kt
@@ -38,7 +38,6 @@ import androidx.compose.material.icons.automirrored.filled.PlaylistAdd
import androidx.compose.material.icons.filled.Cast
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.NotificationImportant
-import androidx.compose.material.icons.filled.NotificationsNone
import androidx.compose.material.icons.filled.PlayCircle
import androidx.compose.material.icons.filled.QueueMusic
import androidx.compose.material.icons.filled.Search
@@ -64,7 +63,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.presentation.common.CircleBackButton
-import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialogOpen
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/SignupSheet.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/SignupSheet.kt
index 615d77bf..7bab432b 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/SignupSheet.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/screens/components/SignupSheet.kt
@@ -52,7 +52,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEvent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEvent.kt
index 8e9652c2..c80a31fe 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEvent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEvent.kt
@@ -21,18 +21,18 @@
*/
package luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
sealed class MainEvent {
data class OnSearchQueryChange(val query: String): MainEvent()
data object OnDismissUserMessage: MainEvent()
data object OnEnableOfflineMode: MainEvent()
data object OnLogout: MainEvent() // TODO move this to AuthViewModel
- data class AddSongsToQueueAndPlay(val song: Song, val songList: List): MainEvent()
- data class AddSongsToQueueAndPlayShuffled(val songList: List): MainEvent()
- data class PlaySongAddToQueueTop(val song: Song, val songList: List): MainEvent()
- data class PlaySongReplacePlaylist(val song: Song, val songList: List): MainEvent()
- data class PlaySong(val song: Song): MainEvent()
+ data class AddSongsToQueueAndPlay(val song: SongUI, val songList: List): MainEvent()
+ data class AddSongsToQueueAndPlayShuffled(val songList: List): MainEvent()
+ data class PlaySongAddToQueueTop(val song: SongUI, val songList: List): MainEvent()
+ data class PlaySongReplacePlaylist(val song: SongUI, val songList: List): MainEvent()
+ data class PlaySong(val song: SongUI): MainEvent()
data object PlayPauseCurrent: MainEvent()
data object SkipNext: MainEvent()
data object SkipPrevious: MainEvent()
@@ -43,18 +43,18 @@ sealed class MainEvent {
data object Reset: MainEvent()
data object FavouriteSong: MainEvent()
data class UpdateProgress(val newProgress: Float): MainEvent()
- data class OnAddSongToQueue(val song: Song): MainEvent()
- data class OnAddSongToPlaylist(val song: Song): MainEvent()
- data class OnAddSongToQueueNext(val song: Song): MainEvent()
- data class OnShareSong(val song: Song): MainEvent()
- data class OnShareSongWebUrl(val song: Song): MainEvent()
- data class OnRateSong(val song: Song, val rate: Int): MainEvent()
- data class OnDownloadSong(val song: Song): MainEvent()
- data class OnDownloadSongs(val songs: List): MainEvent()
+ data class OnAddSongToQueue(val song: SongUI): MainEvent()
+ data class OnAddSongToPlaylist(val song: SongUI): MainEvent()
+ data class OnAddSongToQueueNext(val song: SongUI): MainEvent()
+ data class OnShareSong(val song: SongUI): MainEvent()
+ data class OnShareSongWebUrl(val song: SongUI): MainEvent()
+ data class OnRateSong(val song: SongUI, val rate: Int): MainEvent()
+ data class OnDownloadSong(val song: SongUI): MainEvent()
+ data class OnDownloadSongs(val songs: List): MainEvent()
data object OnStopDownloadSongs: MainEvent()
- data class OnDownloadedSongDelete(val song: Song): MainEvent()
- data class OnDownloadedSongListDelete(val songs: List): MainEvent()
- data class OnExportDownloadedSong(val song: Song): MainEvent()
+ data class OnDownloadedSongDelete(val song: SongUI): MainEvent()
+ data class OnDownloadedSongListDelete(val songs: List): MainEvent()
+ data class OnExportDownloadedSong(val song: SongUI): MainEvent()
data object OnFabPress: MainEvent()
data object OnCastPress: MainEvent()
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEventHandlerExt.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEventHandlerExt.kt
index 8c75fe00..78c3db55 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEventHandlerExt.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainEventHandlerExt.kt
@@ -35,9 +35,9 @@ import luci.sixsixsix.powerampache2.common.exportSong
import luci.sixsixsix.powerampache2.common.startCastPluginActivity
import luci.sixsixsix.powerampache2.common.toMediaItem
import luci.sixsixsix.powerampache2.worker.SongDownloadWorker
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.player.PlayerEvent
import luci.sixsixsix.powerampache2.player.PlayerEvent.*
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.toSong
/**
* UI ACTIONS AND EVENTS (play, stop, skip, like, download, etc ...)
@@ -93,10 +93,10 @@ fun MainViewModel.handleEvent(event: MainEvent, context: Context) {
is MainEvent.OnDownloadSong ->
downloadSong(event.song)
is MainEvent.OnShareSong -> viewModelScope.launch {
- shareManager.shareSongDeepLink(context, event.song)
+ shareManager.shareSongDeepLink(context, event.song.toSong())
}
is MainEvent.OnShareSongWebUrl -> viewModelScope.launch {
- shareManager.shareSongWeb(context, event.song)
+ shareManager.shareSongWeb(context, event.song.toSong())
}
is MainEvent.Repeat -> viewModelScope.launch {
val nextRepeatMode = nextRepeatMode()
@@ -108,20 +108,20 @@ fun MainViewModel.handleEvent(event: MainEvent, context: Context) {
shuffleOn = event.shuffleOn
}
is MainEvent.SkipNext -> viewModelScope.launch {
- simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.SkipForward)
+ simpleMediaServiceHandler.onPlayerEvent(SkipForward)
}
is MainEvent.SkipPrevious -> viewModelScope.launch {
- simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.SkipBack)
+ simpleMediaServiceHandler.onPlayerEvent(SkipBack)
}
is MainEvent.UpdateProgress -> viewModelScope.launch {
progress = event.newProgress
simpleMediaServiceHandler.onPlayerEvent(Progress(event.newProgress))
}
MainEvent.Backwards -> viewModelScope.launch {
- simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.Backward)
+ simpleMediaServiceHandler.onPlayerEvent(Backward)
}
MainEvent.Forward -> viewModelScope.launch {
- simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.Forward)
+ simpleMediaServiceHandler.onPlayerEvent(Forward)
}
MainEvent.FavouriteSong -> currentSong()?.let {
favouriteSong(it)
@@ -144,7 +144,10 @@ fun MainViewModel.handleEvent(event: MainEvent, context: Context) {
}
is MainEvent.OnExportDownloadedSong -> viewModelScope.launch {
try {
- context.exportSong(event.song, songsRepository.getSongUri(event.song))
+ context.exportSong(
+ mimeType = event.song.mime,
+ offlineUri = songsRepository.getSongUri(event.song.toSong()),
+ )
} catch (e: Exception) {
errorHandler.updateErrorLogMessage(e.stackTraceToString())
}
@@ -169,17 +172,18 @@ fun MainViewModel.handleEvent(event: MainEvent, context: Context) {
// send queue to cast plugin
if (isChromecastPluginInstalled()) {
viewModelScope.launch {
- sendQueueToChromecastUseCase(currentQueue().value).also { isSuccess ->
- if (!isSuccess) {
- // this is just a safety net, the error should never happen because
- // the queue has been reduced before sending it to the Cast plugin.
- Toast.makeText(context,
- context.getString(R.string.plugin_cast_queueTooLarge_error),
- Toast.LENGTH_LONG
- ).show()
- // TODO: showing toast from view model, violating Clean Architecture?
+ sendQueueToChromecastUseCase(currentQueue().value.toSong())
+ .also { isSuccess ->
+ if (!isSuccess) {
+ // this is just a safety net, the error should never happen because
+ // the queue has been reduced before sending it to the Cast plugin.
+ Toast.makeText(context,
+ context.getString(R.string.plugin_cast_queueTooLarge_error),
+ Toast.LENGTH_LONG
+ ).show()
+ // TODO: showing toast from view model, violating Clean Architecture?
+ }
}
- }
}
}
context.startCastPluginActivity()
@@ -192,7 +196,7 @@ fun MainViewModel.handleEvent(event: MainEvent, context: Context) {
* to play albums and playlists
*/
@UnstableApi
-fun MainViewModel.addSongsToQueueAndPlay(song: Song, songList: List) {
+fun MainViewModel.addSongsToQueueAndPlay(song: SongUI, songList: List) {
startPlayLoading()
playlistManager.updateCurrentSong(song)
playlistManager.addToCurrentQueueTop(songList)
@@ -203,7 +207,7 @@ fun MainViewModel.addSongsToQueueAndPlay(song: Song, songList: List) {
* select a single song, play, and put it on the top of the queue
* the song list is just for verification (TODO: should that be optional?)
*/
-private fun MainViewModel.playSongAddToQueueTop(song: Song, songList: List) {
+private fun MainViewModel.playSongAddToQueueTop(song: SongUI, songList: List) {
startPlayLoading()
playlistManager.addToCurrentQueueUpdateTopSong(song, songList)
play(song)
@@ -214,7 +218,7 @@ private fun MainViewModel.playSongAddToQueueTop(song: Song, songList: List
* the song list is just for verification (TODO: should that be optional?)
*/
@OptIn(UnstableApi::class)
-private fun MainViewModel.playSongReplacePlaylist(song: Song, songList: List) {
+private fun MainViewModel.playSongReplacePlaylist(song: SongUI, songList: List) {
startPlayLoading()
playlistManager.replaceQueuePlaySong(songList, song)
play(song)
@@ -223,13 +227,13 @@ private fun MainViewModel.playSongReplacePlaylist(song: Song, songList: List) {
+private fun MainViewModel.addSongsToQueueAndPlayShuffled(songList: List) {
startPlayLoading()
val shuffled = songList.shuffled()
playlistManager.replaceCurrentQueue(shuffled)
@@ -246,7 +250,7 @@ private fun MainViewModel.addSongsToQueueAndPlayShuffled(songList: List)
*
* call stopPlayLoading() in case of errors
*/
-private fun MainViewModel.play(song: Song) {
+private fun MainViewModel.play(song: SongUI) {
startPlayLoading()
if (loadSongDataJob?.isActive == true) {
loadSongDataJob?.invokeOnCompletion {
@@ -263,17 +267,19 @@ private fun MainViewModel.play(song: Song) {
}
}
-private fun MainViewModel.playSongForce(song: Song) = viewModelScope.launch {
+private fun MainViewModel.playSongForce(song: SongUI) = viewModelScope.launch {
L( "MainEvent.Play", "playing song")
try {
simpleMediaServiceHandler.onPlayerEvent(
- PlayerEvent.ForcePlay(
- song.toMediaItem(songsRepository.getSongUri(song)))
+ ForcePlay(
+ song.toMediaItem(songsRepository.getSongUri(song.toSong()))
+ )
)
} catch (e: Exception) {
logToErrorLogs("fun MainViewModel.playSongForce EXCEPTION, loading song data now")
logToErrorLogs(e.stackTraceToString())
}
+
stopPlayLoading()
L( "MainEvent.Play", "aaaa play song launched. After")
}
@@ -283,7 +289,7 @@ private fun MainViewModel.playPauseSong() = viewModelScope.launch {
startMusicServiceIfNecessary()
L( "MainEvent.Play", "playing song")
try {
- simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.PlayPause)
+ simpleMediaServiceHandler.onPlayerEvent(PlayPause)
} catch (e: Exception) {
stopPlayLoading()
logToErrorLogs(e.stackTraceToString())
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainViewModel.kt
index 193beed0..f1812cc2 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/MainViewModel.kt
@@ -58,8 +58,7 @@ import luci.sixsixsix.powerampache2.domain.SongsRepository
import luci.sixsixsix.powerampache2.domain.common.Constants
import luci.sixsixsix.powerampache2.domain.common.WeakContext
import luci.sixsixsix.powerampache2.domain.errors.ErrorHandler
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.domain.models.isFavourite
+import luci.sixsixsix.powerampache2.presentation.models.isFavourite
import luci.sixsixsix.powerampache2.domain.usecase.DownloadSongUseCase
import luci.sixsixsix.powerampache2.domain.usecase.SessionFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.plugin.IsChromecastPluginInstalled
@@ -75,6 +74,9 @@ import luci.sixsixsix.powerampache2.player.MusicPlaylistManager
import luci.sixsixsix.powerampache2.player.PlayerEvent
import luci.sixsixsix.powerampache2.player.RepeatMode
import luci.sixsixsix.powerampache2.player.SimpleMediaServiceHandler
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.toSong
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
import kotlin.math.abs
@@ -130,8 +132,8 @@ class MainViewModel @Inject constructor(
var emittedDownloads by savedStateHandle.saveable { mutableStateOf(listOf()) }
// TODO: there is no queue to restore! because the queue is in MusicPlaylistManager
- var restoredSong: Song? = null
- var restoredQueue = listOf()
+ var restoredSong: SongUI? = null
+ var restoredQueue = listOf()
val mainLock = Any()
@@ -147,6 +149,7 @@ class MainViewModel @Inject constructor(
delay(6000)
if (Constants.config.featureString.isNotBlank() &&
application.isFeatureAvailable(Constants.config.featureString)) {
+ // TODO: investigate if we should use a kotlin function here instead
System.exit(0)
}
}
@@ -161,10 +164,6 @@ class MainViewModel @Inject constructor(
fun onEvent(event: MainEvent) =
weakContext.get()?.applicationContext?.let { handleEvent(event, it) }
- fun isOfflineSong(song: Song, callback: (Boolean) -> Unit) = viewModelScope.launch {
- callback(isSongAvailableOfflineUseCase(song))
- }
-
/**
* set isPlayLoading to true, the play button is listening to this variable
*/
@@ -194,7 +193,9 @@ class MainViewModel @Inject constructor(
songsRepository.getSongsForQuickPlay().collect { result ->
when (result) {
is Resource.Success -> {
- result.data?.let { songs ->
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
if (songs.isNotEmpty()) {
addSongsToQueueAndPlay(songs[0], songs)
}
@@ -207,7 +208,7 @@ class MainViewModel @Inject constructor(
}
}
- fun favouriteSong(song: Song) = viewModelScope.launch {
+ fun favouriteSong(song: SongUI) = viewModelScope.launch {
songsRepository.likeSong(song.mediaId, (song.flag != 1)).collect { result ->
when (result) {
is Resource.Success -> result.data?.let {
@@ -221,7 +222,7 @@ class MainViewModel @Inject constructor(
}
}
- fun rateSong(song: Song, rate: Int) = viewModelScope.launch {
+ fun rateSong(song: SongUI, rate: Int) = viewModelScope.launch {
songsRepository.rateSong(song.mediaId, rate).collect { result ->
when (result) {
is Resource.Success -> result.data?.let {
@@ -265,11 +266,18 @@ class MainViewModel @Inject constructor(
private suspend fun playDeepLinkedSong(id: String, title: String, artist: String, webLink: String) {
shareManager.fetchDeepLinkedSong(id, title, artist,
songCallback = {
- onEvent(MainEvent.PlaySongAddToQueueTop(it, currentQueue().value))
- },
+ onEvent(MainEvent.PlaySongAddToQueueTop(
+ song = it.toSongUI(
+ isSongAvailableOfflineUseCase(it)
+ ),
+ songList = currentQueue().value)
+ )},
songsCallback = {
- onEvent(MainEvent.AddSongsToQueueAndPlayShuffled(it))
- },
+ onEvent(MainEvent.AddSongsToQueueAndPlayShuffled(
+ songList = it.toSongUI { song ->
+ isSongAvailableOfflineUseCase(song)
+ }
+ ))},
errorCallback = {
weakContext.get()?.let { context ->
if (webLink.isNotBlank()) {
@@ -282,8 +290,8 @@ class MainViewModel @Inject constructor(
)
}
- fun downloadSong(song: Song) = viewModelScope.launch {
- songsRepository.downloadSong(song).collect { result ->
+ fun downloadSong(song: SongUI) = viewModelScope.launch {
+ songsRepository.downloadSong(song.toSong()).collect { result ->
when (result) {
is Resource.Success -> {
result.data?.let {
@@ -297,12 +305,12 @@ class MainViewModel @Inject constructor(
}
}
- fun downloadSongs(songs: List) {
- viewModelScope.launch { songsRepository.downloadSongs(songs) }
+ fun downloadSongs(songs: List) {
+ viewModelScope.launch { songsRepository.downloadSongs(songs.toSong()) }
}
- fun deleteDownloadedSong(song: Song) = viewModelScope.launch {
- songsRepository.deleteDownloadedSong(song).collect { result ->
+ fun deleteDownloadedSong(song: SongUI) = viewModelScope.launch {
+ songsRepository.deleteDownloadedSong(song.toSong()).collect { result ->
when (result) {
is Resource.Success -> {
result.data?.let {
@@ -317,10 +325,10 @@ class MainViewModel @Inject constructor(
}
}
- fun deleteDownloadedSongs(songs: List) = viewModelScope.launch {
+ fun deleteDownloadedSongs(songs: List) = viewModelScope.launch {
var count = 0
songs.forEach { song ->
- songsRepository.deleteDownloadedSong(song).collect { result ->
+ songsRepository.deleteDownloadedSong(song.toSong()).collect { result ->
when (result) {
is Resource.Success -> {
result.data?.let { ++count }
@@ -373,9 +381,9 @@ class MainViewModel @Inject constructor(
logToErrorLogs("Load song data START")
val mediaItemList = mutableListOf()
- for (song: Song? in playlistManager.currentQueueState.value) {
+ for (song: SongUI? in playlistManager.currentQueueState.value) {
song?.let {
- mediaItemList.add(it.toMediaItem(songsRepository.getSongUri(it)))
+ mediaItemList.add(it.toMediaItem(songsRepository.getSongUri(it.toSong())))
}
}
@@ -409,14 +417,14 @@ class MainViewModel @Inject constructor(
RepeatMode.ALL -> RepeatMode.OFF
}
- fun scrobble(song: Song) {
+ fun scrobble(song: SongUI) {
scrobbleJob?.cancel()
scrobbleJob = viewModelScope.launch {
delay(LOCAL_SCROBBLE_TIMEOUT_MS) // add song to history after 30s
- songsRepository.addToHistory(song)
+ songsRepository.addToHistory(song.toSong())
// send scrobble to backend
- songsRepository.scrobble(song).collect { response ->
+ songsRepository.scrobble(song.toSong()).collect { response ->
when (response) {
is Resource.Error -> { }
is Resource.Loading -> { }
@@ -426,7 +434,7 @@ class MainViewModel @Inject constructor(
}
}
- fun downloadAfterPlayback(song: Song) {
+ fun downloadAfterPlayback(song: SongUI) {
// do not cancel, let the previous finish download // downloadAfterPlaybackJob?.cancel()
downloadAfterPlaybackJob = viewModelScope.launch {
// start downloading half way
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveDownloadsExt.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveDownloadsExt.kt
index f3bceac4..d06eb52e 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveDownloadsExt.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveDownloadsExt.kt
@@ -22,7 +22,6 @@
package luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel
import android.content.Context
-import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewModelScope
import androidx.work.WorkInfo
import androidx.work.WorkManager
@@ -30,6 +29,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import luci.sixsixsix.mrlog.L
import luci.sixsixsix.powerampache2.R
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import luci.sixsixsix.powerampache2.worker.SongDownloadWorker
internal fun MainViewModel.observeDownloads(application: Context) {
@@ -71,12 +71,12 @@ internal fun MainViewModel.observeDownloads(application: Context) {
}
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
- workInfo?.outputData?.getString(SongDownloadWorker.KEY_RESULT_SONG)?.let { songId ->
+ workInfo.outputData.getString(SongDownloadWorker.KEY_RESULT_SONG)?.let { songId ->
viewModelScope.launch {
if (!emittedDownloads.contains(songId)) {
emittedDownloads = emittedDownloads.toMutableList().apply { add(songId) }
songsRepository.getDownloadedSongById(songId)?.let { finishedSong ->
- playlistManager.updateDownloadedSong(finishedSong)
+ playlistManager.updateDownloadedSong(finishedSong.toSongUI())
errorHandler.updateUserMessage(
application.getString(R.string.downloaded_snackbar_title, finishedSong.name)
) //"${finishedSong.name} downloaded")
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObservePlaylistManagerExt.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObservePlaylistManagerExt.kt
index 6c4b8f2d..1b393fde 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObservePlaylistManagerExt.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObservePlaylistManagerExt.kt
@@ -64,11 +64,10 @@ fun MainViewModel.observePlaylistManager() {
// listen to queue changes
viewModelScope.launch {
- playlistManager.currentQueueState.collectLatest { q ->
- val queue = q.filterNotNull()
- if (!queue.isNullOrEmpty()) {
+ playlistManager.currentQueueState.collectLatest { queue ->
+ if (queue.isNotEmpty()) {
startMusicServiceIfNecessary()
- } else if (queue.isNullOrEmpty() && currentSong() == null) {
+ } else if (currentSong() == null) {
stopMusicService()
}
L("**** observing playlist change queue (before Load song data) :", queue.size)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveSessionExt.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveSessionExt.kt
index 70f06d18..6ad903d8 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveSessionExt.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/main/viewmodel/ObserveSessionExt.kt
@@ -32,7 +32,7 @@ fun MainViewModel.observeSession() {
synchronized(mainLock) {
val oldToken = authToken
authToken = it?.auth ?: ""
- logToErrorLogs(" old toke $oldToken, new one: $authToken")
+ logToErrorLogs(" old token $oldToken, new one: $authToken")
if (authToken.isNotBlank()) {
// refresh the playlist with new urls with the new token
// only if a queue exists
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/notifications/NotificationsViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/notifications/NotificationsViewModel.kt
index 43a14430..580177c8 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/notifications/NotificationsViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/notifications/NotificationsViewModel.kt
@@ -24,7 +24,6 @@ package luci.sixsixsix.powerampache2.presentation.screens.notifications
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import luci.sixsixsix.powerampache2.domain.errors.ErrorHandler
-import luci.sixsixsix.powerampache2.player.MusicPlaylistManager
import javax.inject.Inject
@HiltViewModel
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsEvent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsEvent.kt
index 8e04c222..daf3d5af 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsEvent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsEvent.kt
@@ -1,7 +1,7 @@
package luci.sixsixsix.powerampache2.presentation.screens.offline
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
sealed class OfflineSongsEvent {
- data class OnSongSelected(val song: Song): OfflineSongsEvent()
+ data class OnSongSelected(val song: SongUI): OfflineSongsEvent()
}
\ No newline at end of file
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsScreen.kt
index b16a0edc..39ffdcff 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsScreen.kt
@@ -80,6 +80,7 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.EraseConfirmDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
import luci.sixsixsix.powerampache2.presentation.dialogs.info.ShowSongInfoDialogOpen
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -98,7 +99,7 @@ fun OfflineSongsScreen(
// This variable can be passed into subscreen which will handle the creation of the playlist.
// For example the offline screen will add offline songs to a playlist, the queue screen will
- // add the queue, etc..
+ // add the queue, etc.
// The button that sets this variable is the add-to-playlist button on the top bar
val isPlaylistAddDialogOpen = remember { mutableStateOf(false) }
val titleOfflineSongs = stringResource(R.string.menu_drawer_offline)
@@ -168,10 +169,10 @@ fun OfflineSongsScreen(
.padding(top = dimensionResource(id = R.dimen.albumDetailScreen_top_padding)),
) {
OfflineSongsMainContent(
+ modifier = modifier,
navigator = navigator,
mainViewModel = mainViewModel,
viewModel = viewModel,
- modifier = modifier,
playlistOrQueueDialogOpen = isPlaylistAddDialogOpen,
offlineScreenBarTitle = { title ->
barTitle = title
@@ -184,9 +185,9 @@ fun OfflineSongsScreen(
@Composable
@Destination(start = false)
fun OfflineSongsMainContent(
+ modifier: Modifier = Modifier,
navigator: DestinationsNavigator? = Ampache2NavGraphs.navigator,
mainViewModel: MainViewModel,
- modifier: Modifier = Modifier,
playlistOrQueueDialogOpen: MutableState,
viewModel: OfflineSongsViewModel = hiltViewModel(),
addToPlaylistOrQueueDialogViewModel: AddToPlaylistOrQueueDialogViewModel = hiltViewModel(),
@@ -226,7 +227,7 @@ fun OfflineSongsMainContent(
}
}
- var showDeleteSongDialog by remember { mutableStateOf(null) }
+ var showDeleteSongDialog by remember { mutableStateOf(null) }
showDeleteSongDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -242,7 +243,7 @@ fun OfflineSongsMainContent(
)
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -266,7 +267,7 @@ fun OfflineSongsMainContent(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
@@ -297,7 +298,6 @@ fun OfflineSongsMainContent(
items(state.songs) { song ->
SongItem(
song = song,
- isSongDownloaded = true,
showDownloadedSongMarker = false,
songItemEventListener = { event ->
when(event) {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsState.kt
index 461831f5..25f221e1 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsState.kt
@@ -1,8 +1,8 @@
package luci.sixsixsix.powerampache2.presentation.screens.offline
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
data class OfflineSongsState(
- val songs: List = emptyList(),
+ val songs: List = emptyList(),
val isLoading: Boolean = false,
)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsViewModel.kt
index 89a361b7..7f56fd9d 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/offline/OfflineSongsViewModel.kt
@@ -29,6 +29,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import luci.sixsixsix.powerampache2.domain.usecase.songs.OfflineSongsFlow
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
@HiltViewModel
@@ -40,7 +41,9 @@ class OfflineSongsViewModel @Inject constructor(
// TODO check consistency of downloaded songs and database entries every time,
// delete data accordingly. Do this in data layer
OfflineSongsState(
- songs = songs,
+ songs = songs.map {
+ it.toSongUI(true)
+ },
isLoading = false
)
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/playlists/PlaylistsViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/playlists/PlaylistsViewModel.kt
index 5e07caf5..4af43020 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/playlists/PlaylistsViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/playlists/PlaylistsViewModel.kt
@@ -40,6 +40,7 @@ import luci.sixsixsix.powerampache2.common.Resource
import luci.sixsixsix.powerampache2.domain.PlaylistsRepository
import luci.sixsixsix.powerampache2.domain.common.Constants.ALWAYS_FETCH_ALL_PLAYLISTS
import luci.sixsixsix.powerampache2.domain.models.Playlist
+import luci.sixsixsix.powerampache2.domain.models.isOwnerAdmin
import luci.sixsixsix.powerampache2.domain.usecase.UserFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsFlow
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsUseCase
@@ -75,6 +76,7 @@ class PlaylistsViewModel @Inject constructor(
when (event) {
is PlaylistEvent.Refresh ->
getPlaylists(fetchRemote = true)
+ // TODO: inverse the check to get rid of if empty body
is PlaylistEvent.OnSearchQueryChange -> if (event.query.isBlank() && state.searchQuery.isBlank()) {
} else {
state = state.copy(searchQuery = event.query)
@@ -109,10 +111,11 @@ class PlaylistsViewModel @Inject constructor(
.collect { result ->
when (result) {
is Resource.Success -> {
+ // TODO: why is it commented out only partially? Check if needed at all
result.data?.let { playlists ->
- // playlist updated automatically through live data from database
-// state = state.copy(playlists = playlists)
-// L("viewmodel.getPlaylists size", state.playlists.size)
+ // // playlist updated automatically through live data from database
+ // state = state.copy(playlists = playlists)
+ // L("viewmodel.getPlaylists size", state.playlists.size)
}
isEndOfDataReached =
(result.networkData?.isEmpty() == true && offset > 0)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueEvent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueEvent.kt
index bdc04ea1..c665f788 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueEvent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueEvent.kt
@@ -1,10 +1,10 @@
package luci.sixsixsix.powerampache2.presentation.screens.queue
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
sealed class QueueEvent {
- data class OnSongSelected(val song: Song): QueueEvent()
- data class OnSongRemove(val song: Song): QueueEvent()
+ data class OnSongSelected(val song: SongUI): QueueEvent()
+ data class OnSongRemove(val song: SongUI): QueueEvent()
data object OnPlayQueue: QueueEvent()
data object OnClearQueue: QueueEvent()
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueViewModel.kt
index ad84a2e2..ada75b84 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/QueueViewModel.kt
@@ -24,7 +24,6 @@ package luci.sixsixsix.powerampache2.presentation.screens.queue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.onEach
import luci.sixsixsix.powerampache2.player.MusicPlaylistManager
import luci.sixsixsix.powerampache2.player.SimpleMediaServiceHandler
import javax.inject.Inject
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/components/QueueScreenContent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/components/QueueScreenContent.kt
index 70b5acc0..a1a63b11 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/components/QueueScreenContent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/queue/components/QueueScreenContent.kt
@@ -40,7 +40,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItem
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItemEvent
import luci.sixsixsix.powerampache2.presentation.common.songitem.SubtitleString
@@ -52,6 +51,7 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.EraseConfirmDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
import luci.sixsixsix.powerampache2.presentation.dialogs.info.ShowSongInfoDialogOpen
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -86,7 +86,7 @@ fun QueueScreenContent(
}
}
- var showRemoveFromQueueDialog by remember { mutableStateOf(null) }
+ var showRemoveFromQueueDialog by remember { mutableStateOf(null) }
showRemoveFromQueueDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -101,7 +101,7 @@ fun QueueScreenContent(
)
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -125,7 +125,7 @@ fun QueueScreenContent(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchResultsScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchResultsScreen.kt
index 8853b991..84ddf1b2 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchResultsScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchResultsScreen.kt
@@ -132,7 +132,7 @@ fun SearchResultsScreen(
}
} else if (searchState.isNoResults) {
// show no results screen (search query present but no results)
- showHideEmptyResultsView(searchState.isLoading, searchState.isFetchingMore, searchState.isNoResults)
+ ShowHideEmptyResultsView(searchState.isLoading, searchState.isFetchingMore, searchState.isNoResults)
} else {
// show search results
ResultsListView(
@@ -172,7 +172,7 @@ fun SearchResultsScreen(
}
@Composable
-private fun showHideEmptyResultsView(isLoading: Boolean, isRefreshing: Boolean, isNoResults: Boolean) {
+private fun ShowHideEmptyResultsView(isLoading: Boolean, isRefreshing: Boolean, isNoResults: Boolean) {
if (!isLoading && !isRefreshing && isNoResults){
Card(modifier = Modifier.fillMaxSize()) {
Column(
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchScreenState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchScreenState.kt
index 7859d7bc..769a6cff 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchScreenState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchScreenState.kt
@@ -28,13 +28,13 @@ import luci.sixsixsix.powerampache2.domain.models.Album
import luci.sixsixsix.powerampache2.domain.models.Artist
import luci.sixsixsix.powerampache2.domain.models.Genre
import luci.sixsixsix.powerampache2.domain.models.Playlist
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
@Parcelize
data class SearchScreenState (
val selectedGenre: Genre? = null,
val genres: List = emptyList(),
- val songs: List = emptyList(),
+ val songs: List = emptyList(),
val albums: List = emptyList(),
val artists: List = emptyList(),
val playlists: List = emptyList(),
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchViewModel.kt
index fc448d48..e07b3265 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/SearchViewModel.kt
@@ -47,10 +47,12 @@ import luci.sixsixsix.powerampache2.domain.usecase.artists.ArtistsByGenreUseCase
import luci.sixsixsix.powerampache2.domain.usecase.artists.ArtistsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.playlists.PlaylistsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.settings.LocalSettingsFlowUseCase
+import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.GetSongsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.OfflineSongsFlow
import luci.sixsixsix.powerampache2.domain.usecase.songs.SongsByGenreUseCase
import luci.sixsixsix.powerampache2.player.MusicPlaylistManager
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
// minimum allowed size for a search query to trigger a search
@@ -58,6 +60,7 @@ private const val MIN_QUERY_SIZE = 3
@HiltViewModel
class SearchViewModel @Inject constructor(
+ private val isSongAvailableOfflineUseCase: IsSongAvailableOfflineUseCase,
private val genresUseCase: GenresUseCase,
private val getSongsUseCase: GetSongsUseCase,
private val songsByGenreUseCase: SongsByGenreUseCase,
@@ -147,7 +150,9 @@ class SearchViewModel @Inject constructor(
private suspend fun fetchGenresOffline() = getSongsUseCase().collect { result ->
when (result) {
is Resource.Success ->
- result.data?.let { songs ->
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
val genres: List = HashSet().apply {
songs.map { it.genre }.forEach { attributes ->
addAll(attributes.map { Genre(
@@ -184,7 +189,9 @@ class SearchViewModel @Inject constructor(
songsByGenreUseCase(genre).collect { result ->
when (result) {
is Resource.Success ->
- result.data?.let { songs ->
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
state = state.copy(songs = songs)
}
is Resource.Error ->
@@ -198,11 +205,12 @@ class SearchViewModel @Inject constructor(
getSongsUseCase().collect { result ->
when (result) {
is Resource.Success ->
- result.data?.let { songs ->
- val mapped = songs.filter {
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
+ state = state.copy(songs = songs.filter {
it.genre.joinToString(", ").contains(genre.name)
- }
- state = state.copy(songs = mapped)
+ })
}
is Resource.Error ->
state = state.copy(isLoading = false)
@@ -252,7 +260,9 @@ class SearchViewModel @Inject constructor(
if (state.searchQuery.isNotBlank()) {
// only display the list if there is a search term present.
// Avoids race conditions when quickly deleting and re-typing search terms.
- result.data?.let { songs ->
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
state = state.copy(songs = songs)
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/screens/ResultsListView.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/screens/ResultsListView.kt
index 79ce4b8a..b28852f0 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/screens/ResultsListView.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/search/screens/ResultsListView.kt
@@ -38,16 +38,16 @@ import luci.sixsixsix.powerampache2.domain.models.Album
import luci.sixsixsix.powerampache2.domain.models.AmpacheModel
import luci.sixsixsix.powerampache2.domain.models.Artist
import luci.sixsixsix.powerampache2.domain.models.Playlist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.common.AmpacheListItem
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItemEvent
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.search.SearchViewEvent
@Composable
@Destination
fun ResultsListView(
- songs: List,
+ songs: List,
albums: List,
artists: List,
playlists: List,
@@ -56,14 +56,14 @@ fun ResultsListView(
isRefreshing: Boolean,
modifier: Modifier = Modifier,
onEvent: (SearchViewEvent) -> Unit,
- onSongSelected: (Song) -> Unit,
+ onSongSelected: (SongUI) -> Unit,
onAlbumSelected: (albumId: String, album: Album?) -> Unit,
onArtistSelected: (artistId: String, artist: Artist?) -> Unit,
onPlaylistSelected: (Playlist) -> Unit,
onSongEvent: (MainEvent) -> Unit,
- onOpenPlaylistDialog: (List) -> Unit,
- onShowDeleteFromDownloadsDialog: (Song) -> Unit,
- onShowSongInfoDialog: (Song) -> Unit
+ onOpenPlaylistDialog: (List) -> Unit,
+ onShowDeleteFromDownloadsDialog: (SongUI) -> Unit,
+ onShowSongInfoDialog: (SongUI) -> Unit
) {
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing)
@@ -88,10 +88,9 @@ fun ResultsListView(
items(megaList) { item ->
AmpacheListItem(
item = item,
- isSongDownloaded = false,
songItemEventListener = {
onSongItemEvent(
- song = (item as Song),
+ song = (item as SongUI),
event = it,
onSongEvent,
onAlbumSelected,
@@ -105,7 +104,7 @@ fun ResultsListView(
.fillMaxWidth()
.clickable {
when (item) {
- is Song -> onSongSelected(item)
+ is SongUI -> onSongSelected(item)
is Album -> onAlbumSelected(item.id, item)
is Artist -> onArtistSelected(item.id, item)
is Playlist -> onPlaylistSelected(item)
@@ -121,14 +120,14 @@ fun ResultsListView(
}
private fun onSongItemEvent(
- song: Song,
+ song: SongUI,
event: SongItemEvent,
onSongEvent: (MainEvent) -> Unit,
onAlbumSelected: (albumId: String, album: Album?) -> Unit,
onArtistSelected: (artistId: String, artist: Artist?) -> Unit,
- onOpenPlaylistDialog: (List) -> Unit,
- onShowDeleteFromDownloadsDialog: (Song) -> Unit,
- onShowSongInfoDialog: (Song) -> Unit
+ onOpenPlaylistDialog: (List) -> Unit,
+ onShowDeleteFromDownloadsDialog: (SongUI) -> Unit,
+ onShowSongInfoDialog: (SongUI) -> Unit
) {
when(event) {
SongItemEvent.PLAY_NEXT ->
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsListScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsListScreen.kt
index beaf6588..46dabf98 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsListScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsListScreen.kt
@@ -29,15 +29,15 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material3.CircularProgressIndicator
+//import androidx.compose.material3.CircularProgressIndicator
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.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
+//import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.media3.common.util.UnstableApi
@@ -46,7 +46,7 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.common.LoadingScreen
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItem
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItemEvent
@@ -92,7 +92,7 @@ fun SongsListScreen(
}
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -116,7 +116,7 @@ fun SongsListScreen(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
@@ -149,8 +149,7 @@ fun SongsListScreen(
state.songs.size,
//key = { i -> state.songs[i].mediaId }
) { i ->
- val song = state.songs[i].song
- val isOffline = state.songs[i].isOffline
+ val song = state.songs[i]
SongItem(
song = song,
songItemEventListener = { event ->
@@ -179,7 +178,6 @@ fun SongsListScreen(
}
},
subtitleString = SubtitleString.ARTIST,
- isSongDownloaded = isOffline,
modifier = Modifier
.fillMaxWidth()
.clickable {
@@ -192,7 +190,7 @@ fun SongsListScreen(
}
)
// TODO decide to include or not this
- // footer(i = i, state = state)
+ // Footer(i = i, state = state)
}
}
}
@@ -200,25 +198,25 @@ fun SongsListScreen(
}
}
-@Composable
-fun footer(i: Int, state: SongsState) {
- if (i < state.songs.size - 1) {
- // if not last item add a divider
- // TODO: do I want a divider? Divider(modifier = Modifier.padding(horizontal = 16.dp))
- } else if (i == state.songs.size - 1) {
- // TODO should this screen be allowed to load more ?
- Column(modifier = Modifier.fillMaxWidth()) {
- CircularProgressIndicator(
- modifier = Modifier
- .align(Alignment.CenterHorizontally)
- .alpha(
- if (state.isFetchingMore) {
- 1.0f
- } else {
- 0.0f
- }
- )
- )
- }
- }
-}
+//@Composable
+//fun Footer(i: Int, state: SongsState) {
+// if (i < state.songs.size - 1) {
+// // if not last item add a divider
+// // TODO: do I want a divider? Divider(modifier = Modifier.padding(horizontal = 16.dp))
+// } else if (i == state.songs.size - 1) {
+// // TODO should this screen be allowed to load more ?
+// Column(modifier = Modifier.fillMaxWidth()) {
+// CircularProgressIndicator(
+// modifier = Modifier
+// .align(Alignment.CenterHorizontally)
+// .alpha(
+// if (state.isFetchingMore) {
+// 1.0f
+// } else {
+// 0.0f
+// }
+// )
+// )
+// }
+// }
+//}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsState.kt
index 7c05bc3e..9954de8e 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsState.kt
@@ -1,14 +1,13 @@
package luci.sixsixsix.powerampache2.presentation.screens.songs
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
data class SongsState(
- val songs: List = emptyList(),
+ val songs: List = emptyList(),
val isLoading: Boolean = false,
val isRefreshing: Boolean = false,
val searchQuery: String = "",
val isFetchingMore: Boolean = false
) {
- fun getSongList(): List = songs.map { it.song }
+ fun getSongList(): List = songs
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsViewModel.kt
index 7a82e1c9..fe5a4f5f 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens/songs/SongsViewModel.kt
@@ -12,12 +12,10 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import luci.sixsixsix.mrlog.L
import luci.sixsixsix.powerampache2.common.Resource
-import luci.sixsixsix.powerampache2.domain.SettingsRepository
-import luci.sixsixsix.powerampache2.domain.SongsRepository
import luci.sixsixsix.powerampache2.domain.usecase.settings.OfflineModeFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.GetSongsUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
@HiltViewModel
@@ -51,11 +49,11 @@ class SongsViewModel @Inject constructor(
L("SongsEvent.OnSearchQueryChange")
// force refresh when deleting the search query
// start a search only if the new query is different from the previous
+ // TODO: invert the check to avoid empty if body
if (event.query.isBlank() && state.searchQuery.isBlank()) {
-
} else {
state = state.copy(searchQuery = event.query)
- getSongs(refresh = event.query.isNullOrEmpty())
+ getSongs(refresh = event.query.isEmpty())
}
}
@@ -78,17 +76,11 @@ class SongsViewModel @Inject constructor(
when(result) {
is Resource.Success -> {
result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song)
- )
- )
- }
- state = state.copy(songs = songWrapperList)
- L("viewmodel.getSongs SONGS size at the end", state.songs.size)
+ state = state.copy(songs = songs.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ })
+ L("viewmodel.getSongs SONGS size at the end",
+ state.songs.size)
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailScreen.kt
index 09f0935e..6787c2c9 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailScreen.kt
@@ -66,7 +66,6 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.domain.models.Album
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.common.LoadingScreen
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongInfoThirdRow
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItem
@@ -80,6 +79,7 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogAlbum
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
import luci.sixsixsix.powerampache2.presentation.dialogs.info.ShowSongInfoDialogOpen
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -91,10 +91,10 @@ import luci.sixsixsix.powerampache2.presentation.screens_detail.album_detail.com
@Composable
@Destination
fun AlbumDetailScreen(
+ modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
albumId: String,
album: Album? = null,
- modifier: Modifier = Modifier,
viewModel: AlbumDetailViewModel = hiltViewModel(),
mainViewModel: MainViewModel,
addToPlaylistOrQueueDialogViewModel: AddToPlaylistOrQueueDialogViewModel = hiltViewModel()
@@ -167,7 +167,7 @@ fun AlbumDetailScreen(
)
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -191,7 +191,7 @@ fun AlbumDetailScreen(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
@@ -214,6 +214,7 @@ fun AlbumDetailScreen(
var imageUrl: String? by remember { mutableStateOf(null) }
var imageError by remember { mutableStateOf(false) }
+ // TODO: investigate the warning about suspicious cascading if expression (also isNotBlank warning)
val artUrl = if (isOffline != null && isOffline == true && songs.isNotEmpty()) {
// in offline mode, grab url from a song
songs[0].imageUrl
@@ -321,7 +322,7 @@ fun AlbumDetailScreen(
when(event) {
AlbumInfoViewEvents.PLAY_ALBUM -> {
- if (state.isLoading || viewModel.state.songs.isNullOrEmpty()) return@AlbumInfoSection
+ if (state.isLoading || viewModel.state.songs.isEmpty()) return@AlbumInfoSection
if (isPlayingAlbum) {
// will pause if playing
@@ -368,8 +369,7 @@ fun AlbumDetailScreen(
.fillMaxSize()
) {
items(state.songs.size) { i ->
- val song = state.songs[i].song
- val isOffline = state.songs[i].isOffline
+ val song = state.songs[i]
SongItem(
song = song,
isLandscape = isLandscape,
@@ -412,7 +412,6 @@ fun AlbumDetailScreen(
},
subtitleString = SubtitleString.NOTHING,
songInfoThirdRow = SongInfoThirdRow.Time,
- isSongDownloaded = isOffline
)
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailState.kt
index 0aed1108..74b1316f 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailState.kt
@@ -24,11 +24,11 @@ package luci.sixsixsix.powerampache2.presentation.screens_detail.album_detail
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import luci.sixsixsix.powerampache2.domain.models.Artist
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
@Parcelize
data class AlbumDetailState (
- val songs: List = emptyList(),
+ val songs: List = emptyList(),
val recommendedArtists: List = emptyList(),
val isLoading: Boolean = false,
val isAlbumDownloaded: Boolean = false,
@@ -37,5 +37,5 @@ data class AlbumDetailState (
val searchQuery: String = "",
val isFetchingMore: Boolean = false,
): Parcelable {
- fun getSongList() = songs.map { it.song }
+ fun getSongList() = songs
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailViewModel.kt
index 314eb726..03fd4356 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/AlbumDetailViewModel.kt
@@ -60,14 +60,16 @@ import luci.sixsixsix.powerampache2.domain.usecase.settings.OfflineModeFlowUseCa
import luci.sixsixsix.powerampache2.domain.usecase.settings.ToggleGlobalShuffleUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.OfflineSongsFlow
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.isAvailableOffline
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
@HiltViewModel
class AlbumDetailViewModel @Inject constructor(
@ApplicationContext private val application: Context,
- private val savedStateHandle: SavedStateHandle, // a way to get access to navigation arguments
- // in the view model directly without passing them from the UI or the previos view model, we
+ savedStateHandle: SavedStateHandle, // a way to get access to navigation arguments
+ // in the view model directly without passing them from the UI or the previous view model, we
// need this because we're passing the symbol around
offlineModeFlowUseCase: OfflineModeFlowUseCase,
localSettingsFlowUseCase: LocalSettingsFlowUseCase,
@@ -165,7 +167,7 @@ class AlbumDetailViewModel @Inject constructor(
}
private fun refreshFromCache() {
- if (!albumStateFlow.value.id.isNullOrBlank()) {
+ if (albumStateFlow.value.id.isNotBlank()) {
L("AlbumDetailEvent.RefreshFromCache", albumStateFlow.value.id)
getSongsFromAlbum(albumId = albumStateFlow.value.id, fetchRemote = false)
}
@@ -204,9 +206,9 @@ class AlbumDetailViewModel @Inject constructor(
}
}
- private fun isAlbumDownloaded(songs: List): Boolean {
+ private fun isAlbumDownloaded(songs: List): Boolean {
songs.forEach {
- if (!it.isOffline) return false
+ if (!it.isAvailableOffline()) return false
}
return true
}
@@ -219,16 +221,13 @@ class AlbumDetailViewModel @Inject constructor(
when (result) {
is Resource.Success -> {
result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song)
- )
- )
+ val songUIList = songs.toSongUI {
+ isSongAvailableOfflineUseCase(it)
}
- state = state.copy(songs = songWrapperList, isAlbumDownloaded = isAlbumDownloaded(songWrapperList))
+ state = state.copy(
+ songs = songUIList,
+ isAlbumDownloaded = isAlbumDownloaded(songUIList)
+ )
L("AlbumDetailViewModel.getSongsFromAlbum size", result.data?.size, "network", result.networkData?.size)
}
}
@@ -240,6 +239,7 @@ class AlbumDetailViewModel @Inject constructor(
}
}
+ // TODO: this is never used
private fun getRecommendedArtists(artistId: String, fetchRemote: Boolean = true) {
viewModelScope.launch {
recommendedArtistsUseCase(baseArtistId = artistId, fetchRemote = fetchRemote)
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/components/AlbumInfoButtonsRow.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/components/AlbumInfoButtonsRow.kt
index 63ebb0c7..474c7a54 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/components/AlbumInfoButtonsRow.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/album_detail/components/AlbumInfoButtonsRow.kt
@@ -45,6 +45,7 @@ import luci.sixsixsix.powerampache2.presentation.common.ShuffleToggleButton
@Composable
fun AlbumInfoButtonsRow(
+ // TODO: this is never used?
album: Album,
isPlayingAlbum: Boolean,
isAlbumDownloaded: Boolean,
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailScreen.kt
index 95a0ab25..a12f75f6 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailScreen.kt
@@ -22,7 +22,6 @@
package luci.sixsixsix.powerampache2.presentation.screens_detail.artist_detail
import android.content.res.Configuration
-import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
@@ -58,9 +57,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.media3.common.util.UnstableApi
@@ -77,7 +74,6 @@ import luci.sixsixsix.powerampache2.presentation.destinations.AlbumDetailScreenD
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialogOpen
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialogViewModel
-import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogAlbum
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogArtist
import luci.sixsixsix.powerampache2.presentation.screens.albums.components.AlbumItem
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
@@ -94,10 +90,10 @@ private const val GRID_ITEMS_ROW_LAND = 5
@Composable
@Destination
fun ArtistDetailScreen(
+ modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
artistId: String,
artist: Artist? = null,
- modifier: Modifier = Modifier,
viewModel: ArtistDetailViewModel = hiltViewModel(),
mainViewModel: MainViewModel,
addToPlaylistOrQueueDialogViewModel: AddToPlaylistOrQueueDialogViewModel = hiltViewModel()
@@ -121,6 +117,7 @@ fun ArtistDetailScreen(
snapshotFlow { configuration.orientation }
.collect { orientation = it }
}
+ // TODO: unused?
val isLandscape = when (orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
cardsPerRow = GRID_ITEMS_ROW_LAND
@@ -155,6 +152,8 @@ fun ArtistDetailScreen(
var artUrlTop = generateArtistArtUrl(state.artist, state.albums).ifBlank {
infoPluginArtistState?.imageUrl
}
+
+ // TODO: should be val?
var artUrlBottom = generateArtistArtUrl(state.artist, state.albums).ifBlank {
infoPluginArtistState?.imageUrl
}
@@ -331,7 +330,7 @@ fun ArtistDetailScreen(
}
}
-fun generateArtistArtUrl(artist: Artist, albums: List) = if(artist.artUrl.isNullOrBlank()) {
+fun generateArtistArtUrl(artist: Artist, albums: List) = if(artist.artUrl.isBlank()) {
if (albums.isNotEmpty()) {
albums[albums.indices.random()].artUrl
} else ""
@@ -374,10 +373,10 @@ private val screenBackgroundGradient
@Destination
@Composable
fun ArtistDetailScreen2(
+ modifier: Modifier = Modifier,
navigator: DestinationsNavigator,
artistId: String,
viewModel: ArtistDetailViewModel = hiltViewModel(),
- modifier: Modifier = Modifier
) {
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = viewModel.state.isRefreshing)
val state = viewModel.state
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailViewModel.kt
index 8bf1b041..66aa22aa 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/ArtistDetailViewModel.kt
@@ -40,7 +40,6 @@ import luci.sixsixsix.powerampache2.common.delegates.FetchArtistSongsHandler
import luci.sixsixsix.powerampache2.common.delegates.FetchArtistSongsHandlerImpl
import luci.sixsixsix.powerampache2.domain.errors.ErrorHandler
import luci.sixsixsix.powerampache2.domain.models.Artist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.models.settings.LocalSettings
import luci.sixsixsix.powerampache2.domain.usecase.albums.AlbumsFromArtistUseCase
import luci.sixsixsix.powerampache2.domain.usecase.artists.ArtistUseCase
@@ -51,6 +50,8 @@ import luci.sixsixsix.powerampache2.domain.usecase.plugin.IsInfoPluginInstalled
import luci.sixsixsix.powerampache2.domain.usecase.settings.LocalSettingsFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.settings.OfflineModeFlowUseCase
import luci.sixsixsix.powerampache2.domain.usecase.settings.ToggleGlobalShuffleUseCase
+import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import javax.inject.Inject
import kotlin.math.abs
@@ -66,8 +67,12 @@ class ArtistDetailViewModel @Inject constructor(
private val toggleGlobalShuffle: ToggleGlobalShuffleUseCase,
private val isInfoPluginInstalled: IsInfoPluginInstalled,
private val artistDataFromPluginUseCase: ArtistDataFromPluginUseCase,
- private val errorHandler: ErrorHandler
-) : ViewModel(), FetchArtistSongsHandler by FetchArtistSongsHandlerImpl(songsFromArtistUseCase) {
+ private val errorHandler: ErrorHandler,
+ private val isSongAvailableOfflineUseCase: IsSongAvailableOfflineUseCase,
+) : ViewModel(), FetchArtistSongsHandler by FetchArtistSongsHandlerImpl(
+ songsFromArtistUseCase,
+ isSongAvailableOfflineUseCase,
+) {
var state by mutableStateOf(ArtistDetailState())
// private val isOfflineModeState = offlineModeFlowUseCase()
@@ -83,7 +88,7 @@ class ArtistDetailViewModel @Inject constructor(
savedStateHandle.get("artist")?.let { artist ->
// if artist provided, first check if there's an entry in the db, if not use the provided
- // as fallback. This is important, because there is not certainty that the album is
+ // as fallback. This is important, because there is no certainty that the album is
// in the internal db
state = state.copy(artist = artist)
getArtist(id, fetchRemote = false)
@@ -147,14 +152,14 @@ class ArtistDetailViewModel @Inject constructor(
fun fetchSongsFromArtist(
artistId: String = state.artist.id,
fetchRemote: Boolean = true,
- songsCallback: (List) -> Unit
+ songsCallback: (List) -> Unit
) = viewModelScope.launch {
getSongsFromArtist(
artistId = artistId, fetchRemote = fetchRemote,
isOfflineMode = offlineModeFlowUseCase().first(),
songsCallback = songsCallback,
loadingCallback = { state = state.copy(isLoading = it) },
- errorCallback = { state.copy(isLoading = false) }
+ errorCallback = { state = state.copy(isLoading = false) }
)
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/components/ArtistInfoSection.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/components/ArtistInfoSection.kt
index 2eedb731..2656a448 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/components/ArtistInfoSection.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/artist_detail/components/ArtistInfoSection.kt
@@ -46,7 +46,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import androidx.core.text.HtmlCompat
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.domain.models.Artist
import luci.sixsixsix.powerampache2.domain.plugin.info.PluginArtistData
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailEvent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailEvent.kt
index ea54b1b9..3ad3d87a 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailEvent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailEvent.kt
@@ -22,12 +22,12 @@
package luci.sixsixsix.powerampache2.presentation.screens_detail.playlist_detail
import luci.sixsixsix.powerampache2.domain.models.Playlist
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
sealed class PlaylistDetailEvent {
data object Refresh: PlaylistDetailEvent()
data class Fetch(val playlist: Playlist): PlaylistDetailEvent()
- data class OnSongSelected(val song: Song): PlaylistDetailEvent()
+ data class OnSongSelected(val song: SongUI): PlaylistDetailEvent()
data object OnLikePlaylist: PlaylistDetailEvent()
data object OnPlayPlaylist: PlaylistDetailEvent()
data object OnSharePlaylist: PlaylistDetailEvent()
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailScreen.kt
index e86e95c0..86bd7bd5 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailScreen.kt
@@ -33,7 +33,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
@@ -76,7 +76,6 @@ import luci.sixsixsix.powerampache2.domain.models.FrequentPlaylist
import luci.sixsixsix.powerampache2.domain.models.HighestPlaylist
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.RecentPlaylist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.common.LoadingScreen
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongInfoThirdRow
import luci.sixsixsix.powerampache2.presentation.common.songitem.SongItem
@@ -90,7 +89,7 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.EraseConfirmDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
import luci.sixsixsix.powerampache2.presentation.dialogs.info.ShowSongInfoDialogOpen
-import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs.navigateToArtist
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -152,7 +151,7 @@ fun PlaylistDetailScreen(
}
}
- var showDeleteFromPlaylistDialog by remember { mutableStateOf(null) }
+ var showDeleteFromPlaylistDialog by remember { mutableStateOf(null) }
showDeleteFromPlaylistDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -185,7 +184,7 @@ fun PlaylistDetailScreen(
}
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -209,7 +208,7 @@ fun PlaylistDetailScreen(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
@@ -322,17 +321,17 @@ fun PlaylistDetailScreen(
isDownloading = mainViewModel.state.isDownloading,
isGlobalShuffleOn = state.isGlobalShuffleOn,
isPlayLoading = mainViewModel.isPlayLoading(),
- enabled = !state.songs.isNullOrEmpty(),
+ enabled = state.songs.isNotEmpty(),
songs = viewModel.state.getSongList(),
artistClickListener = {
- artistId -> Ampache2NavGraphs.navigateToArtist(navigator, artistId)
+ artistId -> navigateToArtist(navigator, artistId)
},
isPlaylistEditLoading = addToPlaylistOrQueueDialogViewModel.state.isPlaylistEditLoading,
eventListener = { event ->
when(event) {
PlaylistInfoViewEvents.PLAY_PLAYLIST -> {
- if (viewModel.state.songs.isNullOrEmpty()) return@PlaylistInfoSection
+ if (viewModel.state.songs.isEmpty()) return@PlaylistInfoSection
if (isPlayingPlaylist){
// will pause if playing
@@ -340,7 +339,7 @@ fun PlaylistDetailScreen(
} else if (state.songs.isNotEmpty()) {
if (!state.isGlobalShuffleOn) {
mainViewModel.onEvent(
- MainEvent.AddSongsToQueueAndPlay(state.songs[0].song, state.getSongList())
+ MainEvent.AddSongsToQueueAndPlay(state.songs[0], state.getSongList())
)
} else {
mainViewModel.onEvent(
@@ -386,7 +385,7 @@ fun PlaylistDetailScreen(
}
)
- showHideEmptyPlaylistView(playlist = currentPlaylistState, state = state)
+ ShowHideEmptyPlaylistView(playlist = currentPlaylistState, state = state)
SwipeRefresh(
state = swipeRefreshState,
@@ -396,17 +395,13 @@ fun PlaylistDetailScreen(
modifier = Modifier
.fillMaxSize()
) {
- itemsIndexed(
+ items(
items = state.songs,
- //key = { _, item -> item }
- ) { _, songWrapped ->
- val song = songWrapped.song
- val isOffline = songWrapped.isOffline
+ ) { song ->
SongItem(
song = song,
isLandscape = isLandscape,
isEditMode = isEditMode,
- isSongDownloaded = isOffline,
songItemEventListener = { event ->
when(event) {
SongItemEvent.PLAY_NEXT ->
@@ -477,13 +472,14 @@ fun PlaylistDetailScreen(
}
@Composable
-private fun showHideEmptyPlaylistView(playlist: Playlist, state: PlaylistDetailState) {
+private fun ShowHideEmptyPlaylistView(playlist: Playlist, state: PlaylistDetailState) {
if (
+ // TODO: check if the commented out stuff is still relevant in any way or should be removed
/*(playlist is RecentPlaylist ||
playlist is FrequentPlaylist ||
playlist is HighestPlaylist ||
playlist is FlaggedPlaylist) &&*/
- !state.isLoading && !state.isRefreshing && state.songs.isNullOrEmpty()
+ !state.isLoading && !state.isRefreshing && state.songs.isEmpty()
){
Card(modifier = Modifier.fillMaxSize()) {
Column(
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailState.kt
index 3da857ec..fbcc2871 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailState.kt
@@ -29,18 +29,17 @@ import luci.sixsixsix.powerampache2.domain.models.HighestPlaylist
import luci.sixsixsix.powerampache2.domain.models.settings.LocalSettings
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.RecentPlaylist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.models.settings.SortMode
import luci.sixsixsix.powerampache2.domain.models.settings.defaultPlaylistSort
import luci.sixsixsix.powerampache2.domain.models.isSmartPlaylist
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
@Parcelize
data class PlaylistDetailState (
//val playlist: Playlist = Playlist("", ""),
val isNotStatPlaylist: Boolean = false,
val isGeneratedOrSmartPlaylist: Boolean = false,
- val songs: List = emptyList(),
+ val songs: List = emptyList(),
val isLoading: Boolean = false,
val isLikeLoading: Boolean = false,
val isRefreshing: Boolean = false,
@@ -51,7 +50,7 @@ data class PlaylistDetailState (
val isUserOwner: Boolean = false,
val isGlobalShuffleOn: Boolean = LocalSettings.SETTINGS_DEFAULTS_GLOBAL_SHUFFLE
): Parcelable {
- fun getSongList(): List = songs.map { it.song }
+ fun getSongList(): List = songs
companion object {
fun isGeneratedOrSmartPlaylist(playlist: Playlist) = when (playlist) {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailViewModel.kt
index 506fe628..3c212013 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailViewModel.kt
@@ -58,7 +58,6 @@ import luci.sixsixsix.powerampache2.domain.models.HighestPlaylist
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.PlaylistType
import luci.sixsixsix.powerampache2.domain.models.RecentPlaylist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.models.isSmartPlaylist
import luci.sixsixsix.powerampache2.domain.models.settings.SortMode
import luci.sixsixsix.powerampache2.domain.usecase.UserFlowUseCase
@@ -69,13 +68,15 @@ import luci.sixsixsix.powerampache2.domain.usecase.settings.LocalSettingsFlowUse
import luci.sixsixsix.powerampache2.domain.usecase.settings.ToggleGlobalShuffleUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.IsSongAvailableOfflineUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.OfflineSongsFlow
-import luci.sixsixsix.powerampache2.presentation.common.songitem.SongWrapper
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.toSong
+import luci.sixsixsix.powerampache2.presentation.models.toSongUI
import javax.inject.Inject
@HiltViewModel
class PlaylistDetailViewModel @Inject constructor(
@ApplicationContext private val application: Context,
- private val savedStateHandle: SavedStateHandle,
+ savedStateHandle: SavedStateHandle,
private val toggleGlobalShuffle: ToggleGlobalShuffleUseCase,
localSettingsFlowUseCase: LocalSettingsFlowUseCase,
playlistFlow: PlaylistFlow,
@@ -241,7 +242,7 @@ class PlaylistDetailViewModel @Inject constructor(
}
}
- fun isEditSongSelected(song: Song): Boolean = editState.selectedSongs.contains(song)
+ fun isEditSongSelected(song: SongUI): Boolean = editState.selectedSongs.contains(song)
private fun ratePlaylist(playlist: Playlist, rate: Int) = viewModelScope.launch {
playlistsRepository.ratePlaylist(playlist.id, rate).collect { result ->
@@ -292,19 +293,13 @@ class PlaylistDetailViewModel @Inject constructor(
.collect { result ->
when(result) {
is Resource.Success -> {
- result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song)
- )
- )
- }
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.toMutableList()?.let { songs ->
state = state.copy(
- songs = songWrapperList.apply {
- if (state.sortMode == SortMode.DESC) { reverse() } }
+ songs = songs.apply {
+ if (state.sortMode == SortMode.DESC) { reverse() }
+ }
)
}
}
@@ -319,13 +314,13 @@ class PlaylistDetailViewModel @Inject constructor(
private fun editPlaylist(
playlist: Playlist = playlistStateFlow.value,
- newList: List = state.getSongList()
+ newList: List = state.getSongList()
) = viewModelScope.launch {
playlistsRepository
.editPlaylist(
playlistId = playlist.id,
playlistName = playlist.name,
- items = newList,
+ items = newList.toSong(),
owner = playlist.owner,
playlistType = playlist.type ?: PlaylistType.private
).collect { result ->
@@ -361,19 +356,15 @@ class PlaylistDetailViewModel @Inject constructor(
}
}
+ // TODO: fetchRemote isn't used?
private fun getRecentSongs(fetchRemote: Boolean = true) = viewModelScope.launch {
songsRepository.getRecentSongs().collect { result ->
when(result) {
is Resource.Success -> {
- result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(song = song,
- isOffline = isSongAvailableOfflineUseCase(song))
- )
- }
- state = state.copy(songs = songWrapperList)
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
+ state = state.copy(songs = songs)
L("PlaylistDetailViewModel.getRecentSongs size ${state.songs.size}")
}
}
@@ -389,6 +380,7 @@ class PlaylistDetailViewModel @Inject constructor(
}
+ // TODO: fetchRemote isn't used?
private fun getFlaggedSongs(fetchRemote: Boolean = true) {
viewModelScope.launch {
songsRepository
@@ -396,17 +388,10 @@ class PlaylistDetailViewModel @Inject constructor(
.collect { result ->
when(result) {
is Resource.Success -> {
- result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song)
- )
- )
- }
- state = state.copy(songs = songWrapperList)
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
+ state = state.copy(songs = songs)
L("PlaylistDetailViewModel.getFlaggedSongs size ${state.songs.size}")
}
L( "PlaylistDetailViewModel.getFlaggedSongs size of network array ${result.networkData?.size}")
@@ -423,24 +408,18 @@ class PlaylistDetailViewModel @Inject constructor(
}
}
- private fun getFrequentSongs(fetchRemote: Boolean = true, ) {
+ // TODO: fetchRemote isn't used?
+ private fun getFrequentSongs(fetchRemote: Boolean = true) {
viewModelScope.launch {
songsRepository
.getFrequentSongs()
.collect { result ->
when(result) {
is Resource.Success -> {
- result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song = song)
- )
- )
- }
- state = state.copy(songs = songWrapperList)
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
+ state = state.copy(songs = songs)
L("PlaylistDetailViewModel.getFrequentSongs size ${state.songs.size}")
}
L( "PlaylistDetailViewModel.getFrequentSongs size of network array ${result.networkData?.size}")
@@ -457,24 +436,18 @@ class PlaylistDetailViewModel @Inject constructor(
}
}
- private fun getHighestSongs(fetchRemote: Boolean = true, ) {
+ // TODO: fetchRemote isn't used?
+ private fun getHighestSongs(fetchRemote: Boolean = true) {
viewModelScope.launch {
songsRepository
.getHighestSongs()
.collect { result ->
when(result) {
is Resource.Success -> {
- result.data?.let { songs ->
- val songWrapperList = mutableListOf()
- songs.forEach { song ->
- songWrapperList.add(
- SongWrapper(
- song = song,
- isOffline = isSongAvailableOfflineUseCase(song)
- )
- )
- }
- state = state.copy(songs = songWrapperList)
+ result.data?.toSongUI {
+ isSongAvailableOfflineUseCase(it)
+ }?.let { songs ->
+ state = state.copy(songs = songs)
L("PlaylistDetailViewModel.getHighestSongs size ${state.songs.size}")
}
L( "PlaylistDetailViewModel.getHighestSongs size of network array ${result.networkData?.size}")
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailsEditEvent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailsEditEvent.kt
index ae83d6bb..004b5d63 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailsEditEvent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistDetailsEditEvent.kt
@@ -21,13 +21,13 @@
*/
package luci.sixsixsix.powerampache2.presentation.screens_detail.playlist_detail
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
sealed class PlaylistDetailsEditEvent {
- data class OnSongSelected(val isSelected: Boolean, val song: Song): PlaylistDetailsEditEvent()
- data class OnRemoveSong(val song: Song): PlaylistDetailsEditEvent()
- data class OnMoveUpSong(val song: Song): PlaylistDetailsEditEvent()
- data class OnMoveDownSong(val song: Song): PlaylistDetailsEditEvent()
+ data class OnSongSelected(val isSelected: Boolean, val song: SongUI): PlaylistDetailsEditEvent()
+ data class OnRemoveSong(val song: SongUI): PlaylistDetailsEditEvent()
+ data class OnMoveUpSong(val song: SongUI): PlaylistDetailsEditEvent()
+ data class OnMoveDownSong(val song: SongUI): PlaylistDetailsEditEvent()
data object OnRemoveSongDismiss: PlaylistDetailsEditEvent()
data object OnDeleteSelectedSongs: PlaylistDetailsEditEvent()
data object OnConfirmEdit: PlaylistDetailsEditEvent()
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistEditState.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistEditState.kt
index db8a2558..74ab5d5f 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistEditState.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/PlaylistEditState.kt
@@ -21,8 +21,8 @@
*/
package luci.sixsixsix.powerampache2.presentation.screens_detail.playlist_detail
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
data class PlaylistEditState(
- val selectedSongs: List
+ val selectedSongs: List
)
\ No newline at end of file
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/components/PlaylistInfoSection.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/components/PlaylistInfoSection.kt
index 0b96a055..9348cb14 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/components/PlaylistInfoSection.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/playlist_detail/components/PlaylistInfoSection.kt
@@ -44,9 +44,9 @@ import luci.sixsixsix.powerampache2.domain.models.ArtistId
import luci.sixsixsix.powerampache2.domain.models.MusicAttribute
import luci.sixsixsix.powerampache2.domain.models.Playlist
import luci.sixsixsix.powerampache2.domain.models.PlaylistType
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.screens_detail.album_detail.components.AttributeText
import luci.sixsixsix.powerampache2.presentation.common.MusicAttributeChips
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
enum class PlaylistInfoViewEvents {
PLAY_PLAYLIST,
@@ -72,7 +72,7 @@ fun PlaylistInfoSection(
isLikeLoading: Boolean,
isPlayLoading: Boolean,
enabled: Boolean,
- songs: List,
+ songs: List,
eventListener: (playlistInfoViewEvents: PlaylistInfoViewEvents) -> Unit,
artistClickListener: (ArtistId) -> Unit
) {
@@ -172,7 +172,10 @@ fun PlaylistInfoSectionPreview() {
isPlayingPlaylist = true,
isDownloading = false,
isGlobalShuffleOn = true,
- songs = listOf(Song.mockSong, Song.mockSong, Song.mockSong, Song.mockSong, Song.mockSong),
+ songs = listOf(
+ SongUI.mockSongUI, SongUI.mockSongUI, SongUI.mockSongUI,
+ SongUI.mockSongUI, SongUI.mockSongUI
+ ),
isPlaylistEditLoading = true,
isLikeLoading = false,
isLikeAvailable = true,
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailScreen.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailScreen.kt
index a196570f..bdb9ac62 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailScreen.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailScreen.kt
@@ -29,7 +29,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@@ -43,12 +42,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.media3.common.util.UnstableApi
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialogViewModel
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
import luci.sixsixsix.powerampache2.presentation.screens_detail.song_detail.components.SongDetailContent
import luci.sixsixsix.powerampache2.presentation.screens_detail.song_detail.components.SongDetailQueueDragHandle
import luci.sixsixsix.powerampache2.presentation.screens_detail.song_detail.components.TabbedSongDetailView
+import androidx.compose.runtime.collectAsState
@androidx.annotation.OptIn(UnstableApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@@ -86,7 +86,8 @@ fun SongDetailScreen(
val selectedTabIndex = remember { mutableIntStateOf(0) }
val queuePosStr = getQueuePositionStr(
- currentQueue = viewModel.currentQueue().value,
+ // TODO: the line below was missing collectAsState(), which was an error. Change needs testing/confirmation
+ currentQueue = viewModel.currentQueue().collectAsState().value,
// currentQueuePosition is USER FACING: start from 1, not zero
currentQueuePosition = viewModel.currentQueuePosition() + 1,
isScreenOpen = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded
@@ -128,7 +129,7 @@ fun SongDetailScreen(
}
}
-private fun getQueuePositionStr(currentQueue: List, currentQueuePosition: Int, isScreenOpen: Boolean) =
+private fun getQueuePositionStr(currentQueue: List, currentQueuePosition: Int, isScreenOpen: Boolean) =
if (
currentQueuePosition > 0
&& currentQueue.isNotEmpty()
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailViewModel.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailViewModel.kt
index 7e24cbc8..da2d27e3 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailViewModel.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/SongDetailViewModel.kt
@@ -32,7 +32,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import luci.sixsixsix.powerampache2.common.Resource
import luci.sixsixsix.powerampache2.domain.models.Artist
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.domain.plugin.info.PluginSongData
import luci.sixsixsix.powerampache2.domain.plugin.lyrics.getAvailableLyrics
import luci.sixsixsix.powerampache2.domain.usecase.ServerInfoStateFlowUseCase
@@ -43,6 +42,8 @@ import luci.sixsixsix.powerampache2.domain.usecase.plugin.IsLyricsPluginInstalle
import luci.sixsixsix.powerampache2.domain.usecase.plugin.LyricsFromPluginUseCase
import luci.sixsixsix.powerampache2.domain.usecase.plugin.SongDataFromPluginUseCase
import luci.sixsixsix.powerampache2.domain.usecase.songs.SongFromIdUseCase
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.toSong
import javax.inject.Inject
@HiltViewModel
@@ -57,6 +58,7 @@ class SongDetailViewModel @Inject constructor(
private val isChromecastPluginInstalled: IsChromecastPluginInstalled,
) : ViewModel() {
private val _recommendedArtistsStateFlow = MutableStateFlow>(listOf())
+ // TODO: is this needed?
val recommendedArtistsStateFlow = _recommendedArtistsStateFlow.asStateFlow()
private val _pluginInfo = MutableStateFlow(null)
@@ -75,7 +77,7 @@ class SongDetailViewModel @Inject constructor(
private var lyricsJob: Job? = null
private var songInfoJob: Job? = null
- private suspend fun getRecommendedArtists(song: Song) {
+ private suspend fun getRecommendedArtists(song: SongUI) {
recommendedArtistsUseCase(song.artist.id).collectLatest { result ->
when(result) {
is Resource.Error -> {}
@@ -90,7 +92,7 @@ class SongDetailViewModel @Inject constructor(
}
}
- fun onNewSong(song: Song) {
+ fun onNewSong(song: SongUI) {
lyricsJob?.cancel()
lyricsJob = viewModelScope.launch {
getSongLyrics(song)
@@ -108,7 +110,7 @@ class SongDetailViewModel @Inject constructor(
_isChromecastPluginInstalled.value = isChromecastPluginInstalled()
}
- private suspend fun getSongLyricsFromPlugin(song: Song) {
+ private suspend fun getSongLyricsFromPlugin(song: SongUI) {
_pluginLyrics.value = ""
// only fetch if no lyrics already present
if (lyrics.value.isBlank() && isLyricsPluginInstalledUseCase()) {
@@ -121,7 +123,7 @@ class SongDetailViewModel @Inject constructor(
}
- private suspend fun getSongLyrics(song: Song) {
+ private suspend fun getSongLyrics(song: SongUI) {
// if we already have lyrics, for a song with the same id there is no need to fetch again
if (lyrics.value.isNotBlank() && song.id == songId) return
songId = song.id
@@ -136,10 +138,10 @@ class SongDetailViewModel @Inject constructor(
}
}
- private suspend fun getSongInfoFromPlugin(song: Song) {
+ private suspend fun getSongInfoFromPlugin(song: SongUI) {
// only fetch if no lyrics already present
if (isInfoPluginInstalled()) {
- _pluginInfo.value = getSongInfoPluginUseCase(song)
+ _pluginInfo.value = getSongInfoPluginUseCase(song.toSong())
}
}
}
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/MiniPlayer.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/MiniPlayer.kt
index 7c3e1fd3..764088fc 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/MiniPlayer.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/MiniPlayer.kt
@@ -69,9 +69,9 @@ import androidx.media3.common.util.UnstableApi
import coil.compose.AsyncImage
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.domain.common.WeakContext
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.player.RepeatMode
import luci.sixsixsix.powerampache2.presentation.common.PlayButton
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -103,10 +103,11 @@ fun MiniPlayer(
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MiniPlayerContent(
- song: Song,
+ song: SongUI,
isPlaying: Boolean,
isPlayLoading: Boolean,
isBuffering: Boolean,
+ // TODO: unused parameters
shuffleOn: Boolean,
repeatMode: RepeatMode,
modifier: Modifier = Modifier,
@@ -257,9 +258,9 @@ fun MiniPlayerContent(
@Composable
@Preview
-fun previewMiniPlayer() {
+fun PreviewMiniPlayer() {
MiniPlayerContent(
- song = Song.mockSong,
+ song = SongUI.mockSongUI,
modifier = Modifier
.width(400.dp)
.height(50.dp),
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailButtonRow.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailButtonRow.kt
index 7c7c81af..8abfe009 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailButtonRow.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailButtonRow.kt
@@ -93,17 +93,18 @@ fun SongDetailButtonRow(
}
PlayerButton(
text = R.string.player_buttonText_download,
- icon = if (!isOffline)
- Icons.Outlined.DownloadForOffline
- else
- Icons.Outlined.OfflinePin,
+ icon =
+ if (isOffline)
+ Icons.Outlined.OfflinePin
+ else
+ Icons.Outlined.DownloadForOffline,
tint = tint,
modifier = btnModifier
) {
- if (!isOffline) {
- eventListener(SongDetailButtonEvents.DOWNLOAD_SONG)
- } else {
+ if (isOffline) {
eventListener(SongDetailButtonEvents.DELETE_DOWNLOADED_SONG)
+ } else {
+ eventListener(SongDetailButtonEvents.DOWNLOAD_SONG)
}
}
PlayerButton(
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailContent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailContent.kt
index 38a1d488..04a087d1 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailContent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailContent.kt
@@ -42,7 +42,6 @@ import androidx.compose.material3.BottomSheetScaffoldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.IconButtonColors
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -69,8 +68,7 @@ import coil.compose.AsyncImage
import kotlinx.coroutines.launch
import luci.sixsixsix.powerampache2.R
import luci.sixsixsix.powerampache2.common.fontDimensionResource
-import luci.sixsixsix.powerampache2.domain.models.Song
-import luci.sixsixsix.powerampache2.domain.models.totalTime
+import luci.sixsixsix.powerampache2.presentation.models.totalTime
import luci.sixsixsix.powerampache2.domain.plugin.info.PluginSongData
import luci.sixsixsix.powerampache2.presentation.common.LikeButton
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialog
@@ -78,6 +76,8 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDia
import luci.sixsixsix.powerampache2.presentation.dialogs.AddToPlaylistOrQueueDialogViewModel
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
+import luci.sixsixsix.powerampache2.presentation.models.isAvailableOffline
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -121,7 +121,7 @@ fun SongDetailContent(
var isOffline by remember { mutableStateOf(false) }
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
@@ -227,9 +227,7 @@ fun SongDetailContent(
Spacer(modifier = Modifier.height(16.dp))
currentSongState?.let { song ->
- mainViewModel.isOfflineSong(song) {
- isOffline = it
- }
+ isOffline = song.isAvailableOffline()
SongDetailButtonRow(
modifier = Modifier
.fillMaxWidth()
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueDragHandle.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueDragHandle.kt
index 9ba0e5cd..c59ca90c 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueDragHandle.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueDragHandle.kt
@@ -72,7 +72,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.text.HtmlCompat
import kotlinx.coroutines.launch
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
import luci.sixsixsix.powerampache2.ui.theme.additionalColours
import java.net.MalformedURLException
@@ -81,7 +81,7 @@ import java.net.URL
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SongDetailQueueDragHandle(
- song: Song?,
+ song: SongUI?,
lyrics: String,
scaffoldState: BottomSheetScaffoldState,
selectedTabIndex: MutableIntState,
@@ -136,7 +136,7 @@ fun SongDetailQueueDragHandle(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SongDetailQueueTopBar(
- song: Song?,
+ song: SongUI?,
lyrics: String,
modifier: Modifier = Modifier,
pagerState: PagerState,
@@ -259,7 +259,7 @@ fun WebPageView(url: String, modifier: Modifier = Modifier) {
}
},
update = {
- if (currentUrl.isNotBlank() && it.url?.toString() != currentUrl) {
+ if (currentUrl.isNotBlank() && it.url != currentUrl) {
it.loadUrl(currentUrl)
}
}
@@ -270,19 +270,20 @@ fun WebPageView(url: String, modifier: Modifier = Modifier) {
@Composable
fun SongHandleTabRow(
modifier: Modifier = Modifier,
- song: Song?,
+ // TODO: isn't used?
+ song: SongUI?,
lyrics: String,
scaffoldState: BottomSheetScaffoldState,
pagerState: PagerState,
upNextText: String = stringResource(id = R.string.player_queue_upNext),
selectedTabIndex: MutableIntState
) {
- LaunchedEffect(selectedTabIndex.value) {
- pagerState.animateScrollToPage(selectedTabIndex.value)
+ LaunchedEffect(selectedTabIndex.intValue) {
+ pagerState.animateScrollToPage(selectedTabIndex.intValue)
}
LaunchedEffect(pagerState.currentPage, pagerState.isScrollInProgress) {
if (!pagerState.isScrollInProgress) {
- selectedTabIndex.value = pagerState.currentPage
+ selectedTabIndex.intValue = pagerState.currentPage
}
}
@@ -293,23 +294,23 @@ fun SongHandleTabRow(
},
modifier = modifier,
- selectedTabIndex = selectedTabIndex.value,
+ selectedTabIndex = selectedTabIndex.intValue,
contentColor = textColour,
containerColor = Color.Transparent
) {
Tab(
unselectedContentColor = textColour.copy(alpha = 0.66f),
- selected = selectedTabIndex.value == 0,
+ selected = selectedTabIndex.intValue == 0,
onClick = {
scope.launch {
// if we're in the tab that is selected just close the drawer
if (scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded &&
- selectedTabIndex.value == 0) {
+ selectedTabIndex.intValue == 0) {
scaffoldState.bottomSheetState.partialExpand()
} else {
scaffoldState.bottomSheetState.expand()
}
- selectedTabIndex.value = 0
+ selectedTabIndex.intValue = 0
}
},
text = {
@@ -326,18 +327,18 @@ fun SongHandleTabRow(
if (lyrics != "") {
Tab(
unselectedContentColor = textColour.copy(alpha = 0.66f),
- selected = selectedTabIndex.value == 1,
+ selected = selectedTabIndex.intValue == 1,
onClick = {
scope.launch {
// if we're in the tab that is selected just close the drawer
if (scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded &&
- selectedTabIndex.value == 1
+ selectedTabIndex.intValue == 1
) {
scaffoldState.bottomSheetState.partialExpand()
} else {
scaffoldState.bottomSheetState.expand()
}
- selectedTabIndex.value = 1
+ selectedTabIndex.intValue = 1
}
},
text = {
diff --git a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueScreenContent.kt b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueScreenContent.kt
index 33da925b..ec90ff06 100644
--- a/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueScreenContent.kt
+++ b/app/src/main/java/luci/sixsixsix/powerampache2/presentation/screens_detail/song_detail/components/SongDetailQueueScreenContent.kt
@@ -44,7 +44,6 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.launch
import luci.sixsixsix.powerampache2.R
-import luci.sixsixsix.powerampache2.domain.models.Song
import luci.sixsixsix.powerampache2.presentation.navigation.Ampache2NavGraphs
import luci.sixsixsix.powerampache2.presentation.screens.queue.QueueEvent
import luci.sixsixsix.powerampache2.presentation.screens.queue.QueueViewModel
@@ -58,6 +57,7 @@ import luci.sixsixsix.powerampache2.presentation.dialogs.EraseConfirmDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.ShareDialog
import luci.sixsixsix.powerampache2.presentation.dialogs.info.InfoDialogSong
import luci.sixsixsix.powerampache2.presentation.dialogs.info.ShowSongInfoDialogOpen
+import luci.sixsixsix.powerampache2.presentation.models.SongUI
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainEvent
import luci.sixsixsix.powerampache2.presentation.screens.main.viewmodel.MainViewModel
@@ -91,7 +91,7 @@ fun SongDetailQueueScreenContent(
}
}
- var showRemoveFromQueueDialog by remember { mutableStateOf(null) }
+ var showRemoveFromQueueDialog by remember { mutableStateOf(null) }
showRemoveFromQueueDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -106,7 +106,7 @@ fun SongDetailQueueScreenContent(
)
}
- var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
+ var showDeleteFromDownloadsDialog by remember { mutableStateOf(null) }
showDeleteFromDownloadsDialog?.let { songToRemove ->
EraseConfirmDialog(
onDismissRequest = {
@@ -130,7 +130,7 @@ fun SongDetailQueueScreenContent(
}
}
- var songToShare: Song? by remember { mutableStateOf(null) }
+ var songToShare: SongUI? by remember { mutableStateOf(null) }
AnimatedVisibility(songToShare != null) {
songToShare?.let { songS ->
ShareDialog(
diff --git a/domain/src/main/java/luci/sixsixsix/powerampache2/domain/utils/ShareManager.kt b/domain/src/main/java/luci/sixsixsix/powerampache2/domain/utils/ShareManager.kt
index a06394c9..51df6f5d 100644
--- a/domain/src/main/java/luci/sixsixsix/powerampache2/domain/utils/ShareManager.kt
+++ b/domain/src/main/java/luci/sixsixsix/powerampache2/domain/utils/ShareManager.kt
@@ -31,8 +31,8 @@ interface ShareManager {
suspend fun shareSongWeb(context: Context, song: Song)
suspend fun fetchDeepLinkedSong(id: String, title: String, artist: String,
- songCallback: (song: Song) -> Unit,
- songsCallback: (songs: List) -> Unit,
+ songCallback: suspend (song: Song) -> Unit,
+ songsCallback: suspend (songs: List) -> Unit,
errorCallback: () -> Unit)
companion object {