SeekOverlay: Add seek overlay logic to player
This commit is contained in:
parent
3a40759cd2
commit
72eb3b4415
8 changed files with 206 additions and 57 deletions
|
@ -54,7 +54,7 @@ fun View.animate(
|
||||||
)
|
)
|
||||||
Log.d(TAG, "animate(): $msg")
|
Log.d(TAG, "animate(): $msg")
|
||||||
}
|
}
|
||||||
if (isVisible && enterOrExit) {
|
if (isVisible && enterOrExit && alpha == 1f && animationType == AnimationType.ALPHA) {
|
||||||
if (MainActivity.DEBUG) {
|
if (MainActivity.DEBUG) {
|
||||||
Log.d(TAG, "animate(): view was already visible > view = [$this]")
|
Log.d(TAG, "animate(): view was already visible > view = [$this]")
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,15 @@ fun View.animate(
|
||||||
}
|
}
|
||||||
animate().setListener(null).cancel()
|
animate().setListener(null).cancel()
|
||||||
isVisible = true
|
isVisible = true
|
||||||
|
|
||||||
|
val alphaRelativeDuration = if (enterOrExit && alpha < 1.0f) {
|
||||||
|
(duration * (1 - alpha)).toLong()
|
||||||
|
} else {
|
||||||
|
(duration * alpha).toLong()
|
||||||
|
}
|
||||||
|
|
||||||
when (animationType) {
|
when (animationType) {
|
||||||
AnimationType.ALPHA -> animateAlpha(enterOrExit, duration, delay, execOnEnd)
|
AnimationType.ALPHA -> animateAlpha(enterOrExit, alphaRelativeDuration, delay, execOnEnd)
|
||||||
AnimationType.SCALE_AND_ALPHA -> animateScaleAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
AnimationType.SCALE_AND_ALPHA -> animateScaleAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
||||||
AnimationType.LIGHT_SCALE_AND_ALPHA -> animateLightScaleAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
AnimationType.LIGHT_SCALE_AND_ALPHA -> animateLightScaleAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
||||||
AnimationType.SLIDE_AND_ALPHA -> animateSlideAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
AnimationType.SLIDE_AND_ALPHA -> animateSlideAndAlpha(enterOrExit, duration, delay, execOnEnd)
|
||||||
|
|
|
@ -136,6 +136,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
import com.squareup.picasso.Target;
|
import com.squareup.picasso.Target;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.schabi.newpipe.DownloaderImpl;
|
import org.schabi.newpipe.DownloaderImpl;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -154,6 +155,7 @@ import org.schabi.newpipe.info_list.StreamSegmentAdapter;
|
||||||
import org.schabi.newpipe.ktx.AnimationType;
|
import org.schabi.newpipe.ktx.AnimationType;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||||
|
import org.schabi.newpipe.player.event.DisplayPortion;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerGestureListener;
|
import org.schabi.newpipe.player.event.PlayerGestureListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
|
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
|
||||||
|
@ -188,6 +190,8 @@ import org.schabi.newpipe.util.StreamTypeUtil;
|
||||||
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
import org.schabi.newpipe.views.ExpandableSurfaceView;
|
import org.schabi.newpipe.views.ExpandableSurfaceView;
|
||||||
|
import org.schabi.newpipe.views.player.CircleClipTapView;
|
||||||
|
import org.schabi.newpipe.views.player.PlayerSeekOverlay;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -365,6 +369,7 @@ public final class Player implements
|
||||||
|
|
||||||
private int maxGestureLength; // scaled
|
private int maxGestureLength; // scaled
|
||||||
private GestureDetectorCompat gestureDetector;
|
private GestureDetectorCompat gestureDetector;
|
||||||
|
private PlayerGestureListener playerGestureListener;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Listeners and disposables
|
// Listeners and disposables
|
||||||
|
@ -525,9 +530,10 @@ public final class Player implements
|
||||||
binding.resizeTextView.setOnClickListener(this);
|
binding.resizeTextView.setOnClickListener(this);
|
||||||
binding.playbackLiveSync.setOnClickListener(this);
|
binding.playbackLiveSync.setOnClickListener(this);
|
||||||
|
|
||||||
final PlayerGestureListener listener = new PlayerGestureListener(this, service);
|
playerGestureListener = new PlayerGestureListener(this, service);
|
||||||
gestureDetector = new GestureDetectorCompat(context, listener);
|
gestureDetector = new GestureDetectorCompat(context, playerGestureListener);
|
||||||
binding.getRoot().setOnTouchListener(listener);
|
binding.getRoot().setOnTouchListener(playerGestureListener);
|
||||||
|
setupPlayerSeekOverlay();
|
||||||
|
|
||||||
binding.queueButton.setOnClickListener(this);
|
binding.queueButton.setOnClickListener(this);
|
||||||
binding.segmentsButton.setOnClickListener(this);
|
binding.segmentsButton.setOnClickListener(this);
|
||||||
|
@ -578,6 +584,83 @@ public final class Player implements
|
||||||
v.getPaddingRight(),
|
v.getPaddingRight(),
|
||||||
v.getPaddingBottom()));
|
v.getPaddingBottom()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupPlayerSeekOverlay() {
|
||||||
|
final int fadeDurations = 450;
|
||||||
|
binding.seekOverlay.showCircle(true)
|
||||||
|
.circleBackgroundColorInt(CircleClipTapView.COLOR_DARK_TRANSPARENT)
|
||||||
|
.seekSeconds(retrieveSeekDurationFromPreferences(this) / 1000)
|
||||||
|
.performListener(new PlayerSeekOverlay.PerformListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepare() {
|
||||||
|
if (invalidSeekConditions()) {
|
||||||
|
playerGestureListener.endMultiDoubleTap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.seekOverlay.arcSize(
|
||||||
|
CircleClipTapView.Companion.calculateArcSize(getSurfaceView())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart() {
|
||||||
|
animate(binding.seekOverlay, true, fadeDurations);
|
||||||
|
animate(binding.playbackControlsShadow,
|
||||||
|
!simpleExoPlayer.getPlayWhenReady(), fadeDurations);
|
||||||
|
animate(binding.playerTopShadow, false, fadeDurations);
|
||||||
|
animate(binding.playerBottomShadow, false, fadeDurations);
|
||||||
|
animate(binding.playbackControlRoot, false, fadeDurations);
|
||||||
|
hideSystemUIIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd() {
|
||||||
|
animate(binding.seekOverlay, false, fadeDurations);
|
||||||
|
if (!simpleExoPlayer.getPlayWhenReady()) {
|
||||||
|
showControls(fadeDurations);
|
||||||
|
} else {
|
||||||
|
showHideShadow(false, fadeDurations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean shouldFastForward(@NotNull final DisplayPortion portion) {
|
||||||
|
// Null indicates an invalid area or condition e.g. the middle portion
|
||||||
|
// or video start or end was reached during double tap seeking
|
||||||
|
if (invalidSeekConditions()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (portion == DisplayPortion.LEFT
|
||||||
|
// Small puffer to eliminate infinite rewind seeking
|
||||||
|
&& simpleExoPlayer.getCurrentPosition() > 500L) {
|
||||||
|
return false;
|
||||||
|
} else if (portion == DisplayPortion.RIGHT) {
|
||||||
|
return true;
|
||||||
|
} else /* portion == DisplayPortion.MIDDLE */ {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek(final boolean forward) {
|
||||||
|
playerGestureListener.keepInDoubleTapMode();
|
||||||
|
if (forward) {
|
||||||
|
fastForward();
|
||||||
|
} else {
|
||||||
|
fastRewind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean invalidSeekConditions() {
|
||||||
|
return simpleExoPlayer.getCurrentPosition() == simpleExoPlayer.getDuration()
|
||||||
|
|| currentState == STATE_COMPLETED
|
||||||
|
|| !isPrepared;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
playerGestureListener.doubleTapControls(binding.seekOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
@ -1905,6 +1988,7 @@ public final class Player implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showHideShadow(final boolean show, final long duration) {
|
private void showHideShadow(final boolean show, final long duration) {
|
||||||
|
animate(binding.playbackControlsShadow, show, duration, AnimationType.ALPHA, 0, null);
|
||||||
animate(binding.playerTopShadow, show, duration, AnimationType.ALPHA, 0, null);
|
animate(binding.playerTopShadow, show, duration, AnimationType.ALPHA, 0, null);
|
||||||
animate(binding.playerBottomShadow, show, duration, AnimationType.ALPHA, 0, null);
|
animate(binding.playerBottomShadow, show, duration, AnimationType.ALPHA, 0, null);
|
||||||
}
|
}
|
||||||
|
@ -2179,6 +2263,9 @@ public final class Player implements
|
||||||
stopProgressLoop();
|
stopProgressLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't let UI elements popup during double tap seeking. This state is entered sometimes
|
||||||
|
// during seeking/loading. This if-else check ensures that the controls aren't popping up.
|
||||||
|
if (!playerGestureListener.isDoubleTapping()) {
|
||||||
showControls(400);
|
showControls(400);
|
||||||
binding.loadingPanel.setVisibility(View.GONE);
|
binding.loadingPanel.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
@ -2190,7 +2277,7 @@ public final class Player implements
|
||||||
binding.playPauseButton.requestFocus();
|
binding.playPauseButton.requestFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
changePopupWindowFlags(IDLE_WINDOW_FLAGS);
|
changePopupWindowFlags(IDLE_WINDOW_FLAGS);
|
||||||
|
|
||||||
// Remove running notification when user does not want minimization to background or popup
|
// Remove running notification when user does not want minimization to background or popup
|
||||||
|
@ -2838,7 +2925,6 @@ public final class Player implements
|
||||||
}
|
}
|
||||||
seekBy(retrieveSeekDurationFromPreferences(this));
|
seekBy(retrieveSeekDurationFromPreferences(this));
|
||||||
triggerProgressUpdate();
|
triggerProgressUpdate();
|
||||||
showAndAnimateControl(R.drawable.ic_fast_forward, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fastRewind() {
|
public void fastRewind() {
|
||||||
|
@ -2847,7 +2933,6 @@ public final class Player implements
|
||||||
}
|
}
|
||||||
seekBy(-retrieveSeekDurationFromPreferences(this));
|
seekBy(-retrieveSeekDurationFromPreferences(this));
|
||||||
triggerProgressUpdate();
|
triggerProgressUpdate();
|
||||||
showAndAnimateControl(R.drawable.ic_fast_rewind, true);
|
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
@ -4279,6 +4364,10 @@ public final class Player implements
|
||||||
return binding.currentDisplaySeek;
|
return binding.currentDisplaySeek;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayerSeekOverlay getSeekOverlay() {
|
||||||
|
return binding.seekOverlay;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public WindowManager.LayoutParams getPopupLayoutParams() {
|
public WindowManager.LayoutParams getPopupLayoutParams() {
|
||||||
return popupLayoutParams;
|
return popupLayoutParams;
|
||||||
|
|
|
@ -55,12 +55,10 @@ public class PlayerGestureListener
|
||||||
player.hideControls(0, 0);
|
player.hideControls(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (portion == DisplayPortion.LEFT) {
|
if (portion == DisplayPortion.LEFT || portion == DisplayPortion.RIGHT) {
|
||||||
player.fastRewind();
|
startMultiDoubleTap(event);
|
||||||
} else if (portion == DisplayPortion.MIDDLE) {
|
} else if (portion == DisplayPortion.MIDDLE) {
|
||||||
player.playPause();
|
player.playPause();
|
||||||
} else if (portion == DisplayPortion.RIGHT) {
|
|
||||||
player.fastForward();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +234,7 @@ public class PlayerGestureListener
|
||||||
player.getLoadingPanel().setVisibility(View.GONE);
|
player.getLoadingPanel().setVisibility(View.GONE);
|
||||||
|
|
||||||
player.hideControls(0, 0);
|
player.hideControls(0, 0);
|
||||||
|
animate(player.getSeekOverlay(), false, 0);
|
||||||
animate(player.getCurrentDisplaySeek(), false, 0, ALPHA, 0);
|
animate(player.getCurrentDisplaySeek(), false, 0, ALPHA, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.schabi.newpipe.player.event.DisplayPortion
|
||||||
class CircleClipTapView(context: Context?, attrs: AttributeSet) : View(context, attrs) {
|
class CircleClipTapView(context: Context?, attrs: AttributeSet) : View(context, attrs) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val COLOR_DARK = 0x40000000
|
const val COLOR_DARK = 0x45000000
|
||||||
const val COLOR_DARK_TRANSPARENT = 0x30000000
|
const val COLOR_DARK_TRANSPARENT = 0x30000000
|
||||||
const val COLOR_LIGHT_TRANSPARENT = 0x25EEEEEE
|
const val COLOR_LIGHT_TRANSPARENT = 0x25EEEEEE
|
||||||
|
|
||||||
|
|
|
@ -5,24 +5,30 @@ import android.util.AttributeSet
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.*
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.DimenRes
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StyleRes
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.END
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.START
|
||||||
import androidx.constraintlayout.widget.ConstraintSet
|
import androidx.constraintlayout.widget.ConstraintSet
|
||||||
import androidx.constraintlayout.widget.ConstraintSet.*
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.widget.TextViewCompat
|
import androidx.core.widget.TextViewCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.android.synthetic.main.player_seek_overlay.view.*
|
|
||||||
import org.schabi.newpipe.MainActivity
|
import org.schabi.newpipe.MainActivity
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.player.event.DisplayPortion
|
import org.schabi.newpipe.player.event.DisplayPortion
|
||||||
import org.schabi.newpipe.player.event.DoubleTapListener
|
import org.schabi.newpipe.player.event.DoubleTapListener
|
||||||
|
|
||||||
class PlayerSeekOverlay(context: Context?, private val attrs: AttributeSet?) :
|
class PlayerSeekOverlay(context: Context, private val attrs: AttributeSet?) :
|
||||||
ConstraintLayout(context, attrs), DoubleTapListener {
|
ConstraintLayout(context, attrs), DoubleTapListener {
|
||||||
|
|
||||||
private var secondsView: SecondsView
|
private var secondsView: SecondsView
|
||||||
private var circleClipTapView: CircleClipTapView
|
private var circleClipTapView: CircleClipTapView
|
||||||
|
private var rootConstraintLayout: ConstraintLayout
|
||||||
|
|
||||||
private var isForwarding: Boolean? = null
|
private var isForwarding: Boolean? = null
|
||||||
|
|
||||||
|
@ -31,6 +37,7 @@ class PlayerSeekOverlay(context: Context?, private val attrs: AttributeSet?) :
|
||||||
|
|
||||||
secondsView = findViewById(R.id.seconds_view)
|
secondsView = findViewById(R.id.seconds_view)
|
||||||
circleClipTapView = findViewById(R.id.circle_clip_tap_view)
|
circleClipTapView = findViewById(R.id.circle_clip_tap_view)
|
||||||
|
rootConstraintLayout = findViewById(R.id.root_constraint_layout)
|
||||||
|
|
||||||
initializeAttributes()
|
initializeAttributes()
|
||||||
secondsView.isForward = true
|
secondsView.isForward = true
|
||||||
|
@ -161,11 +168,14 @@ class PlayerSeekOverlay(context: Context?, private val attrs: AttributeSet?) :
|
||||||
val shouldForward: Boolean = performListener?.shouldFastForward(portion) ?: return
|
val shouldForward: Boolean = performListener?.shouldFastForward(portion) ?: return
|
||||||
|
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
Log.d(TAG,"onDoubleTapProgressDown called with " +
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onDoubleTapProgressDown called with " +
|
||||||
"shouldForward = [$shouldForward], " +
|
"shouldForward = [$shouldForward], " +
|
||||||
"isForwarding = [$isForwarding], " +
|
"isForwarding = [$isForwarding], " +
|
||||||
"secondsView#isForward = [${secondsView.isForward}], " +
|
"secondsView#isForward = [${secondsView.isForward}], " +
|
||||||
"initTap = [$initTap], ")
|
"initTap = [$initTap], "
|
||||||
|
)
|
||||||
|
|
||||||
// Using this check prevents from fast switching (one touches)
|
// Using this check prevents from fast switching (one touches)
|
||||||
if (isForwarding != null && isForwarding != shouldForward) {
|
if (isForwarding != null && isForwarding != shouldForward) {
|
||||||
|
@ -234,18 +244,22 @@ class PlayerSeekOverlay(context: Context?, private val attrs: AttributeSet?) :
|
||||||
private fun changeConstraints(forward: Boolean) {
|
private fun changeConstraints(forward: Boolean) {
|
||||||
val constraintSet = ConstraintSet()
|
val constraintSet = ConstraintSet()
|
||||||
with(constraintSet) {
|
with(constraintSet) {
|
||||||
clone(root_constraint_layout)
|
clone(rootConstraintLayout)
|
||||||
if (forward) {
|
if (forward) {
|
||||||
clear(seconds_view.id, START)
|
clear(secondsView.id, START)
|
||||||
connect(seconds_view.id, END,
|
connect(
|
||||||
PARENT_ID, END)
|
secondsView.id, END,
|
||||||
|
PARENT_ID, END
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
clear(seconds_view.id, END)
|
clear(secondsView.id, END)
|
||||||
connect(seconds_view.id, START,
|
connect(
|
||||||
PARENT_ID, START)
|
secondsView.id, START,
|
||||||
|
PARENT_ID, START
|
||||||
|
)
|
||||||
}
|
}
|
||||||
secondsView.start()
|
secondsView.start()
|
||||||
applyTo(root_constraint_layout)
|
applyTo(rootConstraintLayout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import kotlinx.android.synthetic.main.player_seek_seconds_view.view.*
|
import kotlinx.android.synthetic.main.player_seek_seconds_view.view.*
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
|
|
||||||
class SecondsView(context: Context?, attrs: AttributeSet?) :
|
class SecondsView(context: Context, attrs: AttributeSet?) :
|
||||||
ConstraintLayout(context, attrs) {
|
ConstraintLayout(context, attrs) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -45,7 +45,6 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
val textView: TextView
|
val textView: TextView
|
||||||
get() = tv_seconds
|
get() = tv_seconds
|
||||||
|
|
||||||
|
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
var icon: Int = R.drawable.ic_play_seek_triangle
|
var icon: Int = R.drawable.ic_play_seek_triangle
|
||||||
set(value) {
|
set(value) {
|
||||||
|
@ -87,9 +86,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
icon_1.alpha = 0f
|
icon_1.alpha = 0f
|
||||||
icon_2.alpha = 0f
|
icon_2.alpha = 0f
|
||||||
icon_3.alpha = 0f
|
icon_3.alpha = 0f
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
icon_1.alpha = it
|
icon_1.alpha = it
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
secondAnimator.start()
|
secondAnimator.start()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -99,9 +100,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
icon_1.alpha = 1f
|
icon_1.alpha = 1f
|
||||||
icon_2.alpha = 0f
|
icon_2.alpha = 0f
|
||||||
icon_3.alpha = 0f
|
icon_3.alpha = 0f
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
icon_2.alpha = it
|
icon_2.alpha = it
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
thirdAnimator.start()
|
thirdAnimator.start()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -111,10 +114,12 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
icon_1.alpha = 1f
|
icon_1.alpha = 1f
|
||||||
icon_2.alpha = 1f
|
icon_2.alpha = 1f
|
||||||
icon_3.alpha = 0f
|
icon_3.alpha = 0f
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
icon_1.alpha = 1f - icon_3.alpha
|
icon_1.alpha = 1f - icon_3.alpha
|
||||||
icon_3.alpha = it
|
icon_3.alpha = it
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
fourthAnimator.start()
|
fourthAnimator.start()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -124,9 +129,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
icon_1.alpha = 0f
|
icon_1.alpha = 0f
|
||||||
icon_2.alpha = 1f
|
icon_2.alpha = 1f
|
||||||
icon_3.alpha = 1f
|
icon_3.alpha = 1f
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
icon_2.alpha = 1f - it
|
icon_2.alpha = 1f - it
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
fifthAnimator.start()
|
fifthAnimator.start()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -136,15 +143,19 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
icon_1.alpha = 0f
|
icon_1.alpha = 0f
|
||||||
icon_2.alpha = 0f
|
icon_2.alpha = 0f
|
||||||
icon_3.alpha = 1f
|
icon_3.alpha = 1f
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
icon_3.alpha = 1f - it
|
icon_3.alpha = 1f - it
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
firstAnimator.start()
|
firstAnimator.start()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
private inner class CustomValueAnimator(
|
private inner class CustomValueAnimator(
|
||||||
start: () -> Unit, update: (value: Float) -> Unit, end: () -> Unit
|
start: () -> Unit,
|
||||||
|
update: (value: Float) -> Unit,
|
||||||
|
end: () -> Unit
|
||||||
) : ValueAnimator() {
|
) : ValueAnimator() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -164,7 +175,6 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
|
||||||
override fun onAnimationCancel(animation: Animator?) = Unit
|
override fun onAnimationCancel(animation: Animator?) = Unit
|
||||||
|
|
||||||
override fun onAnimationRepeat(animation: Animator?) = Unit
|
override fun onAnimationRepeat(animation: Animator?) = Unit
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,19 @@
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playbackControlsShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_alignBottom="@+id/playbackControlRoot"
|
||||||
|
android:background="@color/video_overlay_color"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/playbackControlRoot"
|
android:id="@+id/playbackControlRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/video_overlay_color"
|
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
@ -754,4 +762,11 @@
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.player.PlayerSeekOverlay
|
||||||
|
android:id="@+id/seekOverlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:alpha="0" /> <!-- Required to make the first appearance fading correctly -->
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
|
@ -54,11 +54,19 @@
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playbackControlsShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_alignBottom="@+id/playbackControlRoot"
|
||||||
|
android:background="@color/video_overlay_color"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/playbackControlRoot"
|
android:id="@+id/playbackControlRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/video_overlay_color"
|
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
@ -751,4 +759,11 @@
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.player.PlayerSeekOverlay
|
||||||
|
android:id="@+id/seekOverlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:alpha="0" /> <!-- Required to make the first appearance fading corectly -->
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
Loading…
Reference in a new issue