Skip to content

Commit

Permalink
Merge pull request #6304 from Bnyro/master
Browse files Browse the repository at this point in the history
feat: button to screenshot/capture current frame
  • Loading branch information
Bnyro committed Jul 29, 2024
2 parents 39da265 + 52fb2a9 commit ff28918
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Bitmap
import android.media.session.PlaybackState
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.PixelCopy
import android.view.SurfaceView
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.motion.widget.TransitionAdapter
import androidx.core.content.ContextCompat
Expand Down Expand Up @@ -118,6 +123,7 @@ import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.ceil


@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
class PlayerFragment : Fragment(), OnlinePlayerOptions {
private var _binding: FragmentPlayerBinding? = null
Expand Down Expand Up @@ -352,6 +358,21 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
)

private var screenshotBitmap: Bitmap? = null
private val openScreenshotFile =
registerForActivityResult(ActivityResultContracts.CreateDocument("image/png")) { uri ->
if (uri == null) {
screenshotBitmap = null
return@registerForActivityResult
}

context?.contentResolver?.openOutputStream(uri)?.use { outputStream ->
screenshotBitmap?.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
}

screenshotBitmap = null
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val playerData = requireArguments().parcelable<PlayerData>(IntentData.playerData)!!
Expand Down Expand Up @@ -526,9 +547,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}

activity?.supportFragmentManager
?.setFragmentResultListener(CommentsSheet.HANDLE_LINK_REQUEST_KEY, viewLifecycleOwner) { _, bundle ->
?.setFragmentResultListener(
CommentsSheet.HANDLE_LINK_REQUEST_KEY,
viewLifecycleOwner
) { _, bundle ->
bundle.getString(IntentData.url)?.let { handleLink(it) }
}
}

binding.commentsToggle.setOnClickListener {
if (!this::streams.isInitialized) return@setOnClickListener
Expand Down Expand Up @@ -628,6 +652,28 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
binding.relPlayerScreenshot.setOnClickListener {
if (!this::exoPlayer.isInitialized || !this::streams.isInitialized) return@setOnClickListener
val surfaceView =
binding.player.videoSurfaceView as? SurfaceView ?: return@setOnClickListener

val bmp = Bitmap.createBitmap(
surfaceView.width,
surfaceView.height,
Bitmap.Config.ARGB_8888
)

PixelCopy.request(surfaceView, bmp, { _ ->
screenshotBitmap = bmp
val currentPosition = exoPlayer.currentPosition.toFloat() / 1000
openScreenshotFile.launch("${streams.title}-${currentPosition}.png")
}, Handler(Looper.getMainLooper()))
}
} else {
binding.relPlayerScreenshot.isGone = true
}

binding.playerChannel.setOnClickListener {
if (!this::streams.isInitialized) return@setOnClickListener

Expand Down Expand Up @@ -702,7 +748,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
binding.player.updateMarginsByFullscreenMode()

// set status bar icon color back to theme color after fullscreen dialog closed!
windowInsetsControllerCompat.isAppearanceLightStatusBars = !ThemeHelper.isDarkMode(requireContext())
windowInsetsControllerCompat.isAppearanceLightStatusBars =
!ThemeHelper.isDarkMode(requireContext())
}

/**
Expand Down Expand Up @@ -1031,13 +1078,17 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {

// close comment bottom sheet if opened for next video
activity?.supportFragmentManager?.fragments?.filterIsInstance<CommentsSheet>()
?.firstOrNull()?.dismiss()
?.firstOrNull()?.dismiss()
}

@SuppressLint("SetTextI18n")
private fun initializePlayerView() {
// initialize the player view actions
binding.player.initialize(doubleTapOverlayBinding, playerGestureControlsViewBinding, chaptersViewModel)
binding.player.initialize(
doubleTapOverlayBinding,
playerGestureControlsViewBinding,
chaptersViewModel
)
binding.player.initPlayerOptions(viewModel, viewLifecycleOwner, trackSelector, this)

binding.descriptionLayout.setStreams(streams)
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/res/layout-land/fragment_player.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
style="@style/PlayerActionsButton"
android:text="@string/pip"
app:icon="@drawable/ic_open" />

<com.google.android.material.button.MaterialButton
android:id="@+id/relPlayer_screenshot"
style="@style/PlayerActionsButton"
android:text="@string/screenshot"
app:icon="@drawable/ic_copy" />
</LinearLayout>

</HorizontalScrollView>
Expand Down Expand Up @@ -206,7 +212,8 @@
app:layout_constraintEnd_toEndOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container"
app:show_buffering="when_playing">
app:show_buffering="when_playing"
app:surface_type="surface_view">

<com.github.libretube.ui.views.DoubleTapOverlay
android:id="@+id/doubleTapOverlay"
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/res/layout/fragment_player.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
style="@style/PlayerActionsButton"
android:text="@string/pip"
app:icon="@drawable/ic_open" />

<com.google.android.material.button.MaterialButton
android:id="@+id/relPlayer_screenshot"
style="@style/PlayerActionsButton"
android:text="@string/screenshot"
app:icon="@drawable/ic_copy" />
</LinearLayout>

</HorizontalScrollView>
Expand Down Expand Up @@ -179,7 +185,8 @@
app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container"
app:show_buffering="when_playing">
app:show_buffering="when_playing"
app:surface_type="surface_view">

<com.github.libretube.ui.views.DoubleTapOverlay
android:id="@+id/doubleTapOverlay"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@
<string name="default_language">Default</string>
<string name="behavior_when_minimized">Behavior when minimized</string>
<string name="external_player">External player</string>
<string name="screenshot">Screenshot</string>

<!-- Backup & Restore Settings -->
<string name="import_subscriptions_from">Import subscriptions from</string>
Expand Down

0 comments on commit ff28918

Please sign in to comment.