diff --git a/app/src/main/java/dev/brahmkshatriya/echo/EchoApplication.kt b/app/src/main/java/dev/brahmkshatriya/echo/EchoApplication.kt index 114673d8..7d9fd838 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/EchoApplication.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/EchoApplication.kt @@ -25,6 +25,8 @@ import dev.brahmkshatriya.echo.plugger.MusicExtensionRepo import dev.brahmkshatriya.echo.plugger.RequiredExtensionsException import dev.brahmkshatriya.echo.plugger.TrackerExtension import dev.brahmkshatriya.echo.plugger.TrackerExtensionRepo +import dev.brahmkshatriya.echo.ui.exception.ExceptionFragment.Companion.getDetails +import dev.brahmkshatriya.echo.ui.exception.ExceptionFragment.Companion.getTitle import dev.brahmkshatriya.echo.ui.settings.LookFragment.Companion.AMOLED_KEY import dev.brahmkshatriya.echo.ui.settings.LookFragment.Companion.COLOR_KEY import dev.brahmkshatriya.echo.ui.settings.LookFragment.Companion.CUSTOM_THEME_KEY @@ -102,7 +104,8 @@ class EchoApplication : Application() { scope.launch { throwableFlow.collect { - it.printStackTrace() + println(getTitle(it)) + println(getDetails(it)) } } diff --git a/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt b/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt index 57135321..d6dca93b 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt @@ -32,7 +32,9 @@ import dev.brahmkshatriya.echo.playback.MediaItemUtils.clientId import dev.brahmkshatriya.echo.playback.MediaItemUtils.track import dev.brahmkshatriya.echo.plugger.MusicExtension import dev.brahmkshatriya.echo.plugger.getExtension +import dev.brahmkshatriya.echo.ui.exception.ExceptionFragment.Companion.toExceptionDetails import dev.brahmkshatriya.echo.utils.getSerialized +import dev.brahmkshatriya.echo.utils.putSerialized import dev.brahmkshatriya.echo.viewmodels.ExtensionViewModel.Companion.noClient import dev.brahmkshatriya.echo.viewmodels.ExtensionViewModel.Companion.searchNotSupported import dev.brahmkshatriya.echo.viewmodels.ExtensionViewModel.Companion.trackNotSupported @@ -129,7 +131,8 @@ class PlayerSessionCallback( runCatching { client.likeTrack(track, rating.isThumbsUp) } }.getOrElse { return@future SessionResult( - SessionError.ERROR_UNKNOWN, bundleOf("error" to it) + SessionError.ERROR_UNKNOWN, + Bundle().apply { putSerialized("error", it.toExceptionDetails(context)) } ) } val newItem = item.run { diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/AddToPlaylistViewModel.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/AddToPlaylistViewModel.kt index 54c4cfc8..2a1384ee 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/AddToPlaylistViewModel.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/AddToPlaylistViewModel.kt @@ -58,6 +58,7 @@ class AddToPlaylistViewModel @Inject constructor( val dismiss = MutableSharedFlow() fun addToPlaylists() = viewModelScope.launch { saving = true + println("selectedPlaylists: ${selectedPlaylists.joinToString(", ") { it.title }}") addToPlaylists( extensionListFlow, messageFlow, app, clientId, selectedPlaylists, tracks ) diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistViewModel.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistViewModel.kt index 8ca5dfbb..596f065c 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistViewModel.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistViewModel.kt @@ -41,8 +41,8 @@ class EditPlaylistViewModel @Inject constructor( val client = extension?.client if (client !is LibraryClient) return@launch withContext(Dispatchers.IO) { - originalList = tryWith(extension.info) { client.loadTracks(playlist).loadAll() } - ?: emptyList() + originalList = + tryWith(extension.info) { client.loadTracks(playlist).loadAll() } ?: emptyList() currentTracks.value = originalList loading = null loadingFlow.emit(null) @@ -51,8 +51,7 @@ class EditPlaylistViewModel @Inject constructor( } private suspend fun libraryClient( - clientId: String, - block: suspend (client: LibraryClient) -> Unit + clientId: String, block: suspend (client: LibraryClient) -> Unit ) { client(clientId, block) } @@ -99,9 +98,8 @@ class EditPlaylistViewModel @Inject constructor( } } - fun deletePlaylist(clientId: String, playlist: Playlist) = viewModelScope.launch { + fun deletePlaylist(clientId: String, playlist: Playlist) = deletePlaylist(extensionListFlow, mutableMessageFlow, context, clientId, playlist) - } private fun mergeActions(actions: MutableList>) { var i = 0 @@ -124,8 +122,7 @@ class EditPlaylistViewModel @Inject constructor( } fun onEditorExit( - clientId: String, - playlist: Playlist + clientId: String, playlist: Playlist ) = viewModelScope.launch(Dispatchers.IO) { if (loading == true) return@launch loading = true @@ -187,7 +184,7 @@ class EditPlaylistViewModel @Inject constructor( } companion object { - suspend fun CatchingViewModel.deletePlaylist( + fun CatchingViewModel.deletePlaylist( extensionListFlow: MutableStateFlow?>, mutableMessageFlow: MutableSharedFlow, context: Context, @@ -197,8 +194,12 @@ class EditPlaylistViewModel @Inject constructor( val extension = extensionListFlow.getExtension(clientId) ?: return val client = extension.client if (client !is LibraryClient) return - withContext(Dispatchers.IO) { - tryWith(extension.info) { client.deletePlaylist(playlist) } ?: return@withContext + viewModelScope.launch(Dispatchers.IO) { + tryWith(extension.info) { + println("deleting playlist : ${playlist.title}") + client.deletePlaylist(playlist) + println("deleted playlist : ${playlist.title}") + } mutableMessageFlow.emit(SnackBar.Message(context.getString(R.string.playlist_deleted))) } } @@ -219,17 +220,22 @@ class EditPlaylistViewModel @Inject constructor( playlists.forEach { playlist -> tryWith(extension.info) { check(playlist.isEditable) - val tracks = client.loadTracks(playlist).loadAll() - listener?.onEnterPlaylistEditor(playlist, tracks) - client.addTracksToPlaylist(playlist, tracks, tracks.size, new) - listener?.onExitPlaylistEditor(playlist, tracks) - } + val loaded = client.loadPlaylist(playlist) + println("loaded : $loaded") + val tracks = client.loadTracks(loaded).loadAll() + println("tracks: ${tracks.size}") + listener?.onEnterPlaylistEditor(loaded, tracks) + client.addTracksToPlaylist(loaded, tracks, tracks.size, new) + println("added to ${loaded.title}") + listener?.onExitPlaylistEditor(loaded, tracks) + Unit + } ?: return@withContext } + val message = + if (playlists.size != 1) context.getString(R.string.saved_to_playlists) + else context.getString(R.string.saved_to_playlist, playlists.first().title) + messageFlow.emit(SnackBar.Message(message)) } - val message = if (playlists.size == 1) - context.getString(R.string.saved_to_playlist, playlists.first()) - else context.getString(R.string.saved_to_playlists) - messageFlow.emit(SnackBar.Message(message)) } } } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/exception/ExceptionFragment.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/exception/ExceptionFragment.kt index 4cc31d90..cfc87936 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/exception/ExceptionFragment.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/exception/ExceptionFragment.kt @@ -89,7 +89,7 @@ class ExceptionFragment : Fragment() { fun Context.getTitle(throwable: Throwable): String = when (throwable) { is IncompatibleClassChangeError -> getString(R.string.extension_out_of_date) is UnknownHostException, is UnresolvedAddressException -> getString(R.string.no_internet) - is PlayerViewModel.PlayerException -> getTitle(throwable.cause) + is PlayerViewModel.PlayerException -> throwable.details.title is AppException -> throwable.run { when (this) { is AppException.Unauthorized -> @@ -114,7 +114,7 @@ Client Id : ${throwable.mediaItem?.clientId} Track : ${throwable.mediaItem?.track} Stream : ${throwable.mediaItem?.run { track.audioStreamables.getOrNull(audioIndex) }} -${getDetails(throwable.cause)} +${throwable.details.causedBy} """.trimIndent() is AppException -> """ @@ -143,6 +143,8 @@ ${getDetails(throwable.cause)} return newInstance(details) } + fun Throwable.toExceptionDetails(context: Context) = + ExceptionDetails(context.getTitle(this), context.getDetails(this)) } @Serializable diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemBottomSheet.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemBottomSheet.kt index b351fb3c..2c166ac9 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemBottomSheet.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemBottomSheet.kt @@ -104,7 +104,7 @@ class ItemBottomSheet : BottomSheetDialogFragment() { } else { binding.recyclerView.adapter = ActionAdapter(getActions(item, true)) } - observe(viewModel.shareLink) { + observe(playerViewModel.shareLink) { requireContext().copyToClipboard(it, it) } } @@ -173,7 +173,7 @@ class ItemBottomSheet : BottomSheetDialogFragment() { else null, if (client is LibraryClient && item.playlist.isEditable) ItemAction.Resource(R.drawable.ic_delete, R.string.delete_playlist) { - viewModel.deletePlaylist(clientId, item.playlist) + playerViewModel.deletePlaylist(clientId, item.playlist) } else null, ) + item.playlist.authors.map { @@ -262,7 +262,7 @@ class ItemBottomSheet : BottomSheetDialogFragment() { R.string.share ) { val shareClient = client as ShareClient - viewModel.onShare(shareClient, item) + playerViewModel.onShare(shareClient, item) } else null ) diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemViewModel.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemViewModel.kt index f0df3cb3..439c6e5b 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemViewModel.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/ItemViewModel.kt @@ -1,6 +1,5 @@ package dev.brahmkshatriya.echo.ui.item -import android.app.Application import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import androidx.paging.PagingData @@ -9,7 +8,6 @@ import dev.brahmkshatriya.echo.common.clients.AlbumClient import dev.brahmkshatriya.echo.common.clients.ArtistClient import dev.brahmkshatriya.echo.common.clients.ArtistFollowClient import dev.brahmkshatriya.echo.common.clients.PlaylistClient -import dev.brahmkshatriya.echo.common.clients.ShareClient import dev.brahmkshatriya.echo.common.clients.TrackClient import dev.brahmkshatriya.echo.common.clients.UserClient import dev.brahmkshatriya.echo.common.helpers.PagedData @@ -23,10 +21,8 @@ import dev.brahmkshatriya.echo.common.models.Track import dev.brahmkshatriya.echo.plugger.ExtensionInfo import dev.brahmkshatriya.echo.plugger.GenericExtension import dev.brahmkshatriya.echo.plugger.MusicExtension -import dev.brahmkshatriya.echo.ui.editplaylist.EditPlaylistViewModel.Companion.deletePlaylist import dev.brahmkshatriya.echo.ui.paging.toFlow import dev.brahmkshatriya.echo.viewmodels.CatchingViewModel -import dev.brahmkshatriya.echo.viewmodels.SnackBar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -38,8 +34,6 @@ import javax.inject.Inject class ItemViewModel @Inject constructor( throwableFlow: MutableSharedFlow, val extensionListFlow: MutableStateFlow?>, - private val mutableMessageFlow: MutableSharedFlow, - private val context: Application ) : CatchingViewModel(throwableFlow) { var item: EchoMediaItem? = null @@ -106,24 +100,6 @@ class ItemViewModel @Inject constructor( } } - val shareLink = MutableSharedFlow() - fun onShare(client: ShareClient, item: EchoMediaItem) { - viewModelScope.launch(Dispatchers.IO) { - val link = when (item) { - is EchoMediaItem.Lists.AlbumItem -> client.onShare(item.album) - is EchoMediaItem.Lists.PlaylistItem -> client.onShare(item.playlist) - is EchoMediaItem.Profile.ArtistItem -> client.onShare(item.artist) - is EchoMediaItem.Profile.UserItem -> client.onShare(item.user) - is EchoMediaItem.TrackItem -> client.onShare(item.track) - } - shareLink.emit(link) - } - } - - fun deletePlaylist(clientId: String, playlist: Playlist) = viewModelScope.launch { - deletePlaylist(extensionListFlow, mutableMessageFlow, context, clientId, playlist) - } - private val songsFlow = MutableStateFlow?>(null) val songsLiveData = songsFlow.asLiveData() diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt index dc17eba0..345616c2 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt @@ -124,9 +124,11 @@ class PlayerTrackAdapter( binding.bgInfoTitle.setTextColor(colors.text) binding.bgInfoArtist.setTextColor(colors.text) - Glide.with(binding.bgImage).load(bitmap) - .apply(RequestOptions.bitmapTransform(BlurTransformation(2, 4))) - .into(binding.bgImage) + runCatching { + Glide.with(binding.bgImage).load(bitmap) + .apply(RequestOptions.bitmapTransform(BlurTransformation(2, 4))) + .into(binding.bgImage) + } } binding.collapsedContainer.root.setOnClickListener { @@ -212,20 +214,21 @@ class PlayerTrackAdapter( } binding.playerControls.trackHeart.run { - viewModel.isLiked.value = item.isLiked - isChecked = item.isLiked val client = viewModel.extensionListFlow.getExtension(clientId)?.client val isLibrary = client is LibraryClient isVisible = isLibrary + + val likeListener = viewModel.likeListener if (isLibrary) { - val likeListener = viewModel.likeListener addOnCheckedStateChangedListener(likeListener) observeCurrent(viewModel.isLiked) { + it ?: return@observeCurrent likeListener.enabled = false - binding.playerControls.trackHeart.isChecked = it ?: false + binding.playerControls.trackHeart.isChecked = it likeListener.enabled = true } - } else removeOnCheckedStateChangedListener(viewModel.likeListener) + } else removeOnCheckedStateChangedListener(likeListener) + viewModel.isLiked.value = item.isLiked } binding.playerControls.seekBar.apply { @@ -344,7 +347,7 @@ class PlayerTrackAdapter( val repeatModes = listOf(REPEAT_MODE_OFF, REPEAT_MODE_ALL, REPEAT_MODE_ONE) var currRepeatMode = viewModel.repeatMode.value fun changeRepeatDrawable(repeatMode: Int) = binding.playerControls.trackRepeat.run { - if(currRepeatMode == repeatMode) { + if (currRepeatMode == repeatMode) { icon = drawables[repeatModes.indexOf(repeatMode)] return } diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerUiListener.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerUiListener.kt index 34c2b3ff..696a857d 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerUiListener.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerUiListener.kt @@ -7,6 +7,7 @@ import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Timeline import androidx.media3.exoplayer.ExoPlayer +import dev.brahmkshatriya.echo.ui.exception.ExceptionFragment.Companion.toExceptionDetails import dev.brahmkshatriya.echo.viewmodels.PlayerViewModel import kotlinx.coroutines.launch @@ -107,6 +108,6 @@ class PlayerUiListener( } override fun onPlayerError(error: PlaybackException) { - viewModel.createException(error) + viewModel.createException(error.toExceptionDetails(viewModel.app)) } } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/viewmodels/PlayerViewModel.kt b/app/src/main/java/dev/brahmkshatriya/echo/viewmodels/PlayerViewModel.kt index db33b5d5..be27d527 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/viewmodels/PlayerViewModel.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/viewmodels/PlayerViewModel.kt @@ -15,8 +15,10 @@ import androidx.media3.session.MediaBrowser import dagger.hilt.android.lifecycle.HiltViewModel import dev.brahmkshatriya.echo.common.clients.AlbumClient import dev.brahmkshatriya.echo.common.clients.PlaylistClient +import dev.brahmkshatriya.echo.common.clients.ShareClient import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem +import dev.brahmkshatriya.echo.common.models.Playlist import dev.brahmkshatriya.echo.common.models.Track import dev.brahmkshatriya.echo.playback.Current import dev.brahmkshatriya.echo.playback.FFTAudioProcessor @@ -26,10 +28,12 @@ import dev.brahmkshatriya.echo.playback.Radio import dev.brahmkshatriya.echo.playback.ResumptionUtils import dev.brahmkshatriya.echo.plugger.MusicExtension import dev.brahmkshatriya.echo.plugger.getExtension +import dev.brahmkshatriya.echo.ui.editplaylist.EditPlaylistViewModel.Companion.deletePlaylist +import dev.brahmkshatriya.echo.ui.exception.ExceptionFragment import dev.brahmkshatriya.echo.ui.player.CheckBoxListener import dev.brahmkshatriya.echo.ui.player.PlayerUiListener import dev.brahmkshatriya.echo.ui.settings.AudioFragment.AudioPreference.Companion.KEEP_QUEUE -import dev.brahmkshatriya.echo.utils.getSerial +import dev.brahmkshatriya.echo.utils.getSerialized import dev.brahmkshatriya.echo.utils.listenFuture import dev.brahmkshatriya.echo.utils.toJson import kotlinx.coroutines.Dispatchers @@ -50,6 +54,7 @@ class PlayerViewModel @Inject constructor( val radioStateFlow: MutableStateFlow, val cache: SimpleCache, val fftAudioProcessor: FFTAudioProcessor, + val mutableMessageFlow: MutableSharedFlow, throwableFlow: MutableSharedFlow, ) : CatchingViewModel(throwableFlow) { @@ -82,7 +87,10 @@ class PlayerViewModel @Inject constructor( fun seekToPrevious() = withBrowser { it.seekToPrevious() } fun seekToNext() = withBrowser { it.seekToNext() } - val likeListener = CheckBoxListener { likeTrack(it) } + val likeListener = CheckBoxListener { + println("sending like $it") + likeTrack(it) + } private fun likeTrack(isLiked: Boolean) = withBrowser { val old = this.isLiked.value this.isLiked.value = isLiked @@ -90,8 +98,9 @@ class PlayerViewModel @Inject constructor( app.listenFuture(future) { sessionResult -> val result = sessionResult.getOrThrow() if (result.resultCode != RESULT_SUCCESS) { - val exception = result.extras.getSerial("error") - ?: Exception("Error : ${result.resultCode}") + val exception = + result.extras.getSerialized("error") + ?: ExceptionFragment.ExceptionDetails("IO Error", "") createException(exception) this.isLiked.value = old } @@ -225,18 +234,18 @@ class PlayerViewModel @Inject constructor( } } - fun createException(exception: Throwable) = withBrowser { + fun createException(exceptionDetails: ExceptionFragment.ExceptionDetails) = withBrowser { viewModelScope.launch { throwableFlow.emit( - PlayerException(exception.cause ?: exception, it.currentMediaItem) + PlayerException(exceptionDetails, it.currentMediaItem) ) } } data class PlayerException( - override val cause: Throwable, + val details: ExceptionFragment.ExceptionDetails, val mediaItem: MediaItem? - ) : Throwable(cause.message) + ) : Throwable() var list: List = listOf() @@ -253,4 +262,22 @@ class PlayerViewModel @Inject constructor( val previousEnabled = MutableStateFlow(false) val repeatMode = MutableStateFlow(0) val shuffleMode = MutableStateFlow(false) + + + val shareLink = MutableSharedFlow() + fun onShare(client: ShareClient, item: EchoMediaItem) { + viewModelScope.launch(Dispatchers.IO) { + val link = when (item) { + is EchoMediaItem.Lists.AlbumItem -> client.onShare(item.album) + is EchoMediaItem.Lists.PlaylistItem -> client.onShare(item.playlist) + is EchoMediaItem.Profile.ArtistItem -> client.onShare(item.artist) + is EchoMediaItem.Profile.UserItem -> client.onShare(item.user) + is EchoMediaItem.TrackItem -> client.onShare(item.track) + } + shareLink.emit(link) + } + } + + fun deletePlaylist(clientId: String, playlist: Playlist) = + deletePlaylist(extensionListFlow, mutableMessageFlow, app, clientId, playlist) } \ No newline at end of file diff --git a/app/src/main/res/values-hng/strings.xml b/app/src/main/res/values-hng/strings.xml index 8ea4e7b8..3ec791e0 100644 --- a/app/src/main/res/values-hng/strings.xml +++ b/app/src/main/res/values-hng/strings.xml @@ -71,7 +71,7 @@ Track se related gaane playlist me automatically add karo, taaki queue infinite ho Track Behavior - Playlists me save kiya + Playlists me save hogaya Artist Unfollow Follow