diff --git a/app/src/main/java/org/mosad/teapod/PlayerActivity.kt b/app/src/main/java/org/mosad/teapod/PlayerActivity.kt index cacaf31..35e65b1 100644 --- a/app/src/main/java/org/mosad/teapod/PlayerActivity.kt +++ b/app/src/main/java/org/mosad/teapod/PlayerActivity.kt @@ -8,6 +8,7 @@ import android.util.Log import android.view.* import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GestureDetectorCompat +import androidx.core.view.isVisible import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player @@ -19,6 +20,7 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.util.Util import kotlinx.android.synthetic.main.activity_player.* import kotlinx.android.synthetic.main.player_controls.* +import kotlinx.coroutines.* import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.util.DataTypes.MediaType @@ -33,8 +35,6 @@ class PlayerActivity : AppCompatActivity() { private lateinit var controller: StyledPlayerControlView private lateinit var gestureDetector: GestureDetectorCompat - private var streamUrl = "" - private var mediaId = 0 private var episodeId = 0 @@ -119,19 +119,13 @@ class PlayerActivity : AppCompatActivity() { initExoPlayer() initVideoView() initController() + initTimeUpdates() } private fun initMedia() { media = AoDParser.getMediaById(mediaId) currentEpisode = media.episodes.first { it.id == episodeId } - streamUrlFromEp(currentEpisode) // get current stream - - // get next episode if present - val nextEpIndex = media.episodes.indexOfFirst { it.id == episodeId } + 1 - if (nextEpIndex < (media.episodes.size - 1)) { - println("has next episode") - nextEpisode = media.episodes[nextEpIndex] - } + nextEpisode = selectNextEpisode() } private fun initExoPlayer() { @@ -140,7 +134,7 @@ class PlayerActivity : AppCompatActivity() { controller = video_view.findViewById(R.id.exo_controller) val mediaSource = HlsMediaSource.Factory(dataSourceFactory) - .createMediaSource(MediaItem.fromUri(Uri.parse(streamUrl))) + .createMediaSource(MediaItem.fromUri(Uri.parse(selectStream(currentEpisode)))) player.playWhenReady = playWhenReady player.setMediaSource(mediaSource) @@ -162,6 +156,11 @@ class PlayerActivity : AppCompatActivity() { View.VISIBLE -> View.INVISIBLE else -> View.VISIBLE } + + if (state == ExoPlayer.STATE_ENDED && nextEpisode != null) { + playNextEpisode() + } + } }) } @@ -198,6 +197,7 @@ class PlayerActivity : AppCompatActivity() { } else { getString(R.string.time_hour_min_sec, hours, minutes, seconds) } + } exo_text_title.text = currentEpisode.title // set media title @@ -207,6 +207,26 @@ class PlayerActivity : AppCompatActivity() { exo_close_player.setOnClickListener { this.finish() } exo_rew_10.setOnClickListener { rewind() } exo_ffwd_10.setOnClickListener { forward() } + button_next_ep.setOnClickListener { playNextEpisode() } + } + + private fun initTimeUpdates() = GlobalScope.launch { + while (true) { + val remainingTime = withContext(Dispatchers.Main) { + player.duration - player.currentPosition + } + + if (remainingTime in 0..20000) { + withContext(Dispatchers.Main) { + // if the next ep button is not visible, make it visible + if (!button_next_ep.isVisible) { + button_next_ep.visibility = View.VISIBLE // TODO animation + } + } + } + + delay(1000) + } } private fun releasePlayer(){ @@ -226,11 +246,23 @@ class PlayerActivity : AppCompatActivity() { player.seekTo(player.currentPosition + fwdTime) } - @Suppress("unused") private fun playNextEpisode() { - nextEpisode?.let { streamUrlFromEp(it) } - // TODO play - // TODO set next episode if present + nextEpisode?.let { nextEp -> + currentEpisode = nextEp // set current ep to next ep + episodeId = nextEp.id + + // update the gui + exo_text_title.text = nextEp.title + button_next_ep.visibility = View.GONE // TODO animation + + player.clearMediaItems() //remove previous item + val mediaSource = HlsMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(selectStream(nextEp)))) + player.setMediaSource(mediaSource) + player.prepare() + + nextEpisode = selectNextEpisode() + } } /** @@ -238,18 +270,30 @@ class PlayerActivity : AppCompatActivity() { * use the secondary stream. Else, if the primary stream is set use the primary stream. * If no stream is present, close the activity. */ - private fun streamUrlFromEp(episode: Episode) { - streamUrl = if ((Preferences.preferSecondary || episode.priStreamUrl.isEmpty()) && episode.secStreamOmU) { + private fun selectStream(episode: Episode): String { + return if ((Preferences.preferSecondary || episode.priStreamUrl.isEmpty()) && episode.secStreamOmU) { episode.secStreamUrl } else if (episode.priStreamUrl.isNotEmpty()) { episode.priStreamUrl } else { Log.e(javaClass.name, "No stream url set.") this.finish() - return + "" } } + /** + * Based on the current episodeId, get the next episode. If there is no next + * episode, return null + */ + private fun selectNextEpisode(): Episode? { + val nextEpIndex = media.episodes.indexOfFirst { it.id == currentEpisode.id } + 1 + return if (nextEpIndex < (media.episodes.size)) { + media.episodes[nextEpIndex] + } else { + null + } + } /** * hide the status and navigation bar diff --git a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt index c625aed..588e930 100644 --- a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt +++ b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt @@ -73,6 +73,7 @@ object AoDParser { val resLogin = Jsoup.connect(baseUrl + loginPath) .method(Connection.Method.POST) + .timeout(60000) // login can take some time .data(data) .postDataCharset("UTF-8") .cookies(authCookies) diff --git a/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt b/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt index ea77f36..6ffd51c 100644 --- a/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt @@ -17,7 +17,6 @@ import kotlinx.android.synthetic.main.fragment_media.* import org.mosad.teapod.MainActivity import org.mosad.teapod.R import org.mosad.teapod.parser.AoDParser -import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.util.DataTypes.MediaType import org.mosad.teapod.util.Episode import org.mosad.teapod.util.Media diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml index b550802..c814052 100644 --- a/app/src/main/res/layout/activity_player.xml +++ b/app/src/main/res/layout/activity_player.xml @@ -24,6 +24,23 @@ android:layout_width="70dp" android:layout_height="70dp" android:layout_gravity="center" + app:indicatorColor="@color/exo_white" tools:visibility="visible" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 6da3fdf..c28e8e6 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -35,6 +35,8 @@ 10 Sekunden zurück Abspielen/Pause 10 Sekunden vorwärts + Nächste Episode + speichern @android:string/cancel diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 430fde1..2ccc748 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,6 +46,7 @@ forward 10 seconds %1$02d:%2$02d %1$d:%2$02d:%3$02d + Next Episode save