feat: add double tap ripple animation (#401)

* Add double tap seeking animation

* Remove unnecessary formatting changes

* Order imports correctly

* Remove needless blank line

* feat: add ripple for playback (play / pause)

* refactor: clean up

---------

Co-authored-by: Jarne Demeulemeester <jarnedemeulemeester@gmail.com>
This commit is contained in:
mustafadakhel 2023-07-29 16:47:36 +03:00 committed by GitHub
parent 57c1e85b11
commit d86b162d4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 5 deletions

View file

@ -10,9 +10,13 @@ import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import dev.jdtech.jellyfin.AppPreferences
@ -72,8 +76,6 @@ class PlayerGestureHelper(
val viewWidth = playerView.measuredWidth
val areaWidth = viewWidth / 5 // Divide the view into 5 parts: 2:1:2
val currentPos = playerView.player?.currentPosition ?: 0
// Define the areas and their boundaries
val leftmostAreaStart = 0
val middleAreaStart = areaWidth * 2
@ -82,15 +84,15 @@ class PlayerGestureHelper(
when (e.x.toInt()) {
in leftmostAreaStart until middleAreaStart -> {
// Tapped on the leftmost area (seek backward)
playerView.player?.seekTo((currentPos - appPreferences.playerSeekBackIncrement).coerceAtLeast(0))
rewind()
}
in middleAreaStart until rightmostAreaStart -> {
// Tapped on the middle area (toggle pause/unpause)
playerView.player?.playWhenReady = !playerView.player?.playWhenReady!!
togglePlayback()
}
in rightmostAreaStart until viewWidth -> {
// Tapped on the rightmost area (seek forward)
playerView.player?.seekTo(currentPos + appPreferences.playerSeekForwardIncrement)
fastForward()
}
}
return true
@ -98,6 +100,68 @@ class PlayerGestureHelper(
},
)
private fun fastForward() {
val currentPosition = playerView.player?.currentPosition ?: 0
val fastForwardPosition = currentPosition + appPreferences.playerSeekForwardIncrement
seekTo(fastForwardPosition)
animateRipple(activity.binding.imageFfwdAnimationRipple)
}
private fun rewind() {
val currentPosition = playerView.player?.currentPosition ?: 0
val rewindPosition = currentPosition - appPreferences.playerSeekBackIncrement
seekTo(rewindPosition.coerceAtLeast(0))
animateRipple(activity.binding.imageRewindAnimationRipple)
}
private fun togglePlayback() {
playerView.player?.playWhenReady = !playerView.player?.playWhenReady!!
animateRipple(activity.binding.imagePlaybackAnimationRipple)
}
private fun seekTo(position: Long) {
playerView.player?.seekTo(position)
}
private fun animateRipple(image: ImageView) {
image
.animateSeekingRippleStart()
.withEndAction {
resetRippleImage(image)
}
.start()
}
private fun ImageView.animateSeekingRippleStart(): ViewPropertyAnimator {
val rippleImageHeight = this.height
val playerViewHeight = playerView.height.toFloat()
val playerViewWidth = playerView.width.toFloat()
val scaleDifference = playerViewHeight / rippleImageHeight
val playerViewAspectRatio = playerViewWidth / playerViewHeight
val scaleValue = scaleDifference * playerViewAspectRatio
return animate()
.alpha(1f)
.scaleX(scaleValue)
.scaleY(scaleValue)
.setDuration(180)
.setInterpolator(DecelerateInterpolator())
}
private fun resetRippleImage(image: ImageView) {
image
.animateSeekingRippleEnd()
.withEndAction {
image.scaleX = 1f
image.scaleY = 1f
}
.start()
}
private fun ImageView.animateSeekingRippleEnd() = animate()
.alpha(0f)
.setDuration(150)
.setInterpolator(AccelerateInterpolator())
private val seekGestureDetector = GestureDetector(
playerView.context,
object : GestureDetector.SimpleOnGestureListener() {

View file

@ -113,4 +113,30 @@
tools:ignore="ContentDescription" />
</LinearLayout>
<ImageView
android:id="@+id/image_ffwd_animation_ripple"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|end"
android:alpha="0"
android:src="@drawable/transparent_circle"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/image_rewind_animation_ripple"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|start"
android:alpha="0"
android:src="@drawable/transparent_circle"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/image_playback_animation_ripple"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:alpha="0"
android:src="@drawable/transparent_circle"
tools:ignore="ContentDescription" />
</FrameLayout>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="24dp"
android:height="24dp" />
<solid android:color="#22ffffff" />
</shape>