package dev.jdtech.jellyfin import android.annotation.SuppressLint 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.Color import android.graphics.Rect import android.media.AudioManager import android.os.Build import android.os.Bundle import android.os.Process import android.provider.Settings 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.DefaultTimeBar import androidx.media3.ui.PlayerControlView import androidx.media3.ui.PlayerView import androidx.navigation.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder 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.models.VideoQuality 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 var wasZoom: Boolean = false 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) val changeQualityButton: ImageButton = findViewById(R.id.btnChangeQuality) changeQualityButton.setOnClickListener { showQualitySelectionDialog() } 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() } 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