From affd23b14ec87fffb8c7b8213a50f9f5c99a9603 Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Mon, 8 May 2017 10:33:26 -0300 Subject: [PATCH] Fix animations --- .../newpipe/fragments/BaseFragment.java | 68 +------ .../fragments/channel/ChannelFragment.java | 2 + .../fragments/detail/VideoDetailFragment.java | 26 +-- .../fragments/search/SearchFragment.java | 8 +- .../newpipe/player/MainVideoPlayer.java | 89 ++++----- .../newpipe/player/PopupVideoPlayer.java | 28 ++- .../schabi/newpipe/player/VideoPlayer.java | 173 ++++++------------ .../schabi/newpipe/util/AnimationUtils.java | 126 +++++++++++++ 8 files changed, 261 insertions(+), 259 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java index d51267104..5337912b8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.fragments; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; @@ -28,6 +26,8 @@ import org.schabi.newpipe.R; import java.util.concurrent.atomic.AtomicBoolean; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + public abstract class BaseFragment extends Fragment { protected final String TAG = "BaseFragment@" + Integer.toHexString(hashCode()); protected static final boolean DEBUG = MainActivity.DEBUG; @@ -130,70 +130,6 @@ public abstract class BaseFragment extends Fragment { // Utils //////////////////////////////////////////////////////////////////////////*/ - public void animateView(final View view, final boolean enterOrExit, long duration) { - animateView(view, enterOrExit, duration, 0, null); - } - - public void animateView(final View view, final boolean enterOrExit, long duration, final Runnable execOnEnd) { - animateView(view, enterOrExit, duration, 0, execOnEnd); - } - - public void animateView(final View view, final boolean enterOrExit, long duration, long delay) { - animateView(view, enterOrExit, duration, delay, null); - } - - /** - * Animate the view - * - * @param view view that will be animated - * @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 take to start, in milliseconds - * @param execOnEnd runnable that will be executed when the animation ends - */ - public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) { - if (DEBUG) Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], execOnEnd = [" + execOnEnd + "]"); - if (view == null) return; - - if (view.getVisibility() == View.VISIBLE && enterOrExit) { - 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) { - 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); - - if (enterOrExit) { - view.animate().alpha(1f).setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (execOnEnd != null) execOnEnd.run(); - } - }).start(); - } else { - view.animate().alpha(0f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) execOnEnd.run(); - } - }) - .start(); - } - } - protected void setErrorMessage(String message, boolean showRetryButton) { if (errorTextView == null || activity == null) return; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java index fa02dbfdb..032212ed6 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java @@ -36,6 +36,8 @@ import java.io.Serializable; import java.text.NumberFormat; import java.util.ArrayList; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + public class ChannelFragment extends BaseFragment implements ChannelExtractorWorker.OnChannelInfoReceive { private final String TAG = "ChannelFragment@" + Integer.toHexString(hashCode()); 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 8d50f0035..65ddc36ed 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 @@ -40,7 +40,6 @@ import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import org.schabi.newpipe.ImageErrorLoadingListener; -import org.schabi.newpipe.Localization; import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.download.DownloadDialog; @@ -56,6 +55,8 @@ import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.PlayVideoActivity; import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.Utils; @@ -65,6 +66,8 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Stack; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + public class VideoDetailFragment extends BaseFragment implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener { private final String TAG = "VideoDetailFragment@" + Integer.toHexString(hashCode()); @@ -72,9 +75,6 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor private static final int INITIAL_RELATED_VIDEOS = 8; private static final String KORE_PACKET = "org.xbmc.kore"; - private static final String SERVICE_ID_KEY = "service_id_key"; - private static final String VIDEO_URL_KEY = "video_url_key"; - private static final String VIDEO_TITLE_KEY = "video_title_key"; private static final String STACK_KEY = "stack_key"; private static final String INFO_KEY = "info_key"; private static final String WAS_RELATED_EXPANDED_KEY = "was_related_expanded_key"; @@ -171,9 +171,9 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); if (savedInstanceState != null) { - videoTitle = savedInstanceState.getString(VIDEO_TITLE_KEY); - videoUrl = savedInstanceState.getString(VIDEO_URL_KEY); - serviceId = savedInstanceState.getInt(SERVICE_ID_KEY, 0); + videoTitle = savedInstanceState.getString(Constants.KEY_TITLE); + videoUrl = savedInstanceState.getString(Constants.KEY_URL); + serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, 0); wasRelatedStreamsExpanded = savedInstanceState.getBoolean(WAS_RELATED_EXPANDED_KEY, false); Serializable serializable = savedInstanceState.getSerializable(STACK_KEY); if (serializable instanceof Stack) { @@ -290,9 +290,9 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor @Override public void onSaveInstanceState(Bundle outState) { if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]"); - outState.putString(VIDEO_URL_KEY, videoUrl); - outState.putString(VIDEO_TITLE_KEY, videoTitle); - outState.putInt(SERVICE_ID_KEY, serviceId); + outState.putString(Constants.KEY_URL, videoUrl); + outState.putString(Constants.KEY_TITLE, videoTitle); + outState.putInt(Constants.KEY_SERVICE_ID, serviceId); outState.putSerializable(STACK_KEY, stack); int nextCount = currentStreamInfo != null && currentStreamInfo.next_video != null ? 2 : 0; @@ -804,7 +804,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor * If it is, check if the cache contains the info already.
* If the cache doesn't have the info, load from the network. * - * @param info info to prepare and load, can be null + * @param info info to prepare and load, can be null * @param scrollToTop whether or not scroll the scrollView to y = 0 */ public void prepareAndLoad(StreamInfo info, boolean scrollToTop) { @@ -848,7 +848,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor else parallaxScrollRootView.scrollTo(0, 0); } - animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, new Runnable() { + animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, 0, new Runnable() { @Override public void run() { handleStreamInfo(infoFinal, false); @@ -1056,7 +1056,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor private void setErrorImage(final int imageResource) { if (thumbnailImageView == null || activity == null) return; thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, imageResource)); - animateView(thumbnailImageView, false, 0, new Runnable() { + animateView(thumbnailImageView, false, 0, 0, new Runnable() { @Override public void run() { animateView(thumbnailImageView, true, 500); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java index 8071f4a36..11707c38d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java @@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.search.SearchResult; import org.schabi.newpipe.fragments.BaseFragment; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.workers.SearchWorker; import org.schabi.newpipe.workers.SuggestionWorker; @@ -45,12 +46,13 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + public class SearchFragment extends BaseFragment implements SuggestionWorker.OnSuggestionResult, SearchWorker.OnSearchResult { private final String TAG = "SearchFragment@" + Integer.toHexString(hashCode()); // savedInstanceBundle arguments private static final String QUERY_KEY = "query_key"; private static final String PAGE_NUMBER_KEY = "page_number_key"; - private static final String SERVICE_KEY = "service_key"; private static final String INFO_LIST_KEY = "info_list_key"; private static final String WAS_LOADING_KEY = "was_loading_key"; private static final String ERROR_KEY = "error_key"; @@ -101,7 +103,7 @@ public class SearchFragment extends BaseFragment implements SuggestionWorker.OnS setHasOptionsMenu(true); if (savedInstanceState != null) { searchQuery = savedInstanceState.getString(QUERY_KEY); - serviceId = savedInstanceState.getInt(SERVICE_KEY, 0); + serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, 0); pageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY, 0); wasLoading.set(savedInstanceState.getBoolean(WAS_LOADING_KEY, false)); filterItemCheckedId = savedInstanceState.getInt(FILTER_CHECKED_ID_KEY, 0); @@ -171,7 +173,7 @@ public class SearchFragment extends BaseFragment implements SuggestionWorker.OnS String query = searchEditText != null && !TextUtils.isEmpty(searchEditText.getText().toString()) ? searchEditText.getText().toString() : searchQuery; outState.putString(QUERY_KEY, query); - outState.putInt(SERVICE_KEY, serviceId); + outState.putInt(Constants.KEY_SERVICE_ID, serviceId); outState.putInt(PAGE_NUMBER_KEY, pageNumber); outState.putSerializable(INFO_LIST_KEY, ((ArrayList) infoListAdapter.getItemsList())); outState.putBoolean(WAS_LOADING_KEY, curSearchWorker != null && curSearchWorker.isRunning()); diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index ce3525f17..45221325b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -21,10 +21,13 @@ import android.widget.TextView; import android.widget.Toast; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ThemeHelper; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + /** * Activity Player implementing VideoPlayer * @@ -37,14 +40,7 @@ public class MainVideoPlayer extends Activity { private AudioManager audioManager; private GestureDetector gestureDetector; - private final Runnable hideUiRunnable = new Runnable() { - @Override - public void run() { - hideSystemUi(); - } - }; private boolean activityPaused; - private VideoPlayerImpl playerImpl; /*////////////////////////////////////////////////////////////////////////// @@ -265,14 +261,15 @@ public class MainVideoPlayer extends Activity { else if (v.getId() == screenRotationButton.getId()) onScreenRotationClicked(); if (getCurrentState() != STATE_COMPLETED) { + getControlsVisibilityHandler().removeCallbacksAndMessages(null); animateView(playerImpl.getControlsRoot(), true, 300, 0, new Runnable() { @Override public void run() { if (getCurrentState() == STATE_PLAYING && !playerImpl.isQualityMenuVisible()) { - animateView(playerImpl.getControlsRoot(), false, 300, DEFAULT_CONTROLS_HIDE_TIME, true); + hideControls(300, DEFAULT_CONTROLS_HIDE_TIME); } } - }, false); + }); } } @@ -285,14 +282,14 @@ public class MainVideoPlayer extends Activity { public void onStopTrackingTouch(SeekBar seekBar) { super.onStopTrackingTouch(seekBar); if (playerImpl.wasPlaying()) { - animateView(playerImpl.getControlsRoot(), false, 100, 0); + hideControls(100, 0); } } @Override public void onDismiss(PopupMenu menu) { super.onDismiss(menu); - if (isPlaying()) animateView(getControlsRoot(), false, 500, 0); + if (isPlaying()) hideControls(300, 0); } @Override @@ -310,26 +307,23 @@ public class MainVideoPlayer extends Activity { public void onLoading() { super.onLoading(); playPauseButton.setImageResource(R.drawable.ic_pause_white); - animateView(playPauseButton, false, 100, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); } @Override public void onBuffering() { super.onBuffering(); - animateView(playPauseButton, false, 100, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); } @Override public void onPlaying() { super.onPlaying(); - //playPauseButton.setImageResource(R.drawable.ic_pause_white); - //animateView(playPauseButton, true, 500, 0); - - animateView(playPauseButton, false, 80, 0, new Runnable() { + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_pause_white); - animateView(playPauseButton, true, 200, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200); } }); @@ -339,14 +333,11 @@ public class MainVideoPlayer extends Activity { @Override public void onPaused() { super.onPaused(); - //playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); - //animateView(playPauseButton, true, 100, 0); - - animateView(playPauseButton, false, 80, 0, new Runnable() { + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); - animateView(playPauseButton, true, 200, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200); } }); @@ -356,7 +347,7 @@ public class MainVideoPlayer extends Activity { @Override public void onPausedSeek() { super.onPausedSeek(); - animateView(playPauseButton, false, 100, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100); } @@ -366,11 +357,11 @@ public class MainVideoPlayer extends Activity { playPauseButton.setImageResource(R.drawable.ic_pause_white); } else { showSystemUi(); - animateView(playPauseButton, false, 0, 0, new Runnable() { + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, new Runnable() { @Override public void run() { playPauseButton.setImageResource(R.drawable.ic_replay_white); - animateView(playPauseButton, true, 300, 0); + animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300); } }); } @@ -382,19 +373,20 @@ public class MainVideoPlayer extends Activity { //////////////////////////////////////////////////////////////////////////*/ @Override - public void animateView(View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd, boolean hideUi) { - //if (execOnEnd == null) playerImpl.setDefaultAnimationEnd(hideUiRunnable); - - if (hideUi && execOnEnd != null) { - Runnable combinedRunnable = new Runnable() { - @Override - public void run() { - execOnEnd.run(); - hideUiRunnable.run(); - } - }; - super.animateView(view, enterOrExit, duration, delay, combinedRunnable, true); - } else super.animateView(view, enterOrExit, duration, delay, hideUi ? hideUiRunnable : execOnEnd, hideUi); + public void hideControls(final long duration, long delay) { + if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]"); + getControlsVisibilityHandler().removeCallbacksAndMessages(null); + getControlsVisibilityHandler().postDelayed(new Runnable() { + @Override + public void run() { + animateView(getControlsRoot(), false, duration, 0, new Runnable() { + @Override + public void run() { + hideSystemUi(); + } + }); + } + }, delay); } /////////////////////////////////////////////////////////////////////////// @@ -443,14 +435,9 @@ public class MainVideoPlayer extends Activity { if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); if (playerImpl.getCurrentState() != BasePlayer.STATE_PLAYING) return true; - if (playerImpl.isControlsVisible()) playerImpl.animateView(playerImpl.getControlsRoot(), false, 150, 0, true); + if (playerImpl.isControlsVisible()) playerImpl.hideControls(150, 0); else { - playerImpl.animateView(playerImpl.getControlsRoot(), true, 500, 0, new Runnable() { - @Override - public void run() { - playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true); - } - }); + playerImpl.showControlsThenHide(); showSystemUi(); } return true; @@ -500,7 +487,7 @@ public class MainVideoPlayer extends Activity { if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume); playerImpl.getVolumeTextView().setText(volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%"); - if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), true, 200, 0); + if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getVolumeTextView(), true, 200); if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE); } else { WindowManager.LayoutParams lp = getWindow().getAttributes(); @@ -515,7 +502,7 @@ public class MainVideoPlayer extends Activity { playerImpl.getBrightnessTextView().setText(brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%"); - if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), true, 200, 0); + if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), true, 200); if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE); } return true; @@ -527,11 +514,11 @@ public class MainVideoPlayer extends Activity { eventsNum = 0; /* if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE); if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);*/ - if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), false, 200, 200); - if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200); + if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) animateView(playerImpl.getVolumeTextView(), false, 200, 200); + if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), false, 200, 200); if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) { - playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true); + playerImpl.hideControls(300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 920f119e3..0bc91509b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -25,6 +25,7 @@ import android.view.View; import android.view.WindowManager; import android.widget.PopupMenu; import android.widget.RemoteViews; +import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; @@ -45,6 +46,8 @@ import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.Utils; import org.schabi.newpipe.workers.StreamExtractorWorker; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + /** * Service Popup Player implementing VideoPlayer * @@ -408,7 +411,7 @@ public class PopupVideoPlayer extends Service { @Override public void onDismiss(PopupMenu menu) { super.onDismiss(menu); - if (isPlaying()) animateView(getControlsRoot(), false, 500, 0); + if (isPlaying()) hideControls(500, 0); } @Override @@ -418,7 +421,14 @@ public class PopupVideoPlayer extends Service { stopSelf(); } - /*////////////////////////////////////////////////////////////////////////// + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + super.onStopTrackingTouch(seekBar); + if (playerImpl.wasPlaying()) { + hideControls(100, 0); + } + } +/*////////////////////////////////////////////////////////////////////////// // Broadcast Receiver //////////////////////////////////////////////////////////////////////////*/ @@ -541,9 +551,10 @@ public class PopupVideoPlayer extends Service { if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); playerImpl.showAndAnimateControl(-1, true); playerImpl.getLoadingPanel().setVisibility(View.GONE); - playerImpl.animateView(playerImpl.getControlsRoot(), false, 0, 0); - playerImpl.animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); - playerImpl.animateView(playerImpl.getResizingIndicator(), true, 200, 0); + + playerImpl.hideControls(0, 0); + animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); + animateView(playerImpl.getResizingIndicator(), true, 200, 0); isResizing = true; isResizingRightSide = e.getRawX() > windowLayoutParams.x + (windowLayoutParams.width / 2f); @@ -553,7 +564,8 @@ public class PopupVideoPlayer extends Service { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (isResizing) return false; - if (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f) playerImpl.animateView(playerImpl.getControlsRoot(), true, 0, 0); + if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING + && (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0); isMoving = true; float diffX = (int) (e2.getRawX() - e1.getRawX()), posX = (int) (initialPopupX + diffX); @@ -582,7 +594,7 @@ public class PopupVideoPlayer extends Service { private void onScrollEnd() { if (DEBUG) Log.d(TAG, "onScrollEnd() called"); if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) { - playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME); + playerImpl.hideControls(300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME); } } @@ -610,7 +622,7 @@ public class PopupVideoPlayer extends Service { if (isResizing) { isResizing = false; - playerImpl.animateView(playerImpl.getResizingIndicator(), false, 100, 0); + animateView(playerImpl.getResizingIndicator(), false, 100, 0); playerImpl.changeState(playerImpl.getCurrentState()); } savePositionAndSize(); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index d64e60747..84f2192f9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -12,6 +12,7 @@ import android.graphics.Color; import android.graphics.PorterDuff; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Menu; @@ -36,12 +37,15 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream_info.AudioStream; import org.schabi.newpipe.extractor.stream_info.VideoStream; +import org.schabi.newpipe.util.AnimationUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Vector; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + /** * Base for video players * @@ -101,6 +105,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. private ImageButton fullScreenButton; private ValueAnimator controlViewAnimator; + private Handler controlsVisibilityHandler = new Handler(); private boolean isQualityPopupMenuVisible = false; private boolean qualityChanged = false; @@ -235,6 +240,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (!isProgressLoopRunning.get()) startProgressLoop(); + controlsVisibilityHandler.removeCallbacksAndMessages(null); + animateView(controlsRoot, false, 300); + showAndAnimateControl(-1, true); playbackSeekBar.setEnabled(true); playbackSeekBar.setProgress(0); @@ -242,10 +250,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. // Bug on lower api, disabling and enabling the seekBar resets the thumb color -.-, so sets the color again if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN); - animateView(endScreen, false, 0, 0); + animateView(endScreen, false, 0); loadingPanel.setBackgroundColor(Color.BLACK); - animateView(loadingPanel, true, 0, 0); - animateView(surfaceForeground, true, 100, 0); + animateView(loadingPanel, true, 0); + animateView(surfaceForeground, true, 100); } @Override @@ -254,26 +262,21 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (!isProgressLoopRunning.get()) startProgressLoop(); showAndAnimateControl(-1, true); loadingPanel.setVisibility(View.GONE); - animateView(controlsRoot, true, 500, 0, new Runnable() { - @Override - public void run() { - animateView(controlsRoot, false, 500, DEFAULT_CONTROLS_HIDE_TIME, true); - } - }); - animateView(currentDisplaySeek, false, 200, 0); + showControlsThenHide(); + animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); } @Override public void onBuffering() { if (DEBUG) Log.d(TAG, "onBuffering() called"); loadingPanel.setBackgroundColor(Color.TRANSPARENT); - animateView(loadingPanel, true, 500, 0); + animateView(loadingPanel, true, 500); } @Override public void onPaused() { if (DEBUG) Log.d(TAG, "onPaused() called"); - animateView(controlsRoot, true, 500, 100); + showControls(400); loadingPanel.setVisibility(View.GONE); } @@ -289,9 +292,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (isProgressLoopRunning.get()) stopProgressLoop(); - animateView(controlsRoot, true, 500, 0); - animateView(endScreen, true, 800, 0); - animateView(currentDisplaySeek, false, 200, 0); + showControls(500); + animateView(endScreen, true, 800); + animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); loadingPanel.setVisibility(View.GONE); playbackSeekBar.setMax((int) simpleExoPlayer.getDuration()); @@ -302,7 +305,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. // Bug on lower api, disabling and enabling the seekBar resets the thumb color -.-, so sets the color again if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN); - animateView(surfaceForeground, true, 100, 0); + animateView(surfaceForeground, true, 100); if (currentRepeatMode == RepeatMode.REPEAT_ONE) { changeState(STATE_LOADING); @@ -324,7 +327,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. @Override public void onRenderedFirstFrame() { - animateView(surfaceForeground, false, 100, 0); + animateView(surfaceForeground, false, 100); } /*////////////////////////////////////////////////////////////////////////// @@ -443,7 +446,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (DEBUG) Log.d(TAG, "onQualitySelectorClicked() called"); qualityPopupMenu.show(); isQualityPopupMenuVisible = true; - animateView(getControlsRoot(), true, 300, 0); + showControls(300); VideoStream videoStream = getSelectedVideoStream(); qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution); @@ -469,8 +472,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. wasPlaying = isPlaying(); if (isPlaying()) simpleExoPlayer.setPlayWhenReady(false); - animateView(controlsRoot, true, 0, 0); - animateView(currentDisplaySeek, true, 300, 0); + showControls(0); + animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300); } @Override @@ -481,7 +484,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (wasPlaying || simpleExoPlayer.getDuration() == seekBar.getProgress()) simpleExoPlayer.setPlayWhenReady(true); playbackCurrentTime.setText(getTimeString(seekBar.getProgress())); - animateView(currentDisplaySeek, false, 200, 0); + animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); if (getCurrentState() == STATE_PAUSED_SEEK) changeState(STATE_BUFFERING); if (!isProgressLoopRunning.get()) startProgressLoop(); @@ -550,107 +553,37 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. controlViewAnimator.start(); } - public void animateView(View view, boolean enterOrExit, long duration, long delay) { - animateView(view, enterOrExit, duration, delay, null, false); - } - - public void animateView(View view, boolean enterOrExit, long duration, long delay, boolean hideUi) { - animateView(view, enterOrExit, duration, delay, null, hideUi); - } - - public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) { - animateView(view, enterOrExit, duration, delay, execOnEnd, false); - } - - /** - * Animate the view - * - * @param view view that will be animated - * @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 - * @param hideUi need to hide ui when animation ends, - * just a helper for classes extending this - */ - public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd, boolean hideUi) { - if (DEBUG) { - Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], delay = [" + delay + "], execOnEnd = [" + execOnEnd + "]"); - } - 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); - - if (view == controlsRoot) { - if (enterOrExit) { - view.animate().alpha(1f).setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (execOnEnd != null) execOnEnd.run(); - } - }).start(); - } else { - view.animate().alpha(0f) - .setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) execOnEnd.run(); - } - }) - .start(); - } - return; - } - - if (enterOrExit) { - view.setAlpha(0f); - view.setScaleX(.8f); - view.setScaleY(.8f); - view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (execOnEnd != null) execOnEnd.run(); - } - }).start(); - } else { - view.setAlpha(1f); - view.setScaleX(1f); - view.setScaleY(1f); - view.animate().alpha(0f).scaleX(.8f).scaleY(.8f).setDuration(duration).setStartDelay(delay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setVisibility(View.GONE); - if (execOnEnd != null) execOnEnd.run(); - } - }) - .start(); - } - } - public boolean isQualityMenuVisible() { return isQualityPopupMenuVisible; } + public void showControlsThenHide() { + if (DEBUG) Log.d(TAG, "showControlsThenHide() called"); + animateView(controlsRoot, true, 300, 0, new Runnable() { + @Override + public void run() { + hideControls(300, DEFAULT_CONTROLS_HIDE_TIME); + } + }); + } + + public void showControls(long duration) { + if (DEBUG) Log.d(TAG, "showControls() called"); + controlsVisibilityHandler.removeCallbacksAndMessages(null); + animateView(controlsRoot, true, duration); + } + + public void hideControls(final long duration, long delay) { + if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]"); + controlsVisibilityHandler.removeCallbacksAndMessages(null); + controlsVisibilityHandler.postDelayed(new Runnable() { + @Override + public void run() { + animateView(controlsRoot, false, duration); + } + }, delay); + } + /*////////////////////////////////////////////////////////////////////////// // Getters and Setters //////////////////////////////////////////////////////////////////////////*/ @@ -711,6 +644,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. this.startedFromNewPipe = startedFromNewPipe; } + public Handler getControlsVisibilityHandler() { + return controlsVisibilityHandler; + } + public View getRootView() { return rootView; } diff --git a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java new file mode 100644 index 000000000..c37eaa560 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java @@ -0,0 +1,126 @@ +package org.schabi.newpipe.util; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.util.Log; +import android.view.View; + +import org.schabi.newpipe.player.BasePlayer; + +public class AnimationUtils { + private static final String TAG = "AnimationUtils"; + private static final boolean DEBUG = BasePlayer.DEBUG; + + public enum Type { + ALPHA, SCALE_AND_ALPHA + } + + public static void animateView(View view, boolean enterOrExit, long duration) { + animateView(view, Type.ALPHA, enterOrExit, duration, 0, null); + } + + public static void animateView(View view, boolean enterOrExit, long duration, long delay) { + animateView(view, Type.ALPHA, enterOrExit, duration, delay, null); + } + + public static void animateView(View view, boolean enterOrExit, long duration, long delay, Runnable execOnEnd) { + animateView(view, Type.ALPHA, enterOrExit, duration, delay, execOnEnd); + } + + public static void animateView(View view, Type animationType, boolean enterOrExit, long duration) { + animateView(view, animationType, enterOrExit, duration, 0, null); + } + + public static void animateView(View view, Type animationType, boolean enterOrExit, long duration, 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, Type animationType, boolean enterOrExit, long duration, long delay, Runnable execOnEnd) { + if (DEBUG) { + Log.d(TAG, "animateView() called with: view = [" + view + "], animationType = [" + animationType + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], delay = [" + delay + "], execOnEnd = [" + execOnEnd + "]"); + } + + 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; + } + } + + private static void animateScaleAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) { + if (enterOrExit) { + view.setAlpha(0f); + view.setScaleX(.8f); + view.setScaleY(.8f); + view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (execOnEnd != null) execOnEnd.run(); + } + }).start(); + } else { + view.setAlpha(1f); + view.setScaleX(1f); + view.setScaleY(1f); + view.animate().alpha(0f).scaleX(.8f).scaleY(.8f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(View.GONE); + if (execOnEnd != null) execOnEnd.run(); + } + }).start(); + } + } + + + private static void animateAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) { + if (enterOrExit) { + view.animate().alpha(1f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (execOnEnd != null) execOnEnd.run(); + } + }).start(); + } else { + view.animate().alpha(0f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(View.GONE); + if (execOnEnd != null) execOnEnd.run(); + } + }).start(); + } + } +}