Created
June 25, 2024 22:57
-
-
Save sajjadyousefnia/dbe56da92193cfcaa24702801e2b6eb7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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