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
|
@ -6,7 +6,6 @@ apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 31
|
||||||
buildToolsVersion = '29.0.3'
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
|
@ -33,6 +32,8 @@ android {
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
//debuggable true
|
//debuggable true
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
|
@ -104,7 +105,7 @@ dependencies {
|
||||||
//WebServer by NanoHttpd
|
//WebServer by NanoHttpd
|
||||||
implementation "org.nanohttpd:nanohttpd:2.3.1"
|
implementation "org.nanohttpd:nanohttpd:2.3.1"
|
||||||
|
|
||||||
def nav_version = '2.4.0-alpha09'
|
def nav_version = "2.4.0-alpha09"
|
||||||
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||||
|
@ -158,6 +159,7 @@ dependencies {
|
||||||
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
||||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
||||||
|
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||||
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
<!-- Android Auto -->
|
<!-- Android Auto -->
|
||||||
<!-- <meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.car.application"
|
android:name="com.google.android.gms.car.application"
|
||||||
android:resource="@xml/automotive_app_desc"/>
|
android:resource="@xml/automotive_app_desc"/>
|
||||||
<meta-data
|
<meta-data
|
||||||
|
@ -274,7 +274,7 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.car.notification.SmallIcon"
|
android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||||
android:resource="@drawable/ic_notification"/>
|
android:resource="@drawable/ic_notification"/>
|
||||||
-->
|
|
||||||
<!-- ChromeCast -->
|
<!-- ChromeCast -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
|
|
|
@ -32,6 +32,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
|
|
||||||
|
@ -110,8 +111,6 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
||||||
get() = lastColor
|
get() = lastColor
|
||||||
|
|
||||||
private fun updateBlur() {
|
private fun updateBlur() {
|
||||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
|
||||||
binding.colorBackground.clearColorFilter()
|
binding.colorBackground.clearColorFilter()
|
||||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||||
|
|
|
@ -33,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||||
import code.name.monkey.retromusic.util.RetroUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||||
import org.koin.android.ext.android.bind
|
import org.koin.android.ext.android.bind
|
||||||
|
@ -136,8 +137,6 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBlur() {
|
private fun updateBlur() {
|
||||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
|
||||||
binding.colorBackground.clearColorFilter()
|
binding.colorBackground.clearColorFilter()
|
||||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.os.IBinder
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.repository.SongRepository
|
import code.name.monkey.retromusic.repository.SongRepository
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
|
@ -300,7 +301,7 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setShuffleMode(shuffleMode: Int): Boolean {
|
private fun setShuffleMode(shuffleMode: Int): Boolean {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService!!.shuffleMode = shuffleMode
|
musicService!!.shuffleMode = shuffleMode
|
||||||
return true
|
return true
|
||||||
|
@ -319,7 +320,7 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
}
|
}
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
musicService,
|
musicService,
|
||||||
musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue),
|
musicService!!.resources.getString(R.string.added_title_to_playing_queue),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
return true
|
return true
|
||||||
|
@ -335,8 +336,8 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
openQueue(songs, 0, false)
|
openQueue(songs, 0, false)
|
||||||
}
|
}
|
||||||
val toast =
|
val toast =
|
||||||
if (songs.size == 1) musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
||||||
code.name.monkey.retromusic.R.string.added_x_titles_to_playing_queue,
|
R.string.added_x_titles_to_playing_queue,
|
||||||
songs.size
|
songs.size
|
||||||
)
|
)
|
||||||
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
||||||
|
@ -356,7 +357,7 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
}
|
}
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
musicService,
|
musicService,
|
||||||
musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue),
|
musicService!!.resources.getString(R.string.added_title_to_playing_queue),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
return true
|
return true
|
||||||
|
@ -372,8 +373,8 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
openQueue(songs, 0, false)
|
openQueue(songs, 0, false)
|
||||||
}
|
}
|
||||||
val toast =
|
val toast =
|
||||||
if (songs.size == 1) musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
||||||
code.name.monkey.retromusic.R.string.added_x_titles_to_playing_queue,
|
R.string.added_x_titles_to_playing_queue,
|
||||||
songs.size
|
songs.size
|
||||||
)
|
)
|
||||||
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
||||||
|
|
|
@ -1,50 +1,66 @@
|
||||||
package code.name.monkey.retromusic.service
|
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 code.name.monkey.retromusic.service.playback.Playback
|
||||||
import java.util.*
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
|
||||||
class AudioFader(
|
class AudioFader {
|
||||||
private val player: Playback,
|
companion object {
|
||||||
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() {
|
@JvmStatic
|
||||||
timer.scheduleAtFixedRate(
|
inline fun createFadeAnimator(
|
||||||
object : TimerTask() {
|
fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/,
|
||||||
override fun run() {
|
mediaPlayer: MediaPlayer,
|
||||||
setVolume()
|
crossinline endAction: (animator: Animator) -> Unit /* Code to run when Animator Ends*/
|
||||||
if (volume < 0 || volume > 1) {
|
): Animator? {
|
||||||
player.setVolume(maxVolume)
|
val duration = PreferenceUtil.crossFadeDuration * 1000
|
||||||
stop()
|
if (duration == 0) {
|
||||||
doOnEnd.run()
|
return null
|
||||||
} else {
|
}
|
||||||
player.setVolume(volume)
|
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() {
|
@JvmStatic
|
||||||
timer.purge()
|
fun startFadeAnimator(
|
||||||
timer.cancel()
|
playback: Playback,
|
||||||
}
|
fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/,
|
||||||
|
callback: Runnable /* Code to run when Animator Ends*/
|
||||||
private fun setVolume() {
|
) {
|
||||||
if (fadeIn) {
|
val duration = PreferenceUtil.audioFadeDuration.toLong()
|
||||||
volume += volumeStep
|
if (duration == 0L) {
|
||||||
} else {
|
callback.run()
|
||||||
volume -= volumeStep
|
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.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
import code.name.monkey.retromusic.auto.AutoMediaIDHelper
|
import code.name.monkey.retromusic.auto.AutoMediaIDHelper
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
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 code.name.monkey.retromusic.util.MusicUtil
|
||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
import org.koin.core.inject
|
import org.koin.core.inject
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,8 +56,7 @@ class MediaSessionCallback(
|
||||||
println(musicId)
|
println(musicId)
|
||||||
val itemId = musicId?.toLong() ?: -1
|
val itemId = musicId?.toLong() ?: -1
|
||||||
val songs: ArrayList<Song> = ArrayList()
|
val songs: ArrayList<Song> = ArrayList()
|
||||||
val category = AutoMediaIDHelper.extractCategory(mediaId)
|
when (val category = AutoMediaIDHelper.extractCategory(mediaId)) {
|
||||||
when (category) {
|
|
||||||
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> {
|
AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> {
|
||||||
val album: Album = albumRepository.album(itemId)
|
val album: Album = albumRepository.album(itemId)
|
||||||
songs.addAll(album.songs)
|
songs.addAll(album.songs)
|
||||||
|
@ -111,6 +110,43 @@ class MediaSessionCallback(
|
||||||
musicService.play()
|
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() {
|
override fun onPlay() {
|
||||||
super.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.COLORED_NOTIFICATION;
|
||||||
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
|
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.ConstantsKt.TOGGLE_HEADSET;
|
||||||
|
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.appwidget.AppWidgetManager;
|
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.PackageValidator;
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
import code.name.monkey.retromusic.util.RetroUtil;
|
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
|
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
|
||||||
*/
|
*/
|
||||||
public class MusicService extends MediaBrowserServiceCompat
|
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 TAG = MusicService.class.getSimpleName();
|
||||||
public static final String RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic";
|
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> originalPlayingQueue = new ArrayList<>();
|
||||||
private List<Song> playingQueue = new ArrayList<>();
|
private List<Song> playingQueue = new ArrayList<>();
|
||||||
private boolean pausedByTransientLossOfFocus;
|
private boolean pausedByTransientLossOfFocus;
|
||||||
|
private AudioVolumeObserver audioVolumeObserver = null;
|
||||||
|
|
||||||
private final BroadcastReceiver becomingNoisyReceiver =
|
private final BroadcastReceiver becomingNoisyReceiver =
|
||||||
new BroadcastReceiver() {
|
new BroadcastReceiver() {
|
||||||
|
@ -348,7 +352,6 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
private ThrottledSeekHandler throttledSeekHandler;
|
private ThrottledSeekHandler throttledSeekHandler;
|
||||||
private Handler uiThreadHandler;
|
private Handler uiThreadHandler;
|
||||||
private PowerManager.WakeLock wakeLock;
|
private PowerManager.WakeLock wakeLock;
|
||||||
private AudioFader fader;
|
|
||||||
|
|
||||||
private static Bitmap copy(Bitmap bitmap) {
|
private static Bitmap copy(Bitmap bitmap) {
|
||||||
Bitmap.Config config = bitmap.getConfig();
|
Bitmap.Config config = bitmap.getConfig();
|
||||||
|
@ -446,6 +449,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
.registerContentObserver(
|
.registerContentObserver(
|
||||||
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
|
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
|
||||||
|
|
||||||
|
audioVolumeObserver = new AudioVolumeObserver(this);
|
||||||
|
audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
|
||||||
|
|
||||||
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
|
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
|
||||||
|
|
||||||
restoreState();
|
restoreState();
|
||||||
|
@ -491,6 +497,23 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
wakeLock.acquire(milli);
|
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) {
|
public void addSong(int position, Song song) {
|
||||||
playingQueue.add(position, song);
|
playingQueue.add(position, song);
|
||||||
originalPlayingQueue.add(position, song);
|
originalPlayingQueue.add(position, song);
|
||||||
|
@ -558,10 +581,10 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
}
|
}
|
||||||
|
|
||||||
public Song getNextSong() {
|
public Song getNextSong() {
|
||||||
if (!isLastTrack() || getRepeatMode() == REPEAT_MODE_THIS) {
|
if (isLastTrack() && getRepeatMode() == REPEAT_MODE_NONE) {
|
||||||
return getSongAt(getNextPosition(false));
|
|
||||||
} else {
|
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
return getSongAt(getNextPosition(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -926,6 +949,13 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
playerHandler.sendEmptyMessage(TRACK_ENDED);
|
playerHandler.sendEmptyMessage(TRACK_ENDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackEndedWithCrossfade() {
|
||||||
|
trackEndedByCrossfade = true;
|
||||||
|
acquireWakeLock(30000);
|
||||||
|
playerHandler.sendEmptyMessage(TRACK_ENDED);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrackWentToNext() {
|
public void onTrackWentToNext() {
|
||||||
playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
|
playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
|
||||||
|
@ -981,16 +1011,11 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
public void pause() {
|
public void pause() {
|
||||||
pausedByTransientLossOfFocus = false;
|
pausedByTransientLossOfFocus = false;
|
||||||
if (playback != null && playback.isPlaying()) {
|
if (playback != null && playback.isPlaying()) {
|
||||||
if (fader != null) {
|
startFadeAnimator(playback, false, () -> {
|
||||||
fader.stop();
|
//Code to run when Animator Ends
|
||||||
}
|
playback.pause();
|
||||||
fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> {
|
notifyChange(PLAY_STATE_CHANGED);
|
||||||
if (playback != null && playback.isPlaying()) {
|
|
||||||
playback.pause();
|
|
||||||
notifyChange(PLAY_STATE_CHANGED);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
fader.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,10 +1038,8 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
if (MusicPlayerRemote.INSTANCE.isCasting()) {
|
if (MusicPlayerRemote.INSTANCE.isCasting()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fader != null) {
|
startFadeAnimator(playback, true, () -> {
|
||||||
fader.stop();
|
// Code when Animator Ends
|
||||||
}
|
|
||||||
fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> {
|
|
||||||
if (!becomingNoisyReceiverRegistered) {
|
if (!becomingNoisyReceiverRegistered) {
|
||||||
registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter);
|
registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter);
|
||||||
becomingNoisyReceiverRegistered = true;
|
becomingNoisyReceiverRegistered = true;
|
||||||
|
@ -1031,9 +1054,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
||||||
playerHandler.removeMessages(DUCK);
|
playerHandler.removeMessages(DUCK);
|
||||||
playerHandler.sendEmptyMessage(UNDUCK);
|
playerHandler.sendEmptyMessage(UNDUCK);
|
||||||
});
|
});
|
||||||
|
//Start Playback with Animator
|
||||||
playback.start();
|
playback.start();
|
||||||
notifyChange(PLAY_STATE_CHANGED);
|
notifyChange(PLAY_STATE_CHANGED);
|
||||||
fader.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -51,5 +51,7 @@ interface Playback {
|
||||||
fun onTrackWentToNext()
|
fun onTrackWentToNext()
|
||||||
|
|
||||||
fun onTrackEnded()
|
fun onTrackEnded()
|
||||||
|
|
||||||
|
fun onTrackEndedWithCrossfade()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,22 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
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.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.adapter.Storage;
|
||||||
|
import code.name.monkey.retromusic.model.Song;
|
||||||
|
import code.name.monkey.retromusic.repository.RealSongRepository;
|
||||||
|
import code.name.monkey.retromusic.repository.SortedCursor;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.model.Song;
|
import code.name.monkey.retromusic.model.Song;
|
||||||
import code.name.monkey.retromusic.repository.RealSongRepository;
|
import code.name.monkey.retromusic.repository.RealSongRepository;
|
||||||
|
@ -258,4 +267,76 @@ public final class FileUtil {
|
||||||
return file.getAbsoluteFile();
|
return file.getAbsoluteFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/DrKLO/Telegram/blob/ab221dafadbc17459d78d9ea3e643ae18e934b16/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java#L939
|
||||||
|
public static ArrayList<Storage> listRoots() {
|
||||||
|
ArrayList<Storage> storageItems = new ArrayList<>();
|
||||||
|
HashSet<String> paths = new HashSet<>();
|
||||||
|
String defaultPath = Environment.getExternalStorageDirectory().getPath();
|
||||||
|
String defaultPathState = Environment.getExternalStorageState();
|
||||||
|
if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
|
||||||
|
Storage ext = new Storage();
|
||||||
|
if (Environment.isExternalStorageRemovable()) {
|
||||||
|
ext.title = "SD Card";
|
||||||
|
} else {
|
||||||
|
ext.title = "Internal Storage";
|
||||||
|
}
|
||||||
|
ext.file = Environment.getExternalStorageDirectory();
|
||||||
|
storageItems.add(ext);
|
||||||
|
paths.add(defaultPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedReader bufferedReader = null;
|
||||||
|
try {
|
||||||
|
bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
if (line.contains("vfat") || line.contains("/mnt")) {
|
||||||
|
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||||
|
tokens.nextToken();
|
||||||
|
String path = tokens.nextToken();
|
||||||
|
if (paths.contains(path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.contains("/dev/block/vold")) {
|
||||||
|
if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) {
|
||||||
|
if (!new File(path).isDirectory()) {
|
||||||
|
int index = path.lastIndexOf('/');
|
||||||
|
if (index != -1) {
|
||||||
|
String newPath = "/storage/" + path.substring(index + 1);
|
||||||
|
if (new File(newPath).isDirectory()) {
|
||||||
|
path = newPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths.add(path);
|
||||||
|
try {
|
||||||
|
Storage item = new Storage();
|
||||||
|
if (path.toLowerCase().contains("sd")) {
|
||||||
|
item.title = "SD Card";
|
||||||
|
} else {
|
||||||
|
item.title = "External Storage";
|
||||||
|
}
|
||||||
|
item.file = new File(path);
|
||||||
|
storageItems.add(item);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (bufferedReader != null) {
|
||||||
|
try {
|
||||||
|
bufferedReader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storageItems;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,8 @@ object PreferenceUtil {
|
||||||
BLURRED_ALBUM_ART, false
|
BLURRED_ALBUM_ART, false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val blurAmount get() = sharedPreferences.getInt(NEW_BLUR_AMOUNT, 25)
|
||||||
|
|
||||||
val isCarouselEffect
|
val isCarouselEffect
|
||||||
get() = sharedPreferences.getBoolean(
|
get() = sharedPreferences.getBoolean(
|
||||||
CAROUSEL_EFFECT, false
|
CAROUSEL_EFFECT, false
|
||||||
|
@ -608,8 +610,7 @@ object PreferenceUtil {
|
||||||
val isWhiteList: Boolean
|
val isWhiteList: Boolean
|
||||||
get() = sharedPreferences.getBoolean(WHITELIST_MUSIC, false)
|
get() = sharedPreferences.getBoolean(WHITELIST_MUSIC, false)
|
||||||
|
|
||||||
var crossFadeDuration
|
val crossFadeDuration
|
||||||
get() = sharedPreferences
|
get() = sharedPreferences
|
||||||
.getInt(CROSS_FADE_DURATION, 0)
|
.getInt(CROSS_FADE_DURATION, 0)
|
||||||
set(value) = sharedPreferences.edit { putInt(CROSS_FADE_DURATION, value) }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
android:defaultValue="0"
|
android:defaultValue="0"
|
||||||
android:key="cross_fade_duration"
|
android:key="cross_fade_duration"
|
||||||
android:layout="@layout/list_item_view_seekbar"
|
android:layout="@layout/list_item_view_seekbar"
|
||||||
|
app:ateKey_pref_unit="s"
|
||||||
android:max="12"
|
android:max="12"
|
||||||
android:summary="@string/pref_summary_cross_fade"
|
android:summary="@string/pref_summary_cross_fade"
|
||||||
android:title="@string/pref_title_cross_fade"
|
android:title="@string/pref_title_cross_fade"
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
android:summary="@string/pref_blur_amount_summary"
|
android:summary="@string/pref_blur_amount_summary"
|
||||||
android:title="@string/pref_blur_amount_title"
|
android:title="@string/pref_blur_amount_title"
|
||||||
app:icon="@drawable/ic_blur_on"
|
app:icon="@drawable/ic_blur_on"
|
||||||
|
app:ateKey_pref_unit="px"
|
||||||
app:showSeekBarValue="true" />
|
app:showSeekBarValue="true" />
|
||||||
</code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceCategory>
|
</code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceCategory>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package code.name.monkey.appthemehelper.common.prefs.supportv7
|
package code.name.monkey.appthemehelper.common.prefs.supportv7
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.text.Editable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||||
import androidx.core.graphics.BlendModeCompat
|
import androidx.core.graphics.BlendModeCompat
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.preference.PreferenceViewHolder
|
import androidx.preference.PreferenceViewHolder
|
||||||
import androidx.preference.SeekBarPreference
|
import androidx.preference.SeekBarPreference
|
||||||
import code.name.monkey.appthemehelper.R
|
import code.name.monkey.appthemehelper.R
|
||||||
|
@ -19,7 +22,16 @@ class ATESeekBarPreference @JvmOverloads constructor(
|
||||||
defStyleRes: Int = -1
|
defStyleRes: Int = -1
|
||||||
) : SeekBarPreference(context, attrs, defStyleAttr, defStyleRes) {
|
) : SeekBarPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
|
||||||
|
var unit: String = ""
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
val attributes =
|
||||||
|
context.obtainStyledAttributes(attrs, R.styleable.ATESeekBarPreference, 0, 0)
|
||||||
|
|
||||||
|
attributes.getString(R.styleable.ATESeekBarPreference_ateKey_pref_unit)?.let {
|
||||||
|
unit = it
|
||||||
|
}
|
||||||
|
attributes.recycle()
|
||||||
icon?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
icon?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||||
ATHUtil.resolveColor(
|
ATHUtil.resolveColor(
|
||||||
context,
|
context,
|
||||||
|
@ -32,5 +44,17 @@ class ATESeekBarPreference @JvmOverloads constructor(
|
||||||
super.onBindViewHolder(view)
|
super.onBindViewHolder(view)
|
||||||
val seekBar = view.findViewById(R.id.seekbar) as SeekBar
|
val seekBar = view.findViewById(R.id.seekbar) as SeekBar
|
||||||
TintHelper.setTintAuto(seekBar, ThemeStore.accentColor(context), false)
|
TintHelper.setTintAuto(seekBar, ThemeStore.accentColor(context), false)
|
||||||
|
(view.findViewById(R.id.seekbar_value) as TextView).apply {
|
||||||
|
appendUnit(editableText)
|
||||||
|
doAfterTextChanged {
|
||||||
|
appendUnit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TextView.appendUnit(editable: Editable?) {
|
||||||
|
if (!editable.toString().endsWith(unit)) {
|
||||||
|
append(unit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
appthemehelper/src/main/res/values/attrs.xml
Executable file → Normal file
4
appthemehelper/src/main/res/values/attrs.xml
Executable file → Normal file
|
@ -16,6 +16,10 @@
|
||||||
<attr name="ateKey_pref_color" format="string" />
|
<attr name="ateKey_pref_color" format="string" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="ATESeekBarPreference">
|
||||||
|
<attr name="ateKey_pref_unit" format="string" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="ATEDialogPreference">
|
<declare-styleable name="ATEDialogPreference">
|
||||||
<attr name="ateKey_pref_dialog" format="string" />
|
<attr name="ateKey_pref_dialog" format="string" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
Loading…
Reference in a new issue