Skip to content

Instantly share code, notes, and snippets.

@sajjadyousefnia
Created June 25, 2024 22:57
Show Gist options
  • Save sajjadyousefnia/dbe56da92193cfcaa24702801e2b6eb7 to your computer and use it in GitHub Desktop.
Save sajjadyousefnia/dbe56da92193cfcaa24702801e2b6eb7 to your computer and use it in GitHub Desktop.
package com.sands.android.view.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.content.res.Configuration
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment
import com.google.android.material.card.MaterialCardView
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.sands.android.App
import com.sands.android.R
import com.sands.android.dao.ApiBase
import com.sands.android.dao.ApiDelegates
import com.sands.android.dao.SharedPreferencesHelper
import com.sands.android.dao.entity.MdlLastPlay
import com.sands.android.dao.entity.MdlSub
import com.sands.android.dao.entity.MdlVideo
import com.sands.android.databinding.ActivityVideoPlayerLayoutBinding
import com.sands.android.helper.Constant
import com.sands.android.helper.PopMenu
import okhttp3.OkHttpClient
import okhttp3.Request
import org.videolan.libvlc.LibVLC
import org.videolan.libvlc.Media
import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.MediaPlayer.TrackDescription
import org.videolan.libvlc.interfaces.IMedia
import org.videolan.libvlc.util.VLCVideoLayout
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.concurrent.Executors
import com.sands.android.App.Companion.dpToPx
import com.sands.android.dao.entity.SubtitleColorModel
import com.sands.android.view.dialog.DialogSubtitleSettings
import com.sands.android.view.dialog.DialogSubtitleSettings.Interaction
class PlayerActivity : ActivityBase(), MediaPlayer.EventListener, View.OnClickListener {
private val TAG = "PlayerActivity"
private var soundMenuItemMargin: Int = 0
private var soundMenuItemWidth: Int = 0
private var soundMenuItemHeight: Int = 0
private var soundMenuItemFontSize: Float = 0f
private var isSubtitleSettingsBtnEnabled = false
private var isFirstTimeTouchSubtitleDialog = true
private var lastVideoId = ""
private var lastVideoType = ""
private var lastVideoResume = false
private var lastPlayTime = 0.toLong()
private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var binding: ActivityVideoPlayerLayoutBinding
private var subtitleList: ArrayList<MdlSub> = arrayListOf()
private val myTag = "sand"
private val typeSubtitleTrack = 0
private val typeAudioTrack = 1
private val typeScale = 3
private val typeSpeed = 4
private var onSeekBarSeeking = false
private lateinit var mActivity: Activity
private lateinit var mediaPlayer: MediaPlayer
private var libVLC: LibVLC? = null
private lateinit var vlcVideoLayout: VLCVideoLayout
private var lastVolume = 0
private lateinit var controllerTop: ConstraintLayout
private lateinit var controllerBottom: ConstraintLayout
private lateinit var videoTitle: TextView
private lateinit var currTime: TextView
private lateinit var countTime: TextView
private lateinit var speedBtn: TextView
private lateinit var scaleBtn: TextView
private lateinit var subTracksBtn: MaterialCardView
private lateinit var audioTracksBtn: MaterialCardView
private lateinit var currPosition: SeekBar
private lateinit var subTrackMenu: PopMenu
private lateinit var audioTrackMenu: PopMenu
private lateinit var scaleTypeMenu: PopMenu
private lateinit var speedMenu: PopMenu
private var isClosing = false
private val speedRate = arrayListOf(0.5f, 1.0f, 1.5f, 2.0f)
private var isLastPlayApplied = false
private var updateTimeCount = 0.toLong()
private lateinit var currItem: MdlVideo
private var isSystemUiVisible = false
private fun hideSystemUI() {
// Enables regular immersive mode
isSystemUiVisible = false
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
private fun showSystemUI() {
isSystemUiVisible = true
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
window.decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
}
override fun onResume() {
super.onResume()
//mediaPlayer.play()
wakeLock = (getSystemService(POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.FULL_WAKE_LOCK, "myapp:player_wake"
)
wakeLock.acquire(120 * 60 * 1000L /*120 minutes*/)
// Runtime.getRuntime().gc();
}
override fun onPause() {
super.onPause()
//mediaPlayer.pause()
if (::wakeLock.isInitialized) wakeLock.release()
val videoLastPlayForSave: MdlLastPlay
val lastPlay = App.db.appDao().getLastPlayTime(lastVideoId, lastVideoType)
if (lastPlay != null) {
lastPlay.last_play_time = lastPlayTime.toString()
videoLastPlayForSave = lastPlay
} else {
videoLastPlayForSave =
MdlLastPlay(0, lastVideoId, lastVideoType, lastPlayTime.toString())
}
App.db.appDao().insertLastPlay(videoLastPlayForSave)
if (!isClosing) {
playOrPause()
}
//
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
// requestWindowFeature(Window.FEATURE_NO_TITLE);
hideSystemUI()
initPopMenuSizes()
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN
)
binding = ActivityVideoPlayerLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.VideoView.setOnClickListener {
toggleView()
}
binding.crdFull.setOnClickListener {
hideViews()
if (isSystemUiVisible) {
hideSystemUI()
} else {
showSystemUI()
}
}
mActivity = this;
initVlc();
initController();
currItem = MdlVideo()
currItem.Url =
"http://62.60.212.247:8000/api/stream/?path=sands/img/move_files/blizzard_of_souls/720e.mkv"
currItem.Name = ""
if (intent.extras != null) {
currItem.Url = intent.extras!!.getString(
"url",
"http://62.60.212.247:8000/api/stream/?path=sands/img/move_files/blizzard_of_souls/720e.mkv"
)
currItem.Name = intent.extras!!.getString("title", "")
val idf = intent.extras!!.getString("idf", "0")
val videoType = intent.extras!!.getString("vid_type", "movie")
val videoId = intent.extras!!.getString("vid_id", "0")
val videoResume = intent.extras!!.getBoolean("vid_resume", false)
lastVideoId = videoId
lastVideoType = videoType
lastVideoResume = videoResume
if (idf != "0") {
val isMovie = intent.extras!!.getBoolean("is_movie", false)
getSubtitle({
play()
}, intent.extras!!.getString("idf", "0"), isMovie)
//mediaPlayer.spu
} else {
play()
}
if (intent.extras!!.getString("sub", "").isNotEmpty()) {
val subUrl = intent.extras!!.getString("sub", "")
subtitleList.clear()
downloadSubAndEdition(subUrl,
cacheDir.absolutePath + "/custom_sub" + System.currentTimeMillis()
.toString() + ".vtt",
object : OnSubDownloadSingle {
override fun downloadDone(file: String) {
subtitleList.add(MdlSub(0, "", "vtt", file))
play()
//Constant.log(file)
}
override fun downloadError(error: String) {
Constant.log(error)
}
})
}
} else {
onBackPressed()
}
binding.crdClose.setOnClickListener {
isClosing = true
stop()
}
binding.btnSettings.setOnClickListener {
if (isSubtitleSettingsBtnEnabled == true) {
if (isFirstTimeTouchSubtitleDialog == true) {
val subtitleDialog: DialogFragment =
DialogSubtitleSettings.newInstance(object : Interaction {
override fun increaseSubtitleSize() {
}
override fun decreaseSubtitleSize() {
}
override fun selectSubtitleColor(item: SubtitleColorModel) {
}
})
subtitleDialog.show(supportFragmentManager, "dialog_settings")
subtitleDialog.dismissNow()
isFirstTimeTouchSubtitleDialog = false
}
}
val subtitleDialog: DialogFragment =
DialogSubtitleSettings.newInstance(object : Interaction {
override fun increaseSubtitleSize() {
}
override fun decreaseSubtitleSize() {
}
override fun selectSubtitleColor(item: SubtitleColorModel) {
}
})
subtitleDialog.show(supportFragmentManager, "dialog_settings")
}
}
private fun initPopMenuSizes() {
soundMenuItemHeight = dpToPx(this, 20)
soundMenuItemWidth = dpToPx(this, 50)
soundMenuItemMargin = dpToPx(this, 2)
val textView = TextView(this)/*
textView.setTextSize(
TypedValue.COMPLEX_UNIT_SP, 8f
)
*/
// val textSizeInSp = (textView.textSize) / resources.displayMetrics.scaledDensity
soundMenuItemFontSize = 22f
}
interface OnSubDownload {
fun downloadDone(subtitleListFinal: ArrayList<MdlSub>)
fun downloadError(error: String)
}
interface OnSubDownloadSingle {
fun downloadDone(file: String)
fun downloadError(error: String)
}
class DownloadTask(
private var subtitleListData: ArrayList<MdlSub>,
private var filePath: String,
var delegate: OnSubDownload
) : AsyncTask<Void, Void, String>() {
private var subtitleListFinal: ArrayList<MdlSub> = arrayListOf()
override fun doInBackground(vararg params: Void?): String? {
for (i in 0 until subtitleListData.size) {
val sub = subtitleListData[i]
Constant.log("SUB: start " + i)
val filePath = filePath + System.currentTimeMillis().toString() + ".vtt"
subtitleListFinal.add(MdlSub(0, "", "vtt", filePath))
val client = OkHttpClient()
val request: Request = Request.Builder().url(sub.url).build()
try {
client.newCall(request).execute().use { response ->
val data = response.body!!.string()
val finalData = Constant.subSensor(data)
val stream = FileOutputStream(filePath)
stream.use { _ ->
stream.write(finalData.toByteArray())
stream.close()
}
Constant.log("SUB: download done " + filePath)
}
} catch (e: Exception) {
Constant.log("SUB: download error " + e.message)
delegate.downloadError(e.message.toString())
}
}
Constant.log("SUB: download complete ")
return ""
}
override fun onPreExecute() {
super.onPreExecute()
// ...
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
Constant.log("SUB: onPostExecute ")
delegate.downloadDone(subtitleListFinal)
// ...
}
}
/*
class DownloadAllSubs(var subtitle: String, var filePath: String, var delegate: OnSubDownload) : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String? {
val client = OkHttpClient()
val request: Request = Request.Builder()
.url(subtitle)
.build()
try {
client.newCall(request).execute().use { response ->
val data = response.body!!.string()
val finalData = Constant.subSensor(data)
val stream = FileOutputStream(filePath)
stream.use { _ ->
stream.write(finalData.toByteArray())
stream.close()
}
Constant.log("SUB: download done " + filePath)
}
} catch (e: Exception) {
Constant.log("SUB: download error " + e.message)
delegate.downloadError(e.message.toString())
}
return ""
}
override fun onPreExecute() {
super.onPreExecute()
// ...
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
delegate.downloadDone(filePath)
// ...
}
}
*/
private fun downloadSubAndEdition(
subtitle: String, filePath: String, delegate: OnSubDownloadSingle
) {
//subtitleList.clear()
// subtitleList.addAll(res)
Constant.log("SUB: download start " + filePath)
val CPU_COUNT = Runtime.getRuntime().availableProcessors()
val MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1
val service = Executors.newFixedThreadPool(MAXIMUM_POOL_SIZE)
service.execute(Runnable {
// Fetch user data here or do whatever you want to do on background thread.
// This is same as doInBackground method of AsyncTask which executes on the background thread.
val client = OkHttpClient()
val request: Request = Request.Builder().url(subtitle).build()
try {
client.newCall(request).execute().use { response ->
val data = response.body!!.string()
val finalData = Constant.subSensor(data)
val stream = FileOutputStream(filePath)
stream.use { _ ->
stream.write(finalData.toByteArray())
stream.close()
}
Constant.log("SUB: download done " + filePath)
delegate.downloadDone(filePath)
}
} catch (e: Exception) {
Constant.log("SUB: download error " + e.message)
delegate.downloadError(e.message.toString())
}
});
}
private fun getSubtitle(runnable: Runnable, idf: String, isMovie: Boolean = false) {
//http://tino1.catafilaming.xyz/api/subtitles/by/episode/
//https://tino1.catafilaming.xyz/api/subtitles/by/episode/112765/4F5A8C3D9A86FA54EACEDDD635185/
val subUrlMovie = SharedPreferencesHelper(this@PlayerActivity).sharedPreferencesLoad(
SharedPreferencesHelper.KEY_BASE_SUB_MOVIE, ""
)//.replace("http://","https://")
val subUrlSeries = SharedPreferencesHelper(this@PlayerActivity).sharedPreferencesLoad(
SharedPreferencesHelper.KEY_BASE_SUB_SERIES, ""
)//.replace("http://","https://")
val urlReplacer = SharedPreferencesHelper(this@PlayerActivity).sharedPreferencesLoad(
SharedPreferencesHelper.KEY_BASE_URL_REPLACE, ""
)//.replace("http://","https://")
val secKey = "4F5A8C3D9A86FA54EACEDDD635185"
val url = if (isMovie) "$subUrlMovie$idf/$secKey/" else "$subUrlSeries$idf/$secKey/"
//url = "https://tino1.catafilaming.xyz/api/subtitles/by/episode/112765/4F5A8C3D9A86FA54EACEDDD635185/"
//showLoading()
val lastUrl =
if (urlReplacer.isNotEmpty()) url.replace("phone.b1400.xyz", urlReplacer) else url
val call = retrofitService.getSubList(lastUrl)
ApiBase().execute(call, object : ApiDelegates.OnWebServicesResponse {
@SuppressLint("NotifyDataSetChanged")
override fun onSuccess(response: Any) {
val jsonArray = Gson().toJsonTree(response).asJsonArray.toString()
val listType = object : TypeToken<ArrayList<MdlSub>>() {}.type
val res = Gson().fromJson<ArrayList<MdlSub>>(jsonArray, listType)
if (res.isEmpty()) {
Constant.log("SUB: download done no sub")
runnable.run()
} else {
val basePath = cacheDir.absolutePath + "/custom_sub"
Constant.log("SUB: download size " + res.size)
DownloadTask(res, basePath, object : OnSubDownload {
override fun downloadDone(subtitleListFinal: ArrayList<MdlSub>) {
subtitleList.clear()
subtitleList.addAll(subtitleListFinal)
Constant.log("SUB: last file >>>>>")
runnable.run()
}
override fun downloadError(error: String) {
Constant.log(error)
Constant.log("SUB: download err ")
}
}).execute()
}
}
override fun onFailed(error: String) {
// App.getApp(application).toaster(error)
runnable.run()
}
override fun onLogOut() {
runnable.run()
// App.getApp(application).logout(this@PlayerActivity)
}
override fun onResult(code: Int) {
hideLoading()
}
})
}
override fun onLowMemory() {
super.onLowMemory()
Constant.log("lowMemory")
}
private fun initVlc() {
val vlcoptions: MutableList<String> = ArrayList()
//vlcoptions.add("-v")
vlcVideoLayout = binding.VideoView
libVLC = LibVLC(this, vlcoptions)
mediaPlayer = MediaPlayer(libVLC)
mediaPlayer.attachViews(vlcVideoLayout, null, true, true)
mediaPlayer.setEventListener(this)
// Runtime.getRuntime().gc();
}
override fun onEvent(event: MediaPlayer.Event) {
when (event.type) {
MediaPlayer.Event.Playing -> {
hideViews()
Log.d(myTag, "onEvent: Playing")
initMenu()
binding.imgPause.visibility = View.VISIBLE
binding.imgPlay.visibility = View.GONE
val subTrackList = mediaPlayer.spuTracks
if (subTrackList != null) {
for (sub in subTrackList) {
mediaPlayer.setSpuTrack(sub.id)
}
}
val lastPlay = App.db.appDao().getLastPlayTime(lastVideoId, lastVideoType)
if (lastVideoResume && lastPlay != null && isLastPlayApplied.not()) {
isLastPlayApplied = true
val startTime: Long = lastPlay.last_play_time.toLong() /// 10000L / 1000L
setTimeOnSeekBar(startTime, false)
//if (startTime > 60) media.addOption(":start-time=$startTime")
}
}
MediaPlayer.Event.Paused -> {
binding.imgPause.visibility = View.GONE
binding.imgPlay.visibility = View.VISIBLE
}
MediaPlayer.Event.Stopped -> {
Log.d(myTag, "onEvent: Stopped")
stop()
//ReportPlayState(JfClient.ReportType.stop)//1917
//playNext()
}
MediaPlayer.Event.Opening -> {
Log.d(myTag, "onEvent: Opening")
}
MediaPlayer.Event.Buffering -> {
val buffering = event.buffering.toInt()
if (buffering >= 100) {
binding.loading.visibility = View.GONE
} else {
if (binding.loading.visibility == View.GONE) {
binding.loading.visibility = View.VISIBLE
}
}
}
MediaPlayer.Event.EndReached -> {
Log.d(myTag, "onEvent: EndReached")
}
MediaPlayer.Event.EncounteredError -> {
Log.d(myTag, "onEvent: EncounteredError")
stop()
}
MediaPlayer.Event.TimeChanged -> {
updateTimeCount += event.timeChanged - currItem.PositionTicks
currItem.PositionTicks = event.timeChanged
if (updateTimeCount > 20000) {
updateTimeCount = 0
}
}
MediaPlayer.Event.PositionChanged -> {}
MediaPlayer.Event.SeekableChanged -> {
Log.d(myTag, "onEvent: SeekableChanged:" + event.seekable)
}
MediaPlayer.Event.PausableChanged -> {
Log.d(myTag, "onEvent: PausableChanged")
}
MediaPlayer.Event.LengthChanged -> {
Log.d(myTag, "onEvent: LengthChanged")
}
MediaPlayer.Event.Vout -> {
Log.d(myTag, "onEvent: Vout")
}
MediaPlayer.Event.ESAdded, MediaPlayer.Event.ESDeleted, MediaPlayer.Event.ESSelected -> {
Log.d(
myTag,
"onEvent: ES:" + event.type + ":" + event.esChangedType + ":" + event.esChangedID
)
}
MediaPlayer.Event.RecordChanged -> {
Log.d(myTag, "onEvent: RecordChanged")
}
}
}
private fun initController() {
controllerTop = binding.topBar
controllerBottom = binding.navigationBar
videoTitle = binding.videoTitle
currTime = binding.currTime
countTime = binding.countTime
speedBtn = binding.speedBtn
scaleBtn = binding.scaleBtn
currPosition = binding.currPosition
binding.playPauseBtn.setOnClickListener(this)
currPosition.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, iSFromUser: Boolean) {
//Constant.log("onProgressChanged$p1")
if (iSFromUser) {
val percent = (p1 * 100) / 1000
val currentPlayerMove = (mediaPlayer.length * percent) / 100
currTime.text = trickToTime(currentPlayerMove)
// Constant.log(" iSFromUser" + p1)
} else {
// Constant.log("!!!! iSFromUser" + p1)
}
}
override fun onStartTrackingTouch(p0: SeekBar?) {
onSeekBarSeeking = true
//Constant.log("onStartTrackingTouch")
}
override fun onStopTrackingTouch(p0: SeekBar?) {
onSeekBarSeeking = false
//Constant.log("onStopTrackingTouch")
//if (iSFromUser) {
val percent = (p0!!.progress * 100) / 1000
val currentPlayerMove = (mediaPlayer.length * percent) / 100
setTimeOnSeekBar(currentPlayerMove, false)
// Constant.log("All onStopTrackingTouch" + p0.progress)
//}
}
})
currPosition.setOnKeyListener { _, _, keyEvent ->
var rv = false
val action = keyEvent.action
if (action == 1) { //按键up
val keycode = keyEvent.keyCode
if (keycode == KeyEvent.KEYCODE_DPAD_RIGHT) {
rv =
setTimeOnSeekBar(currItem.PositionTicks + (mediaPlayer.length * 0.05).toLong())
} else if (keycode == KeyEvent.KEYCODE_DPAD_LEFT) {
rv =
setTimeOnSeekBar(currItem.PositionTicks - (mediaPlayer.length * 0.05).toLong())
}
}
rv
}
binding.crdMute.setOnClickListener {
if (mediaPlayer.volume > 0) {
lastVolume = mediaPlayer.volume
mediaPlayer.setVolume(0)
binding.imgMute.visibility = View.GONE
binding.imgUnMute.visibility = View.VISIBLE
} else {
mediaPlayer.setVolume(lastVolume)
binding.imgMute.visibility = View.VISIBLE
binding.imgUnMute.visibility = View.GONE
}
}
}
private fun initMenu() {
val subTrackList = mediaPlayer.spuTracks
val audioTrackList = mediaPlayer.audioTracks
subTracksBtn = binding.subTracksBtn
audioTracksBtn = binding.audioTracksBtn
if (null != subTrackList && subTrackList.size > 1) {
initSubTrackMenu(subTrackList)
} else {
subTracksBtn.visibility = View.GONE
binding.btnSettings.visibility = View.GONE
}
if (null != audioTrackList && audioTrackList.size > 1) {
initAudioTrackMenu(audioTrackList)
} else {
audioTracksBtn.visibility = View.GONE
}
scaleBtn.setOnClickListener(this)
scaleBtn.text = getVlcScaleTypeName(mediaPlayer.videoScale.name)
scaleTypeMenu = PopMenu(this, scaleBtn)
val scaleTypes: Array<MediaPlayer.ScaleType> =
org.videolan.libvlc.MediaPlayer.ScaleType.entries.toTypedArray()
for (i in scaleTypes.indices) {
val menu = scaleTypeMenu.add(typeScale, i, i, getVlcScaleTypeName(scaleTypes[i].name))
if (mediaPlayer.videoScale == scaleTypes[i]) {
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
// Adjust the size of the menu item view
val layoutParams = menu.v.layoutParams
layoutParams.width = soundMenuItemWidth // Set desired width in pixels
layoutParams.height = soundMenuItemHeight // Set desired height in pixels
menu.v.layoutParams = layoutParams
if (menu.v is TextView) {
(menu.v as TextView).setTextSize(
TypedValue.COMPLEX_UNIT_PX, soundMenuItemFontSize
) // Set desired text size in SP
(menu.v as TextView).setPadding(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
) // Set padding to zero
(menu.v as TextView).gravity = Gravity.CENTER
// Add margins in pixels (left, top, right, bottom)
val marginParams = menu.v.layoutParams as ViewGroup.MarginLayoutParams
marginParams.setMargins(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
)
menu.v.layoutParams = marginParams
}
menu.v.setOnClickListener {
scaleTypeMenu.dismiss()
if (mediaPlayer.videoScale != scaleTypes[i]) {
mediaPlayer.videoScale = scaleTypes[i]
scaleBtn.text = getVlcScaleTypeName(mediaPlayer.videoScale.name)
makeAllMenuWhite(scaleTypeMenu)
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
}
}
speedBtn.setOnClickListener(this)
speedBtn.text = mediaPlayer.rate.toString()
speedMenu = PopMenu(this, speedBtn)
for (i in 0 until speedRate.size) {
val menu = speedMenu.add(typeSpeed, i, i, java.lang.String.valueOf(speedRate[i]))
if (mediaPlayer.rate == speedRate[i]) {
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
// Adjust the size of the menu item view
val layoutParams = menu.v.layoutParams
layoutParams.width = soundMenuItemWidth // Set desired width in pixels
layoutParams.height = soundMenuItemHeight // Set desired height in pixels
menu.v.layoutParams = layoutParams
if (menu.v is TextView) {
(menu.v as TextView).setTextSize(
TypedValue.COMPLEX_UNIT_PX, soundMenuItemFontSize
) // Set desired text size in SP
(menu.v as TextView).setPadding(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
) // Set padding to zero
(menu.v as TextView).gravity = Gravity.CENTER
// Add margins in pixels (left, top, right, bottom)
val marginParams = menu.v.layoutParams as ViewGroup.MarginLayoutParams
marginParams.setMargins(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
)
menu.v.layoutParams = marginParams
}
menu.v.setOnClickListener {
speedMenu.dismiss()
if (mediaPlayer.rate != speedRate[i]) {
mediaPlayer.rate = speedRate[i]
speedBtn.text = mediaPlayer.rate.toString()
makeAllMenuWhite(speedMenu)
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
}
}
}
private fun initSubTrackMenu(subTrackList: Array<TrackDescription>) {
subTrackMenu = PopMenu(this, subTracksBtn)
for (i in subTrackList.indices) {
val track = "Subtitle $i"
val menu = subTrackMenu.add(typeSubtitleTrack, subTrackList[i].id, i, track)
if (mediaPlayer.spuTrack == subTrackList[i].id) {
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
// Adjust the size of the menu item view
val layoutParams = menu.v.layoutParams
layoutParams.width = soundMenuItemWidth // Set desired width in pixels
layoutParams.height = soundMenuItemHeight // Set desired height in pixels
menu.v.layoutParams = layoutParams
if (menu.v is TextView) {
(menu.v as TextView).setTextSize(
TypedValue.COMPLEX_UNIT_PX, soundMenuItemFontSize
) // Set desired text size in SP
(menu.v as TextView).setPadding(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
) // Set padding to zero
(menu.v as TextView).gravity = Gravity.CENTER
// Add margins in pixels (left, top, right, bottom)
val marginParams = menu.v.layoutParams as ViewGroup.MarginLayoutParams
marginParams.setMargins(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
)
menu.v.layoutParams = marginParams
}
menu.v.setOnClickListener {
subTrackMenu.dismiss()
if (menu.id != mediaPlayer.spuTrack) {
mediaPlayer.setSpuTrack(menu.id)
makeAllMenuWhite(subTrackMenu)
(menu.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
}
}
subTracksBtn.setOnClickListener(this)
binding.btnSettings.visibility = View.VISIBLE
isSubtitleSettingsBtnEnabled = true
}
private fun makeAllMenuWhite(subTrackMenu: PopMenu) {
subTrackMenu.menus.forEach {
(it.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.app_bright_white_color
)
)
}
}
private fun initAudioTrackMenu(audioTrackList: Array<TrackDescription>) {
audioTrackMenu = PopMenu(this, audioTracksBtn)
for (i in audioTrackList.indices) {
val track = "Sound $i"
val m = audioTrackMenu.add(typeAudioTrack, audioTrackList[i].id, i, track)
if (mediaPlayer.audioTrack == i) {
(m.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
// Adjust the size of the menu item view
val layoutParams = m.v.layoutParams
layoutParams.width = soundMenuItemWidth // Set desired width in pixels
layoutParams.height = soundMenuItemHeight // Set desired height in pixels
m.v.layoutParams = layoutParams
if (m.v is TextView) {
(m.v as TextView).setTextSize(
TypedValue.COMPLEX_UNIT_PX, soundMenuItemFontSize
) // Set desired text size in SP
(m.v as TextView).setPadding(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
) // Set padding to zero
(m.v as TextView).gravity = Gravity.CENTER
// Add margins in pixels (left, top, right, bottom)
val marginParams = m.v.layoutParams as ViewGroup.MarginLayoutParams
marginParams.setMargins(
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin,
soundMenuItemMargin
)
m.v.layoutParams = marginParams
}
m.v.setOnClickListener {
audioTrackMenu.dismiss()
if (m.id != mediaPlayer.audioTrack) {
mediaPlayer.setAudioTrack(m.id)
makeAllMenuWhite(audioTrackMenu)
(m.v as TextView).setTextColor(
ContextCompat.getColor(
this,
R.color.orange_controls
)
)
}
}
}
audioTracksBtn.setOnClickListener(this)
}
private val myHandler: Handler = Handler(Looper.getMainLooper())
private val mUpdateSeekBar: Runnable = object : Runnable {
override fun run() {
setSeekBar(currItem.PositionTicks)
lastPlayTime = currItem.PositionTicks
myHandler.postDelayed(this, 1000)
}
}
private fun toggleView() {
if (controllerTop.visibility == View.GONE) {
showControls()
} else {
hideViews()
}
}
private fun showControls() {
if (controllerTop.visibility == View.GONE) {
controllerTop.visibility = View.VISIBLE
}
if (controllerBottom.visibility == View.GONE) {
controllerBottom.visibility = View.VISIBLE
myHandler.postDelayed(mUpdateSeekBar, 0)
binding.playPauseBtn.requestFocus()
}
/* Handler(Looper.getMainLooper()).postDelayed({
controllerTop.visibility = View.GONE
controllerBottom.visibility = View.GONE
}, (sec * 1000).toLong())*/
}
private fun hideViews() {
if (controllerTop.visibility == View.VISIBLE) {
controllerTop.visibility = View.GONE
}
if (controllerBottom.visibility == View.VISIBLE) {
controllerBottom.visibility = View.GONE
myHandler.removeCallbacks(mUpdateSeekBar)
}
}
private fun setSeekBar(p: Long) {
if (controllerBottom.visibility == View.VISIBLE && onSeekBarSeeking.not()) {
if (mediaPlayer.length > 0) {
//val i = p.toDouble() / 1000
val duration = mediaPlayer.length
if (duration > 0) {
val pos = 1000L * p / duration
currPosition.progress = pos.toInt()
}
currTime.text = trickToTime(p)
countTime.text = trickToTime(duration)
}
}
}
private fun play() {
videoTitle.text = currItem.Name
val media: Media = getMedia()
mediaPlayer.setMedia(media)
media.release()
mediaPlayer.play()
}
@Throws(IOException::class)
private fun copyFile(`in`: InputStream, out: OutputStream) {
val buffer = ByteArray(1024)
var read: Int
while (`in`.read(buffer).also { read = it } != -1) {
out.write(buffer, 0, read)
}
}
private fun getMedia(): Media {
val uri = Uri.parse(currItem.Url)
val media = Media(libVLC, uri)
for (i in 0 until subtitleList.size) {
val sub = subtitleList[i]
val slave = IMedia.Slave(IMedia.Slave.Type.Subtitle, i, "file:///" + sub.url)
media.addSlave(slave)
}/*for (sub in subtitleList) {
}*/
// val slave = IMedia.Slave(IMedia.Slave.Type.Subtitle, 0, "file:///storage/emulated/0/Android/data/com.sands.android/cache/sub_test.vtt")
// media.addSlave(slave)
media.setHWDecoderEnabled(true, false)//1917
//media.addOption(":codec=mediacodec_ndk,mediacodec_jni,none"); //硬件加速
val startTime: Long = currItem.startPositionTicks / 10000L / 1000L
if (startTime > 60) media.addOption(":start-time=$startTime")
return media
}
private fun stop() {
mediaPlayer.stop()
mediaPlayer.release()
libVLC!!.release()
onBackPressed()
}
private fun playOrPause() {
try {
if (mediaPlayer.isPlaying) {
mediaPlayer.pause()
binding.imgPlay.visibility = View.VISIBLE
binding.imgPause.visibility = View.GONE
} else {
mediaPlayer.play()
binding.imgPause.visibility = View.VISIBLE
binding.imgPlay.visibility = View.GONE
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun setTimeOnSeekBar(p: Long, isSeekBarSet: Boolean = true): Boolean {
if (p < mediaPlayer.length && p >= 0) {
mediaPlayer.setTime(p, true)
if (isSeekBarSet) setSeekBar(p)
}
return true
}
private fun trickToTime(trick: Long): String {
val time: String
val totalSeconds = trick / 1000
val seconds = totalSeconds % 60
val minutes = totalSeconds / 60 % 60
val hours = totalSeconds / 3600
time = String.format("%02d:%02d:%02d", hours, minutes, seconds)
return time
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (controllerBottom.visibility == View.GONE) {
when (keyCode) {
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN -> {
this.showControls()
return true
}
KeyEvent.KEYCODE_DPAD_RIGHT -> {
mediaPlayer.setTime(mediaPlayer.time + 30000)
return true
}
KeyEvent.KEYCODE_DPAD_LEFT -> {
mediaPlayer.setTime(mediaPlayer.time - 10000)
return true
}
KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
playOrPause()
return true
}
KeyEvent.KEYCODE_ESCAPE, KeyEvent.KEYCODE_BACK -> {
stop()
return true
}
}
} else {
when (keyCode) {
KeyEvent.KEYCODE_ESCAPE, KeyEvent.KEYCODE_BACK -> {
hideViews()
return true
}
}
}
return super.onKeyDown(keyCode, event)
}
override fun onClick(view: View) {
val id = view.id
when (id) {
R.id.playPauseBtn -> {
playOrPause()
}
R.id.subTracksBtn -> {
subTrackMenu.show(mediaPlayer.spuTrack)
}
R.id.audioTracksBtn -> {
audioTrackMenu.show(mediaPlayer.audioTrack)
}
R.id.scaleBtn -> {
scaleTypeMenu.show(getVlcScaleTypeName(mediaPlayer.videoScale.name))
}
R.id.speedBtn -> {
speedMenu.show(mediaPlayer.rate.toString())
}
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
//return super.onTouchEvent(event);
this.showControls()
return true
}
override fun finish() {
myHandler.removeCallbacksAndMessages(null)
super.finish()
}
private fun getVlcScaleTypeName(scaleName: String?): String {
when (scaleName) {
"SURFACE_BEST_FIT" -> return "Best Fit"
"SURFACE_FIT_SCREEN" -> return "Fit Screen"
"SURFACE_FILL" -> return "Fill"
"SURFACE_16_9" -> return "16:9"
"SURFACE_4_3" -> return "4:3"
"SURFACE_16_10" -> return "16:10"
"SURFACE_221_1" -> return "2.21:1"
"SURFACE_235_1" -> return "2.35:1"
"SURFACE_239_1" -> return "2.39:1"
"SURFACE_21_9" -> return "21:9"
"SURFACE_5_4" -> return "5:4"
"SURFACE_ORIGINAL" -> return "Default"
}
//= ["4:3","5:4","16:9","16:10","2.21:1","2.35:1","2.39:1","21:9”]
return ""
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment