commit
c5e5643164
13 changed files with 203 additions and 105 deletions
|
@ -14,7 +14,7 @@ android {
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
applicationId "code.name.monkey.retromusic"
|
applicationId "code.name.monkey.retromusic"
|
||||||
versionCode 10590
|
versionCode 10591
|
||||||
versionName '6.0.0-beta'
|
versionName '6.0.0-beta'
|
||||||
|
|
||||||
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||||
|
@ -125,7 +125,7 @@ dependencies {
|
||||||
def retrofit_version = '2.9.0'
|
def retrofit_version = '2.9.0'
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.7'
|
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.8'
|
||||||
|
|
||||||
def material_dialog_version = "3.3.0"
|
def material_dialog_version = "3.3.0"
|
||||||
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
||||||
|
@ -167,5 +167,4 @@ dependencies {
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.8'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.8'
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.4.0'
|
implementation 'cat.ereza:customactivityoncrash:2.4.0'
|
||||||
implementation 'me.tankery.lib:circularSeekBar:1.4.0'
|
implementation 'me.tankery.lib:circularSeekBar:1.4.0'
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
|
||||||
}
|
}
|
|
@ -77,14 +77,6 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
|
||||||
baseUrl += query
|
baseUrl += query
|
||||||
return baseUrl
|
return baseUrl
|
||||||
}
|
}
|
||||||
private val syairSearchLrcUrl: String
|
|
||||||
get() {
|
|
||||||
var baseUrl = "https://www.syair.info/search?"
|
|
||||||
var query = song.title + "+" + song.artistName
|
|
||||||
query = "q=" + query.replace(" ", "+")
|
|
||||||
baseUrl += query
|
|
||||||
return baseUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
|
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||||
private lateinit var newSyncedLyricsLauncher: ActivityResultLauncher<Intent>
|
private lateinit var newSyncedLyricsLauncher: ActivityResultLauncher<Intent>
|
||||||
|
@ -201,12 +193,7 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
|
||||||
|
|
||||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == R.id.action_search) {
|
if (item.itemId == R.id.action_search) {
|
||||||
openUrl(when (binding.lyricsPager.currentItem) {
|
openUrl(googleSearchLrcUrl)
|
||||||
0 -> syairSearchLrcUrl
|
|
||||||
1 -> googleSearchLrcUrl
|
|
||||||
else -> googleSearchLrcUrl
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ package code.name.monkey.retromusic.service
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
import code.name.monkey.retromusic.service.playback.Playback
|
import code.name.monkey.retromusic.service.playback.Playback
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
@ -59,9 +57,7 @@ class AudioFader {
|
||||||
animator.doOnEnd {
|
animator.doOnEnd {
|
||||||
callback.run()
|
callback.run()
|
||||||
}
|
}
|
||||||
Handler(Looper.getMainLooper()).post {
|
animator.start()
|
||||||
animator.start()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -33,15 +33,19 @@ class CastPlayer(castSession: CastSession) : Playback,
|
||||||
|
|
||||||
override var callbacks: Playback.PlaybackCallbacks? = null
|
override var callbacks: Playback.PlaybackCallbacks? = null
|
||||||
|
|
||||||
override fun setDataSource(song: Song, force: Boolean): Boolean {
|
override fun setDataSource(
|
||||||
return try {
|
song: Song,
|
||||||
|
force: Boolean,
|
||||||
|
completion: (success: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
val mediaLoadOptions =
|
val mediaLoadOptions =
|
||||||
MediaLoadOptions.Builder().setPlayPosition(0).setAutoplay(true).build()
|
MediaLoadOptions.Builder().setPlayPosition(0).setAutoplay(true).build()
|
||||||
remoteMediaClient?.load(song.toMediaInfo()!!, mediaLoadOptions)
|
remoteMediaClient?.load(song.toMediaInfo()!!, mediaLoadOptions)
|
||||||
true
|
completion(true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
false
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,17 +120,25 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
|
||||||
override val isPlaying: Boolean
|
override val isPlaying: Boolean
|
||||||
get() = mIsInitialized && getCurrentPlayer()?.isPlaying == true
|
get() = mIsInitialized && getCurrentPlayer()?.isPlaying == true
|
||||||
|
|
||||||
override fun setDataSource(song: Song, force: Boolean): Boolean {
|
override fun setDataSource(
|
||||||
|
song: Song,
|
||||||
|
force: Boolean,
|
||||||
|
completion: (success: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
if (force) hasDataSource = false
|
if (force) hasDataSource = false
|
||||||
mIsInitialized = false
|
mIsInitialized = false
|
||||||
/* We've already set DataSource if initialized is true in setNextDataSource */
|
/* We've already set DataSource if initialized is true in setNextDataSource */
|
||||||
return if (!hasDataSource) {
|
if (!hasDataSource) {
|
||||||
mIsInitialized = setDataSourceImpl(getCurrentPlayer()!!, song.uri.toString())
|
getCurrentPlayer()?.let {
|
||||||
|
setDataSourceImpl(it, song.uri.toString()) { success ->
|
||||||
|
mIsInitialized = success
|
||||||
|
completion(success)
|
||||||
|
}
|
||||||
|
}
|
||||||
hasDataSource = true
|
hasDataSource = true
|
||||||
mIsInitialized
|
|
||||||
} else {
|
} else {
|
||||||
|
completion(true)
|
||||||
mIsInitialized = true
|
mIsInitialized = true
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,8 +293,8 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
|
||||||
val nextSong = MusicPlayerRemote.nextSong
|
val nextSong = MusicPlayerRemote.nextSong
|
||||||
// Switch to other player (Crossfade) only if next song exists
|
// Switch to other player (Crossfade) only if next song exists
|
||||||
if (nextSong != null) {
|
if (nextSong != null) {
|
||||||
if (setDataSourceImpl(player, nextSong.uri.toString())) {
|
setDataSourceImpl(player, nextSong.uri.toString()) { success ->
|
||||||
switchPlayer()
|
if (success) switchPlayer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,10 +109,13 @@ abstract class LocalPlayback(val context: Context) : Playback, MediaPlayer.OnErr
|
||||||
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
|
* @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
|
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
|
||||||
*/
|
*/
|
||||||
fun setDataSourceImpl(player: MediaPlayer, path: String): Boolean {
|
fun setDataSourceImpl(
|
||||||
|
player: MediaPlayer,
|
||||||
|
path: String,
|
||||||
|
completion: (success: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
player.reset()
|
||||||
try {
|
try {
|
||||||
player.reset()
|
|
||||||
player.setOnPreparedListener(null)
|
|
||||||
if (path.startsWith("content://")) {
|
if (path.startsWith("content://")) {
|
||||||
player.setDataSource(context, path.toUri())
|
player.setDataSource(context, path.toUri())
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,14 +126,17 @@ abstract class LocalPlayback(val context: Context) : Playback, MediaPlayer.OnErr
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
player.prepare()
|
player.setOnPreparedListener {
|
||||||
|
player.setOnPreparedListener(null)
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
player.prepareAsync()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
completion(false)
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
player.setOnCompletionListener(this)
|
player.setOnCompletionListener(this)
|
||||||
player.setOnErrorListener(this)
|
player.setOnErrorListener(this)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unregisterBecomingNoisyReceiver() {
|
private fun unregisterBecomingNoisyReceiver() {
|
||||||
|
|
|
@ -46,13 +46,19 @@ class MultiPlayer(context: Context) : LocalPlayback(context) {
|
||||||
* @param song The song object you want to play
|
* @param song The song object you want to play
|
||||||
* @return True if the `player` has been prepared and is ready to play, false otherwise
|
* @return True if the `player` has been prepared and is ready to play, false otherwise
|
||||||
*/
|
*/
|
||||||
override fun setDataSource(song: Song, force: Boolean): Boolean {
|
override fun setDataSource(
|
||||||
|
song: Song,
|
||||||
|
force: Boolean,
|
||||||
|
completion: (success: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
isInitialized = false
|
isInitialized = false
|
||||||
isInitialized = setDataSourceImpl(mCurrentMediaPlayer, song.uri.toString())
|
setDataSourceImpl(mCurrentMediaPlayer, song.uri.toString()) { success ->
|
||||||
if (isInitialized) {
|
isInitialized = success
|
||||||
setNextDataSource(null)
|
if (isInitialized) {
|
||||||
|
setNextDataSource(null)
|
||||||
|
}
|
||||||
|
completion(isInitialized)
|
||||||
}
|
}
|
||||||
return isInitialized
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,26 +86,28 @@ class MultiPlayer(context: Context) : LocalPlayback(context) {
|
||||||
mNextMediaPlayer = MediaPlayer()
|
mNextMediaPlayer = MediaPlayer()
|
||||||
mNextMediaPlayer?.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
|
mNextMediaPlayer?.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
|
||||||
mNextMediaPlayer?.audioSessionId = audioSessionId
|
mNextMediaPlayer?.audioSessionId = audioSessionId
|
||||||
if (setDataSourceImpl(mNextMediaPlayer!!, path)) {
|
setDataSourceImpl(mNextMediaPlayer!!, path) { success ->
|
||||||
try {
|
if (success) {
|
||||||
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer)
|
try {
|
||||||
} catch (e: IllegalArgumentException) {
|
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer)
|
||||||
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
||||||
|
if (mNextMediaPlayer != null) {
|
||||||
|
mNextMediaPlayer?.release()
|
||||||
|
mNextMediaPlayer = null
|
||||||
|
}
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
||||||
|
if (mNextMediaPlayer != null) {
|
||||||
|
mNextMediaPlayer?.release()
|
||||||
|
mNextMediaPlayer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (mNextMediaPlayer != null) {
|
if (mNextMediaPlayer != null) {
|
||||||
mNextMediaPlayer?.release()
|
mNextMediaPlayer?.release()
|
||||||
mNextMediaPlayer = null
|
mNextMediaPlayer = null
|
||||||
}
|
}
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
|
||||||
if (mNextMediaPlayer != null) {
|
|
||||||
mNextMediaPlayer?.release()
|
|
||||||
mNextMediaPlayer = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mNextMediaPlayer != null) {
|
|
||||||
mNextMediaPlayer?.release()
|
|
||||||
mNextMediaPlayer = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,11 +117,6 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
private var trackEndedByCrossfade = false
|
private var trackEndedByCrossfade = false
|
||||||
private val serviceScope = CoroutineScope(Job() + Main)
|
private val serviceScope = CoroutineScope(Job() + Main)
|
||||||
|
|
||||||
// Every chromecast method needs to run on main thread or you are greeted with IllegalStateException
|
|
||||||
// So it will use Main dispatcher
|
|
||||||
// And by using Default dispatcher for local playback we are reducing the burden from main thread
|
|
||||||
private val playerDispatcher get() = if (playbackManager.isLocalPlayback) Default else Main
|
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var position = -1
|
var position = -1
|
||||||
private val appWidgetBig = AppWidgetBig.instance
|
private val appWidgetBig = AppWidgetBig.instance
|
||||||
|
@ -441,8 +436,11 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPosition(position: Int) {
|
private fun setPosition(position: Int) {
|
||||||
openTrackAndPrepareNextAt(position)
|
openTrackAndPrepareNextAt(position) { success ->
|
||||||
notifyChange(PLAY_STATE_CHANGED)
|
if (success) {
|
||||||
|
notifyChange(PLAY_STATE_CHANGED)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPreviousPosition(force: Boolean): Int {
|
private fun getPreviousPosition(force: Boolean): Int {
|
||||||
|
@ -634,6 +632,8 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
|
|
||||||
if (playbackManager.maybeSwitchToCrossFade(crossFadeDuration)) {
|
if (playbackManager.maybeSwitchToCrossFade(crossFadeDuration)) {
|
||||||
restorePlaybackState(wasPlaying, progress)
|
restorePlaybackState(wasPlaying, progress)
|
||||||
|
} else {
|
||||||
|
playbackManager.setCrossFadeDuration(crossFadeDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ALBUM_ART_ON_LOCK_SCREEN, BLURRED_ALBUM_ART -> updateMediaSessionMetaData(::updateMediaSessionPlaybackState)
|
ALBUM_ART_ON_LOCK_SCREEN, BLURRED_ALBUM_ART -> updateMediaSessionMetaData(::updateMediaSessionPlaybackState)
|
||||||
|
@ -757,15 +757,16 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun openTrackAndPrepareNextAt(position: Int): Boolean {
|
fun openTrackAndPrepareNextAt(position: Int, completion: (success: Boolean) -> Unit) {
|
||||||
this.position = position
|
this.position = position
|
||||||
val prepared = openCurrent()
|
openCurrent { success ->
|
||||||
if (prepared) {
|
completion(success)
|
||||||
prepareNextImpl()
|
notifyChange(META_CHANGED)
|
||||||
|
notHandledMetaChangedForCurrentTrack = false
|
||||||
|
if (success) {
|
||||||
|
prepareNextImpl()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
notifyChange(META_CHANGED)
|
|
||||||
notHandledMetaChangedForCurrentTrack = false
|
|
||||||
return prepared
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause(force: Boolean = false) {
|
fun pause(force: Boolean = false) {
|
||||||
|
@ -794,11 +795,16 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playSongAt(position: Int) {
|
fun playSongAt(position: Int) {
|
||||||
serviceScope.launch(playerDispatcher) {
|
// Every chromecast method needs to run on main thread or you are greeted with IllegalStateException
|
||||||
if (openTrackAndPrepareNextAt(position)) {
|
// So it will use Main dispatcher
|
||||||
play()
|
// And by using Default dispatcher for local playback we are reduce the burden of main thread
|
||||||
} else {
|
serviceScope.launch(if(playbackManager.isLocalPlayback) Default else Main) {
|
||||||
showToast(R.string.unplayable_file)
|
openTrackAndPrepareNextAt(position) { success ->
|
||||||
|
if (success) {
|
||||||
|
play()
|
||||||
|
} else {
|
||||||
|
showToast(resources.getString(R.string.unplayable_file))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -913,14 +919,15 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
originalPlayingQueue = ArrayList(restoredOriginalQueue)
|
originalPlayingQueue = ArrayList(restoredOriginalQueue)
|
||||||
playingQueue = ArrayList(restoredQueue)
|
playingQueue = ArrayList(restoredQueue)
|
||||||
position = restoredPosition
|
position = restoredPosition
|
||||||
withContext(playerDispatcher) {
|
withContext(Main) {
|
||||||
openCurrent()
|
openCurrent {
|
||||||
prepareNext()
|
prepareNext()
|
||||||
if (restoredPositionInTrack > 0) {
|
if (restoredPositionInTrack > 0) {
|
||||||
seek(restoredPositionInTrack)
|
seek(restoredPositionInTrack)
|
||||||
|
}
|
||||||
|
notHandledMetaChangedForCurrentTrack = true
|
||||||
|
sendChangeInternal(META_CHANGED)
|
||||||
}
|
}
|
||||||
notHandledMetaChangedForCurrentTrack = true
|
|
||||||
sendChangeInternal(META_CHANGED)
|
|
||||||
if (receivedHeadsetConnected) {
|
if (receivedHeadsetConnected) {
|
||||||
play()
|
play()
|
||||||
receivedHeadsetConnected = false
|
receivedHeadsetConnected = false
|
||||||
|
@ -1164,18 +1171,15 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun openCurrent(): Boolean {
|
private fun openCurrent(completion: (success: Boolean) -> Unit) {
|
||||||
val force = if (!trackEndedByCrossfade) {
|
val force = if (!trackEndedByCrossfade) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
trackEndedByCrossfade = false
|
trackEndedByCrossfade = false
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
return try {
|
playbackManager.setDataSource(currentSong, force) { success ->
|
||||||
playbackManager.setDataSource(currentSong, force)
|
completion(success)
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,10 +1193,12 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
|
|
||||||
private fun restorePlaybackState(wasPlaying: Boolean, progress: Int) {
|
private fun restorePlaybackState(wasPlaying: Boolean, progress: Int) {
|
||||||
playbackManager.setCallbacks(this)
|
playbackManager.setCallbacks(this)
|
||||||
if (openTrackAndPrepareNextAt(position)) {
|
openTrackAndPrepareNextAt(position) { success ->
|
||||||
seek(progress)
|
if (success) {
|
||||||
if (wasPlaying) {
|
seek(progress)
|
||||||
play()
|
if (wasPlaying) {
|
||||||
|
play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playbackManager.setCrossFadeDuration(crossFadeDuration)
|
playbackManager.setCrossFadeDuration(crossFadeDuration)
|
||||||
|
|
|
@ -16,7 +16,7 @@ class PlaybackManager(val context: Context) {
|
||||||
var playback: Playback? = null
|
var playback: Playback? = null
|
||||||
private var playbackLocation = PlaybackLocation.LOCAL
|
private var playbackLocation = PlaybackLocation.LOCAL
|
||||||
|
|
||||||
val isLocalPlayback get() = playbackLocation == PlaybackLocation.LOCAL
|
val isLocalPlayback get() = playbackLocation== PlaybackLocation.LOCAL
|
||||||
|
|
||||||
val audioSessionId: Int
|
val audioSessionId: Int
|
||||||
get() = if (playback != null) {
|
get() = if (playback != null) {
|
||||||
|
@ -86,8 +86,12 @@ class PlaybackManager(val context: Context) {
|
||||||
|
|
||||||
fun seek(millis: Int): Int = playback!!.seek(millis)
|
fun seek(millis: Int): Int = playback!!.seek(millis)
|
||||||
|
|
||||||
fun setDataSource(song: Song, force: Boolean): Boolean {
|
fun setDataSource(
|
||||||
return playback?.setDataSource(song, force) == true
|
song: Song,
|
||||||
|
force: Boolean,
|
||||||
|
completion: (success: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
playback?.setDataSource(song, force, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setNextDataSource(trackUri: String) {
|
fun setNextDataSource(trackUri: String) {
|
||||||
|
|
|
@ -25,7 +25,9 @@ interface Playback {
|
||||||
|
|
||||||
val audioSessionId: Int
|
val audioSessionId: Int
|
||||||
|
|
||||||
fun setDataSource(song: Song, force: Boolean):Boolean
|
fun setDataSource(
|
||||||
|
song: Song, force: Boolean, completion: (success: Boolean) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
fun setNextDataSource(path: String?)
|
fun setNextDataSource(path: String?)
|
||||||
|
|
||||||
|
|
72
app/src/main/res/layout-land/fragment_adaptive_player.xml
Normal file
72
app/src/main/res/layout-land/fragment_adaptive_player.xml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface">
|
||||||
|
|
||||||
|
<include layout="@layout/shadow_statusbar_toolbar" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/status_bar" />
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/playerToolbar"
|
||||||
|
style="@style/Toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:contentInsetLeft="0dp"
|
||||||
|
app:contentInsetStart="0dp"
|
||||||
|
app:contentInsetStartWithNavigation="0dp"
|
||||||
|
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
|
||||||
|
app:subtitleTextAppearance="@style/TextViewCaption"
|
||||||
|
app:titleMargin="0dp"
|
||||||
|
app:titleMarginStart="0dp"
|
||||||
|
app:titleTextAppearance="@style/TextViewSubtitle1"
|
||||||
|
tools:subtitle="@tools:sample/full_names"
|
||||||
|
tools:title="@tools:sample/full_names" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/playerAlbumCoverFragment"
|
||||||
|
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:layout="@layout/fragment_album_full_card_cover" />
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/cover_lyrics"
|
||||||
|
android:name="code.name.monkey.retromusic.fragments.player.CoverLyricsFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:elevation="20dp" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/playbackControlsFragment"
|
||||||
|
android:name="code.name.monkey.retromusic.fragments.player.adaptive.AdaptivePlaybackControlsFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:layout="@layout/fragment_adaptive_player_playback_controls" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -112,7 +112,9 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?attr/roundSelector"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
|
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
|
||||||
app:layout_constraintStart_toEndOf="@+id/playPauseButton"
|
app:layout_constraintStart_toEndOf="@+id/playPauseButton"
|
||||||
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
|
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
|
||||||
|
@ -124,7 +126,9 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/roundSelector"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
|
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/playPauseButton"
|
app:layout_constraintEnd_toStartOf="@+id/playPauseButton"
|
||||||
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
|
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
|
||||||
|
@ -195,7 +199,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:gravity="center_vertical|right|end"
|
android:gravity="center_vertical|end"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
@ -209,9 +213,11 @@
|
||||||
android:id="@+id/songCurrentProgress"
|
android:id="@+id/songCurrentProgress"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentStart="true"
|
||||||
android:gravity="center_vertical|left|end"
|
android:layout_marginStart="16dp"
|
||||||
android:paddingLeft="8dp"
|
android:gravity="center_vertical|start"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue