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'
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-ui-ktx:$nav_version"

View File

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

View File

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

View File

@ -28,8 +28,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
const val APP_UPDATE_REQUEST_CODE = 9002
}
private var blockRequestPermissions = false
override fun createContentView(): View {
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?) {
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)
handled = true
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
val id: Int = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
if (id >= 0) {
val id = parseLongFromIntent(intent, "playlistId", "playlist")
if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0)
val songs: List<Song> =
PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id)
@ -116,19 +106,19 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
handled = true
}
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
if (id >= 0) {
val id = parseLongFromIntent(intent, "albumId", "album")
if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue(
libraryViewModel.albumById(id).songs!!,
libraryViewModel.albumById(id).songs,
position,
true
)
handled = true
}
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
val id: Int = parseIdFromIntent(intent, "artistId", "artist").toInt()
if (id >= 0) {
val id = parseLongFromIntent(intent, "artistId", "artist")
if (id >= 0L) {
val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue(
libraryViewModel.artistById(id).songs,
@ -145,7 +135,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
}
private fun parseIdFromIntent(
private fun parseLongFromIntent(
intent: Intent, longKey: String,
stringKey: String
): Long {

View File

@ -40,7 +40,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
val repository by inject<Repository>()
lateinit var saveFab: MaterialButton
protected var id: Int = 0
protected var id: Long = 0
private set
private var paletteColorPrimary: Int = 0
private var isInNoImageMode: Boolean = false
@ -251,7 +251,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
private fun getIntentExtras() {
val intentExtras = intent.extras
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 {

View File

@ -18,6 +18,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
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.RetroColorUtil.generatePalette
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
@ -168,12 +169,8 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
}
override fun getSongPaths(): List<String> {
val songs = repository.albumById(id).songs
val paths = ArrayList<String>(songs!!.size)
for (song in songs) {
paths.add(song.data)
}
return paths
return repository.albumById(id).songs
.map(Song::data)
}
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) {
CategoryInfo categoryInfo = categoryInfos.get(position);
holder.checkBox.setChecked(categoryInfo.visible);
holder.title.setText(holder.title.getResources().getString(categoryInfo.category.stringRes));
holder.checkBox.setChecked(categoryInfo.isVisible());
holder.title.setText(holder.title.getResources().getString(categoryInfo.getCategory().getStringRes()));
holder.itemView.setOnClickListener(v -> {
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
categoryInfo.visible = !categoryInfo.visible;
holder.checkBox.setChecked(categoryInfo.visible);
if (!(categoryInfo.isVisible() && isLastCheckedCategory(categoryInfo))) {
categoryInfo.setVisible(!categoryInfo.isVisible());
holder.checkBox.setChecked(categoryInfo.isVisible());
} else {
Toast.makeText(holder.itemView.getContext(), R.string.you_have_to_select_at_least_one_category,
Toast.LENGTH_SHORT).show();
@ -110,9 +110,9 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
}
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
if (categoryInfo.visible) {
if (categoryInfo.isVisible()) {
for (CategoryInfo c : categoryInfos) {
if (c != categoryInfo && c.visible) {
if (c != categoryInfo && c.isVisible()) {
return false;
}
}

View File

@ -233,7 +233,7 @@ class HomeAdapter(
fun gridLayoutManager() = GridLayoutManager(activity, 1, GridLayoutManager.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(
R.id.artistDetailsFragment,
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(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId),

View File

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

View File

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

View File

@ -113,6 +113,7 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
itemView.setOnLongClickListener(this);
}
@Nullable
@Override
public View getSwipeableContainerView() {
return null;
@ -129,11 +130,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
}
public void setImageTransitionName(@NonNull String transitionName) {
if (imageContainerCard != null) {
itemView.setTransitionName(transitionName);
/* if (imageContainerCard != null) {
imageContainerCard.setTransitionName(transitionName);
}
if (image != null) {
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.menu
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.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.interfaces.CabHolder

View File

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

View File

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

View File

@ -14,14 +14,14 @@ interface PlayCountDao {
fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
@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")
fun playCountSongs(): List<PlayCountEntity>
@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")
fun updateQuantity(id: Int)
fun updateQuantity(id: Long)
}

View File

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

View File

@ -9,7 +9,7 @@ interface PlaylistDao {
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
@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")
fun isPlaylistExists(name: String): List<PlaylistEntity>
@ -18,10 +18,10 @@ interface PlaylistDao {
suspend fun playlists(): List<PlaylistEntity>
@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")
suspend fun deleteSongFromPlaylist(playlistId: Int, songId: Int)
suspend fun deleteSongFromPlaylist(playlistId: Long, songId: Long)
@Transaction
@Query("SELECT * FROM PlaylistEntity")
@ -31,10 +31,10 @@ interface PlaylistDao {
suspend fun insertSongsToPlaylist(songEntities: List<SongEntity>)
@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")
fun songsFromPlaylist(playlistId: Int): LiveData<List<SongEntity>>
fun songsFromPlaylist(playlistId: Long): LiveData<List<SongEntity>>
@Delete
suspend fun deletePlaylist(playlistEntity: PlaylistEntity)
@ -47,10 +47,10 @@ interface PlaylistDao {
@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")
fun favoritesSongs(playlistId: Int): List<SongEntity>
fun favoritesSongs(playlistId: Long): List<SongEntity>
}

View File

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

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 {
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 {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
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 PlayCountEntity.toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
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 HistoryEntity.toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
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 Song.toPlayCount(): PlayCountEntity {
return PlayCountEntity(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist,
System.currentTimeMillis(),
1
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 = System.currentTimeMillis(),
playCount = 1
)
}

View File

@ -11,6 +11,7 @@ import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.EXTRA_SONG
import code.name.monkey.retromusic.R
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.extra
import code.name.monkey.retromusic.extensions.materialDialog
@ -56,8 +57,8 @@ class CreatePlaylistDialog : DialogFragment() {
lifecycleScope.launch(Dispatchers.IO) {
if (libraryViewModel.checkPlaylistExists(playlistName).isEmpty()) {
val playlistId: Long =
libraryViewModel.createPlaylist(PlaylistEntity(playlistName))
libraryViewModel.insertSongs(songs.map { it.toSongEntity(playlistId.toInt()) })
libraryViewModel.createPlaylist(PlaylistEntity(playlistName = playlistName))
libraryViewModel.insertSongs(songs.map { it.toSongEntity(playlistId) })
libraryViewModel.forceReload(Playlists)
} else {
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.view.View
import android.widget.ImageView
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
@ -178,11 +181,21 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
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.PlaylistWithSongs
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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
@ -75,7 +76,6 @@ class LibraryViewModel(
}
fun getHome(): LiveData<List<Home>> {
fetchHomeSections()
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)
}
@ -200,8 +200,8 @@ class LibraryViewModel(
repository.deleteRoomPlaylist(playlists)
}
suspend fun albumById(id: Int) = repository.albumById(id)
suspend fun artistById(id: Int) = repository.artistById(id)
suspend fun albumById(id: Long) = repository.albumById(id)
suspend fun artistById(id: Long) = repository.artistById(id)
suspend fun favoritePlaylist() = repository.favoritePlaylist()
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
suspend fun insertSongs(songs: List<SongEntity>) = repository.insertSongs(songs)
@ -224,9 +224,9 @@ class LibraryViewModel(
}
repository.insertSongs(songEntities)
} else {
val playListId = createPlaylist(PlaylistEntity(playlist.name))
val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name))
val songEntities = playlist.getSongs().map {
it.toSongEntity(playListId.toInt())
it.toSongEntity(playListId)
}
repository.insertSongs(songEntities)
}

View File

@ -10,6 +10,7 @@ import androidx.core.text.HtmlCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
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.color.MediaNotificationProcessor
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_details.*
import kotlinx.coroutines.Dispatchers
@ -70,8 +73,16 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
private val savedSortOrder: String
get() = PreferenceUtil.albumDetailSongSortOrder
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply {
duration = 1000L
pathMotion = MaterialArcMotion()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
mainActivity.hideBottomBarVisibility(false)
mainActivity.addMusicServiceEventListener(detailsViewModel)
@ -79,7 +90,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
toolbar.title = " "
postponeEnterTransition()
detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer {
println(Thread.currentThread().name)
startPostponedEnterTransition()
showAlbum(it)
})
@ -92,11 +102,11 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
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 {
MusicPlayerRemote.openAndShuffleQueue(
album.songs!!,
album.songs,
true
)
}
@ -135,7 +145,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
}
private fun showAlbum(album: Album) {
if (album.songs!!.isEmpty()) {
if (album.songs.isEmpty()) {
return
}
this.album = album
@ -255,10 +265,12 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
playAction.applyOutlineColor(color)
}
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)
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) {
PreferenceUtil.albumDetailSongSortOrder = sortOrder
when (sortOrder) {
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
val songs = when (sortOrder) {
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs.sortedWith { o1, o2 ->
o1.trackNumber.compareTo(
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(
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(
o1.title
)
})
SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 ->
}
SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs.sortedWith { o1, o2 ->
o1.duration.compareTo(
o2.duration
)
})
}
else -> throw IllegalArgumentException("invalid $sortOrder")
}
album.songs?.let { simpleSongAdapter.swapDataSet(it) }
album = album.copy(songs = songs)
simpleSongAdapter.swapDataSet(album.songs)
}
companion object {

View File

@ -13,15 +13,14 @@ import kotlinx.coroutines.Dispatchers.IO
class AlbumDetailsViewModel(
private val repository: RealRepository,
private val albumId: Int
private val albumId: Long
) : ViewModel(), MusicServiceEventListener {
fun getAlbum(): LiveData<Album> = liveData(IO) {
val album = repository.albumByIdAsync(albumId)
emit(album)
emit(repository.albumByIdAsync(albumId))
}
fun getArtist(artistId: Int): LiveData<Artist> = liveData(IO) {
fun getArtist(artistId: Long): LiveData<Artist> = liveData(IO) {
val artist = repository.artistById(artistId)
emit(artist)
}
@ -32,7 +31,7 @@ class AlbumDetailsViewModel(
}
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)
}
}

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.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
AlbumClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
libraryViewModel.getAlbums().observe(viewLifecycleOwner, Observer {
@ -43,7 +49,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
return AlbumAdapter(
requireActivity(),
dataSet,
R.layout.item_grid,
itemLayoutRes(),
null,
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(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId),
@ -285,5 +291,5 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
}
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.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.R
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.util.CustomArtistImageUtil
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
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) {
val finalColor = if (PreferenceUtil.isAdaptiveColor) color
else ThemeStore.accentColor(requireContext())
shuffleAction.applyColor(finalColor)
playAction.applyOutlineColor(finalColor)
shuffleAction.applyColor(color)
playAction.applyOutlineColor(color)
}
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),

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.Dispatchers.IO
class ArtistDetailsViewModel(
private val realRepository: RealRepository,
private val artistId: Int
private val artistId: Long
) : ViewModel(), MusicServiceEventListener {
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.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
ArtistClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -46,7 +51,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
return ArtistAdapter(
requireActivity(),
dataSet,
R.layout.item_grid_circle,
itemLayoutRes(),
null,
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)
controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId))
}
@ -258,5 +263,5 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
}
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.db.PlaylistEntity
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.extensions.hide
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.adapter.GenreAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import com.google.android.material.transition.platform.MaterialFadeThrough
class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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.PreferenceUtil
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.fragment_banner_home.*
import kotlinx.android.synthetic.main.home_content.*
@ -43,6 +44,10 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class HomeFragment :
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()

View File

@ -14,12 +14,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior
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.ripAlpha
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.ViewUtil
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.draggable.RecyclerViewDragDropManager
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_player.*
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),
MusicProgressViewUpdateHelper.Callback,
@ -61,7 +68,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private var playingQueueAdapter: PlayingQueueAdapter? = null
private lateinit var linearLayoutManager: LinearLayoutManager
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
mainActivity.getBottomSheetBehavior().setAllowDragging(false)
playerQueueSheet.setPadding(
@ -74,11 +81,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_EXPANDED,
BottomSheetBehavior.STATE_DRAGGING -> {
STATE_EXPANDED,
STATE_DRAGGING -> {
mainActivity.getBottomSheetBehavior().setAllowDragging(false)
}
BottomSheetBehavior.STATE_COLLAPSED -> {
STATE_COLLAPSED -> {
resetToCurrentPosition()
mainActivity.getBottomSheetBehavior().setAllowDragging(true)
}
@ -167,9 +174,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onBackPressed(): Boolean {
var wasExpanded = false
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) {
wasExpanded = getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED
if (getQueuePanel().state == STATE_EXPANDED) {
wasExpanded = getQueuePanel().state == STATE_EXPANDED
getQueuePanel().state = STATE_COLLAPSED
return wasExpanded
}
return wasExpanded
@ -216,7 +223,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun toggleFavorite(song: Song) {
super.toggleFavorite(song)
if (song.id == MusicPlayerRemote.currentSong.id) {
updateIsFavorite()
updateIsFavoriteIcon()
}
}
@ -224,6 +231,23 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
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() {
if (PreferenceUtil.isVolumeVisibilityMode) {
childFragmentManager.beginTransaction()
@ -241,6 +265,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
updatePlayPauseDrawableState()
updatePlayPauseDrawableState()
updateQueue()
updateIsFavoriteIcon()
}
override fun onPlayStateChanged() {
@ -259,6 +284,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
super.onPlayingMetaChanged()
updateSong()
updateQueuePosition()
updateIsFavoriteIcon()
}
override fun onQueueChanged() {
@ -359,13 +385,10 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateLabel() {
(MusicPlayerRemote.playingQueue.size - 1).apply {
if (this == (MusicPlayerRemote.position)) {
nextSong.hide()
nextSong.text = "Last song"
} else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
nextSong.apply {
text = title
show()
}
nextSong.text = title
}
}
}
@ -460,12 +483,10 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onUpdateProgressViews(progress: Int, total: Int) {
progressSlider.max = total
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
songTotalTime.text = MusicUtil.getReadableDurationString(total.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.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import com.google.android.material.transition.platform.MaterialFadeThrough
import kotlinx.android.synthetic.main.fragment_library.*
class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, LinearLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

@ -15,6 +15,6 @@ class SearchViewModel(private val realRepository: RealRepository) : ViewModel()
fun search(query: String?) = viewModelScope.launch(IO) {
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.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.transition.platform.MaterialFadeThrough
class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View File

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

View File

@ -14,19 +14,15 @@
package code.name.monkey.retromusic.model
import java.util.*
class Album {
val songs: ArrayList<Song>?
val id: Int
get() = safeGetFirstSong().albumId
data class Album(
val id: Long,
val songs: List<Song>
) {
val title: String?
get() = safeGetFirstSong().albumName
val artistId: Int
val artistId: Long
get() = safeGetFirstSong().artistId
val artistName: String?
@ -39,20 +35,17 @@ class Album {
get() = safeGetFirstSong().dateModified
val songCount: Int
get() = songs!!.size
get() = songs.size
val albumArtist: String?
get() = safeGetFirstSong().albumArtist
constructor(songs: ArrayList<Song>) {
this.songs = songs
}
constructor() {
this.songs = ArrayList()
}
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
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import java.util.*
class Artist {
val albums: ArrayList<Album>?
val id: Int
get() = safeGetFirstAlbum().artistId
data class Artist(
val id: Long,
val albums: List<Album>
) {
val name: String
get() {
val name = safeGetFirstAlbum().safeGetFirstSong().albumArtist
if (PreferenceUtil.albumArtistsOnly && MusicUtil.isVariousArtists(name)) {
return VARIOUS_ARTISTS_DISPLAY_NAME
}
return if (MusicUtil.isArtistNameUnknown(name)) {
UNKNOWN_ARTIST_DISPLAY_NAME
} else safeGetFirstAlbum().safeGetFirstSong().artistName
@ -34,37 +37,27 @@ class Artist {
val songCount: Int
get() {
var songCount = 0
for (album in albums!!) {
for (album in albums) {
songCount += album.songCount
}
return songCount
}
val albumCount: Int
get() = albums!!.size
get() = albums.size
val songs: ArrayList<Song>
get() {
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()
}
val songs: List<Song>
get() = albums.flatMap { it.songs }
fun safeGetFirstAlbum(): Album {
return if (albums!!.isEmpty()) Album() else albums[0]
return albums.firstOrNull() ?: Album.empty
}
companion object {
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
class Contributor(
data class Contributor(
val name: 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
@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 code.name.monkey.retromusic.HomeSection
class Home(
data class Home(
val arrayList: List<Any>,
@HomeSection
val homeSection: Int,

View File

@ -10,10 +10,14 @@ import org.koin.core.get
@Parcelize
open class Playlist(
val id: Int = -1,
val name: String = ""
val id: Long,
val name: String
) : Parcelable, KoinComponent {
companion object {
val empty = Playlist(-1, "")
}
// this default implementation covers static playlists
fun getSongs(): List<Song> {
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 kotlinx.android.parcel.Parcelize
// update equals and hashcode if fields changes
@Parcelize
open class Song(
val id: Int,
val title: String,
val trackNumber: Int,
val year: Int,
val duration: Long,
val data: String,
val dateModified: Long,
val albumId: Int,
val albumName: String,
val artistId: Int,
val artistName: String,
val composer: String?,
val albumArtist: String?
open val id: Long,
open val title: String,
open val trackNumber: Int,
open val year: Int,
open val duration: Long,
open val data: String,
open val dateModified: Long,
open val albumId: Long,
open val albumName: String,
open val artistId: Long,
open val artistName: String,
open val composer: String?,
open val albumArtist: String?
) : Parcelable {
fun toHistoryEntity(timePlayed: Long): HistoryEntity {
return HistoryEntity(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist,
timePlayed
)
// need to override manually because is open and cannot be a data class
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Song
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 (composer != other.composer) return false
if (albumArtist != other.albumArtist) return false
return true
}
fun toSongEntity(playListId: Int): SongEntity {
return SongEntity(
playListId,
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
)
override fun hashCode(): Int {
var 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 + (composer?.hashCode() ?: 0)
result = 31 * result + (albumArtist?.hashCode() ?: 0)
return result
}
@ -77,19 +83,19 @@ open class Song(
@JvmStatic
val emptySong = Song(
-1,
"",
-1,
-1,
-1,
"",
-1,
-1,
"",
-1,
"",
"",
""
id = -1,
title = "",
trackNumber = -1,
year = -1,
duration = -1,
data = "",
dateModified = -1,
albumId = -1,
albumName = "",
artistId = -1,
artistName = "",
composer = "",
albumArtist = ""
)
}
}

View File

@ -5,6 +5,9 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.AbsCustomPlaylist
abstract class AbsSmartPlaylist(
name: String = "",
name: String,
@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
@Parcelize
class HistoryPlaylist :
AbsSmartPlaylist(
App.getContext().getString(R.string.history),
R.drawable.ic_history
),
KoinComponent {
class HistoryPlaylist : AbsSmartPlaylist(
name = App.getContext().getString(R.string.history),
iconRes = R.drawable.ic_history
), KoinComponent {
override fun songs(): List<Song> {
return topPlayedRepository.recentlyPlayedTracks()
}

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ interface ArtistRepository {
fun artists(query: String): List<Artist>
fun artist(artistId: Int): Artist
fun artist(artistId: Long): Artist
}
class RealArtistRepository(
@ -39,7 +39,29 @@ class RealArtistRepository(
PreferenceUtil.artistAlbumSortOrder + ", " +
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> {
val songs = songRepository.songs(
songRepository.makeSongCursor(
@ -50,17 +72,6 @@ class RealArtistRepository(
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> {
val songs = songRepository.songs(
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)).sortedWith(compareBy(sortString) { it.name })
return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs))
}
private fun splitIntoAlbumArtists(albums: List<Album>): 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 {
override fun artists(query: String): List<Artist> {
val songs = songRepository.songs(
songRepository.makeSongCursor(
AudioColumns.ARTIST_ID + "=?",
arrayOf(artistId.toString()),
AudioColumns.ARTIST + " LIKE ?",
arrayOf("%$query%"),
getSongLoaderSortOrder()
)
)
return Artist(ArrayList(albumRepository.splitIntoAlbums(songs)))
return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
}
fun splitIntoArtists(albums: List<Album>?): List<Artist> {
val artists = mutableListOf<Artist>()
if (albums != null) {
for (album in albums) {
getOrCreateArtist(artists, album.artistId).albums!!.add(album)
private fun splitIntoAlbumArtists(albums: List<Album>): List<Artist> {
return albums.groupBy { it.albumArtist }
.map {
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) {
return artist
}
}
val album = Artist()
artists.add(album)
return album
fun splitIntoArtists(albums: List<Album>): List<Artist> {
return albums.groupBy { it.artistId }
.map { Artist(it.key, it.value) }
}
}
}

View File

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

View File

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

View File

@ -41,10 +41,10 @@ interface Repository {
fun historySong(): List<HistoryEntity>
fun favorites(): LiveData<List<SongEntity>>
fun observableHistorySongs(): LiveData<List<HistoryEntity>>
fun albumById(albumId: Int): Album
fun albumById(albumId: Long): Album
fun playlistSongs(playlistEntity: PlaylistEntity): LiveData<List<SongEntity>>
suspend fun fetchAlbums(): List<Album>
suspend fun albumByIdAsync(albumId: Int): Album
suspend fun albumByIdAsync(albumId: Long): Album
suspend fun allSongs(): List<Song>
suspend fun fetchArtists(): List<Artist>
suspend fun albumArtists(): List<Artist>
@ -52,10 +52,10 @@ interface Repository {
suspend fun fetchGenres(): List<Genre>
suspend fun search(query: String?): MutableList<Any>
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 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 topArtists(): List<Artist>
suspend fun topAlbums(): List<Album>
@ -70,7 +70,7 @@ interface Repository {
suspend fun playlists(): Home
suspend fun homeSections(): 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 playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song>
suspend fun insertSongs(songs: List<SongEntity>)
@ -78,7 +78,7 @@ interface Repository {
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
suspend fun fetchPlaylists(): 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 removeSongFromPlaylist(songEntity: SongEntity)
suspend fun deletePlaylistSongs(playlists: List<PlaylistEntity>)
@ -93,7 +93,7 @@ interface Repository {
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(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 blackListPaths(): List<BlackListStoreEntity>
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 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 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()
@ -160,7 +160,7 @@ class RealRepository(
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(
name: String,
@ -235,7 +235,7 @@ class RealRepository(
}
override suspend fun playlist(playlistId: Int) =
override suspend fun playlist(playlistId: Long) =
playlistRepository.playlist(playlistId)
override suspend fun fetchPlaylistWithSongs(): List<PlaylistWithSongs> =
@ -263,7 +263,7 @@ class RealRepository(
override suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) =
roomRepository.deletePlaylistEntities(playlists)
override suspend fun renameRoomPlaylist(playlistId: Int, name: String) =
override suspend fun renameRoomPlaylist(playlistId: Long, name: String) =
roomRepository.renamePlaylistEntity(playlistId, name)
override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) =
@ -306,7 +306,7 @@ class RealRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.deleteSongInPlayCount(playCountEntity)
override suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> =
override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
roomRepository.checkSongExistInPlayCount(songId)
override suspend fun playCountSongs(): List<PlayCountEntity> =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
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;
if (name != null && name.length() > 0) {
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<>();
helperList.add(song);
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 ContentResolver resolver = context.getContentResolver();
final String[] projection = new String[]{
@ -180,7 +180,7 @@ public class PlaylistsUtil {
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(
"external", playlistId);
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) {
final int playlistId = songs.get(0).getPlaylistId();
final long playlistId = songs.get(0).getPlaylistId();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
"external", playlistId);
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) {
try {
Cursor c = context.getContentResolver().query(
@ -229,7 +229,7 @@ public class PlaylistsUtil {
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(),
playlistId, from, to);
}

View File

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

View File

@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
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
android:id="@+id/appBarLayout"
@ -48,7 +49,6 @@
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:transitionName="@string/transition_album_art"
app:cardCornerRadius="24dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"

View File

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

View File

@ -41,9 +41,5 @@
android:label=""
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>

View File

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

View File

@ -11,6 +11,7 @@
<item name="action_album_sort_order_desc" type="id" />
<item name="action_album_sort_order_artist" 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_desc" type="id" />
<item name="action_song_sort_order_asc" type="id" />