Merge branch 'room-playlist' of https://github.com/h4h13/RetroMusicPlayer into room-playlist

This commit is contained in:
Hemanth S 2020-08-24 22:00:49 +05:30
commit 3265580275
126 changed files with 1023 additions and 718 deletions

View file

@ -34,7 +34,7 @@ android {
} }
signingConfigs { signingConfigs {
release { release {
Properties properties = getProperties('/Users/h4h13/Documents/Github/retro.properties') Properties properties = getProperties('/Users/apple/Documents/Github/retro.properties ')
storeFile file(getProperty(properties, 'storeFile')) storeFile file(getProperty(properties, 'storeFile'))
keyAlias getProperty(properties, 'keyAlias') keyAlias getProperty(properties, 'keyAlias')
storePassword getProperty(properties, 'storePassword') storePassword getProperty(properties, 'storePassword')
@ -129,6 +129,7 @@ dependencies {
implementation "com.afollestad.material-dialogs:core:$material_dialog_version" implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
implementation "com.afollestad.material-dialogs:commons:$material_dialog_version" implementation "com.afollestad.material-dialogs:commons:$material_dialog_version"
implementation 'com.afollestad:material-cab:0.1.12' implementation 'com.afollestad:material-cab:0.1.12'
implementation 'com.afollestad:recyclical:1.1.1'
implementation 'com.github.bumptech.glide:glide:3.8.0' implementation 'com.github.bumptech.glide:glide:3.8.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0' implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0'
@ -180,5 +181,8 @@ dependencies {
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android" <font-family xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:app="http://schemas.android.com/apk/res-auto">
<font <font
android:font="@font/regular" android:font="@font/google_sans_regular"
android:fontWeight="400" /> android:fontWeight="400" />
<font <font
android:font="@font/medium" android:font="@font/google_sans_medium"
android:fontWeight="600" /> android:fontWeight="600" />
<font <font
android:font="@font/bold" android:font="@font/google_sans_bold"
android:fontWeight="700" /> android:fontWeight="700" />
</font-family> </font-family>

View file

@ -255,11 +255,9 @@
<meta-data <meta-data
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule" android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
android:value="GlideModule" /> android:value="GlideModule" />
<meta-data <meta-data
android:name="com.android.vending.splits.required" android:name="com.android.vending.splits.required"
android:value="true" /> android:value="true" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application> </application>
</manifest> </manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

@ -20,7 +20,6 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import com.amitshekhar.DebugDB
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.TransactionDetails
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
@ -33,7 +32,7 @@ class App : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
instance = this instance = this
DebugDB.getAddressLog();
startKoin { startKoin {
androidContext(this@App) androidContext(this@App)
modules(appModules) modules(appModules)

View file

@ -23,3 +23,6 @@ const val SUGGESTIONS = 5
const val FAVOURITES = 4 const val FAVOURITES = 4
const val GENRES = 6 const val GENRES = 6
const val PLAYLISTS = 7 const val PLAYLISTS = 7
const val HISTORY_PLAYLIST = 8
const val LAST_ADDED_PLAYLIST = 9
const val TOP_PLAYED_PLAYLIST = 10

View file

@ -1,9 +1,12 @@
package code.name.monkey.retromusic package code.name.monkey.retromusic
import code.name.monkey.retromusic.db.PlaylistDatabase import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import code.name.monkey.retromusic.db.BlackListStoreEntity
import code.name.monkey.retromusic.db.PlaylistDao
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.repository.RealRoomPlaylistRepository import code.name.monkey.retromusic.db.RetroDatabase
import code.name.monkey.retromusic.repository.RoomPlaylistRepository
import code.name.monkey.retromusic.fragments.LibraryViewModel 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
@ -13,11 +16,42 @@ import code.name.monkey.retromusic.fragments.search.SearchViewModel
import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.network.networkModule import code.name.monkey.retromusic.network.networkModule
import code.name.monkey.retromusic.repository.* import code.name.monkey.retromusic.repository.*
import code.name.monkey.retromusic.util.FilePathUtil
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.koin.android.ext.koin.androidContext 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 roomModule = module {
single {
Room.databaseBuilder(androidContext(), RetroDatabase::class.java, "playlist.db")
.allowMainThreadQueries()
.addCallback(object : RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
GlobalScope.launch(IO) {
FilePathUtil.blacklistFilePaths().map {
get<PlaylistDao>().insertBlacklistPath(BlackListStoreEntity(it))
}
}
}
})
.fallbackToDestructiveMigration()
.build()
}
factory {
get<RetroDatabase>().playlistDao()
}
single {
RealRoomRepository(get())
} bind RoomPlaylistRepository::class
}
private val mainModule = module { private val mainModule = module {
single { single {
androidContext().contentResolver androidContext().contentResolver
@ -70,14 +104,6 @@ private val dataModule = module {
get() get()
) )
} }
single {
PlaylistDatabase.getDatabase(get()).playlistDao()
}
single {
RealRoomPlaylistRepository(get())
} bind RoomPlaylistRepository::class
} }
private val viewModules = module { private val viewModules = module {
@ -119,4 +145,4 @@ private val viewModules = module {
} }
} }
val appModules = listOf(mainModule, dataModule, viewModules, networkModule) val appModules = listOf(mainModule, dataModule, viewModules, networkModule, roomModule)

View file

@ -218,7 +218,7 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
.build() .build()
.transform(BlurTransformation.Builder(this).build()) .transform(BlurTransformation.Builder(this).build())
.into(object : RetroMusicColoredTarget(image) { .into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(color: MediaNotificationProcessor) { override fun onColorReady(colors: MediaNotificationProcessor) {
} }
}) })
} }

View file

@ -108,7 +108,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
if (intent.action != null && (intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) if (intent.action != null && (intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)
) { ) {
val songs: List<Song> = val songs: List<Song> =
getSongs(this, intent.extras!!) getSongs(intent.extras!!)
if (shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) { if (shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
openAndShuffleQueue(songs, true) openAndShuffleQueue(songs, true)
} else { } else {

View file

@ -104,7 +104,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
} }
} }
}) })
val fastScroller = ThemedFastScroller.create(recyclerView) ThemedFastScroller.create(recyclerView)
} }
private fun checkForPadding() { private fun checkForPadding() {

View file

@ -108,7 +108,7 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
super.onDestroy() super.onDestroy()
} }
private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) : private class RestorePurchaseAsyncTask(purchaseActivity: PurchaseActivity) :
AsyncTask<Void, Void, Boolean>() { AsyncTask<Void, Void, Boolean>() {
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference( private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(

View file

@ -110,7 +110,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
} }
if (requestCode == TEZ_REQUEST_CODE) { if (requestCode == TEZ_REQUEST_CODE) {
// Process based on the data in response. // Process based on the data in response.
Log.d("result", data!!.getStringExtra("Status")) //Log.d("result", data!!.getStringExtra("Status"))
} }
} }
@ -121,7 +121,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
} }
} }
private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) : private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelopmentActivity) :
AsyncTask<Void, Void, List<SkuDetails>>() { AsyncTask<Void, Void, List<SkuDetails>>() {
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference( private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(

View file

@ -160,7 +160,7 @@ class UserInfoActivity : AbsBaseActivity() {
} }
private fun saveImage(bitmap: Bitmap, fileName: String) { private fun saveImage(bitmap: Bitmap, fileName: String) {
CoroutineScope(Dispatchers.IO).launch() { CoroutineScope(Dispatchers.IO).launch {
val appDir = applicationContext.filesDir val appDir = applicationContext.filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false

View file

@ -28,7 +28,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.synthetic.main.sliding_music_panel_layout.* import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
companion object { companion object {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
} }

View file

@ -280,22 +280,22 @@ open class BugReportActivity : AbsThemeActivity() {
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(context) RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(context)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_wrong_credentials) .setMessage(R.string.bug_report_failed_wrong_credentials)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(context) RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(context)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_invalid_token) .setMessage(R.string.bug_report_failed_invalid_token)
.setPositiveButton(android.R.string.ok, null).show() .setPositiveButton(R.string.ok, null).show()
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(context) RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(context)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_issues_not_available) .setMessage(R.string.bug_report_failed_issues_not_available)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(R.string.ok, null)
else -> MaterialAlertDialogBuilder(context) else -> MaterialAlertDialogBuilder(context)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_unknown) .setMessage(R.string.bug_report_failed_unknown)
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() } .setPositiveButton(R.string.ok) { _, _ -> tryToFinishActivity() }
.setNegativeButton(android.R.string.cancel) { _, _ -> { tryToFinishActivity() } } .setNegativeButton(android.R.string.cancel) { _, _ -> tryToFinishActivity() }
} }
} }

View file

@ -44,9 +44,9 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
window.enterTransition = slide window.enterTransition = slide
} }
override fun loadImageFromFile(selectedFileUri: Uri?) { override fun loadImageFromFile(selectedFile: Uri?) {
Glide.with(this@AlbumTagEditorActivity).load(selectedFileUri).asBitmap() Glide.with(this@AlbumTagEditorActivity).load(selectedFile).asBitmap()
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java) .transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : SimpleTarget<BitmapPaletteWrapper>() { .into(object : SimpleTarget<BitmapPaletteWrapper>() {

View file

@ -1,5 +1,6 @@
package code.name.monkey.retromusic.adapter package code.name.monkey.retromusic.adapter
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -22,14 +23,14 @@ class GenreAdapter(
var dataSet: List<Genre>, var dataSet: List<Genre>,
private val mItemLayoutRes: Int private val mItemLayoutRes: Int
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() { ) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
val colors = listOf<Int>(Color.RED, Color.BLUE)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false)) return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val genre = dataSet[position] val genre = dataSet[position]
holder.title?.text = genre.name holder.title?.text = genre.name
holder.text?.text = String.format( holder.text?.text = String.format(
Locale.getDefault(), Locale.getDefault(),

View file

@ -40,8 +40,8 @@ class HomeAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layout = LayoutInflater.from(activity) val layout =
.inflate(R.layout.section_recycler_view, parent, false) LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
return when (viewType) { return when (viewType) {
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout) RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
GENRES -> GenreViewHolder(layout) GENRES -> GenreViewHolder(layout)
@ -64,7 +64,7 @@ class HomeAdapter(
when (getItemViewType(position)) { when (getItemViewType(position)) {
RECENT_ALBUMS -> { RECENT_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder val viewHolder = holder as AlbumViewHolder
viewHolder.bindView(home.arrayList as List<Album>, R.string.recent_albums) viewHolder.bindView(home)
viewHolder.clickableArea.setOnClickListener { viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment, R.id.detailListFragment,
@ -74,7 +74,7 @@ class HomeAdapter(
} }
TOP_ALBUMS -> { TOP_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder val viewHolder = holder as AlbumViewHolder
viewHolder.bindView(home.arrayList as List<Album>, R.string.top_albums) viewHolder.bindView(home)
viewHolder.clickableArea.setOnClickListener { viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment, R.id.detailListFragment,
@ -84,7 +84,7 @@ class HomeAdapter(
} }
RECENT_ARTISTS -> { RECENT_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder val viewHolder = holder as ArtistViewHolder
viewHolder.bindView(home.arrayList, R.string.recent_artists) viewHolder.bindView(home)
viewHolder.clickableArea.setOnClickListener { viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment, R.id.detailListFragment,
@ -94,7 +94,7 @@ class HomeAdapter(
} }
TOP_ARTISTS -> { TOP_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder val viewHolder = holder as ArtistViewHolder
viewHolder.bindView(home.arrayList, R.string.top_artists) viewHolder.bindView(home)
viewHolder.clickableArea.setOnClickListener { viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate( activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment, R.id.detailListFragment,
@ -104,15 +104,21 @@ class HomeAdapter(
} }
SUGGESTIONS -> { SUGGESTIONS -> {
val viewHolder = holder as SuggestionsViewHolder val viewHolder = holder as SuggestionsViewHolder
viewHolder.bindView(home.arrayList) viewHolder.bindView(home)
} }
FAVOURITES -> { FAVOURITES -> {
val viewHolder = holder as PlaylistViewHolder val viewHolder = holder as PlaylistViewHolder
viewHolder.bindView(home.arrayList, R.string.favorites) viewHolder.bindView(home)
viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment,
bundleOf("type" to FAVOURITES)
)
}
} }
GENRES -> { GENRES -> {
val viewHolder = holder as GenreViewHolder val viewHolder = holder as GenreViewHolder
viewHolder.bind(home.arrayList, R.string.genres) viewHolder.bind(home)
} }
PLAYLISTS -> { PLAYLISTS -> {
@ -130,22 +136,22 @@ class HomeAdapter(
} }
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) { private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(albums: List<Album>, titleRes: Int) { fun bindView(home: Home) {
title.text = activity.getString(titleRes) title.setText(home.titleRes)
recyclerView.apply { recyclerView.apply {
adapter = albumAdapter(albums) adapter = albumAdapter(home.arrayList as List<Album>)
layoutManager = gridLayoutManager() layoutManager = gridLayoutManager()
} }
} }
} }
private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(artists: List<Any>, titleRes: Int) { fun bindView(home: Home) {
title.setText(home.titleRes)
recyclerView.apply { recyclerView.apply {
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
adapter = artistsAdapter(artists as List<Artist>) adapter = artistsAdapter(home.arrayList as List<Artist>)
} }
title.text = activity.getString(titleRes)
} }
} }
@ -161,8 +167,7 @@ class HomeAdapter(
R.id.image8 R.id.image8
) )
fun bindView(songs: List<Any>) { fun bindView(home: Home) {
songs as List<Song>
val color = ThemeStore.accentColor(activity) val color = ThemeStore.accentColor(activity)
itemView.findViewById<TextView>(R.id.message).setTextColor(color) itemView.findViewById<TextView>(R.id.message).setTextColor(color)
itemView.findViewById<MaterialCardView>(R.id.card6).apply { itemView.findViewById<MaterialCardView>(R.id.card6).apply {
@ -170,9 +175,9 @@ class HomeAdapter(
} }
images.forEachIndexed { index, id -> images.forEachIndexed { index, id ->
itemView.findViewById<View>(id).setOnClickListener { itemView.findViewById<View>(id).setOnClickListener {
MusicPlayerRemote.playNext(songs[index]) MusicPlayerRemote.playNext(home.arrayList[index] as Song)
} }
SongGlideRequest.Builder.from(Glide.with(activity), songs[index]) SongGlideRequest.Builder.from(Glide.with(activity), home.arrayList[index] as Song)
.asBitmap() .asBitmap()
.build() .build()
.into(itemView.findViewById(id)) .into(itemView.findViewById(id))
@ -182,35 +187,37 @@ class HomeAdapter(
} }
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) { private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(songs: List<Any>, titleRes: Int) { fun bindView(home: Home) {
arrow.hide() title.setText(home.titleRes)
recyclerView.apply { recyclerView.apply {
val songAdapter = SongAdapter( val songAdapter = SongAdapter(
activity, activity,
songs as MutableList<Song>, home.arrayList as MutableList<Song>,
R.layout.item_album_card, null R.layout.item_album_card, null
) )
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
adapter = songAdapter adapter = songAdapter
} }
title.text = activity.getString(titleRes)
} }
} }
private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) { private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) {
fun bind(genres: List<Any>, titleRes: Int) { fun bind(home: Home) {
arrow.hide() arrow.hide()
title.text = activity.getString(titleRes) title.setText(home.titleRes)
val genreAdapter = GenreAdapter(
activity,
home.arrayList as List<Genre>,
R.layout.item_grid_genre
)
recyclerView.apply { recyclerView.apply {
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false) layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false)
val genreAdapter =
GenreAdapter(activity, genres as List<Genre>, R.layout.item_grid_genre)
adapter = genreAdapter adapter = genreAdapter
} }
} }
} }
open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) { open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
val title: AppCompatTextView = itemView.findViewById(R.id.title) val title: AppCompatTextView = itemView.findViewById(R.id.title)
val arrow: ImageView = itemView.findViewById(R.id.arrow) val arrow: ImageView = itemView.findViewById(R.id.arrow)

View file

@ -3,7 +3,6 @@ package code.name.monkey.retromusic.adapter.album
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.albums.AlbumClickListener
import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.AlbumGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -30,8 +29,8 @@ class HorizontalAlbumAdapter(
} }
override fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) { override fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary)) //holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary))
holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary)) //holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary))
} }
override fun loadAlbumCover(album: Album, holder: ViewHolder) { override fun loadAlbumCover(album: Album, holder: ViewHolder) {

View file

@ -6,6 +6,7 @@ import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.R.menu import code.name.monkey.retromusic.R.menu
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.model.PlaylistSong
@ -56,7 +57,7 @@ class OrderablePlaylistSongAdapter(
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<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 -> {
RemoveSongFromPlaylistDialog.create(selection.to(playlist.playListId)) RemoveSongFromPlaylistDialog.create(selection.toSongs(playlist.playListId))
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST") .show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
return return
} }

View file

@ -169,7 +169,7 @@ class PlayingQueueAdapter(
holder: ViewHolder?, holder: ViewHolder?,
position: Int, @SwipeableItemResults result: Int position: Int, @SwipeableItemResults result: Int
): SwipeResultAction { ): SwipeResultAction {
return if (result === SwipeableItemConstants.RESULT_CANCELED) { return if (result == SwipeableItemConstants.RESULT_CANCELED) {
SwipeResultActionDefault() SwipeResultActionDefault()
} else { } else {
SwipedResultActionRemoveItem(this, position, activity) SwipedResultActionRemoveItem(this, position, activity)

View file

@ -0,0 +1,10 @@
package code.name.monkey.retromusic.db
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
class BlackListStoreEntity(
@PrimaryKey
val path: String
)

View file

@ -0,0 +1,32 @@
package code.name.monkey.retromusic.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
class HistoryEntity(
@PrimaryKey
val id: Int,
val title: String,
@ColumnInfo(name = "track_number")
val trackNumber: Int,
val year: Int,
val duration: Long,
val data: String,
@ColumnInfo(name = "date_modified")
val dateModified: Long,
@ColumnInfo(name = "album_id")
val albumId: Int,
@ColumnInfo(name = "album_name")
val albumName: String,
@ColumnInfo(name = "artist_id")
val artistId: Int,
@ColumnInfo(name = "artist_name")
val artistName: String,
val composer: String?,
@ColumnInfo(name = "album_artist")
val albumArtist: String?,
@ColumnInfo(name = "time_played")
val timePlayed: Long
)

View file

@ -0,0 +1,34 @@
package code.name.monkey.retromusic.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
class PlayCountEntity(
@PrimaryKey
val id: Int,
val title: String,
@ColumnInfo(name = "track_number")
val trackNumber: Int,
val year: Int,
val duration: Long,
val data: String,
@ColumnInfo(name = "date_modified")
val dateModified: Long,
@ColumnInfo(name = "album_id")
val albumId: Int,
@ColumnInfo(name = "album_name")
val albumName: String,
@ColumnInfo(name = "artist_id")
val artistId: Int,
@ColumnInfo(name = "artist_name")
val artistName: String,
val composer: String?,
@ColumnInfo(name = "album_artist")
val albumArtist: String?,
@ColumnInfo(name = "time_played")
val timePlayed: Long,
@ColumnInfo(name = "play_count")
var playCount: Int
)

View file

@ -1,5 +1,6 @@
package code.name.monkey.retromusic.db package code.name.monkey.retromusic.db
import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
@Dao @Dao
@ -8,37 +9,84 @@ interface PlaylistDao {
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
@Query("UPDATE PlaylistEntity SET playlist_name = :name WHERE playlist_id = :playlistId") @Query("UPDATE PlaylistEntity SET playlist_name = :name WHERE playlist_id = :playlistId")
suspend fun renamePlaylistEntity(playlistId: Int, name: String) suspend fun renamePlaylist(playlistId: Int, name: String)
@Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name") @Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name")
suspend fun checkPlaylistExists(name: String): List<PlaylistEntity> fun isPlaylistExists(name: String): List<PlaylistEntity>
@Query("SELECT * FROM PlaylistEntity") @Query("SELECT * FROM PlaylistEntity")
suspend fun playlists(): List<PlaylistEntity> suspend fun playlists(): List<PlaylistEntity>
@Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId") @Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId")
suspend fun deleteSongsFromPlaylist(playlistId: Int) suspend fun deleteSongsInPlaylist(playlistId: Int)
@Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
suspend fun removeSongFromPlaylist(playlistId: Int, songId: Int)
@Transaction @Transaction
@Query("SELECT * FROM PlaylistEntity") @Query("SELECT * FROM PlaylistEntity")
suspend fun playlistsWithSong(): List<PlaylistWithSongs> suspend fun playlistsWithSongs(): List<PlaylistWithSongs>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSongs(songEntities: List<SongEntity>) suspend fun insertSongsToPlaylist(songEntities: List<SongEntity>)
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistName AND id = :songId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId")
suspend fun checkSongExistsWithPlaylistName(playlistName: String, songId: Int): List<SongEntity> suspend fun isSongExistsInPlaylist(playlistId: Int, songId: Int): List<SongEntity>
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId") @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId")
suspend fun getSongs(playlistId: Int): List<SongEntity> suspend fun songsFromPlaylist(playlistId: Int): List<SongEntity>
@Delete @Delete
suspend fun deletePlaylistEntity(playlistEntity: PlaylistEntity) suspend fun deletePlaylist(playlistEntity: PlaylistEntity)
@Delete @Delete
suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) suspend fun deletePlaylists(playlistEntities: List<PlaylistEntity>)
@Delete @Delete
suspend fun removeSongsFromPlaylist(songs: List<SongEntity>) suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSongInHistory(historyEntity: HistoryEntity)
@Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1")
suspend fun isSongPresentInHistory(songId: Int): HistoryEntity?
@Update
suspend fun updateHistorySong(historyEntity: HistoryEntity)
@Query("SELECT * FROM HistoryEntity ORDER BY time_played DESC")
fun historySongs(): LiveData<List<HistoryEntity>>
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
fun favoritesSongsLiveData(playlistId: Int): LiveData<List<SongEntity>>
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
fun favoritesSongs(playlistId: Int): List<SongEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
@Update
fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
@Delete
fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
@Query("SELECT * FROM PlayCountEntity WHERE id =:songId")
fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity>
@Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC")
fun playCountSongs(): List<PlayCountEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>)
@Delete
suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
@Query("DELETE FROM BlackListStoreEntity")
suspend fun clearBlacklist()
} }

View file

@ -1,34 +0,0 @@
package code.name.monkey.retromusic.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(
entities = [PlaylistEntity::class, SongEntity::class],
version = 8,
exportSchema = false
)
abstract class PlaylistDatabase : RoomDatabase() {
abstract fun playlistDao(): PlaylistDao
companion object {
@Volatile
private var INSTANCE: PlaylistDatabase? = null
fun getDatabase(
context: Context
): PlaylistDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
PlaylistDatabase::class.java,
"playlists.db"
).fallbackToDestructiveMigration().build()
INSTANCE = instance
instance
}
}
}
}

View file

@ -0,0 +1,13 @@
package code.name.monkey.retromusic.db
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(
entities = [PlaylistEntity::class, SongEntity::class, HistoryEntity::class, PlayCountEntity::class, BlackListStoreEntity::class],
version = 18,
exportSchema = false
)
abstract class RetroDatabase : RoomDatabase() {
abstract fun playlistDao(): PlaylistDao
}

View file

@ -4,7 +4,6 @@ import android.os.Parcelable
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import code.name.monkey.retromusic.model.Song
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
@ -37,27 +36,5 @@ class SongEntity(
@ColumnInfo(name = "song_key") @ColumnInfo(name = "song_key")
var songPrimaryKey: Long = 0 var songPrimaryKey: Long = 0
fun toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
)
}
} }
fun List<SongEntity>.toSongs(): List<Song> {
return map {
it.toSong()
}
}

View file

@ -0,0 +1,83 @@
package code.name.monkey.retromusic.db
import code.name.monkey.retromusic.model.Song
fun List<SongEntity>.toSongs(): List<Song> {
return map {
it.toSong()
}
}
fun SongEntity.toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
)
}
fun PlayCountEntity.toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
)
}
fun HistoryEntity.toSong(): Song {
return Song(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist
)
}
fun Song.toPlayCount(): PlayCountEntity {
return PlayCountEntity(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist,
System.currentTimeMillis(),
1
)
}

View file

@ -13,7 +13,7 @@ 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.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
@ -59,7 +59,7 @@ class AddToRetroPlaylist : BottomSheetDialogFragment() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val songEntities = songs.toSongEntity(playlistEntities[which - 1]) val songEntities = songs.toSongEntity(playlistEntities[which - 1])
repository.insertSongs(songEntities) repository.insertSongs(songEntities)
libraryViewModel.forceReload(ReloadType.Playlists) libraryViewModel.forceReload(Playlists)
} }
} }
dismiss() dismiss()

View file

