Fixed Audio Fade, Crossfade & Android Auto search
This commit is contained in:
parent
7f683eb0ee
commit
ee7545f64e
15 changed files with 269 additions and 79 deletions
|
@ -1,50 +1,66 @@
|
|||
package code.name.monkey.retromusic.service
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.media.MediaPlayer
|
||||
import androidx.core.animation.doOnEnd
|
||||
import code.name.monkey.retromusic.service.playback.Playback
|
||||
import java.util.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
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()
|
||||
class AudioFader {
|
||||
companion object {
|
||||
|
||||
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)
|
||||
}
|
||||
@JvmStatic
|
||||
inline fun createFadeAnimator(
|
||||
fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/,
|
||||
mediaPlayer: MediaPlayer,
|
||||
crossinline endAction: (animator: Animator) -> Unit /* 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
|
||||
return ValueAnimator.ofFloat(startValue, endValue).apply {
|
||||
this.duration = duration.toLong()
|
||||
addUpdateListener { animation: ValueAnimator ->
|
||||
mediaPlayer.setVolume(
|
||||
animation.animatedValue as Float, animation.animatedValue as Float
|
||||
)
|
||||
}
|
||||
}, 0, PERIOD
|
||||
)
|
||||
}
|
||||
doOnEnd {
|
||||
endAction(it)
|
||||
// Set end values
|
||||
mediaPlayer.setVolume(endValue, endValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
timer.purge()
|
||||
timer.cancel()
|
||||
}
|
||||
|
||||
private fun setVolume() {
|
||||
if (fadeIn) {
|
||||
volume += volumeStep
|
||||
} else {
|
||||
volume -= volumeStep
|
||||
@JvmStatic
|
||||
fun startFadeAnimator(
|
||||
playback: Playback,
|
||||
fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/,
|
||||
callback: Runnable /* Code to run when Animator Ends*/
|
||||
) {
|
||||
val duration = PreferenceUtil.audioFadeDuration.toLong()
|
||||
if (duration == 0L) {
|
||||
callback.run()
|
||||
return
|
||||
}
|
||||
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
|
||||
animator.addUpdateListener { animation: ValueAnimator ->
|
||||
playback.setVolume(
|
||||
animation.animatedValue as Float
|
||||
)
|
||||
}
|
||||
animator.doOnEnd {
|
||||
callback.run()
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PERIOD = 100L
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ package code.name.monkey.retromusic.service
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import code.name.monkey.retromusic.auto.AutoMediaIDHelper
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -31,7 +32,6 @@ 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.*
|
||||
|
||||
|
||||
/**
|
||||
|
@ -56,8 +56,7 @@ class MediaSessionCallback(
|
|||
println(musicId)
|
||||
val itemId = musicId?.toLong() ?: -1
|
||||
val songs: ArrayList<Song> = ArrayList()
|
||||
val category = AutoMediaIDHelper.extractCategory(mediaId)
|
||||
when (category) {
|
||||
when (val category = AutoMediaIDHelper.extractCategory(mediaId)) {
|
||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> {
|
||||
val album: Album = albumRepository.album(itemId)
|
||||
songs.addAll(album.songs)
|
||||
|
@ -111,6 +110,43 @@ class MediaSessionCallback(
|
|||
musicService.play()
|
||||
}
|
||||
|
||||
override fun onPlayFromSearch(query: String?, extras: Bundle?) {
|
||||
val songs = ArrayList<Song>()
|
||||
if (query.isNullOrEmpty()) {
|
||||
// The user provided generic string e.g. 'Play music'
|
||||
// Build appropriate playlist queue
|
||||
songs.addAll(songRepository.songs())
|
||||
} else {
|
||||
// Build a queue based on songs that match "query" or "extras" param
|
||||
val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS)
|
||||
if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) {
|
||||
val artistQuery = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
|
||||
if (artistQuery != null) {
|
||||
artistRepository.artists(artistQuery).forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
}
|
||||
} else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) {
|
||||
val albumQuery = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
|
||||
if (albumQuery != null) {
|
||||
albumRepository.albums(albumQuery).forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (songs.isEmpty()) {
|
||||
// No focus found, search by query for song title
|
||||
query?.also {
|
||||
songs.addAll(songRepository.songs(it))
|
||||
}
|
||||
}
|
||||
|
||||
musicService.openQueue(songs, 0, true)
|
||||
|
||||
musicService.play()
|
||||
}
|
||||
|
||||
override fun onPlay() {
|
||||
super.onPlay()
|
||||
|
|
|
@ -21,6 +21,7 @@ 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 static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
|
@ -97,12 +98,14 @@ 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 code.name.monkey.retromusic.volume.AudioVolumeObserver;
|
||||
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener;
|
||||
|
||||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
|
||||
*/
|
||||
public class MusicService extends MediaBrowserServiceCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks, OnAudioVolumeChangedListener {
|
||||
|
||||
public static final String TAG = MusicService.class.getSimpleName();
|
||||
public static final String RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic";
|
||||
|
@ -238,6 +241,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
private List<Song> originalPlayingQueue = new ArrayList<>();
|
||||
private List<Song> playingQueue = new ArrayList<>();
|
||||
private boolean pausedByTransientLossOfFocus;
|
||||
private AudioVolumeObserver audioVolumeObserver = null;
|
||||
|
||||
private final BroadcastReceiver becomingNoisyReceiver =
|
||||
new BroadcastReceiver() {
|
||||
|
@ -348,7 +352,6 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
private ThrottledSeekHandler throttledSeekHandler;
|
||||
private Handler uiThreadHandler;
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
private AudioFader fader;
|
||||
|
||||
private static Bitmap copy(Bitmap bitmap) {
|
||||
Bitmap.Config config = bitmap.getConfig();
|
||||
|
@ -446,6 +449,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
.registerContentObserver(
|
||||
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
|
||||
|
||||
audioVolumeObserver = new AudioVolumeObserver(this);
|
||||
audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
|
||||
|
||||
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
|
||||
|
||||
restoreState();
|
||||
|
@ -491,6 +497,23 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
wakeLock.acquire(milli);
|
||||
}
|
||||
|
||||
boolean pausedByZeroVolume;
|
||||
|
||||
@Override
|
||||
public void onAudioVolumeChanged(int currentVolume, int maxVolume) {
|
||||
if (PreferenceUtil.INSTANCE.isPauseOnZeroVolume()) {
|
||||
if (isPlaying() && currentVolume < 1) {
|
||||
pause();
|
||||
System.out.println("Paused");
|
||||
pausedByZeroVolume = true;
|
||||
} else if (pausedByZeroVolume && currentVolume >= 1) {
|
||||
System.out.println("Played");
|
||||
play();
|
||||
pausedByZeroVolume = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSong(int position, Song song) {
|
||||
playingQueue.add(position, song);
|
||||
originalPlayingQueue.add(position, song);
|
||||
|
@ -558,10 +581,10 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
}
|
||||
|
||||
public Song getNextSong() {
|
||||
if (!isLastTrack() || getRepeatMode() == REPEAT_MODE_THIS) {
|
||||
return getSongAt(getNextPosition(false));
|
||||
} else {
|
||||
if (isLastTrack() && getRepeatMode() == REPEAT_MODE_NONE) {
|
||||
return null;
|
||||
} else {
|
||||
return getSongAt(getNextPosition(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -926,6 +949,13 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
playerHandler.sendEmptyMessage(TRACK_ENDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackEndedWithCrossfade() {
|
||||
trackEndedByCrossfade = true;
|
||||
acquireWakeLock(30000);
|
||||
playerHandler.sendEmptyMessage(TRACK_ENDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackWentToNext() {
|
||||
playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
|
||||
|
@ -981,16 +1011,11 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
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);
|
||||
}
|
||||
startFadeAnimator(playback, false, () -> {
|
||||
//Code to run when Animator Ends
|
||||
playback.pause();
|
||||
notifyChange(PLAY_STATE_CHANGED);
|
||||
});
|
||||
fader.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1013,10 +1038,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
if (MusicPlayerRemote.INSTANCE.isCasting()) {
|
||||
return;
|
||||
}
|
||||
if (fader != null) {
|
||||
fader.stop();
|
||||
}
|
||||
fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> {
|
||||
startFadeAnimator(playback, true, () -> {
|
||||
// Code when Animator Ends
|
||||
if (!becomingNoisyReceiverRegistered) {
|
||||
registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter);
|
||||
becomingNoisyReceiverRegistered = true;
|
||||
|
@ -1031,9 +1054,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
playerHandler.removeMessages(DUCK);
|
||||
playerHandler.sendEmptyMessage(UNDUCK);
|
||||
});
|
||||
//Start Playback with Animator
|
||||
playback.start();
|
||||
notifyChange(PLAY_STATE_CHANGED);
|
||||
fader.start();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -51,5 +51,7 @@ interface Playback {
|
|||
fun onTrackWentToNext()
|
||||
|
||||
fun onTrackEnded()
|
||||
|
||||
fun onTrackEndedWithCrossfade()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue