package dev.jdtech.jellyfin import android.app.AppOpsManager import android.app.PictureInPictureParams import android.content.Context import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Rect import android.media.AudioManager import android.os.Build import android.os.Bundle import android.os.Process import android.util.Rational import android.view.View import android.view.WindowManager import android.widget.Button import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView import android.widget.Space import android.widget.TextView import androidx.activity.viewModels import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.media3.common.C import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.DefaultTimeBar import androidx.media3.ui.PlayerView import androidx.navigation.navArgs import dagger.hilt.android.AndroidEntryPoint import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment import dev.jdtech.jellyfin.mpv.MPVPlayer import dev.jdtech.jellyfin.utils.PlayerGestureHelper import dev.jdtech.jellyfin.utils.PreviewScrubListener import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerEvents import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject import dev.jdtech.jellyfin.core.R as CoreR var isControlsLocked: Boolean = false @AndroidEntryPoint class PlayerActivity : BasePlayerActivity() { @Inject lateinit var appPreferences: AppPreferences lateinit var binding: ActivityPlayerBinding private var playerGestureHelper: PlayerGestureHelper? = null override val viewModel: PlayerActivityViewModel by viewModels() private var previewScrubListener: PreviewScrubListener? = null private val isPipSupported by lazy { // Check if device has PiP feature if (!packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { return@lazy false } // Check if PiP is enabled for the app val appOps = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appOps?.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, Process.myUid(), packageName) == AppOpsManager.MODE_ALLOWED } else { @Suppress("DEPRECATION") appOps?.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, Process.myUid(), packageName) == AppOpsManager.MODE_ALLOWED } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val args: PlayerActivityArgs by navArgs() binding = ActivityPlayerBinding.inflate(layoutInflater) setContentView(binding.root) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) binding.playerView.player = viewModel.player binding.playerView.setControllerVisibilityListener( PlayerView.ControllerVisibilityListener { visibility -> if (visibility == View.GONE) { hideSystemUI() } }, ) val playerControls = binding.playerView.findViewById(R.id.player_controls) val lockedControls = binding.playerView.findViewById(R.id.locked_player_view) isControlsLocked = false configureInsets(playerControls) configureInsets(lockedControls) if (appPreferences.playerGestures) { playerGestureHelper = PlayerGestureHelper( appPreferences, this, binding.playerView, getSystemService(Context.AUDIO_SERVICE) as AudioManager, ) } binding.playerView.findViewById(R.id.back_button).setOnClickListener { finish() } binding.playerView.findViewById(R.id.back_button_alt).setOnClickListener { finish() } val videoNameTextView = binding.playerView.findViewById(R.id.video_name) val audioButton = binding.playerView.findViewById(R.id.btn_audio_track) val subtitleButton = binding.playerView.findViewById(R.id.btn_subtitle) val speedButton = binding.playerView.findViewById(R.id.btn_speed) val skipIntroButton = binding.playerView.findViewById