@ -76,7 +76,7 @@ public class BlacklistFolderChooserDialog extends DialogFragment implements Mate
return new MaterialDialog.Builder(requireActivity()) return new MaterialDialog.Builder(requireActivity())
.title(R.string.md_error_label) .title(R.string.md_error_label)
.content(R.string.md_storage_perm_error) .content(R.string.md_storage_perm_error)
.positiveText(android.R.string.ok) .positiveText(R.string.ok)
.build(); .build();
} }
if (savedInstanceState == null) { if (savedInstanceState == null) {

View file

@ -42,24 +42,25 @@ class DeleteSongsDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
var title = 0 val pair = if (songs.size > 1) {
var message: CharSequence = "" Pair(
if (songs.size > 1) { R.string.delete_songs_title, HtmlCompat.fromHtml(
title = R.string.delete_songs_title
message = HtmlCompat.fromHtml(
String.format(getString(R.string.delete_x_songs), songs.size), String.format(getString(R.string.delete_x_songs), songs.size),
HtmlCompat.FROM_HTML_MODE_LEGACY HtmlCompat.FROM_HTML_MODE_LEGACY
) )
)
} else { } else {
title = R.string.delete_song_title Pair(
message = HtmlCompat.fromHtml( R.string.delete_song_title,
HtmlCompat.fromHtml(
String.format(getString(R.string.delete_song_x), songs[0].title), String.format(getString(R.string.delete_song_x), songs[0].title),
HtmlCompat.FROM_HTML_MODE_LEGACY HtmlCompat.FROM_HTML_MODE_LEGACY
) )
)
} }
return materialDialog(title) return materialDialog(pair.first)
.setMessage(message) .setMessage(pair.second)
.setCancelable(false) .setCancelable(false)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_delete) { _, _ -> .setPositiveButton(R.string.action_delete) { _, _ ->

View file

@ -66,7 +66,8 @@ class RemoveSongFromPlaylistDialog : DialogFragment() {
.setMessage(pair.second) .setMessage(pair.second)
.setPositiveButton(R.string.remove_action) { _, _ -> .setPositiveButton(R.string.remove_action) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
repository.removeSongFromPlaylist(songs) //repository.removeSongFromPlaylist(songs)
repository.deleteSongsInPlaylist(songs)
libraryViewModel.forceReload(Playlists) libraryViewModel.forceReload(Playlists)
} }
} }

View file

@ -8,6 +8,4 @@ class RetroSingleCheckedListAdapter(
context: Context, context: Context,
resource: Int = R.layout.dialog_list_item, resource: Int = R.layout.dialog_list_item,
objects: MutableList<String> objects: MutableList<String>
) : ArrayAdapter<String>(context, resource, objects) { ) : ArrayAdapter<String>(context, resource, objects)
}

View file

@ -158,7 +158,7 @@ class SleepTimerDialog : DialogFragment() {
} }
} }
private inner class TimerUpdater internal constructor() : private inner class TimerUpdater() :
CountDownTimer( CountDownTimer(
PreferenceUtil.nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(), PreferenceUtil.nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(),
1000 1000

View file

@ -21,8 +21,6 @@ import android.os.Bundle
import android.text.Spanned import android.text.Spanned
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.annotation.NonNull import androidx.annotation.NonNull
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
@ -41,12 +39,6 @@ import org.jaudiotagger.tag.TagException
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
inline fun ViewGroup.forEach(action: (View) -> Unit) {
for (i in 0 until childCount) {
action(getChildAt(i))
}
}
class SongDetailDialog : DialogFragment() { class SongDetailDialog : DialogFragment() {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -152,7 +144,7 @@ class SongDetailDialog : DialogFragment() {
} }
} }
return materialDialog(R.string.action_details) return materialDialog(R.string.action_details)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setView(dialogView) .setView(dialogView)
.create() .create()
.colorButtons() .colorButtons()

View file

@ -15,13 +15,8 @@
package code.name.monkey.retromusic.extensions package code.name.monkey.retromusic.extensions
import android.app.Activity import android.app.Activity
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentTransaction
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) { fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
@ -30,41 +25,6 @@ fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
} }
fun FragmentActivity?.addFragment(
@IdRes idRes: Int = R.id.container,
fragment: Fragment,
tag: String? = null,
addToBackStack: Boolean = false
) {
val compatActivity = this as? AppCompatActivity ?: return
compatActivity.supportFragmentManager.beginTransaction()
.apply {
add(fragment, tag)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
if (addToBackStack) {
addToBackStack(null)
}
commitNow()
}
}
fun AppCompatActivity.replaceFragment(
@IdRes id: Int = R.id.container,
fragment: Fragment,
tag: String? = null,
addToBackStack: Boolean = false
) {
val compatActivity = this ?: return
compatActivity.supportFragmentManager.beginTransaction()
.apply {
replace(id, fragment, tag)
if (addToBackStack) {
addToBackStack(null)
}
commit()
}
}
inline fun <reified T : Any> Activity.extra(key: String, default: T? = null) = lazy { inline fun <reified T : Any> Activity.extra(key: String, default: T? = null) = lazy {
val value = intent?.extras?.get(key) val value = intent?.extras?.get(key)
if (value is T) value else default if (value is T) value else default

View file

@ -23,6 +23,7 @@ import android.widget.CheckBox
import android.widget.SeekBar import android.widget.SeekBar
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
@ -130,6 +131,12 @@ fun MaterialButton.applyColor(color: Int) {
iconTint = textColorColorStateList iconTint = textColorColorStateList
} }
fun MaterialButton.applyOutlineColor(color: Int) {
val textColorColorStateList = ColorStateList.valueOf(color)
setTextColor(textColorColorStateList)
iconTint = textColorColorStateList
}
fun TextInputLayout.accentColor() { fun TextInputLayout.accentColor() {
val accentColor = ThemeStore.accentColor(context) val accentColor = ThemeStore.accentColor(context)
val colorState = ColorStateList.valueOf(accentColor) val colorState = ColorStateList.valueOf(accentColor)
@ -141,3 +148,7 @@ fun TextInputLayout.accentColor() {
fun TextInputEditText.accentColor() { fun TextInputEditText.accentColor() {
} }
fun AppCompatImageView.accentColor(): Int {
return ThemeStore.accentColor(context)
}

View file

@ -51,7 +51,8 @@ val FragmentManager.currentNavigationFragment: Fragment?
get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first() get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()
fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? { fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? {
val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(navHostId) as NavHostFragment val navHostFragment: NavHostFragment =
supportFragmentManager.findFragmentById(navHostId) as NavHostFragment
navHostFragment.targetFragment navHostFragment.targetFragment
return navHostFragment.childFragmentManager.fragments.first() return navHostFragment.childFragmentManager.fragments.first()
} }

View file

@ -14,13 +14,10 @@
package code.name.monkey.retromusic.extensions package code.name.monkey.retromusic.extensions
import android.content.res.ColorStateList
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.SeekBar
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper

View file

@ -3,6 +3,8 @@ package code.name.monkey.retromusic.fragments
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -10,15 +12,14 @@ import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.db.toSong
import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.albums.AlbumClickListener
import code.name.monkey.retromusic.fragments.artists.ArtistClickListener import code.name.monkey.retromusic.fragments.artists.ArtistClickListener
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment 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.repository.RealRepository 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.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
@ -48,32 +49,86 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
RECENT_ALBUMS -> { RECENT_ALBUMS -> {
loadAlbums(R.string.recent_albums, RECENT_ALBUMS) loadAlbums(R.string.recent_albums, RECENT_ALBUMS)
} }
FAVOURITES -> { FAVOURITES -> loadFavorite()
loadFavorite() HISTORY_PLAYLIST -> loadHistory()
LAST_ADDED_PLAYLIST -> lastAddedSongs()
TOP_PLAYED_PLAYLIST -> topPlayed()
} }
} }
private fun lastAddedSongs() {
toolbar.setTitle(R.string.last_added)
val songAdapter = SongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
)
recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
lifecycleScope.launch(IO) {
val songs = repository.recentSongs()
withContext(Main) { songAdapter.swapDataSet(songs) }
}
}
private fun topPlayed() {
toolbar.setTitle(R.string.my_top_tracks)
val songAdapter = SongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
)
recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
lifecycleScope.launch(IO) {
val songs = repository.recentSongs()
withContext(Main) { songAdapter.swapDataSet(songs) }
}
}
private fun loadHistory() {
toolbar.setTitle(R.string.history)
val songAdapter = SongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
)
recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
repository.historySong().observe(viewLifecycleOwner, Observer {
val songs = it.map { historyEntity -> historyEntity.toSong() }
songAdapter.swapDataSet(songs)
})
} }
private fun loadFavorite() { private fun loadFavorite() {
toolbar.setTitle(R.string.favorites) toolbar.setTitle(R.string.favorites)
CoroutineScope(IO).launch { val songAdapter = SongAdapter(
val songs = repository.favoritePlaylistHome()
withContext(Main) {
recyclerView.apply {
adapter = SongAdapter(
requireActivity(), requireActivity(),
songs.arrayList as MutableList<Song>, mutableListOf(),
R.layout.item_list, null R.layout.item_list, null
) )
recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
} }
} repository.favorites().observe(viewLifecycleOwner, Observer {
} println(it.size)
val songs = it.map { songEntity -> songEntity.toSong() }
songAdapter.swapDataSet(songs)
})
} }
private fun loadArtists(title: Int, type: Int) { private fun loadArtists(title: Int, type: Int) {
toolbar.setTitle(title) toolbar.setTitle(title)
CoroutineScope(IO).launch { lifecycleScope.launch(IO) {
val artists = val artists =
if (type == TOP_ARTISTS) repository.topArtists() else repository.recentArtists() if (type == TOP_ARTISTS) repository.topArtists() else repository.recentArtists()
withContext(Main) { withContext(Main) {
@ -87,7 +142,7 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
private fun loadAlbums(title: Int, type: Int) { private fun loadAlbums(title: Int, type: Int) {
toolbar.setTitle(title) toolbar.setTitle(title)
CoroutineScope(IO).launch { lifecycleScope.launch(IO) {
val albums = val albums =
if (type == TOP_ALBUMS) repository.topAlbums() else repository.recentAlbums() if (type == TOP_ALBUMS) repository.topAlbums() else repository.recentAlbums()
withContext(Main) { withContext(Main) {

View file

@ -5,7 +5,9 @@ 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.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.toPlayCount
import code.name.monkey.retromusic.fragments.ReloadType.* import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.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.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
@ -15,7 +17,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class LibraryViewModel( class LibraryViewModel(
private val realRepository: RealRepository private val repository: RealRepository
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val paletteColor = MutableLiveData<Int>() private val paletteColor = MutableLiveData<Int>()
@ -49,37 +51,37 @@ class LibraryViewModel(
artists.value = loadArtists.await() artists.value = loadArtists.await()
playlists.value = loadPlaylists.await() playlists.value = loadPlaylists.await()
roomPlaylists.value = loadPlaylistsWithSongs.await() roomPlaylists.value = loadPlaylistsWithSongs.await()
//genres.value = loadGenres.await() genres.value = loadGenres.await()
} }
private val loadHome: Deferred<List<Home>> private val loadHome: Deferred<List<Home>>
get() = viewModelScope.async { realRepository.homeSections() } get() = viewModelScope.async { repository.homeSections() }
private val loadSongs: Deferred<List<Song>> private val loadSongs: Deferred<List<Song>>
get() = viewModelScope.async(IO) { realRepository.allSongs() } get() = viewModelScope.async(IO) { repository.allSongs() }
private val loadAlbums: Deferred<List<Album>> private val loadAlbums: Deferred<List<Album>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
realRepository.allAlbums() repository.allAlbums()
} }
private val loadArtists: Deferred<List<Artist>> private val loadArtists: Deferred<List<Artist>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
realRepository.albumArtists() repository.albumArtists()
} }
private val loadPlaylists: Deferred<List<Playlist>> private val loadPlaylists: Deferred<List<Playlist>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
realRepository.allPlaylists() repository.allPlaylists()
} }
private val loadPlaylistsWithSongs: Deferred<List<PlaylistWithSongs>> private val loadPlaylistsWithSongs: Deferred<List<PlaylistWithSongs>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
realRepository.playlistWithSongs() repository.playlistWithSongs()
} }
private val loadGenres: Deferred<List<Genre>> private val loadGenres: Deferred<List<Genre>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
realRepository.allGenres() repository.allGenres()
} }
@ -119,6 +121,22 @@ class LibraryViewModel(
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
println("onPlayingMetaChanged") println("onPlayingMetaChanged")
viewModelScope.launch(IO) {
val entity = repository.songPresentInHistory(MusicPlayerRemote.currentSong)
if (entity != null) {
repository.updateHistorySong(MusicPlayerRemote.currentSong)
} else {
repository.addSongToHistory(MusicPlayerRemote.currentSong)
}
val songs = repository.checkSongExistInPlayCount(MusicPlayerRemote.currentSong.id)
if (songs.isNotEmpty()) {
repository.updateSongInPlayCount(songs.first().apply {
playCount += playCount + 1
})
} else {
repository.insertSongInPlayCount(MusicPlayerRemote.currentSong.toPlayCount())
}
}
} }
override fun onPlayStateChanged() { override fun onPlayStateChanged() {

View file

@ -26,6 +26,7 @@ import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.dialogs.AddToRetroPlaylist import code.name.monkey.retromusic.dialogs.AddToRetroPlaylist
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.applyOutlineColor
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.AlbumGlideRequest
@ -240,7 +241,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
private fun setColors(color: MediaNotificationProcessor) { private fun setColors(color: MediaNotificationProcessor) {
shuffleAction.applyColor(color.backgroundColor) shuffleAction.applyColor(color.backgroundColor)
playAction.applyColor(color.backgroundColor) playAction.applyOutlineColor(color.backgroundColor)
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Int, view: View) {

View file

@ -27,7 +27,7 @@ class AlbumDetailsViewModel(
fun getAlbum(): LiveData<Album> = _album fun getAlbum(): LiveData<Album> = _album
fun getArtist(): LiveData<Artist> = _artist fun getArtist(): LiveData<Artist> = _artist
fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum
fun getMoreAlbums(): LiveData<List<Album>> = _moreAlbums; fun getMoreAlbums(): LiveData<List<Album>> = _moreAlbums
init { init {
loadAlbumDetails() loadAlbumDetails()

View file

@ -22,6 +22,7 @@ import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.dialogs.AddToRetroPlaylist import code.name.monkey.retromusic.dialogs.AddToRetroPlaylist
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.applyOutlineColor
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.albums.AlbumClickListener
@ -72,7 +73,6 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
setupRecyclerView() setupRecyclerView()
postponeEnterTransition() postponeEnterTransition()
detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer {
showArtist(it) showArtist(it)
startPostponedEnterTransition() startPostponedEnterTransition()
}) })
@ -191,7 +191,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
private fun setColors(color: MediaNotificationProcessor) { private fun setColors(color: MediaNotificationProcessor) {
shuffleAction.applyColor(color.backgroundColor) shuffleAction.applyColor(color.backgroundColor)
playAction.applyColor(color.backgroundColor) playAction.applyOutlineColor(color.backgroundColor)
} }
override fun onAlbumClick(albumId: Int, view: View) { override fun onAlbumClick(albumId: Int, view: View) {

View file

@ -6,8 +6,8 @@ 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.repository.RealRepository
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
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

View file

@ -1,10 +1,8 @@
package code.name.monkey.retromusic.fragments.base package code.name.monkey.retromusic.fragments.base
import android.annotation.SuppressLint
import android.content.ContentUris import android.content.ContentUris
import android.content.Intent import android.content.Intent
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
@ -22,29 +20,34 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.PaletteColorHolder import code.name.monkey.retromusic.interfaces.PaletteColorHolder
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.Lyrics import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.util.*
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.* import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.FileNotFoundException import java.io.FileNotFoundException
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout), abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks { Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
protected val libraryViewModel by sharedViewModel<LibraryViewModel>() protected val libraryViewModel by sharedViewModel<LibraryViewModel>()
@ -70,7 +73,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true return true
} }
R.id.action_add_to_playlist -> { R.id.action_add_to_playlist -> {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(IO) {
val playlists = get<RealRepository>().roomPlaylists() val playlists = get<RealRepository>().roomPlaylists()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
AddToRetroPlaylist.create(playlists, song) AddToRetroPlaylist.create(playlists, song)
@ -159,7 +162,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
} }
protected open fun toggleFavorite(song: Song) { protected open fun toggleFavorite(song: Song) {
MusicUtil.toggleFavorite(requireActivity(), song) lifecycleScope.launch(IO) {
val playlist: PlaylistEntity = repository.favoritePlaylist().first()
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
if (isFavorite) {
repository.removeSongFromPlaylist(songEntity)
} else {
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
libraryViewModel.forceReload(Playlists)
}
requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
}
} }
abstract fun playerToolbar(): Toolbar? abstract fun playerToolbar(): Toolbar?
@ -182,84 +196,62 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
updateLyrics() updateLyrics()
} }
override fun onDestroyView() {
if (updateIsFavoriteTask != null && !updateIsFavoriteTask!!.isCancelled) {
updateIsFavoriteTask!!.cancel(true)
}
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
updateLyricsAsyncTask!!.cancel(true)
}
super.onDestroyView()
}
@SuppressLint("StaticFieldLeak")
fun updateIsFavorite() { fun updateIsFavorite() {
if (updateIsFavoriteTask != null) { lifecycleScope.launch(IO) {
updateIsFavoriteTask!!.cancel(false) val playlist: PlaylistEntity = repository.favoritePlaylist().first()
} val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
updateIsFavoriteTask = object : AsyncTask<Song, Void, Boolean>() { val isFavorite = repository.isFavoriteSong(song).isNotEmpty()
override fun doInBackground(vararg params: Song): Boolean { withContext(Dispatchers.Main) {
return MusicUtil.isFavorite(requireActivity(), params[0]) val icon = if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
}
override fun onPostExecute(isFavorite: Boolean) {
val res = if (isFavorite)
R.drawable.ic_favorite
else
R.drawable.ic_favorite_border
val drawable = val drawable =
RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor()) RetroUtil.getTintedVectorDrawable(requireContext(), icon, toolbarIconColor())
if (playerToolbar() != null && playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite) != null) if (playerToolbar() != null) {
playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable) playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)
.title = ?.setIcon(drawable)?.title =
if (isFavorite) getString(R.string.action_remove_from_favorites) else getString( if (isFavorite) getString(R.string.action_remove_from_favorites)
R.string.action_add_to_favorites else getString(R.string.action_add_to_favorites)
) }
}
} }
}.execute(MusicPlayerRemote.currentSong)
} }
@SuppressLint("StaticFieldLeak")
private fun updateLyrics() { private fun updateLyrics() {
if (updateLyricsAsyncTask != null) updateLyricsAsyncTask!!.cancel(false)
updateLyricsAsyncTask = object : AsyncTask<Song, Void, Lyrics>() {
override fun onPreExecute() {
super.onPreExecute()
setLyrics(null) setLyrics(null)
} lifecycleScope.launch(IO) {
val song = MusicPlayerRemote.currentSong
override fun doInBackground(vararg params: Song): Lyrics? { val lyrics = try {
try { var data: String? = LyricUtil.getStringFromFile(song.title, song.artistName)
var data: String? = if (TextUtils.isEmpty(data)) {
LyricUtil.getStringFromFile(params[0].title, params[0].artistName) data = MusicUtil.getLyrics(song)
return if (TextUtils.isEmpty(data)) { if (TextUtils.isEmpty(data)) {
data = MusicUtil.getLyrics(params[0])
return if (TextUtils.isEmpty(data)) {
null null
} else { } else {
Lyrics.parse(params[0], data) Lyrics.parse(song, data)
} }
} else Lyrics.parse(params[0], data!!) } else Lyrics.parse(song, data!!)
} catch (err: FileNotFoundException) { } catch (err: FileNotFoundException) {
return null null
}
withContext(Main) {
setLyrics(lyrics)
} }
} }
override fun onPostExecute(l: Lyrics?) {
setLyrics(l)
}
override fun onCancelled(s: Lyrics?) {
onPostExecute(null)
}
}.execute(MusicPlayerRemote.currentSong)
} }
open fun setLyrics(l: Lyrics?) { open fun setLyrics(l: Lyrics?) {
} }
private val repository by inject<RealRepository>()
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launch(IO) {
if (repository.checkPlaylistExists(getString(R.string.favorites)).isEmpty()) {
repository.createPlaylist(PlaylistEntity(getString(R.string.favorites)))
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (PreferenceUtil.isFullScreenMode && if (PreferenceUtil.isFullScreenMode &&
@ -267,8 +259,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
) { ) {
view.findViewById<View>(R.id.status_bar).visibility = View.GONE view.findViewById<View>(R.id.status_bar).visibility = View.GONE
} }
playerAlbumCoverFragment = playerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment)
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment?
playerAlbumCoverFragment?.setCallbacks(this) playerAlbumCoverFragment?.setCallbacks(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)

View file

@ -290,7 +290,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
} }
} }
if (startIndex > -1) { if (startIndex > -1) {
MusicPlayerRemote.INSTANCE.openQueue(songs, startIndex, true); MusicPlayerRemote.openQueue(songs, startIndex, true);
} else { } else {
final File finalFile = file1; final File finalFile = file1;
Snackbar.make(coordinatorLayout, Html.fromHtml( Snackbar.make(coordinatorLayout, Html.fromHtml(

View file

@ -36,7 +36,7 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
mainActivity.addMusicServiceEventListener(detailsViewModel) mainActivity.addMusicServiceEventListener(detailsViewModel)
mainActivity.setSupportActionBar(toolbar) mainActivity.setSupportActionBar(toolbar)
mainActivity.hideBottomBarVisibility(false) mainActivity.hideBottomBarVisibility(false)
progressIndicator.hide()
setupRecyclerView() setupRecyclerView()
detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer { detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
songs(it) songs(it)

View file

@ -21,10 +21,11 @@ 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.lifecycle.lifecycleScope
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.HISTORY_PLAYLIST
import code.name.monkey.retromusic.LAST_ADDED_PLAYLIST
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.TOP_PLAYED_PLAYLIST
import code.name.monkey.retromusic.adapter.HomeAdapter import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.extensions.findActivityNavController
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
@ -32,9 +33,6 @@ 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.model.smartplaylist.HistoryPlaylist
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist
import code.name.monkey.retromusic.repository.Repository 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
@ -74,15 +72,15 @@ class HomeFragment :
lastAdded.setOnClickListener { lastAdded.setOnClickListener {
findActivityNavController(R.id.fragment_container).navigate( findActivityNavController(R.id.fragment_container).navigate(
R.id.playlistDetailsFragment, R.id.detailListFragment,
bundleOf(EXTRA_PLAYLIST to LastAddedPlaylist()) bundleOf("type" to LAST_ADDED_PLAYLIST)
) )
} }
topPlayed.setOnClickListener { topPlayed.setOnClickListener {
findActivityNavController(R.id.fragment_container).navigate( findActivityNavController(R.id.fragment_container).navigate(
R.id.playlistDetailsFragment, R.id.detailListFragment,
bundleOf(EXTRA_PLAYLIST to TopTracksPlaylist()) bundleOf("type" to TOP_PLAYED_PLAYLIST)
) )
} }
@ -96,9 +94,9 @@ class HomeFragment :
} }
history.setOnClickListener { history.setOnClickListener {
requireActivity().findNavController(R.id.fragment_container).navigate( findActivityNavController(R.id.fragment_container).navigate(
R.id.playlistDetailsFragment, R.id.detailListFragment,
bundleOf(EXTRA_PLAYLIST to HistoryPlaylist()) bundleOf("type" to HISTORY_PLAYLIST)
) )
} }

View file

@ -3,9 +3,7 @@ package code.name.monkey.retromusic.fragments.player.fit
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
@ -26,7 +24,6 @@ import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.android.synthetic.main.fragment_fit_playback_controls.* import kotlinx.android.synthetic.main.fragment_fit_playback_controls.*

View file

@ -127,7 +127,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
return String(Character.toChars(unicode)) return String(Character.toChars(unicode))
} }
public override fun onPause() { override fun onPause() {
if (recyclerViewDragDropManager != null) { if (recyclerViewDragDropManager != null) {
recyclerViewDragDropManager!!.cancelDrag() recyclerViewDragDropManager!!.cancelDrag()
} }

View file

@ -31,7 +31,8 @@ class PlaylistDetailsViewModel(
loadPlaylistSongs(playlist) loadPlaylistSongs(playlist)
} }
private fun loadPlaylistSongs(playlist: PlaylistWithSongs) = viewModelScope.launch(Dispatchers.IO) { private fun loadPlaylistSongs(playlist: PlaylistWithSongs) =
viewModelScope.launch(Dispatchers.IO) {
val songs: List<Song> = realRepository.playlistSongs(playlist) val songs: List<Song> = realRepository.playlistSongs(playlist)
withContext(Main) { _playListSongs.postValue(songs) } withContext(Main) { _playListSongs.postValue(songs) }
} }

View file

@ -15,8 +15,8 @@
package code.name.monkey.retromusic.glide.artistimage package code.name.monkey.retromusic.glide.artistimage
import android.content.Context import android.content.Context
import code.name.monkey.retromusic.deezer.DeezerApiService
import code.name.monkey.retromusic.deezer.Data import code.name.monkey.retromusic.deezer.Data
import code.name.monkey.retromusic.deezer.DeezerApiService
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.bumptech.glide.Priority import com.bumptech.glide.Priority
@ -69,13 +69,13 @@ class ArtistImageFetcher(
val response = deezerApiService.getArtistImage(artists[0]).execute() val response = deezerApiService.getArtistImage(artists[0]).execute()
if (!response.isSuccessful) { if (!response.isSuccessful) {
throw IOException("Request failed with code: " + response.code()); throw IOException("Request failed with code: " + response.code())
} }
if (isCancelled) return null if (isCancelled) return null
return try { return try {
val deezerResponse = response.body(); val deezerResponse = response.body()
val imageUrl = deezerResponse?.data?.get(0)?.let { getHighestQuality(it) } val imageUrl = deezerResponse?.data?.get(0)?.let { getHighestQuality(it) }
// Fragile way to detect a place holder image returned from Deezer: // Fragile way to detect a place holder image returned from Deezer:
// ex: "https://e-cdns-images.dzcdn.net/images/artist//250x250-000000-80-0-0.jpg" // ex: "https://e-cdns-images.dzcdn.net/images/artist//250x250-000000-80-0-0.jpg"

View file

@ -21,7 +21,7 @@ import code.name.monkey.retromusic.R
object HorizontalAdapterHelper { object HorizontalAdapterHelper {
const val LAYOUT_RES = R.layout.item_image const val LAYOUT_RES = R.layout.item_album_card
private const val TYPE_FIRST = 1 private const val TYPE_FIRST = 1
private const val TYPE_MIDDLE = 2 private const val TYPE_MIDDLE = 2

View file

@ -13,7 +13,6 @@
*/ */
package code.name.monkey.retromusic.helper package code.name.monkey.retromusic.helper
import android.content.Context
import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Playlist
import java.io.BufferedWriter import java.io.BufferedWriter
import java.io.File import java.io.File
@ -24,14 +23,13 @@ object M3UWriter : M3UConstants {
@JvmStatic @JvmStatic
@Throws(IOException::class) @Throws(IOException::class)
fun write( fun write(
context: Context,
dir: File, dir: File,
playlist: Playlist playlist: Playlist
): 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() val songs = playlist.getSongs()
if (songs.size > 0) { if (songs.isNotEmpty()) {
val bw = BufferedWriter(FileWriter(file)) val bw = BufferedWriter(FileWriter(file))
bw.write(M3UConstants.HEADER) bw.write(M3UConstants.HEADER)
for (song in songs) { for (song in songs) {

View file

@ -15,7 +15,6 @@
package code.name.monkey.retromusic.helper package code.name.monkey.retromusic.helper
import android.app.SearchManager import android.app.SearchManager
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.model.Song import code.name.monkey.retromusic.model.Song
@ -33,7 +32,7 @@ object SearchQueryHelper : KoinComponent {
var songs = ArrayList<Song>() var songs = ArrayList<Song>()
@JvmStatic @JvmStatic
fun getSongs(context: Context, extras: Bundle): List<Song> { fun getSongs(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)
@ -45,9 +44,9 @@ object SearchQueryHelper : KoinComponent {
songRepository.makeSongCursor( songRepository.makeSongCursor(
ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION,
arrayOf( arrayOf(
artistName.toLowerCase(), artistName.toLowerCase(Locale.getDefault()),
albumName.toLowerCase(), albumName.toLowerCase(Locale.getDefault()),
titleName.toLowerCase() titleName.toLowerCase(Locale.getDefault())
) )
) )
) )
@ -59,7 +58,10 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ARTIST_SELECTION + AND + TITLE_SELECTION, ARTIST_SELECTION + AND + TITLE_SELECTION,
arrayOf(artistName.toLowerCase(), titleName.toLowerCase()) arrayOf(
artistName.toLowerCase(Locale.getDefault()),
titleName.toLowerCase(Locale.getDefault())
)
) )
) )
} }
@ -70,7 +72,10 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ALBUM_SELECTION + AND + TITLE_SELECTION, ALBUM_SELECTION + AND + TITLE_SELECTION,
arrayOf(albumName.toLowerCase(), titleName.toLowerCase()) arrayOf(
albumName.toLowerCase(Locale.getDefault()),
titleName.toLowerCase(Locale.getDefault())
)
) )
) )
} }
@ -81,7 +86,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ARTIST_SELECTION, ARTIST_SELECTION,
arrayOf(artistName.toLowerCase()) arrayOf(artistName.toLowerCase(Locale.getDefault()))
) )
) )
} }
@ -92,7 +97,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ALBUM_SELECTION, ALBUM_SELECTION,
arrayOf(albumName.toLowerCase()) arrayOf(albumName.toLowerCase(Locale.getDefault()))
) )
) )
} }
@ -103,7 +108,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
TITLE_SELECTION, TITLE_SELECTION,
arrayOf(titleName.toLowerCase()) arrayOf(titleName.toLowerCase(Locale.getDefault()))
) )
) )
} }
@ -113,7 +118,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ARTIST_SELECTION, ARTIST_SELECTION,
arrayOf(query.toLowerCase()) arrayOf(query.toLowerCase(Locale.getDefault()))
) )
) )
@ -123,7 +128,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
ALBUM_SELECTION, ALBUM_SELECTION,
arrayOf(query.toLowerCase()) arrayOf(query.toLowerCase(Locale.getDefault()))
) )
) )
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
@ -132,7 +137,7 @@ object SearchQueryHelper : KoinComponent {
songs = songRepository.songs( songs = songRepository.songs(
songRepository.makeSongCursor( songRepository.makeSongCursor(
TITLE_SELECTION, TITLE_SELECTION,
arrayOf(query.toLowerCase()) arrayOf(query.toLowerCase(Locale.getDefault()))
) )
) )
return if (songs.isNotEmpty()) { return if (songs.isNotEmpty()) {

View file

@ -15,7 +15,6 @@
package code.name.monkey.retromusic.helper.menu package code.name.monkey.retromusic.helper.menu
import android.app.Activity
import android.content.Context import android.content.Context
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
@ -90,7 +89,6 @@ object PlaylistMenuHelper : KoinComponent {
} }
private fun getPlaylistSongs( private fun getPlaylistSongs(
activity: Activity,
playlist: Playlist playlist: Playlist
): List<Song> { ): List<Song> {
return if (playlist is AbsCustomPlaylist) { return if (playlist is AbsCustomPlaylist) {
@ -100,7 +98,7 @@ object PlaylistMenuHelper : KoinComponent {
} }
} }
private class SavePlaylistAsyncTask internal constructor(context: Context) : private class SavePlaylistAsyncTask(context: Context) :
WeakContextAsyncTask<Playlist, String, String>(context) { WeakContextAsyncTask<Playlist, String, String>(context) {
override fun doInBackground(vararg params: Playlist): String { override fun doInBackground(vararg params: Playlist): String {

View file

@ -11,20 +11,20 @@ public class Lrc {
private long time; private long time;
private String text; private String text;
public void setTime(long time) {
this.time = time;
}
public void setText(String text) {
this.text = text;
}
public long getTime() { public long getTime() {
return time; return time;
} }
public void setTime(long time) {
this.time = time;
}
public String getText() { public String getText() {
return text; return text;
} }
public void setText(String text) {
this.text = text;
}
} }

View file

@ -23,6 +23,9 @@ import android.text.TextUtils;
* 一行歌词实体 * 一行歌词实体
*/ */
class LrcEntry implements Comparable<LrcEntry> { class LrcEntry implements Comparable<LrcEntry> {
public static final int GRAVITY_CENTER = 0;
public static final int GRAVITY_LEFT = 1;
public static final int GRAVITY_RIGHT = 2;
private long time; private long time;
private String text; private String text;
private String secondText; private String secondText;
@ -31,9 +34,6 @@ class LrcEntry implements Comparable<LrcEntry> {
* 歌词距离视图顶部的距离 * 歌词距离视图顶部的距离
*/ */
private float offset = Float.MIN_VALUE; private float offset = Float.MIN_VALUE;
public static final int GRAVITY_CENTER = 0;
public static final int GRAVITY_LEFT = 1;
public static final int GRAVITY_RIGHT = 2;
LrcEntry(long time, String text) { LrcEntry(long time, String text) {
this.time = time; this.time = time;

View file

@ -28,6 +28,7 @@ import java.io.InputStreamReader;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -77,7 +78,7 @@ class LrcUtils {
List<LrcEntry> entryList = new ArrayList<>(); List<LrcEntry> entryList = new ArrayList<>();
try { try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(lrcFile), "utf-8")); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(lrcFile), StandardCharsets.UTF_8));
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
List<LrcEntry> list = parseLine(line); List<LrcEntry> list = parseLine(line);

View file

@ -35,6 +35,8 @@ import android.view.View;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
import android.widget.Scroller; import android.widget.Scroller;
import androidx.core.content.res.ResourcesCompat;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -510,6 +512,7 @@ public class LrcView extends View {
if (i > 0) { if (i > 0) {
y += ((mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) >> 1) + mDividerHeight; y += ((mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) >> 1) + mDividerHeight;
} }
mLrcPaint.setTypeface(ResourcesCompat.getFont(getContext(), R.font.sans));
if (i == mCurrentLine) { if (i == mCurrentLine) {
mLrcPaint.setTextSize(mCurrentTextSize); mLrcPaint.setTextSize(mCurrentTextSize);
mLrcPaint.setColor(mCurrentTextColor); mLrcPaint.setColor(mCurrentTextColor);

View file

@ -14,10 +14,13 @@
package code.name.monkey.retromusic.model package code.name.monkey.retromusic.model
import androidx.annotation.StringRes
import code.name.monkey.retromusic.HomeSection import code.name.monkey.retromusic.HomeSection
class Home( class Home(
val arrayList: List<Any>, val arrayList: List<Any>,
@HomeSection @HomeSection
val homeSection: Int val homeSection: Int,
@StringRes
val titleRes: Int
) )

View file

@ -14,6 +14,7 @@
package code.name.monkey.retromusic.model package code.name.monkey.retromusic.model
import android.os.Parcelable import android.os.Parcelable
import code.name.monkey.retromusic.db.HistoryEntity
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@ -33,6 +34,25 @@ open class Song(
val composer: String?, val composer: String?,
val albumArtist: String? val albumArtist: String?
) : Parcelable { ) : Parcelable {
fun toHistoryEntity(timePlayed: Long): HistoryEntity {
return HistoryEntity(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
composer,
albumArtist,
timePlayed
)
}
fun toSongEntity(playListId: Int): SongEntity { fun toSongEntity(playListId: Int): SongEntity {
return SongEntity( return SongEntity(
playListId, playListId,

View file

@ -106,7 +106,7 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
override fun onPageScrollStateChanged(state: Int) { override fun onPageScrollStateChanged(state: Int) {
} }
private class AlbumCoverStyleAdapter internal constructor(private val context: Context) : private class AlbumCoverStyleAdapter(private val context: Context) :
PagerAdapter() { PagerAdapter() {
override fun instantiateItem(collection: ViewGroup, position: Int): Any { override fun instantiateItem(collection: ViewGroup, position: Int): Any {

View file

@ -72,9 +72,7 @@ class LibraryPreferenceDialog : DialogFragment() {
categoryAdapter.categoryInfos = PreferenceUtil.defaultCategories categoryAdapter.categoryInfos = PreferenceUtil.defaultCategories
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton( .setPositiveButton(R.string.ok) { _, _ -> updateCategories(categoryAdapter.categoryInfos) }
android.R.string.ok
) { _, _ -> updateCategories(categoryAdapter.categoryInfos) }
.setView(view) .setView(view)
.create() .create()
.colorButtons() .colorButtons()

View file

@ -108,7 +108,7 @@ public class SongPlayCountStore extends SQLiteOpenHelper {
*/ */
@NonNull @NonNull
private static String getColumnNameForWeek(final int week) { private static String getColumnNameForWeek(final int week) {
return SongPlayCountColumns.WEEK_PLAY_COUNT + String.valueOf(week); return SongPlayCountColumns.WEEK_PLAY_COUNT + week;
} }
/** /**

View file

@ -15,10 +15,9 @@
package code.name.monkey.retromusic.repository package code.name.monkey.retromusic.repository
import android.content.Context import android.content.Context
import androidx.lifecycle.LiveData
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist
import code.name.monkey.retromusic.network.LastFMService import code.name.monkey.retromusic.network.LastFMService
@ -32,91 +31,65 @@ import kotlinx.coroutines.flow.flow
interface Repository { interface Repository {
fun songsFlow(): Flow<Result<List<Song>>> fun songsFlow(): Flow<Result<List<Song>>>
fun albumsFlow(): Flow<Result<List<Album>>> fun albumsFlow(): Flow<Result<List<Album>>>
fun artistsFlow(): Flow<Result<List<Artist>>> fun artistsFlow(): Flow<Result<List<Artist>>>
fun playlistsFlow(): Flow<Result<List<Playlist>>> fun playlistsFlow(): Flow<Result<List<Playlist>>>
fun genresFlow(): Flow<Result<List<Genre>>> fun genresFlow(): Flow<Result<List<Genre>>>
suspend fun allAlbums(): List<Album> suspend fun allAlbums(): List<Album>
suspend fun albumById(albumId: Int): Album suspend fun albumById(albumId: Int): Album
suspend fun allSongs(): List<Song> suspend fun allSongs(): List<Song>
suspend fun allArtists(): List<Artist> suspend fun allArtists(): List<Artist>
suspend fun albumArtists(): List<Artist> suspend fun albumArtists(): List<Artist>
suspend fun allPlaylists(): List<Playlist> suspend fun allPlaylists(): List<Playlist>
suspend fun allGenres(): List<Genre> suspend fun allGenres(): List<Genre>
suspend fun search(query: String?): MutableList<Any> suspend fun search(query: String?): MutableList<Any>
suspend fun getPlaylistSongs(playlist: Playlist): List<Song> suspend fun getPlaylistSongs(playlist: Playlist): List<Song>
suspend fun getGenre(genreId: Int): List<Song> suspend fun getGenre(genreId: Int): List<Song>
suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist
suspend fun albumInfo(artist: String, album: String): LastFmAlbum suspend fun albumInfo(artist: String, album: String): LastFmAlbum
suspend fun artistById(artistId: Int): Artist suspend fun artistById(artistId: Int): Artist
suspend fun recentArtists(): List<Artist> suspend fun recentArtists(): List<Artist>
suspend fun topArtists(): List<Artist> suspend fun topArtists(): List<Artist>
suspend fun topAlbums(): List<Album> suspend fun topAlbums(): List<Album>
suspend fun recentAlbums(): List<Album> suspend fun recentAlbums(): List<Album>
suspend fun recentArtistsHome(): Home suspend fun recentArtistsHome(): Home
suspend fun topArtistsHome(): Home suspend fun topArtistsHome(): Home
suspend fun topAlbumsHome(): Home suspend fun topAlbumsHome(): Home
suspend fun recentAlbumsHome(): Home suspend fun recentAlbumsHome(): Home
suspend fun favoritePlaylistHome(): Home suspend fun favoritePlaylistHome(): Home
suspend fun suggestionsHome(): Home suspend fun suggestionsHome(): Home
suspend fun genresHome(): Home suspend fun genresHome(): Home
suspend fun playlists(): Home suspend fun playlists(): Home
suspend fun homeSections(): List<Home> suspend fun homeSections(): List<Home>
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>> suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
suspend fun playlist(playlistId: Int): Playlist suspend fun playlist(playlistId: Int): Playlist
suspend fun playlistWithSongs(): List<PlaylistWithSongs> suspend fun playlistWithSongs(): List<PlaylistWithSongs>
suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song>
suspend fun insertSongs(songs: List<SongEntity>) suspend fun insertSongs(songs: List<SongEntity>)
suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity>
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
suspend fun roomPlaylists(): List<PlaylistEntity> suspend fun roomPlaylists(): List<PlaylistEntity>
suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>)
suspend fun renameRoomPlaylist(playlistId: Int, name: String) suspend fun renameRoomPlaylist(playlistId: Int, name: String)
suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
suspend fun removeSongFromPlaylist(songs: List<SongEntity>) suspend fun removeSongFromPlaylist(songEntity: SongEntity)
suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>)
suspend fun favoritePlaylist(): List<PlaylistEntity>
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
suspend fun addSongToHistory(currentSong: Song)
suspend fun songPresentInHistory(currentSong: Song): HistoryEntity?
suspend fun updateHistorySong(currentSong: Song)
suspend fun favoritePlaylistSongs(): List<SongEntity>
suspend fun recentSongs(): List<Song>
suspend fun topPlayedSongs(): List<Song>
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity>
fun historySong(): LiveData<List<HistoryEntity>>
fun favorites(): LiveData<List<SongEntity>>
} }
class RealRepository( class RealRepository(
@ -129,8 +102,8 @@ class RealRepository(
private val lastAddedRepository: LastAddedRepository, private val lastAddedRepository: LastAddedRepository,
private val playlistRepository: PlaylistRepository, private val playlistRepository: PlaylistRepository,
private val searchRepository: RealSearchRepository, private val searchRepository: RealSearchRepository,
private val playedTracksRepository: TopPlayedRepository, private val topPlayedRepository: TopPlayedRepository,
private val roomPlaylistRepository: RoomPlaylistRepository private val roomRepository: RoomPlaylistRepository
) : Repository { ) : Repository {
override suspend fun allAlbums(): List<Album> = albumRepository.albums() override suspend fun allAlbums(): List<Album> = albumRepository.albums()
@ -147,9 +120,9 @@ class RealRepository(
override suspend fun recentAlbums(): List<Album> = lastAddedRepository.recentAlbums() override suspend fun recentAlbums(): List<Album> = lastAddedRepository.recentAlbums()
override suspend fun topArtists(): List<Artist> = playedTracksRepository.topArtists() override suspend fun topArtists(): List<Artist> = topPlayedRepository.topArtists()
override suspend fun topAlbums(): List<Album> = playedTracksRepository.topAlbums() override suspend fun topAlbums(): List<Album> = topPlayedRepository.topAlbums()
override suspend fun allPlaylists(): List<Playlist> = playlistRepository.playlists() override suspend fun allPlaylists(): List<Playlist> = playlistRepository.playlists()
@ -214,13 +187,14 @@ class RealRepository(
override suspend fun homeSections(): List<Home> { override suspend fun homeSections(): List<Home> {
val homeSections = mutableListOf<Home>() val homeSections = mutableListOf<Home>()
val sections = listOf( val sections: List<Home> = listOf(
suggestionsHome(),
topArtistsHome(), topArtistsHome(),
topAlbumsHome(), topAlbumsHome(),
recentArtistsHome(), recentArtistsHome(),
recentAlbumsHome(), recentAlbumsHome(),
suggestionsHome(), favoritePlaylistHome(),
favoritePlaylistHome() genresHome()
) )
for (section in sections) { for (section in sections) {
if (section.arrayList.isNotEmpty()) { if (section.arrayList.isNotEmpty()) {
@ -230,16 +204,12 @@ class RealRepository(
return homeSections return homeSections
} }
override suspend fun playlists(): Home {
val playlist = playlistRepository.playlists()
return Home(playlist, TOP_ALBUMS)
}
override suspend fun playlist(playlistId: Int) = override suspend fun playlist(playlistId: Int) =
playlistRepository.playlist(playlistId) playlistRepository.playlist(playlistId)
override suspend fun playlistWithSongs(): List<PlaylistWithSongs> = override suspend fun playlistWithSongs(): List<PlaylistWithSongs> =
roomPlaylistRepository.playlistWithSongs() roomRepository.playlistWithSongs()
override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> { override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> {
return playlistWithSongs.songs.map { return playlistWithSongs.songs.map {
@ -248,71 +218,118 @@ class RealRepository(
} }
override suspend fun insertSongs(songs: List<SongEntity>) = override suspend fun insertSongs(songs: List<SongEntity>) =
roomPlaylistRepository.insertSongs(songs) roomRepository.insertSongs(songs)
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> = override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
roomPlaylistRepository.checkPlaylistExists(playlistName) roomRepository.checkPlaylistExists(playlistName)
override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long = override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long =
roomPlaylistRepository.createPlaylist(playlistEntity) roomRepository.createPlaylist(playlistEntity)
override suspend fun roomPlaylists(): List<PlaylistEntity> = roomPlaylistRepository.playlists() override suspend fun roomPlaylists(): List<PlaylistEntity> = roomRepository.playlists()
override suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) = override suspend fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) =
roomPlaylistRepository.deletePlaylistEntities(playlists) roomRepository.deletePlaylistEntities(playlists)
override suspend fun renameRoomPlaylist(playlistId: Int, name: String) = override suspend fun renameRoomPlaylist(playlistId: Int, name: String) =
roomPlaylistRepository.renamePlaylistEntity(playlistId, name) roomRepository.renamePlaylistEntity(playlistId, name)
override suspend fun removeSongFromPlaylist(songs: List<SongEntity>) = override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) =
roomPlaylistRepository.removeSongsFromPlaylist(songs) roomRepository.deleteSongsInPlaylist(songs)
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
roomRepository.removeSongFromPlaylist(songEntity)
override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) = override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) =
roomPlaylistRepository.deleteSongsFromPlaylist(playlists) roomRepository.deleteSongsFromPlaylist(playlists)
override suspend fun favoritePlaylist(): List<PlaylistEntity> =
roomRepository.favoritePlaylist(context.getString(R.string.favorites))
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
roomRepository.isFavoriteSong(songEntity)
override suspend fun addSongToHistory(currentSong: Song) =
roomRepository.addSongToHistory(currentSong)
override suspend fun songPresentInHistory(currentSong: Song): HistoryEntity? =
roomRepository.songPresentInHistory(currentSong)
override suspend fun updateHistorySong(currentSong: Song) =
roomRepository.updateHistorySong(currentSong)
override suspend fun favoritePlaylistSongs(): List<SongEntity> =
roomRepository.favoritePlaylistSongs(context.getString(R.string.favorites))
override suspend fun recentSongs(): List<Song> = lastAddedRepository.recentSongs()
override suspend fun topPlayedSongs(): List<Song> = topPlayedRepository.topTracks()
override suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.insertSongInPlayCount(playCountEntity)
override suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.updateSongInPlayCount(playCountEntity)
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.deleteSongInPlayCount(playCountEntity)
override suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> =
roomRepository.checkSongExistInPlayCount(songId)
override suspend fun playCountSongs(): List<PlayCountEntity> =
roomRepository.playCountSongs()
override fun historySong(): LiveData<List<HistoryEntity>> =
roomRepository.historySongs()
override fun favorites(): LiveData<List<SongEntity>> =
roomRepository.favoritePlaylistLiveData(context.getString(R.string.favorites))
override suspend fun suggestionsHome(): Home { override suspend fun suggestionsHome(): Home {
val songs = val songs =
NotPlayedPlaylist().songs().shuffled().takeIf { NotPlayedPlaylist().songs().shuffled().takeIf {
it.size > 9 it.size > 9
} ?: emptyList() } ?: emptyList()
println(songs.size) return Home(songs, SUGGESTIONS, R.string.suggestion_songs)
return Home(songs, SUGGESTIONS)
} }
override suspend fun genresHome(): Home { override suspend fun genresHome(): Home {
val genres = genreRepository.genres().shuffled() val genres = genreRepository.genres().shuffled()
return Home(genres, GENRES) return Home(genres, GENRES, R.string.genres)
} }
override suspend fun playlists(): Home {
val playlist = playlistRepository.playlists()
return Home(playlist, PLAYLISTS, R.string.playlists)
}
override suspend fun recentArtistsHome(): Home { override suspend fun recentArtistsHome(): Home {
val artists = lastAddedRepository.recentArtists().take(5) val artists = lastAddedRepository.recentArtists().take(5)
return Home(artists, RECENT_ARTISTS) return Home(artists, RECENT_ARTISTS, R.string.recent_artists)
} }
override suspend fun recentAlbumsHome(): Home { override suspend fun recentAlbumsHome(): Home {
val albums = lastAddedRepository.recentAlbums().take(5) val albums = lastAddedRepository.recentAlbums().take(5)
return Home(albums, RECENT_ALBUMS) return Home(albums, RECENT_ALBUMS, R.string.recent_albums)
} }
override suspend fun topAlbumsHome(): Home { override suspend fun topAlbumsHome(): Home {
val albums = playedTracksRepository.topAlbums().take(5) val albums = topPlayedRepository.topAlbums().take(5)
return Home(albums, TOP_ALBUMS) return Home(albums, TOP_ALBUMS, R.string.top_albums)
} }
override suspend fun topArtistsHome(): Home { override suspend fun topArtistsHome(): Home {
val artists = playedTracksRepository.topArtists().take(5) val artists = topPlayedRepository.topArtists().take(5)
return Home(artists, TOP_ARTISTS) return Home(artists, TOP_ARTISTS, R.string.top_artists)
} }
override suspend fun favoritePlaylistHome(): Home { override suspend fun favoritePlaylistHome(): Home {
val playlists = val songs = favoritePlaylistSongs().map {
playlistRepository.favoritePlaylist(context.getString(R.string.favorites)).take(5) it.toSong()
val songs = if (playlists.isNotEmpty()) }
PlaylistSongsLoader.getPlaylistSongList(context, playlists[0]) println(songs.size)
else emptyList() return Home(songs, FAVOURITES, R.string.favorites)
return Home(songs, FAVOURITES)
} }
override fun songsFlow(): Flow<Result<List<Song>>> = flow { override fun songsFlow(): Flow<Result<List<Song>>> = flow {

View file

@ -1,10 +1,9 @@
package code.name.monkey.retromusic.repository package code.name.monkey.retromusic.repository
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import code.name.monkey.retromusic.db.PlaylistDao import androidx.lifecycle.LiveData
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.db.SongEntity
interface RoomPlaylistRepository { interface RoomPlaylistRepository {
@ -16,11 +15,25 @@ interface RoomPlaylistRepository {
suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity> suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity>
suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>)
suspend fun renamePlaylistEntity(playlistId: Int, name: String) suspend fun renamePlaylistEntity(playlistId: Int, name: String)
suspend fun removeSongsFromPlaylist(songs: List<SongEntity>) suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>)
suspend fun favoritePlaylist(favorite: String): List<PlaylistEntity>
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
suspend fun removeSongFromPlaylist(songEntity: SongEntity)
suspend fun addSongToHistory(currentSong: Song)
suspend fun songPresentInHistory(song: Song): HistoryEntity?
suspend fun updateHistorySong(song: Song)
suspend fun favoritePlaylistSongs(favorite: String): List<SongEntity>
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity>
fun historySongs(): LiveData<List<HistoryEntity>>
fun favoritePlaylistLiveData(favorite: String): LiveData<List<SongEntity>>
} }
class RealRoomPlaylistRepository( class RealRoomRepository(
private val playlistDao: PlaylistDao private val playlistDao: PlaylistDao
) : RoomPlaylistRepository { ) : RoomPlaylistRepository {
@WorkerThread @WorkerThread
@ -29,14 +42,14 @@ class RealRoomPlaylistRepository(
@WorkerThread @WorkerThread
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> = override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
playlistDao.checkPlaylistExists(playlistName) playlistDao.isPlaylistExists(playlistName)
@WorkerThread @WorkerThread
override suspend fun playlists(): List<PlaylistEntity> = playlistDao.playlists() override suspend fun playlists(): List<PlaylistEntity> = playlistDao.playlists()
@WorkerThread @WorkerThread
override suspend fun playlistWithSongs(): List<PlaylistWithSongs> = override suspend fun playlistWithSongs(): List<PlaylistWithSongs> =
playlistDao.playlistsWithSong() playlistDao.playlistsWithSongs()
@WorkerThread @WorkerThread
override suspend fun insertSongs(songs: List<SongEntity>) { override suspend fun insertSongs(songs: List<SongEntity>) {
@ -46,26 +59,77 @@ class RealRoomPlaylistRepository(
}.first() }.first()
println("Existing ${existingSongs.size}") println("Existing ${existingSongs.size}")
tempList.removeAll(existingSongs)*/ tempList.removeAll(existingSongs)*/
playlistDao.insertSongs(songs) playlistDao.insertSongsToPlaylist(songs)
} }
override suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity> { override suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity> {
return playlistDao.getSongs(playlistEntity.playListId) return playlistDao.songsFromPlaylist(playlistEntity.playListId)
} }
override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) = override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) =
playlistDao.deletePlaylistEntities(playlistEntities) playlistDao.deletePlaylists(playlistEntities)
override suspend fun renamePlaylistEntity(playlistId: Int, name: String) = override suspend fun renamePlaylistEntity(playlistId: Int, name: String) =
playlistDao.renamePlaylistEntity(playlistId, name) playlistDao.renamePlaylist(playlistId, name)
override suspend fun removeSongsFromPlaylist(songs: List<SongEntity>) = override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) =
playlistDao.removeSongsFromPlaylist(songs) playlistDao.deleteSongsInPlaylist(songs)
override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) { override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) {
playlists.forEach { playlists.forEach {
playlistDao.deleteSongsFromPlaylist(it.playListId) playlistDao.deleteSongsInPlaylist(it.playListId)
} }
} }
override suspend fun favoritePlaylist(favorite: String): List<PlaylistEntity> =
playlistDao.isPlaylistExists(favorite)
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
playlistDao.isSongExistsInPlaylist(
songEntity.playlistCreatorId,
songEntity.id
)
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
playlistDao.removeSongFromPlaylist(songEntity.playlistCreatorId, songEntity.id)
override suspend fun addSongToHistory(currentSong: Song) =
playlistDao.insertSongInHistory(currentSong.toHistoryEntity(System.currentTimeMillis()))
override suspend fun songPresentInHistory(song: Song): HistoryEntity? =
playlistDao.isSongPresentInHistory(song.id)
override suspend fun updateHistorySong(song: Song) =
playlistDao.updateHistorySong(song.toHistoryEntity(System.currentTimeMillis()))
override fun historySongs(): LiveData<List<HistoryEntity>> {
return playlistDao.historySongs()
}
override fun favoritePlaylistLiveData(favorite: String): LiveData<List<SongEntity>> =
playlistDao.favoritesSongsLiveData(
playlistDao.isPlaylistExists(favorite).first().playListId
)
override suspend fun favoritePlaylistSongs(favorite: String): List<SongEntity> {
return if (playlistDao.isPlaylistExists(favorite).isNotEmpty())
playlistDao.favoritesSongs(
playlistDao.isPlaylistExists(favorite).first().playListId
) else emptyList()
}
override suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) =
playlistDao.insertSongInPlayCount(playCountEntity)
override suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) =
playlistDao.updateSongInPlayCount(playCountEntity)
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
playlistDao.deleteSongInPlayCount(playCountEntity)
override suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity> =
playlistDao.checkSongExistInPlayCount(songId)
override suspend fun playCountSongs(): List<PlayCountEntity> =
playlistDao.playCountSongs()
} }

View file

@ -116,13 +116,13 @@ class RealTopPlayedRepository(
private fun makeTopTracksCursorImpl(): 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 cursor =
SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS) SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
songs.use { localSongs -> cursor.use { songs ->
return makeSortedCursor( return makeSortedCursor(
localSongs, songs,
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID) songs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)
) )
} }
} }

View file

@ -22,8 +22,8 @@ import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.text.Html
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.text.HtmlCompat
import androidx.media.app.NotificationCompat.MediaStyle import androidx.media.app.NotificationCompat.MediaStyle
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
@ -132,9 +132,19 @@ class PlayingNotificationImpl : PlayingNotification() {
.setLargeIcon(bitmapFinal) .setLargeIcon(bitmapFinal)
.setContentIntent(clickIntent) .setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent) .setDeleteIntent(deleteIntent)
.setContentTitle(Html.fromHtml("<b>" + song.title + "</b>")) .setContentTitle(
HtmlCompat.fromHtml(
"<b>" + song.title + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY
)
)
.setContentText(song.artistName) .setContentText(song.artistName)
.setSubText(Html.fromHtml("<b>" + song.albumName + "</b>")) .setSubText(
HtmlCompat.fromHtml(
"<b>" + song.albumName + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY
)
)
.setOngoing(isPlaying) .setOngoing(isPlaying)
.setShowWhen(false) .setShowWhen(false)
.addAction(toggleFavorite) .addAction(toggleFavorite)

View file

@ -162,7 +162,6 @@ public class AutoGeneratedPlaylistBitmap {
} }
;
Log.d(TAG, "getBitmapCollection: smalltime = " + (System.currentTimeMillis() - start)); Log.d(TAG, "getBitmapCollection: smalltime = " + (System.currentTimeMillis() - start));
if (round) if (round)
return BitmapEditor.getRoundedCornerBitmap(bitmap, bitmap.getWidth() / 40); return BitmapEditor.getRoundedCornerBitmap(bitmap, bitmap.getWidth() / 40);

View file

@ -83,16 +83,16 @@ public final class BitmapEditor {
int wh = w * h; int wh = w * h;
int div = radius + radius + 1; int div = radius + radius + 1;
int r[] = new int[wh]; int[] r = new int[wh];
int g[] = new int[wh]; int[] g = new int[wh];
int b[] = new int[wh]; int[] b = new int[wh];
int a[] = new int[wh]; int[] a = new int[wh];
int rsum, gsum, bsum, asum, x, y, i, p, yp, yi, yw; int rsum, gsum, bsum, asum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)]; int[] vmin = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1; int divsum = (div + 1) >> 1;
divsum *= divsum; divsum *= divsum;
int dv[] = new int[256 * divsum]; int[] dv = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) { for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum); dv[i] = (i / divsum);
} }
@ -295,7 +295,7 @@ public final class BitmapEditor {
public static boolean PerceivedBrightness(int will_White, int[] c) { public static boolean PerceivedBrightness(int will_White, int[] c) {
double TBT = Math.sqrt(c[0] * c[0] * .241 + c[1] * c[1] * .691 + c[2] * c[2] * .068); double TBT = Math.sqrt(c[0] * c[0] * .241 + c[1] * c[1] * .691 + c[2] * c[2] * .068);
// Log.d("themee",TBT+""); // Log.d("themee",TBT+"");
return (TBT > will_White) ? false : true; return !(TBT > will_White);
} }
public static int[] getAverageColorRGB(Bitmap bitmap) { public static int[] getAverageColorRGB(Bitmap bitmap) {
@ -404,15 +404,15 @@ public final class BitmapEditor {
int wh = w * h; int wh = w * h;
int div = radius + radius + 1; int div = radius + radius + 1;
int r[] = new int[wh]; int[] r = new int[wh];
int g[] = new int[wh]; int[] g = new int[wh];
int b[] = new int[wh]; int[] b = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)]; int[] vmin = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1; int divsum = (div + 1) >> 1;
divsum *= divsum; divsum *= divsum;
int dv[] = new int[256 * divsum]; int[] dv = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) { for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum); dv[i] = (i / divsum);
} }
@ -635,8 +635,7 @@ public final class BitmapEditor {
public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) { public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) {
int sizeBitmap = (bitmap.getHeight() > bitmap.getWidth()) ? bitmap.getHeight() : bitmap.getWidth(); int sizeBitmap = (bitmap.getHeight() > bitmap.getWidth()) ? bitmap.getHeight() : bitmap.getWidth();
if (sizeBitmap > size) return true; return sizeBitmap > size;
return false;
} }
public static Bitmap GetRoundedBitmapWithBlurShadow(Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) { public static Bitmap GetRoundedBitmapWithBlurShadow(Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) {

View file

@ -0,0 +1,16 @@
package code.name.monkey.retromusic.util
import android.os.Environment
import java.io.File
object FilePathUtil {
fun blacklistFilePaths(): List<String> {
return listOf<File>(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS),
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES),
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)
).map {
FileUtil.safeGetCanonicalPath(it)
}
}
}

View file

@ -245,13 +245,9 @@ public final class FileUtil {
.equals(android.os.Environment.MEDIA_MOUNTED); .equals(android.os.Environment.MEDIA_MOUNTED);
Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable(); Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable();
if (isSDSupportedDevice && isSDPresent) {
// yes SD-card is present // yes SD-card is present
return true;
} else {
return false;
// Sorry // Sorry
} return isSDSupportedDevice && isSDPresent;
} }
public static File safeGetCanonicalFile(File file) { public static File safeGetCanonicalFile(File file) {

View file

@ -26,7 +26,7 @@ import java.io.FileWriter;
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.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
/** /**
* Created by hefuyi on 2016/11/8. * Created by hefuyi on 2016/11/8.
@ -102,7 +102,7 @@ public class LyricUtil {
} }
private static String getLrcOriginalPath(String filePath) { private static String getLrcOriginalPath(String filePath) {
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1, filePath.length()), "lrc"); return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc");
} }
@NonNull @NonNull
@ -110,16 +110,9 @@ public class LyricUtil {
if (str == null || str.length() == 0) { if (str == null || str.length() == 0) {
return null; return null;
} }
try { byte[] encode = str.getBytes(StandardCharsets.UTF_8);
byte[] encode = str.getBytes("UTF-8");
// base64 解密 // base64 解密
return new String(Base64.decode(encode, 0, encode.length, Base64.DEFAULT), "UTF-8"); return new String(Base64.decode(encode, 0, encode.length, Base64.DEFAULT), StandardCharsets.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
} }
@NonNull @NonNull

View file

@ -310,7 +310,7 @@ object MusicUtil : KoinComponent {
context: Context, context: Context,
playlist: Playlist playlist: Playlist
): Boolean { ): Boolean {
return playlist.name != null && playlist.name == context.getString(R.string.favorites) return playlist.name == context.getString(R.string.favorites)
} }
fun toggleFavorite(context: Context, song: Song) { fun toggleFavorite(context: Context, song: Song) {
@ -349,7 +349,7 @@ object MusicUtil : KoinComponent {
BaseColumns._ID, MediaStore.MediaColumns.DATA BaseColumns._ID, MediaStore.MediaColumns.DATA
) )
// Split the query into multiple batches, and merge the resulting cursors // Split the query into multiple batches, and merge the resulting cursors
var batchStart = 0 var batchStart: Int
var batchEnd = 0 var batchEnd = 0
val batchSize = 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 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

View file

@ -195,7 +195,7 @@ public class PlaylistsUtil {
final int playlistId = songs.get(0).getPlaylistId(); final int playlistId = songs.get(0).getPlaylistId();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
"external", playlistId); "external", playlistId);
String selectionArgs[] = new String[songs.size()]; String[] selectionArgs = new String[songs.size()];
for (int i = 0; i < selectionArgs.length; i++) { for (int i = 0; i < selectionArgs.length; i++) {
selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList()); selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList());
} }
@ -247,7 +247,7 @@ public class PlaylistsUtil {
} }
public static File savePlaylist(Context context, Playlist playlist) throws IOException { public static File savePlaylist(Context context, Playlist playlist) throws IOException {
return M3UWriter.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist); return M3UWriter.write(new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
} }
public static boolean doesPlaylistExist(@NonNull final Context context, final int playlistId) { public static boolean doesPlaylistExist(@NonNull final Context context, final int playlistId) {

View file

@ -93,7 +93,7 @@ object PreferenceUtil {
} }
} }
val languageCode get() = sharedPreferences.getString(LANGUAGE_NAME, "auto") val languageCode: String get() = sharedPreferences.getString(LANGUAGE_NAME, "auto") ?: "auto"
var userName var userName
get() = sharedPreferences.getString( get() = sharedPreferences.getString(
@ -528,7 +528,7 @@ object PreferenceUtil {
get() { get() {
val folderPath = FoldersFragment.getDefaultStartDirectory().path val folderPath = FoldersFragment.getDefaultStartDirectory().path
val filePath: String = sharedPreferences.getStringOrDefault(START_DIRECTORY, folderPath) val filePath: String = sharedPreferences.getStringOrDefault(START_DIRECTORY, folderPath)
return File(filePath) ?: File(FoldersFragment.getDefaultStartDirectory().path) return File(filePath)
} }
set(value) = sharedPreferences.edit { set(value) = sharedPreferences.edit {
putString( putString(

View file

@ -70,7 +70,7 @@ public class RetroUtil {
} }
public static String formatValue(float value) { public static String formatValue(float value) {
String arr[] = {"", "K", "M", "B", "T", "P", "E"}; String[] arr = {"", "K", "M", "B", "T", "P", "E"};
int index = 0; int index = 0;
while ((value / 1000) >= 1) { while ((value / 1000) >= 1) {
value = value / 1000; value = value / 1000;

View file

@ -77,7 +77,7 @@ class RingtoneManager(val context: Context) {
return MaterialAlertDialogBuilder(context) return MaterialAlertDialogBuilder(context)
.setTitle(R.string.dialog_title_set_ringtone) .setTitle(R.string.dialog_title_set_ringtone)
.setMessage(R.string.dialog_message_set_ringtone) .setMessage(R.string.dialog_message_set_ringtone)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS) val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
intent.data = Uri.parse("package:" + context.applicationContext.packageName) intent.data = Uri.parse("package:" + context.applicationContext.packageName)
context.startActivity(intent) context.startActivity(intent)

View file

@ -36,11 +36,62 @@ public class ImageUtils {
private static final int ALPHA_TOLERANCE = 50; private static final int ALPHA_TOLERANCE = 50;
// Size of the smaller bitmap we're actually going to scan. // Size of the smaller bitmap we're actually going to scan.
private static final int COMPACT_BITMAP_SIZE = 64; // pixels private static final int COMPACT_BITMAP_SIZE = 64; // pixels
private final Matrix mTempMatrix = new Matrix();
private int[] mTempBuffer; private int[] mTempBuffer;
private Bitmap mTempCompactBitmap; private Bitmap mTempCompactBitmap;
private Canvas mTempCompactBitmapCanvas; private Canvas mTempCompactBitmapCanvas;
private Paint mTempCompactBitmapPaint; private Paint mTempCompactBitmapPaint;
private final Matrix mTempMatrix = new Matrix();
/**
* Classifies a color as grayscale or not. Grayscale here means "very close to a perfect
* gray"; if all three channels are approximately equal, this will return true.
* <p>
* Note that really transparent colors are always grayscale.
*/
public static boolean isGrayscale(int color) {
int alpha = 0xFF & (color >> 24);
if (alpha < ALPHA_TOLERANCE) {
return true;
}
int r = 0xFF & (color >> 16);
int g = 0xFF & (color >> 8);
int b = 0xFF & color;
return Math.abs(r - g) < TOLERANCE
&& Math.abs(r - b) < TOLERANCE
&& Math.abs(g - b) < TOLERANCE;
}
/**
* Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
*/
public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
int maxHeight) {
if (drawable == null) {
return null;
}
int originalWidth = drawable.getIntrinsicWidth();
int originalHeight = drawable.getIntrinsicHeight();
if ((originalWidth <= maxWidth) && (originalHeight <= maxHeight) &&
(drawable instanceof BitmapDrawable)) {
return ((BitmapDrawable) drawable).getBitmap();
}
if (originalHeight <= 0 || originalWidth <= 0) {
return null;
}
// create a new bitmap, scaling down to fit the max dimensions of
// a large notification icon if necessary
float ratio = Math.min((float) maxWidth / (float) originalWidth,
(float) maxHeight / (float) originalHeight);
ratio = Math.min(1.0f, ratio);
int scaledWidth = (int) (ratio * originalWidth);
int scaledHeight = (int) (ratio * originalHeight);
Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
// and paint our app bitmap on it
Canvas canvas = new Canvas(result);
drawable.setBounds(0, 0, scaledWidth, scaledHeight);
drawable.draw(canvas);
return result;
}
/** /**
* Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
@ -93,55 +144,4 @@ public class ImageUtils {
mTempBuffer = new int[size]; mTempBuffer = new int[size];
} }
} }
/**
* Classifies a color as grayscale or not. Grayscale here means "very close to a perfect
* gray"; if all three channels are approximately equal, this will return true.
* <p>
* Note that really transparent colors are always grayscale.
*/
public static boolean isGrayscale(int color) {
int alpha = 0xFF & (color >> 24);
if (alpha < ALPHA_TOLERANCE) {
return true;
}
int r = 0xFF & (color >> 16);
int g = 0xFF & (color >> 8);
int b = 0xFF & color;
return Math.abs(r - g) < TOLERANCE
&& Math.abs(r - b) < TOLERANCE
&& Math.abs(g - b) < TOLERANCE;
}
/**
* Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
*/
public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
int maxHeight) {
if (drawable == null) {
return null;
}
int originalWidth = drawable.getIntrinsicWidth();
int originalHeight = drawable.getIntrinsicHeight();
if ((originalWidth <= maxWidth) && (originalHeight <= maxHeight) &&
(drawable instanceof BitmapDrawable)) {
return ((BitmapDrawable) drawable).getBitmap();
}
if (originalHeight <= 0 || originalWidth <= 0) {
return null;
}
// create a new bitmap, scaling down to fit the max dimensions of
// a large notification icon if necessary
float ratio = Math.min((float) maxWidth / (float) originalWidth,
(float) maxHeight / (float) originalHeight);
ratio = Math.min(1.0f, ratio);
int scaledWidth = (int) (ratio * originalWidth);
int scaledHeight = (int) (ratio * originalHeight);
Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
// and paint our app bitmap on it
Canvas canvas = new Canvas(result);
drawable.setBounds(0, 0, scaledWidth, scaledHeight);
drawable.draw(canvas);
return result;
}
} }

