4.9.2 commit
This commit is contained in:
parent
c2fb0f4a31
commit
ae82900ae0
|
@ -158,8 +158,8 @@ android {
|
|||
// Version code schema (not used):
|
||||
// "1.2.3-beta4" -> 1020304
|
||||
// "1.2.3" -> 1020395
|
||||
versionCode 3020132
|
||||
versionName "4.9.1"
|
||||
versionCode 3020133
|
||||
versionName "4.9.2"
|
||||
|
||||
def commit = ""
|
||||
try {
|
||||
|
|
|
@ -23,12 +23,10 @@ object PlayableUtils {
|
|||
|
||||
if (playable is FeedMedia) {
|
||||
val item = playable.item
|
||||
if (item != null && item.isNew) {
|
||||
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.id)
|
||||
}
|
||||
if (item != null && item.isNew) DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.id)
|
||||
|
||||
if (playable.startPosition >= 0 && playable.getPosition() > playable.startPosition) {
|
||||
playable.playedDuration = (playable.playedDurationWhenStarted
|
||||
+ playable.getPosition() - playable.startPosition)
|
||||
playable.playedDuration = (playable.playedDurationWhenStarted + playable.getPosition() - playable.startPosition)
|
||||
}
|
||||
DBWriter.setFeedMediaPlaybackInformation(playable)
|
||||
}
|
||||
|
|
|
@ -52,18 +52,13 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
EventBus.getDefault().register(this)
|
||||
eventsRegistered = true
|
||||
}
|
||||
if (PlaybackService.isRunning) {
|
||||
initServiceRunning()
|
||||
} else {
|
||||
updatePlayButtonShowsPlay(true)
|
||||
}
|
||||
if (PlaybackService.isRunning) initServiceRunning()
|
||||
else updatePlayButtonShowsPlay(true)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: PlaybackServiceEvent) {
|
||||
if (event.action == PlaybackServiceEvent.Action.SERVICE_STARTED) {
|
||||
init()
|
||||
}
|
||||
if (event.action == PlaybackServiceEvent.Action.SERVICE_STARTED) init()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -72,20 +67,16 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
initialized = true
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
activity.registerReceiver(statusUpdate, IntentFilter(
|
||||
PlaybackService.ACTION_PLAYER_STATUS_CHANGED), Context.RECEIVER_NOT_EXPORTED)
|
||||
activity.registerReceiver(notificationReceiver, IntentFilter(
|
||||
PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION), Context.RECEIVER_NOT_EXPORTED)
|
||||
activity.registerReceiver(statusUpdate, IntentFilter(PlaybackService.ACTION_PLAYER_STATUS_CHANGED), Context.RECEIVER_NOT_EXPORTED)
|
||||
activity.registerReceiver(notificationReceiver, IntentFilter(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION), Context.RECEIVER_NOT_EXPORTED)
|
||||
} else {
|
||||
activity.registerReceiver(statusUpdate, IntentFilter(PlaybackService.ACTION_PLAYER_STATUS_CHANGED))
|
||||
activity.registerReceiver(notificationReceiver, IntentFilter(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION))
|
||||
}
|
||||
|
||||
if (!released) {
|
||||
bindToService()
|
||||
} else {
|
||||
throw IllegalStateException("Can't call init() after release() has been called")
|
||||
}
|
||||
if (!released) bindToService()
|
||||
else throw IllegalStateException("Can't call init() after release() has been called")
|
||||
|
||||
checkMediaInfoLoaded()
|
||||
}
|
||||
|
||||
|
@ -157,10 +148,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
if (!released) {
|
||||
queryService()
|
||||
Log.d(TAG, "Connection to Service established")
|
||||
} else {
|
||||
Log.i(TAG, "Connection to playback service has been established, " +
|
||||
"but controller has already been released")
|
||||
}
|
||||
} else Log.i(TAG, "Connection to playback service has been established, but controller has already been released")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,9 +212,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
checkMediaInfoLoaded()
|
||||
when (status) {
|
||||
PlayerStatus.PLAYING -> updatePlayButtonShowsPlay(false)
|
||||
PlayerStatus.PREPARING -> if (playbackService != null) {
|
||||
updatePlayButtonShowsPlay(!playbackService!!.isStartWhenPrepared)
|
||||
}
|
||||
PlayerStatus.PREPARING -> if (playbackService != null) updatePlayButtonShowsPlay(!playbackService!!.isStartWhenPrepared)
|
||||
PlayerStatus.FALLBACK, PlayerStatus.PAUSED, PlayerStatus.PREPARED, PlayerStatus.STOPPED, PlayerStatus.INITIALIZED ->
|
||||
updatePlayButtonShowsPlay(true)
|
||||
else -> {}
|
||||
|
@ -262,8 +248,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
mediaInfoLoaded = false
|
||||
handleStatus()
|
||||
} else {
|
||||
Log.e(TAG,
|
||||
"queryService() was called without an existing connection to playbackservice")
|
||||
Log.e(TAG, "queryService() was called without an existing connection to playbackservice")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,9 +347,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
}
|
||||
|
||||
fun speedForward(speed: Float) {
|
||||
if (playbackService != null) {
|
||||
playbackService!!.speedForward(speed)
|
||||
}
|
||||
playbackService?.speedForward(speed)
|
||||
}
|
||||
|
||||
fun fallbackSpeed(speed: Float) {
|
||||
|
@ -392,9 +375,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
|
||||
val audioTracks: List<String>
|
||||
get() {
|
||||
if (playbackService?.audioTracks.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
if (playbackService?.audioTracks.isNullOrEmpty()) return emptyList()
|
||||
return playbackService!!.audioTracks.filterNotNull().map { it }
|
||||
}
|
||||
|
||||
|
@ -409,15 +390,9 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
|
||||
val isPlayingVideoLocally: Boolean
|
||||
get() = when {
|
||||
PlaybackService.isCasting -> {
|
||||
false
|
||||
}
|
||||
playbackService != null -> {
|
||||
PlaybackService.currentMediaType == MediaType.VIDEO
|
||||
}
|
||||
else -> {
|
||||
getMedia()?.getMediaType() == MediaType.VIDEO
|
||||
}
|
||||
PlaybackService.isCasting -> false
|
||||
playbackService != null -> PlaybackService.currentMediaType == MediaType.VIDEO
|
||||
else -> getMedia()?.getMediaType() == MediaType.VIDEO
|
||||
}
|
||||
|
||||
val videoSize: Pair<Int, Int>?
|
||||
|
|
|
@ -333,12 +333,8 @@ abstract class PlaybackServiceMediaPlayer protected constructor(protected val co
|
|||
|
||||
if (newMedia != null && newStatus != PlayerStatus.INDETERMINATE) {
|
||||
when {
|
||||
oldPlayerStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING -> {
|
||||
callback.onPlaybackPause(newMedia, position)
|
||||
}
|
||||
oldPlayerStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING -> {
|
||||
callback.onPlaybackStart(newMedia, position)
|
||||
}
|
||||
oldPlayerStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING -> callback.onPlaybackPause(newMedia, position)
|
||||
oldPlayerStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING -> callback.onPlaybackStart(newMedia, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,17 +37,10 @@ object RewindAfterPauseUtils {
|
|||
var rewindTime: Long = 0
|
||||
|
||||
when {
|
||||
elapsedTime > ELAPSED_TIME_FOR_LONG_REWIND -> {
|
||||
rewindTime = LONG_REWIND
|
||||
}
|
||||
elapsedTime > ELAPSED_TIME_FOR_MEDIUM_REWIND -> {
|
||||
rewindTime = MEDIUM_REWIND
|
||||
}
|
||||
elapsedTime > ELAPSED_TIME_FOR_SHORT_REWIND -> {
|
||||
rewindTime = SHORT_REWIND
|
||||
}
|
||||
elapsedTime > ELAPSED_TIME_FOR_LONG_REWIND -> rewindTime = LONG_REWIND
|
||||
elapsedTime > ELAPSED_TIME_FOR_MEDIUM_REWIND -> rewindTime = MEDIUM_REWIND
|
||||
elapsedTime > ELAPSED_TIME_FOR_SHORT_REWIND -> rewindTime = SHORT_REWIND
|
||||
}
|
||||
|
||||
val newPosition = currentPosition - rewindTime.toInt()
|
||||
|
||||
return max(newPosition.toDouble(), 0.0).toInt()
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package ac.mdiq.podcini.playback.service
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.CommandButton
|
||||
import androidx.media3.session.DefaultMediaNotificationProvider
|
||||
import androidx.media3.session.MediaNotification
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.*
|
||||
import com.google.common.collect.ImmutableList
|
||||
|
||||
@UnstableApi
|
||||
|
@ -15,23 +14,22 @@ class CustomMediaNotificationProvider(context: Context) : DefaultMediaNotificati
|
|||
override fun addNotificationActions(mediaSession: MediaSession, mediaButtons: ImmutableList<CommandButton>, builder: NotificationCompat.Builder, actionFactory: MediaNotification.ActionFactory): IntArray {
|
||||
|
||||
/* Retrieving notification default play/pause button from mediaButtons list. */
|
||||
val defaultPlayPauseCommandButton = mediaButtons.getOrNull(0)
|
||||
val notificationMediaButtons = if (defaultPlayPauseCommandButton != null) {
|
||||
val defaultPlayPauseButton = mediaButtons.getOrNull(1)
|
||||
val defaultRestartButton = mediaButtons.getOrNull(0)
|
||||
val notificationMediaButtons = if (defaultPlayPauseButton != null) {
|
||||
/* Overriding received mediaButtons list to ensure required buttons order: [rewind15, play/pause, forward15]. */
|
||||
ImmutableList.builder<CommandButton>().apply {
|
||||
add(NotificationPlayerCustomCommandButton.REWIND.commandButton)
|
||||
add(defaultPlayPauseCommandButton)
|
||||
add(NotificationPlayerCustomCommandButton.FORWARD.commandButton)
|
||||
if (defaultRestartButton != null) add(defaultRestartButton)
|
||||
add(NotificationCustomButton.REWIND.commandButton)
|
||||
add(defaultPlayPauseButton)
|
||||
add(NotificationCustomButton.FORWARD.commandButton)
|
||||
add(NotificationCustomButton.SKIP.commandButton)
|
||||
}.build()
|
||||
} else {
|
||||
/* Fallback option to handle nullability, in case retrieving default play/pause button fails for some reason (should never happen). */
|
||||
mediaButtons
|
||||
}
|
||||
return super.addNotificationActions(
|
||||
mediaSession,
|
||||
notificationMediaButtons,
|
||||
builder,
|
||||
actionFactory
|
||||
)
|
||||
return super.addNotificationActions(mediaSession, notificationMediaButtons, builder, actionFactory)
|
||||
}
|
||||
|
||||
}
|
|
@ -69,15 +69,9 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
exoPlayer?.addListener(object : Player.Listener {
|
||||
override fun onPlaybackStateChanged(playbackState: @Player.State Int) {
|
||||
when {
|
||||
audioCompletionListener != null && playbackState == Player.STATE_ENDED -> {
|
||||
audioCompletionListener?.run()
|
||||
}
|
||||
playbackState == Player.STATE_BUFFERING -> {
|
||||
bufferingUpdateListener?.accept(BUFFERING_STARTED)
|
||||
}
|
||||
else -> {
|
||||
bufferingUpdateListener?.accept(BUFFERING_ENDED)
|
||||
}
|
||||
audioCompletionListener != null && playbackState == Player.STATE_ENDED -> audioCompletionListener?.run()
|
||||
playbackState == Player.STATE_BUFFERING -> bufferingUpdateListener?.accept(BUFFERING_STARTED)
|
||||
else -> bufferingUpdateListener?.accept(BUFFERING_ENDED)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,21 +81,15 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
} else {
|
||||
var cause = error.cause
|
||||
if (cause is HttpDataSourceException) {
|
||||
if (cause.cause != null) {
|
||||
cause = cause.cause
|
||||
}
|
||||
}
|
||||
if (cause != null && "Source error" == cause.message) {
|
||||
cause = cause.cause
|
||||
if (cause.cause != null) cause = cause.cause
|
||||
}
|
||||
if (cause != null && "Source error" == cause.message) cause = cause.cause
|
||||
audioErrorListener?.accept(if (cause != null) cause.message else error.message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPositionDiscontinuity(oldPosition: PositionInfo, newPosition: PositionInfo, reason: @DiscontinuityReason Int) {
|
||||
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
|
||||
audioSeekCompleteListener?.run()
|
||||
}
|
||||
if (reason == Player.DISCONTINUITY_REASON_SEEK) audioSeekCompleteListener?.run()
|
||||
}
|
||||
|
||||
override fun onAudioSessionIdChanged(audioSessionId: Int) {
|
||||
|
@ -218,6 +206,8 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
}
|
||||
|
||||
fun start() {
|
||||
if (exoPlayer?.playbackState == Player.STATE_IDLE) prepare()
|
||||
|
||||
exoPlayer?.play()
|
||||
// Can't set params when paused - so always set it on start in case they changed
|
||||
exoPlayer!!.playbackParameters = playbackParameters
|
||||
|
@ -271,9 +261,7 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
val availableFormats = formats
|
||||
for (i in 0 until trackSelections.length) {
|
||||
val track = trackSelections[i] as ExoTrackSelection? ?: continue
|
||||
if (availableFormats.contains(track.selectedFormat)) {
|
||||
return availableFormats.indexOf(track.selectedFormat)
|
||||
}
|
||||
if (availableFormats.contains(track.selectedFormat)) return availableFormats.indexOf(track.selectedFormat)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
@ -309,9 +297,7 @@ class ExoPlayerWrapper internal constructor(private val context: Context) {
|
|||
val oldEnhancer = this.loudnessEnhancer
|
||||
if (oldEnhancer != null) {
|
||||
newEnhancer.setEnabled(oldEnhancer.enabled)
|
||||
if (oldEnhancer.enabled) {
|
||||
newEnhancer.setTargetGain(oldEnhancer.targetGain.toInt())
|
||||
}
|
||||
if (oldEnhancer.enabled) newEnhancer.setTargetGain(oldEnhancer.targetGain.toInt())
|
||||
oldEnhancer.release()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,19 @@ import android.os.Bundle
|
|||
import androidx.media3.session.CommandButton
|
||||
import androidx.media3.session.SessionCommand
|
||||
|
||||
private const val CUSTOM_COMMAND_REWIND_ACTION_ID = "REWIND_15"
|
||||
private const val CUSTOM_COMMAND_FORWARD_ACTION_ID = "FAST_FWD_15"
|
||||
private const val CUSTOM_COMMAND_REWIND_ACTION_ID = "1_REWIND"
|
||||
private const val CUSTOM_COMMAND_FORWARD_ACTION_ID = "2_FAST_FWD"
|
||||
private const val CUSTOM_COMMAND_SKIP_ACTION_ID = "3_SKIP"
|
||||
|
||||
enum class NotificationPlayerCustomCommandButton(val customAction: String, val commandButton: CommandButton) {
|
||||
enum class NotificationCustomButton(val customAction: String, val commandButton: CommandButton) {
|
||||
SKIP(
|
||||
customAction = CUSTOM_COMMAND_SKIP_ACTION_ID,
|
||||
commandButton = CommandButton.Builder()
|
||||
.setDisplayName("Skip")
|
||||
.setSessionCommand(SessionCommand(CUSTOM_COMMAND_SKIP_ACTION_ID, Bundle()))
|
||||
.setIconResId(R.drawable.ic_notification_skip)
|
||||
.build(),
|
||||
),
|
||||
REWIND(
|
||||
customAction = CUSTOM_COMMAND_REWIND_ACTION_ID,
|
||||
commandButton = CommandButton.Builder()
|
||||
|
@ -24,5 +33,5 @@ enum class NotificationPlayerCustomCommandButton(val customAction: String, val c
|
|||
.setSessionCommand(SessionCommand(CUSTOM_COMMAND_FORWARD_ACTION_ID, Bundle()))
|
||||
.setIconResId(R.drawable.ic_notification_fast_forward)
|
||||
.build(),
|
||||
);
|
||||
),
|
||||
}
|
|
@ -69,17 +69,15 @@ import ac.mdiq.podcini.util.event.settings.SpeedPresetChangedEvent
|
|||
import ac.mdiq.podcini.util.event.settings.VolumeAdaptionChangedEvent
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.bluetooth.BluetoothA2dp
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.media.AudioManager
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.*
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.IBinder
|
||||
import android.os.Vibrator
|
||||
import android.service.quicksettings.TileService
|
||||
import android.support.v4.media.MediaMetadataCompat
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
|
@ -93,10 +91,13 @@ import android.webkit.URLUtil
|
|||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.Player.*
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.MediaSessionService
|
||||
import androidx.media3.session.*
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.SingleEmitter
|
||||
|
@ -119,9 +120,12 @@ class PlaybackService : MediaSessionService() {
|
|||
private var mediaPlayer: PlaybackServiceMediaPlayer? = null
|
||||
private var positionEventTimer: Disposable? = null
|
||||
|
||||
private lateinit var customMediaNotificationProvider: CustomMediaNotificationProvider
|
||||
private val notificationCustomButtons = NotificationCustomButton.entries.map { command -> command.commandButton }
|
||||
|
||||
private lateinit var taskManager: PlaybackServiceTaskManager
|
||||
private lateinit var stateManager: PlaybackServiceStateManager
|
||||
private lateinit var notificationBuilder: PlaybackServiceNotificationBuilder
|
||||
// private lateinit var stateManager: PlaybackServiceStateManager
|
||||
// private lateinit var notificationBuilder: PlaybackServiceNotificationBuilder
|
||||
private lateinit var castStateListener: CastStateListener
|
||||
|
||||
private var autoSkippedFeedMediaId: String? = null
|
||||
|
@ -156,8 +160,10 @@ class PlaybackService : MediaSessionService() {
|
|||
Log.d(TAG, "Service created.")
|
||||
isRunning = true
|
||||
|
||||
stateManager = PlaybackServiceStateManager(this)
|
||||
notificationBuilder = PlaybackServiceNotificationBuilder(this)
|
||||
// this.startForeground()
|
||||
|
||||
// stateManager = PlaybackServiceStateManager(this)
|
||||
// notificationBuilder = PlaybackServiceNotificationBuilder(this)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
|
||||
registerReceiver(autoStateUpdated, IntentFilter("com.google.android.gms.car.media.STATUS"), RECEIVER_NOT_EXPORTED)
|
||||
|
@ -185,9 +191,15 @@ class PlaybackService : MediaSessionService() {
|
|||
fun recreateMediaSessionIfNeeded() {
|
||||
if (mediaSession != null) return
|
||||
|
||||
Log.d(TAG, "recreateMediaSessionIfNeeded")
|
||||
customMediaNotificationProvider = CustomMediaNotificationProvider(applicationContext)
|
||||
setMediaNotificationProvider(customMediaNotificationProvider)
|
||||
|
||||
if (ExoPlayerWrapper.exoPlayer == null) ExoPlayerWrapper.createStaticPlayer(applicationContext)
|
||||
mediaSession = MediaSession.Builder(applicationContext, ExoPlayerWrapper.exoPlayer!!)
|
||||
.setCallback(sessionCallback)
|
||||
.setCallback(MyCallback())
|
||||
// .setCustomLayout(customMediaNotificationProvider.notificationMediaButtons)
|
||||
.setCustomLayout(notificationCustomButtons)
|
||||
.build()
|
||||
|
||||
recreateMediaPlayer()
|
||||
|
@ -203,39 +215,47 @@ class PlaybackService : MediaSessionService() {
|
|||
mediaPlayer!!.shutdown()
|
||||
}
|
||||
mediaPlayer = CastPsmp.getInstanceIfConnected(this, mediaPlayerCallback)
|
||||
if (mediaPlayer == null) {
|
||||
mediaPlayer = LocalPSMP(applicationContext, mediaPlayerCallback) // Cast not supported or not connected
|
||||
}
|
||||
if (media != null) {
|
||||
mediaPlayer!!.playMediaObject(media, !media.localFileAvailable(), wasPlaying, true)
|
||||
}
|
||||
if (mediaPlayer == null) mediaPlayer = LocalPSMP(applicationContext, mediaPlayerCallback) // Cast not supported or not connected
|
||||
if (media != null) mediaPlayer!!.playMediaObject(media, !media.localFileAvailable(), wasPlaying, true)
|
||||
isCasting = mediaPlayer!!.isCasting()
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
Log.d(TAG, "onTaskRemoved")
|
||||
val player = mediaSession?.player
|
||||
if (player != null) {
|
||||
if (!player.playWhenReady || player.mediaItemCount == 0 || player.playbackState == Player.STATE_ENDED) {
|
||||
// Stop the service if not playing, continue playing in the background
|
||||
// otherwise.
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "Service is about to be destroyed")
|
||||
|
||||
if (notificationBuilder.playerStatus == PlayerStatus.PLAYING || notificationBuilder.playerStatus == PlayerStatus.FALLBACK) {
|
||||
notificationBuilder.playerStatus = PlayerStatus.STOPPED
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
// here to request the missing permissions, and then overriding
|
||||
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
Log.e(TAG, "onDestroy: require POST_NOTIFICATIONS permission")
|
||||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
}
|
||||
stateManager.stopForeground(!isPersistNotify)
|
||||
// if (notificationBuilder.playerStatus == PlayerStatus.PLAYING || notificationBuilder.playerStatus == PlayerStatus.FALLBACK) {
|
||||
// notificationBuilder.playerStatus = PlayerStatus.STOPPED
|
||||
// val notificationManager = NotificationManagerCompat.from(this)
|
||||
// if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
// Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// // TODO: Consider calling
|
||||
// // ActivityCompat#requestPermissions
|
||||
//// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
// // here to request the missing permissions, and then overriding
|
||||
// // public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||
// // int[] grantResults)
|
||||
// // to handle the case where the user grants the permission. See the documentation
|
||||
// // for ActivityCompat#requestPermissions for more details.
|
||||
// Log.e(TAG, "onDestroy: require POST_NOTIFICATIONS permission")
|
||||
// Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
// return
|
||||
// }
|
||||
// notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
// }
|
||||
// stateManager.stopForeground(!isPersistNotify)
|
||||
isRunning = false
|
||||
currentMediaType = MediaType.UNKNOWN
|
||||
castStateListener.destroy()
|
||||
|
@ -259,6 +279,81 @@ class PlaybackService : MediaSessionService() {
|
|||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
private inner class MyCallback : MediaSession.Callback {
|
||||
override fun onConnect(session: MediaSession, controller: MediaSession.ControllerInfo): MediaSession.ConnectionResult {
|
||||
Log.d(TAG, "in onConnect")
|
||||
val sessionCommands = MediaSession.ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
|
||||
// .add(NotificationCustomButton.REWIND)
|
||||
// .add(NotificationCustomButton.FORWARD)
|
||||
if (session.isMediaNotificationController(controller)) {
|
||||
val playerCommands = MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
|
||||
// .remove(COMMAND_SEEK_TO_PREVIOUS)
|
||||
// .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
|
||||
// .remove(COMMAND_SEEK_TO_NEXT)
|
||||
// .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
|
||||
// .removeAll()
|
||||
|
||||
//
|
||||
// // Custom layout and available commands to configure the legacy/framework session.
|
||||
// return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
|
||||
//// .setCustomLayout(
|
||||
//// ImmutableList.of(
|
||||
//// createSeekBackwardButton(NotificationCustomButton.REWIND),
|
||||
//// createSeekForwardButton(customCommandSeekForward))
|
||||
//// )
|
||||
// .setAvailablePlayerCommands(playerCommands.build())
|
||||
// .setAvailableSessionCommands(sessionCommands.build())
|
||||
// .build()
|
||||
|
||||
val connectionResult = super.onConnect(session, controller)
|
||||
val defaultPlayerCommands = connectionResult.availablePlayerCommands
|
||||
Log.d(TAG, defaultPlayerCommands.toString())
|
||||
// for (command in defaultPlayerCommands.toString()) {
|
||||
// Log.d(TAG, command.toString())
|
||||
// }
|
||||
|
||||
// val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
|
||||
|
||||
/* Registering custom player command buttons for player notification. */
|
||||
notificationCustomButtons.forEach { commandButton ->
|
||||
Log.d(TAG, "onConnect commandButton ${commandButton.displayName}")
|
||||
commandButton.sessionCommand?.let(sessionCommands::add)
|
||||
}
|
||||
|
||||
return MediaSession.ConnectionResult.accept(
|
||||
sessionCommands.build(),
|
||||
playerCommands.build()
|
||||
)
|
||||
} else if (session.isAutoCompanionController(controller)) {
|
||||
// Available session commands to accept incoming custom commands from Auto.
|
||||
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
|
||||
.setAvailableSessionCommands(sessionCommands.build())
|
||||
.build()
|
||||
}
|
||||
// Default commands with default custom layout for all other controllers.
|
||||
return MediaSession.ConnectionResult.AcceptedResultBuilder(session).build()
|
||||
}
|
||||
|
||||
override fun onPostConnect(session: MediaSession, controller: MediaSession.ControllerInfo) {
|
||||
super.onPostConnect(session, controller)
|
||||
if (notificationCustomButtons.isNotEmpty()) {
|
||||
/* Setting custom player command buttons to mediaLibrarySession for player notification. */
|
||||
mediaSession?.setCustomLayout(notificationCustomButtons)
|
||||
// mediaSession?.setCustomLayout(customMediaNotificationProvider.notificationMediaButtons)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCustomCommand(session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle): ListenableFuture<SessionResult> {
|
||||
/* Handling custom command buttons from player notification. */
|
||||
when (customCommand.customAction) {
|
||||
NotificationCustomButton.REWIND.customAction -> mediaPlayer?.seekDelta(-rewindSecs * 1000)
|
||||
NotificationCustomButton.FORWARD.customAction -> mediaPlayer?.seekDelta(fastForwardSecs * 1000)
|
||||
NotificationCustomButton.SKIP.customAction -> mediaPlayer?.skip()
|
||||
}
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
|
||||
return mediaSession
|
||||
}
|
||||
|
@ -414,9 +509,9 @@ class PlaybackService : MediaSessionService() {
|
|||
super.onStartCommand(intent, flags, startId)
|
||||
Log.d(TAG, "OnStartCommand called")
|
||||
|
||||
stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
notificationManager.cancel(R.id.notification_streaming_confirmation)
|
||||
// stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
// val notificationManager = NotificationManagerCompat.from(this)
|
||||
// notificationManager.cancel(R.id.notification_streaming_confirmation)
|
||||
|
||||
val keycode = intent?.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1) ?: -1
|
||||
val customAction = intent?.getStringExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION)
|
||||
|
@ -424,13 +519,13 @@ class PlaybackService : MediaSessionService() {
|
|||
val playable = intent?.getParcelableExtra<Playable>(PlaybackServiceInterface.EXTRA_PLAYABLE)
|
||||
if (keycode == -1 && playable == null && customAction == null) {
|
||||
Log.e(TAG, "PlaybackService was started with no arguments")
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
if ((flags and START_FLAG_REDELIVERY) != 0) {
|
||||
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.")
|
||||
stateManager.stopForeground(true)
|
||||
// stateManager.stopForeground(true)
|
||||
} else {
|
||||
when {
|
||||
keycode != -1 -> {
|
||||
|
@ -443,25 +538,21 @@ class PlaybackService : MediaSessionService() {
|
|||
notificationButton = true
|
||||
}
|
||||
val handled = handleKeycode(keycode, notificationButton)
|
||||
if (!handled && !stateManager.hasReceivedValidStartCommand()) {
|
||||
stateManager.stopService()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
// if (!handled && !stateManager.hasReceivedValidStartCommand()) {
|
||||
// stateManager.stopService()
|
||||
// return START_NOT_STICKY
|
||||
// }
|
||||
}
|
||||
playable != null -> {
|
||||
stateManager.validStartCommandWasReceived()
|
||||
// stateManager.validStartCommandWasReceived()
|
||||
val allowStreamThisTime = intent.getBooleanExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, false)
|
||||
val allowStreamAlways = intent.getBooleanExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_ALWAYS, false)
|
||||
sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
if (allowStreamAlways) {
|
||||
isAllowMobileStreaming = true
|
||||
}
|
||||
if (allowStreamAlways) isAllowMobileStreaming = true
|
||||
Observable.fromCallable {
|
||||
if (playable is FeedMedia) {
|
||||
return@fromCallable DBReader.getFeedMedia(playable.id)
|
||||
} else {
|
||||
return@fromCallable playable
|
||||
}
|
||||
if (playable is FeedMedia) return@fromCallable DBReader.getFeedMedia(playable.id)
|
||||
else return@fromCallable playable
|
||||
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -470,7 +561,7 @@ class PlaybackService : MediaSessionService() {
|
|||
{ error: Throwable ->
|
||||
Log.d(TAG, "Playable was not found. Stopping service.")
|
||||
error.printStackTrace()
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
})
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
@ -544,7 +635,7 @@ class PlaybackService : MediaSessionService() {
|
|||
.addAction(R.drawable.ic_notification_stream, getString(R.string.confirm_mobile_streaming_button_once), pendingIntentAllowThisTime)
|
||||
.addAction(R.drawable.ic_notification_stream, getString(R.string.confirm_mobile_streaming_button_always), pendingIntentAlwaysAllow)
|
||||
.setAutoCancel(true)
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
// val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
|
@ -559,7 +650,7 @@ class PlaybackService : MediaSessionService() {
|
|||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_streaming_confirmation, builder.build())
|
||||
// notificationManager.notify(R.id.notification_streaming_confirmation, builder.build())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -573,44 +664,28 @@ class PlaybackService : MediaSessionService() {
|
|||
when (keycode) {
|
||||
KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
|
||||
when {
|
||||
status == PlayerStatus.PLAYING -> {
|
||||
mediaPlayer?.pause(!isPersistNotify, false)
|
||||
}
|
||||
status == PlayerStatus.FALLBACK || status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> {
|
||||
mediaPlayer?.resume()
|
||||
}
|
||||
status == PlayerStatus.PREPARING -> {
|
||||
mediaPlayer?.setStartWhenPrepared(!mediaPlayer!!.isStartWhenPrepared())
|
||||
}
|
||||
status == PlayerStatus.PLAYING -> mediaPlayer?.pause(!isPersistNotify, false)
|
||||
status == PlayerStatus.FALLBACK || status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> mediaPlayer?.resume()
|
||||
status == PlayerStatus.PREPARING -> mediaPlayer?.setStartWhenPrepared(!mediaPlayer!!.isStartWhenPrepared())
|
||||
status == PlayerStatus.INITIALIZED -> {
|
||||
mediaPlayer?.setStartWhenPrepared(true)
|
||||
mediaPlayer?.prepare()
|
||||
}
|
||||
mediaPlayer?.getPlayable() == null -> {
|
||||
startPlayingFromPreferences()
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
mediaPlayer?.getPlayable() == null -> startPlayingFromPreferences()
|
||||
else -> return false
|
||||
}
|
||||
taskManager.restartSleepTimer()
|
||||
return true
|
||||
}
|
||||
KeyEvent.KEYCODE_MEDIA_PLAY -> {
|
||||
when {
|
||||
status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> {
|
||||
mediaPlayer?.resume()
|
||||
}
|
||||
status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> mediaPlayer?.resume()
|
||||
status == PlayerStatus.INITIALIZED -> {
|
||||
mediaPlayer?.setStartWhenPrepared(true)
|
||||
mediaPlayer?.prepare()
|
||||
}
|
||||
mediaPlayer?.getPlayable() == null -> {
|
||||
startPlayingFromPreferences()
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
mediaPlayer?.getPlayable() == null -> startPlayingFromPreferences()
|
||||
else -> return false
|
||||
}
|
||||
taskManager.restartSleepTimer()
|
||||
return true
|
||||
|
@ -624,10 +699,8 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
KeyEvent.KEYCODE_MEDIA_NEXT -> {
|
||||
when {
|
||||
!notificationButton -> {
|
||||
// Handle remapped button as notification button which is not remapped again.
|
||||
return handleKeycode(hardwareForwardButton, true)
|
||||
}
|
||||
// Handle remapped button as notification button which is not remapped again.
|
||||
!notificationButton -> return handleKeycode(hardwareForwardButton, true)
|
||||
this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED -> {
|
||||
mediaPlayer?.skip()
|
||||
return true
|
||||
|
@ -644,10 +717,8 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
|
||||
when {
|
||||
!notificationButton -> {
|
||||
// Handle remapped button as notification button which is not remapped again.
|
||||
return handleKeycode(hardwarePreviousButton, true)
|
||||
}
|
||||
// Handle remapped button as notification button which is not remapped again.
|
||||
!notificationButton -> return handleKeycode(hardwarePreviousButton, true)
|
||||
this.status == PlayerStatus.FALLBACK || this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED -> {
|
||||
mediaPlayer?.seekTo(0)
|
||||
return true
|
||||
|
@ -666,7 +737,7 @@ class PlaybackService : MediaSessionService() {
|
|||
if (this.status == PlayerStatus.FALLBACK || status == PlayerStatus.PLAYING) {
|
||||
mediaPlayer?.pause(true, true)
|
||||
}
|
||||
stateManager.stopForeground(true) // gets rid of persistent notification
|
||||
// stateManager.stopForeground(true) // gets rid of persistent notification
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
|
@ -692,7 +763,7 @@ class PlaybackService : MediaSessionService() {
|
|||
{ error: Throwable ->
|
||||
Log.d(TAG, "Playable was not loaded from preferences. Stopping service.")
|
||||
error.printStackTrace()
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -704,7 +775,7 @@ class PlaybackService : MediaSessionService() {
|
|||
if (stream && !localFeed && !isStreamingAllowed && !allowStreamThisTime) {
|
||||
displayStreamingNotAllowedNotification(PlaybackServiceStarter(this, playable).intent)
|
||||
writeNoMediaPlaying()
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -713,8 +784,8 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
|
||||
mediaPlayer?.playMediaObject(playable, stream, true, true)
|
||||
stateManager.validStartCommandWasReceived()
|
||||
stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
// stateManager.validStartCommandWasReceived()
|
||||
// stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
recreateMediaSessionIfNeeded()
|
||||
updateNotificationAndMediaSession(playable)
|
||||
addPlayableToQueue(playable)
|
||||
|
@ -733,7 +804,7 @@ class PlaybackService : MediaSessionService() {
|
|||
mediaPlayer?.pause(true, false)
|
||||
mediaPlayer?.resetVideoSurface()
|
||||
updateNotificationAndMediaSession(playable)
|
||||
stateManager.stopForeground(!isPersistNotify)
|
||||
// stateManager.stopForeground(!isPersistNotify)
|
||||
}
|
||||
|
||||
private val taskManagerCallback: PSTMCallback = object : PSTMCallback {
|
||||
|
@ -760,21 +831,17 @@ class PlaybackService : MediaSessionService() {
|
|||
if (newInfo != null) {
|
||||
when (newInfo.playerStatus) {
|
||||
PlayerStatus.INITIALIZED -> {
|
||||
if (mediaPlayer != null) {
|
||||
writeMediaPlaying(mediaPlayer!!.pSMPInfo.playable, mediaPlayer!!.pSMPInfo.playerStatus, currentitem)
|
||||
}
|
||||
if (mediaPlayer != null) writeMediaPlaying(mediaPlayer!!.pSMPInfo.playable, mediaPlayer!!.pSMPInfo.playerStatus, currentitem)
|
||||
updateNotificationAndMediaSession(newInfo.playable)
|
||||
}
|
||||
PlayerStatus.PREPARED -> {
|
||||
if (mediaPlayer != null) {
|
||||
writeMediaPlaying(mediaPlayer!!.pSMPInfo.playable, mediaPlayer!!.pSMPInfo.playerStatus, currentitem)
|
||||
}
|
||||
if (mediaPlayer != null) writeMediaPlaying(mediaPlayer!!.pSMPInfo.playable, mediaPlayer!!.pSMPInfo.playerStatus, currentitem)
|
||||
taskManager.startChapterLoader(newInfo.playable!!)
|
||||
}
|
||||
PlayerStatus.PAUSED -> {
|
||||
updateNotificationAndMediaSession(newInfo.playable)
|
||||
if (!isCasting) {
|
||||
stateManager.stopForeground(!isPersistNotify)
|
||||
// stateManager.stopForeground(!isPersistNotify)
|
||||
}
|
||||
cancelPositionObserver()
|
||||
if (mediaPlayer != null) writePlayerStatus(mediaPlayer!!.playerStatus)
|
||||
|
@ -786,8 +853,8 @@ class PlaybackService : MediaSessionService() {
|
|||
recreateMediaSessionIfNeeded()
|
||||
updateNotificationAndMediaSession(newInfo.playable)
|
||||
setupPositionObserver()
|
||||
stateManager.validStartCommandWasReceived()
|
||||
stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
// stateManager.validStartCommandWasReceived()
|
||||
// stateManager.startForeground(R.id.notification_playing, notificationBuilder.build())
|
||||
// set sleep timer if auto-enabled
|
||||
var autoEnableByTime = true
|
||||
val fromSetting = autoEnableFrom()
|
||||
|
@ -807,7 +874,7 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
PlayerStatus.ERROR -> {
|
||||
writeNoMediaPlaying()
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
@ -822,15 +889,14 @@ class PlaybackService : MediaSessionService() {
|
|||
taskManager.requestWidgetUpdate()
|
||||
}
|
||||
|
||||
|
||||
override fun shouldStop() {
|
||||
stateManager.stopForeground(!isPersistNotify)
|
||||
// stateManager.stopForeground(!isPersistNotify)
|
||||
}
|
||||
|
||||
override fun onMediaChanged(reloadUI: Boolean) {
|
||||
Log.d(TAG, "reloadUI callback reached")
|
||||
if (reloadUI) {
|
||||
sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
}
|
||||
if (reloadUI) sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
updateNotificationAndMediaSession(this@PlaybackService.playable)
|
||||
}
|
||||
|
||||
|
@ -840,11 +906,8 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
override fun onPlaybackStart(playable: Playable, position: Int) {
|
||||
taskManager.startWidgetUpdater()
|
||||
if (position != Playable.INVALID_TIME) {
|
||||
playable.setPosition(position)
|
||||
} else {
|
||||
skipIntro(playable)
|
||||
}
|
||||
if (position != Playable.INVALID_TIME) playable.setPosition(position)
|
||||
else skipIntro(playable)
|
||||
playable.onPlaybackStart()
|
||||
taskManager.startPositionSaver()
|
||||
}
|
||||
|
@ -855,9 +918,8 @@ class PlaybackService : MediaSessionService() {
|
|||
saveCurrentPosition(position == Playable.INVALID_TIME || playable == null, playable, position)
|
||||
taskManager.cancelWidgetUpdater()
|
||||
if (playable != null) {
|
||||
if (playable is FeedMedia) {
|
||||
if (playable is FeedMedia)
|
||||
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(applicationContext, playable, false)
|
||||
}
|
||||
playable.onPlaybackPause(applicationContext)
|
||||
}
|
||||
}
|
||||
|
@ -876,19 +938,16 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
|
||||
override fun ensureMediaInfoLoaded(media: Playable) {
|
||||
if (media is FeedMedia && media.item == null) {
|
||||
media.setItem(DBReader.getFeedItem(media.itemId))
|
||||
}
|
||||
if (media is FeedMedia && media.item == null) media.setItem(DBReader.getFeedItem(media.itemId))
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Suppress("unused")
|
||||
fun playerError(event: PlayerErrorEvent?) {
|
||||
if (mediaPlayer?.playerStatus == PlayerStatus.PLAYING || mediaPlayer?.playerStatus == PlayerStatus.FALLBACK) {
|
||||
if (mediaPlayer?.playerStatus == PlayerStatus.PLAYING || mediaPlayer?.playerStatus == PlayerStatus.FALLBACK)
|
||||
mediaPlayer!!.pause(true, false)
|
||||
}
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -920,9 +979,7 @@ class PlaybackService : MediaSessionService() {
|
|||
Log.d(TAG, "onSleepTimerAlmostExpired: $multiplicator")
|
||||
mediaPlayer?.setVolume(multiplicator, multiplicator)
|
||||
}
|
||||
event.isCancelled -> {
|
||||
mediaPlayer?.setVolume(1.0f, 1.0f)
|
||||
}
|
||||
event.isCancelled -> mediaPlayer?.setVolume(1.0f, 1.0f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -933,9 +990,7 @@ class PlaybackService : MediaSessionService() {
|
|||
return null
|
||||
}
|
||||
Log.d(TAG, "getNextInQueue()")
|
||||
if (currentMedia.item == null) {
|
||||
currentMedia.setItem(DBReader.getFeedItem(currentMedia.itemId))
|
||||
}
|
||||
if (currentMedia.item == null) currentMedia.setItem(DBReader.getFeedItem(currentMedia.itemId))
|
||||
val item = currentMedia.item
|
||||
if (item == null) {
|
||||
Log.w(TAG, "getNextInQueue() with FeedMedia object whose FeedItem is null")
|
||||
|
@ -959,7 +1014,7 @@ class PlaybackService : MediaSessionService() {
|
|||
if (!nextItem.media!!.localFileAvailable() && !isStreamingAllowed && isFollowQueue && nextItem.feed != null && !nextItem.feed!!.isLocalFeed) {
|
||||
displayStreamingNotAllowedNotification(PlaybackServiceStarter(this, nextItem.media!!).intent)
|
||||
writeNoMediaPlaying()
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
return null
|
||||
}
|
||||
return nextItem.media
|
||||
|
@ -974,10 +1029,10 @@ class PlaybackService : MediaSessionService() {
|
|||
if (stopPlaying) {
|
||||
taskManager.cancelPositionSaver()
|
||||
cancelPositionObserver()
|
||||
if (!isCasting) {
|
||||
stateManager.stopForeground(true)
|
||||
stateManager.stopService()
|
||||
}
|
||||
// if (!isCasting) {
|
||||
// stateManager.stopForeground(true)
|
||||
// stateManager.stopService()
|
||||
// }
|
||||
}
|
||||
if (mediaType == null) {
|
||||
sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_PLAYBACK_END, 0)
|
||||
|
@ -1020,19 +1075,14 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
if (playable !is FeedMedia) {
|
||||
Log.d(TAG, "Not doing post-playback processing: media not of type FeedMedia")
|
||||
if (ended) {
|
||||
playable.onPlaybackCompleted(applicationContext)
|
||||
} else {
|
||||
playable.onPlaybackPause(applicationContext)
|
||||
}
|
||||
if (ended) playable.onPlaybackCompleted(applicationContext)
|
||||
else playable.onPlaybackPause(applicationContext)
|
||||
// return
|
||||
}
|
||||
val media = playable
|
||||
val item = (media as? FeedMedia)?.item ?: currentitem
|
||||
val smartMarkAsPlayed = hasAlmostEnded(media)
|
||||
if (!ended && smartMarkAsPlayed) {
|
||||
Log.d(TAG, "smart mark as played")
|
||||
}
|
||||
if (!ended && smartMarkAsPlayed) Log.d(TAG, "smart mark as played")
|
||||
|
||||
var autoSkipped = false
|
||||
if (autoSkippedFeedMediaId != null && autoSkippedFeedMediaId == item?.identifyingValue) {
|
||||
|
@ -1068,9 +1118,7 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
}
|
||||
|
||||
if (media is FeedMedia && (ended || skipped || playingNext)) {
|
||||
DBWriter.addItemToPlaybackHistory(media)
|
||||
}
|
||||
if (media is FeedMedia && (ended || skipped || playingNext)) DBWriter.addItemToPlaybackHistory(media)
|
||||
}
|
||||
|
||||
fun setSleepTimer(waitingTime: Long) {
|
||||
|
@ -1158,23 +1206,20 @@ class PlaybackService : MediaSessionService() {
|
|||
WearMediaSession.addWearExtrasToAction(fastForwardBuilder)
|
||||
sessionState.addCustomAction(fastForwardBuilder.build())
|
||||
|
||||
if (showPlaybackSpeedOnFullNotification()) {
|
||||
if (showPlaybackSpeedOnFullNotification())
|
||||
sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED,
|
||||
getString(R.string.playback_speed), R.drawable.ic_notification_playback_speed).build())
|
||||
}
|
||||
|
||||
if (showNextChapterOnFullNotification()) {
|
||||
if (!playable?.getChapters().isNullOrEmpty()) {
|
||||
if (!playable?.getChapters().isNullOrEmpty())
|
||||
sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_NEXT_CHAPTER,
|
||||
getString(R.string.next_chapter), R.drawable.ic_notification_next_chapter).build())
|
||||
}
|
||||
}
|
||||
|
||||
if (showSkipOnFullNotification()) {
|
||||
if (showSkipOnFullNotification())
|
||||
sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_SKIP_TO_NEXT,
|
||||
getString(R.string.skip_episode_label), R.drawable.ic_notification_skip).build()
|
||||
)
|
||||
}
|
||||
getString(R.string.skip_episode_label), R.drawable.ic_notification_skip).build())
|
||||
|
||||
|
||||
if (mediaSession != null) {
|
||||
WearMediaSession.mediaSessionSetExtraForWear(mediaSession!!)
|
||||
|
@ -1199,34 +1244,36 @@ class PlaybackService : MediaSessionService() {
|
|||
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, p.getFeedTitle())
|
||||
|
||||
|
||||
if (notificationBuilder.isIconCached) {
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, notificationBuilder.cachedIcon)
|
||||
} else {
|
||||
var iconUri = p.getImageLocation()
|
||||
// Don't use embedded cover etc, which Android can't load
|
||||
if (p is FeedMedia) {
|
||||
val m = p
|
||||
if (m.item != null) {
|
||||
val item = m.item!!
|
||||
when {
|
||||
item.imageUrl != null -> {
|
||||
iconUri = item.imageUrl
|
||||
}
|
||||
item.feed != null -> {
|
||||
iconUri = item.feed!!.imageUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!iconUri.isNullOrEmpty()) {
|
||||
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, iconUri)
|
||||
}
|
||||
}
|
||||
// if (notificationBuilder.isIconCached) {
|
||||
// builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, notificationBuilder.cachedIcon)
|
||||
// } else {
|
||||
// var iconUri = p.getImageLocation()
|
||||
// // Don't use embedded cover etc, which Android can't load
|
||||
// if (p is FeedMedia) {
|
||||
// val m = p
|
||||
// if (m.item != null) {
|
||||
// val item = m.item!!
|
||||
// when {
|
||||
// item.imageUrl != null -> {
|
||||
// iconUri = item.imageUrl
|
||||
// }
|
||||
// item.feed != null -> {
|
||||
// iconUri = item.feed!!.imageUrl
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (!iconUri.isNullOrEmpty()) {
|
||||
// builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, iconUri)
|
||||
// }
|
||||
// }
|
||||
|
||||
if (stateManager.hasReceivedValidStartCommand()) {
|
||||
mediaSession!!.setSessionActivity(PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
|
||||
getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT
|
||||
or (if (Build.VERSION.SDK_INT >= 31) PendingIntent.FLAG_MUTABLE else 0)))
|
||||
// if (stateManager.hasReceivedValidStartCommand()) {
|
||||
// mediaSession!!.setSessionActivity(PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
|
||||
// getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT
|
||||
// or (if (Build.VERSION.SDK_INT >= 31) PendingIntent.FLAG_MUTABLE else 0)))
|
||||
mediaSession!!.setSessionActivity(PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
|
||||
getPlayerActivityIntent(this), FLAG_IMMUTABLE))
|
||||
// try {
|
||||
// mediaSession!!.setMetadata(builder.build())
|
||||
// } catch (e: OutOfMemoryError) {
|
||||
|
@ -1234,7 +1281,7 @@ class PlaybackService : MediaSessionService() {
|
|||
// builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, null)
|
||||
// mediaSession!!.setMetadata(builder.build())
|
||||
// }
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1252,19 +1299,19 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
if (playable == null || mediaPlayer == null) {
|
||||
Log.d(TAG, "setupNotification: playable=$playable mediaPlayer=$mediaPlayer")
|
||||
if (!stateManager.hasReceivedValidStartCommand()) {
|
||||
stateManager.stopService()
|
||||
}
|
||||
// if (!stateManager.hasReceivedValidStartCommand()) {
|
||||
// stateManager.stopService()
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
val playerStatus = mediaPlayer!!.playerStatus
|
||||
notificationBuilder.setPlayable(playable)
|
||||
if (mediaSession != null) notificationBuilder.setMediaSessionToken(mediaSession!!.getSessionCompatToken())
|
||||
notificationBuilder.playerStatus = playerStatus
|
||||
notificationBuilder.updatePosition(currentPosition, currentPlaybackSpeed)
|
||||
// notificationBuilder.setPlayable(playable)
|
||||
// if (mediaSession != null) notificationBuilder.setMediaSessionToken(mediaSession!!.getSessionCompatToken())
|
||||
// notificationBuilder.playerStatus = playerStatus
|
||||
// notificationBuilder.updatePosition(currentPosition, currentPlaybackSpeed)
|
||||
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
// val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
|
@ -1279,19 +1326,19 @@ class PlaybackService : MediaSessionService() {
|
|||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
// notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
|
||||
if (!notificationBuilder.isIconCached) {
|
||||
playableIconLoaderThread = Thread {
|
||||
Log.d(TAG, "Loading notification icon")
|
||||
notificationBuilder.loadIcon()
|
||||
if (!Thread.currentThread().isInterrupted) {
|
||||
notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
updateMediaSessionMetadata(playable)
|
||||
}
|
||||
}
|
||||
playableIconLoaderThread?.start()
|
||||
}
|
||||
// if (!notificationBuilder.isIconCached) {
|
||||
// playableIconLoaderThread = Thread {
|
||||
// Log.d(TAG, "Loading notification icon")
|
||||
// notificationBuilder.loadIcon()
|
||||
// if (!Thread.currentThread().isInterrupted) {
|
||||
// notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
// updateMediaSessionMetadata(playable)
|
||||
// }
|
||||
// }
|
||||
// playableIconLoaderThread?.start()
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1312,9 +1359,8 @@ class PlaybackService : MediaSessionService() {
|
|||
position = currentPosition
|
||||
duration = this.duration
|
||||
playable = mediaPlayer?.getPlayable()
|
||||
} else {
|
||||
duration = playable?.getDuration() ?: Playable.INVALID_TIME
|
||||
}
|
||||
} else duration = playable?.getDuration() ?: Playable.INVALID_TIME
|
||||
|
||||
if (position != Playable.INVALID_TIME && duration != Playable.INVALID_TIME && playable != null) {
|
||||
// Log.d(TAG, "Saving current position to $position $duration")
|
||||
saveCurrentPosition(playable, position, System.currentTimeMillis())
|
||||
|
@ -1331,9 +1377,7 @@ class PlaybackService : MediaSessionService() {
|
|||
private fun bluetoothNotifyChange(info: PSMPInfo?, whatChanged: String) {
|
||||
var isPlaying = false
|
||||
|
||||
if (info?.playerStatus == PlayerStatus.PLAYING || info?.playerStatus == PlayerStatus.FALLBACK) {
|
||||
isPlaying = true
|
||||
}
|
||||
if (info?.playerStatus == PlayerStatus.PLAYING || info?.playerStatus == PlayerStatus.FALLBACK) isPlaying = true
|
||||
|
||||
if (info?.playable != null) {
|
||||
val i = Intent(whatChanged)
|
||||
|
@ -1358,12 +1402,8 @@ class PlaybackService : MediaSessionService() {
|
|||
} else {
|
||||
val playerStatus = mediaPlayer?.playerStatus
|
||||
when (playerStatus) {
|
||||
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> {
|
||||
mediaPlayer?.resume()
|
||||
}
|
||||
PlayerStatus.PREPARING -> {
|
||||
mediaPlayer?.setStartWhenPrepared(!mediaPlayer!!.isStartWhenPrepared())
|
||||
}
|
||||
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> mediaPlayer?.resume()
|
||||
PlayerStatus.PREPARING -> mediaPlayer?.setStartWhenPrepared(!mediaPlayer!!.isStartWhenPrepared())
|
||||
PlayerStatus.INITIALIZED -> {
|
||||
mediaPlayer?.setStartWhenPrepared(true)
|
||||
mediaPlayer?.prepare()
|
||||
|
@ -1384,20 +1424,16 @@ class PlaybackService : MediaSessionService() {
|
|||
private val PLUGGED = 1
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (isInitialStickyBroadcast) {
|
||||
// Don't pause playback after we just started, just because the receiver
|
||||
// delivers the current headset state (instead of a change)
|
||||
return
|
||||
}
|
||||
// Don't pause playback after we just started, just because the receiver
|
||||
// delivers the current headset state (instead of a change)
|
||||
if (isInitialStickyBroadcast) return
|
||||
|
||||
if (TextUtils.equals(intent.action, Intent.ACTION_HEADSET_PLUG)) {
|
||||
val state = intent.getIntExtra("state", -1)
|
||||
Log.d(TAG, "Headset plug event. State is $state")
|
||||
if (state != -1) {
|
||||
when (state) {
|
||||
UNPLUGGED -> {
|
||||
Log.d(TAG, "Headset was unplugged during playback.")
|
||||
}
|
||||
UNPLUGGED -> Log.d(TAG, "Headset was unplugged during playback.")
|
||||
PLUGGED -> {
|
||||
Log.d(TAG, "Headset was plugged in during playback.")
|
||||
unpauseIfPauseOnDisconnect(false)
|
||||
|
@ -1436,9 +1472,7 @@ class PlaybackService : MediaSessionService() {
|
|||
private fun pauseIfPauseOnDisconnect() {
|
||||
Log.d(TAG, "pauseIfPauseOnDisconnect()")
|
||||
transientPause = (mediaPlayer?.playerStatus == PlayerStatus.PLAYING || mediaPlayer?.playerStatus == PlayerStatus.FALLBACK)
|
||||
if (isPauseOnHeadsetDisconnect && !isCasting) {
|
||||
mediaPlayer?.pause(!isPersistNotify, false)
|
||||
}
|
||||
if (isPauseOnHeadsetDisconnect && !isCasting) mediaPlayer?.pause(!isPersistNotify, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1452,13 +1486,11 @@ class PlaybackService : MediaSessionService() {
|
|||
if (transientPause) {
|
||||
transientPause = false
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
return
|
||||
}
|
||||
when {
|
||||
!bluetooth && isUnpauseOnHeadsetReconnect -> {
|
||||
mediaPlayer?.resume()
|
||||
}
|
||||
!bluetooth && isUnpauseOnHeadsetReconnect -> mediaPlayer?.resume()
|
||||
bluetooth && isUnpauseOnBluetoothReconnect -> {
|
||||
// let the user know we've started playback again...
|
||||
val v = applicationContext.getSystemService(VIBRATOR_SERVICE) as? Vibrator
|
||||
|
@ -1473,7 +1505,7 @@ class PlaybackService : MediaSessionService() {
|
|||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (TextUtils.equals(intent.action, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
|
||||
EventBus.getDefault().post(PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN))
|
||||
stateManager.stopService()
|
||||
// stateManager.stopService()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1492,11 +1524,8 @@ class PlaybackService : MediaSessionService() {
|
|||
val item = (playable as? FeedMedia)?.item ?: currentitem
|
||||
// if (playable is FeedMedia) {
|
||||
if (item?.feed?.id == event.feedId) {
|
||||
if (event.speed == FeedPreferences.SPEED_USE_GLOBAL) {
|
||||
setSpeed(getPlaybackSpeed(playable!!.getMediaType()))
|
||||
} else {
|
||||
setSpeed(event.speed)
|
||||
}
|
||||
if (event.speed == FeedPreferences.SPEED_USE_GLOBAL) setSpeed(getPlaybackSpeed(playable!!.getMediaType()))
|
||||
else setSpeed(event.speed)
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
@ -1570,9 +1599,7 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
if (item != null) {
|
||||
var feed = item.feed
|
||||
if (feed == null) {
|
||||
feed = DBReader.getFeed(item.feedId)
|
||||
}
|
||||
if (feed == null) feed = DBReader.getFeed(item.feedId)
|
||||
if (feed != null) {
|
||||
val feedPreferences = feed.preferences
|
||||
if (feedPreferences != null) {
|
||||
|
@ -1601,9 +1628,8 @@ class PlaybackService : MediaSessionService() {
|
|||
if (!isSpeedForward) {
|
||||
normalSpeed = mediaPlayer!!.getPlaybackSpeed()
|
||||
mediaPlayer!!.setPlaybackParams(speed, isSkipSilence)
|
||||
} else {
|
||||
mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||
}
|
||||
} else mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||
|
||||
isSpeedForward = !isSpeedForward
|
||||
}
|
||||
|
||||
|
@ -1613,9 +1639,8 @@ class PlaybackService : MediaSessionService() {
|
|||
if (!isFallbackSpeed) {
|
||||
normalSpeed = mediaPlayer!!.getPlaybackSpeed()
|
||||
mediaPlayer!!.setPlaybackParams(speed, isSkipSilence)
|
||||
} else {
|
||||
mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||
}
|
||||
} else mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||
|
||||
isFallbackSpeed = !isFallbackSpeed
|
||||
}
|
||||
|
||||
|
@ -1688,13 +1713,13 @@ class PlaybackService : MediaSessionService() {
|
|||
positionEventTimer = Observable.interval(POSITION_EVENT_INTERVAL, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
Log.d(TAG, "notificationBuilder.updatePosition currentPosition: $currentPosition, currentPlaybackSpeed: $currentPlaybackSpeed")
|
||||
Log.d(TAG, "setupPositionObserver currentPosition: $currentPosition, currentPlaybackSpeed: $currentPlaybackSpeed")
|
||||
EventBus.getDefault().post(PlaybackPositionEvent(currentPosition, duration))
|
||||
// TODO: why set SDK_INT < 29
|
||||
if (Build.VERSION.SDK_INT < 29) {
|
||||
notificationBuilder.updatePosition(currentPosition, currentPlaybackSpeed)
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
|
||||
notificationManager?.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
// notificationBuilder.updatePosition(currentPosition, currentPlaybackSpeed)
|
||||
// val notificationManager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
|
||||
// notificationManager?.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
}
|
||||
skipEndingIfNecessary()
|
||||
}
|
||||
|
@ -1712,10 +1737,10 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
}
|
||||
|
||||
private val sessionCallback: MediaSession.Callback = object : MediaSession.Callback {
|
||||
private val TAG = "MediaSessionCompat"
|
||||
// TODO: not used now with media3
|
||||
}
|
||||
// private val sessionCallback: MediaSession.Callback = object : MediaSession.Callback {
|
||||
// private val TAG = "MediaSessionCompat"
|
||||
//// TODO: not used now with media3
|
||||
// }
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PlaybackService"
|
||||
|
@ -1771,17 +1796,11 @@ class PlaybackService : MediaSessionService() {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun getPlayerActivityIntent(context: Context): Intent {
|
||||
val showVideoPlayer = if (isRunning) {
|
||||
currentMediaType == MediaType.VIDEO && !isCasting
|
||||
} else {
|
||||
currentEpisodeIsVideo
|
||||
}
|
||||
val showVideoPlayer = if (isRunning) currentMediaType == MediaType.VIDEO && !isCasting
|
||||
else currentEpisodeIsVideo
|
||||
|
||||
return if (showVideoPlayer) {
|
||||
VideoPlayerActivityStarter(context).intent
|
||||
} else {
|
||||
MainActivityStarter(context).withOpenPlayer().getIntent()
|
||||
}
|
||||
return if (showVideoPlayer) VideoPlayerActivityStarter(context).intent
|
||||
else MainActivityStarter(context).withOpenPlayer().getIntent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1790,12 +1809,8 @@ class PlaybackService : MediaSessionService() {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun getPlayerActivityIntent(context: Context, mediaType: MediaType?): Intent {
|
||||
return if (mediaType == MediaType.VIDEO && !isCasting) {
|
||||
VideoPlayerActivityStarter(context).intent
|
||||
} else {
|
||||
MainActivityStarter(context).withOpenPlayer().getIntent()
|
||||
}
|
||||
return if (mediaType == MediaType.VIDEO && !isCasting) VideoPlayerActivityStarter(context).intent
|
||||
else MainActivityStarter(context).withOpenPlayer().getIntent()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,8 +5,7 @@ object PlaybackServiceInterface {
|
|||
const val EXTRA_ALLOW_STREAM_THIS_TIME: String = "extra.ac.mdiq.podcini.service.allowStream"
|
||||
const val EXTRA_ALLOW_STREAM_ALWAYS: String = "extra.ac.mdiq.podcini.service.allowStreamAlways"
|
||||
|
||||
const val ACTION_PLAYER_NOTIFICATION
|
||||
: String = "action.ac.mdiq.podcini.service.playerNotification"
|
||||
const val ACTION_PLAYER_NOTIFICATION: String = "action.ac.mdiq.podcini.service.playerNotification"
|
||||
const val EXTRA_NOTIFICATION_CODE: String = "extra.ac.mdiq.podcini.service.notificationCode"
|
||||
const val EXTRA_NOTIFICATION_TYPE: String = "extra.ac.mdiq.podcini.service.notificationType"
|
||||
const val NOTIFICATION_TYPE_PLAYBACK_END: Int = 7
|
||||
|
@ -15,6 +14,5 @@ object PlaybackServiceInterface {
|
|||
const val EXTRA_CODE_VIDEO: Int = 2
|
||||
const val EXTRA_CODE_CAST: Int = 3
|
||||
|
||||
const val ACTION_SHUTDOWN_PLAYBACK_SERVICE
|
||||
: String = "action.ac.mdiq.podcini.service.actionShutdownPlaybackService"
|
||||
const val ACTION_SHUTDOWN_PLAYBACK_SERVICE: String = "action.ac.mdiq.podcini.service.actionShutdownPlaybackService"
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
|||
import ac.mdiq.podcini.util.Converter
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
|
||||
// TODO: not needed with media3
|
||||
@UnstableApi
|
||||
class PlaybackServiceNotificationBuilder(private val context: Context) {
|
||||
private var playable: Playable? = null
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
|||
import androidx.core.app.ServiceCompat
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
// TODO: not needed with media3
|
||||
internal class PlaybackServiceStateManager(private val playbackService: PlaybackService) {
|
||||
@Volatile
|
||||
private var isInForeground = false
|
||||
|
|
|
@ -31,9 +31,7 @@ import kotlin.concurrent.Volatile
|
|||
* The PlaybackServiceTaskManager(PSTM) uses a callback object (PSTMCallback)
|
||||
* to notify the PlaybackService about updates from the running tasks.
|
||||
*/
|
||||
class PlaybackServiceTaskManager(private val context: Context,
|
||||
private val callback: PSTMCallback
|
||||
) {
|
||||
class PlaybackServiceTaskManager(private val context: Context, private val callback: PSTMCallback) {
|
||||
private val schedExecutor: ScheduledThreadPoolExecutor
|
||||
|
||||
private var positionSaverFuture: ScheduledFuture<*>? = null
|
||||
|
@ -67,10 +65,8 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
if (!isPositionSaverActive) {
|
||||
var positionSaver = Runnable { callback.positionSaverTick() }
|
||||
positionSaver = useMainThreadIfNecessary(positionSaver)
|
||||
positionSaverFuture =
|
||||
schedExecutor.scheduleWithFixedDelay(positionSaver, POSITION_SAVER_WAITING_INTERVAL.toLong(),
|
||||
POSITION_SAVER_WAITING_INTERVAL.toLong(), TimeUnit.MILLISECONDS)
|
||||
|
||||
positionSaverFuture = schedExecutor.scheduleWithFixedDelay(positionSaver, POSITION_SAVER_WAITING_INTERVAL.toLong(),
|
||||
POSITION_SAVER_WAITING_INTERVAL.toLong(), TimeUnit.MILLISECONDS)
|
||||
Log.d(TAG, "Started PositionSaver")
|
||||
} else {
|
||||
Log.d(TAG, "Call to startPositionSaver was ignored.")
|
||||
|
@ -103,10 +99,8 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
if (!isWidgetUpdaterActive && !schedExecutor.isShutdown) {
|
||||
var widgetUpdater = Runnable { this.requestWidgetUpdate() }
|
||||
widgetUpdater = useMainThreadIfNecessary(widgetUpdater)
|
||||
widgetUpdaterFuture = schedExecutor.scheduleWithFixedDelay(widgetUpdater,
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(),
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(),
|
||||
TimeUnit.MILLISECONDS)
|
||||
widgetUpdaterFuture = schedExecutor.scheduleWithFixedDelay(widgetUpdater, WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(),
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(), TimeUnit.MILLISECONDS)
|
||||
Log.d(TAG, "Started WidgetUpdater")
|
||||
} else {
|
||||
Log.d(TAG, "Call to startWidgetUpdater was ignored.")
|
||||
|
@ -119,11 +113,8 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
@Synchronized
|
||||
fun requestWidgetUpdate() {
|
||||
val state = callback.requestWidgetState()
|
||||
if (!schedExecutor.isShutdown) {
|
||||
schedExecutor.execute { WidgetUpdater.updateWidget(context, state) }
|
||||
} else {
|
||||
Log.d(TAG, "Call to requestWidgetUpdate was ignored.")
|
||||
}
|
||||
if (!schedExecutor.isShutdown) schedExecutor.execute { WidgetUpdater.updateWidget(context, state) }
|
||||
else Log.d(TAG, "Call to requestWidgetUpdate was ignored.")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,9 +129,7 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
require(waitingTime > 0) { "Waiting time <= 0" }
|
||||
|
||||
Log.d(TAG, "Setting sleep timer to $waitingTime milliseconds")
|
||||
if (isSleepTimerActive) {
|
||||
sleepTimerFuture!!.cancel(true)
|
||||
}
|
||||
if (isSleepTimerActive) sleepTimerFuture!!.cancel(true)
|
||||
sleepTimer = SleepTimer(waitingTime)
|
||||
sleepTimerFuture = schedExecutor.schedule(sleepTimer, 0, TimeUnit.MILLISECONDS)
|
||||
EventBus.getDefault().post(SleepTimerUpdatedEvent.justEnabled(waitingTime))
|
||||
|
@ -181,11 +170,7 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
/**
|
||||
* Returns the current sleep timer time or 0 if the sleep timer is not active.
|
||||
*/
|
||||
get() = if (isSleepTimerActive) {
|
||||
sleepTimer!!.getWaitingTime()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
get() = if (isSleepTimerActive) sleepTimer!!.getWaitingTime() else 0
|
||||
|
||||
@get:Synchronized
|
||||
val isWidgetUpdaterActive: Boolean
|
||||
|
@ -224,8 +209,7 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ callback.onChapterLoaded(media) },
|
||||
{ throwable: Throwable? ->
|
||||
Log.d(TAG,
|
||||
"Error loading chapters: " + Log.getStackTraceString(throwable))
|
||||
Log.d(TAG, "Error loading chapters: " + Log.getStackTraceString(throwable))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -299,9 +283,7 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
hasVibrated = true
|
||||
}
|
||||
}
|
||||
if (shakeListener == null && SleepTimerPreferences.shakeToReset()) {
|
||||
shakeListener = ShakeListener(context, this)
|
||||
}
|
||||
if (shakeListener == null && SleepTimerPreferences.shakeToReset()) shakeListener = ShakeListener(context, this)
|
||||
}
|
||||
if (timeLeft <= 0) {
|
||||
Log.d(TAG, "Sleep timer expired")
|
||||
|
@ -329,9 +311,8 @@ class PlaybackServiceTaskManager(private val context: Context,
|
|||
|
||||
fun cancel() {
|
||||
sleepTimerFuture!!.cancel(true)
|
||||
if (shakeListener != null) {
|
||||
shakeListener!!.pause()
|
||||
}
|
||||
shakeListener?.pause()
|
||||
|
||||
EventBus.getDefault().post(SleepTimerUpdatedEvent.cancelled())
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,7 @@ internal class PlaybackVolumeUpdater {
|
|||
volumeAdaptionSetting: VolumeAdaptionSetting) {
|
||||
val playable = mediaPlayer.getPlayable()
|
||||
|
||||
if (playable is FeedMedia) {
|
||||
updateFeedMediaVolumeIfNecessary(mediaPlayer, feedId, volumeAdaptionSetting, playable)
|
||||
}
|
||||
if (playable is FeedMedia) updateFeedMediaVolumeIfNecessary(mediaPlayer, feedId, volumeAdaptionSetting, playable)
|
||||
}
|
||||
|
||||
private fun updateFeedMediaVolumeIfNecessary(mediaPlayer: PlaybackServiceMediaPlayer, feedId: Long,
|
||||
|
@ -21,9 +19,7 @@ internal class PlaybackVolumeUpdater {
|
|||
val preferences = feedMedia.item!!.feed!!.preferences
|
||||
if (preferences != null) preferences.volumeAdaptionSetting = volumeAdaptionSetting
|
||||
|
||||
if (mediaPlayer.playerStatus == PlayerStatus.PLAYING) {
|
||||
forceUpdateVolume(mediaPlayer)
|
||||
}
|
||||
if (mediaPlayer.playerStatus == PlayerStatus.PLAYING) forceUpdateVolume(mediaPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ class QuickSettingsTileService : TileService() {
|
|||
super.onClick()
|
||||
val intent = Intent(this, MediaButtonReceiver::class.java)
|
||||
intent.setAction(MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER)
|
||||
intent.putExtra(Intent.EXTRA_KEY_EVENT,
|
||||
KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
intent.putExtra(Intent.EXTRA_KEY_EVENT, KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
|
@ -47,11 +46,8 @@ class QuickSettingsTileService : TileService() {
|
|||
if (qsTile == null) {
|
||||
Log.d(TAG, "Ignored call to update QS tile: getQsTile() returned null.")
|
||||
} else {
|
||||
val isPlaying = (PlaybackService.isRunning
|
||||
&& PlaybackPreferences.currentPlayerStatus
|
||||
== PlaybackPreferences.PLAYER_STATUS_PLAYING)
|
||||
qsTile.state =
|
||||
if (isPlaying) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
|
||||
val isPlaying = (PlaybackService.isRunning && PlaybackPreferences.currentPlayerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING)
|
||||
qsTile.state = if (isPlaying) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
|
||||
qsTile.updateTile()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,8 @@ internal class ShakeListener(private val mContext: Context, private val mSleepTi
|
|||
// only a precaution, the user should actually not be able to activate shake to reset
|
||||
// when the accelerometer is not available
|
||||
mSensorMgr = mContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
if (mSensorMgr == null) {
|
||||
throw UnsupportedOperationException("Sensors not supported")
|
||||
}
|
||||
if (mSensorMgr == null) throw UnsupportedOperationException("Sensors not supported")
|
||||
|
||||
mAccelerometer = mSensorMgr!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
||||
if (!mSensorMgr!!.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI)) { // if not supported
|
||||
mSensorMgr!!.unregisterListener(this)
|
||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnceOrAsk
|
|||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
||||
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||
import ac.mdiq.podcini.preferences.ThemeSwitcher.getNoTitleTheme
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.backButtonOpensDrawer
|
||||
|
@ -27,6 +28,7 @@ import ac.mdiq.podcini.util.event.FeedUpdateRunningEvent
|
|||
import ac.mdiq.podcini.util.event.MessageEvent
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
|
@ -53,6 +55,8 @@ import androidx.drawerlayout.widget.DrawerLayout
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentContainerView
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaController
|
||||
import androidx.media3.session.SessionToken
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
|
@ -62,6 +66,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
|
@ -463,6 +468,17 @@ class MainActivity : CastEnabledActivity() {
|
|||
super.onStart()
|
||||
EventBus.getDefault().register(this)
|
||||
RatingDialog.init(this)
|
||||
|
||||
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
|
||||
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
|
||||
controllerFuture.addListener({
|
||||
// Call controllerFuture.get() to retrieve the MediaController.
|
||||
// MediaController implements the Player interface, so it can be
|
||||
// attached to the PlayerView UI component.
|
||||
// playerView.setPlayer(controllerFuture.get())
|
||||
},
|
||||
MoreExecutors.directExecutor()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -104,10 +104,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
private var currentMedia: Playable? = null
|
||||
private var currentitem: FeedItem? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = AudioplayerFragmentBinding.inflate(inflater)
|
||||
|
||||
|
@ -143,8 +140,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
.replace(R.id.playerFragment2, playerFragment2!!, InternalPlayerFragment.TAG)
|
||||
.commit()
|
||||
playerView2 = binding.root.findViewById(R.id.playerFragment2)
|
||||
playerView2.setBackgroundColor(
|
||||
SurfaceColors.getColorForElevation(requireContext(), 8 * resources.displayMetrics.density))
|
||||
playerView2.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8 * resources.displayMetrics.density))
|
||||
|
||||
cardViewSeek = binding.cardViewSeek
|
||||
txtvSeek = binding.txtvSeek
|
||||
|
@ -188,9 +184,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPlaybackServiceChanged(event: PlaybackServiceEvent) {
|
||||
if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) {
|
||||
if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN)
|
||||
(activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
||||
|
||||
// private fun setupLengthTextView() {
|
||||
|
@ -223,9 +218,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
disposable = Maybe.create<Playable> { emitter: MaybeEmitter<Playable?> ->
|
||||
val media: Playable? = theMedia
|
||||
if (media != null) {
|
||||
if (includingChapters) {
|
||||
ChapterUtils.loadChapters(media, requireContext(), false)
|
||||
}
|
||||
if (includingChapters) ChapterUtils.loadChapters(media, requireContext(), false)
|
||||
emitter.onSuccess(media)
|
||||
} else {
|
||||
emitter.onComplete()
|
||||
|
@ -238,9 +231,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
updateUi(media)
|
||||
playerFragment1?.updateUi(media)
|
||||
playerFragment2?.updateUi(media)
|
||||
if (!includingChapters) {
|
||||
loadMediaInfo(true)
|
||||
}
|
||||
if (!includingChapters) loadMediaInfo(true)
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) },
|
||||
{
|
||||
updateUi(null)
|
||||
|
@ -276,9 +267,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Suppress("unused")
|
||||
fun sleepTimerUpdate(event: SleepTimerUpdatedEvent) {
|
||||
if (event.isCancelled || event.wasJustEnabled()) {
|
||||
this@AudioPlayerFragment.loadMediaInfo(false)
|
||||
}
|
||||
if (event.isCancelled || event.wasJustEnabled()) this@AudioPlayerFragment.loadMediaInfo(false)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -354,15 +343,12 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
// updateUi(controller!!.getMedia())
|
||||
// sbPosition.highlightCurrentChapter()
|
||||
// }
|
||||
txtvSeek.text = controller!!.getMedia()?.getChapters()?.get(newChapterIndex)?.title ?: (""
|
||||
+ "\n" + Converter.getDurationStringLong(position))
|
||||
txtvSeek.text = controller!!.getMedia()?.getChapters()?.get(newChapterIndex)?.title ?: ("\n${Converter.getDurationStringLong(position)}")
|
||||
} else {
|
||||
txtvSeek.text = Converter.getDurationStringLong(position)
|
||||
}
|
||||
}
|
||||
duration != controller!!.duration -> {
|
||||
updateUi(controller!!.getMedia())
|
||||
}
|
||||
duration != controller!!.duration -> updateUi(controller!!.getMedia())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,9 +382,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
|
||||
fun setupOptionsMenu(media: Playable?) {
|
||||
if (toolbar.menu.size() == 0) {
|
||||
toolbar.inflateMenu(R.menu.mediaplayer)
|
||||
}
|
||||
if (toolbar.menu.size() == 0) toolbar.inflateMenu(R.menu.mediaplayer)
|
||||
|
||||
val isFeedMedia = media is FeedMedia
|
||||
toolbar.menu?.findItem(R.id.open_feed_item)?.setVisible(isFeedMedia)
|
||||
|
@ -422,9 +406,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
var feedItem = currentitem
|
||||
if (feedItem == null && media is FeedMedia) feedItem = media.item
|
||||
// feedItem: FeedItem? = if (media is FeedMedia) media.item else null
|
||||
if (feedItem != null && FeedItemMenuHandler.onMenuItemClicked(this, menuItem.itemId, feedItem)) {
|
||||
return true
|
||||
}
|
||||
if (feedItem != null && FeedItemMenuHandler.onMenuItemClicked(this, menuItem.itemId, feedItem)) return true
|
||||
|
||||
val itemId = menuItem.itemId
|
||||
when (itemId) {
|
||||
|
@ -510,8 +492,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
private var disposable: Disposable? = null
|
||||
|
||||
@UnstableApi
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = InternalPlayerFragmentBinding.inflate(inflater)
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
|
||||
|
@ -610,8 +591,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
}
|
||||
butFF.setOnLongClickListener {
|
||||
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
||||
SkipPreferenceDialog.showSkipPreference(requireContext(), SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
||||
true
|
||||
}
|
||||
butSkip.setOnClickListener {
|
||||
|
@ -630,9 +610,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
@OptIn(UnstableApi::class) private fun setupLengthTextView() {
|
||||
showTimeLeft = UserPreferences.shouldShowRemainingTime()
|
||||
txtvLength.setOnClickListener(View.OnClickListener {
|
||||
if (controller == null) {
|
||||
return@OnClickListener
|
||||
}
|
||||
if (controller == null) return@OnClickListener
|
||||
showTimeLeft = !showTimeLeft
|
||||
UserPreferences.setShowRemainTimeSetting(showTimeLeft)
|
||||
onPositionObserverUpdate(PlaybackPositionEvent(controller!!.position, controller!!.duration))
|
||||
|
@ -649,9 +627,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
@UnstableApi
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPositionObserverUpdate(event: PlaybackPositionEvent) {
|
||||
if (controller == null || controller!!.position == Playable.INVALID_TIME || controller!!.duration == Playable.INVALID_TIME) {
|
||||
return
|
||||
}
|
||||
if (controller == null || controller!!.position == Playable.INVALID_TIME || controller!!.duration == Playable.INVALID_TIME) return
|
||||
|
||||
val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier)
|
||||
val currentPosition: Int = converter.convert(event.position)
|
||||
val duration: Int = converter.convert(event.duration)
|
||||
|
@ -684,12 +661,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPlaybackServiceChanged(event: PlaybackServiceEvent) {
|
||||
when (event.action) {
|
||||
PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN -> {
|
||||
(activity as MainActivity).setPlayerVisible(false)
|
||||
}
|
||||
PlaybackServiceEvent.Action.SERVICE_STARTED -> {
|
||||
(activity as MainActivity).setPlayerVisible(true)
|
||||
}
|
||||
PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN -> (activity as MainActivity).setPlayerVisible(false)
|
||||
PlaybackServiceEvent.Action.SERVICE_STARTED -> (activity as MainActivity).setPlayerVisible(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,9 +676,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
super.onStart()
|
||||
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
||||
if (UserPreferences.speedforwardSpeed > 0.1f) {
|
||||
txtvSkip.text = NumberFormat.getInstance().format(UserPreferences.speedforwardSpeed)
|
||||
} else txtvSkip.visibility = View.GONE
|
||||
if (UserPreferences.speedforwardSpeed > 0.1f) txtvSkip.text = NumberFormat.getInstance().format(UserPreferences.speedforwardSpeed)
|
||||
else txtvSkip.visibility = View.GONE
|
||||
val media = controller?.getMedia() ?: return
|
||||
updatePlaybackSpeedButton(SpeedChangedEvent(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media)))
|
||||
}
|
||||
|
|
|
@ -288,4 +288,9 @@
|
|||
|
||||
* reader mode of episode home view observes the theme of the app
|
||||
* reader mode content of episode home view is cached so that subsequent loading is quicker
|
||||
* episode home reader content can be switched on in player detailed view from the action bar
|
||||
* episode home reader content can be switched on in player detailed view from the action bar
|
||||
|
||||
## 4.9.2
|
||||
|
||||
* fixed the action buttons on notification widget. bit strange with the order though as they appear different on my Android 9 and Android 14 devices
|
||||
* media3 requires quite some logic change, so be mindful with any issues
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
Version 4.9.2 brings several changes:
|
||||
|
||||
* fixed the action buttons on notification widget. bit strange with the order though as they appear different on my Android 9 and Android 14 devices
|
||||
* media3 requires quite some logic change, so be mindful with any issues
|
Loading…
Reference in New Issue