Hemanth S 2020-09-21 16:19:50 +05:30
commit 022bf3624c
76 changed files with 900 additions and 806 deletions

View File

@ -107,6 +107,7 @@ dependencies {
implementation 'androidx.palette:palette-ktx:1.0.0' implementation 'androidx.palette:palette-ktx:1.0.0'
def nav_version = "2.3.0" def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

View File

@ -13,13 +13,13 @@
}, },
{ {
"name": "Daksh P. Jain", "name": "Daksh P. Jain",
"summary": "Telegram group maintainer", "summary": "Support Representative & Moderator",
"link": "https://dakshpjain.eu.org", "link": "https://daksh.eu.org",
"profile_image": "https://i.imgur.com/fnYpg65.jpg" "profile_image": "https://i.imgur.com/fnYpg65.jpg"
}, },
{ {
"name": "Milind Goel", "name": "Milind Goel",
"summary": "Github & Telegram maintainer", "summary": "Support Representative & Moderator",
"link": "https://t.me/MilindGoel15", "link": "https://t.me/MilindGoel15",
"profile_image": "https://i.imgur.com/Bz4De21_d.jpg" "profile_image": "https://i.imgur.com/Bz4De21_d.jpg"
} }

View File

@ -162,14 +162,14 @@ private val viewModules = module {
LibraryViewModel(get()) LibraryViewModel(get())
} }
viewModel { (albumId: Int) -> viewModel { (albumId: Long) ->
AlbumDetailsViewModel( AlbumDetailsViewModel(
get(), get(),
albumId albumId
) )
} }
viewModel { (artistId: Int) -> viewModel { (artistId: Long) ->
ArtistDetailsViewModel( ArtistDetailsViewModel(
get(), get(),
artistId artistId

View File

@ -28,8 +28,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
const val APP_UPDATE_REQUEST_CODE = 9002 const val APP_UPDATE_REQUEST_CODE = 9002
} }
private var blockRequestPermissions = false
override fun createContentView(): View { override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_main_content) return wrapSlidingMusicPanel(R.layout.activity_main_content)
} }
@ -64,14 +62,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (!hasPermissions()) {
//requestPermissions()
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES
) { ) {
@ -107,8 +97,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
MusicPlayerRemote.playFromUri(uri) MusicPlayerRemote.playFromUri(uri)
handled = true handled = true
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
val id: Int = parseIdFromIntent(intent, "playlistId", "playlist").toInt() val id = parseLongFromIntent(intent, "playlistId", "playlist")
if (id >= 0) { if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
val songs: List<Song> = val songs: List<Song> =
PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id) PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id)
@ -116,19 +106,19 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
handled = true handled = true
} }
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt() val id = parseLongFromIntent(intent, "albumId", "album")
if (id >= 0) { if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue( MusicPlayerRemote.openQueue(
libraryViewModel.albumById(id).songs!!, libraryViewModel.albumById(id).songs,
position, position,
true true
) )
handled = true handled = true
} }
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
val id: Int = parseIdFromIntent(intent, "artistId", "artist").toInt() val id = parseLongFromIntent(intent, "artistId", "artist")
if (id >= 0) { if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue( MusicPlayerRemote.openQueue(
libraryViewModel.artistById(id).songs, libraryViewModel.artistById(id).songs,
@ -145,7 +135,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
} }
private fun parseIdFromIntent( private fun parseLongFromIntent(
intent: Intent, longKey: String, intent: Intent, longKey: String,
stringKey: String stringKey: String
): Long { ): Long {

View File

@ -40,7 +40,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
val repository by inject<Repository>() val repository by inject<Repository>()
lateinit var saveFab: MaterialButton lateinit var saveFab: MaterialButton
protected var id: Int = 0 protected var id: Long = 0
private set private set
private var paletteColorPrimary: Int = 0 private var paletteColorPrimary: Int = 0
private var isInNoImageMode: Boolean = false private var isInNoImageMode: Boolean = false
@ -251,7 +251,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
private fun getIntentExtras() { private fun getIntentExtras() {
val intentExtras = intent.extras val intentExtras = intent.extras
if (intentExtras != null) { if (intentExtras != null) {
id = intentExtras.getInt(EXTRA_ID) id = intentExtras.getLong(EXTRA_ID)
} }
} }
@ -396,7 +396,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
} }
} }
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?) class ArtworkInfo constructor(val albumId: Long, val artwork: Bitmap?)
companion object { companion object {

View File

@ -18,6 +18,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.appHandleColor import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.ImageUtil import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
import code.name.monkey.retromusic.util.RetroColorUtil.getColor import code.name.monkey.retromusic.util.RetroColorUtil.getColor
@ -168,12 +169,8 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
} }
override fun getSongPaths(): List<String> { override fun getSongPaths(): List<String> {
val songs = repository.albumById(id).songs return repository.albumById(id).songs
val paths = ArrayList<String>(songs!!.size) .map(Song::data)
for (song in songs) {
paths.add(song.data)
}
return paths
} }
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {

View File

@ -71,13 +71,13 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) {
CategoryInfo categoryInfo = categoryInfos.get(position); CategoryInfo categoryInfo = categoryInfos.get(position);
holder.checkBox.setChecked(categoryInfo.visible); holder.checkBox.setChecked(categoryInfo.isVisible());
holder.title.setText(holder.title.getResources().getString(categoryInfo.category.stringRes)); holder.title.setText(holder.title.getResources().getString(categoryInfo.getCategory().getStringRes()));
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) { if (!(categoryInfo.isVisible() && isLastCheckedCategory(categoryInfo))) {
categoryInfo.visible = !categoryInfo.visible; categoryInfo.setVisible(!categoryInfo.isVisible());
holder.checkBox.setChecked(categoryInfo.visible); holder.checkBox.setChecked(categoryInfo.isVisible());
} else { } else {
Toast.makeText(holder.itemView.getContext(), R.string.you_have_to_select_at_least_one_category, Toast.makeText(holder.itemView.getContext(), R.string.you_have_to_select_at_least_one_category,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
@ -110,9 +110,9 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
} }
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) { private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
if (categoryInfo.visible) { if (categoryInfo.isVisible()) {
for (CategoryInfo c : categoryInfos) { for (CategoryInfo c : categoryInfos) {
if (c != categoryInfo && c.visible) { if (c != categoryInfo && c.isVisible()) {
return false; return false;
} }
} }

View File

@ -233,7 +233,7 @@ class HomeAdapter(
fun gridLayoutManager() = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false) fun gridLayoutManager() = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
fun linearLayoutManager() = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) fun linearLayoutManager() = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
override fun onArtist(artistId: Int, imageView: ImageView) { override fun onArtist(artistId: Long, imageView: ImageView) {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment, R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to artistId), bundleOf(EXTRA_ARTIST_ID to artistId),
@ -244,7 +244,7 @@ class HomeAdapter(
) )
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId), bundleOf(EXTRA_ALBUM_ID to albumId),

View File

@ -136,7 +136,7 @@ open class AlbumAdapter(
private fun getSongList(albums: List<Album>): List<Song> { private fun getSongList(albums: List<Album>): List<Song> {
val songs = ArrayList<Song>() val songs = ArrayList<Song>()
for (album in albums) { for (album in albums) {
songs.addAll(album.songs!!) songs.addAll(album.songs)
} }
return songs return songs
} }
@ -170,8 +170,7 @@ open class AlbumAdapter(
if (isInQuickSelectMode) { if (isInQuickSelectMode) {
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
println(dataSet[layoutPosition].id) albumClickListener?.onAlbumClick(dataSet[layoutPosition].id, itemView)
image?.let { albumClickListener?.onAlbumClick(dataSet[layoutPosition].id, it) }
} }
} }

View File

@ -9,7 +9,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.dialogs.LyricsDialog
import code.name.monkey.retromusic.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -91,7 +90,7 @@ class AlbumCoverPagerAdapter(
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false) val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
albumCover = view.findViewById(R.id.player_image) albumCover = view.findViewById(R.id.player_image)
albumCover.setOnClickListener { albumCover.setOnClickListener {
LyricsDialog().show(childFragmentManager, "LyricsDialog") //LyricsDialog().show(childFragmentManager, "LyricsDialog")
showLyricsDialog() showLyricsDialog()
} }
return view return view

View File

@ -113,6 +113,7 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
itemView.setOnLongClickListener(this); itemView.setOnLongClickListener(this);
} }
@Nullable
@Override @Override
public View getSwipeableContainerView() { public View getSwipeableContainerView() {
return null; return null;
@ -129,11 +130,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
} }
public void setImageTransitionName(@NonNull String transitionName) { public void setImageTransitionName(@NonNull String transitionName) {
if (imageContainerCard != null) { itemView.setTransitionName(transitionName);
/* if (imageContainerCard != null) {
imageContainerCard.setTransitionName(transitionName); imageContainerCard.setTransitionName(transitionName);
} }
if (image != null) { if (image != null) {
image.setTransitionName(transitionName); image.setTransitionName(transitionName);
} }*/
} }
} }

View File

@ -6,6 +6,7 @@ import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.R.menu import code.name.monkey.retromusic.R.menu
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.db.toSongs import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.interfaces.CabHolder

View File

@ -13,7 +13,7 @@ interface HistoryDao {
suspend fun insertSongInHistory(historyEntity: HistoryEntity) suspend fun insertSongInHistory(historyEntity: HistoryEntity)
@Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1") @Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1")
suspend fun isSongPresentInHistory(songId: Int): HistoryEntity? suspend fun isSongPresentInHistory(songId: Long): HistoryEntity?
@Update @Update
suspend fun updateHistorySong(historyEntity: HistoryEntity) suspend fun updateHistorySong(historyEntity: HistoryEntity)

View File

@ -7,7 +7,7 @@ import androidx.room.PrimaryKey
@Entity @Entity
class HistoryEntity( class HistoryEntity(
@PrimaryKey @PrimaryKey
val id: Int, val id: Long,
val title: String, val title: String,
@ColumnInfo(name = "track_number") @ColumnInfo(name = "track_number")
val trackNumber: Int, val trackNumber: Int,
@ -17,11 +17,11 @@ class HistoryEntity(
@ColumnInfo(name = "date_modified") @ColumnInfo(name = "date_modified")
val dateModified: Long, val dateModified: Long,
@ColumnInfo(name = "album_id") @ColumnInfo(name = "album_id")
val albumId: Int, val albumId: Long,
@ColumnInfo(name = "album_name") @ColumnInfo(name = "album_name")
val albumName: String, val albumName: String,
@ColumnInfo(name = "artist_id") @ColumnInfo(name = "artist_id")
val artistId: Int, val artistId: Long,
@ColumnInfo(name = "artist_name") @ColumnInfo(name = "artist_name")
val artistName: String, val artistName: String,
val composer: String?, val composer: String?,

View File

@ -14,14 +14,14 @@ interface PlayCountDao {
fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
@Query("SELECT * FROM PlayCountEntity WHERE id =:songId") @Query("SELECT * FROM PlayCountEntity WHERE id =:songId")
fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
@Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC") @Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC")
fun playCountSongs(): List<PlayCountEntity> fun playCountSongs(): List<PlayCountEntity>
@Query("DELETE FROM SongEntity WHERE id =:songId") @Query("DELETE FROM SongEntity WHERE id =:songId")
fun deleteSong(songId: Int) fun deleteSong(songId: Long)
@Query("UPDATE PlayCountEntity SET play_count = play_count + 1 WHERE id = :id") @Query("UPDATE PlayCountEntity SET play_count = play_count + 1 WHERE id = :id")
fun updateQuantity(id: Int) fun updateQuantity(id: Long)
} }

View File

@ -7,7 +7,7 @@ import androidx.room.PrimaryKey
@Entity @Entity
class PlayCountEntity( class PlayCountEntity(
@PrimaryKey @PrimaryKey
val id: Int, val id: Long,
val title: String, val title: String,
@ColumnInfo(name = "track_number") @ColumnInfo(name = "track_number")
val trackNumber: Int, val trackNumber: Int,
@ -17,11 +17,11 @@ class PlayCountEntity(
@ColumnInfo(name = "date_modified") @ColumnInfo(name = "date_modified")
val dateModified: Long, val dateModified: Long,
@ColumnInfo(name = "album_id") @ColumnInfo(name = "album_id")
val albumId: Int, val albumId: Long,
@ColumnInfo(name = "album_name") @ColumnInfo(name = "album_name")
val albumName: String, val albumName: String,
@ColumnInfo(name = "artist_id") @ColumnInfo(name = "artist_id")
val artistId: Int, val artistId: Long,
@ColumnInfo(name = "artist_name") @ColumnInfo(name = "artist_name")
val artistName: String, val artistName: String,
val composer: String?, val composer: String?,

View File

@ -9,7 +9,7 @@ interface PlaylistDao {
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
@Query("UPDATE PlaylistEntity SET playlist_name = :name WHERE playlist_id = :playlistId") @Query("UPDATE PlaylistEntity SET playlist_name = :name WHERE playlist_id = :playlistId")
suspend fun renamePlaylist(playlistId: Int, name: String) suspend fun renamePlaylist(playlistId: Long, name: String)
@Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name") @Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name")
fun isPlaylistExists(name: String): List<PlaylistEntity> fun isPlaylistExists(name: String): List<PlaylistEntity>
@ -18,10 +18,10 @@ interface PlaylistDao {
suspend fun playlists(): List<PlaylistEntity> suspend fun playlists(): List<PlaylistEntity>
@Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId") @Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId")
suspend fun deletePlaylistSongs(playlistId: Int) suspend fun deletePlaylistSongs(playlistId: Long)
@Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId") @Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
suspend fun deleteSongFromPlaylist(playlistId: Int, songId: Int) suspend fun deleteSongFromPlaylist(playlistId: Long, songId: Long)
@Transaction @Transaction
@Query("SELECT * FROM PlaylistEntity") @Query("SELECT * FROM PlaylistEntity")
@ -31,10 +31,10 @@ interface PlaylistDao {
suspend fun insertSongsToPlaylist(songEntities: List<SongEntity>) suspend fun insertSongsToPlaylist(songEntities: List<SongEntity>)
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
suspend fun isSongExistsInPlaylist(playlistId: Int, songId: Int): List<SongEntity> suspend fun isSongExistsInPlaylist(playlistId: Long, songId: Long): List<SongEntity>
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId")
fun songsFromPlaylist(playlistId: Int): LiveData<List<SongEntity>> fun songsFromPlaylist(playlistId: Long): LiveData<List<SongEntity>>
@Delete @Delete
suspend fun deletePlaylist(playlistEntity: PlaylistEntity) suspend fun deletePlaylist(playlistEntity: PlaylistEntity)
@ -47,10 +47,10 @@ interface PlaylistDao {
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
fun favoritesSongsLiveData(playlistId: Int): LiveData<List<SongEntity>> fun favoritesSongsLiveData(playlistId: Long): LiveData<List<SongEntity>>
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
fun favoritesSongs(playlistId: Int): List<SongEntity> fun favoritesSongs(playlistId: Long): List<SongEntity>
} }

View File

@ -9,10 +9,9 @@ import kotlinx.android.parcel.Parcelize
@Entity @Entity
@Parcelize @Parcelize
class PlaylistEntity( class PlaylistEntity(
@ColumnInfo(name = "playlist_name")
val playlistName: String
) : Parcelable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "playlist_id") @ColumnInfo(name = "playlist_id")
var playListId: Int = 0 val playListId: Long = 0,
} @ColumnInfo(name = "playlist_name")
val playlistName: String
) : Parcelable

View File

@ -10,9 +10,12 @@ import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
@Entity(indices = [Index(value = ["playlist_creator_id", "id"], unique = true)]) @Entity(indices = [Index(value = ["playlist_creator_id", "id"], unique = true)])
class SongEntity( class SongEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "song_key")
val songPrimaryKey: Long = 0L,
@ColumnInfo(name = "playlist_creator_id") @ColumnInfo(name = "playlist_creator_id")
val playlistCreatorId: Int, val playlistCreatorId: Long,
val id: Int, val id: Long,
val title: String, val title: String,
@ColumnInfo(name = "track_number") @ColumnInfo(name = "track_number")
val trackNumber: Int, val trackNumber: Int,
@ -22,19 +25,15 @@ class SongEntity(
@ColumnInfo(name = "date_modified") @ColumnInfo(name = "date_modified")
val dateModified: Long, val dateModified: Long,
@ColumnInfo(name = "album_id") @ColumnInfo(name = "album_id")
val albumId: Int, val albumId: Long,
@ColumnInfo(name = "album_name") @ColumnInfo(name = "album_name")
val albumName: String, val albumName: String,
@ColumnInfo(name = "artist_id") @ColumnInfo(name = "artist_id")
val artistId: Int, val artistId: Long,
@ColumnInfo(name = "artist_name") @ColumnInfo(name = "artist_name")
val artistName: String, val artistName: String,
val composer: String?, val composer: String?,
@ColumnInfo(name = "album_artist") @ColumnInfo(name = "album_artist")
val albumArtist: String? val albumArtist: String?
) : Parcelable { ) : Parcelable
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "song_key")
var songPrimaryKey: Long = 0
}

View File

@ -8,83 +8,121 @@ fun List<SongEntity>.toSongs(): List<Song> {
} }
} }
fun List<Song>.toSongs(playlistId: Int): List<SongEntity> { fun List<Song>.toSongs(playlistId: Long): List<SongEntity> {
return map { return map {
it.toSongEntity(playlistId) it.toSongEntity(playlistId)
} }
} }
fun Song.toHistoryEntity(timePlayed: Long): HistoryEntity {
return HistoryEntity(
id = id,
title = title,
trackNumber = trackNumber,
year = year,
duration = duration,
data = data,
dateModified = dateModified,
albumId = albumId,
albumName = albumName,
artistId = artistId,
artistName = artistName,
composer = composer,
albumArtist = albumArtist,
timePlayed = timePlayed
)
}
fun Song.toSongEntity(playListId: Long): SongEntity {
return SongEntity(
playlistCreatorId = playListId,
id = id,
title = title,
trackNumber = trackNumber,
year = year,
duration = duration,
data = data,
dateModified = dateModified,
albumId = albumId,
albumName = albumName,
artistId = artistId,
artistName = artistName,
composer = composer,
albumArtist = albumArtist
)
}
fun SongEntity.toSong(): Song { fun SongEntity.toSong(): Song {
return Song( return Song(
id, id = id,
title, title = title,
trackNumber, trackNumber = trackNumber,
year, year = year,
duration, duration = duration,
data, data = data,
dateModified, dateModified = dateModified,
albumId, albumId = albumId,
albumName, albumName = albumName,
artistId, artistId = artistId,
artistName, artistName = artistName,
composer, composer = composer,
albumArtist albumArtist = albumArtist
) )
} }
fun PlayCountEntity.toSong(): Song { fun PlayCountEntity.toSong(): Song {
return Song( return Song(
id, id = id,
title, title = title,
trackNumber, trackNumber = trackNumber,
year, year = year,
duration, duration = duration,
data, data = data,
dateModified, dateModified = dateModified,
albumId, albumId = albumId,
albumName, albumName = albumName,
artistId, artistId = artistId,
artistName, artistName = artistName,
composer, composer = composer,
albumArtist albumArtist = albumArtist
) )
} }
fun HistoryEntity.toSong(): Song { fun HistoryEntity.toSong(): Song {
return Song( return Song(
id, id = id,
title, title = title,
trackNumber, trackNumber = trackNumber,
year, year = year,
duration, duration = duration,
data, data = data,
dateModified, dateModified = dateModified,
albumId, albumId = albumId,
albumName, albumName = albumName,
artistId, artistId = artistId,
artistName, artistName = artistName,
composer, composer = composer,
albumArtist albumArtist = albumArtist
) )
} }
fun Song.toPlayCount(): PlayCountEntity { fun Song.toPlayCount(): PlayCountEntity {
return PlayCountEntity( return PlayCountEntity(
id, id = id,
title, title = title,
trackNumber, trackNumber = trackNumber,
year, year = year,
duration, duration = duration,
data, data = data,
dateModified, dateModified = dateModified,
albumId, albumId = albumId,
albumName, albumName = albumName,
artistId, artistId = artistId,
artistName, artistName = artistName,
composer, composer = composer,
albumArtist, albumArtist = albumArtist,
System.currentTimeMillis(), timePlayed = System.currentTimeMillis(),
1 playCount = 1
) )
} }

View File

@ -11,6 +11,7 @@ import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.EXTRA_SONG import code.name.monkey.retromusic.EXTRA_SONG
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.colorButtons import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.extra import code.name.monkey.retromusic.extensions.extra
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
@ -56,8 +57,8 @@ class CreatePlaylistDialog : DialogFragment() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
if (libraryViewModel.checkPlaylistExists(playlistName).isEmpty()) { if (libraryViewModel.checkPlaylistExists(playlistName).isEmpty()) {
val playlistId: Long = val playlistId: Long =
libraryViewModel.createPlaylist(PlaylistEntity(playlistName)) libraryViewModel.createPlaylist(PlaylistEntity(playlistName = playlistName))
libraryViewModel.insertSongs(songs.map { it.toSongEntity(playlistId.toInt()) }) libraryViewModel.insertSongs(songs.map { it.toSongEntity(playlistId) })
libraryViewModel.forceReload(Playlists) libraryViewModel.forceReload(Playlists)
} else { } else {
Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT) Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT)

View File

@ -0,0 +1,37 @@
package code.name.monkey.retromusic.extensions
import android.database.Cursor
// exception is rethrown manually in order to have a readable stacktrace
internal fun Cursor.getInt(columnName: String): Int {
try {
return this.getInt(this.getColumnIndex(columnName))
} catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex)
}
}
internal fun Cursor.getLong(columnName: String): Long {
try {
return this.getLong(this.getColumnIndex(columnName))
} catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex)
}
}
internal fun Cursor.getString(columnName: String): String {
try {
return this.getString(this.getColumnIndex(columnName))
} catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex)
}
}
internal fun Cursor.getStringOrNull(columnName: String): String? {
try {
return this.getString(this.getColumnIndex(columnName))
} catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex)
}
}

View File

@ -3,8 +3,11 @@ package code.name.monkey.retromusic.fragments
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -178,11 +181,21 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
GridLayoutManager(requireContext(), 2, GridLayoutManager.VERTICAL, false) GridLayoutManager(requireContext(), 2, GridLayoutManager.VERTICAL, false)
override fun onArtist(artistId: Int, imageView: ImageView) { override fun onArtist(artistId: Long, imageView: ImageView) {
findNavController().navigate(
R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to artistId),
null,
FragmentNavigatorExtras(imageView to getString(R.string.transition_artist_image))
)
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
findNavController().navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId),
null,
FragmentNavigatorExtras(view to getString(R.string.transition_album_art))
)
} }
} }

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.fragments.ReloadType.* import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
@ -75,7 +76,6 @@ class LibraryViewModel(
} }
fun getHome(): LiveData<List<Home>> { fun getHome(): LiveData<List<Home>> {
fetchHomeSections()
return home return home
} }
@ -184,7 +184,7 @@ class LibraryViewModel(
) )
} }
fun renameRoomPlaylist(playListId: Int, name: String) = viewModelScope.launch(IO) { fun renameRoomPlaylist(playListId: Long, name: String) = viewModelScope.launch(IO) {
repository.renameRoomPlaylist(playListId, name) repository.renameRoomPlaylist(playListId, name)
} }
@ -200,8 +200,8 @@ class LibraryViewModel(
repository.deleteRoomPlaylist(playlists) repository.deleteRoomPlaylist(playlists)
} }
suspend fun albumById(id: Int) = repository.albumById(id) suspend fun albumById(id: Long) = repository.albumById(id)
suspend fun artistById(id: Int) = repository.artistById(id) suspend fun artistById(id: Long) = repository.artistById(id)
suspend fun favoritePlaylist() = repository.favoritePlaylist() suspend fun favoritePlaylist() = repository.favoritePlaylist()
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song) suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
suspend fun insertSongs(songs: List<SongEntity>) = repository.insertSongs(songs) suspend fun insertSongs(songs: List<SongEntity>) = repository.insertSongs(songs)
@ -224,9 +224,9 @@ class LibraryViewModel(
} }
repository.insertSongs(songEntities) repository.insertSongs(songEntities)
} else { } else {
val playListId = createPlaylist(PlaylistEntity(playlist.name)) val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name))
val songEntities = playlist.getSongs().map { val songEntities = playlist.getSongs().map {
it.toSongEntity(playListId.toInt()) it.toSongEntity(playListId)
} }
repository.insertSongs(songEntities) repository.insertSongs(songEntities)
} }

View File

