Recycler View Approach
In this part, we’ll follow more or less a similar algorithm to instantiate the player
class HmsGmsVideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
OnPlayWindowListener, OnWisePlayerListener, OnInteract {
val parent = itemView
private val frameLayout: FrameLayout
private val surfaceView: SurfaceView
private val extender: ImageButton
private val contentSocialLayout: LinearLayout
private val contentInfoLayout: LinearLayout
private val videoText: TextView
private val contentControlLayout: LinearLayout
private val r10s: ImageButton
private val playBtn: ImageButton
private val f10s: ImageButton
private val seekBar: SeekBar
private val lottieAnimationView: LottieAnimationView
private val infoPanelBtn: ImageButton
val sLovely:TextView
val sMessage:TextView
val sShare:TextView
private var dialog = Dialog(parent.context, R.style.BlurTheme)
private lateinit var vServiceProvider: TextView
private lateinit var vVideoUrl : TextView
private lateinit var vVideoSender : TextView
private lateinit var vVideoSenderID : TextView
private lateinit var vWidthHeight : TextView
private lateinit var vPlayMode : TextView
private lateinit var vBitrate : TextView
private lateinit var vVideoLovely : TextView
private lateinit var vVideoComments : TextView
private var isPlaying = false
private val mStopHandler = false
private val player: WisePlayer? by lazy {
WisePlayerInit.createPlayer()
}
private val mHandler: Handler by lazy { Handler() }
private val runnable: Runnable by lazy {
Runnable {
configureContentV()
configureControlV()
if (!mStopHandler) {
mHandler.postDelayed(runnable, Constants.DELAY_SECOND)
}
}
}
…
}
Let’s start by filling overridden methods and custom ones. If below part misses any overridden method you may leave them as they are:
- init()
init {
//initialize()
showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player init")
frameLayout = parent.findViewById(R.id.frameLayout)
surfaceView = parent.findViewById(R.id.surfaceView)
extender = parent.findViewById(R.id.extend_control)
contentSocialLayout = parent.findViewById(R.id.content_social)
contentInfoLayout = parent.findViewById(R.id.content_info)
videoText = parent.findViewById(R.id.video_name)
contentControlLayout = parent.findViewById(R.id.content_control)
r10s = parent.findViewById(R.id.r10s)
playBtn = parent.findViewById(R.id.play_btn)
f10s = parent.findViewById(R.id.f10s)
seekBar = parent.findViewById(R.id.seekBar)
lottieAnimationView = parent.findViewById(R.id.lottieView)
infoPanelBtn = parent.findViewById(R.id.info_panel_btn)
sLovely = parent.findViewById(R.id.lovely)
sMessage = parent.findViewById(R.id.comment)
sShare = parent.findViewById(R.id.share)
lottieAnimationView.visibility = View.VISIBLE
initDialog()
initUI(R.style.DialogSlide)
}
- shareUri(…)
fun shareUri(uri:String){
val sharingIntent = Intent(Intent.ACTION_SEND)
sharingIntent.type="text/html"
sharingIntent.putExtra(Intent.EXTRA_SUBJECT,"Share Video - Entertainment")
sharingIntent.putExtra(Intent.EXTRA_TEXT,uri)
parent.context.startActivity(Intent.createChooser(sharingIntent,"Share Video"))
}
- initDialog()
fun initDialog(){
val width = ViewGroup.LayoutParams.WRAP_CONTENT
val height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window!!.setLayout(width, height)
dialog.window!!.attributes.windowAnimations = R.style.DialogSlide
dialog.setContentView(R.layout.dialog_video_info)
dialog.setCanceledOnTouchOutside(true)
vServiceProvider = dialog.findViewById(R.id.d_video_service_provider)
vVideoUrl = dialog.findViewById(R.id.d_video_url)
vVideoSender = dialog.findViewById(R.id.d_video_sender)
vVideoSenderID = dialog.findViewById(R.id.d_video_sender_id)
vWidthHeight = dialog.findViewById(R.id.d_video_width_height)
vPlayMode = dialog.findViewById(R.id.d_video_play_mode)
vBitrate = dialog.findViewById(R.id.d_video_bitrate)
vVideoLovely = dialog.findViewById(R.id.d_video_given_lovelies)
vVideoComments = dialog.findViewById(R.id.d_video_total_comments)
}
- bindInformativeDialog()
fun bindInformativeDialog() {
val vExit = dialog.findViewById<TextView>(R.id.d_exit)
vServiceProvider.text = parent.context.getString(R.string.hms_video)
vWidthHeight.text = parent.context.getString(R.string.video_width_height_params,player?.videoWidth.toString(),player?.videoHeight.toString())
vPlayMode.text = player?.playMode.toString()
vBitrate.text = "${player?.currentStreamInfo?.bitrate?.toString()}"
vExit.setOnClickListener { dialog.dismiss() }
dialog.show()
}
- bindDialogInfo(…)
fun bindDialogInfo(vUrl:String, vSender:String, vSenderID:String, vLovely:String){
vVideoUrl.text=vUrl
vVideoSender.text=vSender
vVideoSenderID.text=vSenderID
vVideoLovely.text = NumberConvertor.prettyCount(vLovely.toLong())
}
- bindComments(…)
fun bindComments(vComments:String){
vVideoComments.text = NumberConvertor.prettyCount(vComments.toLong())
}
- bindVisibility()
override fun bindVisibility() {
extender.setOnClickListener {
contentInfoLayout.expandView()
contentControlLayout.expandView()
contentSocialLayout.expandView()
seekBar.expandView()
if (seekBar.visibility == View.GONE)
extender.setImageResource(R.drawable.ic_arrow_drop_up)
else
extender.setImageResource(R.drawable.ic_arrow_drop_down)
}
}
- initUI(…)
override fun initUI(type: Int) {
showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player initUI")
player?.setReadyListener(this)
player?.setErrorListener(this)
player?.setEventListener(this)
player?.setResolutionUpdatedListener(this)
player?.setLoadingListener(this)
player?.setPlayEndListener(this)
player?.setSeekEndListener(this)
f10s.setOnClickListener {
player?.seek(player?.currentTime!!+10000)
}
r10s.setOnClickListener {
player?.seek(player?.currentTime!!-10000)
}
surfaceView.holder.addCallback(this)
player?.cycleMode = PlayerConstants.CycleMode.MODE_NORMAL
playBtn.setOnClickListener {
changePlayState()
}
infoPanelBtn.setOnClickListener {
bindInformativeDialog()
}
}
- configureContentV()
override fun configureContentV() {
showLogInfo(
Constants.mHmsGmsVideoViewHolder,
"Player width: ${player?.videoWidth} - Height: ${player?.videoHeight}"
)
showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player is playing: ${player?.isPlaying}")
showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player play mode: ${player?.playMode}")
showLogInfo(
Constants.mHmsGmsVideoViewHolder, "Player bitrate: ${
player?.currentStreamInfo?.bitrate?.toString()
}"
)
}
- configureControlV()
override fun configureControlV() {
seekBar.max = player?.duration!!
seekBar.progress = player?.currentTime!!
seekBar.secondaryProgress = player?.bufferTime!!
}
- restartPlayer(…)
override fun restartPlayer(videoUrl: String) {
player?.reset()
player?.setMute(false)
readyPlayer(videoUrl, videoUrl)
player?.setView(surfaceView)
player?.resume(PlayerConstants.ResumeType.KEEP)
}
- changePlayState()
override fun changePlayState() {
if (isPlaying) {
player?.pause()
isPlaying = false
setPlayView()
} else {
player?.start()
isPlaying = true
setPauseView()
}
}
- setPauseView()
override fun setPauseView() {
playBtn.setImageResource(R.drawable.ic_pause)
lottieAnimationView.visibility=View.GONE
}
- setPlayView()
override fun setPlayView() {
playBtn.setImageResource(R.drawable.ic_play)
lottieAnimationView.visibility=View.VISIBLE
}
- readyPlayer(…)
override fun readyPlayer(videoUrl: String, string: String) {
player?.setPlayUrl(videoUrl)
player?.ready()
videoText.text = string
}
- surfaceCreated(…)
override fun surfaceCreated(holder: SurfaceHolder?) {
player?.setView(surfaceView)
player?.resume(PlayerConstants.ResumeType.KEEP)
}
- surfaceChanged(…)
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
player?.setSurfaceChange()
}
- surfaceDestroyed(…)
override fun surfaceDestroyed(holder: SurfaceHolder?) {
player?.suspend()
}
- onSurfaceTextureAvailable(…)
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
player?.resume(PlayerConstants.ResumeType.KEEP)
}
- onReady(…)
override fun onReady(p0: WisePlayer?) {
showLogInfo(Constants.mHmsGmsVideoViewHolder, "On Ready")
player?.start()
isPlaying = true
val activity = MainActivity()
activity.runOnUiThread {
configureControlV()
mHandler.postDelayed(runnable, Constants.DELAY_SECOND)
}
}
- onPlayEnd(…)
override fun onPlayEnd(p0: WisePlayer?) {
showLogInfo(Constants.mHmsGmsVideoViewHolder, "onPlayEnd")
isPlaying = false
setPlayView()
}
- onStartTrackingTouch(…)
override fun onStartTrackingTouch(seekBar: SeekBar?) {
seekBar?.progress?.let { player?.seek(it) }
}
- onStopTrackingTouch(…)
override fun onStopTrackingTouch(seekBar: SeekBar?) {
seekBar?.progress?.let { player?.seek(it) }
}
Comparing Exo Player with HMS Video Kit
ExoPlayer
- Easy to use lightweight, easy to implement
- For audio-only playback on some devices consumes more battery than standard MediaPlayer.
- Support for DASH and SmoothStreaming, HLS
- Uses Widevine common encryption.
- API level of at least 23 required to operate
- Video stutters when it fills the screen.
- Initial video loads almost instantly but the speed of loading degrades over the length of the video.
- Supports playing “m3u8” live stream formats.
- Reference for Exo Player
HMS Video Kit
- Initial implementations were sometimes complicate things.
- For audio-only playback on some devices consumes more battery than standard MediaPlayer.
- Supports 3GP, MP4, or TS format and compliant with HTTP/HTTPS, HLS, or DASH. Local videos are not supported.
- Uses WisePlay DRM
- HMS Core (APK) 4.0.1.300 or later and above is required to operate
- Video operates normally on a filled screen.
- Initial video load takes time but video almost never falls back down to loading state over the length of the video.
- Reference for HMS Video Kit
Output





References
- Firebase Realtime Database
- Firebase Cloud Storage
- Picasso
- Okhttp
- Okio
- Lottie
- AndroidSlidingUpPanel
- Slidr
- CircleImageView
- BottomBar
- AnimatedBottomBar
- Dagger
- CameraX
- Simple Video View
- Youtube Extractor
- Mockito
HMS
GMS
Pages: 1 2