mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 13:50:30 +05:30
feat: button to screenshot/capture current frame
This commit is contained in:
parent
f086b3ac4a
commit
52fb2a9861
@ -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
|
||||
@ -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
|
||||
@ -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)!!
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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)
|
||||
|
@ -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>
|
||||
@ -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"
|
||||
|
@ -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>
|
||||
@ -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"
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user