Merge pull request #729 from Bnyro/master

Multi-Tap to seek forward/backward
This commit is contained in:
Bnyro 2022-07-08 18:20:20 +02:00 committed by GitHub
commit 6fb343a3e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 169 additions and 101 deletions

View File

@ -38,7 +38,6 @@ import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.google.android.material.color.MaterialColors
import com.google.android.material.elevation.SurfaceColors import com.google.android.material.elevation.SurfaceColors
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -100,7 +99,7 @@ class MainActivity : AppCompatActivity() {
navController = findNavController(R.id.fragment) navController = findNavController(R.id.fragment)
binding.bottomNav.setupWithNavController(navController) binding.bottomNav.setupWithNavController(navController)
// gets the surface color of the bottom navigation view // gets the surface color of the bottom navigation view
val color = SurfaceColors.getColorForElevation(this, 10F) val color = SurfaceColors.getColorForElevation(this, 10F)

View File

@ -1,15 +0,0 @@
package com.github.libretube.activities
import android.app.Activity
import android.os.Bundle
import com.github.libretube.R
import com.google.android.material.color.DynamicColors
class Player : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
DynamicColors.applyToActivityIfAvailable(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
}
}

View File

@ -11,6 +11,8 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION.SDK_INT
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.text.Html import android.text.Html
@ -21,6 +23,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -54,6 +57,7 @@ import com.github.libretube.util.CronetHelper
import com.github.libretube.util.DescriptionAdapter import com.github.libretube.util.DescriptionAdapter
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
import com.github.libretube.views.DoubleClickListener
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
@ -75,6 +79,7 @@ import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSource import com.google.android.exoplayer2.upstream.DefaultDataSource
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.util.RepeatModeUtil import com.google.android.exoplayer2.util.RepeatModeUtil
import com.google.android.exoplayer2.video.VideoSize
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
@ -319,10 +324,6 @@ class PlayerFragment : Fragment() {
isFullScreen = !isFullScreen isFullScreen = !isFullScreen
// scale the exo player center controls // scale the exo player center controls
playerBinding.exoFfwdWithAmount.scaleX = scaleFactor
playerBinding.exoFfwdWithAmount.scaleY = scaleFactor
playerBinding.exoRewWithAmount.scaleX = scaleFactor
playerBinding.exoRewWithAmount.scaleY = scaleFactor
playerBinding.exoPlayPause.scaleX = scaleFactor playerBinding.exoPlayPause.scaleX = scaleFactor
playerBinding.exoPlayPause.scaleY = scaleFactor playerBinding.exoPlayPause.scaleY = scaleFactor
} }
@ -691,6 +692,8 @@ class PlayerFragment : Fragment() {
playerBinding.exoTitle.text = response.title playerBinding.exoTitle.text = response.title
enableDoubleTapToSeek()
// init the chapters recyclerview // init the chapters recyclerview
if (response.chapters != null) initializeChapters(response.chapters) if (response.chapters != null) initializeChapters(response.chapters)
@ -705,7 +708,6 @@ class PlayerFragment : Fragment() {
} }
} }
/*
override fun onVideoSizeChanged( override fun onVideoSizeChanged(
videoSize: VideoSize videoSize: VideoSize
) { ) {
@ -713,14 +715,14 @@ class PlayerFragment : Fragment() {
// height or width must be cast to float as int/int will give 0 // height or width must be cast to float as int/int will give 0
// Redraw the player container with the new layout height // Redraw the player container with the new layout height
val params = binding.player.layoutParams
params.height = videoSize.height / videoSize.width * params.width
binding.player.layoutParams = params
binding.player.requestLayout()
(binding.mainContainer.layoutParams as ConstraintLayout.LayoutParams).apply { (binding.mainContainer.layoutParams as ConstraintLayout.LayoutParams).apply {
matchConstraintPercentHeight = ( matchConstraintPercentHeight = (videoSize.height / videoSize.width).toFloat()
videoSize.height / videoSize.width
).toFloat()
} }
binding.mainContainer.requestLayout()
} }
*/
@Deprecated(message = "Deprecated", level = DeprecationLevel.HIDDEN) @Deprecated(message = "Deprecated", level = DeprecationLevel.HIDDEN)
override fun onPlayerStateChanged( override fun onPlayerStateChanged(
@ -844,6 +846,51 @@ class PlayerFragment : Fragment() {
} }
} }
private fun enableDoubleTapToSeek() {
val seekIncrement =
PreferenceHelper.getString(requireContext(), "seek_increment", "5")?.toLong()!! * 1000
// enable rewind button
binding.rewindFL.setOnClickListener(
DoubleClickListener(
callback = object : DoubleClickListener.Callback {
override fun doubleClicked() {
binding.rewindBTN.visibility = View.VISIBLE
exoPlayer.seekTo(exoPlayer.currentPosition - seekIncrement)
Handler(Looper.getMainLooper()).postDelayed({
binding.rewindBTN.visibility = View.INVISIBLE
}, 700)
}
override fun singleClicked() {
if (exoPlayerView.isControllerFullyVisible) exoPlayerView.hideController()
else exoPlayerView.showController()
}
}
)
)
// enable fast forward button
binding.forwardFL.setOnClickListener(
DoubleClickListener(
callback = object : DoubleClickListener.Callback {
override fun doubleClicked() {
binding.forwardBTN.visibility = View.VISIBLE
exoPlayer.seekTo(exoPlayer.currentPosition + seekIncrement)
Handler(Looper.getMainLooper()).postDelayed({
binding.forwardBTN.visibility = View.INVISIBLE
}, 700)
}
override fun singleClicked() {
if (exoPlayerView.isControllerFullyVisible) exoPlayerView.hideController()
else exoPlayerView.showController()
}
}
)
)
}
private fun initializeChapters(chapters: List<ChapterSegment>) { private fun initializeChapters(chapters: List<ChapterSegment>) {
if (chapters.isNotEmpty()) { if (chapters.isNotEmpty()) {
binding.chaptersRecView.layoutManager = binding.chaptersRecView.layoutManager =
@ -1064,8 +1111,6 @@ class PlayerFragment : Fragment() {
val visibility = if (isLocked) View.VISIBLE else View.GONE val visibility = if (isLocked) View.VISIBLE else View.GONE
playerBinding.exoTopBarRight.visibility = visibility playerBinding.exoTopBarRight.visibility = visibility
playerBinding.exoPlayPause.visibility = visibility playerBinding.exoPlayPause.visibility = visibility
playerBinding.exoFfwdWithAmount.visibility = visibility
playerBinding.exoRewWithAmount.visibility = visibility
playerBinding.exoBottomBar.visibility = visibility playerBinding.exoBottomBar.visibility = visibility
playerBinding.closeImageButton.visibility = visibility playerBinding.closeImageButton.visibility = visibility
playerBinding.exoTitle.visibility = visibility playerBinding.exoTitle.visibility = visibility

View File

@ -0,0 +1,49 @@
package com.github.libretube.views
import android.os.Handler
import android.os.Looper
import android.view.View
class DoubleClickListener(
private val doubleClickTimeLimitMills: Long = 300,
private val callback: Callback
) : View.OnClickListener {
private var lastClicked: Long = -1L
private var doubleClicked: Boolean = false
override fun onClick(v: View?) {
lastClicked = when {
lastClicked == -1L -> {
doubleClicked = false
System.currentTimeMillis()
}
isDoubleClicked() -> {
doubleClicked = true
callback.doubleClicked()
-1L
}
else -> {
Handler(Looper.getMainLooper()).postDelayed({
if (!doubleClicked) callback.singleClicked()
}, doubleClickTimeLimitMills)
System.currentTimeMillis()
}
}
}
private fun getTimeDiff(from: Long, to: Long): Long {
return to - from
}
private fun isDoubleClicked(): Boolean {
return getTimeDiff(
lastClicked,
System.currentTimeMillis()
) <= doubleClickTimeLimitMills
}
interface Callback {
fun doubleClicked()
fun singleClicked()
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@android:color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@android:color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z" />
</vector>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context=".activities.Player">
<com.github.libretube.views.CustomExoPlayerView
android:id="@+id/fullscreen_player"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black" />
</RelativeLayout>

View File

@ -1,26 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2020 The Android Open Source Project <?xml version="1.0" encoding="utf-8"?>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android" <merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- 0dp dimensions are used to prevent this view from influencing the size of
the parent view if it uses "wrap_content". It is expanded to occupy the
entirety of the parent in code, after the parent's size has been
determined. See: https://github.com/google/ExoPlayer/issues/8726.
-->
<View <View
android:id="@id/exo_controls_background" android:id="@id/exo_controls_background"
android:layout_width="0dp" android:layout_width="0dp"
@ -189,7 +170,6 @@
android:id="@+id/progress_bar" android:id="@+id/progress_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_above="@id/exo_basic_controls"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
@ -226,48 +206,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:background="@android:color/transparent"
android:clipToPadding="false" android:clipToPadding="false"
android:gravity="center" android:gravity="center"
android:padding="@dimen/exo_styled_controls_padding"> android:padding="@dimen/exo_styled_controls_padding">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<Button
android:id="@id/exo_rew_with_amount"
style="@style/ExoStyledControls.Button.Center.RewWithAmount"
android:layout_gravity="center"
android:layout_marginStart="20dp"
android:paddingLeft="4dp"
android:paddingRight="4dp" />
</LinearLayout>
<ImageButton <ImageButton
android:id="@id/exo_play_pause" android:id="@id/exo_play_pause"
style="@style/ExoStyledControls.Button.Center.PlayPause" style="@style/ExoStyledControls.Button.Center.PlayPause"
app:tint="@android:color/white" /> app:tint="@android:color/white" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<Button
android:id="@id/exo_ffwd_with_amount"
style="@style/ExoStyledControls.Button.Center.FfwdWithAmount"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:paddingLeft="4dp"
android:paddingRight="4dp" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</merge> </merge>

View File

@ -358,11 +358,50 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@id/main_container" app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container" app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container" app:layout_constraintTop_toTopOf="@id/main_container"
app:show_buffering="always" /> app:show_buffering="always">
<FrameLayout
android:id="@+id/forwardFL"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:layout_marginVertical="50dp">
<ImageButton
android:id="@+id/forwardBTN"
android:layout_width="150dp"
android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless"
android:clickable="false"
android:src="@drawable/ic_forward"
android:visibility="invisible"
app:tint="@android:color/white" />
</FrameLayout>
<FrameLayout
android:id="@+id/rewindFL"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:layout_marginVertical="50dp">
<ImageButton
android:id="@+id/rewindBTN"
android:layout_width="150dp"
android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless"
android:clickable="false"
android:src="@drawable/ic_rewind"
android:visibility="invisible"
app:tint="@android:color/white" />
</FrameLayout>
</com.github.libretube.views.CustomExoPlayerView>
<ImageView <ImageView
android:id="@+id/close_imageView" android:id="@+id/close_imageView"