Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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)
Expand Down
14 changes: 9 additions & 5 deletions app/src/main/java/luci/sixsixsix/powerampache2/common/Mappers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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")}")
Expand Down Expand Up @@ -77,8 +78,8 @@ class ShareManagerImpl @Inject constructor(
id: String,
title: String,
artist: String,
songCallback: (song: Song) -> Unit,
songsCallback: (songs: List<Song>) -> Unit,
songCallback: suspend (song: Song) -> Unit,
songsCallback: suspend (songs: List<Song>) -> Unit,
errorCallback: () -> Unit
) {
val song = getSongFromIdUseCase(id)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Song>) -> Unit,
songsCallback: (List<SongUI>) -> Unit,
loadingCallback: (Boolean) -> Unit = { },
errorCallback: (Throwable?) -> Unit = { }
)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Song>) -> Unit,
songsCallback: (List<SongUI>) -> Unit,
loadingCallback: (Boolean) -> Unit,
errorCallback: (Throwable?) -> Unit
) {
Expand All @@ -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)
})
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Song?>(null)
val currentSongState: StateFlow<Song?> = _currentSongState //val currentSong = _currentSong.asStateFlow()
private val _currentSongState = MutableStateFlow<SongUI?>(null)
val currentSongState: StateFlow<SongUI?> = _currentSongState //val currentSong = _currentSong.asStateFlow()

private val _currentSearchQuery = MutableStateFlow("")
val currentSearchQuery: StateFlow<String> = _currentSearchQuery

private val _currentQueueState = MutableStateFlow(listOf<Song>())
val currentQueueState: StateFlow<List<Song>> = _currentQueueState
private val _currentQueueState = MutableStateFlow(listOf<SongUI>())
val currentQueueState: StateFlow<List<SongUI>> = _currentQueueState

private val _downloadedSongFlow = MutableStateFlow<Song?>(null)
val downloadedSongFlow: StateFlow<Song?> = _downloadedSongFlow
private val _downloadedSongFlow = MutableStateFlow<SongUI?>(null)
// TODO: is this needed?
val downloadedSongFlow: StateFlow<SongUI?> = _downloadedSongFlow

fun updateDownloadedSong(song: Song?) {
fun updateDownloadedSong(song: SongUI?) {
_downloadedSongFlow.value = song
}

Expand All @@ -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<Song>) {
fun addToCurrentQueueUpdateTopSong(newSong: SongUI, newQueue: List<SongUI>) {
// add the current song on top of the queue
val updatedQueue = ArrayList(_currentQueueState.value).apply {
remove(newSong)
Expand All @@ -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
}
Expand All @@ -86,27 +87,27 @@ class MusicPlaylistManager @Inject constructor() {
* same as updateCurrentSong but also provides current queue
* TODO unused function
*/
fun moveToSongInQueue(newSong: Song?, queue: List<Song>) = newSong?.let {
L( "MusicPlaylistManager moveToSongInQueue", newSong)
_currentSongState.value = newSong
}
//fun moveToSongInQueue(newSong: SongUI?, queue: List<SongUI>) = newSong?.let {
// L( "MusicPlaylistManager moveToSongInQueue", newSong)
// _currentSongState.value = newSong
//}

fun replaceCurrentQueue(newQueue: List<Song>) {
fun replaceCurrentQueue(newQueue: List<SongUI>) {
L( "MusicPlaylistManager replaceCurrentQueue", newQueue.size)
_currentQueueState.value = newQueue.filterNotNull().reduceList()
_currentQueueState.value = newQueue.reduceList()
checkCurrentSong()
}

fun replaceQueuePlaySong(newQueue: List<Song>, songToPlay: Song) {
_currentQueueState.value = newQueue.filterNotNull().reduceList()
fun replaceQueuePlaySong(newQueue: List<SongUI>, songToPlay: SongUI) {
_currentQueueState.value = newQueue.reduceList()
_currentSongState.value = songToPlay
}

/**
* 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<Song>) {
fun addToCurrentQueue(newQueue: List<SongUI>) {
L( "MusicPlaylistManager addToCurrentQueue", newQueue.size)
_currentQueueState.value = LinkedHashSet(_currentQueueState.value)
.apply { addAll(newQueue) }
Expand All @@ -118,17 +119,17 @@ 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))
}

/**
* removes a list of songs from the current queue
*/
fun removeFromCurrentQueue(songsToRemove: List<Song>) {
fun removeFromCurrentQueue(songsToRemove: List<SongUI>) {
_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()) {
Expand All @@ -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<Song>) {
fun addToCurrentQueueNext(list: List<SongUI>) {
L( "MusicPlaylistManager addToCurrentQueueNext", list.size)
val queue = ArrayList(_currentQueueState.value)
.apply {
Expand All @@ -167,9 +168,9 @@ class MusicPlaylistManager @Inject constructor() {
replaceCurrentQueue(queue)
}

fun addToCurrentQueueTop(list: List<Song>) {
fun addToCurrentQueueTop(list: List<SongUI>) {
L( "MusicPlaylistManager addToCurrentQueueTop", list.size)
val queue = ArrayList<Song>(currentQueueState.value).apply {
val queue = ArrayList<SongUI>(currentQueueState.value).apply {
addAll(0, list)
}
replaceCurrentQueue(queue)
Expand All @@ -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
Expand All @@ -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))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -112,5 +104,6 @@ class PlayerManager @OptIn(UnstableApi::class)
_playerState.value = null
}

// TODO: unused?
fun isPlayerInitialized(): Boolean = _player != null
}
Loading