View file

@ -0,0 +1,17 @@
package code.name.monkey.retromusic.views
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import code.name.monkey.retromusic.extensions.accentColor
class AccentIcon @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = -1
) : AppCompatImageView(context, attrs, defStyleAttr) {
init {
imageTintList = ColorStateList.valueOf(accentColor())
}
}

View file

@ -39,7 +39,7 @@ class ColorIconsImageView @JvmOverloads constructor(
val attributes = val attributes =
context.obtainStyledAttributes(attrs, R.styleable.ColorIconsImageView, 0, 0) context.obtainStyledAttributes(attrs, R.styleable.ColorIconsImageView, 0, 0)
val color = val color =
attributes.getColor(R.styleable.ColorIconsImageView_iconBackgroundColor, Color.RED); attributes.getColor(R.styleable.ColorIconsImageView_iconBackgroundColor, Color.RED)
setIconBackgroundColor(color) setIconBackgroundColor(color)
attributes.recycle() attributes.recycle()
} }

View file

@ -32,7 +32,7 @@ class RetroShapeableImageView @JvmOverloads constructor(
val typedArray = val typedArray =
context.obtainStyledAttributes(attrs, R.styleable.RetroShapeableImageView, defStyle, -1) context.obtainStyledAttributes(attrs, R.styleable.RetroShapeableImageView, defStyle, -1)
val cornerSize = val cornerSize =
typedArray.getDimension(R.styleable.RetroShapeableImageView_retroCornerSize, 0f); typedArray.getDimension(R.styleable.RetroShapeableImageView_retroCornerSize, 0f)
updateCornerSize(cornerSize) updateCornerSize(cornerSize)
typedArray.recycle() typedArray.recycle()
} }

