diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index 9f1f57998..88f9b78b8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -37,7 +37,7 @@ import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.Disposable; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; public abstract class BaseStateFragment extends BaseFragment implements ViewContract { @State @@ -131,35 +131,35 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC @Override public void showLoading() { if (emptyStateView != null) { - animateView(emptyStateView, false, 150); + animate(emptyStateView, false, 150); } if (loadingProgressBar != null) { - animateView(loadingProgressBar, true, 400); + animate(loadingProgressBar, true, 400); } - animateView(errorPanelRoot, false, 150); + animate(errorPanelRoot, false, 150); } @Override public void hideLoading() { if (emptyStateView != null) { - animateView(emptyStateView, false, 150); + animate(emptyStateView, false, 150); } if (loadingProgressBar != null) { - animateView(loadingProgressBar, false, 0); + animate(loadingProgressBar, false, 0); } - animateView(errorPanelRoot, false, 150); + animate(errorPanelRoot, false, 150); } @Override public void showEmptyState() { isLoading.set(false); if (emptyStateView != null) { - animateView(emptyStateView, true, 200); + animate(emptyStateView, true, 200); } if (loadingProgressBar != null) { - animateView(loadingProgressBar, false, 0); + animate(loadingProgressBar, false, 0); } - animateView(errorPanelRoot, false, 150); + animate(errorPanelRoot, false, 150); } @Override @@ -174,11 +174,11 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC errorTextView.setText(message); if (showRetryButton) { - animateView(errorButtonRetry, true, 600); + animate(errorButtonRetry, true, 600); } else { - animateView(errorButtonRetry, false, 0); + animate(errorButtonRetry, false, 0); } - animateView(errorPanelRoot, true, 300); + animate(errorPanelRoot, true, 300); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index b25d23694..8c9ed2bef 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -76,11 +76,12 @@ import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.EmptyFragment; import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment; +import org.schabi.newpipe.ktx.AnimationType; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.dialog.PlaylistCreationDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.MainPlayer; +import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener; import org.schabi.newpipe.player.helper.PlayerHelper; @@ -125,7 +126,7 @@ import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public final class VideoDetailFragment @@ -745,8 +746,10 @@ public final class VideoDetailFragment } if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - animateView(appendControlsDetail, true, 250, 0, () -> - animateView(appendControlsDetail, false, 1500, 1000)); + animate(appendControlsDetail, true, 250, AnimationType.ALPHA, + 0, () -> + animate(appendControlsDetail, false, 1500, + AnimationType.ALPHA, 1000)); } return false; }; @@ -1334,8 +1337,8 @@ public final class VideoDetailFragment thumbnailImageView.setImageDrawable( AppCompatResources.getDrawable(requireContext(), imageResource)); - animateView(thumbnailImageView, false, 0, 0, - () -> animateView(thumbnailImageView, true, 500)); + animate(thumbnailImageView, false, 0, AnimationType.ALPHA, 0, + () -> animate(thumbnailImageView, true, 500)); } @Override @@ -1417,14 +1420,14 @@ public final class VideoDetailFragment contentRootLayoutHiding.setVisibility(View.INVISIBLE); } - animateView(thumbnailPlayButton, false, 50); - animateView(detailDurationView, false, 100); - animateView(detailPositionView, false, 100); - animateView(positionView, false, 50); + animate(thumbnailPlayButton, false, 50); + animate(detailDurationView, false, 100); + animate(detailPositionView, false, 100); + animate(positionView, false, 50); videoTitleTextView.setText(title); videoTitleTextView.setMaxLines(1); - animateView(videoTitleTextView, true, 0); + animate(videoTitleTextView, true, 0); videoDescriptionRootLayout.setVisibility(View.GONE); videoTitleToggleArrow.setVisibility(View.GONE); @@ -1466,7 +1469,7 @@ public final class VideoDetailFragment player != null && player.isFullscreen() ? View.GONE : View.VISIBLE); } } - animateView(thumbnailPlayButton, true, 200); + animate(thumbnailPlayButton, true, 200); videoTitleTextView.setText(title); if (!isEmpty(info.getSubChannelName())) { @@ -1530,12 +1533,12 @@ public final class VideoDetailFragment detailDurationView.setText(Localization.getDurationString(info.getDuration())); detailDurationView.setBackgroundColor( ContextCompat.getColor(activity, R.color.duration_background_color)); - animateView(detailDurationView, true, 100); + animate(detailDurationView, true, 100); } else if (info.getStreamType() == StreamType.LIVE_STREAM) { detailDurationView.setText(R.string.duration_live); detailDurationView.setBackgroundColor( ContextCompat.getColor(activity, R.color.live_duration_background_color)); - animateView(detailDurationView, true, 100); + animate(detailDurationView, true, 100); } else { detailDurationView.setVisibility(View.GONE); } @@ -1703,8 +1706,8 @@ public final class VideoDetailFragment // Show saved position from backStack if user allows it showPlaybackProgress(playQueue.getItem().getRecoveryPosition(), playQueue.getItem().getDuration() * 1000); - animateView(positionView, true, 500); - animateView(detailPositionView, true, 500); + animate(positionView, true, 500); + animate(detailPositionView, true, 500); } return; } @@ -1718,8 +1721,8 @@ public final class VideoDetailFragment .observeOn(AndroidSchedulers.mainThread()) .subscribe(state -> { showPlaybackProgress(state.getProgressTime(), info.getDuration() * 1000); - animateView(positionView, true, 500); - animateView(detailPositionView, true, 500); + animate(positionView, true, 500); + animate(detailPositionView, true, 500); }, e -> { if (DEBUG) { e.printStackTrace(); @@ -1747,8 +1750,8 @@ public final class VideoDetailFragment detailPositionView.setText(position); } if (positionView.getVisibility() != View.VISIBLE) { - animateView(positionView, true, 100); - animateView(detailPositionView, true, 100); + animate(positionView, true, 100); + animate(detailPositionView, true, 100); } } @@ -1802,8 +1805,8 @@ public final class VideoDetailFragment && player.getPlayQueue() != null && player.getPlayQueue().getItem() != null && player.getPlayQueue().getItem().getUrl().equals(url)) { - animateView(positionView, true, 100); - animateView(detailPositionView, true, 100); + animate(positionView, true, 100); + animate(detailPositionView, true, 100); } break; } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 02c7a4818..b251d4b93 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -46,7 +46,7 @@ import java.util.Arrays; import java.util.List; import java.util.Queue; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; public abstract class BaseListFragment extends BaseStateFragment implements ListViewContract, StateSaver.WriteRead, @@ -406,23 +406,17 @@ public abstract class BaseListFragment extends BaseStateFragment // Contract //////////////////////////////////////////////////////////////////////////*/ - @Override - public void showLoading() { - super.showLoading(); - // animateView(itemsList, false, 400); - } - @Override public void hideLoading() { super.hideLoading(); - animateView(itemsList, true, 300); + animate(itemsList, true, 300); } @Override public void showError(final String message, final boolean showRetryButton) { super.showError(message, showRetryButton); showListFooter(false); - animateView(itemsList, false, 200); + animate(itemsList, false, 200); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 003893517..834d8052f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -37,12 +37,12 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.ktx.AnimationType; import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; @@ -64,9 +64,9 @@ import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.schedulers.Schedulers; -import static org.schabi.newpipe.util.AnimationUtils.animateBackgroundColor; -import static org.schabi.newpipe.util.AnimationUtils.animateTextColor; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; public class ChannelFragment extends BaseListInfoFragment implements View.OnClickListener { @@ -224,7 +224,7 @@ public class ChannelFragment extends BaseListInfoFragment private void monitorSubscription(final ChannelInfo info) { final Consumer onError = (Throwable throwable) -> { - animateView(headerBinding.channelSubscribeButton, false, 100); + animate(headerBinding.channelSubscribeButton, false, 100); showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0); @@ -379,8 +379,8 @@ public class ChannelFragment extends BaseListInfoFragment subscribedText); } - animateView(headerBinding.channelSubscribeButton, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, - true, 100); + animate(headerBinding.channelSubscribeButton, true, 100, + AnimationType.LIGHT_SCALE_AND_ALPHA); } /*////////////////////////////////////////////////////////////////////////// @@ -436,7 +436,7 @@ public class ChannelFragment extends BaseListInfoFragment IMAGE_LOADER.cancelDisplayTask(headerBinding.channelBannerImage); IMAGE_LOADER.cancelDisplayTask(headerBinding.channelAvatarView); IMAGE_LOADER.cancelDisplayTask(headerBinding.subChannelAvatarView); - animateView(headerBinding.channelSubscribeButton, false, 100); + animate(headerBinding.channelSubscribeButton, false, 100); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java index 68b0d823a..c0abc469b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java @@ -16,8 +16,8 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ExtractorHelper; import io.reactivex.rxjava3.core.Single; @@ -84,16 +84,14 @@ public class CommentsFragment extends BaseListInfoFragment { public void handleResult(@NonNull final CommentsInfo result) { super.handleResult(result); - AnimationUtils.slideUp(requireView(), 120, 150, 0.06f); + ViewUtils.slideUp(requireView(), 120, 150, 0.06f); if (!result.getErrors().isEmpty()) { showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0); } - if (disposables != null) { - disposables.clear(); - } + disposables.clear(); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 8770e6936..2e5e64539 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -28,7 +28,7 @@ import org.schabi.newpipe.util.Localization; import icepick.State; import io.reactivex.rxjava3.core.Single; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; /** * Created by Christian Schabesberger on 23.09.17. @@ -160,7 +160,7 @@ public class KioskFragment extends BaseListInfoFragment { @Override public void showLoading() { super.showLoading(); - animateView(itemsList, false, 100); + animate(itemsList, false, 100); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 6e723a686..58985e38a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -61,7 +61,7 @@ import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr; public class PlaylistFragment extends BaseListInfoFragment { @@ -261,19 +261,19 @@ public class PlaylistFragment extends BaseListInfoFragment { @Override public void showLoading() { super.showLoading(); - animateView(headerBinding.getRoot(), false, 200); - animateView(itemsList, false, 100); + animate(headerBinding.getRoot(), false, 200); + animate(itemsList, false, 100); IMAGE_LOADER.cancelDisplayTask(headerBinding.uploaderAvatarView); - animateView(headerBinding.uploaderLayout, false, 200); + animate(headerBinding.uploaderLayout, false, 200); } @Override public void handleResult(@NonNull final PlaylistInfo result) { super.handleResult(result); - animateView(headerBinding.getRoot(), true, 100); - animateView(headerBinding.uploaderLayout, true, 300); + animate(headerBinding.getRoot(), true, 100); + animate(headerBinding.uploaderLayout, true, 300); headerBinding.uploaderLayout.setOnClickListener(null); // If we have an uploader put them into the UI if (!TextUtils.isEmpty(result.getUploaderName())) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 511040827..67663a073 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -51,12 +51,12 @@ import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearch import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.list.BaseListFragment; +import org.schabi.newpipe.ktx.AnimationType; import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorInfo; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; @@ -82,7 +82,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static java.util.Arrays.asList; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public class SearchFragment extends BaseListFragment> @@ -413,7 +413,7 @@ public class SearchFragment extends BaseListFragment view = [$this]") + } + animate().setListener(null).cancel() + isVisible = true + alpha = 1f + execOnEnd?.run() + return + } else if ((isGone || isInvisible) && !enterOrExit) { + if (MainActivity.DEBUG) { + Log.d(TAG, "animate(): view was already gone > view = [$this]") + } + animate().setListener(null).cancel() + isGone = true + alpha = 0f + execOnEnd?.run() + return + } + animate().setListener(null).cancel() + isVisible = true + when (animationType) { + AnimationType.ALPHA -> animateAlpha(enterOrExit, duration, delay, execOnEnd) + AnimationType.SCALE_AND_ALPHA -> animateScaleAndAlpha(enterOrExit, duration, delay, execOnEnd) + AnimationType.LIGHT_SCALE_AND_ALPHA -> animateLightScaleAndAlpha(enterOrExit, duration, delay, execOnEnd) + AnimationType.SLIDE_AND_ALPHA -> animateSlideAndAlpha(enterOrExit, duration, delay, execOnEnd) + AnimationType.LIGHT_SLIDE_AND_ALPHA -> animateLightSlideAndAlpha(enterOrExit, duration, delay, execOnEnd) + } +} + +/** + * Animate the background color of a view. + * + * @param duration the duration of the animation + * @param colorStart the background color to start with + * @param colorEnd the background color to end with + */ +fun View.animateBackgroundColor(duration: Long, @ColorInt colorStart: Int, @ColorInt colorEnd: Int) { + if (MainActivity.DEBUG) { + Log.d( + TAG, + "animateBackgroundColor() called with: " + + "view = [" + this + "], duration = [" + duration + "], " + + "colorStart = [" + colorStart + "], colorEnd = [" + colorEnd + "]" + ) + } + val empty = arrayOf(IntArray(0)) + val viewPropertyAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorStart, colorEnd) + viewPropertyAnimator.interpolator = FastOutSlowInInterpolator() + viewPropertyAnimator.duration = duration + viewPropertyAnimator.addUpdateListener { animation: ValueAnimator -> + backgroundTintListCompat = ColorStateList(empty, intArrayOf(animation.animatedValue as Int)) + } + viewPropertyAnimator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + backgroundTintListCompat = ColorStateList(empty, intArrayOf(colorEnd)) + } + + override fun onAnimationCancel(animation: Animator) { + onAnimationEnd(animation) + } + }) + viewPropertyAnimator.start() +} + +fun View.animateHeight(duration: Long, targetHeight: Int): ValueAnimator { + if (MainActivity.DEBUG) { + Log.d( + TAG, + "animateHeight: duration = [" + duration + "], " + + "from " + height + " to → " + targetHeight + " in: " + this + ) + } + val animator = ValueAnimator.ofFloat(height.toFloat(), targetHeight.toFloat()) + animator.interpolator = FastOutSlowInInterpolator() + animator.duration = duration + animator.addUpdateListener { animation: ValueAnimator -> + val value = animation.animatedValue as Float + layoutParams.height = value.toInt() + requestLayout() + } + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + layoutParams.height = targetHeight + requestLayout() + } + + override fun onAnimationCancel(animation: Animator) { + layoutParams.height = targetHeight + requestLayout() + } + }) + animator.start() + return animator +} + +fun View.animateRotation(duration: Long, targetRotation: Int) { + if (MainActivity.DEBUG) { + Log.d( + TAG, + "animateRotation: duration = [" + duration + "], " + + "from " + rotation + " to → " + targetRotation + " in: " + this + ) + } + animate().setListener(null).cancel() + animate() + .rotation(targetRotation.toFloat()).setDuration(duration) + .setInterpolator(FastOutSlowInInterpolator()) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationCancel(animation: Animator) { + rotation = targetRotation.toFloat() + } + + override fun onAnimationEnd(animation: Animator) { + rotation = targetRotation.toFloat() + } + }).start() +} + +private fun View.animateAlpha(enterOrExit: Boolean, duration: Long, delay: Long, execOnEnd: Runnable?) { + if (enterOrExit) { + animate().setInterpolator(FastOutSlowInInterpolator()).alpha(1f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + execOnEnd?.run() + } + }).start() + } else { + animate().setInterpolator(FastOutSlowInInterpolator()).alpha(0f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isGone = true + execOnEnd?.run() + } + }).start() + } +} + +private fun View.animateScaleAndAlpha(enterOrExit: Boolean, duration: Long, delay: Long, execOnEnd: Runnable?) { + if (enterOrExit) { + scaleX = .8f + scaleY = .8f + animate() + .setInterpolator(FastOutSlowInInterpolator()) + .alpha(1f).scaleX(1f).scaleY(1f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + execOnEnd?.run() + } + }).start() + } else { + scaleX = 1f + scaleY = 1f + animate() + .setInterpolator(FastOutSlowInInterpolator()) + .alpha(0f).scaleX(.8f).scaleY(.8f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isGone = true + execOnEnd?.run() + } + }).start() + } +} + +private fun View.animateLightScaleAndAlpha(enterOrExit: Boolean, duration: Long, delay: Long, execOnEnd: Runnable?) { + if (enterOrExit) { + alpha = .5f + scaleX = .95f + scaleY = .95f + animate() + .setInterpolator(FastOutSlowInInterpolator()) + .alpha(1f).scaleX(1f).scaleY(1f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + execOnEnd?.run() + } + }).start() + } else { + alpha = 1f + scaleX = 1f + scaleY = 1f + animate() + .setInterpolator(FastOutSlowInInterpolator()) + .alpha(0f).scaleX(.95f).scaleY(.95f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isGone = true + execOnEnd?.run() + } + }).start() + } +} + +private fun View.animateSlideAndAlpha(enterOrExit: Boolean, duration: Long, delay: Long, execOnEnd: Runnable?) { + if (enterOrExit) { + translationY = -height.toFloat() + alpha = 0f + animate() + .setInterpolator(FastOutSlowInInterpolator()).alpha(1f).translationY(0f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + execOnEnd?.run() + } + }).start() + } else { + animate() + .setInterpolator(FastOutSlowInInterpolator()) + .alpha(0f).translationY(-height.toFloat()) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isGone = true + execOnEnd?.run() + } + }).start() + } +} + +private fun View.animateLightSlideAndAlpha(enterOrExit: Boolean, duration: Long, delay: Long, execOnEnd: Runnable?) { + if (enterOrExit) { + translationY = -height / 2.0f + alpha = 0f + animate() + .setInterpolator(FastOutSlowInInterpolator()).alpha(1f).translationY(0f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + execOnEnd?.run() + } + }).start() + } else { + animate().setInterpolator(FastOutSlowInInterpolator()) + .alpha(0f).translationY(-height / 2.0f) + .setDuration(duration).setStartDelay(delay) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isGone = true + execOnEnd?.run() + } + }).start() + } +} + +fun View.slideUp(duration: Long, delay: Long, @FloatRange(from = 0.0, to = 1.0) translationPercent: Float) { + val newTranslationY = (resources.displayMetrics.heightPixels * translationPercent).toInt() + animate().setListener(null).cancel() + alpha = 0f + translationY = newTranslationY.toFloat() + visibility = View.VISIBLE + animate() + .alpha(1f) + .translationY(0f) + .setStartDelay(delay) + .setDuration(duration) + .setInterpolator(FastOutSlowInInterpolator()) + .start() +} + +enum class AnimationType { + ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA, SLIDE_AND_ALPHA, LIGHT_SLIDE_AND_ALPHA +} diff --git a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java index 38ecc1c63..d454da08b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java @@ -24,7 +24,7 @@ import org.schabi.newpipe.databinding.PignateFooterBinding; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.list.ListViewContract; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; /** * This fragment is design to be used with persistent data such as @@ -185,10 +185,10 @@ public abstract class BaseLocalListFragment extends BaseStateFragment public void showLoading() { super.showLoading(); if (itemsList != null) { - animateView(itemsList, false, 200); + animate(itemsList, false, 200); } if (headerRootBinding != null) { - animateView(headerRootBinding.getRoot(), false, 200); + animate(headerRootBinding.getRoot(), false, 200); } } @@ -196,10 +196,10 @@ public abstract class BaseLocalListFragment extends BaseStateFragment public void hideLoading() { super.hideLoading(); if (itemsList != null) { - animateView(itemsList, true, 200); + animate(itemsList, true, 200); } if (headerRootBinding != null) { - animateView(headerRootBinding.getRoot(), true, 200); + animate(headerRootBinding.getRoot(), true, 200); } } @@ -209,10 +209,10 @@ public abstract class BaseLocalListFragment extends BaseStateFragment showListFooter(false); if (itemsList != null) { - animateView(itemsList, false, 200); + animate(itemsList, false, 200); } if (headerRootBinding != null) { - animateView(headerRootBinding.getRoot(), false, 200); + animate(headerRootBinding.getRoot(), false, 200); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 6df15c8b2..6e95f009f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -42,9 +42,9 @@ import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.databinding.ErrorRetryBinding import org.schabi.newpipe.databinding.FragmentFeedBinding import org.schabi.newpipe.fragments.list.BaseListFragment +import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.local.feed.service.FeedLoadService import org.schabi.newpipe.report.UserAction -import org.schabi.newpipe.util.AnimationUtils.animateView import org.schabi.newpipe.util.Localization import java.util.Calendar @@ -180,50 +180,50 @@ class FeedFragment : BaseListFragment() { // ///////////////////////////////////////////////////////////////////////// override fun showLoading() { - animateView(feedBinding.refreshRootView, false, 0) - animateView(feedBinding.itemsList, false, 0) + feedBinding.refreshRootView.animate(false, 0) + feedBinding.itemsList.animate(false, 0) - animateView(feedBinding.loadingProgressBar, true, 200) - animateView(feedBinding.loadingProgressText, true, 200) + feedBinding.loadingProgressBar.animate(true, 200) + feedBinding.loadingProgressText.animate(true, 200) - animateView(feedBinding.emptyStateView.root, false, 0) - animateView(errorBinding.root, false, 0) + feedBinding.emptyStateView.root.animate(false, 0) + errorBinding.root.animate(false, 0) } override fun hideLoading() { - animateView(feedBinding.refreshRootView, true, 200) - animateView(feedBinding.itemsList, true, 300) + feedBinding.refreshRootView.animate(true, 200) + feedBinding.itemsList.animate(true, 300) - animateView(feedBinding.loadingProgressBar, false, 0) - animateView(feedBinding.loadingProgressText, false, 0) + feedBinding.loadingProgressBar.animate(false, 0) + feedBinding.loadingProgressText.animate(false, 0) - animateView(feedBinding.emptyStateView.root, false, 0) - animateView(errorBinding.root, false, 0) + feedBinding.emptyStateView.root.animate(false, 0) + errorBinding.root.animate(false, 0) feedBinding.swiperefresh.isRefreshing = false } override fun showEmptyState() { - animateView(feedBinding.refreshRootView, true, 200) - animateView(feedBinding.itemsList, false, 0) + feedBinding.refreshRootView.animate(true, 200) + feedBinding.itemsList.animate(false, 0) - animateView(feedBinding.loadingProgressBar, false, 0) - animateView(feedBinding.loadingProgressText, false, 0) + feedBinding.loadingProgressBar.animate(false, 0) + feedBinding.loadingProgressText.animate(false, 0) - animateView(feedBinding.emptyStateView.root, true, 800) - animateView(errorBinding.root, false, 0) + feedBinding.emptyStateView.root.animate(true, 800) + errorBinding.root.animate(false, 0) } override fun showError(message: String, showRetryButton: Boolean) { infoListAdapter.clearStreamItemList() - animateView(feedBinding.refreshRootView, false, 120) - animateView(feedBinding.itemsList, false, 120) + feedBinding.refreshRootView.animate(false, 120) + feedBinding.itemsList.animate(false, 120) - animateView(feedBinding.loadingProgressBar, false, 120) - animateView(feedBinding.loadingProgressText, false, 120) + feedBinding.loadingProgressBar.animate(false, 120) + feedBinding.loadingProgressText.animate(false, 120) errorBinding.errorMessageView.text = message - animateView(errorBinding.errorButtonRetry, showRetryButton, if (showRetryButton) 600 else 0) - animateView(errorBinding.root, true, 300) + errorBinding.errorButtonRetry.animate(showRetryButton, if (showRetryButton) 600 else 0) + errorBinding.root.animate(true, 300) } override fun handleResult(result: FeedState) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 703271c42..fd6c8d1d1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -12,9 +12,9 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; @@ -117,10 +117,10 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { } else { itemProgressView.setProgress((int) TimeUnit.MILLISECONDS .toSeconds(item.getProgressTime())); - AnimationUtils.animateView(itemProgressView, true, 500); + ViewUtils.animate(itemProgressView, true, 500); } } else if (itemProgressView.getVisibility() == View.VISIBLE) { - AnimationUtils.animateView(itemProgressView, false, 500); + ViewUtils.animate(itemProgressView, false, 500); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index 95768b5c2..7c4e47c36 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -12,9 +12,9 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; @@ -148,10 +148,10 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { } else { itemProgressView.setProgress((int) TimeUnit.MILLISECONDS .toSeconds(item.getProgressTime())); - AnimationUtils.animateView(itemProgressView, true, 500); + ViewUtils.animate(itemProgressView, true, 500); } } else if (itemProgressView.getVisibility() == View.VISIBLE) { - AnimationUtils.animateView(itemProgressView, false, 500); + ViewUtils.animate(itemProgressView, false, 500); } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 08b7101e6..3137de9e6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -58,14 +58,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import icepick.State; -import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.ktx.ViewUtils.animate; public class LocalPlaylistFragment extends BaseLocalListFragment, Void> { // Save the list 10 seconds after the last change occurred @@ -201,8 +201,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment() { override fun showLoading() { super.showLoading() - animateView(binding.itemsList, false, 100) + binding.itemsList.animate(false, 100) } override fun hideLoading() { super.hideLoading() - animateView(binding.itemsList, true, 200) + binding.itemsList.animate(true, 200) } // ///////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt index 5fd70a684..00de83538 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt @@ -17,7 +17,7 @@ import kotlinx.android.synthetic.main.feed_import_export_group.import_from_optio import org.schabi.newpipe.R import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.exceptions.ExtractionException -import org.schabi.newpipe.util.AnimationUtils +import org.schabi.newpipe.ktx.animateRotation import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.ThemeHelper import org.schabi.newpipe.views.CollapsibleView @@ -49,8 +49,7 @@ class FeedImportExportItem( expandIconListener?.let { viewHolder.import_export_options.removeListener(it) } expandIconListener = CollapsibleView.StateListener { newState -> - AnimationUtils.animateRotation( - viewHolder.import_export_expand_icon, + viewHolder.import_export_expand_icon.animateRotation( 250, if (newState == CollapsibleView.COLLAPSED) 0 else 180 ) } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt index fd98d0447..0138b1ffe 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt @@ -9,8 +9,8 @@ import kotlinx.android.synthetic.main.picker_subscription_item.* import kotlinx.android.synthetic.main.picker_subscription_item.view.* import org.schabi.newpipe.R import org.schabi.newpipe.database.subscription.SubscriptionEntity -import org.schabi.newpipe.util.AnimationUtils -import org.schabi.newpipe.util.AnimationUtils.animateView +import org.schabi.newpipe.ktx.AnimationType +import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.util.ImageDisplayConstants data class PickerSubscriptionItem( @@ -41,9 +41,6 @@ data class PickerSubscriptionItem( fun updateSelected(containerView: View, isSelected: Boolean) { this.isSelected = isSelected - animateView( - containerView.selected_highlight, - AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150 - ) + containerView.selected_highlight.animate(isSelected, 150, AnimationType.LIGHT_SCALE_AND_ALPHA) } } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 4e2edaa10..2453117f3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -92,6 +92,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.info_list.StreamSegmentAdapter; +import org.schabi.newpipe.ktx.AnimationType; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; import org.schabi.newpipe.player.event.PlayerEventListener; @@ -116,7 +117,6 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; -import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.KoreUtil; @@ -150,6 +150,8 @@ import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; import static com.google.android.exoplayer2.Player.RepeatMode; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.schabi.newpipe.extractor.ServiceList.YouTube; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND; @@ -176,9 +178,6 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlayerTypeFr import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePopupLayoutParamsFromPrefs; import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences; import static org.schabi.newpipe.player.helper.PlayerHelper.savePlaybackParametersToPrefs; -import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA; -import static org.schabi.newpipe.util.AnimationUtils.animateRotation; -import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -1560,8 +1559,8 @@ public final class Player implements } showControls(0); - animateView(binding.currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, true, - DEFAULT_CONTROLS_DURATION); + animate(binding.currentDisplaySeek, true, DEFAULT_CONTROLS_DURATION, + AnimationType.SCALE_AND_ALPHA); } @Override // seekbar listener @@ -1576,7 +1575,7 @@ public final class Player implements } binding.playbackCurrentTime.setText(getTimeString(seekBar.getProgress())); - animateView(binding.currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); + animate(binding.currentDisplaySeek, false, 200, AnimationType.SCALE_AND_ALPHA); if (currentState == STATE_PAUSED_SEEK) { changeState(STATE_BUFFERING); @@ -1682,8 +1681,8 @@ public final class Player implements : DPAD_CONTROLS_HIDE_TIME; showHideShadow(true, DEFAULT_CONTROLS_DURATION); - animateView(binding.playbackControlRoot, true, DEFAULT_CONTROLS_DURATION, 0, - () -> hideControls(DEFAULT_CONTROLS_DURATION, hideTime)); + animate(binding.playbackControlRoot, true, DEFAULT_CONTROLS_DURATION, + AnimationType.ALPHA, 0, () -> hideControls(DEFAULT_CONTROLS_DURATION, hideTime)); } public void showControls(final long duration) { @@ -1694,7 +1693,7 @@ public final class Player implements showSystemUIPartially(); controlsVisibilityHandler.removeCallbacksAndMessages(null); showHideShadow(true, duration); - animateView(binding.playbackControlRoot, true, duration); + animate(binding.playbackControlRoot, true, duration); } public void hideControls(final long duration, final long delay) { @@ -1708,14 +1707,14 @@ public final class Player implements controlsVisibilityHandler.removeCallbacksAndMessages(null); controlsVisibilityHandler.postDelayed(() -> { showHideShadow(false, duration); - animateView(binding.playbackControlRoot, false, duration, 0, - this::hideSystemUIIfNeeded); + animate(binding.playbackControlRoot, false, duration, AnimationType.ALPHA, + 0, this::hideSystemUIIfNeeded); }, delay); } private void showHideShadow(final boolean show, final long duration) { - animateView(binding.playerTopShadow, show, duration, 0, null); - animateView(binding.playerBottomShadow, show, duration, 0, null); + animate(binding.playerTopShadow, show, duration, AnimationType.ALPHA, 0, null); + animate(binding.playerBottomShadow, show, duration, AnimationType.ALPHA, 0, null); } private void showOrHideButtons() { @@ -1913,15 +1912,15 @@ public final class Player implements } controlsVisibilityHandler.removeCallbacksAndMessages(null); - animateView(binding.playbackControlRoot, false, DEFAULT_CONTROLS_DURATION); + animate(binding.playbackControlRoot, false, DEFAULT_CONTROLS_DURATION); binding.playbackSeekBar.setEnabled(false); binding.playbackSeekBar.getThumb() .setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)); binding.loadingPanel.setBackgroundColor(Color.BLACK); - animateView(binding.loadingPanel, true, 0); - animateView(binding.surfaceForeground, true, 100); + animate(binding.loadingPanel, true, 0); + animate(binding.surfaceForeground, true, 100); binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp); animatePlayButtons(false, 100); @@ -1948,9 +1947,9 @@ public final class Player implements binding.loadingPanel.setVisibility(View.GONE); - animateView(binding.currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); + animate(binding.currentDisplaySeek, false, 200, AnimationType.SCALE_AND_ALPHA); - animateView(binding.playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, + animate(binding.playPauseButton, false, 80, AnimationType.SCALE_AND_ALPHA, 0, () -> { binding.playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp); animatePlayButtons(true, 200); @@ -1991,7 +1990,7 @@ public final class Player implements showControls(400); binding.loadingPanel.setVisibility(View.GONE); - animateView(binding.playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, + animate(binding.playPauseButton, false, 80, AnimationType.SCALE_AND_ALPHA, 0, () -> { binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp); animatePlayButtons(true, 200); @@ -2029,7 +2028,7 @@ public final class Player implements Log.d(TAG, "onCompleted() called"); } - animateView(binding.playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, + animate(binding.playPauseButton, false, 0, AnimationType.SCALE_AND_ALPHA, 0, () -> { binding.playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp); animatePlayButtons(true, DEFAULT_CONTROLS_DURATION); @@ -2051,13 +2050,13 @@ public final class Player implements } showControls(500); - animateView(binding.currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); + animate(binding.currentDisplaySeek, false, 200, AnimationType.SCALE_AND_ALPHA); binding.loadingPanel.setVisibility(View.GONE); - animateView(binding.surfaceForeground, true, 100); + animate(binding.surfaceForeground, true, 100); } private void animatePlayButtons(final boolean show, final int duration) { - animateView(binding.playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration); + animate(binding.playPauseButton, show, duration, AnimationType.SCALE_AND_ALPHA); boolean showQueueButtons = show; if (playQueue == null) { @@ -2065,18 +2064,18 @@ public final class Player implements } if (!showQueueButtons || playQueue.getIndex() > 0) { - animateView( + animate( binding.playPreviousButton, - AnimationUtils.Type.SCALE_AND_ALPHA, showQueueButtons, - duration); + duration, + AnimationType.SCALE_AND_ALPHA); } if (!showQueueButtons || playQueue.getIndex() + 1 < playQueue.getStreams().size()) { - animateView( + animate( binding.playNextButton, - AnimationUtils.Type.SCALE_AND_ALPHA, showQueueButtons, - duration); + duration, + AnimationType.SCALE_AND_ALPHA); } } //endregion @@ -2274,7 +2273,7 @@ public final class Player implements @Override public void onRenderedFirstFrame() { //TODO check if this causes black screen when switching to fullscreen - animateView(binding.surfaceForeground, false, DEFAULT_CONTROLS_DURATION); + animate(binding.surfaceForeground, false, DEFAULT_CONTROLS_DURATION); } //endregion @@ -2871,8 +2870,8 @@ public final class Player implements hideControls(0, 0); binding.itemsListPanel.requestFocus(); - animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, true, - DEFAULT_CONTROLS_DURATION); + animate(binding.itemsListPanel, true, DEFAULT_CONTROLS_DURATION, + AnimationType.SLIDE_AND_ALPHA); binding.itemsList.scrollToPosition(playQueue.getIndex()); } @@ -2905,8 +2904,8 @@ public final class Player implements hideControls(0, 0); binding.itemsListPanel.requestFocus(); - animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, true, - DEFAULT_CONTROLS_DURATION); + animate(binding.itemsListPanel, true, DEFAULT_CONTROLS_DURATION, + AnimationType.SLIDE_AND_ALPHA); final int adapterPosition = getNearestStreamSegmentPosition(simpleExoPlayer .getCurrentPosition()); @@ -2942,8 +2941,8 @@ public final class Player implements itemTouchHelper.attachToRecyclerView(null); } - animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, false, - DEFAULT_CONTROLS_DURATION, 0, () -> { + animate(binding.itemsListPanel, false, DEFAULT_CONTROLS_DURATION, + AnimationType.SLIDE_AND_ALPHA, 0, () -> { // Even when queueLayout is GONE it receives touch events // and ruins normal behavior of the app. This line fixes it binding.itemsListPanel.setTranslationY( @@ -3451,18 +3450,19 @@ public final class Player implements if (currentState != STATE_COMPLETED) { controlsVisibilityHandler.removeCallbacksAndMessages(null); showHideShadow(true, DEFAULT_CONTROLS_DURATION); - animateView(binding.playbackControlRoot, true, DEFAULT_CONTROLS_DURATION, 0, () -> { - if (currentState == STATE_PLAYING && !isSomePopupMenuVisible) { - if (v.getId() == binding.playPauseButton.getId() - // Hide controls in fullscreen immediately - || (v.getId() == binding.screenRotationButton.getId() - && isFullscreen)) { - hideControls(0, 0); - } else { - hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - } - } - }); + animate(binding.playbackControlRoot, true, DEFAULT_CONTROLS_DURATION, + AnimationType.ALPHA, 0, () -> { + if (currentState == STATE_PLAYING && !isSomePopupMenuVisible) { + if (v.getId() == binding.playPauseButton.getId() + // Hide controls in fullscreen immediately + || (v.getId() == binding.screenRotationButton.getId() + && isFullscreen)) { + hideControls(0, 0); + } else { + hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); + } + } + }); } } @@ -3531,9 +3531,8 @@ public final class Player implements animateRotation(binding.moreOptionsButton, DEFAULT_CONTROLS_DURATION, isMoreControlsVisible ? 0 : 180); - animateView(binding.secondaryControls, SLIDE_AND_ALPHA, !isMoreControlsVisible, - DEFAULT_CONTROLS_DURATION, 0, - () -> { + animate(binding.secondaryControls, !isMoreControlsVisible, DEFAULT_CONTROLS_DURATION, + AnimationType.SLIDE_AND_ALPHA, 0, () -> { // Fix for a ripple effect on background drawable. // When view returns from GONE state it takes more milliseconds than returning // from INVISIBLE state. And the delay makes ripple background end to fast diff --git a/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt index 46502a270..989c78c57 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt @@ -7,11 +7,11 @@ import android.view.GestureDetector import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration +import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.player.MainPlayer import org.schabi.newpipe.player.Player import org.schabi.newpipe.player.helper.PlayerHelper import org.schabi.newpipe.player.helper.PlayerHelper.savePopupPositionAndSizeToPrefs -import org.schabi.newpipe.util.AnimationUtils import kotlin.math.abs import kotlin.math.hypot import kotlin.math.max @@ -364,7 +364,7 @@ abstract class BasePlayerGestureListener( } if (!isMovingInPopup) { - AnimationUtils.animateView(player.closeOverlayButton, true, 200) + player.closeOverlayButton.animate(true, 200) } isMovingInPopup = true diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java index 887e32a23..ecc57ff2f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java @@ -17,11 +17,12 @@ import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.helper.PlayerHelper; -import static org.schabi.newpipe.player.Player.STATE_PLAYING; +import static org.schabi.newpipe.ktx.AnimationType.ALPHA; +import static org.schabi.newpipe.ktx.AnimationType.SCALE_AND_ALPHA; +import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.player.Player.DEFAULT_CONTROLS_DURATION; import static org.schabi.newpipe.player.Player.DEFAULT_CONTROLS_HIDE_TIME; -import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA; -import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.player.Player.STATE_PLAYING; /** * GestureListener for the player @@ -123,11 +124,11 @@ public class PlayerGestureListener final View closingOverlayView = player.getClosingOverlayView(); if (player.isInsideClosingRadius(movingEvent)) { if (closingOverlayView.getVisibility() == View.GONE) { - animateView(closingOverlayView, true, 250); + animate(closingOverlayView, true, 250); } } else { if (closingOverlayView.getVisibility() == View.VISIBLE) { - animateView(closingOverlayView, false, 0); + animate(closingOverlayView, false, 0); } } } @@ -153,7 +154,7 @@ public class PlayerGestureListener ); if (player.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) { - animateView(player.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200); + animate(player.getVolumeRelativeLayout(), true, 200, SCALE_AND_ALPHA); } if (player.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { player.getBrightnessRelativeLayout().setVisibility(View.GONE); @@ -195,7 +196,7 @@ public class PlayerGestureListener ); if (player.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) { - animateView(player.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200); + animate(player.getBrightnessRelativeLayout(), true, 200, SCALE_AND_ALPHA); } if (player.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { player.getVolumeRelativeLayout().setVisibility(View.GONE); @@ -215,21 +216,18 @@ public class PlayerGestureListener } if (player.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(player.getVolumeRelativeLayout(), SCALE_AND_ALPHA, - false, 200, 200); + animate(player.getVolumeRelativeLayout(), false, 200, SCALE_AND_ALPHA, + 200); } if (player.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(player.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, - false, 200, 200); + animate(player.getBrightnessRelativeLayout(), false, 200, SCALE_AND_ALPHA, + 200); } if (player.isControlsVisible() && player.getCurrentState() == STATE_PLAYING) { player.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); } } else { - if (player == null) { - return; - } if (player.isControlsVisible() && player.getCurrentState() == STATE_PLAYING) { player.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); } @@ -237,10 +235,10 @@ public class PlayerGestureListener if (player.isInsideClosingRadius(event)) { player.closePopup(); } else { - animateView(player.getClosingOverlayView(), false, 0); + animate(player.getClosingOverlayView(), false, 0); if (!player.isPopupClosing()) { - animateView(player.getCloseOverlayButton(), false, 200); + animate(player.getCloseOverlayButton(), false, 200); } } } @@ -255,8 +253,8 @@ public class PlayerGestureListener player.getLoadingPanel().setVisibility(View.GONE); player.hideControls(0, 0); - animateView(player.getCurrentDisplaySeek(), false, 0, 0); - animateView(player.getResizingIndicator(), true, 200, 0); + animate(player.getCurrentDisplaySeek(), false, 0, ALPHA, 0); + animate(player.getResizingIndicator(), true, 200, ALPHA, 0); } @Override @@ -264,7 +262,7 @@ public class PlayerGestureListener if (DEBUG) { Log.d(TAG, "onPopupResizingEnd called"); } - animateView(player.getResizingIndicator(), false, 100, 0); + animate(player.getResizingIndicator(), false, 100, ALPHA, 0); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java deleted file mode 100644 index 2675c2240..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright 2018 Mauricio Colli - * AnimationUtils.java is part of NewPipe - * - * License: GPL-3.0+ - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.schabi.newpipe.util; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.content.res.ColorStateList; -import android.util.Log; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.ColorInt; -import androidx.annotation.FloatRange; -import androidx.core.view.ViewCompat; -import androidx.interpolator.view.animation.FastOutSlowInInterpolator; - -import org.schabi.newpipe.MainActivity; - -public final class AnimationUtils { - private static final String TAG = "AnimationUtils"; - private static final boolean DEBUG = MainActivity.DEBUG; - - private AnimationUtils() { } - - public static void animateView(final View view, final boolean enterOrExit, - final long duration) { - animateView(view, Type.ALPHA, enterOrExit, duration, 0, null); - } - - public static void animateView(final View view, final boolean enterOrExit, - final long duration, final long delay) { - animateView(view, Type.ALPHA, enterOrExit, duration, delay, null); - } - - public static void animateView(final View view, final boolean enterOrExit, final long duration, - final long delay, final Runnable execOnEnd) { - animateView(view, Type.ALPHA, enterOrExit, duration, delay, execOnEnd); - } - - public static void animateView(final View view, final Type animationType, - final boolean enterOrExit, final long duration) { - animateView(view, animationType, enterOrExit, duration, 0, null); - } - - public static void animateView(final View view, final Type animationType, - final boolean enterOrExit, final long duration, - final long delay) { - animateView(view, animationType, enterOrExit, duration, delay, null); - } - - /** - * Animate the view. - * - * @param view view that will be animated - * @param animationType {@link Type} of the animation - * @param enterOrExit true to enter, false to exit - * @param duration how long the animation will take, in milliseconds - * @param delay how long the animation will wait to start, in milliseconds - * @param execOnEnd runnable that will be executed when the animation ends - */ - public static void animateView(final View view, final Type animationType, - final boolean enterOrExit, final long duration, - final long delay, final Runnable execOnEnd) { - if (DEBUG) { - String id; - try { - id = view.getResources().getResourceEntryName(view.getId()); - } catch (final Exception e) { - id = view.getId() + ""; - } - - final String msg = String.format("%8s → [%s:%s] [%s %s:%s] execOnEnd=%s", enterOrExit, - view.getClass().getSimpleName(), id, animationType, duration, delay, execOnEnd); - Log.d(TAG, "animateView()" + msg); - } - - if (view.getVisibility() == View.VISIBLE && enterOrExit) { - if (DEBUG) { - Log.d(TAG, "animateView() view was already visible > view = [" + view + "]"); - } - view.animate().setListener(null).cancel(); - view.setVisibility(View.VISIBLE); - view.setAlpha(1f); - if (execOnEnd != null) { - execOnEnd.run(); - } - return; - } else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) - && !enterOrExit) { - if (DEBUG) { - Log.d(TAG, "animateView() view was already gone > view = [" + view + "]"); - } - view.animate().setListener(null).cancel(); - view.setVisibility(View.GONE); - view.setAlpha(0f); - if (execOnEnd != null) { - execOnEnd.run(); - } - return; - } - - view.animate().setListener(null).cancel(); - view.setVisibility(View.VISIBLE); - - switch (animationType) { - case ALPHA: - animateAlpha(view, enterOrExit, duration, delay, execOnEnd); - break; - case SCALE_AND_ALPHA: - animateScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd); - break; - case LIGHT_SCALE_AND_ALPHA: - animateLightScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd); - break; - case SLIDE_AND_ALPHA: - animateSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd); - break; - case LIGHT_SLIDE_AND_ALPHA: - animateLightSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd); - break; - } - } - - /** - * Animate the background color of a view. - * - * @param view the view to animate - * @param duration the duration of the animation - * @param colorStart the background color to start with - * @param colorEnd the background color to end with - */ - public static void animateBackgroundColor(final View view, final long duration, - @ColorInt final int colorStart, - @ColorInt final int colorEnd) { - if (DEBUG) { - Log.d(TAG, "animateBackgroundColor() called with: " - + "view = [" + view + "], duration = [" + duration + "], " - + "colorStart = [" + colorStart + "], colorEnd = [" + colorEnd + "]"); - } - - final int[][] empty = {new int[0]}; - final ValueAnimator viewPropertyAnimator = ValueAnimator - .ofObject(new ArgbEvaluator(), colorStart, colorEnd); - viewPropertyAnimator.setInterpolator(new FastOutSlowInInterpolator()); - viewPropertyAnimator.setDuration(duration); - viewPropertyAnimator.addUpdateListener(animation -> - ViewCompat.setBackgroundTintList(view, - new ColorStateList(empty, new int[]{(int) animation.getAnimatedValue()}))); - viewPropertyAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - ViewCompat.setBackgroundTintList(view, - new ColorStateList(empty, new int[]{colorEnd})); - } - - @Override - public void onAnimationCancel(final Animator animation) { - onAnimationEnd(animation); - } - }); - viewPropertyAnimator.start(); - } - - /** - * Animate the text color of any view that extends {@link TextView} (Buttons, EditText...). - * - * @param view the text view to animate - * @param duration the duration of the animation - * @param colorStart the text color to start with - * @param colorEnd the text color to end with - */ - public static void animateTextColor(final TextView view, final long duration, - @ColorInt final int colorStart, - @ColorInt final int colorEnd) { - if (DEBUG) { - Log.d(TAG, "animateTextColor() called with: " - + "view = [" + view + "], duration = [" + duration + "], " - + "colorStart = [" + colorStart + "], colorEnd = [" + colorEnd + "]"); - } - - final ValueAnimator viewPropertyAnimator = ValueAnimator - .ofObject(new ArgbEvaluator(), colorStart, colorEnd); - viewPropertyAnimator.setInterpolator(new FastOutSlowInInterpolator()); - viewPropertyAnimator.setDuration(duration); - viewPropertyAnimator.addUpdateListener(animation -> - view.setTextColor((int) animation.getAnimatedValue())); - viewPropertyAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setTextColor(colorEnd); - } - - @Override - public void onAnimationCancel(final Animator animation) { - view.setTextColor(colorEnd); - } - }); - viewPropertyAnimator.start(); - } - - public static ValueAnimator animateHeight(final View view, final long duration, - final int targetHeight) { - final int height = view.getHeight(); - if (DEBUG) { - Log.d(TAG, "animateHeight: duration = [" + duration + "], " - + "from " + height + " to → " + targetHeight + " in: " + view); - } - - final ValueAnimator animator = ValueAnimator.ofFloat(height, targetHeight); - animator.setInterpolator(new FastOutSlowInInterpolator()); - animator.setDuration(duration); - animator.addUpdateListener(animation -> { - final float value = (float) animation.getAnimatedValue(); - view.getLayoutParams().height = (int) value; - view.requestLayout(); - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.getLayoutParams().height = targetHeight; - view.requestLayout(); - } - - @Override - public void onAnimationCancel(final Animator animation) { - view.getLayoutParams().height = targetHeight; - view.requestLayout(); - } - }); - animator.start(); - - return animator; - } - - public static void animateRotation(final View view, final long duration, - final int targetRotation) { - if (DEBUG) { - Log.d(TAG, "animateRotation: duration = [" + duration + "], " - + "from " + view.getRotation() + " to → " + targetRotation + " in: " + view); - } - view.animate().setListener(null).cancel(); - - view.animate() - .rotation(targetRotation).setDuration(duration) - .setInterpolator(new FastOutSlowInInterpolator()) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(final Animator animation) { - view.setRotation(targetRotation); - } - - @Override - public void onAnimationEnd(final Animator animation) { - view.setRotation(targetRotation); - } - }).start(); - } - - private static void animateAlpha(final View view, final boolean enterOrExit, - final long duration, final long delay, - final Runnable execOnEnd) { - if (enterOrExit) { - view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(1f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } else { - view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(0f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } - } - - /*////////////////////////////////////////////////////////////////////////// - // Internals - //////////////////////////////////////////////////////////////////////////*/ - - private static void animateScaleAndAlpha(final View view, final boolean enterOrExit, - final long duration, final long delay, - final Runnable execOnEnd) { - if (enterOrExit) { - view.setScaleX(.8f); - view.setScaleY(.8f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(1f).scaleX(1f).scaleY(1f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } else { - view.setScaleX(1f); - view.setScaleY(1f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(0f).scaleX(.8f).scaleY(.8f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } - } - - private static void animateLightScaleAndAlpha(final View view, final boolean enterOrExit, - final long duration, final long delay, - final Runnable execOnEnd) { - if (enterOrExit) { - view.setAlpha(.5f); - view.setScaleX(.95f); - view.setScaleY(.95f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(1f).scaleX(1f).scaleY(1f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } else { - view.setAlpha(1f); - view.setScaleX(1f); - view.setScaleY(1f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(0f).scaleX(.95f).scaleY(.95f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } - } - - private static void animateSlideAndAlpha(final View view, final boolean enterOrExit, - final long duration, final long delay, - final Runnable execOnEnd) { - if (enterOrExit) { - view.setTranslationY(-view.getHeight()); - view.setAlpha(0f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } else { - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(0f).translationY(-view.getHeight()) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } - } - - private static void animateLightSlideAndAlpha(final View view, final boolean enterOrExit, - final long duration, final long delay, - final Runnable execOnEnd) { - if (enterOrExit) { - view.setTranslationY(-view.getHeight() / 2.0f); - view.setAlpha(0f); - view.animate() - .setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } else { - view.animate().setInterpolator(new FastOutSlowInInterpolator()) - .alpha(0f).translationY(-view.getHeight() / 2.0f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) { - execOnEnd.run(); - } - } - }).start(); - } - } - - public static void slideUp(final View view, final long duration, final long delay, - @FloatRange(from = 0.0f, to = 1.0f) - final float translationPercent) { - final int translationY = (int) (view.getResources().getDisplayMetrics().heightPixels - * (translationPercent)); - - view.animate().setListener(null).cancel(); - view.setAlpha(0f); - view.setTranslationY(translationY); - view.setVisibility(View.VISIBLE); - view.animate() - .alpha(1f) - .translationY(0) - .setStartDelay(delay) - .setDuration(duration) - .setInterpolator(new FastOutSlowInInterpolator()) - .start(); - } - - public enum Type { - ALPHA, - SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA, - SLIDE_AND_ALPHA, LIGHT_SLIDE_AND_ALPHA - } -} diff --git a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java index b34a6be63..e1ada4f9b 100644 --- a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java +++ b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java @@ -31,7 +31,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import org.schabi.newpipe.util.AnimationUtils; +import org.schabi.newpipe.ktx.ViewUtils; import java.lang.annotation.Retention; import java.util.ArrayList; @@ -128,7 +128,7 @@ public class CollapsibleView extends LinearLayout { if (currentAnimator != null && currentAnimator.isRunning()) { currentAnimator.cancel(); } - currentAnimator = AnimationUtils.animateHeight(this, ANIMATION_DURATION, 0); + currentAnimator = ViewUtils.animateHeight(this, ANIMATION_DURATION, 0); setCurrentState(COLLAPSED); } @@ -151,7 +151,7 @@ public class CollapsibleView extends LinearLayout { if (currentAnimator != null && currentAnimator.isRunning()) { currentAnimator.cancel(); } - currentAnimator = AnimationUtils.animateHeight(this, ANIMATION_DURATION, this.targetHeight); + currentAnimator = ViewUtils.animateHeight(this, ANIMATION_DURATION, this.targetHeight); setCurrentState(EXPANDED); }