package com.applicaster.oasisplayer.player import android.app.Activity import android.app.Application import android.content.Context import android.content.res.Configuration import android.os.Handler import android.util.AttributeSet import android.util.Log import android.view.KeyEvent import android.view.View import android.widget.FrameLayout import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout import com.applicaster.oasisplayer.R import com.applicaster.oasisplayer.chromecast.OasisCastPlayer import com.applicaster.oasisplayer.config.oasis.ContentResourceBuilder import com.applicaster.oasisplayer.config.oasis.OasisContentResourceWrapper import com.applicaster.oasisplayer.config.playable.PlayableConfig import com.applicaster.oasisplayer.som.buildConfig import com.applicaster.oasisplayer.tracker.AnalyticsAgent import com.applicaster.oasisplayer.tracker.PlayerAnalyticsTracker import de.prosiebensat1digital.oasisplayer.player_api.FullscreenListener import de.prosiebensat1digital.oasisplayer.player_api.OasisPlayer import de.prosiebensat1digital.oasisplayer.widget.control.OasisUiRemoteController import de.prosiebensat1digital.somplugin.SomPlugin import kotlinx.android.synthetic.main.view_player_holder.view.* class OasisPlayerViewHolder : ConstraintLayout, PlayerHolder, PlayerInitializationListener { @JvmOverloads constructor(context: Context) : super(context) @JvmOverloads constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) @JvmOverloads constructor(context: Context, attrs: AttributeSet) : super(context, attrs) private var oasisPlayer: OasisPlayer? = null private var contentResource: OasisContentResourceWrapper? = null private val oasisPlayerStateProvider = OasisPlayerStateProvider() private val castPlayer = OasisCastPlayer(this) private var playMode: PlayMode? = null private var isCancellationReceived = false private var remoteController : OasisUiRemoteController? = null private var playerScreen: PlayerScreen? = null fun initPlayer(playerScreen: PlayerScreen, playMode: PlayMode, playableMap: Map?) { Log.d("QB-Oasis", "QB-Oasis initPlayer") this.playerScreen = playerScreen this.playMode = playMode val oasisPlayerBuilder = OasisPlayerBuilder() if (playableMap != null) { contentResource = ContentResourceBuilder().createFromPlayable(playableMap) contentResource?.let { initSomPlugin(it) generateAnalytics(it) castPlayer.onPlayerInitStarted() oasisPlayerBuilder.createPlayer(playerScreen.getActivity(), it, player_view, this) } } this.isFocusableInTouchMode = true; requestFocus(); } override fun isFocused(): Boolean { return true } private fun initSomPlugin(it: OasisContentResourceWrapper) { SomPlugin.onCreate( application = context?.applicationContext as Application, pageTopDomain = it.somConfig.domain.orEmpty(), pageSubDomain = if (it.somConfig.domain != it.somConfig.subdomain) it.somConfig.subdomain else null, contentURL = null, metaData = it.somConfig.countryMetadata.buildConfig() ) } override fun onInitFinished(oasisPlayer: OasisPlayer?) { if (oasisPlayer != null) { this.oasisPlayer = oasisPlayer remoteController = OasisUiRemoteController(oasisPlayer.defaultUi).also { oasisPlayer.addUi(it) } if (isCancellationReceived) { releasePlayerResources() } else { configureFullscreenRotation() oasisPlayer.addTracker(PlayerAnalyticsTracker()) oasisPlayer.addUi(oasisPlayerStateProvider) oasisPlayer.addFullscreenListener(listener = object :FullscreenListener{ override fun onFullscreenChanged(isFullscreen: Boolean) { if (isFullscreen) { setOasisFullscreenViewClickable() } } }) castPlayer.onPlayerCreated(oasisPlayer) if (shouldStartPlaying()) oasisPlayer.play() } } } override fun dispatchKeyEvent(event: KeyEvent): Boolean { return passKeyEventToOasis(event) } override fun dispatchRemoveControllerKeyEvent(event: KeyEvent): Boolean { return passKeyEventToOasis(event) } private fun passKeyEventToOasis(event: KeyEvent): Boolean { if (event.action == KeyEvent.ACTION_UP && event.keyCode == KeyEvent.KEYCODE_BACK) { handlePlayerBack() return super.dispatchKeyEvent(event) } else // Log.d("QB-Oasis", "QB-Oasis OasisPlayerViewHolder dispatchKeyEvent $event") return if (remoteController?.handleKeyEvent(event) == true) { // Key event exclusively handled by the remote controller Log.d("QB-Oasis", "QB-Oasis dispatchRemoteControllerKeyEvent handled by Oasis SDK - TRUE") true } else { // Log.d("QB-Oasis", "QB-Oasis dispatchRemoteControllerKeyEvent handled by Oasis SDK - FALSE") super.dispatchKeyEvent(event) } } override fun getCasterContext(): Context = context override fun getCasterLifecycle() = playerScreen?.getLifecycle() override fun getCurrentVideoPositionSec() = oasisPlayerStateProvider.videoPlaybackPositionSec?.toLong() ?: 0L override fun getPlayableConfig(): PlayableConfig? = contentResource?.playableConfig override fun onCastStarted(deviceName: String?) { when(playMode) { PlayMode.FULLSCREEN -> playerScreen?.finish() PlayMode.INLINE -> { oasisPlayer?.pause() showCastingUI(deviceName.orEmpty()) } } } override fun onCastStopped(videoPositionMillis: Long?) { when(playMode) { PlayMode.INLINE -> { hideCastingUI() if (videoPositionMillis != null && videoPositionMillis > 0) { oasisPlayer?.seekTo(videoPositionMillis/1000.toDouble()) } oasisPlayer?.play() } else -> {} } } override fun hideMinimizedCastView() { // if (playMode == PlayMode.INLINE) { // val castView = rootView.findViewById(R.id.castMiniController) // castView?.apply { // layoutParams.width = 0 // layoutParams.height = 0 // visibility = View.GONE // } // } } /** * Obtaining internal fullscreen root screen and setting it clickable * Temporary workaround for bug in Oasis 12.0.1 SDK * fullscreen_parent can have multiple instances in 12.0.1 SDK at the same time */ fun setOasisFullscreenViewClickable() { val firstFullscreenView = rootView.findViewById(R.id.fullscreen_parent) val parentView = firstFullscreenView?.parent as? FrameLayout if (parentView != null) { for (i in 0..parentView.childCount) { val childView = parentView.getChildAt(i) if (childView?.id == R.id.fullscreen_parent) { childView?.isClickable = true } } } } override fun getCurrentPlayMode() = playMode ?: PlayMode.FULLSCREEN private fun showCastingUI(deviceName: String){ cast_overlay.text = context.getString(R.string.lbl_connected_to_device, deviceName) cast_overlay.visibility = View.VISIBLE } private fun configureFullscreenRotation() { enableFullscreenWithoutOrientationChange() // when (playMode) { // PlayMode.FULLSCREEN -> enableFullscreenWithoutOrientationChange() // PlayMode.INLINE -> { // if (isLandscapeScreen(context)) { // // applicaster root screens doesn't expect to change orientation in runtime // // if screen is already in landscape avoid unnecessary rotation // enableFullscreenWithoutOrientationChange() // } else { // enableLandscapeOnFullscreen() // } // } // } } private fun enableLandscapeOnFullscreen() { oasisPlayer?.enableDefaultFullscreenView( setFullscreenOnLandscape = false, setLandscapeOnFullscreen = true ) } private fun enableFullscreenWithoutOrientationChange() { oasisPlayer?.enableDefaultFullscreenView( setFullscreenOnLandscape = false, setLandscapeOnFullscreen = false ) } private fun isLandscapeScreen(context: Context?) = (context as? Activity)?.resources?.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE private fun hideCastingUI(){ cast_overlay.visibility = View.GONE } private fun shouldStartPlaying() = cast_overlay?.visibility != View.VISIBLE private fun generateAnalytics(contentResource: OasisContentResourceWrapper) { if (playMode == PlayMode.FULLSCREEN) { AnalyticsAgent().trackScreenEvent( context = null, //workaround, with context Applicaster SDK will add unexpected additional params videoTitle = contentResource.playableConfig.title, somDomain = contentResource.somConfig.domain, videoType = contentResource.playableConfig.contentType.id.toUpperCase() ) } } /** * Workaround for AndroidTV: releasing player resources when exiting via back button click * TODO: Should be replaced with call from RN */ private fun handlePlayerBack() { Handler().postDelayed({ releasePlayerResources() }, 2500) } fun onViewDestroyed() { isCancellationReceived = true releasePlayerResources() } private fun releasePlayerResources() { Log.d("OasisPlayerViewHolder", "Releasing player resources") castPlayer.onPlayerDestroyed() oasisPlayer?.destroy() } }