Merge branch 'room-playlist' of https://github.com/h4h13/RetroMusicPlayer into room-playlist
This commit is contained in:
commit
3265580275
126 changed files with 1023 additions and 718 deletions
|
@ -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'
|
||||||
}
|
}
|
BIN
app/src/debug/res/font/google_sans_bold.ttf
Normal file
BIN
app/src/debug/res/font/google_sans_bold.ttf
Normal file
Binary file not shown.
BIN
app/src/debug/res/font/google_sans_medium.ttf
Normal file
BIN
app/src/debug/res/font/google_sans_medium.ttf
Normal file
Binary file not shown.
BIN
app/src/debug/res/font/google_sans_regular.ttf
Normal file
BIN
app/src/debug/res/font/google_sans_regular.ttf
Normal file
Binary file not shown.
|
@ -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>
|
|
@ -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 |
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -104,7 +104,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
val fastScroller = ThemedFastScroller.create(recyclerView)
|
ThemedFastScroller.create(recyclerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkForPadding() {
|
private fun checkForPadding() {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>() {
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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()
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,5 +13,5 @@ data class PlaylistWithSongs(
|
||||||
entityColumn = "playlist_creator_id"
|
entityColumn = "playlist_creator_id"
|
||||||
)
|
)
|
||||||
val songs: List<SongEntity>
|
val songs: List<SongEntity>
|
||||||
):Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
String.format(getString(R.string.delete_x_songs), songs.size),
|
||||||
message = HtmlCompat.fromHtml(
|
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||||
String.format(getString(R.string.delete_x_songs), songs.size),
|
)
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
title = R.string.delete_song_title
|
Pair(
|
||||||
message = HtmlCompat.fromHtml(
|
R.string.delete_song_title,
|
||||||
String.format(getString(R.string.delete_song_x), songs[0].title),
|
HtmlCompat.fromHtml(
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
String.format(getString(R.string.delete_song_x), songs[0].title),
|
||||||
|
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) { _, _ ->
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
requireActivity(),
|
||||||
withContext(Main) {
|
mutableListOf(),
|
||||||
recyclerView.apply {
|
R.layout.item_list, null
|
||||||
adapter = SongAdapter(
|
)
|
||||||
requireActivity(),
|
recyclerView.apply {
|
||||||
songs.arrayList as MutableList<Song>,
|
adapter = songAdapter
|
||||||
R.layout.item_list, null
|
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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
|
||||||
if (updateLyricsAsyncTask != null) updateLyricsAsyncTask!!.cancel(false)
|
|
||||||
|
|
||||||
updateLyricsAsyncTask = object : AsyncTask<Song, Void, Lyrics>() {
|
|
||||||
override fun onPreExecute() {
|
|
||||||
super.onPreExecute()
|
|
||||||
setLyrics(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doInBackground(vararg params: Song): Lyrics? {
|
|
||||||
try {
|
|
||||||
var data: String? =
|
|
||||||
LyricUtil.getStringFromFile(params[0].title, params[0].artistName)
|
|
||||||
return if (TextUtils.isEmpty(data)) {
|
|
||||||
data = MusicUtil.getLyrics(params[0])
|
|
||||||
return if (TextUtils.isEmpty(data)) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
Lyrics.parse(params[0], data)
|
|
||||||
}
|
|
||||||
} else Lyrics.parse(params[0], data!!)
|
|
||||||
} catch (err: FileNotFoundException) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPostExecute(l: Lyrics?) {
|
private fun updateLyrics() {
|
||||||
setLyrics(l)
|
setLyrics(null)
|
||||||
|
lifecycleScope.launch(IO) {
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
val lyrics = try {
|
||||||
|
var data: String? = LyricUtil.getStringFromFile(song.title, song.artistName)
|
||||||
|
if (TextUtils.isEmpty(data)) {
|
||||||
|
data = MusicUtil.getLyrics(song)
|
||||||
|
if (TextUtils.isEmpty(data)) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
Lyrics.parse(song, data)
|
||||||
|
}
|
||||||
|
} else Lyrics.parse(song, data!!)
|
||||||
|
} catch (err: FileNotFoundException) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
withContext(Main) {
|
||||||
override fun onCancelled(s: Lyrics?) {
|
setLyrics(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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.*
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,11 @@ class PlaylistDetailsViewModel(
|
||||||
loadPlaylistSongs(playlist)
|
loadPlaylistSongs(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadPlaylistSongs(playlist: PlaylistWithSongs) = viewModelScope.launch(Dispatchers.IO) {
|
private fun loadPlaylistSongs(playlist: PlaylistWithSongs) =
|
||||||
val songs: List<Song> = realRepository.playlistSongs(playlist)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
withContext(Main) { _playListSongs.postValue(songs) }
|
val songs: List<Song> = realRepository.playlistSongs(playlist)
|
||||||
}
|
withContext(Main) { _playListSongs.postValue(songs) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMediaStoreChanged() {
|
override fun onMediaStoreChanged() {
|
||||||
/*if (playlist !is AbsCustomPlaylist) {
|
/*if (playlist !is AbsCustomPlaylist) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
@ -72,7 +71,7 @@ object PlaylistMenuHelper : KoinComponent {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_rename_playlist -> {
|
R.id.action_rename_playlist -> {
|
||||||
RenameRetroPlaylistDialog.create(playlistWithSongs.playlistEntity )
|
RenameRetroPlaylistDialog.create(playlistWithSongs.playlistEntity)
|
||||||
.show(activity.supportFragmentManager, "RENAME_PLAYLIST")
|
.show(activity.supportFragmentManager, "RENAME_PLAYLIST")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
|
@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
// Sorry
|
||||||
return true;
|
return isSDSupportedDevice && isSDPresent;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
// Sorry
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File safeGetCanonicalFile(File file) {
|
public static File safeGetCanonicalFile(File 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), StandardCharsets.UTF_8);
|
||||||
return new String(Base64.decode(encode, 0, encode.length, Base64.DEFAULT), "UTF-8");
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue