Feature/playback service
This commit is contained in:
parent
48738f100a
commit
dd54d214ff
|
@ -50,4 +50,5 @@ dependencies {
|
||||||
implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'
|
implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'
|
||||||
implementation 'androidx.media3:media3-ui:1.1.1'
|
implementation 'androidx.media3:media3-ui:1.1.1'
|
||||||
implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
|
implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
|
||||||
|
implementation "androidx.media3:media3-session:1.1.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -62,6 +64,15 @@
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/title_activity_settings"
|
android:label="@string/title_activity_settings"
|
||||||
android:theme="@style/Theme.P2play" />
|
android:theme="@style/Theme.P2play" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".services.PlaybackService"
|
||||||
|
android:foregroundServiceType="mediaPlayback"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="androidx.media3.session.MediaSessionService"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -6,22 +6,32 @@ import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.transition.Visibility
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.android.synthetic.main.activity_main.drawer_layout
|
import kotlinx.android.synthetic.main.activity_main.drawer_layout
|
||||||
import kotlinx.android.synthetic.main.activity_main.nav_view
|
import kotlinx.android.synthetic.main.activity_main.nav_view
|
||||||
import kotlinx.android.synthetic.main.app_bar_main.toolbar
|
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.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.nav_header_main.*
|
||||||
|
import kotlinx.android.synthetic.main.view_video.view.thumb
|
||||||
import org.libre.agosto.p2play.adapters.VideosAdapter
|
import org.libre.agosto.p2play.adapters.VideosAdapter
|
||||||
import org.libre.agosto.p2play.ajax.Videos
|
import org.libre.agosto.p2play.ajax.Videos
|
||||||
import org.libre.agosto.p2play.models.VideoModel
|
import org.libre.agosto.p2play.models.VideoModel
|
||||||
|
import org.libre.agosto.p2play.singletons.PlaybackSingleton
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
|
@ -34,7 +44,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
var section: String = ""
|
var section: String = ""
|
||||||
var searchVal: String = ""
|
var searchVal: String = ""
|
||||||
var pagination = 0
|
var pagination = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
@ -62,6 +71,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
this.refresh()
|
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({
|
Handler().postDelayed({
|
||||||
// Title for nav_bar
|
// Title for nav_bar
|
||||||
side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
setSideData()
|
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() {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
@ -18,9 +19,11 @@ import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.MediaMetadata
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl
|
import androidx.media3.exoplayer.DefaultLoadControl
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator
|
import androidx.media3.exoplayer.upstream.DefaultAllocator
|
||||||
|
import androidx.media3.session.MediaController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.squareup.picasso.Picasso
|
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.helpers.setFullscreen
|
||||||
import org.libre.agosto.p2play.models.CommentaryModel
|
import org.libre.agosto.p2play.models.CommentaryModel
|
||||||
import org.libre.agosto.p2play.models.VideoModel
|
import org.libre.agosto.p2play.models.VideoModel
|
||||||
|
import org.libre.agosto.p2play.singletons.PlaybackSingleton
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
class ReproductorActivity : AppCompatActivity() {
|
class ReproductorActivity : AppCompatActivity() {
|
||||||
private val clientVideo: Videos = Videos()
|
private val clientVideo: Videos = Videos()
|
||||||
lateinit var video: VideoModel
|
lateinit var video: VideoModel
|
||||||
|
lateinit var videoPlayback: VideoModel
|
||||||
private val actions: Actions = Actions()
|
private val actions: Actions = Actions()
|
||||||
private val client: Comments = Comments()
|
private val client: Comments = Comments()
|
||||||
private val videos: Videos = Videos()
|
private val videos: Videos = Videos()
|
||||||
|
@ -49,9 +54,14 @@ class ReproductorActivity : AppCompatActivity() {
|
||||||
// Exoplayer
|
// Exoplayer
|
||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
|
|
||||||
|
private lateinit var mediaControl: MediaController
|
||||||
|
|
||||||
// Fullscreen info
|
// Fullscreen info
|
||||||
private var isFullscreen = false
|
private var isFullscreen = false
|
||||||
|
|
||||||
|
// Resume info
|
||||||
|
private var isResume = false
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled", "SetTextI18n")
|
@SuppressLint("SetJavaScriptEnabled", "SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -70,7 +80,15 @@ class ReproductorActivity : AppCompatActivity() {
|
||||||
videoView.settings.domStorageEnabled = true
|
videoView.settings.domStorageEnabled = true
|
||||||
|
|
||||||
try {
|
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
|
tittleVideoTxt.text = this.video.name
|
||||||
viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}"
|
viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}"
|
||||||
userTxt.text = this.video.username
|
userTxt.text = this.video.username
|
||||||
|
@ -113,7 +131,7 @@ class ReproductorActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncTask.execute {
|
AsyncTask.execute {
|
||||||
val video = this.clientVideo.getVideo(this.video.uuid)
|
videoPlayback = this.clientVideo.getVideo(this.video.uuid)
|
||||||
// TODO: Make this configurable
|
// TODO: Make this configurable
|
||||||
val bufferSize = 1024 * 1024 // 1mb
|
val bufferSize = 1024 * 1024 // 1mb
|
||||||
val allocator = DefaultAllocator(true, bufferSize)
|
val allocator = DefaultAllocator(true, bufferSize)
|
||||||
|
@ -123,19 +141,30 @@ class ReproductorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
try {
|
try {
|
||||||
player = ExoPlayer.Builder(this.baseContext)
|
if (PlaybackSingleton.player == null || !PlaybackSingleton.player!!.playWhenReady) {
|
||||||
.setSeekBackIncrementMs(10000)
|
PlaybackSingleton.player = ExoPlayer.Builder(this.baseContext)
|
||||||
.setSeekForwardIncrementMs(10000)
|
.setSeekBackIncrementMs(10000)
|
||||||
.setLoadControl(loadControl).build()
|
.setSeekForwardIncrementMs(10000)
|
||||||
|
.setLoadControl(loadControl).build()
|
||||||
|
}
|
||||||
|
player = PlaybackSingleton.player!!
|
||||||
exoPlayer.player = player
|
exoPlayer.player = player
|
||||||
|
|
||||||
println("----- video --------")
|
println("----- video --------")
|
||||||
println(video.streamingData?.playlistUrl)
|
println(videoPlayback.streamingData?.playlistUrl)
|
||||||
val mediaItem = MediaItem.fromUri(video.streamingData?.playlistUrl!!)
|
|
||||||
// Set the media item to be played.
|
if (!isResume) {
|
||||||
player.setMediaItem(mediaItem)
|
val mediaItem = MediaItem.Builder()
|
||||||
// Prepare the player.
|
.setUri(videoPlayback.streamingData?.playlistUrl!!)
|
||||||
player.prepare()
|
.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.
|
// Start the playback.
|
||||||
// player.play()
|
// player.play()
|
||||||
} catch (err: Exception) {
|
} catch (err: Exception) {
|
||||||
|
@ -392,7 +421,9 @@ class ReproductorActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
player.release()
|
if (!player.isPlaying) {
|
||||||
|
PlaybackSingleton.release()
|
||||||
|
}
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,14 +71,14 @@ class VideoModel(
|
||||||
data.endArray()
|
data.endArray()
|
||||||
}
|
}
|
||||||
"files" -> {
|
"files" -> {
|
||||||
|
data.beginArray()
|
||||||
if (streamingData === null) {
|
if (streamingData === null) {
|
||||||
data.beginArray()
|
|
||||||
if (data.hasNext()) {
|
if (data.hasNext()) {
|
||||||
data.beginObject()
|
data.beginObject()
|
||||||
while (data.hasNext()) {
|
while (data.hasNext()) {
|
||||||
val key2 = data.nextName()
|
val key2 = data.nextName()
|
||||||
when (key2.toString()) {
|
when (key2.toString()) {
|
||||||
"fileDownloadUrl" -> {
|
"fileUrl" -> {
|
||||||
streamingData = StreamingModel()
|
streamingData = StreamingModel()
|
||||||
streamingData!!.playlistUrl = data.nextString()
|
streamingData!!.playlistUrl = data.nextString()
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,11 @@ class VideoModel(
|
||||||
}
|
}
|
||||||
data.endObject()
|
data.endObject()
|
||||||
}
|
}
|
||||||
data.endArray()
|
while (data.hasNext()) {
|
||||||
|
data.skipValue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
data.endArray()
|
||||||
}
|
}
|
||||||
"channel" -> {
|
"channel" -> {
|
||||||
data.beginObject()
|
data.beginObject()
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -379,4 +379,5 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -25,4 +25,16 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/mini"
|
||||||
|
layout="@layout/mini_player"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/androidBackgroundSecondary"
|
||||||
|
android:elevation="5dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:id="@+id/mini_player">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/mini_player_image"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/default_avatar"
|
||||||
|
tools:srcCompat="@drawable/default_avatar" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mini_player_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:maxWidth="180dp"
|
||||||
|
android:text="Video"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/mini_player_image"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textColor="?attr/androidOnBackgroundSecondary"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mini_player_author"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:maxWidth="180dp"
|
||||||
|
android:text="Author"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/mini_player_image"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/mini_play_pause"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:adjustViewBounds="false"
|
||||||
|
android:contentDescription="@string/likeBtn"
|
||||||
|
android:cropToPadding="false"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_pause_24"
|
||||||
|
app:tint="@color/colorAccent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -14,6 +14,8 @@
|
||||||
<item name="colorPrimaryDark">@color/colorAccent</item>
|
<item name="colorPrimaryDark">@color/colorAccent</item>
|
||||||
<item name="colorAccent">@color/md_theme_dark_onBackground</item>
|
<item name="colorAccent">@color/md_theme_dark_onBackground</item>
|
||||||
<item name="android:textColorLink">@color/md_theme_dark_secondary</item>
|
<item name="android:textColorLink">@color/md_theme_dark_secondary</item>
|
||||||
|
<item name="androidBackgroundSecondary">@color/md_theme_dark_tertiaryContainer</item>
|
||||||
|
<item name="androidOnBackgroundSecondary">@color/md_theme_dark_onTertiaryContainer</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
|
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr name="androidBackgroundSecondary" format="reference" />
|
||||||
|
<attr name="androidOnBackgroundSecondary" format="reference" />
|
||||||
|
</resources>
|
|
@ -14,6 +14,8 @@
|
||||||
<item name="colorPrimaryDark">@color/md_theme_light_primary</item>
|
<item name="colorPrimaryDark">@color/md_theme_light_primary</item>
|
||||||
<item name="colorAccent">@color/md_theme_light_secondary</item>
|
<item name="colorAccent">@color/md_theme_light_secondary</item>
|
||||||
<item name="android:textColorLink">@color/md_theme_light_secondary</item>
|
<item name="android:textColorLink">@color/md_theme_light_secondary</item>
|
||||||
|
<item name="androidBackgroundSecondary">@color/md_theme_light_tertiaryContainer</item>
|
||||||
|
<item name="androidOnBackgroundSecondary">@color/md_theme_light_onTertiaryContainer</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
|
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
|
||||||
|
|
Loading…
Reference in New Issue