diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index 53c051c7..f1e52df6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -1,24 +1,70 @@ package code.name.monkey.retromusic +import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel import code.name.monkey.retromusic.fragments.search.SearchViewModel -import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.network.networkModule -import code.name.monkey.retromusic.providers.RepositoryImpl -import org.eclipse.egit.github.core.Repository +import code.name.monkey.retromusic.repository.* +import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.bind import org.koin.dsl.module private val dataModule = module { single { - RepositoryImpl(get(), get()) + RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } bind Repository::class + + single { + RealSongRepository(get()) + } bind SongRepository::class + + single { + RealGenreRepository(get(), get()) + } bind GenreRepository::class + + single { + RealAlbumRepository(get()) + } bind AlbumRepository::class + + single { + RealArtistRepository(get(), get()) + } bind ArtistRepository::class + + single { + RealPlaylistRepository(get()) + } bind PlaylistRepository::class + + single { + RealTopPlayedRepository(get(), get(), get(), get()) + } bind TopPlayedRepository::class + + single { + RealLastAddedRepository( + get(), + get(), + get() + ) + } bind LastAddedRepository::class + + single { + RealSearchRepository( + get(), + get(), + get(), + get(), + get() + ) + } + + single { + androidContext().contentResolver + } } private val viewModules = module { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index a9fbbee6..16491acc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.provider.MediaStore import android.util.Log import android.view.View +import androidx.lifecycle.lifecycleScope import code.name.monkey.retromusic.* import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.extensions.findNavController @@ -16,13 +17,14 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote.playFromUri import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs -import code.name.monkey.retromusic.loaders.AlbumLoader.getAlbum -import code.name.monkey.retromusic.loaders.ArtistLoader.getArtist -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader.getPlaylistSongList import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.PlaylistSongsLoader.getPlaylistSongList +import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.AppRater.appLaunched import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import java.util.* @@ -33,8 +35,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis const val APP_UPDATE_REQUEST_CODE = 9002 } - - private val libraryViewModel: LibraryViewModel by inject() + private val repository by inject() + private val libraryViewModel by inject() private var blockRequestPermissions = false @@ -133,16 +135,20 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { val id = parseIdFromIntent(intent, "albumId", "album").toInt() if (id >= 0) { - val position = intent.getIntExtra("position", 0) - openQueue(getAlbum(this, id).songs!!, position, true) - handled = true + lifecycleScope.launch(Dispatchers.Main) { + val position = intent.getIntExtra("position", 0) + openQueue(repository.albumById(id).songs!!, position, true) + handled = true + } } } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { val id = parseIdFromIntent(intent, "artistId", "artist").toInt() if (id >= 0) { - val position = intent.getIntExtra("position", 0) - openQueue(getArtist(this, id).songs, position, true) - handled = true + lifecycleScope.launch { + val position = intent.getIntExtra("position", 0) + openQueue(repository.artistById(id).songs, position, true) + handled = true + } } } if (handled) { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt index 8b49a57c..d9f006e7 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt @@ -14,6 +14,7 @@ import android.view.MenuItem import android.view.View import android.view.animation.OvershootInterpolator import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.lifecycleScope import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -23,6 +24,7 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R.drawable import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.activities.saf.SAFGuideActivity +import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.SAFUtil import com.google.android.material.button.MaterialButton @@ -31,18 +33,19 @@ import kotlinx.android.synthetic.main.activity_album_tag_editor.* import org.jaudiotagger.audio.AudioFile import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.tag.FieldKey +import org.koin.android.ext.android.inject import java.io.File import java.util.* abstract class AbsTagEditorActivity : AbsBaseActivity() { + val repository by inject() + lateinit var saveFab: MaterialButton protected var id: Int = 0 private set private var paletteColorPrimary: Int = 0 private var isInNoImageMode: Boolean = false private var songPaths: List? = null - lateinit var saveFab: MaterialButton - private var savedSongPaths: List? = null private val currentSongPath: String? = null private var savedTags: Map? = null @@ -172,33 +175,27 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(contentViewLayout) + setStatusbarColorAuto() + setNavigationbarColorAuto() + setTaskDescriptionColorAuto() saveFab = findViewById(R.id.saveTags) getIntentExtras() - songPaths = getSongPaths() - if (songPaths!!.isEmpty()) { - finish() - return + lifecycleScope.launchWhenCreated { + songPaths = getSongPaths() + if (songPaths!!.isEmpty()) { + finish() + } } - setUpViews() - - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() } private fun setUpViews() { - setUpScrollView() setUpFab() setUpImageView() } - private fun setUpScrollView() { - //observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks); - } - private lateinit var items: List private fun setUpImageView() { @@ -261,7 +258,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { } } - protected abstract fun getSongPaths(): List + protected abstract suspend fun getSongPaths(): List protected fun searchWebFor(vararg keys: String) { val stringBuilder = StringBuilder() @@ -336,7 +333,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { hideFab() - savedSongPaths = getSongPaths() + savedSongPaths = songPaths savedTags = fieldKeyValueMap savedArtworkInfo = artworkInfo diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt index b66a0a49..ca67e93b 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt @@ -18,7 +18,6 @@ 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.loaders.AlbumLoader import code.name.monkey.retromusic.util.ImageUtil import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette import code.name.monkey.retromusic.util.RetroColorUtil.getColor @@ -31,6 +30,7 @@ import org.jaudiotagger.tag.FieldKey import java.util.* class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { + override val contentViewLayout: Int get() = R.layout.activity_album_tag_editor @@ -162,13 +162,13 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { writeValuesToFiles( fieldKeyValueMap, - if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null) + if (deleteAlbumArt) ArtworkInfo(id, null) else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!) ) } - override fun getSongPaths(): List { - val songs = AlbumLoader.getAlbum(this, id).songs + override suspend fun getSongPaths(): List { + val songs = repository.albumById(id).songs val paths = ArrayList(songs!!.size) for (song in songs) { paths.add(song.data) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt index d99bb70c..5e3d5909 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt @@ -8,9 +8,10 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.appHandleColor -import code.name.monkey.retromusic.loaders.SongLoader +import code.name.monkey.retromusic.repository.SongRepository import kotlinx.android.synthetic.main.activity_song_tag_editor.* import org.jaudiotagger.tag.FieldKey +import org.koin.android.ext.android.inject import java.util.* class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { @@ -18,6 +19,8 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { override val contentViewLayout: Int get() = R.layout.activity_song_tag_editor + private val songRepository by inject() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -85,9 +88,9 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { writeValuesToFiles(fieldKeyValueMap, null) } - override fun getSongPaths(): List { + override suspend fun getSongPaths(): List { val paths = ArrayList(1) - paths.add(SongLoader.getSong(this, id).data) + paths.add(songRepository.song(id).data) return paths } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java index 7dfa6f7a..650fc6cc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/WriteTagsAsyncTask.java @@ -63,7 +63,7 @@ public class WriteTagsAsyncTask extends DialogAsyncTask { val songs = java.util.ArrayList() if (playlist is AbsSmartPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.getSongs()) } else { songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt index 2f6e7f38..0f073f18 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt @@ -27,6 +27,7 @@ import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder import code.name.monkey.retromusic.glide.audiocover.AudioFileCover import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.interfaces.Callbacks import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.RetroUtil import com.bumptech.glide.Glide @@ -35,7 +36,6 @@ import com.bumptech.glide.signature.MediaStoreSignature import me.zhanghai.android.fastscroll.PopupTextProvider import java.io.File import java.text.DecimalFormat -import java.util.* import kotlin.math.log10 import kotlin.math.pow @@ -135,9 +135,9 @@ class SongFileAdapter( return getFileTitle(`object`) } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { if (callbacks == null) return - callbacks.onMultipleItemAction(menuItem, selection) + callbacks.onMultipleItemAction(menuItem, selection as ArrayList) } override fun getPopupText(position: Int): String { @@ -148,13 +148,6 @@ class SongFileAdapter( return MusicUtil.getSectionName(dataSet[position].name) } - interface Callbacks { - fun onFileSelected(file: File) - - fun onFileMenuClicked(file: File, view: View) - - fun onMultipleItemAction(item: MenuItem, files: ArrayList) - } inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt index f0d3ec6b..73c8b09e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt @@ -129,12 +129,12 @@ open class AlbumAdapter( } override fun onMultipleItemAction( - menuItem: MenuItem, selection: ArrayList + menuItem: MenuItem, selection: List ) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) } - private fun getSongList(albums: List): ArrayList { + private fun getSongList(albums: List): List { val songs = ArrayList() for (album in albums) { songs.addAll(album.songs!!) @@ -156,7 +156,6 @@ open class AlbumAdapter( dataSet[position].year ) } - return MusicUtil.getSectionName(sectionName) } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt index 28505826..5f55486e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt @@ -107,12 +107,12 @@ class ArtistAdapter( } override fun onMultipleItemAction( - menuItem: MenuItem, selection: ArrayList + menuItem: MenuItem, selection: List ) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) } - private fun getSongList(artists: List): ArrayList { + private fun getSongList(artists: List): List { val songs = ArrayList() for (artist in artists) { songs.addAll(artist.songs) // maybe async in future? diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java index 2aebe6c9..bca248f6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.afollestad.materialcab.MaterialCab; import java.util.ArrayList; +import java.util.List; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.interfaces.CabHolder; @@ -24,7 +25,7 @@ public abstract class AbsMultiSelectAdapter checked; + private List checked; private int menuRes; public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) { @@ -86,7 +87,7 @@ public abstract class AbsMultiSelectAdapter selection); + protected abstract void onMultipleItemAction(MenuItem menuItem, List selection); protected void setMultiSelectMenuRes(@MenuRes int menuRes) { this.menuRes = menuRes; diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt index 947e51c9..39d4f8e1 100755 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt @@ -25,11 +25,11 @@ import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.helper.menu.SongsMenuHelper import code.name.monkey.retromusic.interfaces.CabHolder -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist +import code.name.monkey.retromusic.repository.PlaylistSongsLoader import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.RetroColorUtil @@ -122,7 +122,7 @@ class PlaylistAdapter( return playlist.name } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { when (menuItem.itemId) { else -> SongsMenuHelper.handleMenuClick( activity, @@ -132,11 +132,11 @@ class PlaylistAdapter( } } - private fun getSongList(playlists: List): ArrayList { + private fun getSongList(playlists: List): List { val songs = ArrayList() for (playlist in playlists) { if (playlist is AbsCustomPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.songs()) } else { songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) } @@ -144,12 +144,12 @@ class PlaylistAdapter( return songs } - private fun getSongs(playlist: Playlist): ArrayList { + private fun getSongs(playlist: Playlist): List { val songs = ArrayList() if (playlist is AbsSmartPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.songs()) } else { - songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) + songs.addAll(playlist.getSongs()) } return songs } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt index 8d92296b..2b3ccc3c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt @@ -51,7 +51,7 @@ class OrderablePlaylistSongAdapter( return long } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { when (menuItem.itemId) { R.id.action_remove_from_playlist -> { RemoveFromPlaylistDialog.create(selection as ArrayList) diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt index 6f416f23..b7b1f8e4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt @@ -133,7 +133,7 @@ open class SongAdapter( return song.title } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId) } diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt index 1bc47922..fc01fcdf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt @@ -22,8 +22,8 @@ import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutT import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist -import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist +import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService.* @@ -49,7 +49,7 @@ class AppShortcutLauncherActivity : Activity() { } SHORTCUT_TYPE_TOP_TRACKS -> { startServiceWithPlaylist( - SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext) + SHUFFLE_MODE_NONE, TopTracksPlaylist(applicationContext) ) DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id) } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt index c73a8ca4..33f4e1af 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToPlaylistDialog.kt @@ -17,44 +17,55 @@ package code.name.monkey.retromusic.dialogs import android.app.Dialog import android.os.Bundle import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import code.name.monkey.retromusic.EXTRA_SONG import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.colorButtons import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.extensions.materialDialog -import code.name.monkey.retromusic.loaders.PlaylistLoader import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.PlaylistRepository import code.name.monkey.retromusic.util.PlaylistsUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.koin.android.ext.android.inject class AddToPlaylistDialog : DialogFragment() { - + private val playlistRepository by inject() override fun onCreateDialog( savedInstanceState: Bundle? ): Dialog { - val playlists = PlaylistLoader.getAllPlaylists(requireContext()) - val playlistNames = mutableListOf() - playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist)) - for (p in playlists) { - playlistNames.add(p.name) + val materialDialog = materialDialog(R.string.add_playlist_title) + lifecycleScope.launch { + val playlists = playlistRepository.playlists() + val playlistNames = mutableListOf() + playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist)) + for (p in playlists) { + playlistNames.add(p.name) + } + + withContext(Dispatchers.Main) { + materialDialog.setItems(playlistNames.toTypedArray()) { _, which -> + val songs = extraNotNull>(EXTRA_SONG).value + if (which == 0) { + CreatePlaylistDialog.create(songs) + .show(requireActivity().supportFragmentManager, "ADD_TO_PLAYLIST") + } else { + PlaylistsUtil.addToPlaylist( + requireContext(), + songs, + playlists[which - 1].id, + true + ) + } + dismiss() + } + } } return materialDialog(R.string.add_playlist_title) - .setItems(playlistNames.toTypedArray()) { _, which -> - val songs = extraNotNull>(EXTRA_SONG).value - if (which == 0) { - CreatePlaylistDialog.create(songs) - .show(requireActivity().supportFragmentManager, "ADD_TO_PLAYLIST") - } else { - PlaylistsUtil.addToPlaylist( - requireContext(), - songs, - playlists[which - 1].id, - true - ) - } - dismiss() - } .create().colorButtons() } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt index c67605f6..1765d232 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt @@ -98,7 +98,9 @@ class DeleteSongsDialog : DialogFragment() { } fun deleteSongs(songs: List, safUris: List?) { - MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() } + MusicUtil.deleteTracks(requireActivity(), songs, safUris, Runnable { + dismiss() + }) } companion object { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt index 57d4e9bb..53273281 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt @@ -16,7 +16,7 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import kotlinx.android.synthetic.main.fragment_playlist_detail.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -28,7 +28,7 @@ import org.koin.android.ext.android.inject class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail), ArtistClickListener, AlbumClickListener { private val args by navArgs() - private val repository by inject() + private val repository by inject() override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index 8f514c5a..240b08b7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -7,14 +7,14 @@ import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.fragments.ReloadType.* import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.* -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.launch class LibraryViewModel( - private val repository: RepositoryImpl + private val realRepository: RealRepository ) : ViewModel(), MusicServiceEventListener { private val paletteColor = MutableLiveData() @@ -49,29 +49,29 @@ class LibraryViewModel( } private val loadHome: Deferred> - get() = viewModelScope.async { repository.homeSections() } + get() = viewModelScope.async { realRepository.homeSections() } private val loadSongs: Deferred> - get() = viewModelScope.async(IO) { repository.allSongs() } + get() = viewModelScope.async(IO) { realRepository.allSongs() } private val loadAlbums: Deferred> get() = viewModelScope.async(IO) { - repository.allAlbums() + realRepository.allAlbums() } private val loadArtists: Deferred> get() = viewModelScope.async(IO) { - repository.allArtists() + realRepository.allArtists() } private val loadPlaylists: Deferred> get() = viewModelScope.async(IO) { - repository.allPlaylists() + realRepository.allPlaylists() } private val loadGenres: Deferred> get() = viewModelScope.async(IO) { - repository.allGenres() + realRepository.allGenres() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt index 13839ab4..93b637e7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt @@ -8,14 +8,14 @@ import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmAlbum -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch class AlbumDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val albumId: Int ) : ViewModel(), MusicServiceEventListener { @@ -39,12 +39,12 @@ class AlbumDetailsViewModel( } fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) { - val lastFmAlbum = repository.albumInfo(album.artistName ?: "-", album.title ?: "-") + val lastFmAlbum = realRepository.albumInfo(album.artistName ?: "-", album.title ?: "-") _lastFmAlbum.postValue(lastFmAlbum) } fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) { - val artist = repository.artistById(artistId) + val artist = realRepository.artistById(artistId) _artist.postValue(artist) artist.albums?.filter { item -> item.id != albumId }?.let { albums -> @@ -54,7 +54,7 @@ class AlbumDetailsViewModel( private val loadAlbumAsync: Deferred get() = viewModelScope.async(Dispatchers.IO) { - repository.albumById(albumId) + realRepository.albumById(albumId) } override fun onMediaStoreChanged() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt index b16ea87a..aa6613a4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Artist -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.network.model.LastFmArtist import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -14,13 +14,13 @@ import kotlinx.coroutines.async import kotlinx.coroutines.launch class ArtistDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val artistId: Int ) : ViewModel(), MusicServiceEventListener { private val loadArtistDetailsAsync: Deferred get() = viewModelScope.async(Dispatchers.IO) { - repository.artistById(artistId) + realRepository.artistById(artistId) } private val _artist = MutableLiveData() @@ -40,7 +40,7 @@ class ArtistDetailsViewModel( } fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch { - val info = repository.artistInfo(name, lang, cache) + val info = realRepository.artistInfo(name, lang, cache) _lastFmArtist.postValue(info) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java index 6c3ae333..c4ef8d2b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java @@ -60,6 +60,7 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.helper.menu.SongMenuHelper; import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; import code.name.monkey.retromusic.interfaces.CabHolder; +import code.name.monkey.retromusic.interfaces.Callbacks; import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; import code.name.monkey.retromusic.misc.DialogAsyncTask; import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; @@ -76,7 +77,9 @@ import me.zhanghai.android.fastscroll.FastScroller; public class FoldersFragment extends AbsMainActivityFragment implements MainActivityFragmentCallbacks, - CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, + CabHolder, + BreadCrumbLayout.SelectionCallback, + Callbacks, LoaderManager.LoaderCallbacks> { public static final String TAG = FoldersFragment.class.getSimpleName(); @@ -619,7 +622,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } private static class ListSongsAsyncTask - extends ListingFilesDialogAsyncTask> { + extends ListingFilesDialogAsyncTask> { private final Object extra; private WeakReference callbackWeakReference; @@ -633,7 +636,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } @Override - protected ArrayList doInBackground(LoadingInfo... params) { + protected List doInBackground(LoadingInfo... params) { try { LoadingInfo info = params[0]; List files = FileUtil.listFilesDeep(info.files, info.fileFilter); @@ -659,7 +662,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } @Override - protected void onPostExecute(ArrayList songs) { + protected void onPostExecute(List songs) { super.onPostExecute(songs); OnSongsListedCallback callback = checkCallbackReference(); if (songs != null && callback != null) { @@ -692,7 +695,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements public interface OnSongsListedCallback { - void onSongsListed(@NonNull ArrayList songs, Object extra); + void onSongsListed(@NonNull List songs, Object extra); } static class LoadingInfo { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt index 5d87889d..7641784c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt @@ -7,13 +7,13 @@ import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class GenreDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val genre: Genre ) : ViewModel(), MusicServiceEventListener { @@ -31,7 +31,7 @@ class GenreDetailsViewModel( } private fun loadGenreSongs(genre: Genre) = viewModelScope.launch { - val songs = repository.getGenre(genre.id) + val songs = realRepository.getGenre(genre.id) withContext(Main) { _playListSongs.postValue(songs) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt index 01128bb9..30ddcb2f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt @@ -20,6 +20,7 @@ import android.util.DisplayMetrics import android.view.View import androidx.core.os.bundleOf import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.retromusic.EXTRA_PLAYLIST @@ -31,21 +32,24 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest import code.name.monkey.retromusic.glide.UserProfileGlideRequest import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.loaders.SongLoader import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist -import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist +import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist +import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.abs_playlists.* import kotlinx.android.synthetic.main.fragment_banner_home.* import kotlinx.android.synthetic.main.home_content.* +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.sharedViewModel class HomeFragment : AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) { + private val repository by inject() private val libraryViewModel: LibraryViewModel by sharedViewModel() private val displayMetrics: DisplayMetrics @@ -78,12 +82,17 @@ class HomeFragment : topPlayed.setOnClickListener { findActivityNavController(R.id.fragment_container).navigate( R.id.playlistDetailsFragment, - bundleOf(EXTRA_PLAYLIST to MyTopTracksPlaylist(requireActivity())) + bundleOf(EXTRA_PLAYLIST to TopTracksPlaylist(requireActivity())) ) } actionShuffle.setOnClickListener { - MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(requireActivity()), true) + lifecycleScope.launch { + MusicPlayerRemote.openAndShuffleQueue( + repository.allSongs(), + true + ) + } } history.setOnClickListener { @@ -110,8 +119,8 @@ class HomeFragment : } libraryViewModel.homeLiveData.observe(viewLifecycleOwner, Observer { - homeAdapter.swapData(it) - }) + homeAdapter.swapData(it) + }) loadProfile() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index cca5df70..9a2f23c3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -8,6 +8,7 @@ import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.widget.Toolbar import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R @@ -21,20 +22,21 @@ import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper -import code.name.monkey.retromusic.loaders.ArtistLoader import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.model.lyrics.Lyrics +import code.name.monkey.retromusic.repository.ArtistRepository import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_full.* -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.koin.android.ext.android.inject class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), MusicProgressViewUpdateHelper.Callback { + private val artistRepository by inject() private lateinit var lyricsLayout: FrameLayout private lateinit var lyricsLine1: TextView private lateinit var lyricsLine2: TextView @@ -220,9 +222,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), } private fun updateArtistImage() { - CoroutineScope(Dispatchers.IO).launch { - val artist = - ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId) + lifecycleScope.launch { + val artist = artistRepository.artist(MusicPlayerRemote.currentSong.artistId) withContext(Dispatchers.Main) { ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) .generatePalette(requireContext()) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt index 4935fdf3..de61bdeb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt @@ -6,22 +6,20 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.App import code.name.monkey.retromusic.interfaces.MusicServiceEventListener -import code.name.monkey.retromusic.loaders.PlaylistLoader import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.util.PlaylistsUtil import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class PlaylistDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private var playlist: Playlist ) : ViewModel(), MusicServiceEventListener { private val _playListSongs = MutableLiveData>() - private val _playlist = MutableLiveData().apply { postValue(playlist) } @@ -35,7 +33,7 @@ class PlaylistDetailsViewModel( } private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch { - val songs = repository.getPlaylistSongs(playlist) + val songs = realRepository.getPlaylistSongs(playlist) withContext(Main) { _playListSongs.postValue(songs) } } @@ -50,8 +48,10 @@ class PlaylistDetailsViewModel( val playlistName = PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong()) if (playlistName != playlist.name) { - playlist = PlaylistLoader.getPlaylist(App.getContext(), playlist.id) - _playlist.postValue(playlist) + viewModelScope.launch { + playlist = realRepository.playlist(playlist.id) + _playlist.postValue(playlist) + } } } loadPlaylistSongs(playlist) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt index ee6b3b51..eecff933 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt @@ -4,19 +4,19 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import code.name.monkey.retromusic.providers.RepositoryImpl +import code.name.monkey.retromusic.repository.RealRepository import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class SearchViewModel(private val repository: RepositoryImpl) : ViewModel() { +class SearchViewModel(private val realRepository: RealRepository) : ViewModel() { private val results = MutableLiveData>() fun getSearchResult(): LiveData> = results fun search(query: String?) = viewModelScope.launch(IO) { - val result = repository.search(query) + val result = realRepository.search(query) withContext(Main) { results.postValue(result) } } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/AlbumGlideRequest.java b/app/src/main/java/code/name/monkey/retromusic/glide/AlbumGlideRequest.java index 67d3141f..fab61c61 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/AlbumGlideRequest.java +++ b/app/src/main/java/code/name/monkey/retromusic/glide/AlbumGlideRequest.java @@ -34,7 +34,7 @@ public class AlbumGlideRequest { if (ignoreMediaStore) { return requestManager.load(new AudioFileCover(song.getData())); } else { - return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); + return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId())); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java b/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java index cae842ad..c1ff5df9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java +++ b/app/src/main/java/code/name/monkey/retromusic/glide/SongGlideRequest.java @@ -52,7 +52,7 @@ public class SongGlideRequest { if (ignoreMediaStore) { return requestManager.load(new AudioFileCover(song.getData())); } else { - return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); + return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId())); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt index d7793e56..a56b1db2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt @@ -30,7 +30,7 @@ object M3UWriter : M3UConstants { ): File? { if (!dir.exists()) dir.mkdirs() val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION) - val songs = playlist.getSongs(context) + val songs = playlist.getSongs() if (songs.size > 0) { val bw = BufferedWriter(FileWriter(file)) bw.write(M3UConstants.HEADER) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt index 68f10265..30c6afa1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt @@ -23,22 +23,25 @@ import android.os.Build import android.os.Environment import android.os.IBinder import android.provider.DocumentsContract -import android.provider.MediaStore import android.widget.Toast import androidx.core.content.ContextCompat -import code.name.monkey.retromusic.loaders.SongLoader import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.service.MusicService - import code.name.monkey.retromusic.util.PreferenceUtil +import org.koin.core.KoinComponent +import org.koin.core.inject import java.io.File import java.util.* -object MusicPlayerRemote { +object MusicPlayerRemote : KoinComponent { val TAG: String = MusicPlayerRemote::class.java.simpleName private val mConnectionMap = WeakHashMap() var musicService: MusicService? = null + private val songRepository by inject() + + @JvmStatic val isPlaying: Boolean get() = musicService != null && musicService!!.isPlaying @@ -412,24 +415,17 @@ object MusicPlayerRemote { songId = uri.lastPathSegment } if (songId != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - musicService!!, - MediaStore.Audio.AudioColumns._ID + "=?", - arrayOf(songId) - ) - ) + songs = songRepository.songs(songId) } } } if (songs == null) { var songFile: File? = null if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") { - songFile = - File( - Environment.getExternalStorageDirectory(), - uri.path?.split(":".toRegex(), 2)?.get(1) - ) + songFile = File( + Environment.getExternalStorageDirectory(), + uri.path?.split(":".toRegex(), 2)?.get(1) + ) } if (songFile == null) { val path = getFilePathFromUri(musicService!!, uri) @@ -440,13 +436,7 @@ object MusicPlayerRemote { songFile = File(uri.path) } if (songFile != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - musicService!!, - MediaStore.Audio.AudioColumns.DATA + "=?", - arrayOf(songFile.absolutePath) - ) - ) + songs = songRepository.songsByFilePath(songFile.absolutePath) } } if (songs != null && songs.isNotEmpty()) { diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt index 1efe9c57..b05d41f8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt @@ -18,29 +18,31 @@ import android.app.SearchManager import android.content.Context import android.os.Bundle import android.provider.MediaStore -import code.name.monkey.retromusic.loaders.SongLoader import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.RealSongRepository +import org.koin.core.KoinComponent +import org.koin.core.inject import java.util.* -object SearchQueryHelper { +object SearchQueryHelper : KoinComponent { private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?" private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?" private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?" private const val AND = " AND " + private val songRepository by inject() var songs = ArrayList() @JvmStatic - fun getSongs(context: Context, extras: Bundle): ArrayList { + fun getSongs(context: Context, extras: Bundle): List { val query = extras.getString(SearchManager.QUERY, null) val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null) val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null) val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null) - var songs = ArrayList() + var songs = listOf() if (artistName != null && albumName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf( artistName.toLowerCase(), @@ -54,9 +56,8 @@ object SearchQueryHelper { return songs } if (artistName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION + AND + TITLE_SELECTION, arrayOf(artistName.toLowerCase(), titleName.toLowerCase()) ) @@ -66,9 +67,8 @@ object SearchQueryHelper { return songs } if (albumName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf(albumName.toLowerCase(), titleName.toLowerCase()) ) @@ -78,9 +78,8 @@ object SearchQueryHelper { return songs } if (artistName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION, arrayOf(artistName.toLowerCase()) ) @@ -90,9 +89,8 @@ object SearchQueryHelper { return songs } if (albumName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION, arrayOf(albumName.toLowerCase()) ) @@ -102,9 +100,8 @@ object SearchQueryHelper { return songs } if (titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( TITLE_SELECTION, arrayOf(titleName.toLowerCase()) ) @@ -113,21 +110,18 @@ object SearchQueryHelper { if (songs.isNotEmpty()) { return songs } - songs = - SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - ARTIST_SELECTION, - arrayOf(query.toLowerCase()) - ) + songs = songRepository.songs( + songRepository.makeSongCursor( + ARTIST_SELECTION, + arrayOf(query.toLowerCase()) ) + ) if (songs.isNotEmpty()) { return songs } - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION, arrayOf(query.toLowerCase()) ) @@ -135,9 +129,8 @@ object SearchQueryHelper { if (songs.isNotEmpty()) { return songs } - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( TITLE_SELECTION, arrayOf(query.toLowerCase()) ) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt index b6238a85..08b9c3a2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt @@ -14,42 +14,43 @@ package code.name.monkey.retromusic.helper.menu -import android.app.Activity import android.view.MenuItem import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.loaders.GenreLoader import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song -import java.util.* +import code.name.monkey.retromusic.repository.GenreRepository +import org.koin.core.KoinComponent +import org.koin.core.inject -object GenreMenuHelper { +object GenreMenuHelper : KoinComponent { + private val genreRepository by inject() fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean { when (item.itemId) { R.id.action_play -> { - MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) + MusicPlayerRemote.openQueue(getGenreSongs(genre), 0, true) return true } R.id.action_play_next -> { - MusicPlayerRemote.playNext(getGenreSongs(activity, genre)) + MusicPlayerRemote.playNext(getGenreSongs(genre)) return true } R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(getGenreSongs(activity, genre)) + AddToPlaylistDialog.create(getGenreSongs(genre)) .show(activity.supportFragmentManager, "ADD_PLAYLIST") return true } R.id.action_add_to_current_playing -> { - MusicPlayerRemote.enqueue(getGenreSongs(activity, genre)) + MusicPlayerRemote.enqueue(getGenreSongs(genre)) return true } } return false } - private fun getGenreSongs(activity: Activity, genre: Genre): ArrayList { - return GenreLoader.getSongs(activity, genre.id) + private fun getGenreSongs(genre: Genre): List { + return genreRepository.songs(genre.id) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt index 6d7de7cf..37e5e3d1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt @@ -26,13 +26,11 @@ import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader import code.name.monkey.retromusic.misc.WeakContextAsyncTask import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.PlaylistsUtil -import java.util.* object PlaylistMenuHelper { @@ -80,11 +78,11 @@ object PlaylistMenuHelper { private fun getPlaylistSongs( activity: Activity, playlist: Playlist - ): ArrayList { + ): List { return if (playlist is AbsCustomPlaylist) { - playlist.getSongs(activity) + playlist.songs() } else { - PlaylistSongsLoader.getPlaylistSongList(activity, playlist) + playlist.getSongs() } } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt index 36d110f5..871619e1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt @@ -15,19 +15,17 @@ package code.name.monkey.retromusic.helper.menu import androidx.fragment.app.FragmentActivity - import code.name.monkey.retromusic.R import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.DeleteSongsDialog import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song -import java.util.* object SongsMenuHelper { fun handleMenuClick( activity: FragmentActivity, - songs: ArrayList, + songs: List, menuItemId: Int ): Boolean { when (menuItemId) { diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/Callbacks.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/Callbacks.kt new file mode 100644 index 00000000..998e23d7 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/Callbacks.kt @@ -0,0 +1,13 @@ +package code.name.monkey.retromusic.interfaces + +import android.view.MenuItem +import android.view.View +import java.io.File + +interface Callbacks { + fun onFileSelected(file: File) + + fun onFileMenuClicked(file: File, view: View) + + fun onMultipleItemAction(item: MenuItem, files: ArrayList) +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt deleted file mode 100644 index 71f49017..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistLoader.kt +++ /dev/null @@ -1,140 +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.loaders - -import android.content.Context -import android.database.Cursor -import android.provider.BaseColumns -import android.provider.MediaStore -import android.provider.MediaStore.Audio.PlaylistsColumns -import code.name.monkey.retromusic.model.Playlist -import java.util.* - -/** - * Created by hemanths on 16/08/17. - */ - -object PlaylistLoader { - - private fun getPlaylist( - cursor: Cursor? - ): Playlist { - var playlist = Playlist() - - if (cursor != null && cursor.moveToFirst()) { - playlist = getPlaylistFromCursorImpl(cursor) - } - cursor?.close() - return playlist - } - - fun searchPlaylist(context: Context, searchString: String): List { - return getAllPlaylists( - makePlaylistCursor( - context, PlaylistsColumns.NAME + "=?", arrayOf(searchString) - ) - ) - } - - fun getPlaylist( - context: Context, - playlistName: String - ): Playlist { - return getPlaylist( - makePlaylistCursor( - context, - PlaylistsColumns.NAME + "=?", - arrayOf(playlistName) - ) - ) - } - - fun getAllPlaylists(context: Context): ArrayList { - return getAllPlaylists(makePlaylistCursor(context, null, null)) - } - - fun getFavoritePlaylist(context: Context): ArrayList { - return getAllPlaylists( - makePlaylistCursor( - context, - PlaylistsColumns.NAME + "=?", - arrayOf(context.getString(code.name.monkey.retromusic.R.string.favorites)) - ) - ) - } - - fun getAllPlaylists(cursor: Cursor?): ArrayList { - val playlists = ArrayList() - - if (cursor != null && cursor.moveToFirst()) { - do { - playlists.add(getPlaylistFromCursorImpl(cursor)) - } while (cursor.moveToNext()) - } - cursor?.close() - return playlists - } - - fun deletePlaylists(context: Context, playlistId: Long) { - val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI - val localStringBuilder = StringBuilder() - localStringBuilder.append("_id IN (") - localStringBuilder.append(playlistId) - localStringBuilder.append(")") - context.contentResolver.delete(localUri, localStringBuilder.toString(), null) - } - - private fun makePlaylistCursor( - context: Context, - selection: String?, - values: Array? - ): Cursor? { - try { - return context.contentResolver.query( - MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, - arrayOf( - BaseColumns._ID, /* 0 */ - PlaylistsColumns.NAME /* 1 */ - ), - selection, - values, - MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER - ) - } catch (e: SecurityException) { - return null - } - } - - fun getPlaylist( - context: Context, - playlistId: Int - ): Playlist { - return getPlaylist( - makePlaylistCursor( - context, - BaseColumns._ID + "=?", - arrayOf(playlistId.toString()) - ) - ) - } - - private fun getPlaylistFromCursorImpl( - cursor: Cursor - ): Playlist { - val id = cursor.getInt(0) - val name = cursor.getString(1) - return Playlist(id, name) - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.java deleted file mode 100644 index 819371b7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.java +++ /dev/null @@ -1,45 +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.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ - -public abstract class AbsCustomPlaylist extends Playlist { - - public AbsCustomPlaylist(int id, String name) { - super(id, name); - } - - public AbsCustomPlaylist() { - } - - public AbsCustomPlaylist(Parcel in) { - super(in); - } - - @NonNull - public abstract ArrayList getSongs(@NotNull Context context); -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt new file mode 100644 index 00000000..8df825ca --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt @@ -0,0 +1,21 @@ +package code.name.monkey.retromusic.model + +import code.name.monkey.retromusic.repository.LastAddedRepository +import code.name.monkey.retromusic.repository.SongRepository +import code.name.monkey.retromusic.repository.TopPlayedRepository +import org.koin.core.KoinComponent +import org.koin.core.inject + +abstract class AbsCustomPlaylist( + id: Int = -1, + name: String = "" +) : Playlist(id, name), KoinComponent { + + abstract fun songs(): List + + protected val songRepository by inject() + + protected val topPlayedRepository by inject() + + protected val lastAddedRepository by inject() +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java deleted file mode 100644 index 107b287d..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.java +++ /dev/null @@ -1,120 +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.content.Context; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader; -import code.name.monkey.retromusic.util.MusicUtil; - - -public class Playlist implements Parcelable { - - public static final Creator CREATOR = new Creator() { - public Playlist createFromParcel(Parcel source) { - return new Playlist(source); - } - - public Playlist[] newArray(int size) { - return new Playlist[size]; - } - }; - - public final int id; - - public final String name; - - public Playlist(final int id, final String name) { - this.id = id; - this.name = name; - } - - public Playlist() { - this.id = -1; - this.name = ""; - } - - protected Playlist(Parcel in) { - this.id = in.readInt(); - this.name = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Playlist playlist = (Playlist) o; - - if (id != playlist.id) { - return false; - } - return name != null ? name.equals(playlist.name) : playlist.name == null; - - } - - @NonNull - public ArrayList getSongs(@NonNull Context context) { - // this default implementation covers static playlists - return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, id); - } - - @Override - public int hashCode() { - int result = id; - result = 31 * result + (name != null ? name.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Playlist{" + - "id=" + id + - ", name='" + name + '\'' + - '}'; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(this.id); - dest.writeString(this.name); - } - - @NonNull - public String getInfoString(@NonNull Context context) { - int songCount = getSongs(context).size(); - String songCountString = MusicUtil.getSongCountString(context, songCount); - - return MusicUtil.buildInfoString( - songCountString, - "" - ); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt new file mode 100644 index 00000000..e18d4971 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt @@ -0,0 +1,30 @@ +package code.name.monkey.retromusic.model + +import android.content.Context +import android.os.Parcelable +import code.name.monkey.retromusic.repository.RealPlaylistRepository +import code.name.monkey.retromusic.util.MusicUtil +import kotlinx.android.parcel.Parcelize +import org.koin.core.KoinComponent +import org.koin.core.get + +@Parcelize +open class Playlist( + val id: Int = -1, + val name: String = "" +) : Parcelable, KoinComponent { + + // this default implementation covers static playlists + fun getSongs(): List { + return RealPlaylistRepository(get()).playlistSongs(id) + } + + open fun getInfoString(context: Context): String { + val songCount = getSongs().size + val songCountString = MusicUtil.getSongCountString(context, songCount) + return MusicUtil.buildInfoString( + songCountString, + "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.java deleted file mode 100644 index b99c9abf..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.java +++ /dev/null @@ -1,83 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.DrawableRes; -import androidx.annotation.Nullable; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.AbsCustomPlaylist; - - -public abstract class AbsSmartPlaylist extends AbsCustomPlaylist { - - @DrawableRes - public final int iconRes; - - public AbsSmartPlaylist(final String name, final int iconRes) { - super(-Math.abs(31 * name.hashCode() + (iconRes * name.hashCode() * 31 * 31)), name); - this.iconRes = iconRes; - } - - public AbsSmartPlaylist() { - super(); - this.iconRes = R.drawable.ic_queue_music; - } - - protected AbsSmartPlaylist(Parcel in) { - super(in); - this.iconRes = in.readInt(); - } - - public abstract void clear(Context context); - - @Override - public int describeContents() { - return 0; - } - - @Override - public boolean equals(@Nullable final Object obj) { - if (super.equals(obj)) { - if (getClass() != obj.getClass()) { - return false; - } - final AbsSmartPlaylist other = (AbsSmartPlaylist) obj; - return iconRes == other.iconRes; - } - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + iconRes; - return result; - } - - public boolean isClearable() { - return true; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(this.iconRes); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.kt new file mode 100644 index 00000000..93cc2ccf --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/AbsSmartPlaylist.kt @@ -0,0 +1,10 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import androidx.annotation.DrawableRes +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.AbsCustomPlaylist + +abstract class AbsSmartPlaylist( + name: String = "", + @DrawableRes val iconRes: Int = R.drawable.ic_queue_music +) : AbsCustomPlaylist(-Math.abs(31 * name.hashCode() + iconRes * name.hashCode() * 31 * 31), name) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java deleted file mode 100644 index cbcf95c7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.java +++ /dev/null @@ -1,69 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.providers.HistoryStore; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public class HistoryPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public HistoryPlaylist createFromParcel(Parcel source) { - return new HistoryPlaylist(source); - } - - public HistoryPlaylist[] newArray(int size) { - return new HistoryPlaylist[size]; - } - }; - - public HistoryPlaylist(@NonNull Context context) { - super(context.getString(R.string.history), R.drawable.ic_history); - } - - protected HistoryPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - HistoryStore.getInstance(context).clear(); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt new file mode 100644 index 00000000..747c49e3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt @@ -0,0 +1,15 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import android.content.Context +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song +import org.koin.core.KoinComponent + +class HistoryPlaylist( + context: Context +) : AbsSmartPlaylist(context.getString(R.string.history), R.drawable.ic_history), KoinComponent { + + override fun songs(): List { + return topPlayedRepository.recentlyPlayedTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java deleted file mode 100644 index 7ff02060..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.java +++ /dev/null @@ -1,70 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.LastAddedSongsLoader; -import code.name.monkey.retromusic.model.Song; - - -public class LastAddedPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public LastAddedPlaylist createFromParcel(Parcel source) { - return new LastAddedPlaylist(source); - } - - public LastAddedPlaylist[] newArray(int size) { - return new LastAddedPlaylist[size]; - } - }; - - public LastAddedPlaylist(@NonNull Context context) { - super(context.getString(R.string.last_added), R.drawable.ic_library_add); - } - - protected LastAddedPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context); - } - - @Override - public boolean isClearable() { - return false; - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.kt new file mode 100644 index 00000000..7c8ac666 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/LastAddedPlaylist.kt @@ -0,0 +1,12 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import android.content.Context +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song + +class LastAddedPlaylist(context: Context) : + AbsSmartPlaylist(context.getString(R.string.last_added), R.drawable.ic_library_add) { + override fun songs(): List { + return lastAddedRepository.recentSongs() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java deleted file mode 100644 index c9f176d8..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/MyTopTracksPlaylist.java +++ /dev/null @@ -1,69 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.providers.SongPlayCountStore; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public class MyTopTracksPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public MyTopTracksPlaylist createFromParcel(Parcel source) { - return new MyTopTracksPlaylist(source); - } - - public MyTopTracksPlaylist[] newArray(int size) { - return new MyTopTracksPlaylist[size]; - } - }; - - public MyTopTracksPlaylist(@NonNull Context context) { - super(context.getString(R.string.my_top_tracks), R.drawable.ic_trending_up); - } - - protected MyTopTracksPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - SongPlayCountStore.getInstance(context).clear(); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotPlayedPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotPlayedPlaylist.kt new file mode 100644 index 00000000..a47219f9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotPlayedPlaylist.kt @@ -0,0 +1,13 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import android.content.Context +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song + +class NotPlayedPlaylist( + context: Context +) : AbsSmartPlaylist(context.getString(R.string.not_recently_played), R.drawable.ic_watch_later) { + override fun songs(): List { + return topPlayedRepository.notRecentlyPlayedTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java deleted file mode 100644 index b5804aa3..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/NotRecentlyPlayedPlaylist.java +++ /dev/null @@ -1,69 +0,0 @@ -package code.name.monkey.retromusic.model.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.util.MusicUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; - -/** - * @author SC (soncaokim) - */ -public class NotRecentlyPlayedPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public NotRecentlyPlayedPlaylist createFromParcel(Parcel source) { - return new NotRecentlyPlayedPlaylist(source); - } - - public NotRecentlyPlayedPlaylist[] newArray(int size) { - return new NotRecentlyPlayedPlaylist[size]; - } - }; - - public NotRecentlyPlayedPlaylist(@NonNull Context context) { - super(context.getString(R.string.not_recently_played), R.drawable.ic_watch_later); - } - - protected NotRecentlyPlayedPlaylist(Parcel in) { - super(in); - } - - @NonNull - @Override - public String getInfoString(@NonNull Context context) { - String cutoff = PreferenceUtil.INSTANCE.getRecentlyPlayedCutoffText(context); - - return MusicUtil.buildInfoString( - cutoff, - super.getInfoString(context) - ); - } - - @NonNull - @Override - public ArrayList getSongs(@NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getNotRecentlyPlayedTracks(context); - } - - @Override - public void clear(@NonNull Context context) { - } - - @Override - public boolean isClearable() { - return false; - } - - @Override - public int describeContents() { - return 0; - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java deleted file mode 100644 index 68e968f1..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.java +++ /dev/null @@ -1,65 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Song; - -public class ShuffleAllPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public ShuffleAllPlaylist createFromParcel(Parcel source) { - return new ShuffleAllPlaylist(source); - } - - public ShuffleAllPlaylist[] newArray(int size) { - return new ShuffleAllPlaylist[size]; - } - }; - - public ShuffleAllPlaylist(@NonNull Context context) { - super(context.getString(R.string.action_shuffle_all), R.drawable.ic_shuffle); - } - - protected ShuffleAllPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - // Shuffle all is not a real "Smart Playlist" - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull Context context) { - return SongLoader.INSTANCE.getAllSongs(context); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.kt new file mode 100644 index 00000000..4c8df453 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/ShuffleAllPlaylist.kt @@ -0,0 +1,13 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import android.content.Context +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song + +class ShuffleAllPlaylist( + context: Context +) : AbsSmartPlaylist(context.getString(R.string.action_shuffle_all), R.drawable.ic_shuffle) { + override fun songs(): List { + return songRepository.songs() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/TopTracksPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/TopTracksPlaylist.kt new file mode 100644 index 00000000..d00fc4a5 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/TopTracksPlaylist.kt @@ -0,0 +1,16 @@ +package code.name.monkey.retromusic.model.smartplaylist + +import android.content.Context +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.model.Song + +class TopTracksPlaylist( + context: Context +) : AbsSmartPlaylist( + context.getString(R.string.my_top_tracks), + R.drawable.ic_trending_up +) { + override fun songs(): List { + return topPlayedRepository.topTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java index 9c721322..ee2e040e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java @@ -24,11 +24,11 @@ import android.provider.MediaStore.Audio.AudioColumns; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import java.util.ArrayList; import java.util.List; -import code.name.monkey.retromusic.loaders.SongLoader; +import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.model.Song; +import code.name.monkey.retromusic.repository.RealSongRepository; /** * @author Andrew Neal, modified for Phonograph by Karim Abou Zeid @@ -76,12 +76,12 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { } @NonNull - public ArrayList getSavedOriginalPlayingQueue() { + public List getSavedOriginalPlayingQueue() { return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME); } @NonNull - public ArrayList getSavedPlayingQueue() { + public List getSavedPlayingQueue() { return getQueue(PLAYING_QUEUE_TABLE_NAME); } @@ -157,10 +157,10 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { } @NonNull - private ArrayList getQueue(@NonNull final String tableName) { + private List getQueue(@NonNull final String tableName) { Cursor cursor = getReadableDatabase().query(tableName, null, null, null, null, null, null); - return SongLoader.INSTANCE.getSongs(cursor); + return new RealSongRepository(App.Companion.getContext()).songs(cursor); } /** diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt deleted file mode 100644 index 2f5fc2a1..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt +++ /dev/null @@ -1,89 +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.providers.interfaces - -import code.name.monkey.retromusic.Result -import code.name.monkey.retromusic.model.* -import code.name.monkey.retromusic.network.model.LastFmAlbum -import code.name.monkey.retromusic.network.model.LastFmArtist -import kotlinx.coroutines.flow.Flow - -/** - * Created by hemanths on 11/08/17. - */ - -interface Repository { - - suspend fun allAlbums(): List - - suspend fun albumById(albumId: Int): Album - - suspend fun allSongs(): List - - suspend fun allArtists(): List - - suspend fun allPlaylists(): List - - suspend fun allGenres(): List - - suspend fun search(query: String?): MutableList - - suspend fun getPlaylistSongs(playlist: Playlist): ArrayList - - suspend fun getGenre(genreId: Int): ArrayList - - suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist - - suspend fun albumInfo(artist: String, album: String): LastFmAlbum - - suspend fun artistById(artistId: Int): Artist - - suspend fun recentArtists(): List - - suspend fun topArtists(): List - - suspend fun topAlbums(): List - - suspend fun recentAlbums(): List - - suspend fun recentArtistsHome(): Home - - suspend fun topArtistsHome(): Home - - suspend fun topAlbumsHome(): Home - - suspend fun recentAlbumsHome(): Home - - suspend fun favoritePlaylistHome(): Home - - suspend fun suggestionsHome(): Home - - suspend fun genresHome(): Home - - suspend fun homeSections(): List - - suspend fun homeSectionsFlow(): Flow>> - - fun songsFlow(): Flow>> - - fun albumsFlow(): Flow>> - - fun artistsFlow(): Flow>> - - fun playlistsFlow(): Flow>> - - fun genresFlow(): Flow>> - -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt similarity index 78% rename from app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt index 51aa4d7a..5e83a641 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/AlbumLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt @@ -12,9 +12,8 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository -import android.content.Context import android.provider.MediaStore.Audio.AudioColumns import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album @@ -27,16 +26,31 @@ import kotlin.collections.ArrayList /** * Created by hemanths on 11/08/17. */ +interface AlbumRepository { + fun albums(): List -object AlbumLoader { + fun albums(query: String): List - fun getAlbums( - context: Context, - query: String - ): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + fun album(albumId: Int): Album +} + +class RealAlbumRepository(private val songRepository: RealSongRepository) : + AlbumRepository { + + override fun albums(): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( + null, + null, + getSongLoaderSortOrder() + ) + ) + return splitIntoAlbums(songs) + } + + override fun albums(query: String): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( AudioColumns.ALBUM + " LIKE ?", arrayOf("%$query%"), getSongLoaderSortOrder() @@ -45,41 +59,22 @@ object AlbumLoader { return splitIntoAlbums(songs) } - @JvmStatic - fun getAlbum( - context: Context, - albumId: Int - ): Album { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + override fun album(albumId: Int): Album { + val songs = songRepository.songs( + songRepository.makeSongCursor( AudioColumns.ALBUM_ID + "=?", arrayOf(albumId.toString()), getSongLoaderSortOrder() ) ) - val album = Album(songs) - sortSongsByTrackNumber(album) + val album = Album(ArrayList(songs)) + sortAlbumSongs(album) return album } - fun getAllAlbums( - context: Context - ): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - null, - null, - getSongLoaderSortOrder() - ) - ) - return splitIntoAlbums(songs) - } - fun splitIntoAlbums( - songs: ArrayList? - ): ArrayList { + songs: List? + ): List { val albums = ArrayList() if (songs != null) { for (song in songs) { @@ -87,7 +82,7 @@ object AlbumLoader { } } for (album in albums) { - sortSongsByTrackNumber(album) + sortAlbumSongs(album) } return albums } @@ -106,7 +101,7 @@ object AlbumLoader { return album } - private fun sortSongsByTrackNumber(album: Album) { + private fun sortAlbumSongs(album: Album) { when (PreferenceUtil.albumDetailSongSortOrder) { SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> o1.trackNumber.compareTo( @@ -135,4 +130,6 @@ object AlbumLoader { return PreferenceUtil.albumSortOrder + ", " + PreferenceUtil.albumSongSortOrder } + + } diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt similarity index 62% rename from app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt index 37274c54..fdbe2648 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/ArtistLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt @@ -12,46 +12,66 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository -import android.content.Context import android.provider.MediaStore.Audio.AudioColumns import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.util.PreferenceUtil -object ArtistLoader { +interface ArtistRepository { + fun artists(): List + + fun artists(query: String): List + + fun artist(artistId: Int): Artist +} + +class RealArtistRepository( + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository +) : ArtistRepository { + private fun getSongLoaderSortOrder(): String { return PreferenceUtil.artistSortOrder + ", " + PreferenceUtil.artistAlbumSortOrder + ", " + PreferenceUtil.artistSongSortOrder } - fun getAllArtists(context: Context): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + override fun artists(): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( null, null, getSongLoaderSortOrder() ) ) - return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)) + return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) } - fun getArtists(context: Context, query: String): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + override fun artists(query: String): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( AudioColumns.ARTIST + " LIKE ?", arrayOf("%$query%"), getSongLoaderSortOrder() ) ) - return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)) + return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) } - fun splitIntoArtists(albums: ArrayList?): ArrayList { - val artists = ArrayList() + override fun artist(artistId: Int): Artist { + val songs = songRepository.songs( + songRepository.makeSongCursor( + AudioColumns.ARTIST_ID + "=?", + arrayOf(artistId.toString()), + getSongLoaderSortOrder() + ) + ) + return Artist(ArrayList(albumRepository.splitIntoAlbums(songs))) + } + + fun splitIntoArtists(albums: List?): List { + val artists = mutableListOf() if (albums != null) { for (album in albums) { getOrCreateArtist(artists, album.artistId).albums!!.add(album) @@ -60,7 +80,7 @@ object ArtistLoader { return artists } - private fun getOrCreateArtist(artists: ArrayList, artistId: Int): Artist { + private fun getOrCreateArtist(artists: MutableList, 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 @@ -70,17 +90,4 @@ object ArtistLoader { artists.add(album) return album } - - @JvmStatic - fun getArtist(context: Context, artistId: Int): Artist { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - AudioColumns.ARTIST_ID + "=?", - arrayOf(artistId.toString()), - getSongLoaderSortOrder() - ) - ) - return Artist(AlbumLoader.splitIntoAlbums(songs)) - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt similarity index 50% rename from app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt index 3e57ebe0..ff7f3f5e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/GenreLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt @@ -12,12 +12,13 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository -import android.content.Context +import android.content.ContentResolver import android.database.Cursor import android.net.Uri import android.provider.BaseColumns +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 @@ -25,49 +26,52 @@ import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.PreferenceUtil +interface GenreRepository { + fun genres(): List -object GenreLoader { + fun songs(genreId: Int): List +} - fun getAllGenres(context: Context): ArrayList { - return getGenresFromCursor(context, makeGenreCursor(context)) +class RealGenreRepository( + private val contentResolver: ContentResolver, + private val songRepository: RealSongRepository +) : GenreRepository { + + override fun genres(): List { + return getGenresFromCursor(makeGenreCursor()) } - fun searchGenres(context: Context): ArrayList { - return getGenresFromCursorForSearch(context, makeGenreCursor(context)) - } - - fun getSongs(context: Context, genreId: Int): ArrayList { + override fun songs(genreId: Int): List { // 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) { - getSongsWithNoGenre(context) - } else SongLoader.getSongs(makeGenreSongCursor(context, genreId)) - + getSongsWithNoGenre() + } else songRepository.songs(makeGenreSongCursor(genreId)) } - private fun getGenreFromCursor(context: Context, cursor: Cursor): Genre { + private fun getGenreFromCursor(cursor: Cursor): Genre { val id = cursor.getInt(0) val name = cursor.getString(1) - val songCount = getSongs(context, id).size + val songCount = songs(id).size return Genre(id, name, songCount) } - private fun getGenreFromCursorWithOutSongs(context: Context, cursor: Cursor): Genre { + private fun getGenreFromCursorWithOutSongs(cursor: Cursor): Genre { val id = cursor.getInt(0) val name = cursor.getString(1) return Genre(id, name, -1) } - private fun getSongsWithNoGenre(context: Context): ArrayList { - val selection = BaseColumns._ID + " NOT IN " + - "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" - return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null)) + private fun getSongsWithNoGenre(): List { + val selection = + BaseColumns._ID + " NOT IN " + "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" + return songRepository.songs(songRepository.makeSongCursor(selection, null)) } - private fun hasSongsWithNoGenre(context: Context): Boolean { - val allSongsCursor = SongLoader.makeSongCursor(context, null, null) - val allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context) + private fun hasSongsWithNoGenre(): Boolean { + val allSongsCursor = songRepository.makeSongCursor(null, null) + val allSongsWithGenreCursor = makeAllSongsWithGenreCursor() if (allSongsCursor == null || allSongsWithGenreCursor == null) { return false @@ -79,44 +83,36 @@ object GenreLoader { return hasSongsWithNoGenre } - private fun makeAllSongsWithGenreCursor(context: Context): Cursor? { - return try { - context.contentResolver.query( - Uri.parse("content://media/external/audio/genres/all/members"), - arrayOf(Genres.Members.AUDIO_ID), null, null, null - ) - } catch (e: SecurityException) { - null - } + private fun makeAllSongsWithGenreCursor(): Cursor? { + println(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI.toString()) + return contentResolver.query( + Uri.parse("content://media/external/audio/genres/all/members"), + arrayOf(Genres.Members.AUDIO_ID), null, null, null + ) } - private fun makeGenreSongCursor(context: Context, genreId: Int): Cursor? { - try { - return context.contentResolver.query( - Genres.Members.getContentUri("external", genreId.toLong()), - baseProjection, - IS_MUSIC, - null, - PreferenceUtil.songSortOrder - ) - } catch (e: SecurityException) { - return null - } - + private fun makeGenreSongCursor(genreId: Int): Cursor? { + return contentResolver.query( + Genres.Members.getContentUri("external", genreId.toLong()), + baseProjection, + IS_MUSIC, + null, + PreferenceUtil.songSortOrder + ) } - private fun getGenresFromCursor(context: Context, cursor: Cursor?): ArrayList { + private fun getGenresFromCursor(cursor: Cursor?): ArrayList { val genres = arrayListOf() if (cursor != null) { if (cursor.moveToFirst()) { do { - val genre = getGenreFromCursor(context, cursor) + val genre = getGenreFromCursor(cursor) if (genre.songCount > 0) { genres.add(genre) } else { // try to remove the empty genre from the media store try { - context.contentResolver.delete( + contentResolver.delete( Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null @@ -133,11 +129,11 @@ object GenreLoader { return genres } - private fun getGenresFromCursorForSearch(context: Context, cursor: Cursor?): ArrayList { - val genres = arrayListOf() + private fun getGenresFromCursorForSearch(cursor: Cursor?): List { + val genres = mutableListOf() if (cursor != null && cursor.moveToFirst()) { do { - genres.add(getGenreFromCursorWithOutSongs(context, cursor)) + genres.add(getGenreFromCursorWithOutSongs(cursor)) } while (cursor.moveToNext()) } cursor?.close() @@ -145,18 +141,16 @@ object GenreLoader { } - private fun makeGenreCursor(context: Context): Cursor? { + private fun makeGenreCursor(): Cursor? { val projection = arrayOf(Genres._ID, Genres.NAME) - try { - return context.contentResolver.query( - Genres.EXTERNAL_CONTENT_URI, - projection, - null, - null, - PreferenceUtil.genreSortOrder - ) - } catch (e: SecurityException) { - return null - } + return contentResolver.query( + Genres.EXTERNAL_CONTENT_URI, + projection, + null, + null, + PreferenceUtil.genreSortOrder + ) } + + } diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt similarity index 58% rename from app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt index 50c669e9..153eb375 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/LastAddedSongsLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt @@ -12,9 +12,8 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository -import android.content.Context import android.database.Cursor import android.provider.MediaStore import code.name.monkey.retromusic.model.Album @@ -25,28 +24,37 @@ import code.name.monkey.retromusic.util.PreferenceUtil /** * Created by hemanths on 16/08/17. */ +interface LastAddedRepository { + fun recentSongs(): List -object LastAddedSongsLoader { + fun recentAlbums(): List - fun getLastAddedSongs(context: Context): ArrayList { - return SongLoader.getSongs(makeLastAddedCursor(context)) + fun recentArtists(): List +} + +class RealLastAddedRepository( + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository, + private val artistRepository: RealArtistRepository +) : LastAddedRepository { + override fun recentSongs(): List { + return songRepository.songs(makeLastAddedCursor()) } - private fun makeLastAddedCursor(context: Context): Cursor? { + override fun recentAlbums(): List { + return albumRepository.splitIntoAlbums(recentSongs()) + } + + override fun recentArtists(): List { + return artistRepository.splitIntoArtists(recentAlbums()) + } + + private fun makeLastAddedCursor(): Cursor? { val cutoff = PreferenceUtil.lastAddedCutoff - return SongLoader.makeSongCursor( - context, + return songRepository.makeSongCursor( MediaStore.Audio.Media.DATE_ADDED + ">?", arrayOf(cutoff.toString()), MediaStore.Audio.Media.DATE_ADDED + " DESC" ) } - - fun getLastAddedAlbums(context: Context): ArrayList { - return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context)) - } - - fun getLastAddedArtists(context: Context): ArrayList { - return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context)) - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt new file mode 100644 index 00000000..3462df9d --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt @@ -0,0 +1,206 @@ +/* + * 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.repository + +import android.content.ContentResolver +import android.database.Cursor +import android.provider.BaseColumns +import android.provider.MediaStore +import android.provider.MediaStore.Audio.PlaylistsColumns +import code.name.monkey.retromusic.Constants +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.PlaylistSong +import code.name.monkey.retromusic.model.Song + +/** + * Created by hemanths on 16/08/17. + */ +interface PlaylistRepository { + fun playlist(cursor: Cursor?): Playlist + + fun searchPlaylist(query: String): List + + fun playlist(playlistName: String): Playlist + + fun playlists(): List + + fun playlists(cursor: Cursor?): List + + fun favoritePlaylist(playlistName: String): List + + fun deletePlaylist(playlistId: Int) + + fun playlist(playlistId: Int): Playlist + + fun playlistSongs(playlistId: Int): List +} + +class RealPlaylistRepository( + private val contentResolver: ContentResolver +) : PlaylistRepository { + + override fun playlist(cursor: Cursor?): Playlist { + var playlist = Playlist() + if (cursor != null && cursor.moveToFirst()) { + playlist = getPlaylistFromCursorImpl(cursor) + } + cursor?.close() + return playlist + } + + override fun playlist(playlistName: String): Playlist { + return playlist(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(playlistName))) + } + + override fun playlist(playlistId: Int): Playlist { + return playlist( + makePlaylistCursor( + BaseColumns._ID + "=?", + arrayOf(playlistId.toString()) + ) + ) + } + + override fun searchPlaylist(query: String): List { + return playlists(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(query))) + } + + override fun playlists(): List { + return playlists(makePlaylistCursor(null, null)) + } + + override fun playlists(cursor: Cursor?): List { + val playlists = mutableListOf() + if (cursor != null && cursor.moveToFirst()) { + do { + playlists.add(getPlaylistFromCursorImpl(cursor)) + } while (cursor.moveToNext()) + } + cursor?.close() + return playlists + } + + override fun favoritePlaylist(playlistName: String): List { + return playlists( + makePlaylistCursor( + PlaylistsColumns.NAME + "=?", + arrayOf(playlistName) + ) + ) + } + + override fun deletePlaylist(playlistId: Int) { + val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI + val localStringBuilder = StringBuilder() + localStringBuilder.append("_id IN (") + localStringBuilder.append(playlistId) + localStringBuilder.append(")") + contentResolver.delete(localUri, localStringBuilder.toString(), null) + } + + private fun getPlaylistFromCursorImpl( + cursor: Cursor + ): Playlist { + val id = cursor.getInt(0) + val name = cursor.getString(1) + return Playlist(id, name) + } + + override fun playlistSongs(playlistId: Int): List { + val songs = arrayListOf() + val cursor = makePlaylistSongCursor(playlistId) + + if (cursor != null && cursor.moveToFirst()) { + do { + songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)) + } while (cursor.moveToNext()) + } + cursor?.close() + 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) + return PlaylistSong( + id, + title, + trackNumber, + year, + duration, + data, + dateModified, + albumId, + albumName, + artistId, + artistName, + playlistId, + idInPlaylist, + composer, + albumArtist + ) + } + + private fun makePlaylistCursor( + selection: String?, + values: Array? + ): Cursor? { + return contentResolver.query( + MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + arrayOf( + BaseColumns._ID, /* 0 */ + PlaylistsColumns.NAME /* 1 */ + ), + selection, + values, + MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER + ) + } + + + private fun makePlaylistSongCursor(playlistId: Int): Cursor? { + return contentResolver.query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), + 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 + "album_artist"//13 + ), Constants.IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER + ) + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistSongsLoader.kt similarity index 87% rename from app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/PlaylistSongsLoader.kt index e46f452e..383785b8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistSongsLoader.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository import android.content.Context import android.database.Cursor @@ -34,19 +34,31 @@ object PlaylistSongsLoader { fun getPlaylistSongList( context: Context, playlist: Playlist - ): ArrayList { - return (playlist as? AbsCustomPlaylist)?.getSongs(context) - ?: getPlaylistSongList(context, playlist.id) + ): List { + return if (playlist is AbsCustomPlaylist) { + return playlist.songs() + } else { + getPlaylistSongList(context, playlist.id) + } } @JvmStatic fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList { val songs = arrayListOf() - val cursor = makePlaylistSongCursor(context, playlistId) + val cursor = + makePlaylistSongCursor( + context, + playlistId + ) if (cursor != null && cursor.moveToFirst()) { do { - songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)) + songs.add( + getPlaylistSongFromCursorImpl( + cursor, + playlistId + ) + ) } while (cursor.moveToNext()) } cursor?.close() diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt b/app/src/main/java/code/name/monkey/retromusic/repository/RealRepository.kt similarity index 55% rename from app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/RealRepository.kt index 8f3c18ce..7b6000cd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/RealRepository.kt @@ -12,68 +12,133 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.providers +package code.name.monkey.retromusic.repository import android.content.Context import code.name.monkey.retromusic.* -import code.name.monkey.retromusic.loaders.* import code.name.monkey.retromusic.model.* -import code.name.monkey.retromusic.model.smartplaylist.NotRecentlyPlayedPlaylist +import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist import code.name.monkey.retromusic.network.LastFMService import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmArtist -import code.name.monkey.retromusic.providers.interfaces.Repository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flow -class RepositoryImpl( +interface Repository { + + suspend fun allAlbums(): List + + suspend fun albumById(albumId: Int): Album + + suspend fun allSongs(): List + + suspend fun allArtists(): List + + suspend fun allPlaylists(): List + + suspend fun allGenres(): List + + suspend fun search(query: String?): MutableList + + suspend fun getPlaylistSongs(playlist: Playlist): List + + suspend fun getGenre(genreId: Int): List + + suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist + + suspend fun albumInfo(artist: String, album: String): LastFmAlbum + + suspend fun artistById(artistId: Int): Artist + + suspend fun recentArtists(): List + + suspend fun topArtists(): List + + suspend fun topAlbums(): List + + suspend fun recentAlbums(): List + + suspend fun recentArtistsHome(): Home + + suspend fun topArtistsHome(): Home + + suspend fun topAlbumsHome(): Home + + suspend fun recentAlbumsHome(): Home + + suspend fun favoritePlaylistHome(): Home + + suspend fun suggestionsHome(): Home + + suspend fun genresHome(): Home + + suspend fun homeSections(): List + + suspend fun homeSectionsFlow(): Flow>> + + fun songsFlow(): Flow>> + + fun albumsFlow(): Flow>> + + fun artistsFlow(): Flow>> + + fun playlistsFlow(): Flow>> + + fun genresFlow(): Flow>> + + suspend fun playlist(playlistId: Int): Playlist +} + +class RealRepository( private val context: Context, - private val lastFMService: LastFMService + private val lastFMService: LastFMService, + private val songRepository: SongRepository, + private val albumRepository: AlbumRepository, + private val artistRepository: ArtistRepository, + private val genreRepository: GenreRepository, + private val lastAddedRepository: LastAddedRepository, + private val playlistRepository: PlaylistRepository, + private val searchRepository: RealSearchRepository, + private val playedTracksRepository: TopPlayedRepository ) : Repository { - override suspend fun allAlbums(): List = AlbumLoader.getAllAlbums(context) + override suspend fun allAlbums(): List = albumRepository.albums() - override suspend fun albumById(albumId: Int): Album = AlbumLoader.getAlbum(context, albumId) + override suspend fun albumById(albumId: Int): Album = albumRepository.album(albumId) - override suspend fun allArtists(): List = ArtistLoader.getAllArtists(context) + override suspend fun allArtists(): List = artistRepository.artists() - override suspend fun artistById(artistId: Int): Artist = - ArtistLoader.getArtist(context, artistId) + override suspend fun artistById(artistId: Int): Artist = artistRepository.artist(artistId) - override suspend fun recentArtists(): List = - LastAddedSongsLoader.getLastAddedArtists(context) + override suspend fun recentArtists(): List = lastAddedRepository.recentArtists() - override suspend fun topArtists(): List = - TopAndRecentlyPlayedTracksLoader.getTopArtists(context) + override suspend fun recentAlbums(): List = lastAddedRepository.recentAlbums() - override suspend fun topAlbums(): List = - TopAndRecentlyPlayedTracksLoader.getTopAlbums(context) + override suspend fun topArtists(): List = playedTracksRepository.topArtists() - override suspend fun recentAlbums(): List = - LastAddedSongsLoader.getLastAddedAlbums(context) + override suspend fun topAlbums(): List = playedTracksRepository.topAlbums() - override suspend fun allPlaylists(): List = PlaylistLoader.getAllPlaylists(context) + override suspend fun allPlaylists(): List = playlistRepository.playlists() - override suspend fun allGenres(): List = GenreLoader.getAllGenres(context) + override suspend fun allGenres(): List = genreRepository.genres() - override suspend fun allSongs(): List = SongLoader.getAllSongs(context) + override suspend fun allSongs(): List = songRepository.songs() override suspend fun search(query: String?): MutableList = - SearchLoader.searchAll(context, query) + searchRepository.searchAll(context, query) - override suspend fun getPlaylistSongs(playlist: Playlist): ArrayList { + override suspend fun getPlaylistSongs(playlist: Playlist): List { return if (playlist is AbsCustomPlaylist) { - playlist.getSongs(context) + playlist.songs() } else { PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) } } - override suspend fun getGenre(genreId: Int): ArrayList = - GenreLoader.getSongs(context, genreId) + override suspend fun getGenre(genreId: Int): List = genreRepository.songs(genreId) override suspend fun artistInfo( @@ -137,13 +202,16 @@ class RepositoryImpl( } suspend fun playlists(): Home { - val playlist = PlaylistLoader.getAllPlaylists(context) + val playlist = playlistRepository.playlists() return Home(playlist, TOP_ALBUMS) } + suspend fun playlists(playlistId: Int) = + playlistRepository.playlist(playlistId) + override suspend fun suggestionsHome(): Home { val songs = - NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().takeIf { + NotPlayedPlaylist(context).songs().shuffled().takeIf { it.size > 9 } ?: emptyList() println(songs.size) @@ -151,33 +219,34 @@ class RepositoryImpl( } override suspend fun genresHome(): Home { - val genres = GenreLoader.getAllGenres(context).shuffled() + val genres = genreRepository.genres().shuffled() return Home(genres, GENRES) } override suspend fun recentArtistsHome(): Home { - val artists = LastAddedSongsLoader.getLastAddedArtists(context).take(5) + val artists = lastAddedRepository.recentArtists().take(5) return Home(artists, RECENT_ARTISTS) } override suspend fun recentAlbumsHome(): Home { - val albums = LastAddedSongsLoader.getLastAddedAlbums(context).take(5) + val albums = lastAddedRepository.recentAlbums().take(5) return Home(albums, RECENT_ALBUMS) } override suspend fun topAlbumsHome(): Home { - val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context).take(5) + val albums = playedTracksRepository.topAlbums().take(5) return Home(albums, TOP_ALBUMS) } override suspend fun topArtistsHome(): Home { - val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context).take(5) + val artists = playedTracksRepository.topArtists().take(5) return Home(artists, TOP_ARTISTS) } override suspend fun favoritePlaylistHome(): Home { - val playlists = PlaylistLoader.getFavoritePlaylist(context).take(5) + val playlists = + playlistRepository.favoritePlaylist(context.getString(R.string.favorites)).take(5) val songs = if (playlists.isNotEmpty()) PlaylistSongsLoader.getPlaylistSongList(context, playlists[0]) else emptyList() @@ -187,7 +256,7 @@ class RepositoryImpl( override fun songsFlow(): Flow>> = flow { emit(Result.Loading) - val data = SongLoader.getAllSongs(context) + val data = songRepository.songs() if (data.isEmpty()) { emit(Result.Error) } else { @@ -197,7 +266,7 @@ class RepositoryImpl( override fun albumsFlow(): Flow>> = flow { emit(Result.Loading) - val data = AlbumLoader.getAllAlbums(context) + val data = albumRepository.albums() if (data.isEmpty()) { emit(Result.Error) } else { @@ -207,7 +276,7 @@ class RepositoryImpl( override fun artistsFlow(): Flow>> = flow { emit(Result.Loading) - val data = ArtistLoader.getAllArtists(context) + val data = artistRepository.artists() if (data.isEmpty()) { emit(Result.Error) } else { @@ -217,7 +286,7 @@ class RepositoryImpl( override fun playlistsFlow(): Flow>> = flow { emit(Result.Loading) - val data = PlaylistLoader.getAllPlaylists(context) + val data = playlistRepository.playlists() if (data.isEmpty()) { emit(Result.Error) } else { @@ -227,11 +296,14 @@ class RepositoryImpl( override fun genresFlow(): Flow>> = flow { emit(Result.Loading) - val data = GenreLoader.getAllGenres(context) + val data = genreRepository.genres() if (data.isEmpty()) { emit(Result.Error) } else { emit(Result.Success(data)) } } + + override suspend fun playlist(playlistId: Int): Playlist = + playlistRepository.playlist(playlistId) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt similarity index 63% rename from app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt index d13990ad..3aa55242 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SearchLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt @@ -12,48 +12,51 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository import android.content.Context import code.name.monkey.retromusic.R import code.name.monkey.retromusic.model.Genre import java.util.* -object SearchLoader { +class RealSearchRepository( + private val songRepository: SongRepository, + private val albumRepository: AlbumRepository, + private val artistRepository: RealArtistRepository, + private val genreRepository: GenreRepository, + private val playlistRepository: PlaylistRepository +) { fun searchAll(context: Context, query: String?): MutableList { val results = mutableListOf() query?.let { searchString -> - val songs = SongLoader.getSongs(context, searchString) + val songs = songRepository.songs(searchString) if (songs.isNotEmpty()) { results.add(context.resources.getString(R.string.songs)) results.addAll(songs) } - - val artists = ArtistLoader.getArtists(context, searchString) + val artists = artistRepository.artists(searchString) if (artists.isNotEmpty()) { results.add(context.resources.getString(R.string.artists)) results.addAll(artists) } - val albums = AlbumLoader.getAlbums(context, searchString) + val albums = albumRepository.albums(searchString) if (albums.isNotEmpty()) { results.add(context.resources.getString(R.string.albums)) results.addAll(albums) } - val genres: List = GenreLoader.searchGenres(context) - .filter { genre -> - genre.name.toLowerCase(Locale.getDefault()) - .contains(searchString.toLowerCase(Locale.getDefault())) - } + val genres: List = genreRepository.genres().filter { genre -> + genre.name.toLowerCase(Locale.getDefault()) + .contains(searchString.toLowerCase(Locale.getDefault())) + } if (genres.isNotEmpty()) { results.add(context.resources.getString(R.string.genres)) results.addAll(genres) } - val playlist = PlaylistLoader.getAllPlaylists(context) - .filter { playlist -> - playlist.name.toLowerCase(Locale.getDefault()) - .contains(searchString.toLowerCase(Locale.getDefault())) - } + val playlist = playlistRepository.playlists().filter { playlist -> + playlist.name.toLowerCase(Locale.getDefault()) + .contains(searchString.toLowerCase(Locale.getDefault())) + } if (playlist.isNotEmpty()) { results.add(context.getString(R.string.playlists)) results.addAll(playlist) diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt similarity index 75% rename from app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt index 641f2eb3..430401a6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository import android.content.Context import android.database.Cursor @@ -29,55 +29,63 @@ import java.util.* /** * Created by hemanths on 10/08/17. */ +interface SongRepository { -object SongLoader { + fun songs(): List - fun getAllSongs( - context: Context - ): ArrayList { - val cursor = makeSongCursor(context, null, null) - return getSongs(cursor) + fun songs(cursor: Cursor?): List + + fun songs(query: String): List + + fun songsByFilePath(filePath: String): List + + fun song(cursor: Cursor?): Song + + fun song(songId: Int): Song +} + +class RealSongRepository(private val context: Context) : SongRepository { + + override fun songs(): List { + return songs(makeSongCursor(null, null)) } - fun getSongs( - cursor: Cursor? - ): ArrayList { + override fun songs(cursor: Cursor?): List { val songs = arrayListOf() if (cursor != null && cursor.moveToFirst()) { do { songs.add(getSongFromCursorImpl(cursor)) } while (cursor.moveToNext()) } - cursor?.close() return songs } - fun getSongs( - context: Context, - query: String - ): ArrayList { - val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")) - return getSongs(cursor) - } - - fun getSong( - cursor: Cursor? - ): Song { - val song: Song - if (cursor != null && cursor.moveToFirst()) { - song = getSongFromCursorImpl(cursor) + override fun song(cursor: Cursor?): Song { + val song: Song = if (cursor != null && cursor.moveToFirst()) { + getSongFromCursorImpl(cursor) } else { - song = Song.emptySong + Song.emptySong } cursor?.close() return song } - @JvmStatic - fun getSong(context: Context, queryId: Int): Song { - val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString())) - return getSong(cursor) + override fun songs(query: String): List { + return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))) + } + + override fun song(songId: Int): Song { + return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString()))) + } + + override fun songsByFilePath(filePath: String): List { + return songs( + makeSongCursor( + MediaStore.Audio.AudioColumns.DATA + "=?", + arrayOf(filePath) + ) + ) } private fun getSongFromCursorImpl( @@ -115,7 +123,6 @@ object SongLoader { @JvmOverloads fun makeSongCursor( - context: Context, selection: String?, selectionValues: Array?, sortOrder: String = PreferenceUtil.songSortOrder @@ -131,8 +138,16 @@ object SongLoader { // Blacklist val paths = BlacklistStore.getInstance(context).paths if (paths.isNotEmpty()) { - selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) - selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) + selectionFinal = + generateBlacklistSelection( + selectionFinal, + paths.size + ) + selectionValuesFinal = + addBlacklistSelectionValues( + selectionValuesFinal, + paths + ) } selectionFinal = selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000) diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SortedCursor.java b/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java similarity index 99% rename from app/src/main/java/code/name/monkey/retromusic/loaders/SortedCursor.java rename to app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java index 54049643..701d31da 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SortedCursor.java +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java @@ -11,7 +11,7 @@ * 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.loaders; +package code.name.monkey.retromusic.repository; import android.database.AbstractCursor; import android.database.Cursor; diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SortedLongCursor.java b/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java similarity index 99% rename from app/src/main/java/code/name/monkey/retromusic/loaders/SortedLongCursor.java rename to app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java index 8368c040..727cddd8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SortedLongCursor.java +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java @@ -11,7 +11,7 @@ * 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.loaders; +package code.name.monkey.retromusic.repository; import android.database.AbstractCursor; import android.database.Cursor; diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt b/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt similarity index 62% rename from app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt index da1c21e8..2ad30559 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/TopAndRecentlyPlayedTracksLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt @@ -12,59 +12,85 @@ * See the GNU General Public License for more details. */ -package code.name.monkey.retromusic.loaders +package code.name.monkey.retromusic.repository import android.content.Context import android.database.Cursor import android.provider.BaseColumns import android.provider.MediaStore import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS -import code.name.monkey.retromusic.loaders.SongLoader.makeSongCursor import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.providers.HistoryStore import code.name.monkey.retromusic.providers.SongPlayCountStore import code.name.monkey.retromusic.util.PreferenceUtil -import java.util.* /** * Created by hemanths on 16/08/17. */ -object TopAndRecentlyPlayedTracksLoader { +interface TopPlayedRepository { + fun recentlyPlayedTracks(): List - fun getRecentlyPlayedTracks(context: Context): ArrayList { - return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)) + fun topTracks(): List + + fun notRecentlyPlayedTracks(): List + + fun topAlbums(): List + + fun topArtists(): List +} + +class RealTopPlayedRepository( + private val context: Context, + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository, + private val artistRepository: RealArtistRepository +) : TopPlayedRepository { + + override fun recentlyPlayedTracks(): List { + return songRepository.songs(makeRecentTracksCursorAndClearUpDatabase()) } - fun getTopTracks(context: Context): ArrayList { - return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)) + override fun topTracks(): List { + return songRepository.songs(makeTopTracksCursorAndClearUpDatabase()) } - fun getNotRecentlyPlayedTracks(context: Context): ArrayList { - val allSongs = SongLoader.getSongs( - makeSongCursor( - context, - null, null, - MediaStore.Audio.Media.DATE_ADDED + " ASC" + override fun notRecentlyPlayedTracks(): List { + val allSongs = mutableListOf().apply { + addAll( + songRepository.songs( + songRepository.makeSongCursor( + null, null, + MediaStore.Audio.Media.DATE_ADDED + " ASC" + ) + ) ) + } + val playedSongs = songRepository.songs( + makePlayedTracksCursorAndClearUpDatabase() ) - val playedSongs = SongLoader.getSongs( - makePlayedTracksCursorAndClearUpDatabase(context) - ) - val notRecentlyPlayedSongs = SongLoader.getSongs( - makeNotRecentTracksCursorAndClearUpDatabase(context) + val notRecentlyPlayedSongs = songRepository.songs( + makeNotRecentTracksCursorAndClearUpDatabase() ) allSongs.removeAll(playedSongs) allSongs.addAll(notRecentlyPlayedSongs) return allSongs } - private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? { - val retCursor = makeTopTracksCursorImpl(context) + override fun topAlbums(): List { + return albumRepository.splitIntoAlbums(topTracks()) + } + override fun topArtists(): List { + return artistRepository.splitIntoArtists(topAlbums()) + } + + + private fun makeTopTracksCursorAndClearUpDatabase(): Cursor? { + val retCursor = makeTopTracksCursorImpl() // clean up the databases with any ids not found if (retCursor != null) { val missingIds = retCursor.missingIds @@ -77,33 +103,31 @@ object TopAndRecentlyPlayedTracksLoader { return retCursor } - private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? { + private fun makeRecentTracksCursorImpl(): SortedLongCursor? { // first get the top results ids from the internal database val songs = HistoryStore.getInstance(context).queryRecentIds() songs.use { return makeSortedCursor( - context, it, it.getColumnIndex(HistoryStore.RecentStoreColumns.ID) ) } } - private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? { + private fun makeTopTracksCursorImpl(): SortedLongCursor? { // first get the top results ids from the internal database val songs = SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS) songs.use { localSongs -> return makeSortedCursor( - context, localSongs, + localSongs, localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID) ) } } private fun makeSortedCursor( - context: Context, cursor: Cursor?, idColumn: Int ): SortedLongCursor? { @@ -131,48 +155,46 @@ object TopAndRecentlyPlayedTracksLoader { selection.append(")") // get a list of songs with the data given the selection statement - val songCursor = SongLoader.makeSongCursor(context, selection.toString(), null) + val songCursor = songRepository.makeSongCursor(selection.toString(), null) if (songCursor != null) { // now return the wrapped TopTracksCursor to handle sorting given order - return SortedLongCursor(songCursor, order, BaseColumns._ID) + return SortedLongCursor( + songCursor, + order, + BaseColumns._ID + ) } } return null } - fun getTopAlbums( - context: Context - ): ArrayList { - arrayListOf() - return AlbumLoader.splitIntoAlbums(getTopTracks(context)) + private fun makeRecentTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = false, + reverseOrder = false + ) } - fun getTopArtists(context: Context): ArrayList { - return ArtistLoader.splitIntoArtists(getTopAlbums(context)) + private fun makePlayedTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = true, + reverseOrder = false + ) } - - fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, false) - } - - - fun makePlayedTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, true, false) - } - - - fun makeNotRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, true) + private fun makeNotRecentTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = false, + reverseOrder = true + ) } private fun makeRecentTracksCursorAndClearUpDatabaseImpl( - context: Context, ignoreCutoffTime: Boolean, reverseOrder: Boolean ): SortedLongCursor? { - val retCursor = makeRecentTracksCursorImpl(context, ignoreCutoffTime, reverseOrder) + val retCursor = makeRecentTracksCursorImpl(ignoreCutoffTime, reverseOrder) // clean up the databases with any ids not found // clean up the databases with any ids not found if (retCursor != null) { @@ -186,9 +208,7 @@ object TopAndRecentlyPlayedTracksLoader { return retCursor } - private fun makeRecentTracksCursorImpl( - context: Context, ignoreCutoffTime: Boolean, reverseOrder: Boolean ): SortedLongCursor? { @@ -198,7 +218,6 @@ object TopAndRecentlyPlayedTracksLoader { HistoryStore.getInstance(context).queryRecentIds(cutoff * if (reverseOrder) -1 else 1) return songs.use { makeSortedCursor( - context, it, it.getColumnIndex(HistoryStore.RecentStoreColumns.ID) ) diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java index 56ac1afd..64dfe785 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java @@ -327,7 +327,7 @@ public class MusicService extends Service implements } private static String getTrackUri(@NonNull Song song) { - return MusicUtil.getSongFileUri(song.getId()).toString(); + return MusicUtil.INSTANCE.getSongFileUri(song.getId()).toString(); } @Override @@ -790,7 +790,7 @@ public class MusicService extends Service implements pendingQuit = true; break; case TOGGLE_FAVORITE: - MusicUtil.toggleFavorite(getApplicationContext(), getCurrentSong()); + MusicUtil.INSTANCE.toggleFavorite(getApplicationContext(), getCurrentSong()); break; } } @@ -1216,7 +1216,7 @@ public class MusicService extends Service implements Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode()); if (playlist != null) { - ArrayList playlistSongs = playlist.getSongs(getApplicationContext()); + List playlistSongs = playlist.getSongs(); if (!playlistSongs.isEmpty()) { if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { int startPosition = new Random().nextInt(playlistSongs.size()); @@ -1329,7 +1329,7 @@ public class MusicService extends Service implements TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon) .build()); - final int favoriteIcon = MusicUtil.isFavorite(getApplicationContext(), getCurrentSong()) + final int favoriteIcon = MusicUtil.INSTANCE.isFavorite(getApplicationContext(), getCurrentSong()) ? R.drawable.ic_favorite : R.drawable.ic_favorite_border; stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder( TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon) diff --git a/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java b/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java index 1e5764c7..a1fe1e22 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java @@ -172,7 +172,7 @@ public class AutoGeneratedPlaylistBitmap { private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Integer id) { try { return Glide.with(context) - .load(MusicUtil.getMediaStoreAlbumCoverUri(id)) + .load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id)) .asBitmap() .into(200, 200) .get(); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java index 9476e86d..1ea5adff 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java @@ -31,15 +31,14 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.loaders.SortedCursor; import code.name.monkey.retromusic.model.Song; +import code.name.monkey.retromusic.repository.RealSongRepository; +import code.name.monkey.retromusic.repository.SortedCursor; public final class FileUtil { @@ -59,9 +58,9 @@ public final class FileUtil { } @NonNull - public static ArrayList matchFilesWithMediaStore(@NonNull Context context, - @Nullable List files) { - return SongLoader.INSTANCE.getSongs(makeSongCursor(context, files)); + public static List matchFilesWithMediaStore(@NonNull Context context, + @Nullable List files) { + return new RealSongRepository(context).songs(makeSongCursor(context, files)); } public static String safeGetCanonicalPath(File file) { @@ -89,7 +88,7 @@ public final class FileUtil { } } - Cursor songCursor = SongLoader.INSTANCE.makeSongCursor(context, selection, selection == null ? null : paths); + Cursor songCursor = new RealSongRepository(context).makeSongCursor(selection, selection == null ? null : paths); return songCursor == null ? null : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java deleted file mode 100644 index 6f8d4f61..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java +++ /dev/null @@ -1,428 +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.util; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.provider.BaseColumns; -import android.provider.MediaStore; -import android.text.TextUtils; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; - -import org.jaudiotagger.audio.AudioFileIO; -import org.jaudiotagger.tag.FieldKey; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Pattern; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.loaders.PlaylistLoader; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Playlist; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics; -import code.name.monkey.retromusic.service.MusicService; - - -public class MusicUtil { - - public static final String TAG = MusicUtil.class.getSimpleName(); - - private static Playlist playlist; - - /** - * Build a concatenated string from the provided arguments - * The intended purpose is to show extra annotations - * to a music library item. - * Ex: for a given album --> buildInfoString(album.artist, album.songCount) - */ - @NonNull - public static String buildInfoString(@Nullable final String string1, @Nullable final String string2) { - // Skip empty strings - if (TextUtils.isEmpty(string1)) { - //noinspection ConstantConditions - return TextUtils.isEmpty(string2) ? "" : string2; - } - if (TextUtils.isEmpty(string2)) { - //noinspection ConstantConditions - return TextUtils.isEmpty(string1) ? "" : string1; - } - - return string1 + " • " + string2; - } - - @NonNull - public static File createAlbumArtFile() { - return new File(createAlbumArtDir(), String.valueOf(System.currentTimeMillis())); - } - - @NonNull - public static Intent createShareSongFileIntent(@NonNull final Song song, @NonNull Context context) { - try { - return new Intent() - .setAction(Intent.ACTION_SEND) - .putExtra(Intent.EXTRA_STREAM, FileProvider - .getUriForFile(context, context.getApplicationContext().getPackageName(), - new File(song.getData()))) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setType("audio/*"); - } catch (IllegalArgumentException e) { - // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... - e.printStackTrace(); - Toast.makeText(context, "Could not share this file, I'm aware of the issue.", Toast.LENGTH_SHORT).show(); - return new Intent(); - } - } - - public static void deleteAlbumArt(@NonNull Context context, int albumId) { - ContentResolver contentResolver = context.getContentResolver(); - Uri localUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); - contentResolver.notifyChange(localUri, null); - } - - public static void deleteTracks( - @NonNull final Activity activity, - @NonNull final List songs, - @Nullable final List safUris, - @Nullable final Runnable callback) { - final String[] projection = new String[]{ - BaseColumns._ID, MediaStore.MediaColumns.DATA - }; - - // Split the query into multiple batches, and merge the resulting cursors - int batchStart = 0; - int batchEnd = 0; - final int batchSize = 1000000 - / 10; // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID - final int songCount = songs.size(); - - while (batchEnd < songCount) { - batchStart = batchEnd; - - final StringBuilder selection = new StringBuilder(); - selection.append(BaseColumns._ID + " IN ("); - - for (int i = 0; (i < batchSize - 1) && (batchEnd < songCount - 1); i++, batchEnd++) { - selection.append(songs.get(batchEnd).getId()); - selection.append(","); - } - // The last element of a batch - selection.append(songs.get(batchEnd).getId()); - batchEnd++; - selection.append(")"); - - try { - final Cursor cursor = activity.getContentResolver().query( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), - null, null); - // TODO: At this point, there is no guarantee that the size of the cursor is the same as the size of the selection string. - // Despite that, the Step 3 assumes that the safUris elements are tracking closely the content of the cursor. - - if (cursor != null) { - // Step 1: Remove selected tracks from the current playlist, as well - // as from the album art cache - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - final int id = cursor.getInt(0); - final Song song = SongLoader.getSong(activity, id); - MusicPlayerRemote.removeFromQueue(song); - cursor.moveToNext(); - } - - // Step 2: Remove selected tracks from the database - activity.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - selection.toString(), null); - - // Step 3: Remove files from card - cursor.moveToFirst(); - int i = batchStart; - while (!cursor.isAfterLast()) { - final String name = cursor.getString(1); - final Uri safUri = safUris == null || safUris.size() <= i ? null : safUris.get(i); - SAFUtil.delete(activity, name, safUri); - i++; - cursor.moveToNext(); - } - cursor.close(); - } - } catch (SecurityException ignored) { - } - } - - activity.getContentResolver().notifyChange(Uri.parse("content://media"), null); - - activity.runOnUiThread(() -> { - Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songCount), Toast.LENGTH_SHORT) - .show(); - if (callback != null) { - callback.run(); - } - }); - } - - @NonNull - public static String getArtistInfoString(@NonNull final Context context, - @NonNull final Artist artist) { - int albumCount = artist.getAlbumCount(); - int songCount = artist.getSongCount(); - String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) - : context.getResources().getString(R.string.albums); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return albumCount + " " + albumString + " • " + songCount + " " + songString; - } - - //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. - //this method converts those values to normal tracknumbers - public static int getFixedTrackNumber(int trackNumberToFix) { - return trackNumberToFix % 1000; - } - - @Nullable - public static String getLyrics(@NonNull Song song) { - String lyrics = null; - - File file = new File(song.getData()); - - try { - lyrics = AudioFileIO.read(file).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); - } catch (Exception e) { - e.printStackTrace(); - } - - if (lyrics == null || lyrics.trim().isEmpty() || !AbsSynchronizedLyrics - .isSynchronized(lyrics)) { - File dir = file.getAbsoluteFile().getParentFile(); - - if (dir != null && dir.exists() && dir.isDirectory()) { - String format = ".*%s.*\\.(lrc|txt)"; - String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); - String songtitle = Pattern.quote(song.getTitle()); - - final ArrayList patterns = new ArrayList<>(); - patterns.add(Pattern.compile(String.format(format, filename), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - patterns.add(Pattern.compile(String.format(format, songtitle), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - - File[] files = dir.listFiles(f -> { - for (Pattern pattern : patterns) { - if (pattern.matcher(f.getName()).matches()) { - return true; - } - } - return false; - }); - - if (files != null && files.length > 0) { - for (File f : files) { - try { - String newLyrics = FileUtil.read(f); - if (newLyrics != null && !newLyrics.trim().isEmpty()) { - if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { - return newLyrics; - } - lyrics = newLyrics; - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - } - - return lyrics; - } - - @NonNull - public static Uri getMediaStoreAlbumCoverUri(int albumId) { - final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); - return ContentUris.withAppendedId(sArtworkUri, albumId); - } - - @NonNull - public static Playlist getPlaylist() { - return playlist; - } - - public static void setPlaylist(@NonNull Playlist playlist) { - MusicUtil.playlist = playlist; - } - - @NonNull - public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List songs) { - final long duration = getTotalDuration(songs); - - return MusicUtil.buildInfoString( - MusicUtil.getSongCountString(context, songs.size()), - MusicUtil.getReadableDurationString(duration) - ); - } - - public static String getReadableDurationString(long songDurationMillis) { - long minutes = (songDurationMillis / 1000) / 60; - long seconds = (songDurationMillis / 1000) % 60; - if (minutes < 60) { - return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds); - } else { - long hours = minutes / 60; - minutes = minutes % 60; - return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds); - } - } - - @NonNull - public static String getSectionName(@Nullable String musicMediaTitle) { - try { - if (TextUtils.isEmpty(musicMediaTitle)) { - return ""; - } - - musicMediaTitle = musicMediaTitle.trim().toLowerCase(); - if (musicMediaTitle.startsWith("the ")) { - musicMediaTitle = musicMediaTitle.substring(4); - } else if (musicMediaTitle.startsWith("a ")) { - musicMediaTitle = musicMediaTitle.substring(2); - } - if (musicMediaTitle.isEmpty()) { - return ""; - } - return musicMediaTitle.substring(0, 1).toUpperCase(); - } catch (Exception e) { - return ""; - } - } - - @NonNull - public static String getSongCountString(@NonNull final Context context, int songCount) { - final String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return songCount + " " + songString; - } - - public static Uri getSongFileUri(int songId) { - return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); - } - - public static long getTotalDuration(@NonNull List songs) { - long duration = 0; - for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).getDuration(); - } - return duration; - } - - @NonNull - public static String getYearString(int year) { - return year > 0 ? String.valueOf(year) : "-"; - } - - public static int indexOfSongInList(@NonNull List songs, int songId) { - for (int i = 0; i < songs.size(); i++) { - if (songs.get(i).getId() == songId) { - return i; - } - } - return -1; - } - - public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { - ContentResolver contentResolver = context.getContentResolver(); - - Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); - - ContentValues values = new ContentValues(); - values.put("album_id", albumId); - values.put("_data", path); - - contentResolver.insert(artworkUri, values); - contentResolver.notifyChange(artworkUri, null); - } - - public static boolean isArtistNameUnknown(@Nullable String artistName) { - if (TextUtils.isEmpty(artistName)) { - return false; - } - if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { - return true; - } - String tempName = artistName.trim().toLowerCase(); - return tempName.equals("unknown") || tempName.equals(""); - } - - public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { - return PlaylistsUtil - .doPlaylistContains(context, getFavoritesPlaylist(context).id, song.getId()); - } - - public static boolean isFavoritePlaylist(@NonNull final Context context, - @NonNull final Playlist playlist) { - return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); - } - - public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { - if (isFavorite(context, song)) { - PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id); - } else { - PlaylistsUtil.addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).id, - false); - } - context.sendBroadcast(new Intent(MusicService.FAVORITE_STATE_CHANGED)); - } - - @NonNull - @SuppressWarnings("ResultOfMethodCallIgnored") - private static File createAlbumArtDir() { - File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); - if (!albumArtDir.exists()) { - albumArtDir.mkdirs(); - try { - new File(albumArtDir, ".nomedia").createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return albumArtDir; - } - - private static Playlist getFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, context.getString(R.string.favorites)); - } - - private static Playlist getOrCreateFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, - PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt new file mode 100644 index 00000000..130cee69 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt @@ -0,0 +1,418 @@ +package code.name.monkey.retromusic.util + +import android.content.ContentUris +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Environment +import android.provider.BaseColumns +import android.provider.MediaStore +import android.text.TextUtils +import android.widget.Toast +import androidx.core.content.FileProvider +import androidx.fragment.app.FragmentActivity +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.model.Playlist +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics +import code.name.monkey.retromusic.repository.RealPlaylistRepository +import code.name.monkey.retromusic.repository.SongRepository +import code.name.monkey.retromusic.service.MusicService +import org.jaudiotagger.audio.AudioFileIO +import org.jaudiotagger.tag.FieldKey +import org.koin.core.KoinComponent +import org.koin.core.get +import java.io.File +import java.io.IOException +import java.util.* +import java.util.regex.Pattern + + +object MusicUtil : KoinComponent { + fun createShareSongFileIntent(song: Song, context: Context): Intent? { + return try { + Intent().setAction(Intent.ACTION_SEND).putExtra( + Intent.EXTRA_STREAM, + FileProvider.getUriForFile( + context, + context.applicationContext.packageName, + File(song.data) + ) + ).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("audio/*") + } catch (e: IllegalArgumentException) { + // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... + e.printStackTrace() + Toast.makeText( + context, + "Could not share this file, I'm aware of the issue.", + Toast.LENGTH_SHORT + ).show() + Intent() + } + } + + fun buildInfoString(string1: String?, string2: String?): String { + if (string1.isNullOrEmpty()) { + return if (string2.isNullOrEmpty()) "" else string2 + } + return if (string2.isNullOrEmpty()) if (string1.isNullOrEmpty()) "" else string1 else "$string1 • $string2" + } + + fun createAlbumArtFile(): File { + return File( + createAlbumArtDir(), + System.currentTimeMillis().toString() + ) + } + + private fun createAlbumArtDir(): File { + val albumArtDir = File(Environment.getExternalStorageDirectory(), "/albumthumbs/") + if (!albumArtDir.exists()) { + albumArtDir.mkdirs() + try { + File(albumArtDir, ".nomedia").createNewFile() + } catch (e: IOException) { + e.printStackTrace() + } + } + return albumArtDir + } + + fun deleteAlbumArt(context: Context, albumId: Int) { + val contentResolver = context.contentResolver + val localUri = Uri.parse("content://media/external/audio/albumart") + contentResolver.delete(ContentUris.withAppendedId(localUri, albumId.toLong()), null, null) + contentResolver.notifyChange(localUri, null) + } + + fun getArtistInfoString( + context: Context, + artist: Artist + ): String { + val albumCount = artist.albumCount + val songCount = artist.songCount + val albumString = + if (albumCount == 1) context.resources.getString(R.string.album) + else context.resources.getString(R.string.albums) + val songString = + if (songCount == 1) context.resources.getString(R.string.song) + else context.resources.getString(R.string.songs) + return "$albumCount $albumString • $songCount $songString" + } + + //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. + //this method converts those values to normal tracknumbers + fun getFixedTrackNumber(trackNumberToFix: Int): Int { + return trackNumberToFix % 1000 + } + + fun getLyrics(song: Song): String? { + var lyrics: String? = null + val file = File(song.data) + try { + lyrics = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS) + } catch (e: Exception) { + e.printStackTrace() + } + if (lyrics == null || lyrics.trim { it <= ' ' }.isEmpty() || !AbsSynchronizedLyrics + .isSynchronized(lyrics) + ) { + val dir = file.absoluteFile.parentFile + if (dir != null && dir.exists() && dir.isDirectory) { + val format = ".*%s.*\\.(lrc|txt)" + val filename = Pattern.quote( + FileUtil.stripExtension(file.name) + ) + val songtitle = Pattern.quote(song.title) + val patterns = + ArrayList() + patterns.add( + Pattern.compile( + String.format(format, filename), + Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE + ) + ) + patterns.add( + Pattern.compile( + String.format(format, songtitle), + Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE + ) + ) + val files = + dir.listFiles { f: File -> + for (pattern in patterns) { + if (pattern.matcher(f.name).matches()) { + return@listFiles true + } + } + false + } + if (files != null && files.size > 0) { + for (f in files) { + try { + val newLyrics = + FileUtil.read(f) + if (newLyrics != null && !newLyrics.trim { it <= ' ' }.isEmpty()) { + if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { + return newLyrics + } + lyrics = newLyrics + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + } + return lyrics + } + + fun getMediaStoreAlbumCoverUri(albumId: Int): Uri { + val sArtworkUri = + Uri.parse("content://media/external/audio/albumart") + return ContentUris.withAppendedId(sArtworkUri, albumId.toLong()) + } + + + fun getPlaylistInfoString( + context: Context, + songs: List + ): String { + val duration = getTotalDuration(songs) + return buildInfoString( + getSongCountString(context, songs.size), + getReadableDurationString(duration) + ) + } + + fun getReadableDurationString(songDurationMillis: Long): String? { + var minutes = songDurationMillis / 1000 / 60 + val seconds = songDurationMillis / 1000 % 60 + return if (minutes < 60) { + String.format( + Locale.getDefault(), + "%02d:%02d", + minutes, + seconds + ) + } else { + val hours = minutes / 60 + minutes = minutes % 60 + String.format( + Locale.getDefault(), + "%02d:%02d:%02d", + hours, + minutes, + seconds + ) + } + } + + fun getSectionName(musicMediaTitle: String?): String { + var musicMediaTitle = musicMediaTitle + return try { + if (TextUtils.isEmpty(musicMediaTitle)) { + return "" + } + musicMediaTitle = musicMediaTitle!!.trim { it <= ' ' }.toLowerCase() + if (musicMediaTitle.startsWith("the ")) { + musicMediaTitle = musicMediaTitle.substring(4) + } else if (musicMediaTitle.startsWith("a ")) { + musicMediaTitle = musicMediaTitle.substring(2) + } + if (musicMediaTitle.isEmpty()) { + "" + } else musicMediaTitle.substring(0, 1).toUpperCase() + } catch (e: Exception) { + "" + } + } + + fun getSongCountString(context: Context, songCount: Int): String { + val songString = if (songCount == 1) context.resources + .getString(R.string.song) else context.resources.getString(R.string.songs) + return "$songCount $songString" + } + + fun getSongFileUri(songId: Int): Uri { + return ContentUris.withAppendedId( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + songId.toLong() + ) + } + + fun getTotalDuration(songs: List): Long { + var duration: Long = 0 + for (i in songs.indices) { + duration += songs[i].duration + } + return duration + } + + fun getYearString(year: Int): String { + return if (year > 0) year.toString() else "-" + } + + fun indexOfSongInList(songs: List, songId: Int): Int { + for (i in songs.indices) { + if (songs[i].id == songId) { + return i + } + } + return -1 + } + + fun insertAlbumArt( + context: Context, + albumId: Int, + path: String? + ) { + val contentResolver = context.contentResolver + val artworkUri = + Uri.parse("content://media/external/audio/albumart") + contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId.toLong()), null, null) + val values = ContentValues() + values.put("album_id", albumId) + values.put("_data", path) + contentResolver.insert(artworkUri, values) + contentResolver.notifyChange(artworkUri, null) + } + + fun isArtistNameUnknown(artistName: String?): Boolean { + if (TextUtils.isEmpty(artistName)) { + return false + } + if (artistName == Artist.UNKNOWN_ARTIST_DISPLAY_NAME) { + return true + } + val tempName = artistName!!.trim { it <= ' ' }.toLowerCase() + return tempName == "unknown" || tempName == "" + } + + fun isFavorite(context: Context, song: Song): Boolean { + return PlaylistsUtil + .doPlaylistContains(context, getFavoritesPlaylist(context).id.toLong(), song.id) + } + + fun isFavoritePlaylist( + context: Context, + playlist: Playlist + ): Boolean { + return playlist.name != null && playlist.name == context.getString(R.string.favorites) + } + + fun toggleFavorite(context: Context, song: Song) { + if (isFavorite(context, song)) { + PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id) + } else { + PlaylistsUtil.addToPlaylist( + context, song, getOrCreateFavoritesPlaylist(context).id, + false + ) + } + context.sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED)) + } + + private fun getFavoritesPlaylist(context: Context): Playlist { + return RealPlaylistRepository(context.contentResolver).playlist(context.getString(R.string.favorites)) + } + + private fun getOrCreateFavoritesPlaylist(context: Context): Playlist { + return RealPlaylistRepository(context.contentResolver).playlist( + PlaylistsUtil.createPlaylist( + context, + context.getString(R.string.favorites) + ) + ) + } + + fun deleteTracks( + activity: FragmentActivity, + songs: List, + safUris: List?, + callback: Runnable? + ) { + val songRepository: SongRepository = get() + val projection = arrayOf( + BaseColumns._ID, MediaStore.MediaColumns.DATA + ) + // Split the query into multiple batches, and merge the resulting cursors + var batchStart = 0 + var batchEnd = 0 + val batchSize = + 1000000 / 10 // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID + val songCount = songs.size + + while (batchEnd < songCount) { + batchStart = batchEnd + + val selection = StringBuilder() + selection.append(BaseColumns._ID + " IN (") + + var i = 0 + while (i < batchSize - 1 && batchEnd < songCount - 1) { + selection.append(songs[batchEnd].id) + selection.append(",") + i++ + batchEnd++ + } + // The last element of a batch + // The last element of a batch + selection.append(songs[batchEnd].id) + batchEnd++ + selection.append(")") + + try { + val cursor = activity.contentResolver.query( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), + null, null + ) + if (cursor != null) { + // Step 1: Remove selected tracks from the current playlist, as well + // as from the album art cache + cursor.moveToFirst() + while (!cursor.isAfterLast) { + val id = cursor.getInt(0) + val song: Song = songRepository.song(id) + removeFromQueue(song) + cursor.moveToNext() + } + + // Step 2: Remove selected tracks from the database + activity.contentResolver.delete( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + selection.toString(), null + ) + // Step 3: Remove files from card + cursor.moveToFirst() + var index = batchStart + while (!cursor.isAfterLast) { + val name = cursor.getString(1) + val safUri = + if (safUris == null || safUris.size <= index) null else safUris[index] + SAFUtil.delete(activity, name, safUri) + index++ + cursor.moveToNext() + } + cursor.close() + } + } catch (ignored: SecurityException) { + + } + activity.contentResolver.notifyChange(Uri.parse("content://media"), null) + activity.runOnUiThread { + Toast.makeText( + activity, + activity.getString(R.string.deleted_x_songs, songCount), + Toast.LENGTH_SHORT + ) + .show() + callback?.run() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java index b0edd623..84b596f5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java @@ -87,7 +87,7 @@ public class PlaylistsUtil { final StringBuilder selection = new StringBuilder(); selection.append(MediaStore.Audio.Playlists._ID + " IN ("); for (int i = 0; i < playlists.size(); i++) { - selection.append(playlists.get(i).id); + selection.append(playlists.get(i).getId()); if (i < playlists.size() - 1) { selection.append(","); }