Here's a list of changes/features: https://github.com/RetroMusicPlayer/RetroMusicPlayer/releases/tag/v5.0

Internal Changes:
1) Migrated to ViewBinding
2) Migrated to Glide V4
3) Migrated to kotlin version of Material Dialogs
This commit is contained in:
Prathamesh More 2021-09-09 00:00:20 +05:30
parent fc42767031
commit bce6dbfa27
421 changed files with 13285 additions and 5757 deletions

View file

@ -0,0 +1,50 @@
package code.name.monkey.retromusic.service
import code.name.monkey.retromusic.service.playback.Playback
import java.util.*
class AudioFader(
private val player: Playback,
durationMillis: Long,
private val fadeIn: Boolean,
private val doOnEnd: Runnable
) {
val timer = Timer()
var volume = if (fadeIn) 0F else 1F
val maxVolume = if (fadeIn) 1F else 0F
private val volumeStep: Float = PERIOD / durationMillis.toFloat()
fun start() {
timer.scheduleAtFixedRate(
object : TimerTask() {
override fun run() {
setVolume()
if (volume < 0 || volume > 1) {
player.setVolume(maxVolume)
stop()
doOnEnd.run()
} else {
player.setVolume(volume)
}
}
}, 0, PERIOD
)
}
fun stop() {
timer.purge()
timer.cancel()
}
private fun setVolume() {
if (fadeIn) {
volume += volumeStep
} else {
volume -= volumeStep
}
}
companion object {
const val PERIOD = 100L
}
}

View file

@ -0,0 +1,406 @@
package code.name.monkey.retromusic.service
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.MediaPlayer
import android.media.audiofx.AudioEffect
import android.net.Uri
import android.os.Handler
import android.os.Message
import android.os.PowerManager
import android.widget.Toast
import androidx.core.animation.doOnEnd
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.service.playback.Playback
import code.name.monkey.retromusic.service.playback.Playback.PlaybackCallbacks
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
/** @author Prathamesh M */
/*
* To make Crossfade work we need two MediaPlayer's
* Basically, we switch back and forth between those two mp's
* e.g. When song is about to end (Reaches Crossfade duration) we let current mediaplayer
* play but with decreasing volume and start the player with the next song with increasing volume
* and vice versa for upcoming song and so on.
*/
class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener {
private var currentPlayer: CurrentPlayer = CurrentPlayer.NOT_SET
private var player1 = MediaPlayer()
private var player2 = MediaPlayer()
private var durationListener = DurationListener()
private var trackEndHandledByCrossFade = false
private var mIsInitialized = false
private var hasDataSource: Boolean = false /* Whether first player has DataSource */
private var fadeInAnimator: Animator? = null
private var fadeOutAnimator: Animator? = null
private var callbacks: PlaybackCallbacks? = null
init {
player1.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
player2.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
currentPlayer = CurrentPlayer.PLAYER_ONE
}
override fun start(): Boolean {
durationListener.start()
return try {
getCurrentPlayer()?.start()
true
} catch (e: IllegalStateException) {
e.printStackTrace()
false
}
}
override fun release() {
getCurrentPlayer()?.release()
getNextPlayer()?.release()
durationListener.stop()
}
override fun setCallbacks(callbacks: PlaybackCallbacks) {
this.callbacks = callbacks
}
override fun stop() {
getCurrentPlayer()?.reset()
mIsInitialized = false
}
override fun pause(): Boolean {
durationListener.stop()
cancelFade()
getCurrentPlayer()?.let {
if (it.isPlaying) {
it.pause()
}
}
getNextPlayer()?.let {
if (it.isPlaying) {
it.pause()
}
}
return true
}
override fun seek(whereto: Int): Int {
cancelFade()
getNextPlayer()?.stop()
return try {
getCurrentPlayer()?.seekTo(whereto)
whereto
} catch (e: java.lang.IllegalStateException) {
e.printStackTrace()
-1
}
}
override fun setVolume(vol: Float): Boolean {
cancelFade()
return try {
getCurrentPlayer()?.setVolume(vol, vol)
true
} catch (e: IllegalStateException) {
e.printStackTrace()
false
}
}
override val isInitialized: Boolean
get() = mIsInitialized
override val isPlaying: Boolean
get() = mIsInitialized && getCurrentPlayer()?.isPlaying == true
// This has to run when queue is changed or song is changed manually by user
fun sourceChangedByUser() {
this.hasDataSource = false
cancelFade()
getCurrentPlayer()?.apply {
if (isPlaying) stop()
}
getNextPlayer()?.apply {
if (isPlaying) stop()
}
}
override fun setDataSource(path: String): Boolean {
cancelFade()
mIsInitialized = false
/* We've already set DataSource if initialized is true in setNextDataSource */
if (!hasDataSource) {
getCurrentPlayer()?.let { mIsInitialized = setDataSourceImpl(it, path) }
hasDataSource = true
} else {
mIsInitialized = true
}
return mIsInitialized
}
override fun setNextDataSource(path: String?) {}
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
private fun setDataSourceImpl(
player: MediaPlayer,
path: String
): Boolean {
player.reset()
player.setOnPreparedListener(null)
try {
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path))
} else {
player.setDataSource(path)
}
player.setAudioAttributes(
AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build()
)
player.prepare()
} catch (e: Exception) {
e.printStackTrace()
return false
}
player.setOnCompletionListener(this)
player.setOnErrorListener(this)
val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId)
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
context.sendBroadcast(intent)
return true
}
override fun setAudioSessionId(sessionId: Int): Boolean {
return try {
getCurrentPlayer()?.audioSessionId = sessionId
true
} catch (e: IllegalArgumentException) {
e.printStackTrace()
false
} catch (e: IllegalStateException) {
e.printStackTrace()
false
}
}
override val audioSessionId: Int
get() = getCurrentPlayer()?.audioSessionId!!
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
override fun duration(): Int {
return if (!mIsInitialized) {
-1
} else try {
getCurrentPlayer()?.duration!!
} catch (e: IllegalStateException) {
e.printStackTrace()
-1
}
}
/**
* Gets the current position in audio.
* @return The position in milliseconds
*/
override fun position(): Int {
return if (!mIsInitialized) {
-1
} else try {
getCurrentPlayer()?.currentPosition!!
} catch (e: IllegalStateException) {
e.printStackTrace()
-1
}
}
override fun onCompletion(mp: MediaPlayer?) {
if (mp == getNextPlayer()) {
if (trackEndHandledByCrossFade) {
trackEndHandledByCrossFade = false
} else {
notifyTrackEnded()
}
}
}
private fun notifyTrackEnded(){
if (callbacks != null) {
callbacks?.onTrackEnded()
}
}
private fun getCurrentPlayer(): MediaPlayer? {
return when (currentPlayer) {
CurrentPlayer.PLAYER_ONE -> {
player1
}
CurrentPlayer.PLAYER_TWO -> {
player2
}
CurrentPlayer.NOT_SET -> {
null
}
}
}
private fun getNextPlayer(): MediaPlayer? {
return when (currentPlayer) {
CurrentPlayer.PLAYER_ONE -> {
player2
}
CurrentPlayer.PLAYER_TWO -> {
player1
}
CurrentPlayer.NOT_SET -> {
null
}
}
}
private fun fadeIn(mediaPlayer: MediaPlayer) {
fadeInAnimator = createFadeAnimator(true, mediaPlayer) {
println("Fade In Completed")
fadeInAnimator = null
}
fadeInAnimator?.start()
}
private fun fadeOut(mediaPlayer: MediaPlayer) {
fadeOutAnimator = createFadeAnimator(false, mediaPlayer) {
println("Fade Out Completed")
fadeOutAnimator = null
}
fadeOutAnimator?.start()
}
private fun cancelFade() {
fadeInAnimator?.cancel()
fadeOutAnimator?.cancel()
fadeInAnimator = null
fadeOutAnimator = null
getCurrentPlayer()?.setVolume(1f, 1f)
getNextPlayer()?.setVolume(0f, 0f)
}
private fun createFadeAnimator(
fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/,
mediaPlayer: MediaPlayer,
callback: Runnable /* Code to run when Animator Ends*/
): Animator? {
val duration = PreferenceUtil.crossFadeDuration * 1000
if (duration == 0) {
return null
}
val startValue = if (fadeIn) 0f else 1.0f
val endValue = if (fadeIn) 1.0f else 0f
val animator = ValueAnimator.ofFloat(startValue, endValue)
animator.duration = duration.toLong()
animator.addUpdateListener { animation: ValueAnimator ->
mediaPlayer.setVolume(
animation.animatedValue as Float, animation.animatedValue as Float
)
}
animator.doOnEnd {
callback.run()
// Set end values
mediaPlayer.setVolume(endValue, endValue)
}
return animator
}
override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
mIsInitialized = false
mp?.release()
player1 = MediaPlayer()
player2 = MediaPlayer()
mIsInitialized = true
mp?.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
Toast.makeText(
context,
context.resources.getString(R.string.unplayable_file),
Toast.LENGTH_SHORT
)
.show()
return false
}
enum class CurrentPlayer {
PLAYER_ONE,
PLAYER_TWO,
NOT_SET
}
inner class DurationListener : Handler() {
fun start() {
nextRefresh()
}
fun stop() {
removeMessages(DURATION_CHANGED)
}
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == DURATION_CHANGED) {
nextRefresh()
onDurationUpdated(position(), duration())
}
}
private fun nextRefresh() {
val message = obtainMessage(DURATION_CHANGED)
removeMessages(DURATION_CHANGED)
sendMessageDelayed(message, 100)
}
}
fun onDurationUpdated(progress: Int, total: Int) {
if (total > 0 && (total - progress).div(1000) == PreferenceUtil.crossFadeDuration) {
getNextPlayer()?.let { player ->
val nextSong = MusicPlayerRemote.nextSong
if (nextSong != null) {
setDataSourceImpl(player, MusicUtil.getSongFileUri(nextSong.id).toString())
// Switch to other player / Crossfade only if next song exists
switchPlayer()
}
}
}
}
private fun switchPlayer() {
getNextPlayer()?.start()
getCurrentPlayer()?.let { fadeOut(it) }
getNextPlayer()?.let { fadeIn(it) }
currentPlayer =
if (currentPlayer == CurrentPlayer.PLAYER_ONE || currentPlayer == CurrentPlayer.NOT_SET) {
CurrentPlayer.PLAYER_TWO
} else {
CurrentPlayer.PLAYER_ONE
}
notifyTrackEnded()
trackEndHandledByCrossFade = true
}
companion object {
private const val DURATION_CHANGED = 1
}
}

View file

@ -18,11 +18,19 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v4.media.session.MediaSessionCompat
import code.name.monkey.retromusic.auto.AutoMediaIDHelper
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicPlayerRemote.cycleRepeatMode
import code.name.monkey.retromusic.helper.ShuffleHelper.makeShuffleList
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.*
import code.name.monkey.retromusic.service.MusicService.*
import code.name.monkey.retromusic.util.MusicUtil
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.util.*
@ -33,7 +41,76 @@ import java.util.*
class MediaSessionCallback(
private val context: Context,
private val musicService: MusicService
) : MediaSessionCompat.Callback() {
) : MediaSessionCompat.Callback(), KoinComponent {
private val songRepository by inject<SongRepository>()
private val albumRepository by inject<AlbumRepository>()
private val artistRepository by inject<ArtistRepository>()
private val genreRepository by inject<GenreRepository>()
private val playlistRepository by inject<PlaylistRepository>()
private val topPlayedRepository by inject<TopPlayedRepository>()
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
super.onPlayFromMediaId(mediaId, extras)
val musicId = AutoMediaIDHelper.extractMusicID(mediaId!!)
println(musicId)
val itemId = musicId?.toLong() ?: -1
val songs: ArrayList<Song> = ArrayList()
val category = AutoMediaIDHelper.extractCategory(mediaId)
when (category) {
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> {
val album: Album = albumRepository.album(itemId)
songs.addAll(album.songs)
musicService.openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST -> {
val artist: Artist = artistRepository.artist(itemId)
songs.addAll(artist.songs)
musicService.openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM_ARTIST -> {
val artist: Artist =
artistRepository.albumArtist(albumRepository.album(itemId).albumArtist!!)
songs.addAll(artist.songs)
musicService.openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST -> {
val playlist: Playlist = playlistRepository.playlist(itemId)
songs.addAll(playlist.getSongs())
musicService.openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE -> {
songs.addAll(genreRepository.songs(itemId))
musicService.openQueue(songs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SHUFFLE -> {
val allSongs: ArrayList<Song> = songRepository.songs() as ArrayList<Song>
makeShuffleList(allSongs, -1)
musicService.openQueue(allSongs, 0, true)
}
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY,
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS,
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS,
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_QUEUE -> {
val tracks: List<Song> = when (category) {
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_HISTORY -> topPlayedRepository.recentlyPlayedTracks()
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_SUGGESTIONS -> topPlayedRepository.recentlyPlayedTracks()
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_TOP_TRACKS -> topPlayedRepository.recentlyPlayedTracks()
else -> musicService.playingQueue as List<Song>
}
songs.addAll(tracks)
var songIndex = MusicUtil.indexOfSongInList(tracks, itemId)
if (songIndex == -1) {
songIndex = 0
}
musicService.openQueue(songs, songIndex, true)
}
else -> {
}
}
musicService.play()
}
override fun onPlay() {
super.onPlay()

View file

@ -23,300 +23,302 @@ import android.net.Uri;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.PreferenceUtil;
/** @author Andrew Neal, Karim Abou Zeid (kabouzeid) */
public class MultiPlayer
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer;
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer;
private Context context;
@Nullable private Playback.PlaybackCallbacks callbacks;
private Context context;
@Nullable private Playback.PlaybackCallbacks callbacks;
private boolean mIsInitialized = false;
private boolean mIsInitialized = false;
/** Constructor of <code>MultiPlayer</code> */
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
}
/** Constructor of <code>MultiPlayer</code> */
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
}
/**
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
@Override
public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
if (mIsInitialized) {
setNextDataSource(null);
/**
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
@Override
public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
if (mIsInitialized) {
setNextDataSource(null);
}
return mIsInitialized;
}
return mIsInitialized;
}
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
if (context == null) {
return false;
}
try {
player.reset();
player.setOnPreparedListener(null);
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path));
} else {
player.setDataSource(path);
}
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
return false;
}
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
context.sendBroadcast(intent);
return true;
}
/**
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
*
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
*/
@Override
public void setNextDataSource(@Nullable final String path) {
if (context == null) {
return;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(null);
} catch (IllegalArgumentException e) {
Log.i(TAG, "Next media player is current one, continuing");
} catch (IllegalStateException e) {
Log.e(TAG, "Media player not initialized!");
return;
}
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
if (path == null) {
return;
}
if (PreferenceUtil.INSTANCE.isGapLessPlayback()) {
mNextMediaPlayer = new MediaPlayer();
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) {
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
if (context == null) {
return false;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
if (mNextMediaPlayer != null) {
player.reset();
player.setOnPreparedListener(null);
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path));
} else {
player.setDataSource(path);
}
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
return false;
}
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
context.sendBroadcast(intent);
return true;
}
/**
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
*
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
*/
@Override
public void setNextDataSource(@Nullable final String path) {
if (context == null) {
return;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(null);
} catch (IllegalArgumentException e) {
Log.i(TAG, "Next media player is current one, continuing");
} catch (IllegalStateException e) {
Log.e(TAG, "Media player not initialized!");
return;
}
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
} else {
if (path == null) {
return;
}
if (PreferenceUtil.INSTANCE.isGapLessPlayback()) {
mNextMediaPlayer = new MediaPlayer();
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) {
try {
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
} else {
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
}
}
/**
* Sets the callbacks
*
* @param callbacks The callbacks to use
*/
@Override
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
}
/** @return True if the player is ready to go, false otherwise */
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/** Starts or resumes playback. */
@Override
public boolean start() {
try {
mCurrentMediaPlayer.start();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/** Resets the MediaPlayer to its uninitialized state. */
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/** Releases resources associated with this MediaPlayer object. */
@Override
public void release() {
stop();
mCurrentMediaPlayer.release();
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
mNextMediaPlayer.release();
}
}
}
}
/**
* Sets the callbacks
*
* @param callbacks The callbacks to use
*/
@Override
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
}
/** @return True if the player is ready to go, false otherwise */
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/** Starts or resumes playback. */
@Override
public boolean start() {
try {
mCurrentMediaPlayer.start();
return true;
} catch (IllegalStateException e) {
return false;
/** Pauses playback. Call start() to resume. */
@Override
public boolean pause() {
try {
mCurrentMediaPlayer.pause();
return true;
} catch (IllegalStateException e) {
return false;
}
}
}
/** Resets the MediaPlayer to its uninitialized state. */
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/** Releases resources associated with this MediaPlayer object. */
@Override
public void release() {
stop();
mCurrentMediaPlayer.release();
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
/** Checks whether the MultiPlayer is playing. */
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
}
}
/** Pauses playback. Call start() to resume. */
@Override
public boolean pause() {
try {
mCurrentMediaPlayer.pause();
return true;
} catch (IllegalStateException e) {
return false;
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
@Override
public int duration() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getDuration();
} catch (IllegalStateException e) {
return -1;
}
}
}
/** Checks whether the MultiPlayer is playing. */
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
}
/**
* Gets the current playback position.
*
* @return The current position in milliseconds
*/
@Override
public int position() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
@Override
public int duration() {
if (!mIsInitialized) {
return -1;
/**
* Gets the current playback position.
*
* @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to
*/
@Override
public int seek(final int whereto) {
try {
mCurrentMediaPlayer.seekTo(whereto);
return whereto;
} catch (IllegalStateException e) {
return -1;
}
}
try {
return mCurrentMediaPlayer.getDuration();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @return The current position in milliseconds
*/
@Override
public int position() {
if (!mIsInitialized) {
return -1;
@Override
public boolean setVolume(final float vol) {
try {
mCurrentMediaPlayer.setVolume(vol, vol);
return true;
} catch (IllegalStateException e) {
return false;
}
}
try {
return mCurrentMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to
*/
@Override
public int seek(final int whereto) {
try {
mCurrentMediaPlayer.seekTo(whereto);
return whereto;
} catch (IllegalStateException e) {
return -1;
/**
* Sets the audio session ID.
*
* @param sessionId The audio session ID
*/
@Override
public boolean setAudioSessionId(final int sessionId) {
try {
mCurrentMediaPlayer.setAudioSessionId(sessionId);
return true;
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
return false;
}
}
}
@Override
public boolean setVolume(final float vol) {
try {
mCurrentMediaPlayer.setVolume(vol, vol);
return true;
} catch (IllegalStateException e) {
return false;
/**
* Returns the audio session ID.
*
* @return The current audio session ID.
*/
@Override
public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId();
}
}
/**
* Sets the audio session ID.
*
* @param sessionId The audio session ID
*/
@Override
public boolean setAudioSessionId(final int sessionId) {
try {
mCurrentMediaPlayer.setAudioSessionId(sessionId);
return true;
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
return false;
/** {@inheritDoc} */
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer();
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
if (context != null) {
Toast.makeText(
context,
context.getResources().getString(R.string.unplayable_file),
Toast.LENGTH_SHORT)
.show();
}
return false;
}
}
/**
* Returns the audio session ID.
*
* @return The current audio session ID.
*/
@Override
public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId();
}
/** {@inheritDoc} */
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer();
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
if (context != null) {
Toast.makeText(
context,
context.getResources().getString(R.string.unplayable_file),
Toast.LENGTH_SHORT)
.show();
/** {@inheritDoc} */
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true;
mNextMediaPlayer = null;
if (callbacks != null) callbacks.onTrackWentToNext();
} else {
if (callbacks != null) callbacks.onTrackEnded();
}
}
return false;
}
/** {@inheritDoc} */
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true;
mNextMediaPlayer = null;
if (callbacks != null) callbacks.onTrackWentToNext();
} else {
if (callbacks != null) callbacks.onTrackEnded();
}
}
}
}

