⏳ History playlist add
This commit is contained in:
parent
f013cf296d
commit
b22b3a627f
46 changed files with 303 additions and 220 deletions
|
@ -53,6 +53,7 @@ object Constants {
|
||||||
)
|
)
|
||||||
const val NUMBER_OF_TOP_TRACKS = 99
|
const val NUMBER_OF_TOP_TRACKS = 99
|
||||||
}
|
}
|
||||||
|
const val HISTORY = 8
|
||||||
const val EXTRA_GENRE = "extra_genre"
|
const val EXTRA_GENRE = "extra_genre"
|
||||||
const val EXTRA_PLAYLIST = "extra_playlist"
|
const val EXTRA_PLAYLIST = "extra_playlist"
|
||||||
const val EXTRA_PLAYLIST_ID = "extra_playlist_id"
|
const val EXTRA_PLAYLIST_ID = "extra_playlist_id"
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ class OrderablePlaylistSongAdapter(
|
||||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_remove_from_playlist -> {
|
R.id.action_remove_from_playlist -> {
|
||||||
RemoveSongFromPlaylistDialog.create( song.toSongEntity(playlistId))
|
RemoveSongFromPlaylistDialog.create(song.toSongEntity(playlistId))
|
||||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package code.name.monkey.retromusic.db
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
|
||||||
|
@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
|
||||||
|
) {
|
||||||
|
fun toSong(): Song {
|
||||||
|
return Song(
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
trackNumber,
|
||||||
|
year,
|
||||||
|
duration,
|
||||||
|
data,
|
||||||
|
dateModified,
|
||||||
|
albumId,
|
||||||
|
albumName,
|
||||||
|
artistId,
|
||||||
|
artistName,
|
||||||
|
composer,
|
||||||
|
albumArtist
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
@ -44,4 +45,16 @@ interface PlaylistDao {
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun removeSongsFromPlaylist(songs: List<SongEntity>)
|
suspend fun removeSongsFromPlaylist(songs: List<SongEntity>)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun addSong(historyEntity: HistoryEntity)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1")
|
||||||
|
suspend fun songPresentInHistory(songId: Int): HistoryEntity?
|
||||||
|
|
||||||
|
@Update
|
||||||
|
suspend fun updateHistorySong(historyEntity: HistoryEntity)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM HistoryEntity ORDER BY time_played DESC")
|
||||||
|
fun historySongs(): LiveData<List<HistoryEntity>>
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [PlaylistEntity::class, SongEntity::class],
|
entities = [PlaylistEntity::class, SongEntity::class, HistoryEntity::class],
|
||||||
version = 8,
|
version = 16,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
abstract class RetroDatabase : RoomDatabase() {
|
abstract class RetroDatabase : RoomDatabase() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -54,7 +54,7 @@ fun AppCompatActivity.replaceFragment(
|
||||||
tag: String? = null,
|
tag: String? = null,
|
||||||
addToBackStack: Boolean = false
|
addToBackStack: Boolean = false
|
||||||
) {
|
) {
|
||||||
val compatActivity = this ?: return
|
val compatActivity = this
|
||||||
compatActivity.supportFragmentManager.beginTransaction()
|
compatActivity.supportFragmentManager.beginTransaction()
|
||||||
.apply {
|
.apply {
|
||||||
replace(id, fragment, tag)
|
replace(id, fragment, tag)
|
||||||
|
|
|
@ -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,7 @@ 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.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
@ -48,12 +49,29 @@ 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 -> loadHistory()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
lifecycleScope.launch(IO) {
|
lifecycleScope.launch(IO) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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.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 +16,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>()
|
||||||
|
@ -53,33 +54,33 @@ class LibraryViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +120,14 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayStateChanged() {
|
override fun onPlayStateChanged() {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -21,9 +21,9 @@ 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.EXTRA_PLAYLIST
|
||||||
|
import code.name.monkey.retromusic.HISTORY
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
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
|
||||||
|
@ -32,7 +32,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.LastAddedPlaylist
|
||||||
import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist
|
import code.name.monkey.retromusic.model.smartplaylist.TopTracksPlaylist
|
||||||
import code.name.monkey.retromusic.repository.Repository
|
import code.name.monkey.retromusic.repository.Repository
|
||||||
|
@ -96,9 +95,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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -72,7 +72,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
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,7 +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.HistoryEntity
|
||||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
import code.name.monkey.retromusic.db.SongEntity
|
import code.name.monkey.retromusic.db.SongEntity
|
||||||
|
@ -32,97 +34,56 @@ 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 removeSongsFromPlaylist(songs: List<SongEntity>)
|
suspend fun removeSongsFromPlaylist(songs: List<SongEntity>)
|
||||||
|
|
||||||
suspend fun removeSongFromPlaylist(songEntity: 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 favoritePlaylist(): List<PlaylistEntity>
|
||||||
|
|
||||||
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
||||||
|
suspend fun addSongToHistory(currentSong: Song)
|
||||||
|
suspend fun songPresentInHistory(currentSong: Song): HistoryEntity?
|
||||||
|
suspend fun updateHistorySong(currentSong: Song)
|
||||||
|
fun historySong(): LiveData<List<HistoryEntity>>
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealRepository(
|
class RealRepository(
|
||||||
|
@ -136,7 +97,7 @@ class RealRepository(
|
||||||
private val playlistRepository: PlaylistRepository,
|
private val playlistRepository: PlaylistRepository,
|
||||||
private val searchRepository: RealSearchRepository,
|
private val searchRepository: RealSearchRepository,
|
||||||
private val playedTracksRepository: TopPlayedRepository,
|
private val playedTracksRepository: 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()
|
||||||
|
@ -241,7 +202,7 @@ class RealRepository(
|
||||||
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 {
|
||||||
|
@ -250,37 +211,49 @@ 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 removeSongsFromPlaylist(songs: List<SongEntity>) =
|
override suspend fun removeSongsFromPlaylist(songs: List<SongEntity>) =
|
||||||
roomPlaylistRepository.removeSongsFromPlaylist(songs)
|
roomRepository.removeSongsFromPlaylist(songs)
|
||||||
|
|
||||||
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
|
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
|
||||||
roomPlaylistRepository.removeSongFromPlaylist(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> =
|
override suspend fun favoritePlaylist(): List<PlaylistEntity> =
|
||||||
roomPlaylistRepository.favoritePlaylist(context.getString(R.string.favorites))
|
roomRepository.favoritePlaylist(context.getString(R.string.favorites))
|
||||||
|
|
||||||
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
|
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
|
||||||
roomPlaylistRepository.isFavoriteSong(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 fun historySong(): LiveData<List<HistoryEntity>> =
|
||||||
|
roomRepository.historySongs()
|
||||||
|
|
||||||
override suspend fun suggestionsHome(): Home {
|
override suspend fun suggestionsHome(): Home {
|
||||||
val songs =
|
val songs =
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -21,6 +20,10 @@ interface RoomPlaylistRepository {
|
||||||
suspend fun favoritePlaylist(favorite: String): List<PlaylistEntity>
|
suspend fun favoritePlaylist(favorite: String): List<PlaylistEntity>
|
||||||
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
||||||
suspend fun removeSongFromPlaylist(songEntity: SongEntity)
|
suspend fun removeSongFromPlaylist(songEntity: SongEntity)
|
||||||
|
suspend fun addSongToHistory(currentSong: Song)
|
||||||
|
suspend fun songPresentInHistory(song: Song): HistoryEntity?
|
||||||
|
suspend fun updateHistorySong(song: Song)
|
||||||
|
fun historySongs(): LiveData<List<HistoryEntity>>
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealRoomRepository(
|
class RealRoomRepository(
|
||||||
|
@ -83,4 +86,17 @@ class RealRoomRepository(
|
||||||
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
|
override suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
|
||||||
playlistDao.removeSong(songEntity.playlistCreatorId, songEntity.id)
|
playlistDao.removeSong(songEntity.playlistCreatorId, songEntity.id)
|
||||||
|
|
||||||
|
override suspend fun addSongToHistory(currentSong: Song) =
|
||||||
|
playlistDao.addSong(currentSong.toHistoryEntity(System.currentTimeMillis()))
|
||||||
|
|
||||||
|
override suspend fun songPresentInHistory(song: Song): HistoryEntity? =
|
||||||
|
playlistDao.songPresentInHistory(song.id)
|
||||||
|
|
||||||
|
override suspend fun updateHistorySong(song: Song) =
|
||||||
|
playlistDao.updateHistorySong(song.toHistoryEntity(System.currentTimeMillis()))
|
||||||
|
|
||||||
|
override fun historySongs(): LiveData<List<HistoryEntity>> {
|
||||||
|
return playlistDao.historySongs()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:layoutAnimation="@anim/layout_animation_fall_down"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
@ -72,10 +73,10 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.ProgressIndicator
|
<com.google.android.material.progressindicator.ProgressIndicator
|
||||||
android:layout_width="100dp"
|
|
||||||
android:id="@+id/progressIndicator"
|
android:id="@+id/progressIndicator"
|
||||||
|
android:layout_width="100dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:indeterminate="true"
|
android:gravity="center"
|
||||||
android:gravity="center" />
|
android:indeterminate="true" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
Loading…
Reference in a new issue