add media session & update exo player #41
|
@ -11,7 +11,7 @@ android {
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 4180 //00.04.100
|
versionCode 4180 //00.04.100
|
||||||
versionName "0.4.2-alpha1"
|
versionName "0.4.2-alpha2"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resValue "string", "build_time", buildTime()
|
resValue "string", "build_time", buildTime()
|
||||||
|
@ -55,10 +55,11 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
implementation 'com.google.code.gson:gson:2.8.7'
|
implementation 'com.google.code.gson:gson:2.8.7'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3'
|
implementation 'com.google.android.exoplayer:exoplayer-core:2.14.0'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.3'
|
implementation 'com.google.android.exoplayer:exoplayer-hls:2.14.0'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.3'
|
implementation 'com.google.android.exoplayer:exoplayer-dash:2.14.0'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
|
implementation 'com.google.android.exoplayer:exoplayer-ui:2.14.0'
|
||||||
|
implementation 'com.google.android.exoplayer:extension-mediasession:2.14.0'
|
||||||
|
|
||||||
implementation 'org.jsoup:jsoup:1.13.1'
|
implementation 'org.jsoup:jsoup:1.13.1'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||||
|
|
|
@ -26,9 +26,7 @@ import com.google.android.exoplayer2.ui.StyledPlayerControlView
|
||||||
import com.google.android.exoplayer2.util.Util
|
import com.google.android.exoplayer2.util.Util
|
||||||
import kotlinx.android.synthetic.main.activity_player.*
|
import kotlinx.android.synthetic.main.activity_player.*
|
||||||
import kotlinx.android.synthetic.main.player_controls.*
|
import kotlinx.android.synthetic.main.player_controls.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.preferences.Preferences
|
import org.mosad.teapod.preferences.Preferences
|
||||||
import org.mosad.teapod.ui.components.EpisodesListPlayer
|
import org.mosad.teapod.ui.components.EpisodesListPlayer
|
||||||
|
@ -187,7 +185,7 @@ class PlayerActivity : AppCompatActivity() {
|
||||||
* set play when ready and listeners
|
* set play when ready and listeners
|
||||||
*/
|
*/
|
||||||
private fun initExoPlayer() {
|
private fun initExoPlayer() {
|
||||||
model.player.addListener(object : Player.EventListener {
|
model.player.addListener(object : Player.Listener {
|
||||||
override fun onPlaybackStateChanged(state: Int) {
|
override fun onPlaybackStateChanged(state: Int) {
|
||||||
super.onPlaybackStateChanged(state)
|
super.onPlaybackStateChanged(state)
|
||||||
|
|
||||||
|
@ -208,7 +206,7 @@ class PlayerActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// start playing the current episode, after all needed player components have been initialized
|
// start playing the current episode, after all needed player components have been initialized
|
||||||
model.playEpisode(model.currentEpisode, true)
|
model.playEpisode(model.currentEpisode, true)
|
||||||
}
|
}
|
||||||
|
@ -256,30 +254,26 @@ class PlayerActivity : AppCompatActivity() {
|
||||||
|
|
||||||
timerUpdates = Timer().scheduleAtFixedRate(0, 500) {
|
timerUpdates = Timer().scheduleAtFixedRate(0, 500) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
var btnNextEpIsVisible: Boolean
|
val btnNextEpIsVisible = button_next_ep.isVisible
|
||||||
var controlsVisible: Boolean
|
val controlsVisible = controller.isVisible
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
if (model.player.duration > 0) {
|
||||||
if (model.player.duration > 0) {
|
remainingTime = model.player.duration - model.player.currentPosition
|
||||||
remainingTime = model.player.duration - model.player.currentPosition
|
remainingTime = if (remainingTime < 0) 0 else remainingTime
|
||||||
remainingTime = if (remainingTime < 0) 0 else remainingTime
|
|
||||||
}
|
|
||||||
btnNextEpIsVisible = button_next_ep.isVisible
|
|
||||||
controlsVisible = controller.isVisible
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingTime in 1..20000) {
|
if (remainingTime in 1..20000) {
|
||||||
// if the next ep button is not visible, make it visible. Don't show in pip mode
|
// if the next ep button is not visible, make it visible. Don't show in pip mode
|
||||||
if (!btnNextEpIsVisible && model.nextEpisode != null && Preferences.autoplay && !isInPiPMode()) {
|
if (!btnNextEpIsVisible && model.nextEpisode != null && Preferences.autoplay && !isInPiPMode()) {
|
||||||
withContext(Dispatchers.Main) { showButtonNextEp() }
|
showButtonNextEp()
|
||||||
}
|
}
|
||||||
} else if (btnNextEpIsVisible) {
|
} else if (btnNextEpIsVisible) {
|
||||||
withContext(Dispatchers.Main) { hideButtonNextEp() }
|
hideButtonNextEp()
|
||||||
}
|
}
|
||||||
|
|
||||||
// if controls are visible, update them
|
// if controls are visible, update them
|
||||||
if (controlsVisible) {
|
if (controlsVisible) {
|
||||||
withContext(Dispatchers.Main) { updateControls() }
|
updateControls()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,15 @@ package org.mosad.teapod.ui.activity.player
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.android.exoplayer2.C
|
import com.google.android.exoplayer2.C
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||||
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
|
||||||
|
@ -23,6 +26,7 @@ import org.mosad.teapod.util.Media
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PlayerViewModel handles all stuff related to media/episodes.
|
* PlayerViewModel handles all stuff related to media/episodes.
|
||||||
* When currentEpisode is changed the player will start playing it (not initial media),
|
* When currentEpisode is changed the player will start playing it (not initial media),
|
||||||
|
@ -31,10 +35,13 @@ import kotlin.collections.ArrayList
|
||||||
class PlayerViewModel(application: Application) : AndroidViewModel(application) {
|
class PlayerViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
val player = SimpleExoPlayer.Builder(application).build()
|
val player = SimpleExoPlayer.Builder(application).build()
|
||||||
val dataSourceFactory = DefaultDataSourceFactory(application, Util.getUserAgent(application, "Teapod"))
|
private val dataSourceFactory = DefaultDataSourceFactory(application, Util.getUserAgent(application, "Teapod"))
|
||||||
|
private val mediaSession = MediaSessionCompat(application, "TEAPOD_PLAYER_SESSION")
|
||||||
|
|
||||||
|
lateinit var mStateBuilder: PlaybackStateCompat
|
||||||
|
|
||||||
val currentEpisodeChangedListener = ArrayList<() -> Unit>()
|
val currentEpisodeChangedListener = ArrayList<() -> Unit>()
|
||||||
val preferredLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN
|
private val preferredLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN
|
||||||
|
|
||||||
var media: Media = Media(-1, "", DataTypes.MediaType.OTHER)
|
var media: Media = Media(-1, "", DataTypes.MediaType.OTHER)
|
||||||
internal set
|
internal set
|
||||||
|
@ -45,13 +52,30 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
var currentLanguage: Locale = Locale.ROOT
|
var currentLanguage: Locale = Locale.ROOT
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
|
init {
|
||||||
|
initMediaSession()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
|
||||||
|
mediaSession.release()
|
||||||
player.release()
|
player.release()
|
||||||
|
|
||||||
Log.d(javaClass.name, "Released player")
|
Log.d(javaClass.name, "Released player")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the media session to active
|
||||||
|
* create a media session connector to set title and description
|
||||||
|
*/
|
||||||
|
private fun initMediaSession() {
|
||||||
|
val mediaSessionConnector = MediaSessionConnector(mediaSession)
|
||||||
|
mediaSessionConnector.setPlayer(player)
|
||||||
|
|
||||||
|
mediaSession.isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
fun loadMedia(mediaId: Int, episodeId: Int) {
|
fun loadMedia(mediaId: Int, episodeId: Int) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
media = AoDParser.getMediaById(mediaId)
|
media = AoDParser.getMediaById(mediaId)
|
||||||
|
@ -115,6 +139,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* change the players media source and start playback
|
||||||
|
*/
|
||||||
fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) {
|
fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) {
|
||||||
if (replace || player.contentDuration == C.TIME_UNSET) {
|
if (replace || player.contentDuration == C.TIME_UNSET) {
|
||||||
player.setMediaSource(source)
|
player.setMediaSource(source)
|
||||||
|
|
Loading…
Reference in New Issue