503 lines
20 KiB
Kotlin
503 lines
20 KiB
Kotlin
package org.libre.agosto.p2play
|
|
|
|
import android.annotation.SuppressLint
|
|
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
|
|
import android.view.View
|
|
import android.view.WindowManager
|
|
import android.webkit.WebChromeClient
|
|
import android.widget.EditText
|
|
import android.widget.FrameLayout
|
|
import android.widget.ImageView
|
|
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
|
|
import kotlinx.android.synthetic.main.activity_reproductor.*
|
|
import kotlinx.android.synthetic.main.comment_component.commentaryBtn
|
|
import kotlinx.android.synthetic.main.comment_component.commentaryLayout
|
|
import kotlinx.android.synthetic.main.comment_component.commentaryText
|
|
import kotlinx.android.synthetic.main.comment_component.userImgCom
|
|
import org.libre.agosto.p2play.adapters.CommentariesAdapter
|
|
import org.libre.agosto.p2play.ajax.Actions
|
|
import org.libre.agosto.p2play.ajax.Comments
|
|
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()
|
|
|
|
// Commentaries adapter values
|
|
private lateinit var recyclerView: RecyclerView
|
|
private lateinit var viewAdapter: RecyclerView.Adapter<*>
|
|
private lateinit var viewManager: RecyclerView.LayoutManager
|
|
|
|
// 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)
|
|
setContentView(R.layout.activity_reproductor)
|
|
|
|
val fullscreenButton: ImageView = exoPlayer.findViewById(R.id.exo_fullscreen_custom)
|
|
val fullscreenButton2 = fullscreenPlayer.findViewById<ImageView>(R.id.exo_fullscreen_custom)
|
|
|
|
videoView.webChromeClient = WebClient()
|
|
videoView.settings.javaScriptEnabled = true
|
|
videoView.settings.allowContentAccess = true
|
|
videoView.settings.javaScriptCanOpenWindowsAutomatically = true
|
|
videoView.settings.allowFileAccess = true
|
|
videoView.settings.allowFileAccessFromFileURLs = true
|
|
videoView.settings.allowUniversalAccessFromFileURLs = true
|
|
videoView.settings.domStorageEnabled = true
|
|
|
|
try {
|
|
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
|
|
descriptionVideoTxt.text = this.video.description
|
|
val haveDescription = this.video.description.endsWith("...")
|
|
if (haveDescription) {
|
|
showMoreBtn.visibility = View.VISIBLE
|
|
}
|
|
hostTxt.text = this.video.userHost
|
|
|
|
// Check if user had profile image
|
|
if (this.video.userImageUrl != "") {
|
|
Picasso.get().load("https://" + ManagerSingleton.url + this.video.userImageUrl).into(userImg)
|
|
}
|
|
// Load the video
|
|
videoView.loadUrl("https://" + ManagerSingleton.url + this.video.embedUrl)
|
|
} catch (err: Exception) {
|
|
err.printStackTrace()
|
|
}
|
|
|
|
viewManager = LinearLayoutManager(this)
|
|
this.setDataComments(arrayListOf())
|
|
|
|
this.getComments()
|
|
|
|
subscribeBtn.setOnClickListener { subscribe() }
|
|
likeLayout.setOnClickListener { rate("like") }
|
|
dislikeLayout.setOnClickListener { rate("dislike") }
|
|
commentaryBtn.setOnClickListener { makeComment() }
|
|
showMoreBtn.setOnClickListener { getDescription() }
|
|
shareLayout.setOnClickListener { shareIntent() }
|
|
reportLayout.setOnClickListener { reportIntent() }
|
|
fullscreenButton.setOnClickListener { toggleFullscreen() }
|
|
fullscreenButton2.setOnClickListener { toggleFullscreen() }
|
|
|
|
userImg.setOnClickListener {
|
|
val intent = Intent(this, ChannelActivity::class.java)
|
|
intent.putExtra("channel", video.getChannel())
|
|
startActivity(intent)
|
|
}
|
|
|
|
AsyncTask.execute {
|
|
videoPlayback = this.clientVideo.getVideo(this.video.uuid)
|
|
// TODO: Make this configurable
|
|
val bufferSize = 1024 * 1024 // 1mb
|
|
val allocator = DefaultAllocator(true, bufferSize)
|
|
val loadControl = DefaultLoadControl.Builder()
|
|
.setAllocator(allocator)
|
|
.build()
|
|
|
|
runOnUiThread {
|
|
try {
|
|
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(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) {
|
|
err.printStackTrace()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun subscribe() {
|
|
AsyncTask.execute {
|
|
if (Looper.myLooper() == null) {
|
|
Looper.prepare()
|
|
}
|
|
val res = this.actions.subscribe(ManagerSingleton.token.token, video.getChannel())
|
|
if (res == 1) {
|
|
runOnUiThread {
|
|
ManagerSingleton.toast(getString(R.string.subscribeMsg), this)
|
|
this.changeSubscribeBtn(true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun unSubscribe() {
|
|
AsyncTask.execute {
|
|
if (Looper.myLooper() == null) {
|
|
Looper.prepare()
|
|
}
|
|
val res = this.actions.unSubscribe(ManagerSingleton.token.token, video.getChannel())
|
|
if (res == 1) {
|
|
runOnUiThread {
|
|
ManagerSingleton.toast(getString(R.string.unSubscribeMsg), this)
|
|
this.changeSubscribeBtn(false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun rate(rate: String) {
|
|
AsyncTask.execute {
|
|
if (Looper.myLooper() == null) {
|
|
Looper.prepare()
|
|
}
|
|
val res = this.actions.rate(ManagerSingleton.token.token, this.video.id, rate)
|
|
if (res == 1) {
|
|
runOnUiThread {
|
|
ManagerSingleton.toast(getString(R.string.rateMsg), this)
|
|
if (rate == "like") {
|
|
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike))
|
|
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
} else if (rate == "dislike") {
|
|
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.colorDislike))
|
|
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getRate() {
|
|
AsyncTask.execute {
|
|
if (Looper.myLooper() == null) {
|
|
Looper.prepare()
|
|
}
|
|
val rate = this.actions.getRate(ManagerSingleton.token.token, this.video.id)
|
|
runOnUiThread {
|
|
when (rate) {
|
|
"like" -> {
|
|
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike))
|
|
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
}
|
|
"dislike" -> {
|
|
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.colorDislike))
|
|
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
}
|
|
else -> {
|
|
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getSubscription() {
|
|
val account = this.video.nameChannel + "@" + this.video.userHost
|
|
AsyncTask.execute {
|
|
if (Looper.myLooper() == null) {
|
|
Looper.prepare()
|
|
}
|
|
val isSubscribed = this.actions.getSubscription(ManagerSingleton.token.token, account)
|
|
runOnUiThread {
|
|
this.changeSubscribeBtn(isSubscribed)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun changeSubscribeBtn(subscribed: Boolean) {
|
|
if (subscribed) {
|
|
subscribeBtn.text = getText(R.string.unSubscribeBtn)
|
|
subscribeBtn.setOnClickListener { this.unSubscribe() }
|
|
} else {
|
|
subscribeBtn.text = getText(R.string.subscribeBtn)
|
|
subscribeBtn.setOnClickListener { this.subscribe() }
|
|
}
|
|
}
|
|
|
|
private fun setDataComments(data: ArrayList<CommentaryModel>) {
|
|
// Set data for RecyclerView
|
|
viewAdapter = CommentariesAdapter(data).setFragmentManager(supportFragmentManager)
|
|
|
|
recyclerView = findViewById<RecyclerView>(R.id.listCommentaries).apply {
|
|
// use this setting to improve performance if you know that changes
|
|
// in content do not change the layout size of the RecyclerView
|
|
setHasFixedSize(true)
|
|
|
|
// use a linear layout manager
|
|
layoutManager = viewManager
|
|
|
|
// specify an viewAdapter (see also next example)
|
|
adapter = viewAdapter
|
|
}
|
|
}
|
|
|
|
private fun getComments() {
|
|
AsyncTask.execute {
|
|
val data = this.client.getCommentaries(this.video.id)
|
|
runOnUiThread {
|
|
this.setDataComments(data)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun makeComment() {
|
|
if (commentaryText.text.toString() == "") {
|
|
ManagerSingleton.toast(getString(R.string.emptyCommentaryMsg), this)
|
|
return
|
|
}
|
|
val text = commentaryText.text.toString()
|
|
AsyncTask.execute {
|
|
val res = this.client.makeCommentary(ManagerSingleton.token.token, this.video.id, text)
|
|
runOnUiThread {
|
|
if (res) {
|
|
ManagerSingleton.toast(getString(R.string.makedCommentaryMsg), this)
|
|
commentaryText.text?.clear()
|
|
this.getComments()
|
|
} else {
|
|
ManagerSingleton.toast(getString(R.string.errorCommentaryMsg), this)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
if (ManagerSingleton.user.status == 1) {
|
|
this.getRate()
|
|
this.getSubscription()
|
|
actionsLayout.visibility = View.VISIBLE
|
|
subscribeBtn.visibility = View.VISIBLE
|
|
commentaryLayout.visibility = View.VISIBLE
|
|
if (ManagerSingleton.user.avatar != "") {
|
|
Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(userImgCom)
|
|
}
|
|
} else {
|
|
commentaryLayout.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
private fun getDescription() {
|
|
AsyncTask.execute {
|
|
val fullDescription = this.videos.fullDescription(this.video.id)
|
|
runOnUiThread {
|
|
descriptionVideoTxt.text = fullDescription
|
|
showMoreBtn.visibility = View.GONE
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun shareIntent() {
|
|
val sendIntent: Intent = Intent().apply {
|
|
action = Intent.ACTION_SEND
|
|
putExtra(Intent.EXTRA_TEXT, "${video.name} ${video.getVideoUrl()}")
|
|
type = "text/plain"
|
|
}
|
|
startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.shareBtn)))
|
|
}
|
|
|
|
private fun reportIntent() {
|
|
val builder = AlertDialog.Builder(this)
|
|
// Get the layout inflater
|
|
val dialog = layoutInflater.inflate(R.layout.report_dialog, null)
|
|
|
|
val inputReason = dialog.findViewById<EditText>(R.id.reportText)
|
|
|
|
// Inflate and set the layout for the dialog
|
|
// Pass null as the parent view because its going in the dialog layout
|
|
builder.setView(dialog)
|
|
.setTitle(R.string.reportDialog)
|
|
// Add action buttons
|
|
.setPositiveButton(R.string.reportBtn) { _, _ ->
|
|
val reason = inputReason.text.toString()
|
|
reportVideo(reason)
|
|
}
|
|
.setNegativeButton("Cancel") { dialog, _ ->
|
|
dialog.run { cancel() }
|
|
}
|
|
val alertDialog = builder.create()
|
|
alertDialog.show()
|
|
}
|
|
|
|
private fun reportVideo(reason: String) {
|
|
AsyncTask.execute {
|
|
val res = actions.reportVideo(video.id, reason, ManagerSingleton.token.token)
|
|
|
|
runOnUiThread {
|
|
if (res) {
|
|
ManagerSingleton.toast(getText(R.string.reportDialogMsg).toString(), this)
|
|
} else {
|
|
ManagerSingleton.toast(getText(R.string.errorMsg).toString(), this)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun toggleFullscreen() {
|
|
if (isFullscreen) {
|
|
nonFullScreen.visibility = View.VISIBLE
|
|
fullScreenExo.visibility = View.GONE
|
|
exoPlayer.player = player
|
|
fullscreenPlayer.player = null
|
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
|
|
|
val attrs = window.attributes
|
|
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
|
|
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
|
|
window.attributes = attrs
|
|
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
|
|
|
isFullscreen = false
|
|
} else {
|
|
val matchParent = WindowManager.LayoutParams.MATCH_PARENT
|
|
|
|
nonFullScreen.visibility = View.GONE
|
|
fullScreenExo.visibility = View.VISIBLE
|
|
|
|
exoPlayer.player = null
|
|
fullscreenPlayer.player = player
|
|
|
|
setFullscreen(window)
|
|
|
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
|
isFullscreen = true
|
|
}
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
if (!player.isPlaying) {
|
|
PlaybackSingleton.release()
|
|
}
|
|
super.onDestroy()
|
|
}
|
|
|
|
internal inner class WebClient : WebChromeClient() {
|
|
private var mCustomView: View? = null
|
|
private var mCustomViewCallback: WebChromeClient.CustomViewCallback? = null
|
|
private var mOriginalOrientation: Int = 0
|
|
private var mOriginalSystemUiVisibility: Int = 0
|
|
|
|
override fun getDefaultVideoPoster(): Bitmap? {
|
|
AsyncTask.execute {
|
|
this@ReproductorActivity.actions.watchVideo(this@ReproductorActivity.video.id, ManagerSingleton.token.token)
|
|
}
|
|
|
|
return if (mCustomView == null) {
|
|
null
|
|
} else {
|
|
BitmapFactory.decodeResource(this@ReproductorActivity.resources, 2130837573)
|
|
}
|
|
}
|
|
|
|
override fun onHideCustomView() {
|
|
try {
|
|
this@ReproductorActivity.nonFullScreen.visibility = View.VISIBLE
|
|
this@ReproductorActivity.fullScreen.visibility = View.GONE
|
|
this@ReproductorActivity.fullScreen.removeView(this.mCustomView)
|
|
this.mCustomView = null
|
|
|
|
this.mCustomViewCallback!!.onCustomViewHidden()
|
|
this.mCustomViewCallback = null
|
|
|
|
this@ReproductorActivity.requestedOrientation = this.mOriginalOrientation
|
|
// this@ReproductorActivity.window.decorView.systemUiVisibility = this.mOriginalSystemUiVisibility
|
|
|
|
val attrs = this@ReproductorActivity.window.attributes
|
|
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
|
|
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
|
|
window.attributes = attrs
|
|
this@ReproductorActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
|
} catch (err: Exception) {
|
|
err.printStackTrace()
|
|
}
|
|
}
|
|
|
|
override fun onShowCustomView(paramView: View, paramCustomViewCallback: WebChromeClient.CustomViewCallback) {
|
|
if (this.mCustomView != null) {
|
|
this.onHideCustomView()
|
|
return
|
|
}
|
|
try {
|
|
this.mCustomView = paramView
|
|
this.mOriginalSystemUiVisibility = this@ReproductorActivity.window.decorView.systemUiVisibility
|
|
this.mOriginalOrientation = this@ReproductorActivity.requestedOrientation
|
|
this.mCustomViewCallback = paramCustomViewCallback
|
|
val matchParent = WindowManager.LayoutParams.MATCH_PARENT
|
|
|
|
this@ReproductorActivity.nonFullScreen.visibility = View.GONE
|
|
this@ReproductorActivity.fullScreen.visibility = View.VISIBLE
|
|
|
|
this@ReproductorActivity.fullScreen.addView(paramView, FrameLayout.LayoutParams(matchParent, matchParent))
|
|
|
|
setFullscreen(this@ReproductorActivity.window)
|
|
|
|
this@ReproductorActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
|
} catch (err: Exception) {
|
|
err.printStackTrace()
|
|
}
|
|
}
|
|
}
|
|
}
|