diff --git a/app/build.gradle b/app/build.gradle
index adbf60d..96a8791 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -50,4 +50,5 @@ dependencies {
implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'
implementation 'androidx.media3:media3-ui:1.1.1'
implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
+ implementation "androidx.media3:media3-session:1.1.1"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2371a50..7721137 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt b/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt
index b804471..ba68b37 100644
--- a/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt
+++ b/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt
@@ -6,22 +6,32 @@ import android.os.Bundle
import android.os.Handler
import android.view.Menu
import android.view.MenuItem
+import android.view.View
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import androidx.transition.Visibility
import com.google.android.material.navigation.NavigationView
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_main.drawer_layout
import kotlinx.android.synthetic.main.activity_main.nav_view
import kotlinx.android.synthetic.main.app_bar_main.toolbar
+import kotlinx.android.synthetic.main.content_main.mini
import kotlinx.android.synthetic.main.content_main.swipeContainer
+import kotlinx.android.synthetic.main.mini_player.mini_play_pause
+import kotlinx.android.synthetic.main.mini_player.mini_player
+import kotlinx.android.synthetic.main.mini_player.mini_player_author
+import kotlinx.android.synthetic.main.mini_player.mini_player_image
+import kotlinx.android.synthetic.main.mini_player.mini_player_title
import kotlinx.android.synthetic.main.nav_header_main.*
+import kotlinx.android.synthetic.main.view_video.view.thumb
import org.libre.agosto.p2play.adapters.VideosAdapter
import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.models.VideoModel
+import org.libre.agosto.p2play.singletons.PlaybackSingleton
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var recyclerView: RecyclerView
@@ -34,7 +44,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
var section: String = ""
var searchVal: String = ""
var pagination = 0
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
@@ -62,6 +71,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
this.refresh()
}
+ mini_player_image.setOnClickListener { this.resumeVideo() }
+ mini_player_title.setOnClickListener { this.resumeVideo() }
+ mini_player_author.setOnClickListener { this.resumeVideo() }
+ mini.setOnClickListener { this.resumeVideo() }
+ mini_play_pause.setOnClickListener { this.playPausePlayer() }
+
Handler().postDelayed({
// Title for nav_bar
side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
@@ -347,6 +362,23 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
override fun onResume() {
super.onResume()
setSideData()
+
+ if (PlaybackSingleton.player != null && PlaybackSingleton.player!!.isPlaying) {
+ PlaybackSingleton.runMediaSession(this)
+ mini_player_title.text = PlaybackSingleton.video!!.name
+ mini_player_author.text = PlaybackSingleton.video!!.username
+ Picasso.get().load("https://${ManagerSingleton.url}${PlaybackSingleton.video!!.thumbUrl}").into(mini_player_image)
+ mini.visibility = View.VISIBLE
+ } else {
+ mini.visibility = View.GONE
+ }
+ }
+
+ override fun onDestroy() {
+ if (PlaybackSingleton.player != null) {
+ PlaybackSingleton.release()
+ }
+ super.onDestroy()
}
private fun setSideData() {
@@ -423,4 +455,22 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
}
}
}
+
+ private fun resumeVideo () {
+ val intent = Intent(this, ReproductorActivity::class.java)
+ intent.putExtra("resume", true)
+ startActivity(intent)
+ }
+
+ private fun playPausePlayer () {
+ PlaybackSingleton.player?.let {
+ if (it.isPlaying) {
+ it.pause()
+ mini_play_pause.setImageResource(R.drawable.ic_play_arrow_24)
+ } else {
+ it.play()
+ mini_play_pause.setImageResource(R.drawable.ic_pause_24)
+ }
+ }
+ }
}
diff --git a/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt b/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt
index 41fb856..1a45138 100644
--- a/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt
+++ b/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
+import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.os.Looper
@@ -18,9 +19,11 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaMetadata
import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.upstream.DefaultAllocator
+import androidx.media3.session.MediaController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
@@ -32,11 +35,13 @@ import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.helpers.setFullscreen
import org.libre.agosto.p2play.models.CommentaryModel
import org.libre.agosto.p2play.models.VideoModel
+import org.libre.agosto.p2play.singletons.PlaybackSingleton
@Suppress("NAME_SHADOWING")
class ReproductorActivity : AppCompatActivity() {
private val clientVideo: Videos = Videos()
lateinit var video: VideoModel
+ lateinit var videoPlayback: VideoModel
private val actions: Actions = Actions()
private val client: Comments = Comments()
private val videos: Videos = Videos()
@@ -49,9 +54,14 @@ class ReproductorActivity : AppCompatActivity() {
// Exoplayer
private lateinit var player: ExoPlayer
+ private lateinit var mediaControl: MediaController
+
// Fullscreen info
private var isFullscreen = false
+ // Resume info
+ private var isResume = false
+
@SuppressLint("SetJavaScriptEnabled", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -70,7 +80,15 @@ class ReproductorActivity : AppCompatActivity() {
videoView.settings.domStorageEnabled = true
try {
- video = this.intent.extras?.getSerializable("video") as VideoModel
+ val resume = this.intent.extras?.getSerializable("resume")
+ if (resume == null) {
+ video = this.intent.extras?.getSerializable("video") as VideoModel
+ isResume = false
+ } else {
+ video = PlaybackSingleton.video!!
+ isResume = true
+ }
+
tittleVideoTxt.text = this.video.name
viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}"
userTxt.text = this.video.username
@@ -113,7 +131,7 @@ class ReproductorActivity : AppCompatActivity() {
}
AsyncTask.execute {
- val video = this.clientVideo.getVideo(this.video.uuid)
+ videoPlayback = this.clientVideo.getVideo(this.video.uuid)
// TODO: Make this configurable
val bufferSize = 1024 * 1024 // 1mb
val allocator = DefaultAllocator(true, bufferSize)
@@ -123,19 +141,30 @@ class ReproductorActivity : AppCompatActivity() {
runOnUiThread {
try {
- player = ExoPlayer.Builder(this.baseContext)
- .setSeekBackIncrementMs(10000)
- .setSeekForwardIncrementMs(10000)
- .setLoadControl(loadControl).build()
+ if (PlaybackSingleton.player == null || !PlaybackSingleton.player!!.playWhenReady) {
+ PlaybackSingleton.player = ExoPlayer.Builder(this.baseContext)
+ .setSeekBackIncrementMs(10000)
+ .setSeekForwardIncrementMs(10000)
+ .setLoadControl(loadControl).build()
+ }
+ player = PlaybackSingleton.player!!
exoPlayer.player = player
println("----- video --------")
- println(video.streamingData?.playlistUrl)
- val mediaItem = MediaItem.fromUri(video.streamingData?.playlistUrl!!)
- // Set the media item to be played.
- player.setMediaItem(mediaItem)
- // Prepare the player.
- player.prepare()
+ println(videoPlayback.streamingData?.playlistUrl)
+
+ if (!isResume) {
+ val mediaItem = MediaItem.Builder()
+ .setUri(videoPlayback.streamingData?.playlistUrl!!)
+ .setMediaMetadata(
+ MediaMetadata.Builder()
+ .setArtist(videoPlayback.username)
+ .setTitle(videoPlayback.name)
+ .setArtworkUri(Uri.parse("https://${ManagerSingleton.url}${videoPlayback.thumbUrl}"))
+ .build(),
+ ).build()
+ PlaybackSingleton.setData(mediaItem, video)
+ }
// Start the playback.
// player.play()
} catch (err: Exception) {
@@ -392,7 +421,9 @@ class ReproductorActivity : AppCompatActivity() {
}
override fun onDestroy() {
- player.release()
+ if (!player.isPlaying) {
+ PlaybackSingleton.release()
+ }
super.onDestroy()
}
diff --git a/app/src/main/java/org/libre/agosto/p2play/models/VideoModel.kt b/app/src/main/java/org/libre/agosto/p2play/models/VideoModel.kt
index 5955616..8bc594e 100644
--- a/app/src/main/java/org/libre/agosto/p2play/models/VideoModel.kt
+++ b/app/src/main/java/org/libre/agosto/p2play/models/VideoModel.kt
@@ -71,14 +71,14 @@ class VideoModel(
data.endArray()
}
"files" -> {
+ data.beginArray()
if (streamingData === null) {
- data.beginArray()
if (data.hasNext()) {
data.beginObject()
while (data.hasNext()) {
val key2 = data.nextName()
when (key2.toString()) {
- "fileDownloadUrl" -> {
+ "fileUrl" -> {
streamingData = StreamingModel()
streamingData!!.playlistUrl = data.nextString()
}
@@ -87,8 +87,11 @@ class VideoModel(
}
data.endObject()
}
- data.endArray()
+ while (data.hasNext()) {
+ data.skipValue()
+ }
}
+ data.endArray()
}
"channel" -> {
data.beginObject()
diff --git a/app/src/main/java/org/libre/agosto/p2play/services/PlaybackService.kt b/app/src/main/java/org/libre/agosto/p2play/services/PlaybackService.kt
new file mode 100644
index 0000000..bc13c2e
--- /dev/null
+++ b/app/src/main/java/org/libre/agosto/p2play/services/PlaybackService.kt
@@ -0,0 +1,47 @@
+package org.libre.agosto.p2play.services
+
+import android.app.PendingIntent
+import android.content.Intent
+import androidx.media3.session.MediaSession
+import androidx.media3.session.MediaSessionService
+import org.libre.agosto.p2play.ReproductorActivity
+import org.libre.agosto.p2play.singletons.PlaybackSingleton
+
+class PlaybackService : MediaSessionService() {
+ private var mediaSession: MediaSession? = null
+
+ // Create your Player and MediaSession in the onCreate lifecycle event
+ override fun onCreate() {
+ super.onCreate()
+ val player = PlaybackSingleton.player!!
+ mediaSession = MediaSession.Builder(this, player)
+ .build()
+ val contentIntent = Intent(this, ReproductorActivity::class.java)
+ contentIntent.putExtra("resume", true)
+ val pendingIntent = PendingIntent.getActivity(
+ this,
+ 0,
+ contentIntent,
+ PendingIntent.FLAG_MUTABLE,
+ )
+ mediaSession!!.setSessionActivity(pendingIntent)
+ }
+
+ // Remember to release the player and media session in onDestroy
+ override fun onDestroy() {
+ mediaSession?.run {
+ release()
+ mediaSession = null
+ }
+ super.onDestroy()
+ }
+
+ override fun onTaskRemoved(rootIntent: Intent?) {
+ this.mediaSession!!.player.stop()
+ super.onTaskRemoved(rootIntent)
+ }
+
+ override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
+ return mediaSession
+ }
+}
diff --git a/app/src/main/java/org/libre/agosto/p2play/singletons/PlaybackSingleton.kt b/app/src/main/java/org/libre/agosto/p2play/singletons/PlaybackSingleton.kt
new file mode 100644
index 0000000..c63537c
--- /dev/null
+++ b/app/src/main/java/org/libre/agosto/p2play/singletons/PlaybackSingleton.kt
@@ -0,0 +1,52 @@
+package org.libre.agosto.p2play.singletons
+
+import android.content.ComponentName
+import android.content.Context
+import android.util.Log
+import androidx.media3.common.MediaItem
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.session.MediaController
+import androidx.media3.session.SessionToken
+import com.google.common.util.concurrent.MoreExecutors
+import org.libre.agosto.p2play.models.VideoModel
+import org.libre.agosto.p2play.services.PlaybackService
+
+object PlaybackSingleton {
+ var player: ExoPlayer? = null
+ var video: VideoModel? = null
+ private var withMediaSession = false
+
+ fun setData(mediaItem: MediaItem, video: VideoModel): ExoPlayer? {
+ player?.let {
+ if (it.isPlaying) {
+ it.pause()
+ }
+ it.setMediaItem(mediaItem)
+ it.prepare()
+ this.video = video
+ return it
+ }
+
+ return null
+ }
+
+ fun release() {
+ player?.release()
+ }
+
+ fun runMediaSession(context: Context) {
+ if (!this.withMediaSession) {
+ val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
+
+ val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
+
+ controllerFuture.addListener(
+ {
+ val med = controllerFuture.get()
+ },
+ MoreExecutors.directExecutor(),
+ )
+ this.withMediaSession = true
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_reproductor.xml b/app/src/main/res/layout/activity_reproductor.xml
index 892d06c..38b99c8 100644
--- a/app/src/main/res/layout/activity_reproductor.xml
+++ b/app/src/main/res/layout/activity_reproductor.xml
@@ -379,4 +379,5 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index 4577734..0ccecaf 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -25,4 +25,16 @@
app:layout_constraintTop_toTopOf="parent" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/mini_player.xml b/app/src/main/res/layout/mini_player.xml
new file mode 100644
index 0000000..ca3bc6f
--- /dev/null
+++ b/app/src/main/res/layout/mini_player.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 5246f13..0f3c88f 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -14,6 +14,8 @@
- @color/colorAccent
- @color/md_theme_dark_onBackground
- @color/md_theme_dark_secondary
+ - @color/md_theme_dark_tertiaryContainer
+ - @color/md_theme_dark_onTertiaryContainer