@ -10,6 +10,7 @@ import androidx.core.text.HtmlCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
@ -46,6 +47,8 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.transition.platform.MaterialArcMotion
import com.google.android.material.transition.platform.MaterialContainerTransform
import kotlinx.android.synthetic.main.fragment_album_content.* import kotlinx.android.synthetic.main.fragment_album_content.*
import kotlinx.android.synthetic.main.fragment_album_details.* import kotlinx.android.synthetic.main.fragment_album_details.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -70,8 +73,16 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
private val savedSortOrder: String private val savedSortOrder: String
get() = PreferenceUtil.albumDetailSongSortOrder get() = PreferenceUtil.albumDetailSongSortOrder
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply {
duration = 1000L
pathMotion = MaterialArcMotion()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
mainActivity.hideBottomBarVisibility(false) mainActivity.hideBottomBarVisibility(false)
mainActivity.addMusicServiceEventListener(detailsViewModel) mainActivity.addMusicServiceEventListener(detailsViewModel)
@ -79,7 +90,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
toolbar.title = " " toolbar.title = " "
postponeEnterTransition() postponeEnterTransition()
detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer {
println(Thread.currentThread().name)
startPostponedEnterTransition() startPostponedEnterTransition()
showAlbum(it) showAlbum(it)
}) })
@ -92,11 +102,11 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
bundleOf(EXTRA_ARTIST_ID to album.artistId) bundleOf(EXTRA_ARTIST_ID to album.artistId)
) )
} }
playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs, 0, true) }
shuffleAction.setOnClickListener { shuffleAction.setOnClickListener {
MusicPlayerRemote.openAndShuffleQueue( MusicPlayerRemote.openAndShuffleQueue(
album.songs!!, album.songs,
true true
) )
} }
@ -135,7 +145,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
} }
private fun showAlbum(album: Album) { private fun showAlbum(album: Album) {
if (album.songs!!.isEmpty()) { if (album.songs.isEmpty()) {
return return
} }
this.album = album this.album = album
@ -255,10 +265,12 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
playAction.applyOutlineColor(color) playAction.applyOutlineColor(color)
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
findNavController().navigate( findNavController().navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId) bundleOf(EXTRA_ALBUM_ID to albumId),
null,
FragmentNavigatorExtras(view to getString(R.string.transition_album_art))
) )
} }
@ -350,29 +362,31 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
private fun setSaveSortOrder(sortOrder: String) { private fun setSaveSortOrder(sortOrder: String) {
PreferenceUtil.albumDetailSongSortOrder = sortOrder PreferenceUtil.albumDetailSongSortOrder = sortOrder
when (sortOrder) { val songs = when (sortOrder) {
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs.sortedWith { o1, o2 ->
o1.trackNumber.compareTo( o1.trackNumber.compareTo(
o2.trackNumber o2.trackNumber
) )
}) }
SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs.sortedWith { o1, o2 ->
o1.title.compareTo( o1.title.compareTo(
o2.title o2.title
) )
}) }
SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs.sortedWith { o1, o2 ->
o2.title.compareTo( o2.title.compareTo(
o1.title o1.title
) )
}) }
SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs.sortedWith { o1, o2 ->
o1.duration.compareTo( o1.duration.compareTo(
o2.duration o2.duration
) )
}) }
else -> throw IllegalArgumentException("invalid $sortOrder")
} }
album.songs?.let { simpleSongAdapter.swapDataSet(it) } album = album.copy(songs = songs)
simpleSongAdapter.swapDataSet(album.songs)
} }
companion object { companion object {

View File

@ -13,15 +13,14 @@ import kotlinx.coroutines.Dispatchers.IO
class AlbumDetailsViewModel( class AlbumDetailsViewModel(
private val repository: RealRepository, private val repository: RealRepository,
private val albumId: Int private val albumId: Long
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
fun getAlbum(): LiveData<Album> = liveData(IO) { fun getAlbum(): LiveData<Album> = liveData(IO) {
val album = repository.albumByIdAsync(albumId) emit(repository.albumByIdAsync(albumId))
emit(album)
} }
fun getArtist(artistId: Int): LiveData<Artist> = liveData(IO) { fun getArtist(artistId: Long): LiveData<Artist> = liveData(IO) {
val artist = repository.artistById(artistId) val artist = repository.artistById(artistId)
emit(artist) emit(artist)
} }
@ -32,7 +31,7 @@ class AlbumDetailsViewModel(
} }
fun getMoreAlbums(artist: Artist): LiveData<List<Album>> = liveData(IO) { fun getMoreAlbums(artist: Artist): LiveData<List<Album>> = liveData(IO) {
artist.albums?.filter { item -> item.id != albumId }?.let { albums -> artist.albums.filter { item -> item.id != albumId }.let { albums ->
if (albums.isNotEmpty()) emit(albums) if (albums.isNotEmpty()) emit(albums)
} }
} }

View File

@ -16,11 +16,17 @@ import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(), class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
AlbumClickListener { AlbumClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.getAlbums().observe(viewLifecycleOwner, Observer { libraryViewModel.getAlbums().observe(viewLifecycleOwner, Observer {
@ -43,7 +49,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
return AlbumAdapter( return AlbumAdapter(
requireActivity(), requireActivity(),
dataSet, dataSet,
R.layout.item_grid, itemLayoutRes(),
null, null,
this this
) )
@ -97,7 +103,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
} }
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
findActivityNavController(R.id.fragment_container).navigate( findActivityNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId), bundleOf(EXTRA_ALBUM_ID to albumId),
@ -285,5 +291,5 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
} }
interface AlbumClickListener { interface AlbumClickListener {
fun onAlbumClick(albumId: Int, view: View) fun onAlbumClick(albumId: Long, view: View)
} }

View File

@ -17,7 +17,6 @@ import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
@ -38,7 +37,6 @@ import code.name.monkey.retromusic.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.CustomArtistImageUtil
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.fragment_artist_content.* import kotlinx.android.synthetic.main.fragment_artist_content.*
@ -199,14 +197,12 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
} }
private fun setColors(color: Int) { private fun setColors(color: Int) {
val finalColor = if (PreferenceUtil.isAdaptiveColor) color shuffleAction.applyColor(color)
else ThemeStore.accentColor(requireContext()) playAction.applyOutlineColor(color)
shuffleAction.applyColor(finalColor)
playAction.applyOutlineColor(finalColor)
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
findNavController().navigate( findNavController().navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId), bundleOf(EXTRA_ALBUM_ID to albumId),

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.Dispatchers.IO
class ArtistDetailsViewModel( class ArtistDetailsViewModel(
private val realRepository: RealRepository, private val realRepository: RealRepository,
private val artistId: Int private val artistId: Long
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
fun getArtist(): LiveData<Artist> = liveData(IO) { fun getArtist(): LiveData<Artist> = liveData(IO) {

View File

@ -15,10 +15,15 @@ import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeF
import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(), class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
ArtistClickListener { ArtistClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -46,7 +51,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
return ArtistAdapter( return ArtistAdapter(
requireActivity(), requireActivity(),
dataSet, dataSet,
R.layout.item_grid_circle, itemLayoutRes(),
null, null,
this this
) )
@ -96,7 +101,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
} }
} }
override fun onArtist(artistId: Int, imageView: ImageView) { override fun onArtist(artistId: Long, imageView: ImageView) {
val controller = findActivityNavController(R.id.fragment_container) val controller = findActivityNavController(R.id.fragment_container)
controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId)) controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId))
} }
@ -258,5 +263,5 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
} }
interface ArtistClickListener { interface ArtistClickListener {
fun onArtist(artistId: Int, imageView: ImageView) fun onArtist(artistId: Long, imageView: ImageView)
} }

View File

@ -23,6 +23,7 @@ import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.extensions.whichFragment

View File

@ -21,8 +21,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.GenreAdapter import code.name.monkey.retromusic.adapter.GenreAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import com.google.android.material.transition.platform.MaterialFadeThrough
class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>() { class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -36,6 +36,7 @@ import code.name.monkey.retromusic.glide.UserProfileGlideRequest
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.transition.platform.MaterialFadeThrough
import kotlinx.android.synthetic.main.abs_playlists.* import kotlinx.android.synthetic.main.abs_playlists.*
import kotlinx.android.synthetic.main.fragment_banner_home.* import kotlinx.android.synthetic.main.fragment_banner_home.*
import kotlinx.android.synthetic.main.home_content.* import kotlinx.android.synthetic.main.home_content.*
@ -43,6 +44,10 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class HomeFragment : class HomeFragment :
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) { AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
private val libraryViewModel: LibraryViewModel by sharedViewModel() private val libraryViewModel: LibraryViewModel by sharedViewModel()

View File

@ -14,12 +14,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
@ -36,7 +40,7 @@ import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.util.ViewUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior.*
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
@ -45,6 +49,9 @@ import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
import kotlinx.android.synthetic.main.fragment_gradient_controls.* import kotlinx.android.synthetic.main.fragment_gradient_controls.*
import kotlinx.android.synthetic.main.fragment_gradient_player.* import kotlinx.android.synthetic.main.fragment_gradient_player.*
import kotlinx.android.synthetic.main.status_bar.* import kotlinx.android.synthetic.main.status_bar.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player), class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player),
MusicProgressViewUpdateHelper.Callback, MusicProgressViewUpdateHelper.Callback,
@ -61,7 +68,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private var playingQueueAdapter: PlayingQueueAdapter? = null private var playingQueueAdapter: PlayingQueueAdapter? = null
private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var linearLayoutManager: LinearLayoutManager
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() { private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { override fun onSlide(bottomSheet: View, slideOffset: Float) {
mainActivity.getBottomSheetBehavior().setAllowDragging(false) mainActivity.getBottomSheetBehavior().setAllowDragging(false)
playerQueueSheet.setPadding( playerQueueSheet.setPadding(
@ -74,11 +81,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) { when (newState) {
BottomSheetBehavior.STATE_EXPANDED, STATE_EXPANDED,
BottomSheetBehavior.STATE_DRAGGING -> { STATE_DRAGGING -> {
mainActivity.getBottomSheetBehavior().setAllowDragging(false) mainActivity.getBottomSheetBehavior().setAllowDragging(false)
} }
BottomSheetBehavior.STATE_COLLAPSED -> { STATE_COLLAPSED -> {
resetToCurrentPosition() resetToCurrentPosition()
mainActivity.getBottomSheetBehavior().setAllowDragging(true) mainActivity.getBottomSheetBehavior().setAllowDragging(true)
} }
@ -167,9 +174,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
var wasExpanded = false var wasExpanded = false
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) { if (getQueuePanel().state == STATE_EXPANDED) {
wasExpanded = getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED wasExpanded = getQueuePanel().state == STATE_EXPANDED
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED getQueuePanel().state = STATE_COLLAPSED
return wasExpanded return wasExpanded
} }
return wasExpanded return wasExpanded
@ -216,7 +223,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun toggleFavorite(song: Song) { override fun toggleFavorite(song: Song) {
super.toggleFavorite(song) super.toggleFavorite(song)
if (song.id == MusicPlayerRemote.currentSong.id) { if (song.id == MusicPlayerRemote.currentSong.id) {
updateIsFavorite() updateIsFavoriteIcon()
} }
} }
@ -224,6 +231,23 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
toggleFavorite(MusicPlayerRemote.currentSong) toggleFavorite(MusicPlayerRemote.currentSong)
} }
private fun updateIsFavoriteIcon() {
lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
if (playlist != null) {
val song: SongEntity =
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
withContext(Dispatchers.Main) {
val icon =
if (isFavorite) R.drawable.ic_favorite
else R.drawable.ic_favorite_border
songFavourite.setImageResource(icon)
}
}
}
}
private fun hideVolumeIfAvailable() { private fun hideVolumeIfAvailable() {
if (PreferenceUtil.isVolumeVisibilityMode) { if (PreferenceUtil.isVolumeVisibilityMode) {
childFragmentManager.beginTransaction() childFragmentManager.beginTransaction()
@ -241,6 +265,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateQueue() updateQueue()
updateIsFavoriteIcon()
} }
override fun onPlayStateChanged() { override fun onPlayStateChanged() {
@ -259,6 +284,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
super.onPlayingMetaChanged() super.onPlayingMetaChanged()
updateSong() updateSong()
updateQueuePosition() updateQueuePosition()
updateIsFavoriteIcon()
} }
override fun onQueueChanged() { override fun onQueueChanged() {
@ -359,13 +385,10 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateLabel() { private fun updateLabel() {
(MusicPlayerRemote.playingQueue.size - 1).apply { (MusicPlayerRemote.playingQueue.size - 1).apply {
if (this == (MusicPlayerRemote.position)) { if (this == (MusicPlayerRemote.position)) {
nextSong.hide() nextSong.text = "Last song"
} else { } else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
nextSong.apply { nextSong.text = title
text = title
show()
}
} }
} }
} }
@ -460,12 +483,10 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onUpdateProgressViews(progress: Int, total: Int) { override fun onUpdateProgressViews(progress: Int, total: Int) {
progressSlider.max = total progressSlider.max = total
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator() animator.interpolator = LinearInterpolator()
animator.start() animator.start()
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
} }

View File

@ -1,72 +0,0 @@
package code.name.monkey.retromusic.fragments.playlists
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.LegacyPlaylistAdapter
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.model.Playlist
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
class ImportPlaylistFragment :
AbsRecyclerViewFragment<LegacyPlaylistAdapter, LinearLayoutManager>(),
LegacyPlaylistAdapter.PlaylistClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
libraryViewModel.getLegacyPlaylist().observe(viewLifecycleOwner, Observer {
if (it.isNotEmpty())
adapter?.swapData(it)
else
adapter?.swapData(listOf())
})
}
override fun createLayoutManager(): LinearLayoutManager {
return LinearLayoutManager(requireContext())
}
override fun createAdapter(): LegacyPlaylistAdapter {
return LegacyPlaylistAdapter(
requireActivity(),
ArrayList(),
R.layout.item_list_no_image,
this
)
}
override fun onPlaylistClick(playlist: Playlist) {
Toast.makeText(requireContext(), "Importing ${playlist.name}", Toast.LENGTH_LONG).show()
lifecycleScope.launch(IO) {
if (playlist.name.isNotEmpty()) {
if (libraryViewModel.checkPlaylistExists(playlist.name).isEmpty()) {
val playlistId: Long =
libraryViewModel.createPlaylist(PlaylistEntity(playlist.name))
libraryViewModel.insertSongs(playlist.getSongs().map {
it.toSongEntity(playlistId.toInt())
})
libraryViewModel.forceReload(Playlists)
} else {
Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT)
.show()
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.removeItem(R.id.action_grid_size)
menu.removeItem(R.id.action_layout_type)
menu.removeItem(R.id.action_sort_order)
super.onCreateOptionsMenu(menu, inflater)
}
}

View File

@ -11,9 +11,14 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import com.google.android.material.transition.platform.MaterialFadeThrough
import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library.*
class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, LinearLayoutManager>() { class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, LinearLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -15,6 +15,6 @@ class SearchViewModel(private val realRepository: RealRepository) : ViewModel()
fun search(query: String?) = viewModelScope.launch(IO) { fun search(query: String?) = viewModelScope.launch(IO) {
val result = realRepository.search(query) val result = realRepository.search(query)
results.value = result results.postValue(result)
} }
} }

View File

@ -12,10 +12,14 @@ import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeF
import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>() { class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -7,8 +7,8 @@ import org.koin.core.KoinComponent
import org.koin.core.inject import org.koin.core.inject
abstract class AbsCustomPlaylist( abstract class AbsCustomPlaylist(
id: Int = -1, id: Long,
name: String = "" name: String
) : Playlist(id, name), KoinComponent { ) : Playlist(id, name), KoinComponent {
abstract fun songs(): List<Song> abstract fun songs(): List<Song>

View File

@ -14,19 +14,15 @@
package code.name.monkey.retromusic.model package code.name.monkey.retromusic.model
import java.util.* data class Album(
val id: Long,
class Album { val songs: List<Song>
) {
val songs: ArrayList<Song>?
val id: Int
get() = safeGetFirstSong().albumId
val title: String? val title: String?
get() = safeGetFirstSong().albumName get() = safeGetFirstSong().albumName
val artistId: Int val artistId: Long
get() = safeGetFirstSong().artistId get() = safeGetFirstSong().artistId
val artistName: String? val artistName: String?
@ -39,20 +35,17 @@ class Album {
get() = safeGetFirstSong().dateModified get() = safeGetFirstSong().dateModified
val songCount: Int val songCount: Int
get() = songs!!.size get() = songs.size
val albumArtist: String? val albumArtist: String?
get() = safeGetFirstSong().albumArtist get() = safeGetFirstSong().albumArtist
constructor(songs: ArrayList<Song>) {
this.songs = songs
}
constructor() {
this.songs = ArrayList()
}
fun safeGetFirstSong(): Song { fun safeGetFirstSong(): Song {
return if (songs!!.isEmpty()) Song.emptySong else songs[0] return songs.firstOrNull() ?: Song.emptySong
} }
companion object {
val empty = Album(-1, emptyList())
}
} }

View File

@ -15,17 +15,20 @@
package code.name.monkey.retromusic.model package code.name.monkey.retromusic.model
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import java.util.* import java.util.*
class Artist { data class Artist(
val albums: ArrayList<Album>? val id: Long,
val albums: List<Album>
val id: Int ) {
get() = safeGetFirstAlbum().artistId
val name: String val name: String
get() { get() {
val name = safeGetFirstAlbum().safeGetFirstSong().albumArtist val name = safeGetFirstAlbum().safeGetFirstSong().albumArtist
if (PreferenceUtil.albumArtistsOnly && MusicUtil.isVariousArtists(name)) {
return VARIOUS_ARTISTS_DISPLAY_NAME
}
return if (MusicUtil.isArtistNameUnknown(name)) { return if (MusicUtil.isArtistNameUnknown(name)) {
UNKNOWN_ARTIST_DISPLAY_NAME UNKNOWN_ARTIST_DISPLAY_NAME
} else safeGetFirstAlbum().safeGetFirstSong().artistName } else safeGetFirstAlbum().safeGetFirstSong().artistName
@ -34,37 +37,27 @@ class Artist {
val songCount: Int val songCount: Int
get() { get() {
var songCount = 0 var songCount = 0
for (album in albums!!) { for (album in albums) {
songCount += album.songCount songCount += album.songCount
} }
return songCount return songCount
} }
val albumCount: Int val albumCount: Int
get() = albums!!.size get() = albums.size
val songs: ArrayList<Song> val songs: List<Song>
get() { get() = albums.flatMap { it.songs }
val songs = ArrayList<Song>()
for (album in albums!!) {
songs.addAll(album.songs!!)
}
return songs
}
constructor(albums: ArrayList<Album>) {
this.albums = albums
}
constructor() {
this.albums = ArrayList()
}
fun safeGetFirstAlbum(): Album { fun safeGetFirstAlbum(): Album {
return if (albums!!.isEmpty()) Album() else albums[0] return albums.firstOrNull() ?: Album.empty
} }
companion object { companion object {
const val UNKNOWN_ARTIST_DISPLAY_NAME = "Unknown Artist" const val UNKNOWN_ARTIST_DISPLAY_NAME = "Unknown Artist"
const val VARIOUS_ARTISTS_DISPLAY_NAME = "Various Artists"
const val VARIOUS_ARTISTS_ID : Long = -2
val empty = Artist(-1, emptyList())
} }
} }

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.model;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import code.name.monkey.retromusic.R;
public class CategoryInfo implements Parcelable {
public static final Creator<CategoryInfo> CREATOR = new Creator<CategoryInfo>() {
public CategoryInfo createFromParcel(Parcel source) {
return new CategoryInfo(source);
}
public CategoryInfo[] newArray(int size) {
return new CategoryInfo[size];
}
};
public Category category;
public boolean visible;
public CategoryInfo(Category category, boolean visible) {
this.category = category;
this.visible = visible;
}
private CategoryInfo(Parcel source) {
category = (Category) source.readSerializable();
visible = source.readInt() == 1;
}
@Override
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(category);
dest.writeInt(visible ? 1 : 0);
}
public enum Category {
Home(R.id.action_home, R.string.for_you, R.drawable.ic_face),
Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack),
Albums(R.id.action_album, R.string.albums, R.drawable.ic_album),
Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist),
Playlists(R.id.action_playlist, R.string.playlists, (R.drawable.ic_queue_music)),
Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar),
Folder(R.id.action_folder, R.string.folders, R.drawable.ic_folder);
public final int icon;
public final int id;
public final int stringRes;
Category(int id, @StringRes int stringRes, @DrawableRes int icon) {
this.stringRes = stringRes;
this.id = id;
this.icon = icon;
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.model
import android.os.Parcelable
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import code.name.monkey.retromusic.R
import kotlinx.android.parcel.Parcelize
@Parcelize
data class CategoryInfo(
val category: Category,
@get:JvmName("isVisible")
var visible: Boolean
) : Parcelable {
enum class Category(
val id: Int,
@StringRes val stringRes: Int,
@DrawableRes val icon: Int
) {
Home(R.id.action_home, R.string.for_you, R.drawable.ic_face),
Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack),
Albums(R.id.action_album, R.string.albums, R.drawable.ic_album),
Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist),
Playlists(R.id.action_playlist, R.string.playlists, R.drawable.ic_queue_music),
Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar),
Folder(R.id.action_folder, R.string.folders, R.drawable.ic_folder);
}
}

View File

@ -16,8 +16,10 @@ package code.name.monkey.retromusic.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
class Contributor( data class Contributor(
val name: String, val name: String,
val summary: String, val summary: String,
val link: String, @SerializedName("profile_image") val profileImage: String val link: String,
@SerializedName("profile_image")
val profileImage: String
) )

View File

@ -18,4 +18,8 @@ import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
data class Genre(val id: Int = -1, val name: String, val songCount: Int) : Parcelable data class Genre(
val id: Long,
val name: String,
val songCount: Int
) : Parcelable

View File

@ -17,7 +17,7 @@ package code.name.monkey.retromusic.model
import androidx.annotation.StringRes import androidx.annotation.StringRes
import code.name.monkey.retromusic.HomeSection import code.name.monkey.retromusic.HomeSection
class Home( data class Home(
val arrayList: List<Any>, val arrayList: List<Any>,
@HomeSection @HomeSection
val homeSection: Int, val homeSection: Int,

View File

@ -10,10 +10,14 @@ import org.koin.core.get
@Parcelize @Parcelize
open class Playlist( open class Playlist(
val id: Int = -1, val id: Long,
val name: String = "" val name: String
) : Parcelable, KoinComponent { ) : Parcelable, KoinComponent {
companion object {
val empty = Playlist(-1, "")
}
// this default implementation covers static playlists // this default implementation covers static playlists
fun getSongs(): List<Song> { fun getSongs(): List<Song> {
return RealPlaylistRepository(get()).playlistSongs(id) return RealPlaylistRepository(get()).playlistSongs(id)
@ -27,4 +31,24 @@ open class Playlist(
"" ""
) )
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Playlist
if (id != other.id) return false
if (name != other.name) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
return result
}
} }

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.model;
import org.jetbrains.annotations.NotNull;
import kotlinx.android.parcel.Parcelize;
/**
* Created by hemanths on 3/4/19
*/
@Parcelize
public class PlaylistSong extends Song {
final int idInPlayList;
final int playlistId;
public PlaylistSong(int id,
@NotNull String title,
int trackNumber,
int year,
long duration,
@NotNull String data,
long dateModified,
int albumId,
@NotNull String albumName,
int artistId,
@NotNull String artistName,
int playlistId,
int idInPlayList,
@NotNull String composer,
String albumArtist) {
super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, composer, albumArtist);
this.playlistId = playlistId;
this.idInPlayList = idInPlayList;
}
public int getIdInPlayList() {
return idInPlayList;
}
public int getPlaylistId() {
return playlistId;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.model
import kotlinx.android.parcel.Parcelize
/**
* Created by hemanths on 3/4/19
*/
@Parcelize
class PlaylistSong(
override val id: Long,
override val title: String,
override val trackNumber: Int,
override val year: Int,
override val duration: Long,
override val data: String,
override val dateModified: Long,
override val albumId: Long,
override val albumName: String,
override val artistId: Long,
override val artistName: String,
val playlistId: Long,
val idInPlayList: Long,
override val composer: String,
override val albumArtist: String?
) : Song(
id = id,
title = title,
trackNumber = trackNumber,
year = year,
duration = duration,
data = data,
dateModified = dateModified,
albumId = albumId,
albumName = albumName,
artistId = artistId,
artistName = artistName,
composer = composer,
albumArtist = albumArtist
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
other as PlaylistSong
if (id != other.id) return false
if (title != other.title) return false
if (trackNumber != other.trackNumber) return false
if (year != other.year) return false
if (duration != other.duration) return false
if (data != other.data) return false
if (dateModified != other.dateModified) return false
if (albumId != other.albumId) return false
if (albumName != other.albumName) return false
if (artistId != other.artistId) return false
if (artistName != other.artistName) return false
if (playlistId != other.playlistId) return false
if (idInPlayList != other.idInPlayList) return false
if (composer != other.composer) return false
if (albumArtist != other.albumArtist) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + id.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + trackNumber
result = 31 * result + year
result = 31 * result + duration.hashCode()
result = 31 * result + data.hashCode()
result = 31 * result + dateModified.hashCode()
result = 31 * result + albumId.hashCode()
result = 31 * result + albumName.hashCode()
result = 31 * result + artistId.hashCode()
result = 31 * result + artistName.hashCode()
result = 31 * result + playlistId.hashCode()
result = 31 * result + idInPlayList.hashCode()
result = 31 * result + composer.hashCode()
result = 31 * result + (albumArtist?.hashCode() ?: 0)
return result
}
}

View File

@ -18,58 +18,64 @@ import code.name.monkey.retromusic.db.HistoryEntity
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
// update equals and hashcode if fields changes
@Parcelize @Parcelize
open class Song( open class Song(
val id: Int, open val id: Long,
val title: String, open val title: String,
val trackNumber: Int, open val trackNumber: Int,
val year: Int, open val year: Int,
val duration: Long, open val duration: Long,
val data: String, open val data: String,
val dateModified: Long, open val dateModified: Long,
val albumId: Int, open val albumId: Long,
val albumName: String, open val albumName: String,
val artistId: Int, open val artistId: Long,
val artistName: String, open val artistName: String,
val composer: String?, open val composer: String?,
val albumArtist: String? open val albumArtist: String?
) : Parcelable { ) : Parcelable {
fun toHistoryEntity(timePlayed: Long): HistoryEntity {
return HistoryEntity(
id, // need to override manually because is open and cannot be a data class
title, override fun equals(other: Any?): Boolean {
trackNumber, if (this === other) return true
year, if (javaClass != other?.javaClass) return false
duration,
data, other as Song
dateModified,
albumId, if (id != other.id) return false
albumName, if (title != other.title) return false
artistId, if (trackNumber != other.trackNumber) return false
artistName, if (year != other.year) return false
composer, if (duration != other.duration) return false
albumArtist, if (data != other.data) return false
timePlayed if (dateModified != other.dateModified) return false
) if (albumId != other.albumId) return false
if (albumName != other.albumName) return false
if (artistId != other.artistId) return false
if (artistName != other.artistName) return false
if (composer != other.composer) return false
if (albumArtist != other.albumArtist) return false
return true
} }
fun toSongEntity(playListId: Int): SongEntity { override fun hashCode(): Int {
return SongEntity( var result = id.hashCode()
playListId, result = 31 * result + title.hashCode()
id, result = 31 * result + trackNumber
title, result = 31 * result + year
trackNumber, result = 31 * result + duration.hashCode()
year, result = 31 * result + data.hashCode()
duration, result = 31 * result + dateModified.hashCode()
data, result = 31 * result + albumId.hashCode()
dateModified, result = 31 * result + albumName.hashCode()
albumId, result = 31 * result + artistId.hashCode()
albumName, result = 31 * result + artistName.hashCode()
artistId, result = 31 * result + (composer?.hashCode() ?: 0)
artistName, result = 31 * result + (albumArtist?.hashCode() ?: 0)
composer, return result
albumArtist
)
} }
@ -77,19 +83,19 @@ open class Song(
@JvmStatic @JvmStatic
val emptySong = Song( val emptySong = Song(
-1, id = -1,
"", title = "",
-1, trackNumber = -1,
-1, year = -1,
-1, duration = -1,
"", data = "",
-1, dateModified = -1,
-1, albumId = -1,
"", albumName = "",
-1, artistId = -1,
"", artistName = "",
"", composer = "",
"" albumArtist = ""
) )
} }
} }

View File

@ -5,6 +5,9 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.AbsCustomPlaylist
abstract class AbsSmartPlaylist( abstract class AbsSmartPlaylist(
name: String = "", name: String,
@DrawableRes val iconRes: Int = R.drawable.ic_queue_music @DrawableRes val iconRes: Int = R.drawable.ic_queue_music
) : AbsCustomPlaylist(-Math.abs(31 * name.hashCode() + iconRes * name.hashCode() * 31 * 31), name) ) : AbsCustomPlaylist(
id = PlaylistIdGenerator(name, iconRes),
name = name
)

View File

@ -7,12 +7,11 @@ import kotlinx.android.parcel.Parcelize
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
@Parcelize @Parcelize
class HistoryPlaylist : class HistoryPlaylist : AbsSmartPlaylist(
AbsSmartPlaylist( name = App.getContext().getString(R.string.history),
App.getContext().getString(R.string.history), iconRes = R.drawable.ic_history
R.drawable.ic_history ), KoinComponent {
),
KoinComponent {
override fun songs(): List<Song> { override fun songs(): List<Song> {
return topPlayedRepository.recentlyPlayedTracks() return topPlayedRepository.recentlyPlayedTracks()
} }

View File

@ -6,8 +6,10 @@ import code.name.monkey.retromusic.model.Song
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
class LastAddedPlaylist : class LastAddedPlaylist : AbsSmartPlaylist(
AbsSmartPlaylist(App.getContext().getString(R.string.last_added), R.drawable.ic_library_add) { name = App.getContext().getString(R.string.last_added),
iconRes = R.drawable.ic_library_add
) {
override fun songs(): List<Song> { override fun songs(): List<Song> {
return lastAddedRepository.recentSongs() return lastAddedRepository.recentSongs()
} }

View File

@ -7,8 +7,8 @@ import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
class NotPlayedPlaylist : AbsSmartPlaylist( class NotPlayedPlaylist : AbsSmartPlaylist(
App.getContext().getString(R.string.not_recently_played), name = App.getContext().getString(R.string.not_recently_played),
R.drawable.ic_watch_later iconRes = R.drawable.ic_watch_later
) { ) {
override fun songs(): List<Song> { override fun songs(): List<Song> {
return topPlayedRepository.notRecentlyPlayedTracks() return topPlayedRepository.notRecentlyPlayedTracks()

View File

@ -0,0 +1,12 @@
package code.name.monkey.retromusic.model.smartplaylist
import androidx.annotation.DrawableRes
import kotlin.math.abs
object PlaylistIdGenerator {
operator fun invoke(name: String, @DrawableRes iconRes: Int): Long {
return -abs(31L * name.hashCode() + iconRes * name.hashCode() * 31L * 31L)
}
}

View File

@ -7,8 +7,8 @@ import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
class ShuffleAllPlaylist : AbsSmartPlaylist( class ShuffleAllPlaylist : AbsSmartPlaylist(
App.getContext().getString(R.string.action_shuffle_all), name = App.getContext().getString(R.string.action_shuffle_all),
R.drawable.ic_shuffle iconRes = R.drawable.ic_shuffle
) { ) {
override fun songs(): List<Song> { override fun songs(): List<Song> {
return songRepository.songs() return songRepository.songs()

View File

@ -7,8 +7,8 @@ import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
class TopTracksPlaylist : AbsSmartPlaylist( class TopTracksPlaylist : AbsSmartPlaylist(
App.getContext().getString(R.string.my_top_tracks), name = App.getContext().getString(R.string.my_top_tracks),
R.drawable.ic_trending_up iconRes = R.drawable.ic_trending_up
) { ) {
override fun songs(): List<Song> { override fun songs(): List<Song> {
return topPlayedRepository.topTracks() return topPlayedRepository.topTracks()

View File

@ -31,7 +31,7 @@ interface AlbumRepository {
fun albums(query: String): List<Album> fun albums(query: String): List<Album>
fun album(albumId: Int): Album fun album(albumId: Long): Album
} }
class RealAlbumRepository(private val songRepository: RealSongRepository) : class RealAlbumRepository(private val songRepository: RealSongRepository) :
@ -59,70 +59,42 @@ class RealAlbumRepository(private val songRepository: RealSongRepository) :
return splitIntoAlbums(songs) return splitIntoAlbums(songs)
} }
override fun album(albumId: Int): Album { override fun album(albumId: Long): Album {
val cursor = songRepository.makeSongCursor( val cursor = songRepository.makeSongCursor(
AudioColumns.ALBUM_ID + "=?", AudioColumns.ALBUM_ID + "=?",
arrayOf(albumId.toString()), arrayOf(albumId.toString()),
getSongLoaderSortOrder() getSongLoaderSortOrder()
) )
val songs = songRepository.songs(cursor) val songs = songRepository.songs(cursor)
val album = Album(ArrayList(songs)) val album = Album(albumId, songs)
sortAlbumSongs(album) sortAlbumSongs(album)
return album return album
} }
fun splitIntoAlbums( fun splitIntoAlbums(
songs: List<Song>? songs: List<Song>
): List<Album> { ): List<Album> {
val albums = ArrayList<Album>() return songs.groupBy { it.albumId }
if (songs != null) { .map { sortAlbumSongs(Album(it.key, it.value)) }
for (song in songs) {
getOrCreateAlbum(albums, song.albumId).songs?.add(song)
}
}
for (album in albums) {
sortAlbumSongs(album)
}
return albums
} }
private fun getOrCreateAlbum( private fun sortAlbumSongs(album: Album): Album {
albums: ArrayList<Album>, val songs = when (PreferenceUtil.albumDetailSongSortOrder) {
albumId: Int SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs.sortedWith { o1, o2 ->
): Album { o1.trackNumber.compareTo(o2.trackNumber)
for (album in albums) {
if (album.songs!!.isNotEmpty() && album.songs[0].albumId == albumId) {
return album
} }
SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs.sortedWith { o1, o2 ->
o1.title.compareTo(o2.title)
}
SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs.sortedWith { o1, o2 ->
o2.title.compareTo(o1.title)
}
SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs.sortedWith { o1, o2 ->
o1.duration.compareTo(o2.duration)
}
else -> throw IllegalArgumentException("invalid ${PreferenceUtil.albumDetailSongSortOrder}")
} }
val album = Album() return album.copy(songs = songs)
albums.add(album)
return album
}
private fun sortAlbumSongs(album: Album) {
when (PreferenceUtil.albumDetailSongSortOrder) {
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.trackNumber.compareTo(
o2.trackNumber
)
})
SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.title.compareTo(
o2.title
)
})
SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 ->
o2.title.compareTo(
o1.title
)
})
SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.duration.compareTo(
o2.duration
)
})
}
} }
private fun getSongLoaderSortOrder(): String { private fun getSongLoaderSortOrder(): String {

View File

@ -26,7 +26,7 @@ interface ArtistRepository {
fun artists(query: String): List<Artist> fun artists(query: String): List<Artist>
fun artist(artistId: Int): Artist fun artist(artistId: Long): Artist
} }
class RealArtistRepository( class RealArtistRepository(
@ -39,7 +39,29 @@ class RealArtistRepository(
PreferenceUtil.artistAlbumSortOrder + ", " + PreferenceUtil.artistAlbumSortOrder + ", " +
PreferenceUtil.artistSongSortOrder PreferenceUtil.artistSongSortOrder
} }
override fun artist(artistId: Long): Artist {
if (artistId == Artist.VARIOUS_ARTISTS_ID) {
// Get Various Artists
val songs = songRepository.songs(
songRepository.makeSongCursor(
null,
null,
getSongLoaderSortOrder()
)
)
val albums = albumRepository.splitIntoAlbums(songs).filter { it.albumArtist == Artist.VARIOUS_ARTISTS_DISPLAY_NAME }
return Artist(Artist.VARIOUS_ARTISTS_ID, albums)
}
val songs = songRepository.songs(
songRepository.makeSongCursor(
AudioColumns.ARTIST_ID + "=?",
arrayOf(artistId.toString()),
getSongLoaderSortOrder()
)
)
return Artist(artistId, albumRepository.splitIntoAlbums(songs))
}
override fun artists(): List<Artist> { override fun artists(): List<Artist> {
val songs = songRepository.songs( val songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
@ -50,17 +72,6 @@ class RealArtistRepository(
return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
} }
override fun artists(query: String): List<Artist> {
val songs = songRepository.songs(
songRepository.makeSongCursor(
AudioColumns.ARTIST + " LIKE ?",
arrayOf("%$query%"),
getSongLoaderSortOrder()
)
)
return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
}
override fun albumArtists(): List<Artist> { override fun albumArtists(): List<Artist> {
val songs = songRepository.songs( val songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
@ -70,54 +81,41 @@ class RealArtistRepository(
) )
) )
val sortString = if (PreferenceUtil.artistSortOrder.contains("DESC")) String.CASE_INSENSITIVE_ORDER.reversed() else String.CASE_INSENSITIVE_ORDER return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs))
return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs)).sortedWith(compareBy(sortString) { it.name })
} }
private fun splitIntoAlbumArtists(albums: List<Album>): List<Artist> { override fun artists(query: String): List<Artist> {
// First group the songs in albums by filtering each artist name
val amap = hashMapOf<String, Artist>()
albums.forEach {
val key = it.albumArtist
if (key != null) {
val artist: Artist = if (amap[key] != null) amap[key]!! else Artist()
artist.albums?.add(it)
amap[key] = artist
}
}
return ArrayList(amap.values)
}
override fun artist(artistId: Int): Artist {
val songs = songRepository.songs( val songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
AudioColumns.ARTIST_ID + "=?", AudioColumns.ARTIST + " LIKE ?",
arrayOf(artistId.toString()), arrayOf("%$query%"),
getSongLoaderSortOrder() getSongLoaderSortOrder()
) )
) )
return Artist(ArrayList(albumRepository.splitIntoAlbums(songs))) return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
} }
fun splitIntoArtists(albums: List<Album>?): List<Artist> {
val artists = mutableListOf<Artist>() private fun splitIntoAlbumArtists(albums: List<Album>): List<Artist> {
if (albums != null) { return albums.groupBy { it.albumArtist }
for (album in albums) { .map {
getOrCreateArtist(artists, album.artistId).albums!!.add(album) val currentAlbums = it.value
if (currentAlbums.isNotEmpty()) {
if (currentAlbums[0].albumArtist == Artist.VARIOUS_ARTISTS_DISPLAY_NAME) {
Artist(Artist.VARIOUS_ARTISTS_ID, currentAlbums)
} else {
Artist(currentAlbums[0].artistId, currentAlbums)
}
} else {
Artist.empty
}
} }
}
return artists
} }
private fun getOrCreateArtist(artists: MutableList<Artist>, artistId: Int): Artist {
for (artist in artists) {
if (artist.albums!!.isNotEmpty() && artist.albums[0].songs!!.isNotEmpty() && artist.albums[0].songs!![0].artistId == artistId) { fun splitIntoArtists(albums: List<Album>): List<Artist> {
return artist return albums.groupBy { it.artistId }
} .map { Artist(it.key, it.value) }
}
val album = Artist()
artists.add(album)
return album
} }
} }

View File

@ -22,6 +22,8 @@ import android.provider.MediaStore
import android.provider.MediaStore.Audio.Genres import android.provider.MediaStore.Audio.Genres
import code.name.monkey.retromusic.Constants.IS_MUSIC import code.name.monkey.retromusic.Constants.IS_MUSIC
import code.name.monkey.retromusic.Constants.baseProjection import code.name.monkey.retromusic.Constants.baseProjection
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.extensions.getString
import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
@ -29,7 +31,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
interface GenreRepository { interface GenreRepository {
fun genres(): List<Genre> fun genres(): List<Genre>
fun songs(genreId: Int): List<Song> fun songs(genreId: Long): List<Song>
} }
class RealGenreRepository( class RealGenreRepository(
@ -41,25 +43,25 @@ class RealGenreRepository(
return getGenresFromCursor(makeGenreCursor()) return getGenresFromCursor(makeGenreCursor())
} }
override fun songs(genreId: Int): List<Song> { override fun songs(genreId: Long): List<Song> {
// The genres table only stores songs that have a genre specified, // The genres table only stores songs that have a genre specified,
// so we need to get songs without a genre a different way. // so we need to get songs without a genre a different way.
return if (genreId == -1) { return if (genreId == -1L) {
getSongsWithNoGenre() getSongsWithNoGenre()
} else songRepository.songs(makeGenreSongCursor(genreId)) } else songRepository.songs(makeGenreSongCursor(genreId))
} }
private fun getGenreFromCursor(cursor: Cursor): Genre { private fun getGenreFromCursor(cursor: Cursor): Genre {
val id = cursor.getInt(0) val id = cursor.getLong(Genres._ID)
val name = cursor.getString(1) val name = cursor.getString(Genres.NAME)
val songCount = songs(id).size val songCount = songs(id).size
return Genre(id, name, songCount) return Genre(id, name, songCount)
} }
private fun getGenreFromCursorWithOutSongs(cursor: Cursor): Genre { private fun getGenreFromCursorWithOutSongs(cursor: Cursor): Genre {
val id = cursor.getInt(0) val id = cursor.getLong(Genres._ID)
val name = cursor.getString(1) val name = cursor.getString(Genres.NAME)
return Genre(id, name, -1) return Genre(id, name, -1)
} }
@ -91,9 +93,9 @@ class RealGenreRepository(
) )
} }
private fun makeGenreSongCursor(genreId: Int): Cursor? { private fun makeGenreSongCursor(genreId: Long): Cursor? {
return contentResolver.query( return contentResolver.query(
Genres.Members.getContentUri("external", genreId.toLong()), Genres.Members.getContentUri("external", genreId),
baseProjection, baseProjection,
IS_MUSIC, IS_MUSIC,
null, null,

View File

@ -18,8 +18,14 @@ import android.content.ContentResolver
import android.database.Cursor import android.database.Cursor
import android.provider.BaseColumns import android.provider.BaseColumns
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns
import android.provider.MediaStore.Audio.Playlists.*
import android.provider.MediaStore.Audio.PlaylistsColumns import android.provider.MediaStore.Audio.PlaylistsColumns
import code.name.monkey.retromusic.Constants import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.extensions.getInt
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.extensions.getString
import code.name.monkey.retromusic.extensions.getStringOrNull
import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
@ -40,11 +46,11 @@ interface PlaylistRepository {
fun favoritePlaylist(playlistName: String): List<Playlist> fun favoritePlaylist(playlistName: String): List<Playlist>
fun deletePlaylist(playlistId: Int) fun deletePlaylist(playlistId: Long)
fun playlist(playlistId: Int): Playlist fun playlist(playlistId: Long): Playlist
fun playlistSongs(playlistId: Int): List<Song> fun playlistSongs(playlistId: Long): List<Song>
} }
class RealPlaylistRepository( class RealPlaylistRepository(
@ -52,19 +58,20 @@ class RealPlaylistRepository(
) : PlaylistRepository { ) : PlaylistRepository {
override fun playlist(cursor: Cursor?): Playlist { override fun playlist(cursor: Cursor?): Playlist {
var playlist = Playlist() return cursor.use {
if (cursor != null && cursor.moveToFirst()) { if (cursor?.moveToFirst() == true) {
playlist = getPlaylistFromCursorImpl(cursor) getPlaylistFromCursorImpl(cursor)
} else {
Playlist.empty
}
} }
cursor?.close()
return playlist
} }
override fun playlist(playlistName: String): Playlist { override fun playlist(playlistName: String): Playlist {
return playlist(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(playlistName))) return playlist(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(playlistName)))
} }
override fun playlist(playlistId: Int): Playlist { override fun playlist(playlistId: Long): Playlist {
return playlist( return playlist(
makePlaylistCursor( makePlaylistCursor(
BaseColumns._ID + "=?", BaseColumns._ID + "=?",
@ -101,8 +108,8 @@ class RealPlaylistRepository(
) )
} }
override fun deletePlaylist(playlistId: Int) { override fun deletePlaylist(playlistId: Long) {
val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI val localUri = EXTERNAL_CONTENT_URI
val localStringBuilder = StringBuilder() val localStringBuilder = StringBuilder()
localStringBuilder.append("_id IN (") localStringBuilder.append("_id IN (")
localStringBuilder.append(playlistId) localStringBuilder.append(playlistId)
@ -113,12 +120,12 @@ class RealPlaylistRepository(
private fun getPlaylistFromCursorImpl( private fun getPlaylistFromCursorImpl(
cursor: Cursor cursor: Cursor
): Playlist { ): Playlist {
val id = cursor.getInt(0) val id = cursor.getLong(MediaStore.MediaColumns._ID)
val name = cursor.getString(1) val name = cursor.getString(NAME)
return Playlist(id, name) return Playlist(id, name)
} }
override fun playlistSongs(playlistId: Int): List<Song> { override fun playlistSongs(playlistId: Long): List<Song> {
val songs = arrayListOf<Song>() val songs = arrayListOf<Song>()
val cursor = makePlaylistSongCursor(playlistId) val cursor = makePlaylistSongCursor(playlistId)
@ -131,21 +138,21 @@ class RealPlaylistRepository(
return songs return songs
} }
private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong { private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Long): PlaylistSong {
val id = cursor.getInt(0) val id = cursor.getLong(Members.AUDIO_ID)
val title = cursor.getString(1) val title = cursor.getString(AudioColumns.TITLE)
val trackNumber = cursor.getInt(2) val trackNumber = cursor.getInt(AudioColumns.TRACK)
val year = cursor.getInt(3) val year = cursor.getInt(AudioColumns.YEAR)
val duration = cursor.getLong(4) val duration = cursor.getLong(AudioColumns.DURATION)
val data = cursor.getString(5) val data = cursor.getString(AudioColumns.DATA)
val dateModified = cursor.getLong(6) val dateModified = cursor.getLong(AudioColumns.DATE_MODIFIED)
val albumId = cursor.getInt(7) val albumId = cursor.getLong(AudioColumns.ALBUM_ID)
val albumName = cursor.getString(8) val albumName = cursor.getString(AudioColumns.ALBUM)
val artistId = cursor.getInt(9) val artistId = cursor.getLong(AudioColumns.ARTIST_ID)
val artistName = cursor.getString(10) val artistName = cursor.getString(AudioColumns.ARTIST)
val idInPlaylist = cursor.getInt(11) val idInPlaylist = cursor.getLong(Members._ID)
val composer = cursor.getString(12) val composer = cursor.getStringOrNull(AudioColumns.COMPOSER)
val albumArtist = cursor.getString(13) val albumArtist = cursor.getStringOrNull("album_artist")
return PlaylistSong( return PlaylistSong(
id, id,
title, title,
@ -160,7 +167,7 @@ class RealPlaylistRepository(
artistName, artistName,
playlistId, playlistId,
idInPlaylist, idInPlaylist,
composer, composer ?: "",
albumArtist albumArtist
) )
} }
@ -170,37 +177,37 @@ class RealPlaylistRepository(
values: Array<String>? values: Array<String>?
): Cursor? { ): Cursor? {
return contentResolver.query( return contentResolver.query(
MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, EXTERNAL_CONTENT_URI,
arrayOf( arrayOf(
BaseColumns._ID, /* 0 */ BaseColumns._ID, /* 0 */
PlaylistsColumns.NAME /* 1 */ PlaylistsColumns.NAME /* 1 */
), ),
selection, selection,
values, values,
MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER DEFAULT_SORT_ORDER
) )
} }
private fun makePlaylistSongCursor(playlistId: Int): Cursor? { private fun makePlaylistSongCursor(playlistId: Long): Cursor? {
return contentResolver.query( return contentResolver.query(
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), Members.getContentUri("external", playlistId),
arrayOf( arrayOf(
MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0 Members.AUDIO_ID, // 0
MediaStore.Audio.AudioColumns.TITLE, // 1 AudioColumns.TITLE, // 1
MediaStore.Audio.AudioColumns.TRACK, // 2 AudioColumns.TRACK, // 2
MediaStore.Audio.AudioColumns.YEAR, // 3 AudioColumns.YEAR, // 3
MediaStore.Audio.AudioColumns.DURATION, // 4 AudioColumns.DURATION, // 4
MediaStore.Audio.AudioColumns.DATA, // 5 AudioColumns.DATA, // 5
MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6 AudioColumns.DATE_MODIFIED, // 6
MediaStore.Audio.AudioColumns.ALBUM_ID, // 7 AudioColumns.ALBUM_ID, // 7
MediaStore.Audio.AudioColumns.ALBUM, // 8 AudioColumns.ALBUM, // 8
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 AudioColumns.ARTIST_ID, // 9
MediaStore.Audio.AudioColumns.ARTIST, // 10 AudioColumns.ARTIST, // 10
MediaStore.Audio.Playlists.Members._ID,//11 Members._ID,//11
MediaStore.Audio.AudioColumns.COMPOSER,//12 AudioColumns.COMPOSER,//12
"album_artist"//13 "album_artist"//13
), Constants.IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER ), Constants.IS_MUSIC, null, Members.DEFAULT_SORT_ORDER
) )
} }
} }

View File

@ -18,7 +18,12 @@ import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import android.provider.MediaStore.Audio.Playlists.*
import code.name.monkey.retromusic.Constants.IS_MUSIC import code.name.monkey.retromusic.Constants.IS_MUSIC
import code.name.monkey.retromusic.extensions.getInt
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.extensions.getString
import code.name.monkey.retromusic.extensions.getStringOrNull
import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.AbsCustomPlaylist
import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.model.PlaylistSong
@ -42,7 +47,7 @@ object PlaylistSongsLoader {
} }
@JvmStatic @JvmStatic
fun getPlaylistSongList(context: Context, playlistId: Int): List<Song> { fun getPlaylistSongList(context: Context, playlistId: Long): List<Song> {
val songs = mutableListOf<Song>() val songs = mutableListOf<Song>()
val cursor = val cursor =
makePlaylistSongCursor( makePlaylistSongCursor(
@ -64,21 +69,22 @@ object PlaylistSongsLoader {
return songs return songs
} }
private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong { // TODO duplicated in [PlaylistRepository.kt]
val id = cursor.getInt(0) private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Long): PlaylistSong {
val title = cursor.getString(1) val id = cursor.getLong(Members.AUDIO_ID)
val trackNumber = cursor.getInt(2) val title = cursor.getString(AudioColumns.TITLE)
val year = cursor.getInt(3) val trackNumber = cursor.getInt(AudioColumns.TRACK)
val duration = cursor.getLong(4) val year = cursor.getInt(AudioColumns.YEAR)
val data = cursor.getString(5) val duration = cursor.getLong(AudioColumns.DURATION)
val dateModified = cursor.getLong(6) val data = cursor.getString(AudioColumns.DATA)
val albumId = cursor.getInt(7) val dateModified = cursor.getLong(AudioColumns.DATE_MODIFIED)
val albumName = cursor.getString(8) val albumId = cursor.getLong(AudioColumns.ALBUM_ID)
val artistId = cursor.getInt(9) val albumName = cursor.getString(AudioColumns.ALBUM)
val artistName = cursor.getString(10) val artistId = cursor.getLong(AudioColumns.ARTIST_ID)
val idInPlaylist = cursor.getInt(11) val artistName = cursor.getString(AudioColumns.ARTIST)
val composer = cursor.getString(12) val idInPlaylist = cursor.getLong(Members._ID)
val albumArtist = cursor.getString(13) val composer = cursor.getString(AudioColumns.COMPOSER)
val albumArtist = cursor.getStringOrNull("album_artist")
return PlaylistSong( return PlaylistSong(
id, id,
title, title,
@ -98,12 +104,12 @@ object PlaylistSongsLoader {
) )
} }
private fun makePlaylistSongCursor(context: Context, playlistId: Int): Cursor? { private fun makePlaylistSongCursor(context: Context, playlistId: Long): Cursor? {
try { try {
return context.contentResolver.query( return context.contentResolver.query(
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), Members.getContentUri("external", playlistId),
arrayOf( arrayOf(
MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0 Members.AUDIO_ID, // 0
AudioColumns.TITLE, // 1 AudioColumns.TITLE, // 1
AudioColumns.TRACK, // 2 AudioColumns.TRACK, // 2
AudioColumns.YEAR, // 3 AudioColumns.YEAR, // 3
@ -114,10 +120,10 @@ object PlaylistSongsLoader {
AudioColumns.ALBUM, // 8 AudioColumns.ALBUM, // 8
AudioColumns.ARTIST_ID, // 9 AudioColumns.ARTIST_ID, // 9
AudioColumns.ARTIST, // 10 AudioColumns.ARTIST, // 10
MediaStore.Audio.Playlists.Members._ID,//11 Members._ID,//11
AudioColumns.COMPOSER,//12 AudioColumns.COMPOSER,//12
"album_artist"//13 "album_artist"//13
), IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER ), IS_MUSIC, null, Members.DEFAULT_SORT_ORDER
) )
} catch (e: SecurityException) { } catch (e: SecurityException) {
return null return null

View File

@ -41,10 +41,10 @@ interface Repository {
fun historySong(): List<HistoryEntity> fun historySong(): List<HistoryEntity>
fun favorites(): LiveData<List<SongEntity>> fun favorites(): LiveData<List<SongEntity>>
fun observableHistorySongs(): LiveData<List<HistoryEntity>> fun observableHistorySongs(): LiveData<List<HistoryEntity>>
fun albumById(albumId: Int): Album fun albumById(albumId: Long): Album
fun playlistSongs(playlistEntity: PlaylistEntity): LiveData<List<SongEntity>> fun playlistSongs(playlistEntity: PlaylistEntity): LiveData<List<SongEntity>>
suspend fun fetchAlbums(): List<Album> suspend fun fetchAlbums(): List<Album>
suspend fun albumByIdAsync(albumId: Int): Album suspend fun albumByIdAsync(albumId: Long): Album
suspend fun allSongs(): List<Song> suspend fun allSongs(): List<Song>
suspend fun fetchArtists(): List<Artist> suspend fun fetchArtists(): List<Artist>
suspend fun albumArtists(): List<Artist> suspend fun albumArtists(): List<Artist>
@ -52,10 +52,10 @@ interface Repository {
suspend fun fetchGenres(): List<Genre> suspend fun fetchGenres(): List<Genre>
suspend fun search(query: String?): MutableList<Any> suspend fun search(query: String?): MutableList<Any>
suspend fun getPlaylistSongs(playlist: Playlist): List<Song> suspend fun getPlaylistSongs(playlist: Playlist): List<Song>
suspend fun getGenre(genreId: Int): List<Song> suspend fun getGenre(genreId: Long): List<Song>
suspend fun artistInfo(name: String, lang: String?, cache: String?): Result<LastFmArtist> suspend fun artistInfo(name: String, lang: String?, cache: String?): Result<LastFmArtist>
suspend fun albumInfo(artist: String, album: String): Result<LastFmAlbum> suspend fun albumInfo(artist: String, album: String): Result<LastFmAlbum>
suspend fun artistById(artistId: Int): Artist suspend fun artistById(artistId: Long): Artist
suspend fun recentArtists(): List<Artist> suspend fun recentArtists(): List<Artist>
suspend fun topArtists(): List<Artist> suspend fun topArtists(): List<Artist>
suspend fun topAlbums(): List<Album> suspend fun topAlbums(): List<Album>
@ -70,7 +70,7 @@ interface Repository {
suspend fun playlists(): Home suspend fun playlists(): Home
suspend fun homeSections(): List<Home> suspend fun homeSections(): List<Home>
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>> suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
suspend fun playlist(playlistId: Int): Playlist suspend fun playlist(playlistId: Long): Playlist
suspend fun fetchPlaylistWithSongs(): List<PlaylistWithSongs> suspend fun fetchPlaylistWithSongs(): List<PlaylistWithSongs>
suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song>
suspend fun insertSongs(songs: List<SongEntity>) suspend fun insertSongs(songs: List<SongEntity>)
@ -78,7 +78,7 @@ interface Repository {
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
suspend fun fetchPlaylists(): List<PlaylistEntity> suspend fun fetchPlaylists(): List<PlaylistEntity>
suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>)
suspend fun renameRoomPlaylist(playlistId: Int, name: String) suspend fun renameRoomPlaylist(playlistId: Long, name: String)
suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
suspend fun removeSongFromPlaylist(songEntity: SongEntity) suspend fun removeSongFromPlaylist(songEntity: SongEntity)
suspend fun deletePlaylistSongs(playlists: List<PlaylistEntity>) suspend fun deletePlaylistSongs(playlists: List<PlaylistEntity>)
@ -93,7 +93,7 @@ interface Repository {
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity> suspend fun playCountSongs(): List<PlayCountEntity>
suspend fun blackListPaths(): List<BlackListStoreEntity> suspend fun blackListPaths(): List<BlackListStoreEntity>
suspend fun lyrics(artist: String, title: String): Result<String> suspend fun lyrics(artist: String, title: String): Result<String>
@ -126,15 +126,15 @@ class RealRepository(
override suspend fun fetchAlbums(): List<Album> = albumRepository.albums() override suspend fun fetchAlbums(): List<Album> = albumRepository.albums()
override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId) override suspend fun albumByIdAsync(albumId: Long): Album = albumRepository.album(albumId)
override fun albumById(albumId: Int): Album = albumRepository.album(albumId) override fun albumById(albumId: Long): Album = albumRepository.album(albumId)
override suspend fun fetchArtists(): List<Artist> = artistRepository.artists() override suspend fun fetchArtists(): List<Artist> = artistRepository.artists()
override suspend fun albumArtists(): List<Artist> = artistRepository.albumArtists() override suspend fun albumArtists(): List<Artist> = artistRepository.albumArtists()
override suspend fun artistById(artistId: Int): Artist = artistRepository.artist(artistId) override suspend fun artistById(artistId: Long): Artist = artistRepository.artist(artistId)
override suspend fun recentArtists(): List<Artist> = lastAddedRepository.recentArtists() override suspend fun recentArtists(): List<Artist> = lastAddedRepository.recentArtists()
@ -160,7 +160,7 @@ class RealRepository(
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
} }
override suspend fun getGenre(genreId: Int): List<Song> = genreRepository.songs(genreId) override suspend fun getGenre(genreId: Long): List<Song> = genreRepository.songs(genreId)
override suspend fun artistInfo( override suspend fun artistInfo(
name: String, name: String,
@ -235,7 +235,7 @@ class RealRepository(
} }
override suspend fun playlist(playlistId: Int) = override suspend fun playlist(playlistId: Long) =
playlistRepository.playlist(playlistId) playlistRepository.playlist(playlistId)
override suspend fun fetchPlaylistWithSongs(): List<PlaylistWithSongs> = override suspend fun fetchPlaylistWithSongs(): List<PlaylistWithSongs> =
@ -263,7 +263,7 @@ class RealRepository(
override suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) = override suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) =
roomRepository.deletePlaylistEntities(playlists) roomRepository.deletePlaylistEntities(playlists)
override suspend fun renameRoomPlaylist(playlistId: Int, name: String) = override suspend fun renameRoomPlaylist(playlistId: Long, name: String) =
roomRepository.renamePlaylistEntity(playlistId, name) roomRepository.renamePlaylistEntity(playlistId, name)
override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) = override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) =
@ -306,7 +306,7 @@ class RealRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) = override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.deleteSongInPlayCount(playCountEntity) roomRepository.deleteSongInPlayCount(playCountEntity)
override suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> = override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
roomRepository.checkSongExistInPlayCount(songId) roomRepository.checkSongExistInPlayCount(songId)
override suspend fun playCountSongs(): List<PlayCountEntity> = override suspend fun playCountSongs(): List<PlayCountEntity> =

View File

@ -18,7 +18,7 @@ interface RoomRepository {
suspend fun playlistWithSongs(): List<PlaylistWithSongs> suspend fun playlistWithSongs(): List<PlaylistWithSongs>
suspend fun insertSongs(songs: List<SongEntity>) suspend fun insertSongs(songs: List<SongEntity>)
suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>)
suspend fun renamePlaylistEntity(playlistId: Int, name: String) suspend fun renamePlaylistEntity(playlistId: Long, name: String)
suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
suspend fun deletePlaylistSongs(playlists: List<PlaylistEntity>) suspend fun deletePlaylistSongs(playlists: List<PlaylistEntity>)
suspend fun favoritePlaylist(favorite: String): PlaylistEntity suspend fun favoritePlaylist(favorite: String): PlaylistEntity
@ -31,7 +31,7 @@ interface RoomRepository {
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity> suspend fun playCountSongs(): List<PlayCountEntity>
suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>) suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>)
suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
@ -70,13 +70,13 @@ class RealRoomRepository(
} }
override fun getSongs(playlistEntity: PlaylistEntity): LiveData<List<SongEntity>> = override fun getSongs(playlistEntity: PlaylistEntity): LiveData<List<SongEntity>> =
playlistDao.songsFromPlaylist(playlistEntity.playListId) playlistDao.songsFromPlaylist(playlistEntity.playListId)
override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) = override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) =
playlistDao.deletePlaylists(playlistEntities) playlistDao.deletePlaylists(playlistEntities)
override suspend fun renamePlaylistEntity(playlistId: Int, name: String) = override suspend fun renamePlaylistEntity(playlistId: Long, name: String) =
playlistDao.renamePlaylist(playlistId, name) playlistDao.renamePlaylist(playlistId, name)
override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) { override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) {
@ -95,7 +95,7 @@ class RealRoomRepository(
return if (playlist != null) { return if (playlist != null) {
playlist playlist
} else { } else {
createPlaylist(PlaylistEntity(favorite)) createPlaylist(PlaylistEntity(playlistName = favorite))
playlistDao.isPlaylistExists(favorite).first() playlistDao.isPlaylistExists(favorite).first()
} }
} }
@ -143,7 +143,7 @@ class RealRoomRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) = override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
playCountDao.deleteSongInPlayCount(playCountEntity) playCountDao.deleteSongInPlayCount(playCountEntity)
override suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> = override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
playCountDao.checkSongExistInPlayCount(songId) playCountDao.checkSongExistInPlayCount(songId)
override suspend fun playCountSongs(): List<PlayCountEntity> = override suspend fun playCountSongs(): List<PlayCountEntity> =

View File

@ -18,8 +18,13 @@ import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import android.provider.MediaStore.Audio.Media
import code.name.monkey.retromusic.Constants.IS_MUSIC import code.name.monkey.retromusic.Constants.IS_MUSIC
import code.name.monkey.retromusic.Constants.baseProjection import code.name.monkey.retromusic.Constants.baseProjection
import code.name.monkey.retromusic.extensions.getInt
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.extensions.getString
import code.name.monkey.retromusic.extensions.getStringOrNull
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.providers.BlacklistStore import code.name.monkey.retromusic.providers.BlacklistStore
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
@ -40,7 +45,7 @@ interface SongRepository {
fun song(cursor: Cursor?): Song fun song(cursor: Cursor?): Song
fun song(songId: Int): Song fun song(songId: Long): Song
} }
class RealSongRepository(private val context: Context) : SongRepository { class RealSongRepository(private val context: Context) : SongRepository {
@ -74,14 +79,14 @@ class RealSongRepository(private val context: Context) : SongRepository {
return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))) return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")))
} }
override fun song(songId: Int): Song { override fun song(songId: Long): Song {
return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString()))) return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString())))
} }
override fun songsByFilePath(filePath: String): List<Song> { override fun songsByFilePath(filePath: String): List<Song> {
return songs( return songs(
makeSongCursor( makeSongCursor(
MediaStore.Audio.AudioColumns.DATA + "=?", AudioColumns.DATA + "=?",
arrayOf(filePath) arrayOf(filePath)
) )
) )
@ -90,19 +95,19 @@ class RealSongRepository(private val context: Context) : SongRepository {
private fun getSongFromCursorImpl( private fun getSongFromCursorImpl(
cursor: Cursor cursor: Cursor
): Song { ): Song {
val id = cursor.getInt(0) val id = cursor.getLong(AudioColumns._ID)
val title = cursor.getString(1) val title = cursor.getString(AudioColumns.TITLE)
val trackNumber = cursor.getInt(2) val trackNumber = cursor.getInt(AudioColumns.TRACK)
val year = cursor.getInt(3) val year = cursor.getInt(AudioColumns.YEAR)
val duration = cursor.getLong(4) val duration = cursor.getLong(AudioColumns.DURATION)
val data = cursor.getString(5) val data = cursor.getString(AudioColumns.DATA)
val dateModified = cursor.getLong(6) val dateModified = cursor.getLong(AudioColumns.DATE_MODIFIED)
val albumId = cursor.getInt(7) val albumId = cursor.getLong(AudioColumns.ALBUM_ID)
val albumName = cursor.getString(8) val albumName = cursor.getStringOrNull(AudioColumns.ALBUM)
val artistId = cursor.getInt(9) val artistId = cursor.getLong(AudioColumns.ARTIST_ID)
val artistName = cursor.getString(10) val artistName = cursor.getStringOrNull(AudioColumns.ARTIST)
val composer = cursor.getString(11) val composer = cursor.getStringOrNull(AudioColumns.COMPOSER)
val albumArtist = cursor.getString(12) val albumArtist = cursor.getStringOrNull("album_artist")
return Song( return Song(
id, id,
title, title,
@ -142,18 +147,15 @@ class RealSongRepository(private val context: Context) : SongRepository {
selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths)
} }
selectionFinal = selectionFinal =
selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000) selectionFinal + " AND " + Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
try {
return context.contentResolver.query( return context.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
baseProjection, baseProjection,
selectionFinal, selectionFinal,
selectionValuesFinal, selectionValuesFinal,
sortOrder sortOrder
) )
} catch (e: SecurityException) {
return null
}
} }
private fun generateBlacklistSelection( private fun generateBlacklistSelection(

View File

@ -90,7 +90,7 @@ class MediaSessionCallback(
} }
} }
private fun checkAndStartPlaying(songs: ArrayList<Song>, itemId: Int) { private fun checkAndStartPlaying(songs: ArrayList<Song>, itemId: Long) {
var songIndex = MusicUtil.indexOfSongInList(songs, itemId) var songIndex = MusicUtil.indexOfSongInList(songs, itemId)
if (songIndex == -1) { if (songIndex == -1) {
songIndex = 0 songIndex = 0

View File

@ -618,7 +618,7 @@ public class MusicService extends Service implements
break; break;
case SHUFFLE_MODE_NONE: case SHUFFLE_MODE_NONE:
this.shuffleMode = shuffleMode; this.shuffleMode = shuffleMode;
int currentSongId = Objects.requireNonNull(getCurrentSong()).getId(); long currentSongId = Objects.requireNonNull(getCurrentSong()).getId();
playingQueue = new ArrayList<>(originalPlayingQueue); playingQueue = new ArrayList<>(originalPlayingQueue);
int newPosition = 0; int newPosition = 0;
if (getPlayingQueue() != null) { if (getPlayingQueue() != null) {

View File

@ -34,7 +34,7 @@ public class AutoGeneratedPlaylistBitmap {
if (songPlaylist == null) return null; if (songPlaylist == null) return null;
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
// lấy toàn bộ album id, loại bỏ trùng nhau // lấy toàn bộ album id, loại bỏ trùng nhau
ArrayList<Integer> albumID = new ArrayList<>(); List<Long> albumID = new ArrayList<>();
for (Song song : songPlaylist) { for (Song song : songPlaylist) {
if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId()); if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId());
} }
@ -42,8 +42,8 @@ public class AutoGeneratedPlaylistBitmap {
long start2 = System.currentTimeMillis() - start; long start2 = System.currentTimeMillis() - start;
// lấy toàn bộ art tồn tại // lấy toàn bộ art tồn tại
ArrayList<Bitmap> art = new ArrayList<Bitmap>(); List<Bitmap> art = new ArrayList<Bitmap>();
for (Integer id : albumID) { for (Long id : albumID) {
Bitmap bitmap = getBitmapWithAlbumId(context, id); Bitmap bitmap = getBitmapWithAlbumId(context, id);
if (bitmap != null) art.add(bitmap); if (bitmap != null) art.add(bitmap);
if (art.size() == 6) break; if (art.size() == 6) break;
@ -168,7 +168,7 @@ public class AutoGeneratedPlaylistBitmap {
else return bitmap; else return bitmap;
} }
private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Integer id) { private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Long id) {
try { try {
return Glide.with(context) return Glide.with(context)
.load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id)) .load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id))

View File

@ -16,6 +16,7 @@ import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Playlist
@ -85,7 +86,7 @@ object MusicUtil : KoinComponent {
return albumArtDir return albumArtDir
} }
fun deleteAlbumArt(context: Context, albumId: Int) { fun deleteAlbumArt(context: Context, albumId: Long) {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
val localUri = Uri.parse("content://media/external/audio/albumart") val localUri = Uri.parse("content://media/external/audio/albumart")
contentResolver.delete(ContentUris.withAppendedId(localUri, albumId.toLong()), null, null) contentResolver.delete(ContentUris.withAppendedId(localUri, albumId.toLong()), null, null)
@ -175,10 +176,9 @@ object MusicUtil : KoinComponent {
return lyrics return lyrics
} }
fun getMediaStoreAlbumCoverUri(albumId: Int): Uri { fun getMediaStoreAlbumCoverUri(albumId: Long): Uri {
val sArtworkUri = val sArtworkUri = Uri.parse("content://media/external/audio/albumart")
Uri.parse("content://media/external/audio/albumart") return ContentUris.withAppendedId(sArtworkUri, albumId)
return ContentUris.withAppendedId(sArtworkUri, albumId.toLong())
} }
@ -249,7 +249,7 @@ object MusicUtil : KoinComponent {
return "$songCount $songString" return "$songCount $songString"
} }
fun getSongFileUri(songId: Int): Uri { fun getSongFileUri(songId: Long): Uri {
return ContentUris.withAppendedId( return ContentUris.withAppendedId(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
songId.toLong() songId.toLong()
@ -268,18 +268,13 @@ object MusicUtil : KoinComponent {
return if (year > 0) year.toString() else "-" return if (year > 0) year.toString() else "-"
} }
fun indexOfSongInList(songs: List<Song>, songId: Int): Int { fun indexOfSongInList(songs: List<Song>, songId: Long): Int {
for (i in songs.indices) { return songs.indexOfFirst { it.id == songId }
if (songs[i].id == songId) {
return i
}
}
return -1
} }
fun insertAlbumArt( fun insertAlbumArt(
context: Context, context: Context,
albumId: Int, albumId: Long,
path: String? path: String?
) { ) {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
@ -304,9 +299,19 @@ object MusicUtil : KoinComponent {
return tempName == "unknown" || tempName == "<unknown>" return tempName == "unknown" || tempName == "<unknown>"
} }
fun isVariousArtists(artistName: String?): Boolean {
if (TextUtils.isEmpty(artistName)) {
return false
}
if (artistName == Artist.VARIOUS_ARTISTS_DISPLAY_NAME) {
return true
}
return false
}
fun isFavorite(context: Context, song: Song): Boolean { fun isFavorite(context: Context, song: Song): Boolean {
return PlaylistsUtil return PlaylistsUtil
.doPlaylistContains(context, getFavoritesPlaylist(context).id.toLong(), song.id) .doPlaylistContains(context, getFavoritesPlaylist(context).id, song.id)
} }
fun isFavoritePlaylist( fun isFavoritePlaylist(
@ -387,7 +392,7 @@ object MusicUtil : KoinComponent {
// as from the album art cache // as from the album art cache
cursor.moveToFirst() cursor.moveToFirst()
while (!cursor.isAfterLast) { while (!cursor.isAfterLast) {
val id = cursor.getInt(0) val id = cursor.getLong(BaseColumns._ID)
val song: Song = songRepository.song(id) val song: Song = songRepository.song(id)
removeFromQueue(song) removeFromQueue(song)
cursor.moveToNext() cursor.moveToNext()
@ -452,7 +457,7 @@ object MusicUtil : KoinComponent {
// as from the album art cache // as from the album art cache
cursor.moveToFirst() cursor.moveToFirst()
while (!cursor.isAfterLast) { while (!cursor.isAfterLast) {
val id: Int = cursor.getInt(0) val id = cursor.getLong(BaseColumns._ID)
val song: Song = RealSongRepository(context).song(id) val song: Song = RealSongRepository(context).song(id)
removeFromQueue(song) removeFromQueue(song)
cursor.moveToNext() cursor.moveToNext()

View File

@ -43,7 +43,7 @@ import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
public class PlaylistsUtil { public class PlaylistsUtil {
public static int createPlaylist(@NonNull final Context context, @Nullable final String name) { public static long createPlaylist(@NonNull final Context context, @Nullable final String name) {
int id = -1; int id = -1;
if (name != null && name.length() > 0) { if (name != null && name.length() > 0) {
try { try {
@ -101,13 +101,13 @@ public class PlaylistsUtil {
} }
} }
public static void addToPlaylist(@NonNull final Context context, final Song song, final int playlistId, final boolean showToastOnFinish) { public static void addToPlaylist(@NonNull final Context context, final Song song, final long playlistId, final boolean showToastOnFinish) {
List<Song> helperList = new ArrayList<>(); List<Song> helperList = new ArrayList<>();
helperList.add(song); helperList.add(song);
addToPlaylist(context, helperList, playlistId, showToastOnFinish); addToPlaylist(context, helperList, playlistId, showToastOnFinish);
} }
public static void addToPlaylist(@NonNull final Context context, @NonNull final List<Song> songs, final int playlistId, final boolean showToastOnFinish) { public static void addToPlaylist(@NonNull final Context context, @NonNull final List<Song> songs, final long playlistId, final boolean showToastOnFinish) {
final int size = songs.size(); final int size = songs.size();
final ContentResolver resolver = context.getContentResolver(); final ContentResolver resolver = context.getContentResolver();
final String[] projection = new String[]{ final String[] projection = new String[]{
@ -180,7 +180,7 @@ public class PlaylistsUtil {
return ""; return "";
} }
public static void removeFromPlaylist(@NonNull final Context context, @NonNull final Song song, int playlistId) { public static void removeFromPlaylist(@NonNull final Context context, @NonNull final Song song, long playlistId) {
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
"external", playlistId); "external", playlistId);
String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?"; String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?";
@ -193,7 +193,7 @@ public class PlaylistsUtil {
} }
public static void removeFromPlaylist(@NonNull final Context context, @NonNull final List<PlaylistSong> songs) { public static void removeFromPlaylist(@NonNull final Context context, @NonNull final List<PlaylistSong> songs) {
final int playlistId = songs.get(0).getPlaylistId(); final long playlistId = songs.get(0).getPlaylistId();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
"external", playlistId); "external", playlistId);
String[] selectionArgs = new String[songs.size()]; String[] selectionArgs = new String[songs.size()];
@ -211,7 +211,7 @@ public class PlaylistsUtil {
} }
} }
public static boolean doPlaylistContains(@NonNull final Context context, final long playlistId, final int songId) { public static boolean doPlaylistContains(@NonNull final Context context, final long playlistId, final long songId) {
if (playlistId != -1) { if (playlistId != -1) {
try { try {
Cursor c = context.getContentResolver().query( Cursor c = context.getContentResolver().query(
@ -229,7 +229,7 @@ public class PlaylistsUtil {
return false; return false;
} }
public static boolean moveItem(@NonNull final Context context, int playlistId, int from, int to) { public static boolean moveItem(@NonNull final Context context, long playlistId, int from, int to) {
return MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(), return MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(),
playlistId, from, to); playlistId, from, to);
} }

View File

@ -6,6 +6,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:orientation="vertical" android:orientation="vertical"
android:transitionName="@string/transition_album_art"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<FrameLayout <FrameLayout
@ -46,9 +47,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:transitionName="@string/transition_album_art"
app:cardCornerRadius="24dp" app:cardCornerRadius="24dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/container" app:layout_constraintEnd_toStartOf="@+id/container"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"

View File

@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:transitionName="@string/transition_album_art">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
@ -48,7 +49,6 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:transitionName="@string/transition_album_art"
app:cardCornerRadius="24dp" app:cardCornerRadius="24dp"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@ -12,6 +12,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="never" android:overScrollMode="never"
android:transitionGroup="true"
android:scrollbars="none" android:scrollbars="none"
app:layout_dodgeInsetEdges="bottom" app:layout_dodgeInsetEdges="bottom"
tools:listitem="@layout/item_list" /> tools:listitem="@layout/item_list" />

View File

@ -41,9 +41,5 @@
android:label="" android:label=""
tools:layout="@layout/fragment_banner_home" /> tools:layout="@layout/fragment_banner_home" />
<fragment
android:id="@+id/action_import_playlist"
android:name="code.name.monkey.retromusic.fragments.playlists.ImportPlaylistFragment"
android:label="ImportPlaylist"
tools:layout="@layout/fragment_main_activity_recycler_view" />
</navigation> </navigation>

View File

@ -32,7 +32,7 @@
tools:layout="@layout/fragment_album_details"> tools:layout="@layout/fragment_album_details">
<argument <argument
android:name="extra_album_id" android:name="extra_album_id"
app:argType="integer" /> app:argType="long" />
</fragment> </fragment>
<fragment <fragment
@ -42,7 +42,7 @@
tools:layout="@layout/fragment_artist_details"> tools:layout="@layout/fragment_artist_details">
<argument <argument
android:name="extra_artist_id" android:name="extra_artist_id"
app:argType="integer" /> app:argType="long" />
</fragment> </fragment>
<fragment <fragment

View File

@ -11,6 +11,7 @@
<item name="action_album_sort_order_desc" type="id" /> <item name="action_album_sort_order_desc" type="id" />
<item name="action_album_sort_order_artist" type="id" /> <item name="action_album_sort_order_artist" type="id" />
<item name="action_album_sort_order_year" type="id" /> <item name="action_album_sort_order_year" type="id" />
<item name="action_import_playlist" type="id" />
<item name="action_artist_sort_order_asc" type="id" /> <item name="action_artist_sort_order_asc" type="id" />
<item name="action_artist_sort_order_desc" type="id" /> <item name="action_artist_sort_order_desc" type="id" />
<item name="action_song_sort_order_asc" type="id" /> <item name="action_song_sort_order_asc" type="id" />