History playlist add

This commit is contained in:
Hemanth S 2020-08-21 19:49:15 +05:30
parent f013cf296d
commit b22b3a627f
46 changed files with 303 additions and 220 deletions

View file

@ -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"

View file

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

View file

@ -121,7 +121,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
} }
} }
private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) : private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelopmentActivity) :
AsyncTask<Void, Void, List<SkuDetails>>() { AsyncTask<Void, Void, List<SkuDetails>>() {
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference( private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(

View file

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

View file

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

View file

@ -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
)
}
}

View file

@ -1,5 +1,6 @@
package code.name.monkey.retromusic.db package code.name.monkey.retromusic.db
import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
@Dao @Dao
@ -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>>
} }

View file

@ -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() {

View file

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

View file

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

View file

@ -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)

View file

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

View file

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

View file

@ -3,6 +3,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,10 +49,27 @@ 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() {

View file

@ -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() {

View file

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

View file

@ -6,8 +6,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async

View file

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

View file

@ -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)
) )
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -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 {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,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 =

View file

@ -1,10 +1,9 @@
package code.name.monkey.retromusic.repository package code.name.monkey.retromusic.repository
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import code.name.monkey.retromusic.db.PlaylistDao import androidx.lifecycle.LiveData
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.db.SongEntity
interface RoomPlaylistRepository { interface RoomPlaylistRepository {
@ -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()
}
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -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());
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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>