View file

@ -14,8 +14,15 @@
package code.name.monkey.retromusic.service;
import static org.koin.java.KoinJavaComponent.get;
import static code.name.monkey.retromusic.ConstantsKt.ALBUM_ART_ON_LOCK_SCREEN;
import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART;
import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
@ -34,12 +41,14 @@ import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
import android.provider.MediaStore;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
@ -50,11 +59,10 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.media.MediaBrowserServiceCompat;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.BitmapRequestBuilder;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.request.target.SimpleTarget;
import java.util.ArrayList;
@ -69,11 +77,13 @@ import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
import code.name.monkey.retromusic.appwidgets.AppWidgetText;
import code.name.monkey.retromusic.auto.AutoMediaIDHelper;
import code.name.monkey.retromusic.auto.AutoMusicProvider;
import code.name.monkey.retromusic.glide.BlurTransformation;
import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.glide.GlideApp;
import code.name.monkey.retromusic.glide.RetroGlideExtension;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.helper.ShuffleHelper;
import code.name.monkey.retromusic.model.AbsCustomPlaylist;
import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist;
import code.name.monkey.retromusic.providers.HistoryStore;
@ -84,20 +94,14 @@ import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl;
import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo;
import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.MusicUtil;
import code.name.monkey.retromusic.util.PackageValidator;
import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroUtil;
import static code.name.monkey.retromusic.ConstantsKt.ALBUM_ART_ON_LOCK_SCREEN;
import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART;
import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.GAP_LESS_PLAYBACK;
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
/**
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
*/
public class MusicService extends Service
public class MusicService extends MediaBrowserServiceCompat
implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public static final String TAG = MusicService.class.getSimpleName();
@ -168,6 +172,12 @@ public class MusicService extends Service
@Nullable
public Playback playback;
private PackageValidator mPackageValidator;
private final AutoMusicProvider mMusicProvider = get(AutoMusicProvider.class);
public boolean trackEndedByCrossfade = false;
public int position = -1;
private AppWidgetBig appWidgetBig = AppWidgetBig.Companion.getInstance();
@ -338,6 +348,7 @@ public class MusicService extends Service
private ThrottledSeekHandler throttledSeekHandler;
private Handler uiThreadHandler;
private PowerManager.WakeLock wakeLock;
private AudioFader fader;
private static Bitmap copy(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
@ -375,7 +386,13 @@ public class MusicService extends Service
musicPlayerHandlerThread.start();
playerHandler = new PlaybackHandler(this, musicPlayerHandlerThread.getLooper());
playback = new MultiPlayer(this);
// Set MultiPlayer when crossfade duration is 0 i.e. off
if (PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
playback = new MultiPlayer(this);
} else {
playback = new CrossFadePlayer(this);
}
playback.setCallbacks(this);
setupMediaSession();
@ -437,6 +454,10 @@ public class MusicService extends Service
registerHeadsetEvents();
registerBluetoothConnected();
mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers);
mMusicProvider.setMusicService(this);
setSessionToken(mediaSession.getSessionToken());
}
@Override
@ -536,6 +557,14 @@ public class MusicService extends Service
return getSongAt(getPosition());
}
public Song getNextSong() {
if (!isLastTrack() || getRepeatMode() == REPEAT_MODE_THIS) {
return getSongAt(getNextPosition(false));
} else {
return null;
}
}
@NonNull
public MediaSessionCompat getMediaSession() {
return mediaSession;
@ -764,19 +793,68 @@ public class MusicService extends Service
@NonNull
@Override
public IBinder onBind(Intent intent) {
// For Android auto, need to call super, or onGetRoot won't be called.
if (intent != null && "android.media.browse.MediaBrowserService".equals(intent.getAction())) {
return super.onBind(intent);
}
return musicBind;
}
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
// Check origin to ensure we're not allowing any arbitrary app to browse app contents
if (!mPackageValidator.isKnownCaller(clientPackageName, clientUid)) {
// Request from an untrusted package: return an empty browser root
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
}
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_ROOT, null);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>> result) {
result.sendResult(mMusicProvider.getChildren(parentId, getResources()));
}
@Override
public void onSharedPreferenceChanged(
@NonNull SharedPreferences sharedPreferences, @NonNull String key) {
switch (key) {
case GAP_LESS_PLAYBACK:
if (sharedPreferences.getBoolean(key, false)) {
prepareNext();
} else {
case CROSS_FADE_DURATION:
int progress = getSongProgressMillis();
boolean wasPlaying = isPlaying();
/* Switch to MultiPlayer if Crossfade duration is 0 and
Playback is not an instance of MultiPlayer */
if (!(playback instanceof MultiPlayer) && PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
if (playback != null) {
playback.setNextDataSource(null);
playback.release();
}
playback = null;
playback = new MultiPlayer(this);
playback.setCallbacks(this);
if (openTrackAndPrepareNextAt(position)) {
seek(progress);
if (wasPlaying) {
play();
}
}
}
/* Switch to CrossFadePlayer if Crossfade duration is greater than 0 and
Playback is not an instance of CrossFadePlayer */
else if (!(playback instanceof CrossFadePlayer) && PreferenceUtil.INSTANCE.getCrossFadeDuration() > 0) {
if (playback != null) {
playback.release();
}
playback = null;
playback = new CrossFadePlayer(this);
playback.setCallbacks(this);
if (openTrackAndPrepareNextAt(position)) {
seek(progress);
if (wasPlaying) {
play();
}
}
}
break;
@ -901,6 +979,22 @@ public class MusicService extends Service
}
public void pause() {
pausedByTransientLossOfFocus = false;
if (playback != null && playback.isPlaying()) {
if (fader != null) {
fader.stop();
}
fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> {
if (playback != null && playback.isPlaying()) {
playback.pause();
notifyChange(PLAY_STATE_CHANGED);
}
});
fader.start();
}
}
public void forcePause() {
pausedByTransientLossOfFocus = false;
if (playback != null && playback.isPlaying()) {
playback.pause();
@ -915,21 +1009,31 @@ public class MusicService extends Service
if (!playback.isInitialized()) {
playSongAt(getPosition());
} else {
playback.start();
if (!becomingNoisyReceiverRegistered) {
registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter);
becomingNoisyReceiverRegistered = true;
//Don't Start playing when it's casting
if (MusicPlayerRemote.INSTANCE.isCasting()) {
return;
}
if (notHandledMetaChangedForCurrentTrack) {
handleChangeInternal(META_CHANGED);
notHandledMetaChangedForCurrentTrack = false;
if (fader != null) {
fader.stop();
}
notifyChange(PLAY_STATE_CHANGED);
fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> {
if (!becomingNoisyReceiverRegistered) {
registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter);
becomingNoisyReceiverRegistered = true;
}
if (notHandledMetaChangedForCurrentTrack) {
handleChangeInternal(META_CHANGED);
notHandledMetaChangedForCurrentTrack = false;
}
// fixes a bug where the volume would stay ducked because the
// AudioManager.AUDIOFOCUS_GAIN event is not sent
playerHandler.removeMessages(DUCK);
playerHandler.sendEmptyMessage(UNDUCK);
// fixes a bug where the volume would stay ducked because the
// AudioManager.AUDIOFOCUS_GAIN event is not sent
playerHandler.removeMessages(DUCK);
playerHandler.sendEmptyMessage(UNDUCK);
});
playback.start();
notifyChange(PLAY_STATE_CHANGED);
fader.start();
}
}
} else {
@ -955,6 +1059,14 @@ public class MusicService extends Service
}
public void playSongAtImpl(int position) {
if (!trackEndedByCrossfade) {
// This is only imp if we are using crossfade
if (playback instanceof CrossFadePlayer) {
((CrossFadePlayer) playback).sourceChangedByUser();
}
} else {
trackEndedByCrossfade = false;
}
if (openTrackAndPrepareNextAt(position)) {
play();
} else {
@ -978,7 +1090,7 @@ public class MusicService extends Service
}
}
public boolean prepareNextImpl() {
public void prepareNextImpl() {
synchronized (this) {
try {
int nextPosition = getNextPosition(false);
@ -986,9 +1098,7 @@ public class MusicService extends Service
playback.setNextDataSource(getTrackUri(Objects.requireNonNull(getSongAt(nextPosition))));
}
this.nextPosition = nextPosition;
return true;
} catch (Exception e) {
return false;
}
}
}
@ -1176,9 +1286,7 @@ public class MusicService extends Service
if (PreferenceUtil.INSTANCE.isAlbumArtOnLockScreen()) {
final Point screenSize = RetroUtil.getScreenSize(MusicService.this);
final BitmapRequestBuilder<?, Bitmap> request = SongGlideRequest.Builder.from(Glide.with(MusicService.this), song)
.checkIgnoreMediaStore(MusicService.this)
.asBitmap().build();
final RequestBuilder<Bitmap> request = GlideApp.with(MusicService.this).asBitmap().songCoverOptions(song).load(RetroGlideExtension.INSTANCE.getSongModel(song));
if (PreferenceUtil.INSTANCE.isBlurredAlbumArt()) {
request.transform(new BlurTransformation.Builder(MusicService.this).build());
}
@ -1187,14 +1295,13 @@ public class MusicService extends Service
public void run() {
request.into(new SimpleTarget<Bitmap>(screenSize.x, screenSize.y) {
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
public void onLoadFailed(Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
mediaSession.setMetadata(metaData.build());
}
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
public void onResourceReady(@NonNull Bitmap resource, @Nullable com.bumptech.glide.request.transition.Transition<? super Bitmap> transition) {
metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource));
mediaSession.setMetadata(metaData.build());
}
@ -1238,6 +1345,7 @@ public class MusicService extends Service
case META_CHANGED:
updateNotification();
updateMediaSessionMetaData();
updateMediaSessionPlaybackState();
savePosition();
savePositionInTrack();
final Song currentSong = getCurrentSong();
@ -1266,6 +1374,7 @@ public class MusicService extends Service
return playback.setDataSource(getTrackUri(Objects.requireNonNull(getCurrentSong())));
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

View file

@ -25,10 +25,13 @@ import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import code.name.monkey.retromusic.util.PreferenceUtil;
import java.lang.ref.WeakReference;
import code.name.monkey.retromusic.util.PreferenceUtil;
class PlaybackHandler extends Handler {
@NonNull private final WeakReference<MusicService> mService;
@ -93,6 +96,7 @@ class PlaybackHandler extends Handler {
break;
case TRACK_ENDED:
service.trackEndedByCrossfade = true;
// if there is a timer finished, don't continue
if (service.pendingQuit
|| service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
@ -143,7 +147,7 @@ class PlaybackHandler extends Handler {
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media playback
service.pause();
service.forcePause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
@ -151,7 +155,7 @@ class PlaybackHandler extends Handler {
// playback. We don't release the media playback because playback
// is likely to resume
boolean wasPlaying = service.isPlaying();
service.pause();
service.forcePause();
service.setPausedByTransientLossOfFocus(wasPlaying);
break;

View file

@ -29,7 +29,8 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.service.MusicService.*
@ -37,9 +38,9 @@ import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.koin.core.KoinComponent
@ -79,11 +80,11 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
.getDimensionPixelSize(R.dimen.notification_big_image_size)
service.runOnUiThread {
if (target != null) {
Glide.clear(target)
Glide.with(service).clear(target)
}
target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
//.checkIgnoreMediaStore()
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
@ -91,7 +92,7 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
transition: Transition<in BitmapPaletteWrapper>?
) {
update(
resource.bitmap,
@ -99,8 +100,8 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
)
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(null, Color.TRANSPARENT)
}

View file

@ -28,7 +28,8 @@ import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.MusicService
@ -38,9 +39,9 @@ import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.RetroUtil.createBitmap
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
/**
* @author Hemanth S (h4h13).
@ -95,11 +96,10 @@ class PlayingNotificationOreo : PlayingNotification() {
.getDimensionPixelSize(R.dimen.notification_big_image_size)
service.runOnUiThread {
if (target != null) {
Glide.clear(target)
Glide.with(service).clear(target)
}
target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
@ -107,7 +107,7 @@ class PlayingNotificationOreo : PlayingNotification() {
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
transition: Transition<in BitmapPaletteWrapper>?
) {
/* val mediaNotificationProcessor = MediaNotificationProcessor(
service,
@ -119,8 +119,8 @@ class PlayingNotificationOreo : PlayingNotification() {
update(resource.bitmap, colors.backgroundColor)
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable)
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(
null,
resolveColor(service, R.attr.colorSurface, Color.WHITE)