coding-blocks/CBOnlineApp

View on GitHub
app/src/main/java/com/codingblocks/cbonlineapp/mycourse/content/player/VideoPlayerActivity.kt

Summary

Maintainability
F
1 wk
Test Coverage
package com.codingblocks.cbonlineapp.mycourse.content.player

import android.animation.LayoutTransition
import android.annotation.TargetApi
import android.app.ActivityManager
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Rational
import android.view.View
import android.view.WindowManager
import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.observe
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import com.codingblocks.cbonlineapp.BuildConfig
import com.codingblocks.cbonlineapp.R
import com.codingblocks.cbonlineapp.baseclasses.BaseCBActivity
import com.codingblocks.cbonlineapp.commons.TabLayoutAdapter
import com.codingblocks.cbonlineapp.course.batches.RUNTIERS
import com.codingblocks.cbonlineapp.database.models.NotesModel
import com.codingblocks.cbonlineapp.library.EditNoteClickListener
import com.codingblocks.cbonlineapp.mycourse.content.player.VideoBottomSheet.Companion.VideoSheetType
import com.codingblocks.cbonlineapp.mycourse.content.player.doubts.VideoDoubtFragment
import com.codingblocks.cbonlineapp.mycourse.content.player.notes.VideoNotesFragment
import com.codingblocks.cbonlineapp.util.Animations
import com.codingblocks.cbonlineapp.util.CONTENT_ID
import com.codingblocks.cbonlineapp.util.FileUtils
import com.codingblocks.cbonlineapp.util.LECTURE
import com.codingblocks.cbonlineapp.util.MediaUtils.getYoutubeVideoId
import com.codingblocks.cbonlineapp.util.PreferenceHelper.Companion.getPrefs
import com.codingblocks.cbonlineapp.util.RUN_ATTEMPT_ID
import com.codingblocks.cbonlineapp.util.SECTION_ID
import com.codingblocks.cbonlineapp.util.TITLE
import com.codingblocks.cbonlineapp.util.VIDEO
import com.codingblocks.cbonlineapp.util.VIDEO_ID
import com.codingblocks.cbonlineapp.util.extensions.getPrefs
import com.codingblocks.cbonlineapp.util.extensions.openChrome
import com.codingblocks.cbonlineapp.util.extensions.setRv
import com.codingblocks.cbonlineapp.util.extensions.showDialog
import com.codingblocks.cbonlineapp.util.extensions.showSnackbar
import com.codingblocks.cbonlineapp.util.livedata.getDistinct
import com.codingblocks.cbonlineapp.util.livedata.observer
import com.codingblocks.cbonlineapp.util.widgets.ProgressDialog
import com.codingblocks.cbonlineapp.util.widgets.VdoPlayerControls
import com.codingblocks.cbonlineapp.workers.DownloadWorker
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerFullScreenListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.YouTubePlayerTracker
import com.vdocipher.aegis.media.ErrorDescription
import com.vdocipher.aegis.media.Track
import com.vdocipher.aegis.player.VdoPlayer
import com.vdocipher.aegis.player.VdoPlayer.PlayerHost.VIDEO_STRETCH_MODE_MAINTAIN_ASPECT_RATIO
import com.vdocipher.aegis.player.VdoPlayerSupportFragment
import kotlinx.android.synthetic.main.activity_video_player.*
import kotlinx.android.synthetic.main.my_fab_menu.*
import kotlinx.android.synthetic.main.vdo_control_view.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.design.snackbar
import org.jetbrains.anko.excludeFromRecents
import org.jetbrains.anko.intentFor
import org.jetbrains.anko.singleTop
import org.jetbrains.anko.toast
import org.json.JSONException
import org.koin.androidx.viewmodel.ext.android.stateViewModel
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit

class VideoPlayerActivity :
    BaseCBActivity(),
    EditNoteClickListener,
    AnkoLogger,
    VdoPlayer.InitializationListener,
    VdoPlayerControls.FullscreenActionListener,
    VdoPlayerControls.ControllerVisibilityListener,
    YouTubePlayerFullScreenListener,
    View.OnClickListener {

    private val vm: VideoPlayerViewModel by stateViewModel()

    private val animationUtils by lazy { Animations(this) }
    private val progressDialog by lazy { ProgressDialog.progressDialog(this) }

    val tracker = YouTubePlayerTracker()
    private var hasBeenIntoPIP: Boolean = false
    private var isCallingFromFinish: Boolean = false
    private var isYoutubeVideoReady: Boolean = false
    private var contentable: String = ""
    private lateinit var mPIPParams: PictureInPictureParams.Builder

    private lateinit var playerFragment: VdoPlayerSupportFragment
    private lateinit var videoPlayer: VdoPlayer
    private lateinit var youtubePlayer: YouTubePlayer

    private val sectionItemsAdapter = PlaylistAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_video_player)
        if (savedInstanceState == null) {
            vm.currentContentId = intent.getStringExtra(CONTENT_ID)
            vm.sectionId = intent.getStringExtra(SECTION_ID)
            vm.position = intent.getLongExtra(VIDEO_POSITION, 0)
        }
        setupViewPager()
        setupUI()
    }

    private fun setupUI() {

        vm.offlineSnackbar.observer(this) {
            rootLayout.showSnackbar(it, Snackbar.LENGTH_SHORT, action = false)
        }

        contentRv.setRv(this, sectionItemsAdapter)
        vm.contentList.observer(this) {
            sectionItemsAdapter.submitList(it.contents.filter { it.contentable == VIDEO || it.contentable == LECTURE }
                .sortedBy { it.order }, vm.currentContentId!!)
        }
        sectionItemsAdapter.onItemClick = {
            startActivity(
                createVideoPlayerActivityIntent(
                    this, it.ccid,
                    vm.sectionId
                        ?: ""
                )
            )
        }

        rootLayout.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        vm.currentOrientation = resources.configuration.orientation
        playerControlView.vdo_back.setOnClickListener {
            onBackPressed()
        }
        contentListContainer.setOnClickListener {
            contentListView.isVisible = !contentListView.isVisible
            videoFab.isVisible = !contentListView.isVisible
        }

        registerReceiver(mReceiver, IntentFilter(ACTION_MEDIA_CONTROL))
        videoFab.setOnClickListener {
            with(noteFabTv.isVisible) {
                noteFabTv.isVisible = !this
                doubtFabTv.isVisible = !this

                if (this) {
                    doubtFab.startAnimation(animationUtils.close)
                    noteFab.startAnimation(animationUtils.close)
                    videoFab.startAnimation(animationUtils.anticlock)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        fabMenu.setBackgroundColor(getColor(R.color.white_transparent))
                    } else {
                        fabMenu.setBackgroundColor(
                            ContextCompat.getColor(
                                this@VideoPlayerActivity,
                                R.color.white_transparent
                            )
                        )
                    }
                } else {
                    doubtFab.startAnimation(animationUtils.open)
                    noteFab.startAnimation(animationUtils.open)
                    videoFab.startAnimation(animationUtils.clock)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        fabMenu.setBackgroundColor(getColor(R.color.black_95))
                    } else {
                        fabMenu.setBackgroundColor(
                            ContextCompat.getColor(
                                this@VideoPlayerActivity,
                                R.color.black_95
                            )
                        )
                    }
                }
            }
        }
        doubtFab.setOnClickListener {
            vm.runAttempts.observer(this) {
                if (it.premium && RUNTIERS.LITE.name != it.runTier)
                    showDoubtSheet()
                else {
                    toast("Doubt Support is only available for PREMIUM+ Runs.")
                    openChrome(
                        BuildConfig.DISCUSS_URL + contentTitle.text.toString().replace(" ", "-")
                    )
                }
            }
        }

        noteFab.setOnClickListener {
            val notePos: Double? =
                if (youtubePlayerView.isVisible)
                    (tracker.currentSecond.div(1000)).toDouble()
                else
                    (videoPlayer.currentTime.div(1000)).toDouble()

            val newNote = NotesModel(
                duration = notePos ?: 0.0,
                contentTitle = contentTitle.text.toString()
            )
            showNoteSheet(VideoSheetType.NOTE_CREATE, newNote)
        }
        bookmarkBtn.setOnClickListener {
            if (bookmarkBtn.isActivated)
                vm.removeBookmark()
            else {
                vm.markBookmark()
            }
        }
        downloadBtn.setOnClickListener {
            if (vm.isDownloaded)
                showDeleteDialog()
            else
                startDownloadWorker()
        }
        autoPlaySwitch.setOnCheckedChangeListener { compoundButton, b ->
            getPrefs().SP_AUTO_PLAY = b
        }

        vm.content.getDistinct().observe(this) {
            //            vm.contentLength = it.contentLecture.lectureDuration
            autoPlaySwitch.isChecked = getPrefs().SP_AUTO_PLAY
            sectionItemsAdapter.updateSelectedItem(it.ccid)
            vm.attemptId.value = it.attempt_id
            sectionTitle.text = getString(R.string.section_name, it.sectionTitle)
            contentTitle.text = it.title
            contentable = it.contentable
            if (it.contentable == LECTURE) {
                vm.currentContentProgress = it.progress
                vm.isDownloaded = it.contentLecture.isDownloaded
                downloadBtn.isVisible = true
                downloadBtn.isActivated = vm.isDownloaded
                vm.currentVideoId.value = it.contentLecture.lectureId
                youtubePlayerView.isVisible = false
                videoContainer.visibility = View.VISIBLE
                initializePlayer()
                if (vm.isDownloaded) {
                    vm.getOtpProgress.postValue(true)
                } else {
                    vm.getOtpProgress.postValue(null)
                    vm.getOtp()
                }
            } else if (it.contentable == VIDEO) {
                downloadBtn.isVisible = false
                with(youtubePlayerView) {
                    lifecycle.addObserver(this)
                    isVisible = true
                    videoContainer.visibility = View.GONE
                    addFullScreenListener(this@VideoPlayerActivity)
                }
                setYoutubePlayer(it.contentVideo.videoUrl)
            } else {
                finish()
            }
            vm.bookmark.observe(this) {
                // Don't Remove
                bookmarkBtn.isActivated = if (it == null) false else it.bookmarkUid.isNotEmpty()
            }
        }
    }

    private fun showDeleteDialog() {
        showDialog(
            type = "Delete",
            image = R.drawable.ic_info,
            cancelable = false,
            primaryText = R.string.confirmation,
            secondaryText = R.string.delete_video_desc,
            primaryButtonText = R.string.confirm,
            secondaryButtonText = R.string.cancel,
            callback = { confirmed ->
                if (confirmed) {
                    vm.currentVideoId.value?.let { deleteFolder(it) }
                }
            }
        )
    }

    private fun startDownloadWorker() {
        val constraints = if (vm.prefs.SP_WIFI)
            Constraints.Builder().setRequiredNetworkType(NetworkType.UNMETERED).build()
        else
            Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
        val videoData = workDataOf(
            VIDEO_ID to vm.currentVideoId.value,
            TITLE to contentTitle.text.toString(),
            SECTION_ID to vm.sectionId,
            RUN_ATTEMPT_ID to vm.attemptId.value,
            CONTENT_ID to vm.currentContentId
        )

        val request: OneTimeWorkRequest =
            OneTimeWorkRequestBuilder<DownloadWorker>()
                .setConstraints(constraints)
                .setInputData(videoData)
                .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 20, TimeUnit.SECONDS)
                .build()

        WorkManager.getInstance().enqueue(request)
        rootLayout.showSnackbar("Download Video In Progress", Snackbar.LENGTH_LONG, action = false)
    }

    private fun setupViewPager() {
        val adapter = TabLayoutAdapter(supportFragmentManager)
        adapter.add(VideoDoubtFragment(), "Doubts")
        adapter.add(VideoNotesFragment(), "Notes")

        playerViewPager.adapter = adapter
        playerTabs.setupWithViewPager(playerViewPager)
        playerViewPager.offscreenPageLimit = 2
    }

    private fun initializePlayer() {
        playerFragment =
            supportFragmentManager.findFragmentById(R.id.videoView) as VdoPlayerSupportFragment
        playerFragment.videoStretchMode = VIDEO_STRETCH_MODE_MAINTAIN_ASPECT_RATIO
        playerFragment.initialize(this)
        showControls(false)
    }

    override fun onInitializationSuccess(
        playerHost: VdoPlayer.PlayerHost,
        player: VdoPlayer,
        wasRestored: Boolean
    ) {
        videoPlayer = player
        player.addPlaybackEventListener(playbackListener)
        playerControlView.apply {
            setPlayer(player)
            setFullscreenActionListener(this@VideoPlayerActivity)
            setControllerVisibilityListener(this@VideoPlayerActivity)
            setVdoParamsGenerator(vdoParamsGenerator)
        }
        showControls(true)
        vm.getOtpProgress.observer(this) {
            if (it && ::videoPlayer.isInitialized) {
                try {
                    videoPlayer.load(getVdoParams())
                    if (playerControlView.vdo_error.isVisible)
                        playerControlView.retryAfterError()
                } catch (e: Exception) {
                    toast("Player not ready please try again!!")
                }
            } else
                toast("there was some error with starting feed, try again")
        }
    }

    /**Function to generate new /Reload Video for opt and videoId*/
    @WorkerThread
    private fun getVdoParams(): VdoPlayer.VdoInitParams? {
        return if (vm.isDownloaded) {
            VdoPlayer.VdoInitParams.createParamsForOffline(vm.currentVideoId.value)
        } else {
            VdoPlayer.VdoInitParams.Builder()
                .setOtp(vm.mOtp)
                .setPlaybackInfo(vm.mPlaybackInfo)
                .setPreferredCaptionsLanguage("en")
                .build()
        }
    }

    override fun onInitializationFailure(p0: VdoPlayer.PlayerHost?, p1: ErrorDescription?) {
        toast(p1?.errorMsg + "")
    }

    private fun showControls(show: Boolean) {
        if (show) {
            playerControlView.show()
        } else {
            playerControlView.hide()
        }
    }