View file

@ -19,6 +19,6 @@
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="@color/md_white_1000" android:fillColor="@android:color/white"
android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22 0.03 -1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z" /> android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM7.07,18.28c0.43,-0.9 3.05,-1.78 4.93,-1.78s4.51,0.88 4.93,1.78C15.57,19.36 13.86,20 12,20s-3.57,-0.64 -4.93,-1.72zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8s8,3.59 8,8c0,1.82 -0.62,3.49 -1.64,4.83zM12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13s3.5,-1.56 3.5,-3.5S13.94,6 12,6zM12,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S11.17,8 12,8s1.5,0.67 1.5,1.5S12.83,11 12,11z" />
</vector> </vector>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="Pacifico"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

View file

@ -87,7 +87,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:padding="0dp" android:padding="0dp"
android:textAppearance="@style/TextViewHeadline5" android:textAppearance="@style/TextViewHeadline6"
android:textColor="@color/md_white_1000" android:textColor="@color/md_white_1000"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/userImage" app:layout_constraintBottom_toBottomOf="@+id/userImage"

View file

@ -57,7 +57,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:padding="0dp" android:padding="0dp"
android:textAppearance="@style/TextViewHeadline5" android:textAppearance="@style/TextViewHeadline6"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/userImage" app:layout_constraintBottom_toBottomOf="@+id/userImage"

