diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a6a737a5..33534255 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,14 +12,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 - name: Build with Gradle run: ./gradlew lintDebug ktlintCheck assemble: @@ -29,14 +29,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 - name: Build with Gradle run: ./gradlew assembleDebug # Upload all build artifacts in separate steps. This can be shortened once https://github.com/actions/upload-artifact/pull/354 is merged. diff --git a/README.md b/README.md index 8d3a6ab2..9c7f0390 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,9 @@ I am developing this application in my spare time. - Subtitle codecs: SRT, VTT, SSA/ASS, DVDSUB - Optionally force software decoding when hardware decoding has issues. - Picture-in-picture mode +- Media chapters + - Timeline markers + - Chapter navigation gestures ## Planned features - Android TV diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt index 6045cb2c..1eb0daae 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt @@ -7,11 +7,13 @@ 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 @@ -27,15 +29,14 @@ 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.PlayerControlView 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 @@ -57,6 +58,7 @@ class PlayerActivity : BasePlayerActivity() { 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 @@ -113,10 +115,6 @@ class PlayerActivity : BasePlayerActivity() { 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) @@ -166,6 +164,18 @@ class PlayerActivity : BasePlayerActivity() { it.currentTrickPlay = currentTrickPlay } + // Chapters + if (appPreferences.showChapterMarkers && currentChapters != null) { + currentChapters?.let { chapters -> + val playerControlView = findViewById(R.id.exo_controller) + val numOfChapters = chapters.size + playerControlView.setExtraAdGroupMarkers( + LongArray(numOfChapters) { index -> chapters[index].startPosition }, + BooleanArray(numOfChapters) { false }, + ) + } + } + // File Loaded if (fileLoaded) { audioButton.isEnabled = true @@ -187,6 +197,11 @@ class PlayerActivity : BasePlayerActivity() { viewModel.eventsChannelFlow.collect { event -> when (event) { is PlayerEvents.NavigateBack -> finish() + is PlayerEvents.IsPlayingChanged -> { + if (appPreferences.playerPipGesture) { + setPictureInPictureParams(pipParams(event.isPlaying)) + } + } } } } @@ -256,9 +271,12 @@ class PlayerActivity : BasePlayerActivity() { pictureInPicture() } + // Set marker color + val timeBar = binding.playerView.findViewById(R.id.exo_progress) + timeBar.setAdMarkerColor(Color.WHITE) + if (appPreferences.playerTrickPlay) { val imagePreview = binding.playerView.findViewById(R.id.image_preview) - val timeBar = binding.playerView.findViewById(R.id.exo_progress) previewScrubListener = PreviewScrubListener( imagePreview, timeBar, @@ -281,12 +299,16 @@ class PlayerActivity : BasePlayerActivity() { } override fun onUserLeaveHint() { - if (appPreferences.playerPipGesture && viewModel.player.isPlaying && !isControlsLocked) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && + appPreferences.playerPipGesture && + viewModel.player.isPlaying && + !isControlsLocked + ) { pictureInPicture() } } - private fun pipParams(): PictureInPictureParams { + private fun pipParams(enableAutoEnter: Boolean = viewModel.player.isPlaying): PictureInPictureParams { val displayAspectRatio = Rational(binding.playerView.width, binding.playerView.height) val aspectRatio = binding.playerView.player?.videoSize?.let { @@ -314,24 +336,21 @@ class PlayerActivity : BasePlayerActivity() { ) } - return PictureInPictureParams.Builder() + val builder = PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) - .build() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + builder.setAutoEnterEnabled(enableAutoEnter) + } + + return builder.build() } private fun pictureInPicture() { if (!isPipSupported) { return } - binding.playerView.useController = false - binding.playerView.findViewById