//    override fun onItemClick(position: Int, id: String) {
//        if (viewModel.contentId == id) {
//            if (youtubePlayerView.isVisible)
//                youtubePlayer.seekTo((position * 1000).toFloat())
//            else
//                videoPlayer?.seekTo(position.toLong() * 1000)
//        }
//    }

    private fun showFullScreen(show: Boolean) {
        showSystemUi(show)
        requestedOrientation = if (show) {
            window.setFlags(
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
            )
            // go to landscape orientation for fullscreen mode
            ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
        } else {
            window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
            // go to portrait orientation
            ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
        }
    }

    private fun showSystemUi(show: Boolean) {
        if (show) {
            window.decorView.systemUiVisibility = (
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            or View.SYSTEM_UI_FLAG_FULLSCREEN
                            or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    )
        } else {
            window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        }
    }

    private val playbackListener = object : VdoPlayer.PlaybackEventListener {
        override fun onTracksChanged(p0: Array<out Track>?, p1: Array<out Track>?) {
        }

        override fun onSeekTo(p0: Long) {
        }

        override fun onLoading(p0: VdoPlayer.VdoInitParams?) {
        }

        override fun onLoaded(p0: VdoPlayer.VdoInitParams?) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                if (isInPictureInPictureMode) {
                    playerControlView.isVisible = false
                }
            videoPlayer.playWhenReady = true
            videoPlayer.playbackSpeed = vm.prefs.SP_PLAYBACK_SPEED
            vm.position?.let { videoPlayer.seekTo(it) }
        }

        override fun onBufferUpdate(p0: Long) {
        }

        override fun onProgress(progress: Long) {
            checkProgress(progress, playerFragment.player.duration)
        }

        override fun onPlaybackSpeedChanged(speed: Float) {
            vm.prefs.SP_PLAYBACK_SPEED = speed
        }

        override fun onLoadError(p0: VdoPlayer.VdoInitParams, p1: ErrorDescription) {
            log("Error Message: ${p1.errorMsg}, Error Code: ${p1.errorCode} , ${p1.httpStatusCode}")
            when (p1.errorCode) {
                5110 -> {
                    rootLayout.snackbar("Seems like your download was corrupted.Please Download Again")
                    deleteFolder(vm.currentContentId ?: "")
                }
                in (2010..2020) -> {
                    vm.getOtp()
                }
                6120 -> {
                }
            }
        }

        override fun onMediaEnded(p0: VdoPlayer.VdoInitParams?) {
        }

        override fun onError(p0: VdoPlayer.VdoInitParams?, p1: ErrorDescription?) {
            FirebaseCrashlytics.getInstance().log(
                "Error Message: ${p1?.errorMsg}," +
                        " Error Code: ${p1?.errorCode} , ${p1?.httpStatusCode}"
            )
        }

        override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
        }
    }

    private fun checkProgress(progress: Long, duration: Long) {
        val per = duration * 0.9
        if (progress > per && vm.currentContentProgress != "DONE") {
            vm.currentContentProgress = "DONE"
            vm.updateProgress()
        }
        /**Remove [PlayerState] After 95%*/

        if (progress >= duration && autoPlaySwitch.isChecked) {
            if (sectionItemsAdapter.selectedItem < sectionItemsAdapter.currentList.lastIndex) {
                val nextItem = sectionItemsAdapter.currentList[sectionItemsAdapter.selectedItem + 1]
                startActivity(
                    createVideoPlayerActivityIntent(
                        this, nextItem.ccid,
                        vm.sectionId ?: ""
                    )
                )
            }
        }
        val completion = duration * 0.95
        if (progress > completion) {
            vm.deletePlayerState()
        }
    }

    private fun deleteFolder(contentId: String) {
        val dir = File(getExternalFilesDir(Environment.getDataDirectory().absolutePath), contentId)
        GlobalScope.launch(Dispatchers.Main) {
            progressDialog.show()
            withContext(Dispatchers.IO) { FileUtils.deleteRecursive(dir) }
            delay(3000)
            vm.updateDownload(0, contentId)
            progressDialog.dismiss()
        }
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        val newOrientation = newConfig.orientation
        val oldOrientation = vm.currentOrientation
        vm.currentOrientation = newOrientation
        super.onConfigurationChanged(newConfig)
        when (newOrientation) {
            oldOrientation -> {
            }
            Configuration.ORIENTATION_LANDSCAPE -> {
                // hide other views
                videoContentContainer.isVisible = false
                fabMenu.isVisible = false
                playerControlView.fitsSystemWindows = true

                if (::playerFragment.isInitialized) {
                    val paramsFragment: RelativeLayout.LayoutParams =
                        playerFragment.view?.layoutParams as RelativeLayout.LayoutParams
                    paramsFragment.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
                    paramsFragment.addRule(RelativeLayout.ALIGN_PARENT_TOP)
                    paramsFragment.addRule(RelativeLayout.ALIGN_PARENT_START)
                    paramsFragment.addRule(RelativeLayout.ALIGN_PARENT_END)
                }

                // hide system windows
                showControls(false)
            }
            else -> {
                // show other views
                videoContentContainer.isVisible = true
                fabMenu.isVisible = true
                playerControlView.fitsSystemWindows = false

                if (::playerFragment.isInitialized) {
                    val paramsFragment: RelativeLayout.LayoutParams =
                        playerFragment.view?.layoutParams as RelativeLayout.LayoutParams
                    paramsFragment.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
                    paramsFragment.removeRule(RelativeLayout.ALIGN_PARENT_TOP)
                    paramsFragment.removeRule(RelativeLayout.ALIGN_PARENT_START)
                    paramsFragment.removeRule(RelativeLayout.ALIGN_PARENT_END)
                }
                playerControlView?.setPadding(0, 0, 0, 0)
                // show system windows
            }
        }
    }

    private val mReceiver = object : BroadcastReceiver() {
        @RequiresApi(Build.VERSION_CODES.O)
        override fun onReceive(p0: Context?, intent: Intent?) {
            if (intent!!.action != ACTION_MEDIA_CONTROL) {
                return
            }

            val controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0)
            when (controlType) {
                CONTROL_TYPE_PLAY -> playVideo()
                CONTROL_TYPE_PAUSE -> pauseVideo()
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun playVideo() {
        if (contentable == LECTURE) {
            videoPlayer.playWhenReady = true
        } else {
            if (isYoutubeVideoReady)
                youtubePlayer.play()
            else
                return
        }
        updatePictureInPictureActions(
            R.drawable.ic_pause, "Pause",
            CONTROL_TYPE_PAUSE, REQUEST_PAUSE
        )
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun pauseVideo() {
        if (contentable == LECTURE) {
            videoPlayer.playWhenReady = false
        } else {
            if (isYoutubeVideoReady)
                youtubePlayer.pause()
            else
                return
        }
        updatePictureInPictureActions(
            R.drawable.ic_play, "Play",
            CONTROL_TYPE_PLAY, REQUEST_PLAY
        )
    }

    override fun onUserLeaveHint() {
        super.onUserLeaveHint()
        if (getPrefs().SP_PIP and !isCallingFromFinish) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
                    activatePIPMode()
        }
    }

    override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: Configuration?
    ) {

        if (isInPictureInPictureMode) {
            showControls(false)
            hasBeenIntoPIP = true
            playerControlView.isVisible = false
        } else {
            showControls(true)
            playerControlView.isVisible = true
        }

        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    internal fun updatePictureInPictureActions(
        @DrawableRes iconId: Int,
        title: String,
        controlType: Int,
        requestCode: Int
    ) {
        val actions = ArrayList<RemoteAction>()

        val intent = PendingIntent.getBroadcast(
            this@VideoPlayerActivity,
            requestCode,
            Intent(ACTION_MEDIA_CONTROL)
                .putExtra(EXTRA_CONTROL_TYPE, controlType),
            0
        )
        val icon = Icon.createWithResource(this@VideoPlayerActivity, iconId)
        actions.add(RemoteAction(icon, title, title, intent))

        mPIPParams.setActions(actions)
        setPictureInPictureParams(mPIPParams.build())
    }

    @TargetApi(Build.VERSION_CODES.O)
    fun activatePIPMode() {

        val width: Int
        val height: Int
        when (contentable) {
            LECTURE -> {
                width = playerFragment.requireView().width
                height = playerFragment.requireView().height
            }
            VIDEO -> {
                width = youtubePlayerView.width
                height = youtubePlayerView.height
            }
            else -> return
        }

        val aspectRatio = Rational(width, height)
        mPIPParams = PictureInPictureParams.Builder()
        mPIPParams.setAspectRatio(aspectRatio)

        updatePictureInPictureActions(
            R.drawable.ic_pause, "Pause",
            CONTROL_TYPE_PAUSE, REQUEST_PAUSE
        )

        enterPictureInPictureMode(mPIPParams.build())
    }

    private fun navToLauncherTask(appContext: Context) {
        val activityManager: ActivityManager =
            (appContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
        val appTasks: List<ActivityManager.AppTask> = activityManager.appTasks
        for (task in appTasks) {
            val baseIntent: Intent = task.taskInfo.baseIntent
            val categories = baseIntent.categories
            if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
                task.moveToFront()
                return
            }
        }
    }

    private fun setYoutubePlayer(youtubeUrl: String) {
        if (::youtubePlayer.isInitialized) {
            val id = getYoutubeVideoId(youtubeUrl)
            youtubePlayer.loadVideo(id, 0f)
        }
        youtubePlayerView.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {

            override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
                super.onCurrentSecond(youTubePlayer, second)
                checkProgress(second.toLong(), tracker.videoDuration.toLong())
            }

            override fun onReady(youTubePlayer: YouTubePlayer) {
                youtubePlayer = youTubePlayer
                youtubePlayer.addListener(tracker)
                isYoutubeVideoReady = true
                val id = getYoutubeVideoId(youtubeUrl)
                youTubePlayer.loadVideo(id, vm.position?.toFloat()?.div(1000) ?: 0f)
            }
        })
    }

    override fun onBackPressed() {
        if (vm.currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            showFullScreen(false)
            playerControlView.setFullscreenState(false)
        } else {
            super.onBackPressed()
        }
    }

    override fun onClick(note: NotesModel) {
        showNoteSheet(VideoSheetType.NOTE_EDIT, note)
    }

    private fun showNoteSheet(type: VideoSheetType, note: NotesModel? = null) {
        val args = Bundle()
        args.putSerializable("type", type)
        args.putSerializable("item", note)
        val noteSheet = VideoBottomSheet()
        noteSheet.arguments = args
        noteSheet.show(supportFragmentManager, noteSheet.tag)
    }

    private fun showDoubtSheet() {
        val doubtSheet = VideoBottomSheet()
        val args = Bundle()
        args.putSerializable("type", VideoSheetType.DOUBT_CREATE)
        doubtSheet.arguments = args
        doubtSheet.show(supportFragmentManager, doubtSheet.tag)
    }

    override fun onStop() {
        if (::playerFragment.isInitialized && videoContainer.isVisible) {
            playerFragment.player?.let { currentPlayer ->
                vm.position = currentPlayer.currentTime
                val duration = currentPlayer.duration
                val time = currentPlayer.currentTime
                if (time < duration * 0.95)
                    vm.savePlayerState(time, true)
            }
        } else if (::youtubePlayer.isInitialized) {

            val duration = (tracker.videoDuration * 1000).toLong()
            val time = (tracker.currentSecond * 1000).toLong()
            if (time < duration * 0.95)
                vm.savePlayerState(time, false)
        }
        super.onStop()
    }

    override fun onDestroy() {
        if (::playerFragment.isInitialized) {
            playerFragment.player?.release()
        }
        unregisterReceiver(mReceiver)
        super.onDestroy()
    }

    override fun onControllerVisibilityChange(visibility: Int) {
        if (vm.currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            if (visibility != View.VISIBLE) {
                showSystemUi(false)
            }
        }
    }

    override fun onFullscreenAction(enterFullscreen: Boolean): Boolean {
        showFullScreen(enterFullscreen)
        return true
    }

    override fun onYouTubePlayerEnterFullScreen() {
        youtubePlayerView.enterFullScreen()
        showFullScreen(true)
    }

    override fun onYouTubePlayerExitFullScreen() {
        youtubePlayerView.exitFullScreen()
        showFullScreen(false)
    }

    override fun finish() {
        isCallingFromFinish = true
        if (hasBeenIntoPIP) {
            navToLauncherTask(applicationContext)
        }
        super.finish()
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        vm.currentContentId = intent.getStringExtra(CONTENT_ID) ?: ""
        vm.sectionId = intent.getStringExtra(SECTION_ID) ?: ""
        vm.position = 0L
        if (::playerFragment.isInitialized) {
            try {
                playerFragment.player.stop()
            } catch (e: Exception) {
                // Not reaady
            }
        }
        if (::youtubePlayer.isInitialized) {
            youtubePlayer.pause()
        }
    }

    private val vdoParamsGenerator: VdoPlayerControls.VdoParamsGenerator =
        object : VdoPlayerControls.VdoParamsGenerator {

            override fun getNewVdoInitParams(): VdoPlayer.VdoInitParams? {
                return try {
                    getVdoParams()
                } catch (e: IOException) {
                    e.printStackTrace()
                    log("Error generating new otp and playbackInfo")
                    null
                } catch (e: JSONException) {
                    e.printStackTrace()
                    log("Error generating new otp and playbackInfo")

                    null
                }
            }
        }

    fun hideVideoFab() {
        noteFabTv.isVisible = false
        doubtFabTv.isVisible = false
        doubtFab.startAnimation(animationUtils.close)
        noteFab.startAnimation(animationUtils.close)
        videoFab.startAnimation(animationUtils.anticlock)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            fabMenu.setBackgroundColor(getColor(R.color.white_transparent))
        } else {
            fabMenu.setBackgroundColor(
                ContextCompat.getColor(
                    this@VideoPlayerActivity,
                    R.color.white_transparent
                )
            )
        }
    }

    companion object {

        fun createVideoPlayerActivityIntent(
            context: Context,
            contentId: String,
            sectionId: String,
            position: Long = 0
        ): Intent {
            return context.intentFor<VideoPlayerActivity>(
                CONTENT_ID to contentId,
                VIDEO_POSITION to position,
                SECTION_ID to sectionId
            ).singleTop().apply { if (getPrefs(context).SP_PIP) excludeFromRecents() }
        }

        private val ACTION_MEDIA_CONTROL = "media_control"
        private val EXTRA_CONTROL_TYPE = "control_type"
        private val CONTROL_TYPE_PLAY = 1
        private val CONTROL_TYPE_PAUSE = 2
        private val REQUEST_PLAY = 1
        private val REQUEST_PAUSE = 2
    }

    override fun onClick(v: View) {
    }
}