View file

@ -12,10 +12,10 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:lrcLabel="@string/no_lyrics_found" app:lrcLabel="@string/no_lyrics_found"
app:lrcNormalTextSize="32sp" app:lrcNormalTextSize="24sp"
app:lrcPadding="16dp" app:lrcPadding="24dp"
app:lrcTextGravity="left" app:lrcTextGravity="left"
app:lrcTextSize="32sp" /> app:lrcTextSize="28sp" />
<com.google.android.material.bottomappbar.BottomAppBar <com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"

View file

@ -8,14 +8,17 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/playAction" android:id="@+id/playAction"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/action_play_all" android:text="@string/action_play_all"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
app:backgroundTint="?attr/colorSurface" app:backgroundTint="?attr/colorSurface"
app:icon="@drawable/ic_play_arrow" app:cornerRadius="8dp"
app:layout_constraintEnd_toStartOf="@+id/shuffleAction" app:layout_constraintEnd_toStartOf="@+id/shuffleAction"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -23,14 +26,17 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/shuffleAction" android:id="@+id/shuffleAction"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/shuffle" android:text="@string/shuffle"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
app:backgroundTint="?attr/colorSurface" app:backgroundTint="?attr/colorSurface"
app:icon="@drawable/ic_shuffle" app:cornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="@+id/playAction" app:layout_constraintBottom_toBottomOf="@+id/playAction"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"

View file

@ -91,7 +91,7 @@
app:layout_constraintStart_toEndOf="@id/artistImage" app:layout_constraintStart_toEndOf="@id/artistImage"
app:layout_constraintTop_toTopOf="@id/artistImage" app:layout_constraintTop_toTopOf="@id/artistImage"
tools:ignore="MissingPrefix" tools:ignore="MissingPrefix"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/full_names" />
<code.name.monkey.retromusic.views.BaselineGridTextView <code.name.monkey.retromusic.views.BaselineGridTextView
android:id="@+id/albumText" android:id="@+id/albumText"
@ -100,7 +100,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextViewHeadline6" android:textAppearance="@style/TextViewSubtitle1"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -108,7 +108,7 @@
app:layout_constraintTop_toBottomOf="@id/albumTitle" app:layout_constraintTop_toBottomOf="@id/albumTitle"
app:lineHeightHint="24sp" app:lineHeightHint="24sp"
tools:ignore="MissingPrefix" tools:ignore="MissingPrefix"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/full_names" />
<include <include
layout="@layout/fragment_album_content" layout="@layout/fragment_album_content"

View file

@ -8,13 +8,17 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/playAction" android:id="@+id/playAction"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/action_play_all" android:text="@string/action_play_all"
android:textColor="?android:attr/textColorPrimary"
app:backgroundTint="?attr/colorSurface" app:backgroundTint="?attr/colorSurface"
app:icon="@drawable/ic_play_arrow" app:cornerRadius="8dp"
app:layout_constraintEnd_toStartOf="@+id/shuffleAction" app:layout_constraintEnd_toStartOf="@+id/shuffleAction"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -22,13 +26,17 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/shuffleAction" android:id="@+id/shuffleAction"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/shuffle" android:text="@string/shuffle"
android:textColor="?android:attr/textColorPrimary"
app:backgroundTint="?attr/colorSurface" app:backgroundTint="?attr/colorSurface"
app:icon="@drawable/ic_shuffle" app:cornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="@+id/playAction" app:layout_constraintBottom_toBottomOf="@+id/playAction"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"

View file

@ -86,7 +86,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:textAppearance="@style/TextViewHeadline6" android:textAppearance="@style/TextViewSubtitle1"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -85,7 +85,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:padding="0dp" android:padding="0dp"
android:textAppearance="@style/TextViewHeadline5" android:textAppearance="@style/TextViewHeadline6"
android:textColor="@color/md_white_1000" android:textColor="@color/md_white_1000"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/userImage" app:layout_constraintBottom_toBottomOf="@+id/userImage"

View file

@ -54,7 +54,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:padding="0dp" android:padding="0dp"
android:textAppearance="@style/TextViewHeadline5" android:textAppearance="@style/TextViewHeadline6"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/userImage" app:layout_constraintBottom_toBottomOf="@+id/userImage"

View file

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorSurface"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
tools:ignore="UnusedAttribute">
<include layout="@layout/status_bar" />
</FrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/mainContent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurface"
app:popupTheme="?attr/toolbarPopupTheme"
app:title="@string/app_name"
app:titleTextAppearance="@style/ToolbarTextAppearanceNormal.Library"
app:titleTextColor="?attr/colorControlNormal"
tools:ignore="UnusedAttribute" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height" />
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:name="code.name.monkey.retromusic.fragments.main.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:navGraph="@navigation/main_graph" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show more