parent
ff20b3a052
commit
e159b1a32a
|
@ -1,24 +1,70 @@
|
||||||
package code.name.monkey.retromusic
|
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.albums.AlbumDetailsViewModel
|
||||||
import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
|
import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
|
||||||
import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
|
import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
|
||||||
import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
|
import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
|
||||||
import code.name.monkey.retromusic.fragments.search.SearchViewModel
|
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.Genre
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.network.networkModule
|
import code.name.monkey.retromusic.network.networkModule
|
||||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
import code.name.monkey.retromusic.repository.*
|
||||||
import org.eclipse.egit.github.core.Repository
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.bind
|
import org.koin.dsl.bind
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
private val dataModule = module {
|
private val dataModule = module {
|
||||||
single {
|
single {
|
||||||
RepositoryImpl(get(), get())
|
RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||||
} bind Repository::class
|
} 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 {
|
private val viewModules = module {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.retromusic.*
|
import code.name.monkey.retromusic.*
|
||||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
import code.name.monkey.retromusic.extensions.findNavController
|
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.playFromUri
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode
|
||||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
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.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.service.MusicService
|
||||||
import code.name.monkey.retromusic.util.AppRater.appLaunched
|
import code.name.monkey.retromusic.util.AppRater.appLaunched
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -33,8 +35,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
||||||
const val APP_UPDATE_REQUEST_CODE = 9002
|
const val APP_UPDATE_REQUEST_CODE = 9002
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val repository by inject<Repository>()
|
||||||
private val libraryViewModel: LibraryViewModel by inject()
|
private val libraryViewModel by inject<LibraryViewModel>()
|
||||||
|
|
||||||
private var blockRequestPermissions = false
|
private var blockRequestPermissions = false
|
||||||
|
|
||||||
|
@ -133,18 +135,22 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
|
||||||
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
||||||
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
|
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
|
||||||
if (id >= 0) {
|
if (id >= 0) {
|
||||||
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
val position = intent.getIntExtra("position", 0)
|
val position = intent.getIntExtra("position", 0)
|
||||||
openQueue(getAlbum(this, id).songs!!, position, true)
|
openQueue(repository.albumById(id).songs!!, position, true)
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
||||||
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
|
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
|
||||||
if (id >= 0) {
|
if (id >= 0) {
|
||||||
|
lifecycleScope.launch {
|
||||||
val position = intent.getIntExtra("position", 0)
|
val position = intent.getIntExtra("position", 0)
|
||||||
openQueue(getArtist(this, id).songs, position, true)
|
openQueue(repository.artistById(id).songs, position, true)
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
setIntent(Intent())
|
setIntent(Intent())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.OvershootInterpolator
|
import android.view.animation.OvershootInterpolator
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
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.R.drawable
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
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.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.SAFUtil
|
import code.name.monkey.retromusic.util.SAFUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
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.AudioFile
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
|
val repository by inject<Repository>()
|
||||||
|
|
||||||
|
lateinit var saveFab: MaterialButton
|
||||||
protected var id: Int = 0
|
protected var id: Int = 0
|
||||||
private set
|
private set
|
||||||
private var paletteColorPrimary: Int = 0
|
private var paletteColorPrimary: Int = 0
|
||||||
private var isInNoImageMode: Boolean = false
|
private var isInNoImageMode: Boolean = false
|
||||||
private var songPaths: List<String>? = null
|
private var songPaths: List<String>? = null
|
||||||
lateinit var saveFab: MaterialButton
|
|
||||||
|
|
||||||
private var savedSongPaths: List<String>? = null
|
private var savedSongPaths: List<String>? = null
|
||||||
private val currentSongPath: String? = null
|
private val currentSongPath: String? = null
|
||||||
private var savedTags: Map<FieldKey, String>? = null
|
private var savedTags: Map<FieldKey, String>? = null
|
||||||
|
@ -172,33 +175,27 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(contentViewLayout)
|
setContentView(contentViewLayout)
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
|
||||||
saveFab = findViewById(R.id.saveTags)
|
saveFab = findViewById(R.id.saveTags)
|
||||||
getIntentExtras()
|
getIntentExtras()
|
||||||
|
|
||||||
|
lifecycleScope.launchWhenCreated {
|
||||||
songPaths = getSongPaths()
|
songPaths = getSongPaths()
|
||||||
if (songPaths!!.isEmpty()) {
|
if (songPaths!!.isEmpty()) {
|
||||||
finish()
|
finish()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setUpViews()
|
setUpViews()
|
||||||
|
|
||||||
setStatusbarColorAuto()
|
|
||||||
setNavigationbarColorAuto()
|
|
||||||
setTaskDescriptionColorAuto()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpViews() {
|
private fun setUpViews() {
|
||||||
setUpScrollView()
|
|
||||||
setUpFab()
|
setUpFab()
|
||||||
setUpImageView()
|
setUpImageView()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpScrollView() {
|
|
||||||
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var items: List<String>
|
private lateinit var items: List<String>
|
||||||
|
|
||||||
private fun setUpImageView() {
|
private fun setUpImageView() {
|
||||||
|
@ -261,7 +258,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun getSongPaths(): List<String>
|
protected abstract suspend fun getSongPaths(): List<String>
|
||||||
|
|
||||||
protected fun searchWebFor(vararg keys: String) {
|
protected fun searchWebFor(vararg keys: String) {
|
||||||
val stringBuilder = StringBuilder()
|
val stringBuilder = StringBuilder()
|
||||||
|
@ -336,7 +333,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
hideFab()
|
hideFab()
|
||||||
|
|
||||||
savedSongPaths = getSongPaths()
|
savedSongPaths = songPaths
|
||||||
savedTags = fieldKeyValueMap
|
savedTags = fieldKeyValueMap
|
||||||
savedArtworkInfo = artworkInfo
|
savedArtworkInfo = artworkInfo
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.loaders.AlbumLoader
|
|
||||||
import code.name.monkey.retromusic.util.ImageUtil
|
import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||||
|
@ -31,6 +30,7 @@ import org.jaudiotagger.tag.FieldKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
|
|
||||||
override val contentViewLayout: Int
|
override val contentViewLayout: Int
|
||||||
get() = R.layout.activity_album_tag_editor
|
get() = R.layout.activity_album_tag_editor
|
||||||
|
|
||||||
|
@ -162,13 +162,13 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
|
|
||||||
writeValuesToFiles(
|
writeValuesToFiles(
|
||||||
fieldKeyValueMap,
|
fieldKeyValueMap,
|
||||||
if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null)
|
if (deleteAlbumArt) ArtworkInfo(id, null)
|
||||||
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
|
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSongPaths(): List<String> {
|
override suspend fun getSongPaths(): List<String> {
|
||||||
val songs = AlbumLoader.getAlbum(this, id).songs
|
val songs = repository.albumById(id).songs
|
||||||
val paths = ArrayList<String>(songs!!.size)
|
val paths = ArrayList<String>(songs!!.size)
|
||||||
for (song in songs) {
|
for (song in songs) {
|
||||||
paths.add(song.data)
|
paths.add(song.data)
|
||||||
|
|
|
@ -8,9 +8,10 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.loaders.SongLoader
|
import code.name.monkey.retromusic.repository.SongRepository
|
||||||
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
|
@ -18,6 +19,8 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
override val contentViewLayout: Int
|
override val contentViewLayout: Int
|
||||||
get() = R.layout.activity_song_tag_editor
|
get() = R.layout.activity_song_tag_editor
|
||||||
|
|
||||||
|
private val songRepository by inject<SongRepository>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -85,9 +88,9 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
writeValuesToFiles(fieldKeyValueMap, null)
|
writeValuesToFiles(fieldKeyValueMap, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSongPaths(): List<String> {
|
override suspend fun getSongPaths(): List<String> {
|
||||||
val paths = ArrayList<String>(1)
|
val paths = ArrayList<String>(1)
|
||||||
paths.add(SongLoader.getSong(this, id).data)
|
paths.add(songRepository.song(id).data)
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class WriteTagsAsyncTask extends DialogAsyncTask<WriteTagsAsyncTask.Loadi
|
||||||
File albumArtFile = null;
|
File albumArtFile = null;
|
||||||
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
|
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
|
||||||
try {
|
try {
|
||||||
albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile();
|
albumArtFile = MusicUtil.INSTANCE.createAlbumArtFile().getCanonicalFile();
|
||||||
info.artworkInfo.getArtwork()
|
info.artworkInfo.getArtwork()
|
||||||
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||||
|
@ -120,9 +120,9 @@ public class WriteTagsAsyncTask extends DialogAsyncTask<WriteTagsAsyncTask.Loadi
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
if (wroteArtwork) {
|
if (wroteArtwork) {
|
||||||
MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
|
MusicUtil.INSTANCE.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
|
||||||
} else if (deletedArtwork) {
|
} else if (deletedArtwork) {
|
||||||
MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
|
MusicUtil.INSTANCE.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
|
||||||
import code.name.monkey.retromusic.model.*
|
import code.name.monkey.retromusic.model.*
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||||
|
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class SearchAdapter(
|
||||||
private fun getSongs(playlist: Playlist): java.util.ArrayList<Song> {
|
private fun getSongs(playlist: Playlist): java.util.ArrayList<Song> {
|
||||||
val songs = java.util.ArrayList<Song>()
|
val songs = java.util.ArrayList<Song>()
|
||||||
if (playlist is AbsSmartPlaylist) {
|
if (playlist is AbsSmartPlaylist) {
|
||||||
songs.addAll(playlist.getSongs(activity))
|
songs.addAll(playlist.getSongs())
|
||||||
} else {
|
} else {
|
||||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
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.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
@ -35,7 +36,6 @@ import com.bumptech.glide.signature.MediaStoreSignature
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.util.*
|
|
||||||
import kotlin.math.log10
|
import kotlin.math.log10
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
@ -135,9 +135,9 @@ class SongFileAdapter(
|
||||||
return getFileTitle(`object`)
|
return getFileTitle(`object`)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<File>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<File>) {
|
||||||
if (callbacks == null) return
|
if (callbacks == null) return
|
||||||
callbacks.onMultipleItemAction(menuItem, selection)
|
callbacks.onMultipleItemAction(menuItem, selection as ArrayList<File>)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPopupText(position: Int): String {
|
override fun getPopupText(position: Int): String {
|
||||||
|
@ -148,13 +148,6 @@ class SongFileAdapter(
|
||||||
return MusicUtil.getSectionName(dataSet[position].name)
|
return MusicUtil.getSectionName(dataSet[position].name)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Callbacks {
|
|
||||||
fun onFileSelected(file: File)
|
|
||||||
|
|
||||||
fun onFileMenuClicked(file: File, view: View)
|
|
||||||
|
|
||||||
fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
|
|
||||||
|
|
|
@ -129,12 +129,12 @@ open class AlbumAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(
|
override fun onMultipleItemAction(
|
||||||
menuItem: MenuItem, selection: ArrayList<Album>
|
menuItem: MenuItem, selection: List<Album>
|
||||||
) {
|
) {
|
||||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongList(albums: List<Album>): ArrayList<Song> {
|
private fun getSongList(albums: List<Album>): List<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
for (album in albums) {
|
for (album in albums) {
|
||||||
songs.addAll(album.songs!!)
|
songs.addAll(album.songs!!)
|
||||||
|
@ -156,7 +156,6 @@ open class AlbumAdapter(
|
||||||
dataSet[position].year
|
dataSet[position].year
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MusicUtil.getSectionName(sectionName)
|
return MusicUtil.getSectionName(sectionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,12 +107,12 @@ class ArtistAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(
|
override fun onMultipleItemAction(
|
||||||
menuItem: MenuItem, selection: ArrayList<Artist>
|
menuItem: MenuItem, selection: List<Artist>
|
||||||
) {
|
) {
|
||||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongList(artists: List<Artist>): ArrayList<Song> {
|
private fun getSongList(artists: List<Artist>): List<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
for (artist in artists) {
|
for (artist in artists) {
|
||||||
songs.addAll(artist.songs) // maybe async in future?
|
songs.addAll(artist.songs) // maybe async in future?
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.afollestad.materialcab.MaterialCab;
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.interfaces.CabHolder;
|
import code.name.monkey.retromusic.interfaces.CabHolder;
|
||||||
|
@ -24,7 +25,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
|
||||||
private final CabHolder cabHolder;
|
private final CabHolder cabHolder;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private MaterialCab cab;
|
private MaterialCab cab;
|
||||||
private ArrayList<I> checked;
|
private List<I> checked;
|
||||||
private int menuRes;
|
private int menuRes;
|
||||||
|
|
||||||
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
|
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
|
||||||
|
@ -86,7 +87,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
|
||||||
return cab != null && cab.isActive();
|
return cab != null && cab.isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection);
|
protected abstract void onMultipleItemAction(MenuItem menuItem, List<I> selection);
|
||||||
|
|
||||||
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
|
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
|
||||||
this.menuRes = menuRes;
|
this.menuRes = menuRes;
|
||||||
|
|
|
@ -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.PlaylistMenuHelper
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
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.AbsCustomPlaylist
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
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.AutoGeneratedPlaylistBitmap
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
|
@ -122,7 +122,7 @@ class PlaylistAdapter(
|
||||||
return playlist.name
|
return playlist.name
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Playlist>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Playlist>) {
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
else -> SongsMenuHelper.handleMenuClick(
|
else -> SongsMenuHelper.handleMenuClick(
|
||||||
activity,
|
activity,
|
||||||
|
@ -132,11 +132,11 @@ class PlaylistAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongList(playlists: List<Playlist>): ArrayList<Song> {
|
private fun getSongList(playlists: List<Playlist>): List<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
for (playlist in playlists) {
|
for (playlist in playlists) {
|
||||||
if (playlist is AbsCustomPlaylist) {
|
if (playlist is AbsCustomPlaylist) {
|
||||||
songs.addAll(playlist.getSongs(activity))
|
songs.addAll(playlist.songs())
|
||||||
} else {
|
} else {
|
||||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||||
}
|
}
|
||||||
|
@ -144,12 +144,12 @@ class PlaylistAdapter(
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongs(playlist: Playlist): ArrayList<Song> {
|
private fun getSongs(playlist: Playlist): List<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
if (playlist is AbsSmartPlaylist) {
|
if (playlist is AbsSmartPlaylist) {
|
||||||
songs.addAll(playlist.getSongs(activity))
|
songs.addAll(playlist.songs())
|
||||||
} else {
|
} else {
|
||||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
songs.addAll(playlist.getSongs())
|
||||||
}
|
}
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ class OrderablePlaylistSongAdapter(
|
||||||
return long
|
return long
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.action_remove_from_playlist -> {
|
R.id.action_remove_from_playlist -> {
|
||||||
RemoveFromPlaylistDialog.create(selection as ArrayList<PlaylistSong>)
|
RemoveFromPlaylistDialog.create(selection as ArrayList<PlaylistSong>)
|
||||||
|
|
|
@ -133,7 +133,7 @@ open class SongAdapter(
|
||||||
return song.title
|
return song.title
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
|
||||||
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId)
|
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
|
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.ShuffleAllPlaylist
|
||||||
|
import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
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 -> {
|
SHORTCUT_TYPE_TOP_TRACKS -> {
|
||||||
startServiceWithPlaylist(
|
startServiceWithPlaylist(
|
||||||
SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext)
|
SHUFFLE_MODE_NONE, TopTracksPlaylist(applicationContext)
|
||||||
)
|
)
|
||||||
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id)
|
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,30 +17,37 @@ package code.name.monkey.retromusic.dialogs
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.retromusic.EXTRA_SONG
|
import code.name.monkey.retromusic.EXTRA_SONG
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.extensions.colorButtons
|
import code.name.monkey.retromusic.extensions.colorButtons
|
||||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||||
import code.name.monkey.retromusic.extensions.materialDialog
|
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.model.Song
|
||||||
|
import code.name.monkey.retromusic.repository.PlaylistRepository
|
||||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
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() {
|
class AddToPlaylistDialog : DialogFragment() {
|
||||||
|
private val playlistRepository by inject<PlaylistRepository>()
|
||||||
override fun onCreateDialog(
|
override fun onCreateDialog(
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): Dialog {
|
): Dialog {
|
||||||
val playlists = PlaylistLoader.getAllPlaylists(requireContext())
|
val materialDialog = materialDialog(R.string.add_playlist_title)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val playlists = playlistRepository.playlists()
|
||||||
val playlistNames = mutableListOf<String>()
|
val playlistNames = mutableListOf<String>()
|
||||||
playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist))
|
playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist))
|
||||||
for (p in playlists) {
|
for (p in playlists) {
|
||||||
playlistNames.add(p.name)
|
playlistNames.add(p.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return materialDialog(R.string.add_playlist_title)
|
withContext(Dispatchers.Main) {
|
||||||
.setItems(playlistNames.toTypedArray()) { _, which ->
|
materialDialog.setItems(playlistNames.toTypedArray()) { _, which ->
|
||||||
val songs = extraNotNull<ArrayList<Song>>(EXTRA_SONG).value
|
val songs = extraNotNull<ArrayList<Song>>(EXTRA_SONG).value
|
||||||
if (which == 0) {
|
if (which == 0) {
|
||||||
CreatePlaylistDialog.create(songs)
|
CreatePlaylistDialog.create(songs)
|
||||||
|
@ -55,6 +62,10 @@ class AddToPlaylistDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return materialDialog(R.string.add_playlist_title)
|
||||||
.create().colorButtons()
|
.create().colorButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,9 @@ class DeleteSongsDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) {
|
fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) {
|
||||||
MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() }
|
MusicUtil.deleteTracks(requireActivity(), songs, safUris, Runnable {
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -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.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Song
|
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.android.synthetic.main.fragment_playlist_detail.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
@ -28,7 +28,7 @@ import org.koin.android.ext.android.inject
|
||||||
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
|
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
|
||||||
ArtistClickListener, AlbumClickListener {
|
ArtistClickListener, AlbumClickListener {
|
||||||
private val args by navArgs<DetailListFragmentArgs>()
|
private val args by navArgs<DetailListFragmentArgs>()
|
||||||
private val repository by inject<RepositoryImpl>()
|
private val repository by inject<RealRepository>()
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
|
@ -7,14 +7,14 @@ import androidx.lifecycle.viewModelScope
|
||||||
import code.name.monkey.retromusic.fragments.ReloadType.*
|
import code.name.monkey.retromusic.fragments.ReloadType.*
|
||||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.*
|
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.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class LibraryViewModel(
|
class LibraryViewModel(
|
||||||
private val repository: RepositoryImpl
|
private val realRepository: RealRepository
|
||||||
) : ViewModel(), MusicServiceEventListener {
|
) : ViewModel(), MusicServiceEventListener {
|
||||||
|
|
||||||
private val paletteColor = MutableLiveData<Int>()
|
private val paletteColor = MutableLiveData<Int>()
|
||||||
|
@ -49,29 +49,29 @@ class LibraryViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadHome: Deferred<List<Home>>
|
private val loadHome: Deferred<List<Home>>
|
||||||
get() = viewModelScope.async { repository.homeSections() }
|
get() = viewModelScope.async { realRepository.homeSections() }
|
||||||
|
|
||||||
private val loadSongs: Deferred<List<Song>>
|
private val loadSongs: Deferred<List<Song>>
|
||||||
get() = viewModelScope.async(IO) { repository.allSongs() }
|
get() = viewModelScope.async(IO) { realRepository.allSongs() }
|
||||||
|
|
||||||
private val loadAlbums: Deferred<List<Album>>
|
private val loadAlbums: Deferred<List<Album>>
|
||||||
get() = viewModelScope.async(IO) {
|
get() = viewModelScope.async(IO) {
|
||||||
repository.allAlbums()
|
realRepository.allAlbums()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadArtists: Deferred<List<Artist>>
|
private val loadArtists: Deferred<List<Artist>>
|
||||||
get() = viewModelScope.async(IO) {
|
get() = viewModelScope.async(IO) {
|
||||||
repository.allArtists()
|
realRepository.allArtists()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadPlaylists: Deferred<List<Playlist>>
|
private val loadPlaylists: Deferred<List<Playlist>>
|
||||||
get() = viewModelScope.async(IO) {
|
get() = viewModelScope.async(IO) {
|
||||||
repository.allPlaylists()
|
realRepository.allPlaylists()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadGenres: Deferred<List<Genre>>
|
private val loadGenres: Deferred<List<Genre>>
|
||||||
get() = viewModelScope.async(IO) {
|
get() = viewModelScope.async(IO) {
|
||||||
repository.allGenres()
|
realRepository.allGenres()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,14 @@ import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.network.model.LastFmAlbum
|
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.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class AlbumDetailsViewModel(
|
class AlbumDetailsViewModel(
|
||||||
private val repository: RepositoryImpl,
|
private val realRepository: RealRepository,
|
||||||
private val albumId: Int
|
private val albumId: Int
|
||||||
) : ViewModel(), MusicServiceEventListener {
|
) : ViewModel(), MusicServiceEventListener {
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ class AlbumDetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) {
|
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)
|
_lastFmAlbum.postValue(lastFmAlbum)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) {
|
fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
val artist = repository.artistById(artistId)
|
val artist = realRepository.artistById(artistId)
|
||||||
_artist.postValue(artist)
|
_artist.postValue(artist)
|
||||||
|
|
||||||
artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
|
artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
|
||||||
|
@ -54,7 +54,7 @@ class AlbumDetailsViewModel(
|
||||||
|
|
||||||
private val loadAlbumAsync: Deferred<Album?>
|
private val loadAlbumAsync: Deferred<Album?>
|
||||||
get() = viewModelScope.async(Dispatchers.IO) {
|
get() = viewModelScope.async(Dispatchers.IO) {
|
||||||
repository.albumById(albumId)
|
realRepository.albumById(albumId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaStoreChanged() {
|
override fun onMediaStoreChanged() {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Artist
|
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 code.name.monkey.retromusic.network.model.LastFmArtist
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -14,13 +14,13 @@ import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ArtistDetailsViewModel(
|
class ArtistDetailsViewModel(
|
||||||
private val repository: RepositoryImpl,
|
private val realRepository: RealRepository,
|
||||||
private val artistId: Int
|
private val artistId: Int
|
||||||
) : ViewModel(), MusicServiceEventListener {
|
) : ViewModel(), MusicServiceEventListener {
|
||||||
|
|
||||||
private val loadArtistDetailsAsync: Deferred<Artist?>
|
private val loadArtistDetailsAsync: Deferred<Artist?>
|
||||||
get() = viewModelScope.async(Dispatchers.IO) {
|
get() = viewModelScope.async(Dispatchers.IO) {
|
||||||
repository.artistById(artistId)
|
realRepository.artistById(artistId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _artist = MutableLiveData<Artist>()
|
private val _artist = MutableLiveData<Artist>()
|
||||||
|
@ -40,7 +40,7 @@ class ArtistDetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch {
|
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)
|
_lastFmArtist.postValue(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.SongMenuHelper;
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper;
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper;
|
||||||
import code.name.monkey.retromusic.interfaces.CabHolder;
|
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.interfaces.MainActivityFragmentCallbacks;
|
||||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||||
|
@ -76,7 +77,9 @@ import me.zhanghai.android.fastscroll.FastScroller;
|
||||||
|
|
||||||
public class FoldersFragment extends AbsMainActivityFragment implements
|
public class FoldersFragment extends AbsMainActivityFragment implements
|
||||||
MainActivityFragmentCallbacks,
|
MainActivityFragmentCallbacks,
|
||||||
CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks,
|
CabHolder,
|
||||||
|
BreadCrumbLayout.SelectionCallback,
|
||||||
|
Callbacks,
|
||||||
LoaderManager.LoaderCallbacks<List<File>> {
|
LoaderManager.LoaderCallbacks<List<File>> {
|
||||||
|
|
||||||
public static final String TAG = FoldersFragment.class.getSimpleName();
|
public static final String TAG = FoldersFragment.class.getSimpleName();
|
||||||
|
@ -619,7 +622,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ListSongsAsyncTask
|
private static class ListSongsAsyncTask
|
||||||
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
|
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, List<Song>> {
|
||||||
|
|
||||||
private final Object extra;
|
private final Object extra;
|
||||||
private WeakReference<OnSongsListedCallback> callbackWeakReference;
|
private WeakReference<OnSongsListedCallback> callbackWeakReference;
|
||||||
|
@ -633,7 +636,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ArrayList<Song> doInBackground(LoadingInfo... params) {
|
protected List<Song> doInBackground(LoadingInfo... params) {
|
||||||
try {
|
try {
|
||||||
LoadingInfo info = params[0];
|
LoadingInfo info = params[0];
|
||||||
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
|
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
|
||||||
|
@ -659,7 +662,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(ArrayList<Song> songs) {
|
protected void onPostExecute(List<Song> songs) {
|
||||||
super.onPostExecute(songs);
|
super.onPostExecute(songs);
|
||||||
OnSongsListedCallback callback = checkCallbackReference();
|
OnSongsListedCallback callback = checkCallbackReference();
|
||||||
if (songs != null && callback != null) {
|
if (songs != null && callback != null) {
|
||||||
|
@ -692,7 +695,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
|
||||||
|
|
||||||
public interface OnSongsListedCallback {
|
public interface OnSongsListedCallback {
|
||||||
|
|
||||||
void onSongsListed(@NonNull ArrayList<Song> songs, Object extra);
|
void onSongsListed(@NonNull List<Song> songs, Object extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class LoadingInfo {
|
static class LoadingInfo {
|
||||||
|
|
|
@ -7,13 +7,13 @@ import androidx.lifecycle.viewModelScope
|
||||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class GenreDetailsViewModel(
|
class GenreDetailsViewModel(
|
||||||
private val repository: RepositoryImpl,
|
private val realRepository: RealRepository,
|
||||||
private val genre: Genre
|
private val genre: Genre
|
||||||
) : ViewModel(), MusicServiceEventListener {
|
) : ViewModel(), MusicServiceEventListener {
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class GenreDetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadGenreSongs(genre: Genre) = viewModelScope.launch {
|
private fun loadGenreSongs(genre: Genre) = viewModelScope.launch {
|
||||||
val songs = repository.getGenre(genre.id)
|
val songs = realRepository.getGenre(genre.id)
|
||||||
withContext(Main) { _playListSongs.postValue(songs) }
|
withContext(Main) { _playListSongs.postValue(songs) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.util.DisplayMetrics
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
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.ProfileBannerGlideRequest
|
||||||
import code.name.monkey.retromusic.glide.UserProfileGlideRequest
|
import code.name.monkey.retromusic.glide.UserProfileGlideRequest
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
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.HistoryPlaylist
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
|
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.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.android.synthetic.main.abs_playlists.*
|
import kotlinx.android.synthetic.main.abs_playlists.*
|
||||||
import kotlinx.android.synthetic.main.fragment_banner_home.*
|
import kotlinx.android.synthetic.main.fragment_banner_home.*
|
||||||
import kotlinx.android.synthetic.main.home_content.*
|
import kotlinx.android.synthetic.main.home_content.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
|
|
||||||
class HomeFragment :
|
class HomeFragment :
|
||||||
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
|
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
|
||||||
|
|
||||||
|
private val repository by inject<Repository>()
|
||||||
private val libraryViewModel: LibraryViewModel by sharedViewModel()
|
private val libraryViewModel: LibraryViewModel by sharedViewModel()
|
||||||
|
|
||||||
private val displayMetrics: DisplayMetrics
|
private val displayMetrics: DisplayMetrics
|
||||||
|
@ -78,12 +82,17 @@ class HomeFragment :
|
||||||
topPlayed.setOnClickListener {
|
topPlayed.setOnClickListener {
|
||||||
findActivityNavController(R.id.fragment_container).navigate(
|
findActivityNavController(R.id.fragment_container).navigate(
|
||||||
R.id.playlistDetailsFragment,
|
R.id.playlistDetailsFragment,
|
||||||
bundleOf(EXTRA_PLAYLIST to MyTopTracksPlaylist(requireActivity()))
|
bundleOf(EXTRA_PLAYLIST to TopTracksPlaylist(requireActivity()))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
actionShuffle.setOnClickListener {
|
actionShuffle.setOnClickListener {
|
||||||
MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(requireActivity()), true)
|
lifecycleScope.launch {
|
||||||
|
MusicPlayerRemote.openAndShuffleQueue(
|
||||||
|
repository.allSongs(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
history.setOnClickListener {
|
history.setOnClickListener {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.widget.FrameLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||||
import code.name.monkey.retromusic.R
|
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.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
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.Song
|
||||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||||
|
import code.name.monkey.retromusic.repository.ArtistRepository
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.android.synthetic.main.fragment_full.*
|
import kotlinx.android.synthetic.main.fragment_full.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
|
class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
|
||||||
MusicProgressViewUpdateHelper.Callback {
|
MusicProgressViewUpdateHelper.Callback {
|
||||||
|
private val artistRepository by inject<ArtistRepository>()
|
||||||
private lateinit var lyricsLayout: FrameLayout
|
private lateinit var lyricsLayout: FrameLayout
|
||||||
private lateinit var lyricsLine1: TextView
|
private lateinit var lyricsLine1: TextView
|
||||||
private lateinit var lyricsLine2: TextView
|
private lateinit var lyricsLine2: TextView
|
||||||
|
@ -220,9 +222,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateArtistImage() {
|
private fun updateArtistImage() {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
lifecycleScope.launch {
|
||||||
val artist =
|
val artist = artistRepository.artist(MusicPlayerRemote.currentSong.artistId)
|
||||||
ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId)
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
||||||
.generatePalette(requireContext())
|
.generatePalette(requireContext())
|
||||||
|
|
|
@ -6,22 +6,20 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import code.name.monkey.retromusic.App
|
import code.name.monkey.retromusic.App
|
||||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
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.AbsCustomPlaylist
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.model.Song
|
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 code.name.monkey.retromusic.util.PlaylistsUtil
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class PlaylistDetailsViewModel(
|
class PlaylistDetailsViewModel(
|
||||||
private val repository: RepositoryImpl,
|
private val realRepository: RealRepository,
|
||||||
private var playlist: Playlist
|
private var playlist: Playlist
|
||||||
) : ViewModel(), MusicServiceEventListener {
|
) : ViewModel(), MusicServiceEventListener {
|
||||||
private val _playListSongs = MutableLiveData<List<Song>>()
|
private val _playListSongs = MutableLiveData<List<Song>>()
|
||||||
|
|
||||||
private val _playlist = MutableLiveData<Playlist>().apply {
|
private val _playlist = MutableLiveData<Playlist>().apply {
|
||||||
postValue(playlist)
|
postValue(playlist)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +33,7 @@ class PlaylistDetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch {
|
private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch {
|
||||||
val songs = repository.getPlaylistSongs(playlist)
|
val songs = realRepository.getPlaylistSongs(playlist)
|
||||||
withContext(Main) { _playListSongs.postValue(songs) }
|
withContext(Main) { _playListSongs.postValue(songs) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +48,12 @@ class PlaylistDetailsViewModel(
|
||||||
val playlistName =
|
val playlistName =
|
||||||
PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong())
|
PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong())
|
||||||
if (playlistName != playlist.name) {
|
if (playlistName != playlist.name) {
|
||||||
playlist = PlaylistLoader.getPlaylist(App.getContext(), playlist.id)
|
viewModelScope.launch {
|
||||||
|
playlist = realRepository.playlist(playlist.id)
|
||||||
_playlist.postValue(playlist)
|
_playlist.postValue(playlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
loadPlaylistSongs(playlist)
|
loadPlaylistSongs(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,19 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
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.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class SearchViewModel(private val repository: RepositoryImpl) : ViewModel() {
|
class SearchViewModel(private val realRepository: RealRepository) : ViewModel() {
|
||||||
private val results = MutableLiveData<MutableList<Any>>()
|
private val results = MutableLiveData<MutableList<Any>>()
|
||||||
|
|
||||||
fun getSearchResult(): LiveData<MutableList<Any>> = results
|
fun getSearchResult(): LiveData<MutableList<Any>> = results
|
||||||
|
|
||||||
fun search(query: String?) = viewModelScope.launch(IO) {
|
fun search(query: String?) = viewModelScope.launch(IO) {
|
||||||
val result = repository.search(query)
|
val result = realRepository.search(query)
|
||||||
withContext(Main) { results.postValue(result) }
|
withContext(Main) { results.postValue(result) }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,7 +34,7 @@ public class AlbumGlideRequest {
|
||||||
if (ignoreMediaStore) {
|
if (ignoreMediaStore) {
|
||||||
return requestManager.load(new AudioFileCover(song.getData()));
|
return requestManager.load(new AudioFileCover(song.getData()));
|
||||||
} else {
|
} else {
|
||||||
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class SongGlideRequest {
|
||||||
if (ignoreMediaStore) {
|
if (ignoreMediaStore) {
|
||||||
return requestManager.load(new AudioFileCover(song.getData()));
|
return requestManager.load(new AudioFileCover(song.getData()));
|
||||||
} else {
|
} else {
|
||||||
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ object M3UWriter : M3UConstants {
|
||||||
): File? {
|
): File? {
|
||||||
if (!dir.exists()) dir.mkdirs()
|
if (!dir.exists()) dir.mkdirs()
|
||||||
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
|
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
|
||||||
val songs = playlist.getSongs(context)
|
val songs = playlist.getSongs()
|
||||||
if (songs.size > 0) {
|
if (songs.size > 0) {
|
||||||
val bw = BufferedWriter(FileWriter(file))
|
val bw = BufferedWriter(FileWriter(file))
|
||||||
bw.write(M3UConstants.HEADER)
|
bw.write(M3UConstants.HEADER)
|
||||||
|
|
|
@ -23,22 +23,25 @@ import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.MediaStore
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import code.name.monkey.retromusic.loaders.SongLoader
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
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.service.MusicService
|
||||||
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object MusicPlayerRemote {
|
object MusicPlayerRemote : KoinComponent {
|
||||||
val TAG: String = MusicPlayerRemote::class.java.simpleName
|
val TAG: String = MusicPlayerRemote::class.java.simpleName
|
||||||
private val mConnectionMap = WeakHashMap<Context, ServiceBinder>()
|
private val mConnectionMap = WeakHashMap<Context, ServiceBinder>()
|
||||||
var musicService: MusicService? = null
|
var musicService: MusicService? = null
|
||||||
|
|
||||||
|
private val songRepository by inject<SongRepository>()
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isPlaying: Boolean
|
val isPlaying: Boolean
|
||||||
get() = musicService != null && musicService!!.isPlaying
|
get() = musicService != null && musicService!!.isPlaying
|
||||||
|
@ -412,21 +415,14 @@ object MusicPlayerRemote {
|
||||||
songId = uri.lastPathSegment
|
songId = uri.lastPathSegment
|
||||||
}
|
}
|
||||||
if (songId != null) {
|
if (songId != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(songId)
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
musicService!!,
|
|
||||||
MediaStore.Audio.AudioColumns._ID + "=?",
|
|
||||||
arrayOf(songId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
var songFile: File? = null
|
var songFile: File? = null
|
||||||
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
|
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
|
||||||
songFile =
|
songFile = File(
|
||||||
File(
|
|
||||||
Environment.getExternalStorageDirectory(),
|
Environment.getExternalStorageDirectory(),
|
||||||
uri.path?.split(":".toRegex(), 2)?.get(1)
|
uri.path?.split(":".toRegex(), 2)?.get(1)
|
||||||
)
|
)
|
||||||
|
@ -440,13 +436,7 @@ object MusicPlayerRemote {
|
||||||
songFile = File(uri.path)
|
songFile = File(uri.path)
|
||||||
}
|
}
|
||||||
if (songFile != null) {
|
if (songFile != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songsByFilePath(songFile.absolutePath)
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
musicService!!,
|
|
||||||
MediaStore.Audio.AudioColumns.DATA + "=?",
|
|
||||||
arrayOf(songFile.absolutePath)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (songs != null && songs.isNotEmpty()) {
|
if (songs != null && songs.isNotEmpty()) {
|
||||||
|
|
|
@ -18,29 +18,31 @@ import android.app.SearchManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import code.name.monkey.retromusic.loaders.SongLoader
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
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.*
|
import java.util.*
|
||||||
|
|
||||||
object SearchQueryHelper {
|
object SearchQueryHelper : KoinComponent {
|
||||||
private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?"
|
private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?"
|
||||||
private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?"
|
private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?"
|
||||||
private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?"
|
private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?"
|
||||||
private const val AND = " AND "
|
private const val AND = " AND "
|
||||||
|
private val songRepository by inject<RealSongRepository>()
|
||||||
var songs = ArrayList<Song>()
|
var songs = ArrayList<Song>()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSongs(context: Context, extras: Bundle): ArrayList<Song> {
|
fun getSongs(context: Context, extras: Bundle): List<Song> {
|
||||||
val query = extras.getString(SearchManager.QUERY, null)
|
val query = extras.getString(SearchManager.QUERY, null)
|
||||||
val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null)
|
val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null)
|
||||||
val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null)
|
val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null)
|
||||||
val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null)
|
val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null)
|
||||||
|
|
||||||
var songs = ArrayList<Song>()
|
var songs = listOf<Song>()
|
||||||
if (artistName != null && albumName != null && titleName != null) {
|
if (artistName != null && albumName != null && titleName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION,
|
ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION,
|
||||||
arrayOf(
|
arrayOf(
|
||||||
artistName.toLowerCase(),
|
artistName.toLowerCase(),
|
||||||
|
@ -54,9 +56,8 @@ object SearchQueryHelper {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
if (artistName != null && titleName != null) {
|
if (artistName != null && titleName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ARTIST_SELECTION + AND + TITLE_SELECTION,
|
ARTIST_SELECTION + AND + TITLE_SELECTION,
|
||||||
arrayOf(artistName.toLowerCase(), titleName.toLowerCase())
|
arrayOf(artistName.toLowerCase(), titleName.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -66,9 +67,8 @@ object SearchQueryHelper {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
if (albumName != null && titleName != null) {
|
if (albumName != null && titleName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ALBUM_SELECTION + AND + TITLE_SELECTION,
|
ALBUM_SELECTION + AND + TITLE_SELECTION,
|
||||||
arrayOf(albumName.toLowerCase(), titleName.toLowerCase())
|
arrayOf(albumName.toLowerCase(), titleName.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -78,9 +78,8 @@ object SearchQueryHelper {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
if (artistName != null) {
|
if (artistName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ARTIST_SELECTION,
|
ARTIST_SELECTION,
|
||||||
arrayOf(artistName.toLowerCase())
|
arrayOf(artistName.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -90,9 +89,8 @@ object SearchQueryHelper {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
if (albumName != null) {
|
if (albumName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ALBUM_SELECTION,
|
ALBUM_SELECTION,
|
||||||
arrayOf(albumName.toLowerCase())
|
arrayOf(albumName.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -102,9 +100,8 @@ object SearchQueryHelper {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
if (titleName != null) {
|
if (titleName != null) {
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
TITLE_SELECTION,
|
TITLE_SELECTION,
|
||||||
arrayOf(titleName.toLowerCase())
|
arrayOf(titleName.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -113,10 +110,8 @@ object SearchQueryHelper {
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
songs =
|
songs = songRepository.songs(
|
||||||
SongLoader.getSongs(
|
songRepository.makeSongCursor(
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
context,
|
|
||||||
ARTIST_SELECTION,
|
ARTIST_SELECTION,
|
||||||
arrayOf(query.toLowerCase())
|
arrayOf(query.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -125,9 +120,8 @@ object SearchQueryHelper {
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
ALBUM_SELECTION,
|
ALBUM_SELECTION,
|
||||||
arrayOf(query.toLowerCase())
|
arrayOf(query.toLowerCase())
|
||||||
)
|
)
|
||||||
|
@ -135,9 +129,8 @@ object SearchQueryHelper {
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
songs = SongLoader.getSongs(
|
songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
TITLE_SELECTION,
|
TITLE_SELECTION,
|
||||||
arrayOf(query.toLowerCase())
|
arrayOf(query.toLowerCase())
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,42 +14,43 @@
|
||||||
|
|
||||||
package code.name.monkey.retromusic.helper.menu
|
package code.name.monkey.retromusic.helper.menu
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
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.Genre
|
||||||
import code.name.monkey.retromusic.model.Song
|
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<GenreRepository>()
|
||||||
fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean {
|
fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_play -> {
|
R.id.action_play -> {
|
||||||
MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true)
|
MusicPlayerRemote.openQueue(getGenreSongs(genre), 0, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_play_next -> {
|
R.id.action_play_next -> {
|
||||||
MusicPlayerRemote.playNext(getGenreSongs(activity, genre))
|
MusicPlayerRemote.playNext(getGenreSongs(genre))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_add_to_playlist -> {
|
R.id.action_add_to_playlist -> {
|
||||||
AddToPlaylistDialog.create(getGenreSongs(activity, genre))
|
AddToPlaylistDialog.create(getGenreSongs(genre))
|
||||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_add_to_current_playing -> {
|
R.id.action_add_to_current_playing -> {
|
||||||
MusicPlayerRemote.enqueue(getGenreSongs(activity, genre))
|
MusicPlayerRemote.enqueue(getGenreSongs(genre))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGenreSongs(activity: Activity, genre: Genre): ArrayList<Song> {
|
private fun getGenreSongs(genre: Genre): List<Song> {
|
||||||
return GenreLoader.getSongs(activity, genre.id)
|
return genreRepository.songs(genre.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,11 @@ import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog
|
import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog
|
||||||
import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog
|
import code.name.monkey.retromusic.dialogs.RenamePlaylistDialog
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
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.misc.WeakContextAsyncTask
|
||||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
object PlaylistMenuHelper {
|
object PlaylistMenuHelper {
|
||||||
|
@ -80,11 +78,11 @@ object PlaylistMenuHelper {
|
||||||
private fun getPlaylistSongs(
|
private fun getPlaylistSongs(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
playlist: Playlist
|
playlist: Playlist
|
||||||
): ArrayList<Song> {
|
): List<Song> {
|
||||||
return if (playlist is AbsCustomPlaylist) {
|
return if (playlist is AbsCustomPlaylist) {
|
||||||
playlist.getSongs(activity)
|
playlist.songs()
|
||||||
} else {
|
} else {
|
||||||
PlaylistSongsLoader.getPlaylistSongList(activity, playlist)
|
playlist.getSongs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,17 @@
|
||||||
package code.name.monkey.retromusic.helper.menu
|
package code.name.monkey.retromusic.helper.menu
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
object SongsMenuHelper {
|
object SongsMenuHelper {
|
||||||
fun handleMenuClick(
|
fun handleMenuClick(
|
||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
songs: ArrayList<Song>,
|
songs: List<Song>,
|
||||||
menuItemId: Int
|
menuItemId: Int
|
||||||
): Boolean {
|
): Boolean {
|
||||||
when (menuItemId) {
|
when (menuItemId) {
|
||||||
|
|
|
@ -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<File>)
|
||||||
|
}
|
|
@ -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<Playlist> {
|
|
||||||
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<Playlist> {
|
|
||||||
return getAllPlaylists(makePlaylistCursor(context, null, null))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFavoritePlaylist(context: Context): ArrayList<Playlist> {
|
|
||||||
return getAllPlaylists(
|
|
||||||
makePlaylistCursor(
|
|
||||||
context,
|
|
||||||
PlaylistsColumns.NAME + "=?",
|
|
||||||
arrayOf(context.getString(code.name.monkey.retromusic.R.string.favorites))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAllPlaylists(cursor: Cursor?): ArrayList<Playlist> {
|
|
||||||
val playlists = ArrayList<Playlist>()
|
|
||||||
|
|
||||||
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<String>?
|
|
||||||
): 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> getSongs(@NotNull Context context);
|
|
||||||
}
|
|
|
@ -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<Song>
|
||||||
|
|
||||||
|
protected val songRepository by inject<SongRepository>()
|
||||||
|
|
||||||
|
protected val topPlayedRepository by inject<TopPlayedRepository>()
|
||||||
|
|
||||||
|
protected val lastAddedRepository by inject<LastAddedRepository>()
|
||||||
|
}
|
|
@ -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<Playlist> CREATOR = new Creator<Playlist>() {
|
|
||||||
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<Song> 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,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> {
|
||||||
|
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,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
@ -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<HistoryPlaylist> CREATOR = new Creator<HistoryPlaylist>() {
|
|
||||||
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<Song> getSongs(@NotNull @NonNull Context context) {
|
|
||||||
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> {
|
||||||
|
return topPlayedRepository.recentlyPlayedTracks()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<LastAddedPlaylist> CREATOR = new Creator<LastAddedPlaylist>() {
|
|
||||||
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<Song> getSongs(@NotNull @NonNull Context context) {
|
|
||||||
return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isClearable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> {
|
||||||
|
return lastAddedRepository.recentSongs()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<MyTopTracksPlaylist> CREATOR = new Creator<MyTopTracksPlaylist>() {
|
|
||||||
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<Song> getSongs(@NotNull @NonNull Context context) {
|
|
||||||
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> {
|
||||||
|
return topPlayedRepository.notRecentlyPlayedTracks()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<NotRecentlyPlayedPlaylist> CREATOR = new Creator<NotRecentlyPlayedPlaylist>() {
|
|
||||||
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<Song> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<ShuffleAllPlaylist> CREATOR = new Creator<ShuffleAllPlaylist>() {
|
|
||||||
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<Song> getSongs(@NotNull Context context) {
|
|
||||||
return SongLoader.INSTANCE.getAllSongs(context);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Song> {
|
||||||
|
return songRepository.songs()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Song> {
|
||||||
|
return topPlayedRepository.topTracks()
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,11 +24,11 @@ import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
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.model.Song;
|
||||||
|
import code.name.monkey.retromusic.repository.RealSongRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrew Neal, modified for Phonograph by Karim Abou Zeid
|
* @author Andrew Neal, modified for Phonograph by Karim Abou Zeid
|
||||||
|
@ -76,12 +76,12 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public ArrayList<Song> getSavedOriginalPlayingQueue() {
|
public List<Song> getSavedOriginalPlayingQueue() {
|
||||||
return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
|
return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public ArrayList<Song> getSavedPlayingQueue() {
|
public List<Song> getSavedPlayingQueue() {
|
||||||
return getQueue(PLAYING_QUEUE_TABLE_NAME);
|
return getQueue(PLAYING_QUEUE_TABLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,10 +157,10 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ArrayList<Song> getQueue(@NonNull final String tableName) {
|
private List<Song> getQueue(@NonNull final String tableName) {
|
||||||
Cursor cursor = getReadableDatabase().query(tableName, null,
|
Cursor cursor = getReadableDatabase().query(tableName, null,
|
||||||
null, null, null, null, null);
|
null, null, null, null, null);
|
||||||
return SongLoader.INSTANCE.getSongs(cursor);
|
return new RealSongRepository(App.Companion.getContext()).songs(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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<Album>
|
|
||||||
|
|
||||||
suspend fun albumById(albumId: Int): Album
|
|
||||||
|
|
||||||
suspend fun allSongs(): List<Song>
|
|
||||||
|
|
||||||
suspend fun allArtists(): List<Artist>
|
|
||||||
|
|
||||||
suspend fun allPlaylists(): List<Playlist>
|
|
||||||
|
|
||||||
suspend fun allGenres(): List<Genre>
|
|
||||||
|
|
||||||
suspend fun search(query: String?): MutableList<Any>
|
|
||||||
|
|
||||||
suspend fun getPlaylistSongs(playlist: Playlist): ArrayList<Song>
|
|
||||||
|
|
||||||
suspend fun getGenre(genreId: Int): ArrayList<Song>
|
|
||||||
|
|
||||||
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<Artist>
|
|
||||||
|
|
||||||
suspend fun topArtists(): List<Artist>
|
|
||||||
|
|
||||||
suspend fun topAlbums(): List<Album>
|
|
||||||
|
|
||||||
suspend fun recentAlbums(): List<Album>
|
|
||||||
|
|
||||||
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<Home>
|
|
||||||
|
|
||||||
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
|
|
||||||
|
|
||||||
fun songsFlow(): Flow<Result<List<Song>>>
|
|
||||||
|
|
||||||
fun albumsFlow(): Flow<Result<List<Album>>>
|
|
||||||
|
|
||||||
fun artistsFlow(): Flow<Result<List<Artist>>>
|
|
||||||
|
|
||||||
fun playlistsFlow(): Flow<Result<List<Playlist>>>
|
|
||||||
|
|
||||||
fun genresFlow(): Flow<Result<List<Genre>>>
|
|
||||||
|
|
||||||
}
|
|
|
@ -12,9 +12,8 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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 android.provider.MediaStore.Audio.AudioColumns
|
||||||
import code.name.monkey.retromusic.helper.SortOrder
|
import code.name.monkey.retromusic.helper.SortOrder
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
|
@ -27,16 +26,31 @@ import kotlin.collections.ArrayList
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 11/08/17.
|
* Created by hemanths on 11/08/17.
|
||||||
*/
|
*/
|
||||||
|
interface AlbumRepository {
|
||||||
|
fun albums(): List<Album>
|
||||||
|
|
||||||
object AlbumLoader {
|
fun albums(query: String): List<Album>
|
||||||
|
|
||||||
fun getAlbums(
|
fun album(albumId: Int): Album
|
||||||
context: Context,
|
}
|
||||||
query: String
|
|
||||||
): ArrayList<Album> {
|
class RealAlbumRepository(private val songRepository: RealSongRepository) :
|
||||||
val songs = SongLoader.getSongs(
|
AlbumRepository {
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
context,
|
override fun albums(): List<Album> {
|
||||||
|
val songs = songRepository.songs(
|
||||||
|
songRepository.makeSongCursor(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
getSongLoaderSortOrder()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return splitIntoAlbums(songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun albums(query: String): List<Album> {
|
||||||
|
val songs = songRepository.songs(
|
||||||
|
songRepository.makeSongCursor(
|
||||||
AudioColumns.ALBUM + " LIKE ?",
|
AudioColumns.ALBUM + " LIKE ?",
|
||||||
arrayOf("%$query%"),
|
arrayOf("%$query%"),
|
||||||
getSongLoaderSortOrder()
|
getSongLoaderSortOrder()
|
||||||
|
@ -45,41 +59,22 @@ object AlbumLoader {
|
||||||
return splitIntoAlbums(songs)
|
return splitIntoAlbums(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
override fun album(albumId: Int): Album {
|
||||||
fun getAlbum(
|
val songs = songRepository.songs(
|
||||||
context: Context,
|
songRepository.makeSongCursor(
|
||||||
albumId: Int
|
|
||||||
): Album {
|
|
||||||
val songs = SongLoader.getSongs(
|
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
context,
|
|
||||||
AudioColumns.ALBUM_ID + "=?",
|
AudioColumns.ALBUM_ID + "=?",
|
||||||
arrayOf(albumId.toString()),
|
arrayOf(albumId.toString()),
|
||||||
getSongLoaderSortOrder()
|
getSongLoaderSortOrder()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val album = Album(songs)
|
val album = Album(ArrayList(songs))
|
||||||
sortSongsByTrackNumber(album)
|
sortAlbumSongs(album)
|
||||||
return album
|
return album
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllAlbums(
|
|
||||||
context: Context
|
|
||||||
): ArrayList<Album> {
|
|
||||||
val songs = SongLoader.getSongs(
|
|
||||||
SongLoader.makeSongCursor(
|
|
||||||
context,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
getSongLoaderSortOrder()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return splitIntoAlbums(songs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun splitIntoAlbums(
|
fun splitIntoAlbums(
|
||||||
songs: ArrayList<Song>?
|
songs: List<Song>?
|
||||||
): ArrayList<Album> {
|
): List<Album> {
|
||||||
val albums = ArrayList<Album>()
|
val albums = ArrayList<Album>()
|
||||||
if (songs != null) {
|
if (songs != null) {
|
||||||
for (song in songs) {
|
for (song in songs) {
|
||||||
|
@ -87,7 +82,7 @@ object AlbumLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (album in albums) {
|
for (album in albums) {
|
||||||
sortSongsByTrackNumber(album)
|
sortAlbumSongs(album)
|
||||||
}
|
}
|
||||||
return albums
|
return albums
|
||||||
}
|
}
|
||||||
|
@ -106,7 +101,7 @@ object AlbumLoader {
|
||||||
return album
|
return album
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortSongsByTrackNumber(album: Album) {
|
private fun sortAlbumSongs(album: Album) {
|
||||||
when (PreferenceUtil.albumDetailSongSortOrder) {
|
when (PreferenceUtil.albumDetailSongSortOrder) {
|
||||||
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
|
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
|
||||||
o1.trackNumber.compareTo(
|
o1.trackNumber.compareTo(
|
||||||
|
@ -135,4 +130,6 @@ object AlbumLoader {
|
||||||
return PreferenceUtil.albumSortOrder + ", " +
|
return PreferenceUtil.albumSortOrder + ", " +
|
||||||
PreferenceUtil.albumSongSortOrder
|
PreferenceUtil.albumSongSortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,46 +12,66 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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 android.provider.MediaStore.Audio.AudioColumns
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
|
||||||
object ArtistLoader {
|
interface ArtistRepository {
|
||||||
|
fun artists(): List<Artist>
|
||||||
|
|
||||||
|
fun artists(query: String): List<Artist>
|
||||||
|
|
||||||
|
fun artist(artistId: Int): Artist
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealArtistRepository(
|
||||||
|
private val songRepository: RealSongRepository,
|
||||||
|
private val albumRepository: RealAlbumRepository
|
||||||
|
) : ArtistRepository {
|
||||||
|
|
||||||
private fun getSongLoaderSortOrder(): String {
|
private fun getSongLoaderSortOrder(): String {
|
||||||
return PreferenceUtil.artistSortOrder + ", " +
|
return PreferenceUtil.artistSortOrder + ", " +
|
||||||
PreferenceUtil.artistAlbumSortOrder + ", " +
|
PreferenceUtil.artistAlbumSortOrder + ", " +
|
||||||
PreferenceUtil.artistSongSortOrder
|
PreferenceUtil.artistSongSortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllArtists(context: Context): ArrayList<Artist> {
|
override fun artists(): List<Artist> {
|
||||||
val songs = SongLoader.getSongs(
|
val songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
null, null,
|
null, null,
|
||||||
getSongLoaderSortOrder()
|
getSongLoaderSortOrder()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))
|
return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getArtists(context: Context, query: String): ArrayList<Artist> {
|
override fun artists(query: String): List<Artist> {
|
||||||
val songs = SongLoader.getSongs(
|
val songs = songRepository.songs(
|
||||||
SongLoader.makeSongCursor(
|
songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
AudioColumns.ARTIST + " LIKE ?",
|
AudioColumns.ARTIST + " LIKE ?",
|
||||||
arrayOf("%$query%"),
|
arrayOf("%$query%"),
|
||||||
getSongLoaderSortOrder()
|
getSongLoaderSortOrder()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))
|
return splitIntoArtists(albumRepository.splitIntoAlbums(songs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun splitIntoArtists(albums: ArrayList<Album>?): ArrayList<Artist> {
|
override fun artist(artistId: Int): Artist {
|
||||||
val artists = ArrayList<Artist>()
|
val songs = songRepository.songs(
|
||||||
|
songRepository.makeSongCursor(
|
||||||
|
AudioColumns.ARTIST_ID + "=?",
|
||||||
|
arrayOf(artistId.toString()),
|
||||||
|
getSongLoaderSortOrder()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return Artist(ArrayList(albumRepository.splitIntoAlbums(songs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun splitIntoArtists(albums: List<Album>?): List<Artist> {
|
||||||
|
val artists = mutableListOf<Artist>()
|
||||||
if (albums != null) {
|
if (albums != null) {
|
||||||
for (album in albums) {
|
for (album in albums) {
|
||||||
getOrCreateArtist(artists, album.artistId).albums!!.add(album)
|
getOrCreateArtist(artists, album.artistId).albums!!.add(album)
|
||||||
|
@ -60,7 +80,7 @@ object ArtistLoader {
|
||||||
return artists
|
return artists
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrCreateArtist(artists: ArrayList<Artist>, artistId: Int): Artist {
|
private fun getOrCreateArtist(artists: MutableList<Artist>, artistId: Int): Artist {
|
||||||
for (artist in artists) {
|
for (artist in artists) {
|
||||||
if (artist.albums!!.isNotEmpty() && artist.albums[0].songs!!.isNotEmpty() && artist.albums[0].songs!![0].artistId == artistId) {
|
if (artist.albums!!.isNotEmpty() && artist.albums[0].songs!!.isNotEmpty() && artist.albums[0].songs!![0].artistId == artistId) {
|
||||||
return artist
|
return artist
|
||||||
|
@ -70,17 +90,4 @@ object ArtistLoader {
|
||||||
artists.add(album)
|
artists.add(album)
|
||||||
return 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -12,12 +12,13 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.Audio.Genres
|
import android.provider.MediaStore.Audio.Genres
|
||||||
import code.name.monkey.retromusic.Constants.IS_MUSIC
|
import code.name.monkey.retromusic.Constants.IS_MUSIC
|
||||||
import code.name.monkey.retromusic.Constants.baseProjection
|
import code.name.monkey.retromusic.Constants.baseProjection
|
||||||
|
@ -25,49 +26,52 @@ import code.name.monkey.retromusic.model.Genre
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
|
||||||
|
interface GenreRepository {
|
||||||
|
fun genres(): List<Genre>
|
||||||
|
|
||||||
object GenreLoader {
|
fun songs(genreId: Int): List<Song>
|
||||||
|
}
|
||||||
|
|
||||||
fun getAllGenres(context: Context): ArrayList<Genre> {
|
class RealGenreRepository(
|
||||||
return getGenresFromCursor(context, makeGenreCursor(context))
|
private val contentResolver: ContentResolver,
|
||||||
|
private val songRepository: RealSongRepository
|
||||||
|
) : GenreRepository {
|
||||||
|
|
||||||
|
override fun genres(): List<Genre> {
|
||||||
|
return getGenresFromCursor(makeGenreCursor())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchGenres(context: Context): ArrayList<Genre> {
|
override fun songs(genreId: Int): List<Song> {
|
||||||
return getGenresFromCursorForSearch(context, makeGenreCursor(context))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSongs(context: Context, genreId: Int): ArrayList<Song> {
|
|
||||||
// The genres table only stores songs that have a genre specified,
|
// The genres table only stores songs that have a genre specified,
|
||||||
// so we need to get songs without a genre a different way.
|
// so we need to get songs without a genre a different way.
|
||||||
return if (genreId == -1) {
|
return if (genreId == -1) {
|
||||||
getSongsWithNoGenre(context)
|
getSongsWithNoGenre()
|
||||||
} else SongLoader.getSongs(makeGenreSongCursor(context, genreId))
|
} 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 id = cursor.getInt(0)
|
||||||
val name = cursor.getString(1)
|
val name = cursor.getString(1)
|
||||||
val songCount = getSongs(context, id).size
|
val songCount = songs(id).size
|
||||||
return Genre(id, name, songCount)
|
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 id = cursor.getInt(0)
|
||||||
val name = cursor.getString(1)
|
val name = cursor.getString(1)
|
||||||
return Genre(id, name, -1)
|
return Genre(id, name, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongsWithNoGenre(context: Context): ArrayList<Song> {
|
private fun getSongsWithNoGenre(): List<Song> {
|
||||||
val selection = BaseColumns._ID + " NOT IN " +
|
val selection =
|
||||||
"(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)"
|
BaseColumns._ID + " NOT IN " + "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)"
|
||||||
return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null))
|
return songRepository.songs(songRepository.makeSongCursor(selection, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasSongsWithNoGenre(context: Context): Boolean {
|
private fun hasSongsWithNoGenre(): Boolean {
|
||||||
val allSongsCursor = SongLoader.makeSongCursor(context, null, null)
|
val allSongsCursor = songRepository.makeSongCursor(null, null)
|
||||||
val allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context)
|
val allSongsWithGenreCursor = makeAllSongsWithGenreCursor()
|
||||||
|
|
||||||
if (allSongsCursor == null || allSongsWithGenreCursor == null) {
|
if (allSongsCursor == null || allSongsWithGenreCursor == null) {
|
||||||
return false
|
return false
|
||||||
|
@ -79,44 +83,36 @@ object GenreLoader {
|
||||||
return hasSongsWithNoGenre
|
return hasSongsWithNoGenre
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeAllSongsWithGenreCursor(context: Context): Cursor? {
|
private fun makeAllSongsWithGenreCursor(): Cursor? {
|
||||||
return try {
|
println(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI.toString())
|
||||||
context.contentResolver.query(
|
return contentResolver.query(
|
||||||
Uri.parse("content://media/external/audio/genres/all/members"),
|
Uri.parse("content://media/external/audio/genres/all/members"),
|
||||||
arrayOf(Genres.Members.AUDIO_ID), null, null, null
|
arrayOf(Genres.Members.AUDIO_ID), null, null, null
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeGenreSongCursor(context: Context, genreId: Int): Cursor? {
|
private fun makeGenreSongCursor(genreId: Int): Cursor? {
|
||||||
try {
|
return contentResolver.query(
|
||||||
return context.contentResolver.query(
|
|
||||||
Genres.Members.getContentUri("external", genreId.toLong()),
|
Genres.Members.getContentUri("external", genreId.toLong()),
|
||||||
baseProjection,
|
baseProjection,
|
||||||
IS_MUSIC,
|
IS_MUSIC,
|
||||||
null,
|
null,
|
||||||
PreferenceUtil.songSortOrder
|
PreferenceUtil.songSortOrder
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private fun getGenresFromCursor(cursor: Cursor?): ArrayList<Genre> {
|
||||||
|
|
||||||
private fun getGenresFromCursor(context: Context, cursor: Cursor?): ArrayList<Genre> {
|
|
||||||
val genres = arrayListOf<Genre>()
|
val genres = arrayListOf<Genre>()
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
val genre = getGenreFromCursor(context, cursor)
|
val genre = getGenreFromCursor(cursor)
|
||||||
if (genre.songCount > 0) {
|
if (genre.songCount > 0) {
|
||||||
genres.add(genre)
|
genres.add(genre)
|
||||||
} else {
|
} else {
|
||||||
// try to remove the empty genre from the media store
|
// try to remove the empty genre from the media store
|
||||||
try {
|
try {
|
||||||
context.contentResolver.delete(
|
contentResolver.delete(
|
||||||
Genres.EXTERNAL_CONTENT_URI,
|
Genres.EXTERNAL_CONTENT_URI,
|
||||||
Genres._ID + " == " + genre.id,
|
Genres._ID + " == " + genre.id,
|
||||||
null
|
null
|
||||||
|
@ -133,11 +129,11 @@ object GenreLoader {
|
||||||
return genres
|
return genres
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGenresFromCursorForSearch(context: Context, cursor: Cursor?): ArrayList<Genre> {
|
private fun getGenresFromCursorForSearch(cursor: Cursor?): List<Genre> {
|
||||||
val genres = arrayListOf<Genre>()
|
val genres = mutableListOf<Genre>()
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
genres.add(getGenreFromCursorWithOutSongs(context, cursor))
|
genres.add(getGenreFromCursorWithOutSongs(cursor))
|
||||||
} while (cursor.moveToNext())
|
} while (cursor.moveToNext())
|
||||||
}
|
}
|
||||||
cursor?.close()
|
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)
|
val projection = arrayOf(Genres._ID, Genres.NAME)
|
||||||
try {
|
return contentResolver.query(
|
||||||
return context.contentResolver.query(
|
|
||||||
Genres.EXTERNAL_CONTENT_URI,
|
Genres.EXTERNAL_CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
PreferenceUtil.genreSortOrder
|
PreferenceUtil.genreSortOrder
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,9 +12,8 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.database.Cursor
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import code.name.monkey.retromusic.model.Album
|
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.
|
* Created by hemanths on 16/08/17.
|
||||||
*/
|
*/
|
||||||
|
interface LastAddedRepository {
|
||||||
|
fun recentSongs(): List<Song>
|
||||||
|
|
||||||
object LastAddedSongsLoader {
|
fun recentAlbums(): List<Album>
|
||||||
|
|
||||||
fun getLastAddedSongs(context: Context): ArrayList<Song> {
|
fun recentArtists(): List<Artist>
|
||||||
return SongLoader.getSongs(makeLastAddedCursor(context))
|
}
|
||||||
|
|
||||||
|
class RealLastAddedRepository(
|
||||||
|
private val songRepository: RealSongRepository,
|
||||||
|
private val albumRepository: RealAlbumRepository,
|
||||||
|
private val artistRepository: RealArtistRepository
|
||||||
|
) : LastAddedRepository {
|
||||||
|
override fun recentSongs(): List<Song> {
|
||||||
|
return songRepository.songs(makeLastAddedCursor())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeLastAddedCursor(context: Context): Cursor? {
|
override fun recentAlbums(): List<Album> {
|
||||||
|
return albumRepository.splitIntoAlbums(recentSongs())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recentArtists(): List<Artist> {
|
||||||
|
return artistRepository.splitIntoArtists(recentAlbums())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeLastAddedCursor(): Cursor? {
|
||||||
val cutoff = PreferenceUtil.lastAddedCutoff
|
val cutoff = PreferenceUtil.lastAddedCutoff
|
||||||
return SongLoader.makeSongCursor(
|
return songRepository.makeSongCursor(
|
||||||
context,
|
|
||||||
MediaStore.Audio.Media.DATE_ADDED + ">?",
|
MediaStore.Audio.Media.DATE_ADDED + ">?",
|
||||||
arrayOf(cutoff.toString()),
|
arrayOf(cutoff.toString()),
|
||||||
MediaStore.Audio.Media.DATE_ADDED + " DESC"
|
MediaStore.Audio.Media.DATE_ADDED + " DESC"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLastAddedAlbums(context: Context): ArrayList<Album> {
|
|
||||||
return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLastAddedArtists(context: Context): ArrayList<Artist> {
|
|
||||||
return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context))
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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<Playlist>
|
||||||
|
|
||||||
|
fun playlist(playlistName: String): Playlist
|
||||||
|
|
||||||
|
fun playlists(): List<Playlist>
|
||||||
|
|
||||||
|
fun playlists(cursor: Cursor?): List<Playlist>
|
||||||
|
|
||||||
|
fun favoritePlaylist(playlistName: String): List<Playlist>
|
||||||
|
|
||||||
|
fun deletePlaylist(playlistId: Int)
|
||||||
|
|
||||||
|
fun playlist(playlistId: Int): Playlist
|
||||||
|
|
||||||
|
fun playlistSongs(playlistId: Int): List<Song>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Playlist> {
|
||||||
|
return playlists(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(query)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun playlists(): List<Playlist> {
|
||||||
|
return playlists(makePlaylistCursor(null, null))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun playlists(cursor: Cursor?): List<Playlist> {
|
||||||
|
val playlists = mutableListOf<Playlist>()
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
playlists.add(getPlaylistFromCursorImpl(cursor))
|
||||||
|
} while (cursor.moveToNext())
|
||||||
|
}
|
||||||
|
cursor?.close()
|
||||||
|
return playlists
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun favoritePlaylist(playlistName: String): List<Playlist> {
|
||||||
|
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<Song> {
|
||||||
|
val songs = arrayListOf<Song>()
|
||||||
|
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<String>?
|
||||||
|
): 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
@ -34,19 +34,31 @@ object PlaylistSongsLoader {
|
||||||
fun getPlaylistSongList(
|
fun getPlaylistSongList(
|
||||||
context: Context,
|
context: Context,
|
||||||
playlist: Playlist
|
playlist: Playlist
|
||||||
): ArrayList<Song> {
|
): List<Song> {
|
||||||
return (playlist as? AbsCustomPlaylist)?.getSongs(context)
|
return if (playlist is AbsCustomPlaylist) {
|
||||||
?: getPlaylistSongList(context, playlist.id)
|
return playlist.songs()
|
||||||
|
} else {
|
||||||
|
getPlaylistSongList(context, playlist.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> {
|
fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> {
|
||||||
val songs = arrayListOf<Song>()
|
val songs = arrayListOf<Song>()
|
||||||
val cursor = makePlaylistSongCursor(context, playlistId)
|
val cursor =
|
||||||
|
makePlaylistSongCursor(
|
||||||
|
context,
|
||||||
|
playlistId
|
||||||
|
)
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId))
|
songs.add(
|
||||||
|
getPlaylistSongFromCursorImpl(
|
||||||
|
cursor,
|
||||||
|
playlistId
|
||||||
|
)
|
||||||
|
)
|
||||||
} while (cursor.moveToNext())
|
} while (cursor.moveToNext())
|
||||||
}
|
}
|
||||||
cursor?.close()
|
cursor?.close()
|
|
@ -12,68 +12,133 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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 android.content.Context
|
||||||
import code.name.monkey.retromusic.*
|
import code.name.monkey.retromusic.*
|
||||||
import code.name.monkey.retromusic.loaders.*
|
|
||||||
import code.name.monkey.retromusic.model.*
|
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.LastFMService
|
||||||
import code.name.monkey.retromusic.network.model.LastFmAlbum
|
import code.name.monkey.retromusic.network.model.LastFmAlbum
|
||||||
import code.name.monkey.retromusic.network.model.LastFmArtist
|
import code.name.monkey.retromusic.network.model.LastFmArtist
|
||||||
import code.name.monkey.retromusic.providers.interfaces.Repository
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
class RepositoryImpl(
|
interface Repository {
|
||||||
|
|
||||||
|
suspend fun allAlbums(): List<Album>
|
||||||
|
|
||||||
|
suspend fun albumById(albumId: Int): Album
|
||||||
|
|
||||||
|
suspend fun allSongs(): List<Song>
|
||||||
|
|
||||||
|
suspend fun allArtists(): List<Artist>
|
||||||
|
|
||||||
|
suspend fun allPlaylists(): List<Playlist>
|
||||||
|
|
||||||
|
suspend fun allGenres(): List<Genre>
|
||||||
|
|
||||||
|
suspend fun search(query: String?): MutableList<Any>
|
||||||
|
|
||||||
|
suspend fun getPlaylistSongs(playlist: Playlist): List<Song>
|
||||||
|
|
||||||
|
suspend fun getGenre(genreId: Int): List<Song>
|
||||||
|
|
||||||
|
suspend fun 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<Artist>
|
||||||
|
|
||||||
|
suspend fun topArtists(): List<Artist>
|
||||||
|
|
||||||
|
suspend fun topAlbums(): List<Album>
|
||||||
|
|
||||||
|
suspend fun recentAlbums(): List<Album>
|
||||||
|
|
||||||
|
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<Home>
|
||||||
|
|
||||||
|
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
|
||||||
|
|
||||||
|
fun songsFlow(): Flow<Result<List<Song>>>
|
||||||
|
|
||||||
|
fun albumsFlow(): Flow<Result<List<Album>>>
|
||||||
|
|
||||||
|
fun artistsFlow(): Flow<Result<List<Artist>>>
|
||||||
|
|
||||||
|
fun playlistsFlow(): Flow<Result<List<Playlist>>>
|
||||||
|
|
||||||
|
fun genresFlow(): Flow<Result<List<Genre>>>
|
||||||
|
|
||||||
|
suspend fun playlist(playlistId: Int): Playlist
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealRepository(
|
||||||
private val context: Context,
|
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 {
|
) : Repository {
|
||||||
|
|
||||||
override suspend fun allAlbums(): List<Album> = AlbumLoader.getAllAlbums(context)
|
override suspend fun allAlbums(): List<Album> = 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<Artist> = ArtistLoader.getAllArtists(context)
|
override suspend fun allArtists(): List<Artist> = artistRepository.artists()
|
||||||
|
|
||||||
override suspend fun artistById(artistId: Int): Artist =
|
override suspend fun artistById(artistId: Int): Artist = artistRepository.artist(artistId)
|
||||||
ArtistLoader.getArtist(context, artistId)
|
|
||||||
|
|
||||||
override suspend fun recentArtists(): List<Artist> =
|
override suspend fun recentArtists(): List<Artist> = lastAddedRepository.recentArtists()
|
||||||
LastAddedSongsLoader.getLastAddedArtists(context)
|
|
||||||
|
|
||||||
override suspend fun topArtists(): List<Artist> =
|
override suspend fun recentAlbums(): List<Album> = lastAddedRepository.recentAlbums()
|
||||||
TopAndRecentlyPlayedTracksLoader.getTopArtists(context)
|
|
||||||
|
|
||||||
override suspend fun topAlbums(): List<Album> =
|
override suspend fun topArtists(): List<Artist> = playedTracksRepository.topArtists()
|
||||||
TopAndRecentlyPlayedTracksLoader.getTopAlbums(context)
|
|
||||||
|
|
||||||
override suspend fun recentAlbums(): List<Album> =
|
override suspend fun topAlbums(): List<Album> = playedTracksRepository.topAlbums()
|
||||||
LastAddedSongsLoader.getLastAddedAlbums(context)
|
|
||||||
|
|
||||||
override suspend fun allPlaylists(): List<Playlist> = PlaylistLoader.getAllPlaylists(context)
|
override suspend fun allPlaylists(): List<Playlist> = playlistRepository.playlists()
|
||||||
|
|
||||||
override suspend fun allGenres(): List<Genre> = GenreLoader.getAllGenres(context)
|
override suspend fun allGenres(): List<Genre> = genreRepository.genres()
|
||||||
|
|
||||||
override suspend fun allSongs(): List<Song> = SongLoader.getAllSongs(context)
|
override suspend fun allSongs(): List<Song> = songRepository.songs()
|
||||||
|
|
||||||
|
|
||||||
override suspend fun search(query: String?): MutableList<Any> =
|
override suspend fun search(query: String?): MutableList<Any> =
|
||||||
SearchLoader.searchAll(context, query)
|
searchRepository.searchAll(context, query)
|
||||||
|
|
||||||
override suspend fun getPlaylistSongs(playlist: Playlist): ArrayList<Song> {
|
override suspend fun getPlaylistSongs(playlist: Playlist): List<Song> {
|
||||||
return if (playlist is AbsCustomPlaylist) {
|
return if (playlist is AbsCustomPlaylist) {
|
||||||
playlist.getSongs(context)
|
playlist.songs()
|
||||||
} else {
|
} else {
|
||||||
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
|
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getGenre(genreId: Int): ArrayList<Song> =
|
override suspend fun getGenre(genreId: Int): List<Song> = genreRepository.songs(genreId)
|
||||||
GenreLoader.getSongs(context, genreId)
|
|
||||||
|
|
||||||
|
|
||||||
override suspend fun artistInfo(
|
override suspend fun artistInfo(
|
||||||
|
@ -137,13 +202,16 @@ class RepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun playlists(): Home {
|
suspend fun playlists(): Home {
|
||||||
val playlist = PlaylistLoader.getAllPlaylists(context)
|
val playlist = playlistRepository.playlists()
|
||||||
return Home(playlist, TOP_ALBUMS)
|
return Home(playlist, TOP_ALBUMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun playlists(playlistId: Int) =
|
||||||
|
playlistRepository.playlist(playlistId)
|
||||||
|
|
||||||
override suspend fun suggestionsHome(): Home {
|
override suspend fun suggestionsHome(): Home {
|
||||||
val songs =
|
val songs =
|
||||||
NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().takeIf {
|
NotPlayedPlaylist(context).songs().shuffled().takeIf {
|
||||||
it.size > 9
|
it.size > 9
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
println(songs.size)
|
println(songs.size)
|
||||||
|
@ -151,33 +219,34 @@ class RepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun genresHome(): Home {
|
override suspend fun genresHome(): Home {
|
||||||
val genres = GenreLoader.getAllGenres(context).shuffled()
|
val genres = genreRepository.genres().shuffled()
|
||||||
return Home(genres, GENRES)
|
return Home(genres, GENRES)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun recentArtistsHome(): Home {
|
override suspend fun recentArtistsHome(): Home {
|
||||||
val artists = LastAddedSongsLoader.getLastAddedArtists(context).take(5)
|
val artists = lastAddedRepository.recentArtists().take(5)
|
||||||
return Home(artists, RECENT_ARTISTS)
|
return Home(artists, RECENT_ARTISTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun recentAlbumsHome(): Home {
|
override suspend fun recentAlbumsHome(): Home {
|
||||||
val albums = LastAddedSongsLoader.getLastAddedAlbums(context).take(5)
|
val albums = lastAddedRepository.recentAlbums().take(5)
|
||||||
return Home(albums, RECENT_ALBUMS)
|
return Home(albums, RECENT_ALBUMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun topAlbumsHome(): Home {
|
override suspend fun topAlbumsHome(): Home {
|
||||||
val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context).take(5)
|
val albums = playedTracksRepository.topAlbums().take(5)
|
||||||
return Home(albums, TOP_ALBUMS)
|
return Home(albums, TOP_ALBUMS)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun topArtistsHome(): Home {
|
override suspend fun topArtistsHome(): Home {
|
||||||
val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context).take(5)
|
val artists = playedTracksRepository.topArtists().take(5)
|
||||||
return Home(artists, TOP_ARTISTS)
|
return Home(artists, TOP_ARTISTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun favoritePlaylistHome(): Home {
|
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())
|
val songs = if (playlists.isNotEmpty())
|
||||||
PlaylistSongsLoader.getPlaylistSongList(context, playlists[0])
|
PlaylistSongsLoader.getPlaylistSongList(context, playlists[0])
|
||||||
else emptyList<Song>()
|
else emptyList<Song>()
|
||||||
|
@ -187,7 +256,7 @@ class RepositoryImpl(
|
||||||
|
|
||||||
override fun songsFlow(): Flow<Result<List<Song>>> = flow {
|
override fun songsFlow(): Flow<Result<List<Song>>> = flow {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
val data = SongLoader.getAllSongs(context)
|
val data = songRepository.songs()
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
emit(Result.Error)
|
emit(Result.Error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -197,7 +266,7 @@ class RepositoryImpl(
|
||||||
|
|
||||||
override fun albumsFlow(): Flow<Result<List<Album>>> = flow {
|
override fun albumsFlow(): Flow<Result<List<Album>>> = flow {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
val data = AlbumLoader.getAllAlbums(context)
|
val data = albumRepository.albums()
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
emit(Result.Error)
|
emit(Result.Error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -207,7 +276,7 @@ class RepositoryImpl(
|
||||||
|
|
||||||
override fun artistsFlow(): Flow<Result<List<Artist>>> = flow {
|
override fun artistsFlow(): Flow<Result<List<Artist>>> = flow {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
val data = ArtistLoader.getAllArtists(context)
|
val data = artistRepository.artists()
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
emit(Result.Error)
|
emit(Result.Error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -217,7 +286,7 @@ class RepositoryImpl(
|
||||||
|
|
||||||
override fun playlistsFlow(): Flow<Result<List<Playlist>>> = flow {
|
override fun playlistsFlow(): Flow<Result<List<Playlist>>> = flow {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
val data = PlaylistLoader.getAllPlaylists(context)
|
val data = playlistRepository.playlists()
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
emit(Result.Error)
|
emit(Result.Error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,11 +296,14 @@ class RepositoryImpl(
|
||||||
|
|
||||||
override fun genresFlow(): Flow<Result<List<Genre>>> = flow {
|
override fun genresFlow(): Flow<Result<List<Genre>>> = flow {
|
||||||
emit(Result.Loading)
|
emit(Result.Loading)
|
||||||
val data = GenreLoader.getAllGenres(context)
|
val data = genreRepository.genres()
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
emit(Result.Error)
|
emit(Result.Error)
|
||||||
} else {
|
} else {
|
||||||
emit(Result.Success(data))
|
emit(Result.Success(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun playlist(playlistId: Int): Playlist =
|
||||||
|
playlistRepository.playlist(playlistId)
|
||||||
}
|
}
|
|
@ -12,36 +12,40 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.Context
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
import java.util.*
|
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<Any> {
|
fun searchAll(context: Context, query: String?): MutableList<Any> {
|
||||||
val results = mutableListOf<Any>()
|
val results = mutableListOf<Any>()
|
||||||
query?.let { searchString ->
|
query?.let { searchString ->
|
||||||
val songs = SongLoader.getSongs(context, searchString)
|
val songs = songRepository.songs(searchString)
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
results.add(context.resources.getString(R.string.songs))
|
results.add(context.resources.getString(R.string.songs))
|
||||||
results.addAll(songs)
|
results.addAll(songs)
|
||||||
}
|
}
|
||||||
|
val artists = artistRepository.artists(searchString)
|
||||||
val artists = ArtistLoader.getArtists(context, searchString)
|
|
||||||
if (artists.isNotEmpty()) {
|
if (artists.isNotEmpty()) {
|
||||||
results.add(context.resources.getString(R.string.artists))
|
results.add(context.resources.getString(R.string.artists))
|
||||||
results.addAll(artists)
|
results.addAll(artists)
|
||||||
}
|
}
|
||||||
|
|
||||||
val albums = AlbumLoader.getAlbums(context, searchString)
|
val albums = albumRepository.albums(searchString)
|
||||||
if (albums.isNotEmpty()) {
|
if (albums.isNotEmpty()) {
|
||||||
results.add(context.resources.getString(R.string.albums))
|
results.add(context.resources.getString(R.string.albums))
|
||||||
results.addAll(albums)
|
results.addAll(albums)
|
||||||
}
|
}
|
||||||
val genres: List<Genre> = GenreLoader.searchGenres(context)
|
val genres: List<Genre> = genreRepository.genres().filter { genre ->
|
||||||
.filter { genre ->
|
|
||||||
genre.name.toLowerCase(Locale.getDefault())
|
genre.name.toLowerCase(Locale.getDefault())
|
||||||
.contains(searchString.toLowerCase(Locale.getDefault()))
|
.contains(searchString.toLowerCase(Locale.getDefault()))
|
||||||
}
|
}
|
||||||
|
@ -49,8 +53,7 @@ object SearchLoader {
|
||||||
results.add(context.resources.getString(R.string.genres))
|
results.add(context.resources.getString(R.string.genres))
|
||||||
results.addAll(genres)
|
results.addAll(genres)
|
||||||
}
|
}
|
||||||
val playlist = PlaylistLoader.getAllPlaylists(context)
|
val playlist = playlistRepository.playlists().filter { playlist ->
|
||||||
.filter { playlist ->
|
|
||||||
playlist.name.toLowerCase(Locale.getDefault())
|
playlist.name.toLowerCase(Locale.getDefault())
|
||||||
.contains(searchString.toLowerCase(Locale.getDefault()))
|
.contains(searchString.toLowerCase(Locale.getDefault()))
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
@ -29,55 +29,63 @@ import java.util.*
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 10/08/17.
|
* Created by hemanths on 10/08/17.
|
||||||
*/
|
*/
|
||||||
|
interface SongRepository {
|
||||||
|
|
||||||
object SongLoader {
|
fun songs(): List<Song>
|
||||||
|
|
||||||
fun getAllSongs(
|
fun songs(cursor: Cursor?): List<Song>
|
||||||
context: Context
|
|
||||||
): ArrayList<Song> {
|
fun songs(query: String): List<Song>
|
||||||
val cursor = makeSongCursor(context, null, null)
|
|
||||||
return getSongs(cursor)
|
fun songsByFilePath(filePath: String): List<Song>
|
||||||
|
|
||||||
|
fun song(cursor: Cursor?): Song
|
||||||
|
|
||||||
|
fun song(songId: Int): Song
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealSongRepository(private val context: Context) : SongRepository {
|
||||||
|
|
||||||
|
override fun songs(): List<Song> {
|
||||||
|
return songs(makeSongCursor(null, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSongs(
|
override fun songs(cursor: Cursor?): List<Song> {
|
||||||
cursor: Cursor?
|
|
||||||
): ArrayList<Song> {
|
|
||||||
val songs = arrayListOf<Song>()
|
val songs = arrayListOf<Song>()
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
songs.add(getSongFromCursorImpl(cursor))
|
songs.add(getSongFromCursorImpl(cursor))
|
||||||
} while (cursor.moveToNext())
|
} while (cursor.moveToNext())
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor?.close()
|
cursor?.close()
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSongs(
|
override fun song(cursor: Cursor?): Song {
|
||||||
context: Context,
|
val song: Song = if (cursor != null && cursor.moveToFirst()) {
|
||||||
query: String
|
getSongFromCursorImpl(cursor)
|
||||||
): ArrayList<Song> {
|
|
||||||
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)
|
|
||||||
} else {
|
} else {
|
||||||
song = Song.emptySong
|
Song.emptySong
|
||||||
}
|
}
|
||||||
cursor?.close()
|
cursor?.close()
|
||||||
return song
|
return song
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
override fun songs(query: String): List<Song> {
|
||||||
fun getSong(context: Context, queryId: Int): Song {
|
return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")))
|
||||||
val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString()))
|
}
|
||||||
return getSong(cursor)
|
|
||||||
|
override fun song(songId: Int): Song {
|
||||||
|
return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString())))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun songsByFilePath(filePath: String): List<Song> {
|
||||||
|
return songs(
|
||||||
|
makeSongCursor(
|
||||||
|
MediaStore.Audio.AudioColumns.DATA + "=?",
|
||||||
|
arrayOf(filePath)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongFromCursorImpl(
|
private fun getSongFromCursorImpl(
|
||||||
|
@ -115,7 +123,6 @@ object SongLoader {
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun makeSongCursor(
|
fun makeSongCursor(
|
||||||
context: Context,
|
|
||||||
selection: String?,
|
selection: String?,
|
||||||
selectionValues: Array<String>?,
|
selectionValues: Array<String>?,
|
||||||
sortOrder: String = PreferenceUtil.songSortOrder
|
sortOrder: String = PreferenceUtil.songSortOrder
|
||||||
|
@ -131,8 +138,16 @@ object SongLoader {
|
||||||
// Blacklist
|
// Blacklist
|
||||||
val paths = BlacklistStore.getInstance(context).paths
|
val paths = BlacklistStore.getInstance(context).paths
|
||||||
if (paths.isNotEmpty()) {
|
if (paths.isNotEmpty()) {
|
||||||
selectionFinal = generateBlacklistSelection(selectionFinal, paths.size)
|
selectionFinal =
|
||||||
selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths)
|
generateBlacklistSelection(
|
||||||
|
selectionFinal,
|
||||||
|
paths.size
|
||||||
|
)
|
||||||
|
selectionValuesFinal =
|
||||||
|
addBlacklistSelectionValues(
|
||||||
|
selectionValuesFinal,
|
||||||
|
paths
|
||||||
|
)
|
||||||
}
|
}
|
||||||
selectionFinal =
|
selectionFinal =
|
||||||
selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
|
selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
|
|
@ -11,7 +11,7 @@
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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.AbstractCursor;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
|
@ -11,7 +11,7 @@
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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.AbstractCursor;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
|
@ -12,59 +12,85 @@
|
||||||
* See the GNU General Public License for more details.
|
* 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.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS
|
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.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.providers.HistoryStore
|
import code.name.monkey.retromusic.providers.HistoryStore
|
||||||
import code.name.monkey.retromusic.providers.SongPlayCountStore
|
import code.name.monkey.retromusic.providers.SongPlayCountStore
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hemanths on 16/08/17.
|
* Created by hemanths on 16/08/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
object TopAndRecentlyPlayedTracksLoader {
|
interface TopPlayedRepository {
|
||||||
|
fun recentlyPlayedTracks(): List<Song>
|
||||||
|
|
||||||
fun getRecentlyPlayedTracks(context: Context): ArrayList<Song> {
|
fun topTracks(): List<Song>
|
||||||
return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context))
|
|
||||||
|
fun notRecentlyPlayedTracks(): List<Song>
|
||||||
|
|
||||||
|
fun topAlbums(): List<Album>
|
||||||
|
|
||||||
|
fun topArtists(): List<Artist>
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealTopPlayedRepository(
|
||||||
|
private val context: Context,
|
||||||
|
private val songRepository: RealSongRepository,
|
||||||
|
private val albumRepository: RealAlbumRepository,
|
||||||
|
private val artistRepository: RealArtistRepository
|
||||||
|
) : TopPlayedRepository {
|
||||||
|
|
||||||
|
override fun recentlyPlayedTracks(): List<Song> {
|
||||||
|
return songRepository.songs(makeRecentTracksCursorAndClearUpDatabase())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTopTracks(context: Context): ArrayList<Song> {
|
override fun topTracks(): List<Song> {
|
||||||
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context))
|
return songRepository.songs(makeTopTracksCursorAndClearUpDatabase())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNotRecentlyPlayedTracks(context: Context): ArrayList<Song> {
|
override fun notRecentlyPlayedTracks(): List<Song> {
|
||||||
val allSongs = SongLoader.getSongs(
|
val allSongs = mutableListOf<Song>().apply {
|
||||||
makeSongCursor(
|
addAll(
|
||||||
context,
|
songRepository.songs(
|
||||||
|
songRepository.makeSongCursor(
|
||||||
null, null,
|
null, null,
|
||||||
MediaStore.Audio.Media.DATE_ADDED + " ASC"
|
MediaStore.Audio.Media.DATE_ADDED + " ASC"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val playedSongs = SongLoader.getSongs(
|
|
||||||
makePlayedTracksCursorAndClearUpDatabase(context)
|
|
||||||
)
|
)
|
||||||
val notRecentlyPlayedSongs = SongLoader.getSongs(
|
}
|
||||||
makeNotRecentTracksCursorAndClearUpDatabase(context)
|
val playedSongs = songRepository.songs(
|
||||||
|
makePlayedTracksCursorAndClearUpDatabase()
|
||||||
|
)
|
||||||
|
val notRecentlyPlayedSongs = songRepository.songs(
|
||||||
|
makeNotRecentTracksCursorAndClearUpDatabase()
|
||||||
)
|
)
|
||||||
allSongs.removeAll(playedSongs)
|
allSongs.removeAll(playedSongs)
|
||||||
allSongs.addAll(notRecentlyPlayedSongs)
|
allSongs.addAll(notRecentlyPlayedSongs)
|
||||||
return allSongs
|
return allSongs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
override fun topAlbums(): List<Album> {
|
||||||
val retCursor = makeTopTracksCursorImpl(context)
|
return albumRepository.splitIntoAlbums(topTracks())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun topArtists(): List<Artist> {
|
||||||
|
return artistRepository.splitIntoArtists(topAlbums())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun makeTopTracksCursorAndClearUpDatabase(): Cursor? {
|
||||||
|
val retCursor = makeTopTracksCursorImpl()
|
||||||
// clean up the databases with any ids not found
|
// clean up the databases with any ids not found
|
||||||
if (retCursor != null) {
|
if (retCursor != null) {
|
||||||
val missingIds = retCursor.missingIds
|
val missingIds = retCursor.missingIds
|
||||||
|
@ -77,33 +103,31 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
return retCursor
|
return retCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? {
|
private fun makeRecentTracksCursorImpl(): SortedLongCursor? {
|
||||||
// first get the top results ids from the internal database
|
// first get the top results ids from the internal database
|
||||||
val songs = HistoryStore.getInstance(context).queryRecentIds()
|
val songs = HistoryStore.getInstance(context).queryRecentIds()
|
||||||
songs.use {
|
songs.use {
|
||||||
return makeSortedCursor(
|
return makeSortedCursor(
|
||||||
context,
|
|
||||||
it,
|
it,
|
||||||
it.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
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
|
// first get the top results ids from the internal database
|
||||||
val songs =
|
val songs =
|
||||||
SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
|
SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
|
||||||
|
|
||||||
songs.use { localSongs ->
|
songs.use { localSongs ->
|
||||||
return makeSortedCursor(
|
return makeSortedCursor(
|
||||||
context, localSongs,
|
localSongs,
|
||||||
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)
|
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeSortedCursor(
|
private fun makeSortedCursor(
|
||||||
context: Context,
|
|
||||||
cursor: Cursor?, idColumn: Int
|
cursor: Cursor?, idColumn: Int
|
||||||
): SortedLongCursor? {
|
): SortedLongCursor? {
|
||||||
|
|
||||||
|
@ -131,48 +155,46 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
selection.append(")")
|
selection.append(")")
|
||||||
|
|
||||||
// get a list of songs with the data given the selection statement
|
// 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) {
|
if (songCursor != null) {
|
||||||
// now return the wrapped TopTracksCursor to handle sorting given order
|
// now return the wrapped TopTracksCursor to handle sorting given order
|
||||||
return SortedLongCursor(songCursor, order, BaseColumns._ID)
|
return SortedLongCursor(
|
||||||
|
songCursor,
|
||||||
|
order,
|
||||||
|
BaseColumns._ID
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTopAlbums(
|
private fun makeRecentTracksCursorAndClearUpDatabase(): Cursor? {
|
||||||
context: Context
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(
|
||||||
): ArrayList<Album> {
|
ignoreCutoffTime = false,
|
||||||
arrayListOf<Album>()
|
reverseOrder = false
|
||||||
return AlbumLoader.splitIntoAlbums(getTopTracks(context))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTopArtists(context: Context): ArrayList<Artist> {
|
private fun makePlayedTracksCursorAndClearUpDatabase(): Cursor? {
|
||||||
return ArtistLoader.splitIntoArtists(getTopAlbums(context))
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(
|
||||||
|
ignoreCutoffTime = true,
|
||||||
|
reverseOrder = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun makeNotRecentTracksCursorAndClearUpDatabase(): Cursor? {
|
||||||
fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
return makeRecentTracksCursorAndClearUpDatabaseImpl(
|
||||||
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, false)
|
ignoreCutoffTime = false,
|
||||||
}
|
reverseOrder = true
|
||||||
|
)
|
||||||
|
|
||||||
fun makePlayedTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
|
||||||
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun makeNotRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? {
|
|
||||||
return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeRecentTracksCursorAndClearUpDatabaseImpl(
|
private fun makeRecentTracksCursorAndClearUpDatabaseImpl(
|
||||||
context: Context,
|
|
||||||
ignoreCutoffTime: Boolean,
|
ignoreCutoffTime: Boolean,
|
||||||
reverseOrder: Boolean
|
reverseOrder: Boolean
|
||||||
): SortedLongCursor? {
|
): 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
|
||||||
// clean up the databases with any ids not found
|
// clean up the databases with any ids not found
|
||||||
if (retCursor != null) {
|
if (retCursor != null) {
|
||||||
|
@ -186,9 +208,7 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
return retCursor
|
return retCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun makeRecentTracksCursorImpl(
|
private fun makeRecentTracksCursorImpl(
|
||||||
context: Context,
|
|
||||||
ignoreCutoffTime: Boolean,
|
ignoreCutoffTime: Boolean,
|
||||||
reverseOrder: Boolean
|
reverseOrder: Boolean
|
||||||
): SortedLongCursor? {
|
): SortedLongCursor? {
|
||||||
|
@ -198,7 +218,6 @@ object TopAndRecentlyPlayedTracksLoader {
|
||||||
HistoryStore.getInstance(context).queryRecentIds(cutoff * if (reverseOrder) -1 else 1)
|
HistoryStore.getInstance(context).queryRecentIds(cutoff * if (reverseOrder) -1 else 1)
|
||||||
return songs.use {
|
return songs.use {
|
||||||
makeSortedCursor(
|
makeSortedCursor(
|
||||||
context,
|
|
||||||
it,
|
it,
|
||||||
it.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
it.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
|
||||||
)
|
)
|
|
@ -327,7 +327,7 @@ public class MusicService extends Service implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getTrackUri(@NonNull Song song) {
|
private static String getTrackUri(@NonNull Song song) {
|
||||||
return MusicUtil.getSongFileUri(song.getId()).toString();
|
return MusicUtil.INSTANCE.getSongFileUri(song.getId()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -790,7 +790,7 @@ public class MusicService extends Service implements
|
||||||
pendingQuit = true;
|
pendingQuit = true;
|
||||||
break;
|
break;
|
||||||
case TOGGLE_FAVORITE:
|
case TOGGLE_FAVORITE:
|
||||||
MusicUtil.toggleFavorite(getApplicationContext(), getCurrentSong());
|
MusicUtil.INSTANCE.toggleFavorite(getApplicationContext(), getCurrentSong());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1216,7 +1216,7 @@ public class MusicService extends Service implements
|
||||||
Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST);
|
Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST);
|
||||||
int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode());
|
int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode());
|
||||||
if (playlist != null) {
|
if (playlist != null) {
|
||||||
ArrayList<Song> playlistSongs = playlist.getSongs(getApplicationContext());
|
List<Song> playlistSongs = playlist.getSongs();
|
||||||
if (!playlistSongs.isEmpty()) {
|
if (!playlistSongs.isEmpty()) {
|
||||||
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
|
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
|
||||||
int startPosition = new Random().nextInt(playlistSongs.size());
|
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)
|
TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon)
|
||||||
.build());
|
.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;
|
? R.drawable.ic_favorite : R.drawable.ic_favorite_border;
|
||||||
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
|
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
|
||||||
TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon)
|
TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon)
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class AutoGeneratedPlaylistBitmap {
|
||||||
private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Integer id) {
|
private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Integer id) {
|
||||||
try {
|
try {
|
||||||
return Glide.with(context)
|
return Glide.with(context)
|
||||||
.load(MusicUtil.getMediaStoreAlbumCoverUri(id))
|
.load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id))
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.into(200, 200)
|
.into(200, 200)
|
||||||
.get();
|
.get();
|
||||||
|
|
|
@ -31,15 +31,14 @@ import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
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.model.Song;
|
||||||
|
import code.name.monkey.retromusic.repository.RealSongRepository;
|
||||||
|
import code.name.monkey.retromusic.repository.SortedCursor;
|
||||||
|
|
||||||
|
|
||||||
public final class FileUtil {
|
public final class FileUtil {
|
||||||
|
@ -59,9 +58,9 @@ public final class FileUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static ArrayList<Song> matchFilesWithMediaStore(@NonNull Context context,
|
public static List<Song> matchFilesWithMediaStore(@NonNull Context context,
|
||||||
@Nullable List<File> files) {
|
@Nullable List<File> files) {
|
||||||
return SongLoader.INSTANCE.getSongs(makeSongCursor(context, files));
|
return new RealSongRepository(context).songs(makeSongCursor(context, files));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String safeGetCanonicalPath(File file) {
|
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
|
return songCursor == null ? null
|
||||||
: new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA);
|
: new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA);
|
||||||
|
|
|
@ -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<Song> songs,
|
|
||||||
@Nullable final List<Uri> 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<Pattern> 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<Song> 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<Song> 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<Song> 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("<unknown>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Pattern>()
|
||||||
|
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<Song>
|
||||||
|
): 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<Song>): 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<Song>, 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 == "<unknown>"
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Song>,
|
||||||
|
safUris: List<Uri>?,
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ public class PlaylistsUtil {
|
||||||
final StringBuilder selection = new StringBuilder();
|
final StringBuilder selection = new StringBuilder();
|
||||||
selection.append(MediaStore.Audio.Playlists._ID + " IN (");
|
selection.append(MediaStore.Audio.Playlists._ID + " IN (");
|
||||||
for (int i = 0; i < playlists.size(); i++) {
|
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) {
|
if (i < playlists.size() - 1) {
|
||||||
selection.append(",");
|
selection.append(",");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue