sortedVideos,
+ String playbackQuality) {
+ return videoPlayerSelected() ? getResolutionIndex(context, sortedVideos, playbackQuality)
+ : getPopupResolutionIndex(context, sortedVideos, playbackQuality);
+ }
+ };
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // States
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private void animatePlayButtons(final boolean show, final int duration) {
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
+ if (playQueue.getIndex() > 0)
+ animateView(playPreviousButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
+ if (playQueue.getIndex() + 1 < playQueue.getStreams().size())
+ animateView(playNextButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
+
+ }
+
+ @Override
+ public void changeState(int state) {
+ super.changeState(state);
+ updatePlayback();
+ }
+
+ @Override
+ public void onBlocked() {
+ super.onBlocked();
+ playPauseButton.setImageResource(R.drawable.ic_pause_white);
+ animatePlayButtons(false, 100);
+ getRootView().setKeepScreenOn(false);
+
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_play_arrow_white);
+ }
+
+ @Override
+ public void onBuffering() {
+ super.onBuffering();
+ getRootView().setKeepScreenOn(true);
+
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_play_arrow_white);
+ }
+
+ @Override
+ public void onPlaying() {
+ super.onPlaying();
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
+ playPauseButton.setImageResource(R.drawable.ic_pause_white);
+ animatePlayButtons(true, 200);
+ });
+
+ updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS);
+ checkLandscape();
+ getRootView().setKeepScreenOn(true);
+
+ service.getLockManager().acquireWifiAndCpu();
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_pause_white);
+ }
+
+ @Override
+ public void onPaused() {
+ super.onPaused();
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
+ animatePlayButtons(true, 200);
+ });
+
+ updateWindowFlags(IDLE_WINDOW_FLAGS);
+
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_play_arrow_white);
+
+ getRootView().setKeepScreenOn(false);
+
+ service.getLockManager().releaseWifiAndCpu();
+ }
+
+ @Override
+ public void onPausedSeek() {
+ super.onPausedSeek();
+ animatePlayButtons(false, 100);
+ getRootView().setKeepScreenOn(true);
+
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_play_arrow_white);
+ }
+
+
+ @Override
+ public void onCompleted() {
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, () -> {
+ playPauseButton.setImageResource(R.drawable.ic_replay_white);
+ animatePlayButtons(true, DEFAULT_CONTROLS_DURATION);
+ });
+ getRootView().setKeepScreenOn(false);
+
+ updateWindowFlags(IDLE_WINDOW_FLAGS);
+
+ service.resetNotification();
+ service.updateNotification(R.drawable.ic_replay_white);
+
+ service.getLockManager().releaseWifiAndCpu();
+
+ super.onCompleted();
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Broadcast Receiver
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ protected void setupBroadcastReceiver(IntentFilter intentFilter) {
+ super.setupBroadcastReceiver(intentFilter);
+ if (DEBUG) Log.d(TAG, "setupBroadcastReceiver() called with: intentFilter = [" + intentFilter + "]");
+
+ intentFilter.addAction(ACTION_CLOSE);
+ intentFilter.addAction(ACTION_PLAY_PAUSE);
+ intentFilter.addAction(ACTION_OPEN_CONTROLS);
+ intentFilter.addAction(ACTION_REPEAT);
+ intentFilter.addAction(ACTION_PLAY_PREVIOUS);
+ intentFilter.addAction(ACTION_PLAY_NEXT);
+ intentFilter.addAction(ACTION_FAST_REWIND);
+ intentFilter.addAction(ACTION_FAST_FORWARD);
+
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
+ }
+
+ @Override
+ public void onBroadcastReceived(Intent intent) {
+ super.onBroadcastReceived(intent);
+ if (intent == null || intent.getAction() == null)
+ return;
+
+ if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
+
+ switch (intent.getAction()) {
+ case ACTION_CLOSE:
+ service.onDestroy();
+ break;
+ case ACTION_PLAY_NEXT:
+ onPlayNext();
+ break;
+ case ACTION_PLAY_PREVIOUS:
+ onPlayPrevious();
+ break;
+ case ACTION_FAST_FORWARD:
+ onFastForward();
+ break;
+ case ACTION_FAST_REWIND:
+ onFastRewind();
+ break;
+ case ACTION_PLAY_PAUSE:
+ onPlayPause();
+ break;
+ case ACTION_REPEAT:
+ onRepeatClicked();
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ shouldUpdateOnProgress = true;
+ // Interrupt playback only when screen turns on and user is watching video in fragment
+ if (backgroundPlaybackEnabled() && getPlayer() != null && (isPlaying() || getPlayer().isLoading()))
+ useVideoSource(true);
+ break;
+ case Intent.ACTION_SCREEN_OFF:
+ shouldUpdateOnProgress = false;
+ // Interrupt playback only when screen turns off with video working
+ if (backgroundPlaybackEnabled() && getPlayer() != null && (isPlaying() || getPlayer().isLoading()))
+ useVideoSource(false);
+ break;
+ }
+ service.resetNotification();
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Thumbnail Loading
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
+ super.onLoadingComplete(imageUri, view, loadedImage);
+ // rebuild notification here since remote view does not release bitmaps,
+ // causing memory leaks
+ service.resetNotification();
+ service.updateNotification(-1);
+ }
+
+ @Override
+ public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
+ super.onLoadingFailed(imageUri, view, failReason);
+ service.resetNotification();
+ service.updateNotification(-1);
+ }
+
+ @Override
+ public void onLoadingCancelled(String imageUri, View view) {
+ super.onLoadingCancelled(imageUri, view);
+ service.resetNotification();
+ service.updateNotification(-1);
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Utils
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private void setInitialGestureValues() {
+ if (getAudioReactor() != null) {
+ final float currentVolumeNormalized = (float) getAudioReactor().getVolume() / getAudioReactor().getMaxVolume();
+ volumeProgressBar.setProgress((int) (volumeProgressBar.getMax() * currentVolumeNormalized));
+ }
+ }
+
+ private void choosePlayerTypeFromIntent(Intent intent) {
+ // If you want to open popup from the app just include Constants.POPUP_ONLY into an extra
+ if (intent.getIntExtra(PLAYER_TYPE, PLAYER_TYPE_VIDEO) == PLAYER_TYPE_AUDIO) {
+ playerType = MainPlayer.PlayerType.AUDIO;
+ } else if (intent.getIntExtra(PLAYER_TYPE, PLAYER_TYPE_VIDEO) == PLAYER_TYPE_POPUP) {
+ playerType = MainPlayer.PlayerType.POPUP;
+ } else {
+ playerType = MainPlayer.PlayerType.VIDEO;
+ }
+ }
+
+ public boolean backgroundPlaybackEnabled() {
+ return PlayerHelper.getMinimizeOnExitAction(service) == MINIMIZE_ON_EXIT_MODE_BACKGROUND;
+ }
+
+ public boolean minimizeOnPopupEnabled() {
+ return PlayerHelper.getMinimizeOnExitAction(service) == PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
+ }
+
+ public boolean audioPlayerSelected() {
+ return playerType == MainPlayer.PlayerType.AUDIO;
+ }
+
+ public boolean videoPlayerSelected() {
+ return playerType == MainPlayer.PlayerType.VIDEO;
+ }
+
+ public boolean popupPlayerSelected() {
+ return playerType == MainPlayer.PlayerType.POPUP;
+ }
+
+ private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
+ final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
+ final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
+
+ float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
+ float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
+
+ return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2));
+ }
+
+ private float getClosingRadius() {
+ final int buttonRadius = closeOverlayButton.getWidth() / 2;
+ // 20% wider than the button itself
+ return buttonRadius * 1.2f;
+ }
+
+ public boolean isInsideClosingRadius(MotionEvent popupMotionEvent) {
+ return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
+ }
+
+ public boolean isInFullscreen() {
+ return isFullscreen;
+ }
+
+ @Override
+ public void showControlsThenHide() {
+ if (queueVisible) return;
+
+ showOrHideButtons();
+ super.showControlsThenHide();
+ }
+
+ @Override
+ public void showControls(long duration) {
+ if (queueVisible) return;
+
+ showOrHideButtons();
+ super.showControls(duration);
+ }
+
+ @Override
+ public void hideControls(final long duration, long delay) {
+ if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
+
+ showOrHideButtons();
+
+ getControlsVisibilityHandler().removeCallbacksAndMessages(null);
+ getControlsVisibilityHandler().postDelayed(() ->
+ animateView(getControlsRoot(), false, duration, 0,
+ this::hideSystemUIIfNeeded),
+ /*delayMillis=*/delay
+ );
+ }
+
+ private void showOrHideButtons() {
+ if (playQueue == null)
+ return;
+
+ if (playQueue.getIndex() == 0)
+ playPreviousButton.setVisibility(View.INVISIBLE);
+ else
+ playPreviousButton.setVisibility(View.VISIBLE);
+
+ if (playQueue.getIndex() + 1 == playQueue.getStreams().size())
+ playNextButton.setVisibility(View.INVISIBLE);
+ else
+ playNextButton.setVisibility(View.VISIBLE);
+
+ if (playQueue.getStreams().size() <= 1 || popupPlayerSelected())
+ queueButton.setVisibility(View.GONE);
+ else
+ queueButton.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void hideSystemUIIfNeeded() {
+ if (fragmentListener != null)
+ fragmentListener.hideSystemUIIfNeeded();
+ }
+
+ private void updatePlaybackButtons() {
+ if (repeatButton == null || shuffleButton == null ||
+ simpleExoPlayer == null || playQueue == null) return;
+
+ setRepeatModeButton(repeatButton, getRepeatMode());
+ setShuffleButton(shuffleButton, playQueue.isShuffled());
+ }
+
+ public void checkLandscape() {
+ AppCompatActivity parent = getParentActivity();
+ if (parent != null && service.isLandscape() != isInFullscreen()
+ && getCurrentState() != STATE_COMPLETED && videoPlayerSelected())
+ toggleFullscreen();
+ }
+
+ private void buildQueue() {
+ itemsList.setAdapter(playQueueAdapter);
+ itemsList.setClickable(true);
+ itemsList.setLongClickable(true);
+
+ itemsList.clearOnScrollListeners();
+ itemsList.addOnScrollListener(getQueueScrollListener());
+
+ itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
+ itemTouchHelper.attachToRecyclerView(itemsList);
+
+ playQueueAdapter.setSelectedListener(getOnSelectedListener());
+
+ itemsListCloseButton.setOnClickListener(view -> onQueueClosed());
+ }
+
+ public void useVideoSource(boolean video) {
+ // Return when: old value of audioOnly equals to the new value, audio player is selected,
+ // video player is selected AND fragment is not shown
+ if (playQueue == null
+ || audioOnly == !video
+ || audioPlayerSelected()
+ || (video && videoPlayerSelected() && fragmentListener.isFragmentStopped()))
+ return;
+
+ audioOnly = !video;
+ setRecovery();
+ reload();
+ }
+
+ private OnScrollBelowItemsListener getQueueScrollListener() {
+ return new OnScrollBelowItemsListener() {
+ @Override
+ public void onScrolledDown(RecyclerView recyclerView) {
+ if (playQueue != null && !playQueue.isComplete()) {
+ playQueue.fetch();
+ } else if (itemsList != null) {
+ itemsList.clearOnScrollListeners();
+ }
+ }
+ };
+ }
+
+ private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
+ return new PlayQueueItemTouchCallback() {
+ @Override
+ public void onMove(int sourceIndex, int targetIndex) {
+ if (playQueue != null) playQueue.move(sourceIndex, targetIndex);
+ }
+
+ @Override
+ public void onSwiped(int index) {
+ if (index != -1) playQueue.remove(index);
+ }
+ };
+ }
+
+ private PlayQueueItemBuilder.OnSelectedListener getOnSelectedListener() {
+ return new PlayQueueItemBuilder.OnSelectedListener() {
+ @Override
+ public void selected(PlayQueueItem item, View view) {
+ onSelected(item);
+ }
+
+ @Override
+ public void held(PlayQueueItem item, View view) {
+ final int index = playQueue.indexOf(item);
+ if (index != -1) playQueue.remove(index);
+ }
+
+ @Override
+ public void onStartDrag(PlayQueueItemHolder viewHolder) {
+ if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder);
+ }
+ };
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Init
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @SuppressLint("RtlHardcoded")
+ private void initPopup() {
+ if (DEBUG) Log.d(TAG, "initPopup() called");
+
+ updateScreenSize();
+
+ final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(service);
+ final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width);
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
+ popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
+
+ final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_PHONE :
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+ popupLayoutParams = new WindowManager.LayoutParams(
+ (int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
+ layoutParamType,
+ IDLE_WINDOW_FLAGS,
+ PixelFormat.TRANSLUCENT);
+ popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+
+ int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
+ int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
+ popupLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
+ popupLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
+
+ checkPopupPositionBounds();
+
+ getLoadingPanel().setMinimumWidth(popupLayoutParams.width);
+ getLoadingPanel().setMinimumHeight(popupLayoutParams.height);
+
+ service.removeViewFromParent();
+ windowManager.addView(service.getView(), popupLayoutParams);
+
+ if (getAspectRatioFrameLayout().getResizeMode() == AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
+ onResizeClicked();
+ }
+
+ @SuppressLint("RtlHardcoded")
+ private void initPopupCloseOverlay() {
+ if (DEBUG) Log.d(TAG, "initPopupCloseOverlay() called");
+ closeOverlayView = View.inflate(service, R.layout.player_popup_close_overlay, null);
+ closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
+
+ final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_PHONE :
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+
+ WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ layoutParamType,
+ flags,
+ PixelFormat.TRANSLUCENT);
+ closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ closeOverlayLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+
+ closeOverlayButton.setVisibility(View.GONE);
+ windowManager.addView(closeOverlayView, closeOverlayLayoutParams);
+ }
+
+ private void initVideoPlayer() {
+ service.getView().setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Popup utils
+ //////////////////////////////////////////////////////////////////////////*/
+
+ /**
+ * @see #checkPopupPositionBounds(float, float)
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public boolean checkPopupPositionBounds() {
+ return checkPopupPositionBounds(screenWidth, screenHeight);
+ }
+
+ /**
+ * Check if {@link #popupLayoutParams}' position is within a arbitrary boundary that goes from (0,0) to (boundaryWidth,
+ * boundaryHeight).
+ *
+ * If it's out of these boundaries, {@link #popupLayoutParams}' position is changed and {@code true} is returned
+ * to represent this change.
+ *
+ * @return if the popup was out of bounds and have been moved back to it
+ */
+ public boolean checkPopupPositionBounds(final float boundaryWidth, final float boundaryHeight) {
+ if (DEBUG) {
+ Log.d(TAG, "checkPopupPositionBounds() called with: boundaryWidth = ["
+ + boundaryWidth + "], boundaryHeight = [" + boundaryHeight + "]");
+ }
+
+ if (popupLayoutParams.x < 0) {
+ popupLayoutParams.x = 0;
+ return true;
+ } else if (popupLayoutParams.x > boundaryWidth - popupLayoutParams.width) {
+ popupLayoutParams.x = (int) (boundaryWidth - popupLayoutParams.width);
+ return true;
+ }
+
+ if (popupLayoutParams.y < 0) {
+ popupLayoutParams.y = 0;
+ return true;
+ } else if (popupLayoutParams.y > boundaryHeight - popupLayoutParams.height) {
+ popupLayoutParams.y = (int) (boundaryHeight - popupLayoutParams.height);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void savePositionAndSize() {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
+ sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply();
+ sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply();
+ sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply();
+ }
+
+ private float getMinimumVideoHeight(float width) {
+ //if (DEBUG) Log.d(TAG, "getMinimumVideoHeight() called with: width = [" + width + "], returned: " + height);
+ return width / (16.0f / 9.0f); // Respect the 16:9 ratio that most videos have
+ }
+
+ public void updateScreenSize() {
+ DisplayMetrics metrics = new DisplayMetrics();
+ windowManager.getDefaultDisplay().getMetrics(metrics);
+
+ screenWidth = metrics.widthPixels;
+ screenHeight = metrics.heightPixels;
+ if (DEBUG)
+ Log.d(TAG, "updateScreenSize() called > screenWidth = " + screenWidth + ", screenHeight = " + screenHeight);
+
+ popupWidth = service.getResources().getDimension(R.dimen.popup_default_width);
+ popupHeight = getMinimumVideoHeight(popupWidth);
+
+ minimumWidth = service.getResources().getDimension(R.dimen.popup_minimum_width);
+ minimumHeight = getMinimumVideoHeight(minimumWidth);
+
+ maximumWidth = screenWidth;
+ maximumHeight = screenHeight;
+ }
+
+ public void updatePopupSize(int width, int height) {
+ if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
+
+ if (popupLayoutParams == null || windowManager == null || !popupPlayerSelected() || getRootView().getParent() == null)
+ return;
+
+ width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
+
+ if (height == -1) height = (int) getMinimumVideoHeight(width);
+ else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height);
+
+ popupLayoutParams.width = width;
+ popupLayoutParams.height = height;
+ popupWidth = width;
+ popupHeight = height;
+
+ if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
+ windowManager.updateViewLayout(getRootView(), popupLayoutParams);
+ }
+
+ private void updateWindowFlags(final int flags) {
+ if (popupLayoutParams == null || windowManager == null || !popupPlayerSelected() || getRootView().getParent() == null)
+ return;
+
+ popupLayoutParams.flags = flags;
+ windowManager.updateViewLayout(getRootView(), popupLayoutParams);
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Misc
+ //////////////////////////////////////////////////////////////////////////*/
+
+ public void closePopup() {
+ if (DEBUG) Log.d(TAG, "closePopup() called, isPopupClosing = " + isPopupClosing);
+ if (isPopupClosing) return;
+ isPopupClosing = true;
+
+ savePlaybackState();
+ windowManager.removeView(getRootView());
+
+ animateOverlayAndFinishService();
+ }
+
+ private void animateOverlayAndFinishService() {
+ final int targetTranslationY = (int) (closeOverlayButton.getRootView().getHeight() - closeOverlayButton.getY());
+
+ closeOverlayButton.animate().setListener(null).cancel();
+ closeOverlayButton.animate()
+ .setInterpolator(new AnticipateInterpolator())
+ .translationY(targetTranslationY)
+ .setDuration(400)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ end();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ end();
+ }
+
+ private void end() {
+ windowManager.removeView(closeOverlayView);
+
+ service.onDestroy();
+ }
+ }).start();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Manipulations with listener
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setFragmentListener(PlayerServiceEventListener listener) {
+ fragmentListener = listener;
+ updateMetadata();
+ updatePlayback();
+ triggerProgressUpdate();
+ }
+
+ public void removeFragmentListener(PlayerServiceEventListener listener) {
+ if (fragmentListener == listener) {
+ fragmentListener = null;
+ }
+ }
+
+ /*package-private*/ void setActivityListener(PlayerEventListener listener) {
+ activityListener = listener;
+ updateMetadata();
+ updatePlayback();
+ triggerProgressUpdate();
+ }
+
+ /*package-private*/ void removeActivityListener(PlayerEventListener listener) {
+ if (activityListener == listener) {
+ activityListener = null;
+ }
+ }
+
+ private void updateMetadata() {
+ if (fragmentListener != null && getCurrentMetadata() != null) {
+ fragmentListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ }
+ if (activityListener != null && getCurrentMetadata() != null) {
+ activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ }
+ }
+
+ private void updatePlayback() {
+ if (fragmentListener != null && simpleExoPlayer != null && playQueue != null) {
+ fragmentListener.onPlaybackUpdate(currentState, getRepeatMode(),
+ playQueue.isShuffled(), simpleExoPlayer.getPlaybackParameters());
+ }
+ if (activityListener != null && simpleExoPlayer != null && playQueue != null) {
+ activityListener.onPlaybackUpdate(currentState, getRepeatMode(),
+ playQueue.isShuffled(), getPlaybackParameters());
+ }
+ }
+
+ private void updateProgress(int currentProgress, int duration, int bufferPercent) {
+ if (fragmentListener != null) {
+ fragmentListener.onProgressUpdate(currentProgress, duration, bufferPercent);
+ }
+ if (activityListener != null) {
+ activityListener.onProgressUpdate(currentProgress, duration, bufferPercent);
+ }
+ }
+
+ void stopActivityBinding() {
+ if (fragmentListener != null) {
+ fragmentListener.onServiceStopped();
+ fragmentListener = null;
+ }
+ if (activityListener != null) {
+ activityListener.onServiceStopped();
+ activityListener = null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Getters
+ ///////////////////////////////////////////////////////////////////////////
+
+ public RelativeLayout getVolumeRelativeLayout() {
+ return volumeRelativeLayout;
+ }
+
+ public ProgressBar getVolumeProgressBar() {
+ return volumeProgressBar;
+ }
+
+ public ImageView getVolumeImageView() {
+ return volumeImageView;
+ }
+
+ public RelativeLayout getBrightnessRelativeLayout() {
+ return brightnessRelativeLayout;
+ }
+
+ public ProgressBar getBrightnessProgressBar() {
+ return brightnessProgressBar;
+ }
+
+ public ImageView getBrightnessImageView() {
+ return brightnessImageView;
+ }
+
+ public ImageButton getPlayPauseButton() {
+ return playPauseButton;
+ }
+
+ public int getMaxGestureLength() {
+ return maxGestureLength;
+ }
+
+ public TextView getResizingIndicator() {
+ return resizingIndicator;
+ }
+
+ public GestureDetector getGestureDetector() {
+ return gestureDetector;
+ }
+
+ public WindowManager.LayoutParams getPopupLayoutParams() {
+ return popupLayoutParams;
+ }
+
+ public float getScreenWidth() {
+ return screenWidth;
+ }
+
+ public float getScreenHeight() {
+ return screenHeight;
+ }
+
+ public float getPopupWidth() {
+ return popupWidth;
+ }
+
+ public float getPopupHeight() {
+ return popupHeight;
+ }
+
+ public void setPopupWidth(float width) {
+ popupWidth = width;
+ }
+
+ public void setPopupHeight(float height) {
+ popupHeight = height;
+ }
+
+ public View getCloseOverlayButton() {
+ return closeOverlayButton;
+ }
+
+ public View getCloseOverlayView() {
+ return closeOverlayView;
+ }
+
+ public View getClosingOverlayView() {
+ return closingOverlayView;
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
new file mode 100644
index 000000000..85db3b201
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -0,0 +1,47 @@
+package org.schabi.newpipe.player.event;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import org.schabi.newpipe.R;
+
+public class CustomBottomSheetBehavior extends BottomSheetBehavior {
+
+ public CustomBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
+ // Without overriding scrolling will not work in detail_content_root_layout
+ ViewGroup controls = child.findViewById(R.id.detail_content_root_layout);
+ if (controls != null) {
+ Rect rect = new Rect();
+ controls.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ }
+
+ // Without overriding scrolling will not work on relatedStreamsLayout
+ ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout);
+ if (relatedStreamsLayout != null) {
+ Rect rect = new Rect();
+ relatedStreamsLayout.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ }
+
+ ViewGroup playQueue = child.findViewById(R.id.playQueue);
+ if (playQueue != null) {
+ Rect rect = new Rect();
+ playQueue.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ }
+
+ return super.onInterceptTouchEvent(parent, child, event);
+ }
+
+}
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
new file mode 100644
index 000000000..72462beff
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java
@@ -0,0 +1,462 @@
+package org.schabi.newpipe.player.event;
+
+import android.app.Activity;
+import android.util.Log;
+import android.view.*;
+import androidx.appcompat.content.res.AppCompatResources;
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.player.BasePlayer;
+import org.schabi.newpipe.player.MainPlayer;
+import org.schabi.newpipe.player.VideoPlayerImpl;
+import org.schabi.newpipe.player.helper.PlayerHelper;
+
+import static org.schabi.newpipe.player.BasePlayer.*;
+import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
+import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
+import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
+public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
+ private static final String TAG = ".PlayerGestureListener";
+ private static final boolean DEBUG = BasePlayer.DEBUG;
+
+ private VideoPlayerImpl playerImpl;
+ private MainPlayer service;
+
+ private int initialPopupX, initialPopupY;
+
+ private boolean isMovingInMain, isMovingInPopup;
+
+ private boolean isResizing;
+
+ private int tossFlingVelocity;
+
+ private final boolean isVolumeGestureEnabled;
+ private final boolean isBrightnessGestureEnabled;
+ private final int maxVolume;
+ private static final int MOVEMENT_THRESHOLD = 40;
+
+
+ public PlayerGestureListener(final VideoPlayerImpl playerImpl, final MainPlayer service) {
+ this.playerImpl = playerImpl;
+ this.service = service;
+ this.tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service);
+
+ isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(service);
+ isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(service);
+ maxVolume = playerImpl.getAudioReactor().getMaxVolume();
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Helpers
+ //////////////////////////////////////////////////////////////////////////*/
+
+ /*
+ * Main and popup players' gesture listeners is too different.
+ * So it will be better to have different implementations of them
+ * */
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
+
+ if (playerImpl.popupPlayerSelected()) return onDoubleTapInPopup(e);
+ else return onDoubleTapInMain(e);
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
+
+ if (playerImpl.popupPlayerSelected()) return onSingleTapConfirmedInPopup(e);
+ else return onSingleTapConfirmedInMain(e);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
+
+ if (playerImpl.popupPlayerSelected()) return onDownInPopup(e);
+ else return true;
+ }
+ @Override
+ public void onLongPress(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
+
+ if (playerImpl.popupPlayerSelected()) onLongPressInPopup(e);
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
+ if (playerImpl.popupPlayerSelected()) return onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY);
+ else return onScrollInMain(initialEvent, movingEvent, distanceX, distanceY);
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ if (DEBUG) Log.d(TAG, "onFling() called with velocity: dX=[" + velocityX + "], dY=[" + velocityY + "]");
+
+ if (playerImpl.popupPlayerSelected()) return onFlingInPopup(e1, e2, velocityX, velocityY);
+ else return true;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (DEBUG && false) Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]");
+
+ if (playerImpl.popupPlayerSelected()) return onTouchInPopup(v, event);
+ else return onTouchInMain(v, event);
+ }
+
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Main player listener
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private boolean onDoubleTapInMain(MotionEvent e) {
+ if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
+ playerImpl.onFastForward();
+ } else if (e.getX() < playerImpl.getRootView().getWidth() / 3) {
+ playerImpl.onFastRewind();
+ } else {
+ playerImpl.getPlayPauseButton().performClick();
+ }
+
+ return true;
+ }
+
+
+ private boolean onSingleTapConfirmedInMain(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
+
+ if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) return true;
+
+ if (playerImpl.isControlsVisible()) {
+ playerImpl.hideControls(150, 0);
+ } else {
+ if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
+ playerImpl.showControls(0);
+ } else {
+ playerImpl.showControlsThenHide();
+ }
+ if (playerImpl.isInFullscreen()) {
+ int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ playerImpl.getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
+ playerImpl.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+ playerImpl.getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ }
+ return true;
+ }
+
+ private boolean onScrollInMain(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
+ if (!isVolumeGestureEnabled && !isBrightnessGestureEnabled) return false;
+
+ //noinspection PointlessBooleanExpression
+ if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " +
+ ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" +
+ ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" +
+ ", distanceXy = [" + distanceX + ", " + distanceY + "]");
+
+ final boolean insideThreshold = Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD;
+ if (!isMovingInMain && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY))
+ || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
+ return false;
+ }
+
+ isMovingInMain = true;
+
+ boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
+ boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2;
+ boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea;
+
+ if (isVolumeGestureEnabled && acceptVolumeArea) {
+ playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
+ float currentProgressPercent =
+ (float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
+ int currentVolume = (int) (maxVolume * currentProgressPercent);
+ playerImpl.getAudioReactor().setVolume(currentVolume);
+
+ if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
+
+ final int resId =
+ currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
+ : currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp
+ : currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp
+ : R.drawable.ic_volume_up_white_72dp;
+
+ playerImpl.getVolumeImageView().setImageDrawable(
+ AppCompatResources.getDrawable(service, resId)
+ );
+
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) {
+ animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200);
+ }
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
+ playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
+ }
+ } else if (isBrightnessGestureEnabled && acceptBrightnessArea) {
+ Activity parent = playerImpl.getParentActivity();
+ if (parent == null) return true;
+
+ Window window = parent.getWindow();
+
+ playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
+ float currentProgressPercent =
+ (float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();
+ WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.screenBrightness = currentProgressPercent;
+ window.setAttributes(layoutParams);
+
+ if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
+
+ final int resId =
+ currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
+ : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
+ : R.drawable.ic_brightness_high_white_72dp;
+
+ playerImpl.getBrightnessImageView().setImageDrawable(
+ AppCompatResources.getDrawable(service, resId)
+ );
+
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) {
+ animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200);
+ }
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
+ playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE);
+ }
+ }
+ return true;
+ }
+
+ private void onScrollEndInMain() {
+ if (DEBUG) Log.d(TAG, "onScrollEnd() called");
+
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
+ animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
+ }
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
+ animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
+ }
+
+ if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
+ playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
+ }
+ }
+
+ private boolean onTouchInMain(View v, MotionEvent event) {
+ playerImpl.getGestureDetector().onTouchEvent(event);
+ if (event.getAction() == MotionEvent.ACTION_UP && isMovingInMain) {
+ isMovingInMain = false;
+ onScrollEndInMain();
+ }
+ // This hack allows to stop receiving touch events on appbar while touching video player view
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isInFullscreen());
+ return true;
+ case MotionEvent.ACTION_UP:
+ v.getParent().requestDisallowInterceptTouchEvent(false);
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Popup player listener
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private boolean onDoubleTapInPopup(MotionEvent e) {
+ if (playerImpl == null || !playerImpl.isPlaying()) return false;
+
+ playerImpl.hideControls(0, 0);
+
+ if (e.getX() > playerImpl.getPopupWidth() / 2) {
+ playerImpl.onFastForward();
+ } else {
+ playerImpl.onFastRewind();
+ }
+
+ return true;
+ }
+
+ private boolean onSingleTapConfirmedInPopup(MotionEvent e) {
+ if (playerImpl == null || playerImpl.getPlayer() == null) return false;
+ if (playerImpl.isControlsVisible()) {
+ playerImpl.hideControls(100, 100);
+ } else {
+ playerImpl.showControlsThenHide();
+
+ }
+ playerImpl.onPlayPause();
+ return true;
+ }
+
+ private boolean onDownInPopup(MotionEvent e) {
+ // Fix popup position when the user touch it, it may have the wrong one
+ // because the soft input is visible (the draggable area is currently resized).
+ playerImpl.checkPopupPositionBounds(playerImpl.getCloseOverlayView().getWidth(), playerImpl.getCloseOverlayView().getHeight());
+
+ initialPopupX = playerImpl.getPopupLayoutParams().x;
+ initialPopupY = playerImpl.getPopupLayoutParams().y;
+ playerImpl.setPopupWidth(playerImpl.getPopupLayoutParams().width);
+ playerImpl.setPopupHeight(playerImpl.getPopupLayoutParams().height);
+ return super.onDown(e);
+ }
+
+ private void onLongPressInPopup(MotionEvent e) {
+ playerImpl.updateScreenSize();
+ playerImpl.checkPopupPositionBounds();
+ playerImpl.updatePopupSize((int) playerImpl.getScreenWidth(), -1);
+ }
+
+ private boolean onScrollInPopup(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
+ if (isResizing || playerImpl == null) return super.onScroll(initialEvent, movingEvent, distanceX, distanceY);
+
+ if (!isMovingInPopup) {
+ animateView(playerImpl.getCloseOverlayButton(), true, 200);
+ }
+
+ isMovingInPopup = true;
+
+ float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX);
+ float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY);
+
+ if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth());
+ else if (posX < 0) posX = 0;
+
+ if (posY > (playerImpl.getScreenHeight() - playerImpl.getPopupHeight())) posY = (int) (playerImpl.getScreenHeight() - playerImpl.getPopupHeight());
+ else if (posY < 0) posY = 0;
+
+ playerImpl.getPopupLayoutParams().x = (int) posX;
+ playerImpl.getPopupLayoutParams().y = (int) posY;
+
+ final View closingOverlayView = playerImpl.getClosingOverlayView();
+ if (playerImpl.isInsideClosingRadius(movingEvent)) {
+ if (closingOverlayView.getVisibility() == View.GONE) {
+ animateView(closingOverlayView, true, 250);
+ }
+ } else {
+ if (closingOverlayView.getVisibility() == View.VISIBLE) {
+ animateView(closingOverlayView, false, 0);
+ }
+ }
+
+ //noinspection PointlessBooleanExpression
+ if (DEBUG && false) {
+ Log.d(TAG, "PopupVideoPlayer.onScroll = " +
+ ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + ", e1.getX,Y = [" + initialEvent.getX() + ", " + initialEvent.getY() + "]" +
+ ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + ", e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "]" +
+ ", distanceX,Y = [" + distanceX + ", " + distanceY + "]" +
+ ", posX,Y = [" + posX + ", " + posY + "]" +
+ ", popupW,H = [" + playerImpl.getPopupWidth() + " x " + playerImpl.getPopupHeight() + "]");
+ }
+ playerImpl.windowManager.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams());
+ return true;
+ }
+
+ private void onScrollEndInPopup(MotionEvent event) {
+ if (playerImpl == null) return;
+ if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
+ playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
+ }
+
+ if (playerImpl.isInsideClosingRadius(event)) {
+ playerImpl.closePopup();
+ } else {
+ animateView(playerImpl.getClosingOverlayView(), false, 0);
+
+ if (!playerImpl.isPopupClosing) {
+ animateView(playerImpl.getCloseOverlayButton(), false, 200);
+ }
+ }
+ }
+
+ private boolean onFlingInPopup(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ if (playerImpl == null) return false;
+
+ final float absVelocityX = Math.abs(velocityX);
+ final float absVelocityY = Math.abs(velocityY);
+ if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
+ if (absVelocityX > tossFlingVelocity) playerImpl.getPopupLayoutParams().x = (int) velocityX;
+ if (absVelocityY > tossFlingVelocity) playerImpl.getPopupLayoutParams().y = (int) velocityY;
+ playerImpl.checkPopupPositionBounds();
+ playerImpl.windowManager.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams());
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onTouchInPopup(View v, MotionEvent event) {
+ playerImpl.getGestureDetector().onTouchEvent(event);
+ if (playerImpl == null) return false;
+ if (event.getPointerCount() == 2 && !isMovingInPopup && !isResizing) {
+ if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.");
+ playerImpl.showAndAnimateControl(-1, true);
+ playerImpl.getLoadingPanel().setVisibility(View.GONE);
+
+ playerImpl.hideControls(0, 0);
+ animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0);
+ animateView(playerImpl.getResizingIndicator(), true, 200, 0);
+ isResizing = true;
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) {
+ if (DEBUG) Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
+ return handleMultiDrag(event);
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (DEBUG)
+ Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
+ if (isMovingInPopup) {
+ isMovingInPopup = false;
+ onScrollEndInPopup(event);
+ }
+
+ if (isResizing) {
+ isResizing = false;
+ animateView(playerImpl.getResizingIndicator(), false, 100, 0);
+ playerImpl.changeState(playerImpl.getCurrentState());
+ }
+
+ if (!playerImpl.isPopupClosing) {
+ playerImpl.savePositionAndSize();
+ }
+ }
+
+ v.performClick();
+ return true;
+ }
+
+ private boolean handleMultiDrag(final MotionEvent event) {
+ if (event.getPointerCount() != 2) return false;
+
+ final float firstPointerX = event.getX(0);
+ final float secondPointerX = event.getX(1);
+
+ final float diff = Math.abs(firstPointerX - secondPointerX);
+ if (firstPointerX > secondPointerX) {
+ // second pointer is the anchor (the leftmost pointer)
+ playerImpl.getPopupLayoutParams().x = (int) (event.getRawX() - diff);
+ } else {
+ // first pointer is the anchor
+ playerImpl.getPopupLayoutParams().x = (int) event.getRawX();
+ }
+
+ playerImpl.checkPopupPositionBounds();
+ playerImpl.updateScreenSize();
+
+ final int width = (int) Math.min(playerImpl.getScreenWidth(), diff);
+ playerImpl.updatePopupSize(width, -1);
+
+ return true;
+ }
+
+}
+
+
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
new file mode 100644
index 000000000..7422f9442
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
@@ -0,0 +1,15 @@
+package org.schabi.newpipe.player.event;
+
+import com.google.android.exoplayer2.ExoPlaybackException;
+
+public interface PlayerServiceEventListener extends PlayerEventListener {
+ void onFullscreenStateChanged(boolean fullscreen);
+
+ void onMoreOptionsLongClicked();
+
+ void onPlayerError(ExoPlaybackException error);
+
+ boolean isFragmentStopped();
+
+ void hideSystemUIIfNeeded();
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
index 4feed74fe..457b72120 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
@@ -80,8 +80,10 @@ public class PlaybackParameterDialog extends DialogFragment {
public static PlaybackParameterDialog newInstance(final double playbackTempo,
final double playbackPitch,
- final boolean playbackSkipSilence) {
+ final boolean playbackSkipSilence,
+ Callback callback) {
PlaybackParameterDialog dialog = new PlaybackParameterDialog();
+ dialog.callback = callback;
dialog.initialTempo = playbackTempo;
dialog.initialPitch = playbackPitch;
@@ -99,9 +101,9 @@ public class PlaybackParameterDialog extends DialogFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- if (context != null && context instanceof Callback) {
+ if (context instanceof Callback) {
callback = (Callback) context;
- } else {
+ } else if (callback == null) {
dismiss();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java
new file mode 100644
index 000000000..534fb26c3
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java
@@ -0,0 +1,29 @@
+package org.schabi.newpipe.settings;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+
+public class SettingsContentObserver extends ContentObserver {
+ private OnChangeListener listener;
+
+ public interface OnChangeListener {
+ void onSettingsChanged();
+ }
+
+ public SettingsContentObserver(Handler handler, OnChangeListener listener) {
+ super(handler);
+ this.listener = listener;
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return super.deliverSelfNotifications();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ if (listener != null)
+ listener.onSettingsChanged();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index e2b03c8e8..07ef0b6ac 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -10,6 +10,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
@@ -44,14 +45,9 @@ import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
-import org.schabi.newpipe.player.BackgroundPlayer;
-import org.schabi.newpipe.player.BackgroundPlayerActivity;
-import org.schabi.newpipe.player.BasePlayer;
-import org.schabi.newpipe.player.MainVideoPlayer;
-import org.schabi.newpipe.player.PopupVideoPlayer;
-import org.schabi.newpipe.player.PopupVideoPlayerActivity;
-import org.schabi.newpipe.player.VideoPlayer;
+import org.schabi.newpipe.player.*;
import org.schabi.newpipe.player.playqueue.PlayQueue;
+import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.settings.SettingsActivity;
import java.util.ArrayList;
@@ -78,6 +74,9 @@ public class NavigationHelper {
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback);
+ int playerType = intent.getIntExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, playerType);
+
return intent;
}
@@ -117,10 +116,13 @@ public class NavigationHelper {
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
}
- public static void playOnMainPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
- final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue, resumePlayback);
- playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(playerIntent);
+ public static void playOnMainPlayer(final AppCompatActivity activity, final PlayQueue queue, final boolean resumePlayback) {
+ playOnMainPlayer(activity.getSupportFragmentManager(), queue, resumePlayback);
+ }
+
+ public static void playOnMainPlayer(final FragmentManager fragmentManager, final PlayQueue queue, boolean autoPlay) {
+ PlayQueueItem currentStream = queue.getItem();
+ NavigationHelper.openVideoDetailFragment(fragmentManager, currentStream.getServiceId(), currentStream.getUrl(), currentStream.getTitle(), autoPlay, queue);
}
public static void playOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
@@ -130,12 +132,16 @@ public class NavigationHelper {
}
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
- startService(context, getPlayerIntent(context, PopupVideoPlayer.class, queue, resumePlayback));
+ Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
+ startService(context, intent);
}
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
- startService(context, getPlayerIntent(context, BackgroundPlayer.class, queue, resumePlayback));
+ Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
+ startService(context, intent);
}
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
@@ -149,8 +155,9 @@ public class NavigationHelper {
}
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
- startService(context,
- getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend, resumePlayback));
+ Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
+ startService(context, intent);
}
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
@@ -159,8 +166,9 @@ public class NavigationHelper {
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
- startService(context,
- getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend, resumePlayback));
+ Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
+ startService(context, intent);
}
public static void startService(@NonNull final Context context, @NonNull final Intent intent) {
@@ -281,29 +289,35 @@ public class NavigationHelper {
}
public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title) {
- openVideoDetailFragment(fragmentManager, serviceId, url, title, false);
+ openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null);
}
- public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title, boolean autoPlay) {
- Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_holder);
+ public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title, boolean autoPlay, PlayQueue playQueue) {
+ Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder);
if (title == null) title = "";
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
+ expandMainPlayer(fragment.getActivity());
VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
detailFragment.setAutoplay(autoPlay);
- detailFragment.selectAndLoadVideo(serviceId, url, title);
+ detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue);
return;
}
- VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title);
+ VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title, playQueue);
instance.setAutoplay(autoPlay);
defaultTransaction(fragmentManager)
- .replace(R.id.fragment_holder, instance)
- .addToBackStack(null)
+ .replace(R.id.fragment_player_holder, instance)
+ .runOnCommit(() -> expandMainPlayer(instance.getActivity()))
.commit();
}
+ public static void expandMainPlayer(Context context) {
+ final Intent intent = new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER);
+ context.sendBroadcast(intent);
+ }
+
public static void openChannelFragment(
FragmentManager fragmentManager,
int serviceId,
@@ -458,10 +472,6 @@ public class NavigationHelper {
return getServicePlayerActivityIntent(context, BackgroundPlayerActivity.class);
}
- public static Intent getPopupPlayerActivityIntent(final Context context) {
- return getServicePlayerActivityIntent(context, PopupVideoPlayerActivity.class);
- }
-
private static Intent getServicePlayerActivityIntent(final Context context,
final Class activityClass) {
Intent intent = new Intent(context, activityClass);
diff --git a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java
index c5c78a726..17768cd08 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java
@@ -17,6 +17,6 @@ public class ShareUtils {
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, url);
- context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title)));
+ context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index b535db2b8..cbcc6eeb0 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -2,9 +2,10 @@
@@ -120,7 +121,7 @@
android:layout_height="match_parent"
android:layout_below="@id/playQueueControl"
android:scrollbars="vertical"
- app:layoutManager="LinearLayoutManager"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/play_queue_item"/>
@@ -133,37 +134,46 @@
android:visibility="gone"
tools:visibility="visible">
+
-
+
+
+ tools:ignore="RtlHardcoded"
+ android:layout_weight="1">
+ tools:text="1x"/>
+ tools:ignore="ContentDescription,RtlHardcoded"
+ android:visibility="gone"/>
-
-
+
+
@@ -274,9 +284,7 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
- android:layout_alignParentLeft="true"
android:gravity="center"
android:minWidth="50dp"
android:textColor="@android:color/white"
@@ -291,18 +299,50 @@
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
- android:layout_toLeftOf="@id/switchBackground"
- android:layout_toRightOf="@id/resizeTextView"
android:gravity="center|left"
android:minHeight="35dp"
- android:minWidth="40dp"
- android:paddingLeft="2dp"
- android:paddingRight="2dp"
+ android:minWidth="50dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="RelativeOverlap,RtlHardcoded"
- tools:text="English" />
+ tools:text="English"/>
+
+
+
+
+
+
-
+
+
+
-
-
-
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_fullscreen_white"
+ tools:ignore="ContentDescription,RtlHardcoded"
+ android:visibility="gone"
+ tools:visibility="visible"/>
@@ -388,7 +396,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
- android:minHeight="40dp"
+ android:minHeight="30dp"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
@@ -433,65 +441,52 @@
+
+
+
+
+
+
-
-
-
+
@@ -533,10 +528,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml
index 02b0a7b86..266133a8c 100644
--- a/app/src/main/res/layout-large-land/fragment_video_detail.xml
+++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml
@@ -1,7 +1,12 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -153,7 +189,6 @@
android:id="@+id/detail_content_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:windowBackground"
app:layout_scrollFlags="scroll">
@@ -506,26 +541,6 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 92e73234f..46b88873c 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -2,6 +2,7 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index f2b204b75..6bc259b88 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -2,9 +2,10 @@
@@ -131,37 +132,46 @@
android:visibility="gone"
tools:visibility="visible">
+
-
+
+
+ tools:ignore="RtlHardcoded"
+ android:layout_weight="1">
+ tools:text="1x"/>
+ tools:ignore="ContentDescription,RtlHardcoded"
+ android:visibility="gone"/>
-
-
+
+
@@ -272,9 +282,7 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
- android:layout_alignParentLeft="true"
android:gravity="center"
android:minWidth="50dp"
android:textColor="@android:color/white"
@@ -289,18 +297,50 @@
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
- android:layout_toLeftOf="@id/switchBackground"
- android:layout_toRightOf="@id/resizeTextView"
android:gravity="center|left"
android:minHeight="35dp"
- android:minWidth="40dp"
- android:paddingLeft="2dp"
- android:paddingRight="2dp"
+ android:minWidth="50dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="RelativeOverlap,RtlHardcoded"
- tools:text="English" />
+ tools:text="English"/>
+
+
+
+
+
+
-
+
+
+
-
-
-
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_fullscreen_white"
+ tools:ignore="ContentDescription,RtlHardcoded"
+ android:visibility="gone"
+ tools:visibility="visible"/>
@@ -386,7 +394,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
- android:minHeight="40dp"
+ android:minHeight="30dp"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
@@ -431,65 +439,52 @@
+
+
+
+
+
+
-
-
-
+
@@ -531,10 +526,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index cc57e09d4..5e8d16589 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -5,13 +5,44 @@
android:id="@+id/video_item_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="?attr/windowBackground"
android:focusableInTouchMode="true">
+ android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -149,7 +187,6 @@
android:id="@+id/detail_content_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:windowBackground"
app:layout_scrollFlags="scroll">
@@ -504,25 +541,106 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_play_queue_bg.xml b/app/src/main/res/menu/menu_play_queue_bg.xml
index 1a3ed0e5a..92f5ae67b 100644
--- a/app/src/main/res/menu/menu_play_queue_bg.xml
+++ b/app/src/main/res/menu/menu_play_queue_bg.xml
@@ -7,4 +7,9 @@
android:orderInCategory="1999"
android:title="@string/switch_to_popup"
app:showAsAction="never"/>
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 582c4bade..07b71e5ee 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -48,6 +48,7 @@
18sp
18sp
70dp
+ 60dp
5dp
50dp
diff --git a/app/src/main/res/xml/history_settings.xml b/app/src/main/res/xml/history_settings.xml
index 81dc195f3..6d64f2489 100644
--- a/app/src/main/res/xml/history_settings.xml
+++ b/app/src/main/res/xml/history_settings.xml
@@ -21,7 +21,6 @@
Date: Tue, 31 Dec 2019 05:07:07 +0300
Subject: [PATCH 02/40] Optimizations and fixes of rare situations - popup
after orientation change had incorrect allowed bounds for swiping - popup
could cause a crash after many quick switches to main player and back -
better method of setting fullscreen/non-fullscreen layout using thumbnail
view. Also fixed thumbnail height in fullscreen layout - global settings
observer didn't work when a user closed a service manually via notification
because it checked for service existing - app will now exits from fullscreen
mode when the user switches players - playQueuePanel has visibility "gone" by
default (not "invisible") because "invisible" can cause problems
---
.../fragments/detail/VideoDetailFragment.java | 32 +++++++++----------
.../newpipe/player/VideoPlayerImpl.java | 4 +--
.../player/event/PlayerGestureListener.java | 3 +-
.../activity_main_player.xml | 2 +-
.../main/res/layout/activity_main_player.xml | 2 +-
5 files changed, 22 insertions(+), 21 deletions(-)
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 bae6e57aa..245a9a495 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
@@ -1040,6 +1040,9 @@ public class VideoDetailFragment
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
+ // If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
+ if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
} else {
@@ -1056,6 +1059,9 @@ public class VideoDetailFragment
// See UI changes while remote playQueue changes
if (!bounded) startService(false);
+ // If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
+ if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+
PlayQueue queue = setupPlayQueueForIntent(append);
if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
@@ -1181,17 +1187,7 @@ public class VideoDetailFragment
// Check if viewHolder already contains a child
if (player.getRootView() != viewHolder) removeVideoPlayerView();
-
- final int newHeight;
- if (player.isInFullscreen())
- newHeight = activity.getWindow().getDecorView().getHeight();
- else
- newHeight = FrameLayout.LayoutParams.MATCH_PARENT;
-
- if (viewHolder.getLayoutParams().height != newHeight) {
- viewHolder.getLayoutParams().height = newHeight;
- viewHolder.requestLayout();
- }
+ setHeightThumbnail();
// Prevent from re-adding a view multiple times
if (player.getRootView().getParent() == null) viewHolder.addView(player.getRootView());
@@ -1242,9 +1238,15 @@ public class VideoDetailFragment
private void setHeightThumbnail() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
- int height = isPortrait
- ? (int) (metrics.widthPixels / (16.0f / 9.0f))
- : (int) (metrics.heightPixels / 2f);
+
+ int height;
+ if (player != null && player.isInFullscreen())
+ height = activity.getWindow().getDecorView().getHeight();
+ else
+ height = isPortrait
+ ? (int) (metrics.widthPixels / (16.0f / 9.0f))
+ : (int) (metrics.heightPixels / 2f);;
+
thumbnailImageView.setLayoutParams(
new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
@@ -1334,8 +1336,6 @@ public class VideoDetailFragment
@Override
public void onSettingsChanged() {
- if (player == null) return;
-
if(!globalScreenOrientationLocked())
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 8cef5b453..492fd9ac5 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -1329,7 +1329,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void updatePopupSize(int width, int height) {
if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
- if (popupLayoutParams == null || windowManager == null || !popupPlayerSelected() || getRootView().getParent() == null)
+ if (popupLayoutParams == null || windowManager == null || getParentActivity() != null || getRootView().getParent() == null)
return;
width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
@@ -1347,7 +1347,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void updateWindowFlags(final int flags) {
- if (popupLayoutParams == null || windowManager == null || !popupPlayerSelected() || getRootView().getParent() == null)
+ if (popupLayoutParams == null || windowManager == null || getParentActivity() != null || getRootView().getParent() == null)
return;
popupLayoutParams.flags = flags;
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 72462beff..398cf9534 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
@@ -299,7 +299,8 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
private boolean onDownInPopup(MotionEvent e) {
// Fix popup position when the user touch it, it may have the wrong one
// because the soft input is visible (the draggable area is currently resized).
- playerImpl.checkPopupPositionBounds(playerImpl.getCloseOverlayView().getWidth(), playerImpl.getCloseOverlayView().getHeight());
+ playerImpl.updateScreenSize();
+ playerImpl.checkPopupPositionBounds();
initialPopupX = playerImpl.getPopupLayoutParams().x;
initialPopupY = playerImpl.getPopupLayoutParams().y;
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index cbcc6eeb0..0e2011910 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -53,7 +53,7 @@
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_height="match_parent"
- android:visibility="invisible"
+ android:visibility="gone"
android:background="?attr/queue_background_color"
tools:visibility="visible">
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 6bc259b88..39c3f4410 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -51,7 +51,7 @@
android:id="@+id/playQueuePanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="invisible"
+ android:visibility="gone"
android:background="?attr/queue_background_color"
tools:visibility="visible">
From bc2dc8d933181aca8f9ea62d0d2f102577286e78 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Tue, 31 Dec 2019 19:06:39 +0300
Subject: [PATCH 03/40] First block of fixes for review - popup player click
event changed to show/hide buttons - queue panel WORKS. Finally - removed
theme overriding in fragment - added scroll to top after stream selection -
adjusted padding/margin of buttons in player - player will itself in
fullscreen after user hides it in fullscreen mode and then expands it again
while video still playing
---
.../material/appbar/FlingBehavior.java | 42 +++++++++---
.../fragments/detail/VideoDetailFragment.java | 11 +++-
.../newpipe/player/VideoPlayerImpl.java | 6 +-
.../player/event/PlayerGestureListener.java | 1 -
.../schabi/newpipe/util/NavigationHelper.java | 1 +
.../activity_main_player.xml | 66 +++++++++----------
.../main/res/layout/activity_main_player.xml | 66 +++++++++----------
7 files changed, 109 insertions(+), 84 deletions(-)
diff --git a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
index 4a2662f53..ff2860558 100644
--- a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
+++ b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
@@ -1,12 +1,17 @@
package com.google.android.material.appbar;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.OverScroller;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import org.jetbrains.annotations.NotNull;
+import org.schabi.newpipe.R;
import java.lang.reflect.Field;
@@ -17,21 +22,40 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
super(context, attrs);
}
+ private boolean allowScroll = true;
+ private Rect playQueueRect = new Rect();
+
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- // remove reference to old nested scrolling child
- resetNestedScrollingChild();
- // Stop fling when your finger touches the screen
- stopAppBarLayoutFling();
- break;
- default:
- break;
+ ViewGroup playQueue = child.findViewById(R.id.playQueue);
+ if (playQueue != null) {
+ playQueue.getGlobalVisibleRect(playQueueRect);
+ if (playQueueRect.contains((int) ev.getX(), (int) ev.getY())) {
+ allowScroll = false;
+ return false;
+ }
+ }
+ allowScroll = true;
+
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // remove reference to old nested scrolling child
+ resetNestedScrollingChild();
+ // Stop fling when your finger touches the screen
+ stopAppBarLayoutFling();
}
return super.onInterceptTouchEvent(parent, child, ev);
}
+ @Override
+ public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
+ return allowScroll && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
+ }
+
+ @Override
+ public boolean onNestedFling(@NotNull CoordinatorLayout coordinatorLayout, @NotNull AppBarLayout child, @NotNull View target, float velocityX, float velocityY, boolean consumed) {
+ return allowScroll && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
+ }
+
@Nullable
private OverScroller getScrollerField() {
try {
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 245a9a495..d0ca95d6b 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
@@ -311,7 +311,6 @@ public class VideoDetailFragment
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
- ThemeHelper.setTheme(getContext());
// Let's play all streams automatically
setAutoplay(true);
@@ -953,14 +952,14 @@ public class VideoDetailFragment
showLoading();
initTabs();
- if (scrollToTop) appBarLayout.setExpanded(true, true);
+ if (scrollToTop) scrollToTop();
handleResult(info);
showContent();
}
protected void prepareAndLoadInfo() {
- appBarLayout.setExpanded(true, true);
+ scrollToTop();
startLoading(false);
}
@@ -1029,6 +1028,10 @@ public class VideoDetailFragment
}
}
+ public void scrollToTop() {
+ appBarLayout.setExpanded(true, true);
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Play Utils
//////////////////////////////////////////////////////////////////////////*/
@@ -1862,6 +1865,8 @@ public class VideoDetailFragment
// Disable click because overlay buttons located on top of buttons from the player
setOverlayElementsClickable(false);
hideSystemUIIfNeeded();
+ if (isLandscape() && player != null && player.isPlaying() && !player.isInFullscreen())
+ player.toggleFullscreen();
break;
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 492fd9ac5..71fe21b6d 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -346,7 +346,8 @@ public class VideoPlayerImpl extends VideoPlayer
// Use smaller value to be consistent between screen orientations
// (and to make usage easier)
int width = r - l, height = b - t;
- maxGestureLength = (int) (Math.min(width, height) * MAX_GESTURE_LENGTH);
+ int min = Math.min(width, height);
+ maxGestureLength = (int) (min * MAX_GESTURE_LENGTH);
if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
@@ -354,6 +355,7 @@ public class VideoPlayerImpl extends VideoPlayer
brightnessProgressBar.setMax(maxGestureLength);
setInitialGestureValues();
+ queueLayout.getLayoutParams().height = min - queueLayout.getTop();
}
});
}
@@ -620,8 +622,6 @@ public class VideoPlayerImpl extends VideoPlayer
DEFAULT_CONTROLS_DURATION);
itemsList.scrollToPosition(playQueue.getIndex());
-
- if (playQueue.getStreams().size() > 4 && !isInFullscreen()) toggleFullscreen();
}
private void onQueueClosed() {
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 398cf9534..00a511ca3 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
@@ -292,7 +292,6 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
playerImpl.showControlsThenHide();
}
- playerImpl.onPlayPause();
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 07ef0b6ac..648894cc1 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -301,6 +301,7 @@ public class NavigationHelper {
VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
detailFragment.setAutoplay(autoPlay);
detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue);
+ detailFragment.scrollToTop();
return;
}
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 0e2011910..8d7d9b639 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -149,6 +149,7 @@
android:background="@drawable/player_top_controls_bg"
android:orientation="vertical"
android:gravity="top"
+ android:paddingTop="4dp"
android:baselineAligned="false"
android:layout_toStartOf="@id/fullScreenButton">
@@ -158,7 +159,6 @@
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="top"
- android:paddingTop="4dp"
android:paddingBottom="7dp"
android:paddingLeft="2dp"
android:paddingRight="6dp"
@@ -170,7 +170,8 @@
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
- android:paddingLeft="8dp"
+ android:paddingTop="6dp"
+ android:paddingLeft="16dp"
android:paddingRight="8dp"
tools:ignore="RtlHardcoded"
android:layout_weight="1">
@@ -212,8 +213,9 @@
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginStart="5dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
android:text="720p"
@@ -227,7 +229,8 @@
android:id="@+id/playbackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minHeight="35dp"
android:minWidth="40dp"
@@ -241,11 +244,10 @@
android:id="@+id/queueButton"
android:layout_width="30dp"
android:layout_height="35dp"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
- android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_list_white_24dp"
android:background="?attr/selectableItemBackground"
@@ -256,8 +258,8 @@
android:id="@+id/moreOptionsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="2dp"
- android:padding="5dp"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -272,9 +274,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
- android:paddingTop="4dp"
- android:paddingBottom="7dp"
- android:paddingStart="6dp"
+ android:paddingStart="16dp"
android:paddingEnd="6dp"
android:visibility="invisible"
tools:ignore="RtlHardcoded"
@@ -284,7 +284,8 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginRight="8dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
android:textColor="@android:color/white"
@@ -297,8 +298,8 @@
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
android:minWidth="50dp"
@@ -316,13 +317,12 @@
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 39c3f4410..8d00142a8 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -147,6 +147,7 @@
android:background="@drawable/player_top_controls_bg"
android:orientation="vertical"
android:gravity="top"
+ android:paddingTop="4dp"
android:baselineAligned="false"
android:layout_toStartOf="@id/fullScreenButton">
@@ -156,7 +157,6 @@
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="top"
- android:paddingTop="4dp"
android:paddingBottom="7dp"
android:paddingLeft="2dp"
android:paddingRight="6dp"
@@ -168,7 +168,8 @@
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
- android:paddingLeft="8dp"
+ android:paddingTop="6dp"
+ android:paddingLeft="16dp"
android:paddingRight="8dp"
tools:ignore="RtlHardcoded"
android:layout_weight="1">
@@ -210,8 +211,9 @@
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginStart="5dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
android:text="720p"
@@ -225,7 +227,8 @@
android:id="@+id/playbackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minHeight="35dp"
android:minWidth="40dp"
@@ -239,11 +242,10 @@
android:id="@+id/queueButton"
android:layout_width="30dp"
android:layout_height="35dp"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
- android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_list_white_24dp"
android:background="?attr/selectableItemBackground"
@@ -254,8 +256,8 @@
android:id="@+id/moreOptionsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="2dp"
- android:padding="5dp"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -270,9 +272,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
- android:paddingTop="4dp"
- android:paddingBottom="7dp"
- android:paddingStart="6dp"
+ android:paddingStart="16dp"
android:paddingEnd="6dp"
android:visibility="invisible"
tools:ignore="RtlHardcoded"
@@ -282,7 +282,8 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:layout_marginRight="8dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
android:textColor="@android:color/white"
@@ -295,8 +296,8 @@
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
+ android:padding="6dp"
+ android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
android:minWidth="50dp"
@@ -314,13 +315,12 @@
From 4519dd010dcc8ac8791d97046363481d9afebe72 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Fri, 3 Jan 2020 08:05:31 +0300
Subject: [PATCH 04/40] Second block of fixes for review - hide/show controls
with respect of SystemUI. In fullscreen mode controls will stay away from
NavigationBar - notification from running service will be hidden if a user
disabled background playback - fixed incorrect handling of a system method in
API 19 - better MultiWindow support
---
.../fragments/detail/VideoDetailFragment.java | 28 ++++++------
.../org/schabi/newpipe/player/MainPlayer.java | 7 ++-
.../schabi/newpipe/player/VideoPlayer.java | 10 ++---
.../newpipe/player/VideoPlayerImpl.java | 45 ++++++++++++++++---
.../event/CustomBottomSheetBehavior.java | 21 +++++----
.../player/event/PlayerGestureListener.java | 8 ----
.../activity_main_player.xml | 7 ++-
.../main/res/layout/activity_main_player.xml | 7 ++-
.../main/res/layout/fragment_video_detail.xml | 4 +-
9 files changed, 87 insertions(+), 50 deletions(-)
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 d0ca95d6b..8813d139d 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
@@ -1244,7 +1244,7 @@ public class VideoDetailFragment
int height;
if (player != null && player.isInFullscreen())
- height = activity.getWindow().getDecorView().getHeight();
+ height = isInMultiWindow() ? getView().getHeight() : activity.getWindow().getDecorView().getHeight();
else
height = isPortrait
? (int) (metrics.widthPixels / (16.0f / 9.0f))
@@ -1669,8 +1669,7 @@ public class VideoDetailFragment
player.useVideoSource(false);
else if (player.minimizeOnPopupEnabled())
NavigationHelper.playOnPopupPlayer(activity, playQueue, true);
- else
- player.getPlayer().setPlayWhenReady(false);
+ else player.onPause();
}
else player.useVideoSource(true);
}
@@ -1751,23 +1750,18 @@ public class VideoDetailFragment
getActivity().getWindow().getDecorView().setSystemUiVisibility(0);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
- getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
private void hideSystemUi() {
if (DEBUG) Log.d(TAG, "hideSystemUi() called");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- visibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- }
- activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
- }
+ int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
activity.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
@@ -1813,6 +1807,10 @@ public class VideoDetailFragment
return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels;
}
+ private boolean isInMultiWindow() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode();
+ }
+
/*
* Means that the player fragment was swiped away via BottomSheetLayout and is empty but ready for any new actions. See cleanUp()
* */
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index a0d4eb7d9..6e5082494 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
+import android.util.DisplayMetrics;
import android.view.ViewGroup;
import android.view.WindowManager;
import androidx.core.app.NotificationCompat;
@@ -183,7 +184,11 @@ public final class MainPlayer extends Service {
//////////////////////////////////////////////////////////////////////////*/
boolean isLandscape() {
- return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels;
+ // DisplayMetrics from activity context knows about MultiWindow feature while DisplayMetrics from app context doesn't
+ final DisplayMetrics metrics = playerImpl.getParentActivity() != null ?
+ playerImpl.getParentActivity().getResources().getDisplayMetrics()
+ : getResources().getDisplayMetrics();
+ return metrics.heightPixels < metrics.widthPixels;
}
public View getView() {
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 3a906d80c..07f485e7a 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
@@ -38,11 +38,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.View;
-import android.widget.ImageView;
-import android.widget.PopupMenu;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
+import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -131,7 +127,7 @@ public abstract class VideoPlayer extends BasePlayer
private TextView playbackLiveSync;
private TextView playbackSpeedTextView;
- private View topControlsRoot;
+ private LinearLayout topControlsRoot;
private TextView qualityTextView;
private SubtitleView subtitleView;
@@ -961,7 +957,7 @@ public abstract class VideoPlayer extends BasePlayer
return playbackEndTime;
}
- public View getTopControlsRoot() {
+ public LinearLayout getTopControlsRoot() {
return topControlsRoot;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 71fe21b6d..76c8106be 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
import android.content.*;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
@@ -124,7 +125,6 @@ public class VideoPlayerImpl extends VideoPlayer
private ImageButton shareButton;
private View primaryControls;
- private LinearLayout topControls;
private View secondaryControls;
private int maxGestureLength;
@@ -245,7 +245,6 @@ public class VideoPlayerImpl extends VideoPlayer
this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton);
this.primaryControls = rootView.findViewById(R.id.primaryControls);
- this.topControls = rootView.findViewById(R.id.topControls);
this.secondaryControls = rootView.findViewById(R.id.secondaryControls);
this.shareButton = rootView.findViewById(R.id.share);
@@ -285,7 +284,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE);
queueButton.setVisibility(View.GONE);
moreOptionsButton.setVisibility(View.GONE);
- topControls.setOrientation(LinearLayout.HORIZONTAL);
+ getTopControlsRoot().setOrientation(LinearLayout.HORIZONTAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
secondaryControls.setAlpha(1f);
secondaryControls.setVisibility(View.VISIBLE);
@@ -297,7 +296,7 @@ public class VideoPlayerImpl extends VideoPlayer
fullscreenButton.setVisibility(View.GONE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE);
- topControls.setOrientation(LinearLayout.VERTICAL);
+ getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
secondaryControls.setVisibility(View.GONE);
moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
@@ -507,7 +506,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void toggleFullscreen() {
- if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
+ if (DEBUG) Log.d(TAG, "toggleFullscreen() called");
if (simpleExoPlayer == null || getCurrentMetadata() == null) return;
if (popupPlayerSelected()) {
@@ -535,6 +534,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (fragmentListener == null) return;
isFullscreen = !isFullscreen;
+ setControlsWidth();
fragmentListener.onFullscreenStateChanged(isInFullscreen());
// When user presses back button in landscape mode and in fullscreen and uses ZOOM mode
// a video can be larger than screen. Prevent it like this
@@ -595,7 +595,8 @@ public class VideoPlayerImpl extends VideoPlayer
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
- hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
+ if (v.getId() == playPauseButton.getId()) hideControls(0, 0);
+ else hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
}
});
}
@@ -816,6 +817,8 @@ public class VideoPlayerImpl extends VideoPlayer
service.getLockManager().acquireWifiAndCpu();
service.resetNotification();
service.updateNotification(R.drawable.ic_pause_white);
+
+ service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
}
@Override
@@ -831,6 +834,10 @@ public class VideoPlayerImpl extends VideoPlayer
service.resetNotification();
service.updateNotification(R.drawable.ic_play_arrow_white);
+ // Remove running notification when user don't want music (or video in popup) to be played in background
+ if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected())
+ service.stopForeground(true);
+
getRootView().setKeepScreenOn(false);
service.getLockManager().releaseWifiAndCpu();
@@ -1033,6 +1040,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (queueVisible) return;
showOrHideButtons();
+ showSystemUIPartially();
super.showControlsThenHide();
}
@@ -1041,6 +1049,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (queueVisible) return;
showOrHideButtons();
+ showSystemUIPartially();
super.showControls(duration);
}
@@ -1078,12 +1087,36 @@ public class VideoPlayerImpl extends VideoPlayer
queueButton.setVisibility(View.VISIBLE);
}
+ private void showSystemUIPartially() {
+ if (isInFullscreen()) {
+ int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
+ }
+ }
+
@Override
public void hideSystemUIIfNeeded() {
if (fragmentListener != null)
fragmentListener.hideSystemUIIfNeeded();
}
+ private void setControlsWidth() {
+ Point size = new Point();
+ // This method will give a correct size of a usable area of a window.
+ // It doesn't include NavigationBar, notches, etc.
+ getRootView().getDisplay().getSize(size);
+
+ int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
+ primaryControls.getLayoutParams().width = width;
+ primaryControls.requestLayout();
+ secondaryControls.getLayoutParams().width = width;
+ secondaryControls.requestLayout();
+ getBottomControlsRoot().getLayoutParams().width = width;
+ getBottomControlsRoot().requestLayout();
+ }
+
private void updatePlaybackButtons() {
if (repeatButton == null || shuffleButton == null ||
simpleExoPlayer == null || playQueue == null) return;
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
index 85db3b201..6f4cf41de 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -18,27 +18,30 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior {
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
+ // Behavior of globalVisibleRect is different on different APIs.
+ // For example, on API 19 getGlobalVisibleRect returns a filled rect of a collapsed view while on the latest API
+ // it returns empty rect in that case. So check visibility with return value too
+ boolean visible;
+ Rect rect = new Rect();
+
// Without overriding scrolling will not work in detail_content_root_layout
ViewGroup controls = child.findViewById(R.id.detail_content_root_layout);
if (controls != null) {
- Rect rect = new Rect();
- controls.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ visible = controls.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
}
// Without overriding scrolling will not work on relatedStreamsLayout
ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout);
if (relatedStreamsLayout != null) {
- Rect rect = new Rect();
- relatedStreamsLayout.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ visible = relatedStreamsLayout.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
}
ViewGroup playQueue = child.findViewById(R.id.playQueue);
if (playQueue != null) {
- Rect rect = new Rect();
- playQueue.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY())) return false;
+ visible = playQueue.getGlobalVisibleRect(rect);
+ if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
}
return super.onInterceptTouchEvent(parent, child, event);
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 00a511ca3..db7d8d615 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
@@ -139,14 +139,6 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
} else {
playerImpl.showControlsThenHide();
}
- if (playerImpl.isInFullscreen()) {
- int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
- playerImpl.getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
- playerImpl.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
- playerImpl.getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- }
}
return true;
}
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 8d7d9b639..ff57bc2bf 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -377,12 +377,17 @@
android:visibility="gone"
tools:visibility="visible"/>
+
+
+
+
From e0639677345af36fc1c3cc1704ebbac7ccf35d17 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Fri, 3 Jan 2020 19:19:14 +0300
Subject: [PATCH 05/40] Third block of fixes for review - audio-only streams
plays the same way as video streams - fullscreen mode for tablet with
controls on the right place - hidden controls while swiping mini player down
- mini player works better
---
.../fragments/detail/VideoDetailFragment.java | 11 +++-----
.../newpipe/player/VideoPlayerImpl.java | 26 ++++++++++++++++---
2 files changed, 27 insertions(+), 10 deletions(-)
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 8813d139d..3d42942ba 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
@@ -526,12 +526,7 @@ public class VideoDetailFragment
openChannel();
break;
case R.id.detail_thumbnail_root_layout:
- if (currentInfo.getVideoStreams().isEmpty()
- && currentInfo.getVideoOnlyStreams().isEmpty()) {
- openBackgroundPlayer(false);
- } else {
- openVideoPlayer();
- }
+ openVideoPlayer();
break;
case R.id.detail_title_root_layout:
toggleTitleAndDescription();
@@ -545,6 +540,7 @@ public class VideoDetailFragment
if (player != null) {
player.onPlayPause();
player.hideControls(0,0);
+ showSystemUi();
}
else openVideoPlayer();
@@ -1869,9 +1865,10 @@ public class VideoDetailFragment
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
setOverlayElementsClickable(true);
- if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+ if (player != null && player.isInFullscreen() && player.isPlaying()) showSystemUi();
break;
case BottomSheetBehavior.STATE_DRAGGING:
+ if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
break;
case BottomSheetBehavior.STATE_SETTLING:
break;
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 76c8106be..d5bb5c86d 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -534,7 +534,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (fragmentListener == null) return;
isFullscreen = !isFullscreen;
- setControlsWidth();
+ setControlsSize();
fragmentListener.onFullscreenStateChanged(isInFullscreen());
// When user presses back button in landscape mode and in fullscreen and uses ZOOM mode
// a video can be larger than screen. Prevent it like this
@@ -1102,19 +1102,39 @@ public class VideoPlayerImpl extends VideoPlayer
fragmentListener.hideSystemUIIfNeeded();
}
- private void setControlsWidth() {
+ /*
+ * This method measures width and height of controls visible on screen. It ensures that controls will be side-by-side with
+ * NavigationBar and notches but not under them. Tablets have only bottom NavigationBar
+ * */
+ void setControlsSize() {
Point size = new Point();
+ Display display = getRootView().getDisplay();
+ if (display == null) return;
// This method will give a correct size of a usable area of a window.
// It doesn't include NavigationBar, notches, etc.
- getRootView().getDisplay().getSize(size);
+ display.getSize(size);
int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
+ int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
+
primaryControls.getLayoutParams().width = width;
+ ((LinearLayout.LayoutParams) primaryControls.getLayoutParams()).gravity = gravity;
primaryControls.requestLayout();
+
secondaryControls.getLayoutParams().width = width;
+ ((LinearLayout.LayoutParams) secondaryControls.getLayoutParams()).gravity = gravity;
secondaryControls.requestLayout();
+
getBottomControlsRoot().getLayoutParams().width = width;
+ RelativeLayout.LayoutParams bottomParams = ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
+ bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
+ bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
+ bottomParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START);
getBottomControlsRoot().requestLayout();
+
+ ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackControlRoot);
+ controlsRoot.getLayoutParams().height = isFullscreen ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
+ controlsRoot.requestLayout();
}
private void updatePlaybackButtons() {
From a2d5314cf79d14492a52bfb55e36f21751e7d3fe Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Mon, 6 Jan 2020 13:39:01 +0300
Subject: [PATCH 06/40] Fourth block of fixes for review - wrote more methods
to PlayQueue. Now it supports internal history of played items with ability
to play previous() item. Also it has equals() to check whether queues has the
same content or not - backstack in fragment is more powerful now with help of
PlayQueue's history and able to work great with playlists' PlayQueue and
SinglePlayQueue at the same time - simplified logic inside fragment. Easy to
understand. New PlayQueue will be added in backstack from only one place;
less number of setInitialData() calls - BasePlayer now able to check
PlayQueue and compare it with currently playing. And if it is the same queue
it tries to not init() it twice. It gives possibility to have a great
backstack in fragment since the same queue will not be played from two
different instances and will not be added to backstack twice with duplicated
history inside - better support of Player.STATE_IDLE - worked with layouts of
player and made them better and more universal - service will be stopped when
activity finishes by a user decision - fixed a problem related to
ChannelPlayQueue and PlaylistPlayQueue in initial start of fragment - fixed
crash in popup
---
.../newpipe/fragments/detail/StackItem.java | 6 +-
.../fragments/detail/VideoDetailFragment.java | 71 +++++++++----------
.../org/schabi/newpipe/player/BasePlayer.java | 20 +++++-
.../org/schabi/newpipe/player/MainPlayer.java | 1 +
.../newpipe/player/ServicePlayerActivity.java | 4 ++
.../newpipe/player/VideoPlayerImpl.java | 25 +++++--
.../player/event/PlayerEventListener.java | 2 +
.../newpipe/player/playqueue/PlayQueue.java | 58 ++++++++++++++-
.../activity_main_player.xml | 31 +++++---
.../main/res/layout/activity_main_player.xml | 31 +++++---
10 files changed, 184 insertions(+), 65 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
index 3fb7a7716..2e90a4fc9 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
@@ -7,7 +7,7 @@ import java.io.Serializable;
class StackItem implements Serializable {
private final int serviceId;
private String title;
- private final String url;
+ private String url;
private final PlayQueue playQueue;
StackItem(int serviceId, String url, String title, PlayQueue playQueue) {
@@ -21,6 +21,10 @@ class StackItem implements Serializable {
this.title = title;
}
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
public int getServiceId() {
return serviceId;
}
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 3d42942ba..d35e27c40 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
@@ -126,7 +126,7 @@ public class VideoDetailFragment
@State
protected PlayQueue playQueue;
@State
- int bottomSheetState = BottomSheetBehavior.STATE_HIDDEN;
+ int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
private StreamInfo currentInfo;
private Disposable currentWorker;
@@ -398,7 +398,8 @@ public class VideoDetailFragment
public void onDestroy() {
super.onDestroy();
- unbind();
+ if (!activity.isFinishing()) unbind();
+ else stopService();
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
@@ -850,26 +851,6 @@ public class VideoDetailFragment
*/
protected final LinkedList stack = new LinkedList<>();
- public void pushToStack(int serviceId, String videoUrl, String name, PlayQueue playQueue) {
- if (DEBUG) {
- Log.d(TAG, "pushToStack() called with: serviceId = ["
- + serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "], playQueue = [" + playQueue + "]");
- }
-
- if (stack.size() > 0
- && stack.peek().getServiceId() == serviceId
- && stack.peek().getUrl().equals(videoUrl)
- && stack.peek().getPlayQueue().getClass().equals(playQueue.getClass())) {
- Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = ["
- + serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
- return;
- } else {
- Log.d(TAG, "pushToStack() wasn't equal");
- }
-
- stack.push(new StackItem(serviceId, videoUrl, name, playQueue));
- }
-
public void setTitleToUrl(int serviceId, String videoUrl, String name) {
if (name != null && !name.isEmpty()) {
for (StackItem stackItem : stack) {
@@ -885,12 +866,17 @@ public class VideoDetailFragment
public boolean onBackPressed() {
if (DEBUG) Log.d(TAG, "onBackPressed() called");
+ // If we are in fullscreen mode just exit from it via first back press
if (player != null && player.isInFullscreen()) {
player.onPause();
restoreDefaultOrientation();
return true;
}
+ // If we have something in history of played items we replay it here
+ if (player != null && player.getPlayQueue().previous()) {
+ return true;
+ }
// That means that we are on the start of the stack,
// return false to let the MainActivity handle the onBack
if (stack.size() <= 1) {
@@ -928,15 +914,15 @@ public class VideoDetailFragment
}
public void selectAndLoadVideo(int serviceId, String videoUrl, String name, PlayQueue playQueue) {
- boolean streamIsTheSame = videoUrl.equals(url) && currentInfo != null;
- setInitialData(serviceId, videoUrl, name, playQueue);
-
+ boolean streamIsTheSame = this.playQueue != null && this.playQueue.equals(playQueue);
// Situation when user switches from players to main player. All needed data is here, we can start watching
if (streamIsTheSame) {
- handleResult(currentInfo);
+ //TODO not sure about usefulness of this line in the case when user switches from one player to another
+ // handleResult(currentInfo);
openVideoPlayer();
return;
}
+ setInitialData(serviceId, videoUrl, name, playQueue);
startLoading(false);
}
@@ -944,7 +930,6 @@ public class VideoDetailFragment
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = ["
+ info + "], scrollToTop = [" + scrollToTop + "]");
- setInitialData(info.getServiceId(), info.getUrl(), info.getName(), new SinglePlayQueue(info));
showLoading();
initTabs();
@@ -1390,8 +1375,6 @@ public class VideoDetailFragment
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(),
playQueue == null ? new SinglePlayQueue(info) : playQueue);
- pushToStack(serviceId, url, name, playQueue);
-
if(showRelatedStreams){
if(null == relatedStreamsLayout){ //phone
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedVideosFragment.getInstance(info));
@@ -1627,6 +1610,20 @@ public class VideoDetailFragment
// Player event listener
//////////////////////////////////////////////////////////////////////////*/
+ @Override
+ public void onQueueUpdate(PlayQueue queue) {
+ playQueue = queue;
+ // This should be the only place where we push data to stack. It will allow to have live instance of PlayQueue with actual
+ // information about deleted/added items inside Channel/Playlist queue and makes possible to have a history of played items
+ if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue))
+ stack.push(new StackItem(serviceId, url, name, playQueue));
+
+ if (DEBUG) {
+ Log.d(TAG, "onQueueUpdate() called with: serviceId = ["
+ + serviceId + "], videoUrl = [" + url + "], name = [" + name + "], playQueue = [" + playQueue + "]");
+ }
+ }
+
@Override
public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) {
setOverlayPlayPauseImage();
@@ -1647,11 +1644,6 @@ public class VideoDetailFragment
public void onProgressUpdate(int currentProgress, int duration, int bufferPercent) {
// Progress updates every second even if media is paused. It's useless until playing
if (!player.getPlayer().isPlaying() || playQueue == null) return;
-
- // Update current progress in cached playQueue because playQueue in popup and background players
- // are different instances
- playQueue.setRecovery(playQueue.getIndex(), currentProgress);
-
showPlaybackProgress(currentProgress, duration);
// We don't want to interrupt playback and don't want to see notification if player is stopped
@@ -1672,6 +1664,14 @@ public class VideoDetailFragment
@Override
public void onMetadataUpdate(StreamInfo info) {
+ if (!stack.isEmpty()) {
+ // When PlayQueue can have multiple streams (PlaylistPlayQueue or ChannelPlayQueue) every new played stream gives
+ // new title and url. StackItem contains information about first played stream. Let's update it here
+ StackItem peek = stack.peek();
+ peek.setTitle(info.getName());
+ peek.setUrl(info.getUrl());
+ }
+
if (currentInfo == info) return;
currentInfo = info;
@@ -1865,7 +1865,7 @@ public class VideoDetailFragment
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
setOverlayElementsClickable(true);
- if (player != null && player.isInFullscreen() && player.isPlaying()) showSystemUi();
+ if (player != null && player.isInFullscreen()) showSystemUi();
break;
case BottomSheetBehavior.STATE_DRAGGING:
if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
@@ -1873,7 +1873,6 @@ public class VideoDetailFragment
case BottomSheetBehavior.STATE_SETTLING:
break;
}
- Log.d(TAG, "onStateChanged: " + newState);
}
@Override public void onSlide(@NonNull View bottomSheet, float slideOffset) {
setOverlayLook(appBarLayout, behavior, slideOffset);
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 2ae822d7f..70ab82fb8 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -274,6 +274,16 @@ public abstract class BasePlayer implements
return;
}
+ boolean same = playQueue != null && playQueue.equals(queue);
+
+ // Do not re-init the same PlayQueue. Save time
+ if (same && !playQueue.isDisposed()) {
+ // Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
+ if (simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE)
+ simpleExoPlayer.retry();
+ return;
+ }
+
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
@@ -284,14 +294,17 @@ public abstract class BasePlayer implements
if (simpleExoPlayer != null
&& queue.size() == 1
&& playQueue != null
+ && playQueue.size() == 1
&& playQueue.getItem() != null
&& queue.getItem().getUrl().equals(playQueue.getItem().getUrl())
&& queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET
- ) {
+ && !same) {
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
return;
- } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) && isPlaybackResumeEnabled()) {
+ } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false)
+ && isPlaybackResumeEnabled()
+ && !same) {
final PlayQueueItem item = queue.getItem();
if (item != null && item.getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET) {
stateLoader = recordManager.loadStreamState(item)
@@ -321,7 +334,8 @@ public abstract class BasePlayer implements
}
}
// Good to go...
- initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
+ // In a case of equal PlayQueues we can re-init old one but only when it is disposed
+ initPlayback(same ? playQueue : queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true);
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 6e5082494..4e3c070a5 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -126,6 +126,7 @@ public final class MainPlayer extends Service {
if (playerImpl.getPlayer() != null) {
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
+ // We can't pause the player here because it will make transition from one stream to a new stream not smooth
playerImpl.getPlayer().stop(false);
playerImpl.setRecovery();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
index c8d564557..1c449c77e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
@@ -557,6 +557,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
// Binding Service Listener
////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void onQueueUpdate(PlayQueue queue) {
+ }
+
@Override
public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) {
onStateChanged(state);
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index d5bb5c86d..21934fb70 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -281,6 +281,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupElementsVisibility() {
if (popupPlayerSelected()) {
fullscreenButton.setVisibility(View.VISIBLE);
+ getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.GONE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE);
queueButton.setVisibility(View.GONE);
moreOptionsButton.setVisibility(View.GONE);
@@ -294,10 +295,11 @@ public class VideoPlayerImpl extends VideoPlayer
openInBrowser.setVisibility(View.GONE);
} else {
fullscreenButton.setVisibility(View.GONE);
+ getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.VISIBLE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE);
getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
- primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
+ primaryControls.getLayoutParams().width = secondaryControls.getLayoutParams().width;
secondaryControls.setVisibility(View.GONE);
moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
R.drawable.ic_expand_more_white_24dp));
@@ -500,7 +502,13 @@ public class VideoPlayerImpl extends VideoPlayer
triggerProgressUpdate();
}
- /*//////////////////////////////////////////////////////////////////////////
+ @Override
+ protected void initPlayback(@NonNull PlayQueue queue, int repeatMode, float playbackSpeed, float playbackPitch, boolean playbackSkipSilence, boolean playOnReady) {
+ super.initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, playOnReady);
+ updateQueue(queue);
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
// Player Overrides
//////////////////////////////////////////////////////////////////////////*/
@@ -1088,7 +1096,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void showSystemUIPartially() {
- if (isInFullscreen()) {
+ if (isInFullscreen() && getParentActivity() != null) {
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -1106,7 +1114,7 @@ public class VideoPlayerImpl extends VideoPlayer
* This method measures width and height of controls visible on screen. It ensures that controls will be side-by-side with
* NavigationBar and notches but not under them. Tablets have only bottom NavigationBar
* */
- void setControlsSize() {
+ private void setControlsSize() {
Point size = new Point();
Display display = getRootView().getDisplay();
if (display == null) return;
@@ -1479,6 +1487,15 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
+ private void updateQueue(PlayQueue queue) {
+ if (fragmentListener != null) {
+ fragmentListener.onQueueUpdate(queue);
+ }
+ if (activityListener != null) {
+ activityListener.onQueueUpdate(queue);
+ }
+ }
+
private void updateMetadata() {
if (fragmentListener != null && getCurrentMetadata() != null) {
fragmentListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
index 3a7b29954..37ad9798f 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
@@ -4,8 +4,10 @@ package org.schabi.newpipe.player.event;
import com.google.android.exoplayer2.PlaybackParameters;
import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.player.playqueue.PlayQueue;
public interface PlayerEventListener {
+ void onQueueUpdate(PlayQueue queue);
void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters);
void onProgressUpdate(int currentProgress, int duration, int bufferPercent);
void onMetadataUpdate(StreamInfo info);
diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
index fcb1e2819..12454bde9 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
@@ -46,17 +46,23 @@ public abstract class PlayQueue implements Serializable {
private ArrayList backup;
private ArrayList streams;
+ private ArrayList history;
@NonNull private final AtomicInteger queueIndex;
private transient BehaviorSubject eventBroadcast;
private transient Flowable broadcastReceiver;
private transient Subscription reportingReactor;
+ private transient boolean disposed;
+
PlayQueue(final int index, final List startWith) {
streams = new ArrayList<>();
streams.addAll(startWith);
+ history = new ArrayList<>();
+ history.add(streams.get(index));
queueIndex = new AtomicInteger(index);
+ disposed = false;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -88,6 +94,7 @@ public abstract class PlayQueue implements Serializable {
eventBroadcast = null;
broadcastReceiver = null;
reportingReactor = null;
+ disposed = true;
}
/**
@@ -195,6 +202,7 @@ public abstract class PlayQueue implements Serializable {
int newIndex = index;
if (index < 0) newIndex = 0;
if (index >= streams.size()) newIndex = isComplete() ? index % streams.size() : streams.size() - 1;
+ if (oldIndex != newIndex) history.add(streams.get(newIndex));
queueIndex.set(newIndex);
broadcast(new SelectEvent(oldIndex, newIndex));
@@ -267,6 +275,9 @@ public abstract class PlayQueue implements Serializable {
if (skippable) {
queueIndex.incrementAndGet();
+ if (streams.size() > queueIndex.get()) {
+ history.add(streams.get(queueIndex.get()));
+ }
} else {
removeInternal(index);
}
@@ -292,7 +303,9 @@ public abstract class PlayQueue implements Serializable {
final int backupIndex = backup.indexOf(getItem(removeIndex));
backup.remove(backupIndex);
}
- streams.remove(removeIndex);
+
+ history.remove(streams.remove(removeIndex));
+ history.add(streams.get(queueIndex.get()));
}
/**
@@ -366,6 +379,7 @@ public abstract class PlayQueue implements Serializable {
streams.add(0, streams.remove(newIndex));
}
queueIndex.set(0);
+ history.add(streams.get(0));
broadcast(new ReorderEvent(originIndex, queueIndex.get()));
}
@@ -393,10 +407,52 @@ public abstract class PlayQueue implements Serializable {
} else {
queueIndex.set(0);
}
+ history.add(streams.get(queueIndex.get()));
broadcast(new ReorderEvent(originIndex, queueIndex.get()));
}
+ /**
+ * Selects previous played item
+ *
+ * This method removes currently playing item from history and
+ * starts playing the last item from history if it exists
+ *
+ * Returns true if history is not empty and the item can be played
+ * */
+ public synchronized boolean previous() {
+ if (history.size() <= 1) return false;
+
+ history.remove(history.size() - 1);
+
+ PlayQueueItem last = history.remove(history.size() - 1);
+ setIndex(indexOf(last));
+
+ return true;
+ }
+
+ /*
+ * Compares two PlayQueues. Useful when a user switches players but queue is the same so
+ * we don't have to do anything with new queue. This method also gives a chance to track history of items in a queue in
+ * VideoDetailFragment without duplicating items from two identical queues
+ * */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof PlayQueue) || getStreams().size() != ((PlayQueue) obj).getStreams().size())
+ return false;
+
+ PlayQueue other = (PlayQueue) obj;
+ for (int i = 0; i < getStreams().size(); i++) {
+ if (!getItem(i).getUrl().equals(other.getItem(i).getUrl()))
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean isDisposed() {
+ return disposed;
+ }
/*//////////////////////////////////////////////////////////////////////////
// Rx Broadcast
//////////////////////////////////////////////////////////////////////////*/
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index ff57bc2bf..d0602ed75 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -141,17 +141,29 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
+
+
+
+
+
+ android:paddingEnd="6dp"
+ android:layout_toEndOf="@id/spaceBeforeControls"
+ android:baselineAligned="false">
@@ -274,8 +284,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
- android:paddingStart="16dp"
- android:paddingEnd="6dp"
android:visibility="invisible"
tools:ignore="RtlHardcoded"
tools:visibility="visible">
@@ -302,6 +310,8 @@
android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
+ android:lines="1"
+ android:ellipsize="end"
android:minWidth="50dp"
android:textColor="@android:color/white"
android:textStyle="bold"
@@ -365,10 +375,11 @@
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
- android:paddingEnd="8dp"
- android:paddingTop="8dp"
+ android:layout_marginTop="2dp"
+ android:layout_marginEnd="2dp"
+ android:padding="6dp"
android:layout_alignParentRight="true"
- android:background="@drawable/player_top_controls_bg"
+ android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 1022d2e95..bf9da748f 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -139,17 +139,29 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
+
+
+
+
+
+ android:paddingEnd="6dp"
+ android:layout_toEndOf="@id/spaceBeforeControls"
+ android:baselineAligned="false">
@@ -272,8 +282,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
- android:paddingStart="16dp"
- android:paddingEnd="6dp"
android:visibility="invisible"
tools:ignore="RtlHardcoded"
tools:visibility="visible">
@@ -300,6 +308,8 @@
android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
+ android:lines="1"
+ android:ellipsize="end"
android:minWidth="50dp"
android:textColor="@android:color/white"
android:textStyle="bold"
@@ -363,10 +373,11 @@
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
- android:paddingEnd="8dp"
- android:paddingTop="8dp"
+ android:layout_marginTop="2dp"
+ android:layout_marginEnd="2dp"
+ android:padding="6dp"
android:layout_alignParentRight="true"
- android:background="@drawable/player_top_controls_bg"
+ android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
From 4c57893312bf94adc25f7dd43bd577639e051035 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 8 Jan 2020 19:16:50 +0300
Subject: [PATCH 07/40] New features and fixes - added autoplay options inside
settings: always, only on wifi, never - now statusbar will be shown in
fullscreen mode - playlists, channels can be autoplayed too (if enabled) -
changed title of background activity to Play queue - fixed a crash
---
.../fragments/detail/VideoDetailFragment.java | 80 ++++++++++++-------
.../list/channel/ChannelFragment.java | 2 +-
.../list/playlist/PlaylistFragment.java | 2 +-
.../history/StatisticsPlaylistFragment.java | 2 +-
.../local/playlist/LocalPlaylistFragment.java | 2 +-
.../player/BackgroundPlayerActivity.java | 2 +-
.../newpipe/player/VideoPlayerImpl.java | 14 ++--
.../newpipe/player/helper/PlayerHelper.java | 34 ++++++++
.../org/schabi/newpipe/util/ListHelper.java | 2 +-
.../schabi/newpipe/util/NavigationHelper.java | 4 +-
.../activity_main_player.xml | 12 +--
.../main/res/layout/activity_main_player.xml | 12 +--
app/src/main/res/values/settings_keys.xml | 17 ++++
app/src/main/res/values/strings.xml | 6 ++
app/src/main/res/xml/video_audio_settings.xml | 9 +++
15 files changed, 146 insertions(+), 54 deletions(-)
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 d35e27c40..312e4d9e5 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
@@ -330,7 +330,11 @@ public class VideoDetailFragment
startService(false);
setupBroadcastReceiver();
+
settingsContentObserver = new SettingsContentObserver(new Handler(), this);
+ activity.getContentResolver().registerContentObserver(
+ android.provider.Settings.System.CONTENT_URI, true,
+ settingsContentObserver);
}
@Override
@@ -344,7 +348,6 @@ public class VideoDetailFragment
if (currentWorker != null) currentWorker.dispose();
setupBrightness(true);
- getContext().getContentResolver().unregisterContentObserver(settingsContentObserver);
PreferenceManager.getDefaultSharedPreferences(getContext())
.edit()
.putString(getString(R.string.stream_info_selected_tab_key), pageAdapter.getItemTitle(viewPager.getCurrentItem()))
@@ -356,9 +359,6 @@ public class VideoDetailFragment
super.onResume();
isFragmentStopped = false;
- getContext().getContentResolver().registerContentObserver(
- android.provider.Settings.System.CONTENT_URI, true,
- settingsContentObserver);
setupBrightness(false);
@@ -403,7 +403,8 @@ public class VideoDetailFragment
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
- getActivity().unregisterReceiver(broadcastReceiver);
+ activity.unregisterReceiver(broadcastReceiver);
+ activity.getContentResolver().unregisterContentObserver(settingsContentObserver);
if (positionSubscriber != null) positionSubscriber.dispose();
if (currentWorker != null) currentWorker.dispose();
@@ -566,7 +567,7 @@ public class VideoDetailFragment
openPopupPlayer(true);
break;
case R.id.detail_controls_download:
- NavigationHelper.openDownloads(getActivity());
+ NavigationHelper.openDownloads(activity);
break;
case R.id.overlay_thumbnail:
case R.id.overlay_metadata_layout:
@@ -874,7 +875,7 @@ public class VideoDetailFragment
}
// If we have something in history of played items we replay it here
- if (player != null && player.getPlayQueue().previous()) {
+ if (player != null && player.getPlayQueue() != null && player.getPlayQueue().previous()) {
return true;
}
// That means that we are on the start of the stack,
@@ -1126,7 +1127,7 @@ public class VideoDetailFragment
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
} catch (Exception e) {
- ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
+ ErrorActivity.reportUiError(activity, e);
}
}
}
@@ -1159,9 +1160,26 @@ public class VideoDetailFragment
}
// This method overrides default behaviour when setAutoplay() is called.
- // Don't auto play if the user selected an external player
+ // Don't auto play if the user selected an external player or disabled it in settings
private boolean isAutoplayEnabled() {
- return playQueue != null && playQueue.getStreams().size() != 0 && !isExternalPlayerEnabled() && autoPlayEnabled;
+ return playQueue != null && playQueue.getStreams().size() != 0
+ && autoPlayEnabled
+ && !isExternalPlayerEnabled()
+ && isAutoplayAllowedByUser();
+ }
+
+ private boolean isAutoplayAllowedByUser () {
+ if (activity == null) return false;
+
+ switch (PlayerHelper.getAutoplayType(activity)) {
+ case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER:
+ return false;
+ case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI:
+ return !ListHelper.isMeteredNetwork(activity);
+ case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS:
+ default:
+ return true;
+ }
}
private void addVideoPlayerView() {
@@ -1275,7 +1293,7 @@ public class VideoDetailFragment
}
};
IntentFilter intentFilter = new IntentFilter(ACTION_SHOW_MAIN_PLAYER);
- getActivity().registerReceiver(broadcastReceiver, intentFilter);
+ activity.registerReceiver(broadcastReceiver, intentFilter);
}
@@ -1287,23 +1305,23 @@ public class VideoDetailFragment
// 1: Screen orientation changes using accelerometer
// 0: Screen orientation is locked
return !(android.provider.Settings.System.getInt(
- getContext().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
+ activity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
}
private void restoreDefaultOrientation() {
- if (player == null || !player.videoPlayerSelected()) return;
+ if (player == null || !player.videoPlayerSelected() || activity == null) return;
if (player != null && player.isInFullscreen()) player.toggleFullscreen();
// This will show systemUI and pause the player.
// User can tap on Play button and video will be in fullscreen mode again
if (globalScreenOrientationLocked()) removeVideoPlayerView();
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
private void setupOrientation() {
- if (player == null || !player.videoPlayerSelected()) return;
+ if (player == null || !player.videoPlayerSelected() || activity == null) return;
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
int newOrientation;
if (globalScreenOrientationLocked()) {
boolean lastOrientationWasLandscape
@@ -1314,14 +1332,14 @@ public class VideoDetailFragment
} else
newOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- if (newOrientation != getActivity().getRequestedOrientation())
- getActivity().setRequestedOrientation(newOrientation);
+ if (newOrientation != activity.getRequestedOrientation())
+ activity.setRequestedOrientation(newOrientation);
}
@Override
public void onSettingsChanged() {
- if(!globalScreenOrientationLocked())
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ if(activity != null && !globalScreenOrientationLocked())
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1515,7 +1533,7 @@ public class VideoDetailFragment
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
- downloadDialog.show(getActivity().getSupportFragmentManager(), "downloadDialog");
+ downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
ServiceList.all()
@@ -1525,10 +1543,10 @@ public class VideoDetailFragment
.getName(), "",
R.string.could_not_setup_download_menu);
- ErrorActivity.reportError(getActivity(),
+ ErrorActivity.reportError(activity,
e,
- getActivity().getClass(),
- getActivity().findViewById(android.R.id.content), info);
+ activity.getClass(),
+ activity.findViewById(android.R.id.content), info);
}
}
@@ -1744,13 +1762,17 @@ public class VideoDetailFragment
private void showSystemUi() {
if (DEBUG) Log.d(TAG, "showSystemUi() called");
- getActivity().getWindow().getDecorView().setSystemUiVisibility(0);
- getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+ if (activity == null) return;
+
+ activity.getWindow().getDecorView().setSystemUiVisibility(0);
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
private void hideSystemUi() {
if (DEBUG) Log.d(TAG, "hideSystemUi() called");
+ if (activity == null) return;
+
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -1769,7 +1791,9 @@ public class VideoDetailFragment
}
private void setupBrightness(boolean save) {
- WindowManager.LayoutParams lp = getActivity().getWindow().getAttributes();
+ if (activity == null) return;
+
+ WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
float brightnessLevel;
if (save) {
@@ -1787,7 +1811,7 @@ public class VideoDetailFragment
lp.screenBrightness = brightnessLevel;
}
- getActivity().getWindow().setAttributes(lp);
+ activity.getWindow().setAttributes(lp);
}
private void checkLandscape() {
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 c20ff0fc2..832e2ff9b 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
@@ -389,7 +389,7 @@ public class ChannelFragment extends BaseListInfoFragment {
monitorSubscription(result);
headerPlayAllButton.setOnClickListener(
- view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
+ view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true));
headerPopupButton.setOnClickListener(
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(
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 6941741af..f3f14f746 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
@@ -295,7 +295,7 @@ public class PlaylistFragment extends BaseListInfoFragment {
.subscribe(getPlaylistBookmarkSubscriber());
headerPlayAllButton.setOnClickListener(view ->
- NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
+ NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true));
headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view ->
diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
index 31ae70954..e40549b88 100644
--- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
@@ -310,7 +310,7 @@ public class StatisticsPlaylistFragment
}
headerPlayAllButton.setOnClickListener(view ->
- NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
+ NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true));
headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view ->
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 9e72838ad..33f98614c 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
@@ -319,7 +319,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment
- NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
+ NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true));
headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view ->
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
index bf3e202d2..5078a01b8 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
@@ -21,7 +21,7 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity {
@Override
public String getSupportActionTitle() {
- return getResources().getString(R.string.title_activity_background_player);
+ return getResources().getString(R.string.title_activity_play_queue);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 21934fb70..39a16afae 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -689,9 +689,6 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void onPlaybackSpeedClicked() {
if (videoPlayerSelected()) {
- // It hides status bar in fullscreen mode
- hideSystemUIIfNeeded();
-
PlaybackParameterDialog
.newInstance(getPlaybackSpeed(), getPlaybackPitch(), getPlaybackSkipSilence(), this)
.show(getParentActivity().getSupportFragmentManager(), null);
@@ -1097,9 +1094,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void showSystemUIPartially() {
if (isInFullscreen() && getParentActivity() != null) {
- int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
}
}
@@ -1143,6 +1138,13 @@ public class VideoPlayerImpl extends VideoPlayer
ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackControlRoot);
controlsRoot.getLayoutParams().height = isFullscreen ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
+
+ int statusBarHeight = 0;
+ int resourceId = service.getResources().getIdentifier("status_bar_height_landscape", "dimen", "android");
+ if (resourceId > 0) statusBarHeight = service.getResources().getDimensionPixelSize(resourceId);
+
+ getRootView().findViewById(R.id.playbackWindowRoot).setPadding(0, isFullscreen ? statusBarHeight : 0, 0, 0);
+ getRootView().findViewById(R.id.playbackWindowRoot).requestLayout();
}
private void updatePlaybackButtons() {
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 5ca02980d..80f38afb2 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -44,6 +44,9 @@ import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MOD
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS;
+import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI;
+import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
@@ -64,6 +67,15 @@ public class PlayerHelper {
int MINIMIZE_ON_EXIT_MODE_BACKGROUND = 1;
int MINIMIZE_ON_EXIT_MODE_POPUP = 2;
}
+
+ @Retention(SOURCE)
+ @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI,
+ AUTOPLAY_TYPE_NEVER})
+ public @interface AutoplayType {
+ int AUTOPLAY_TYPE_ALWAYS = 0;
+ int AUTOPLAY_TYPE_WIFI = 1;
+ int AUTOPLAY_TYPE_NEVER = 2;
+ }
////////////////////////////////////////////////////////////////////////////
// Exposed helpers
////////////////////////////////////////////////////////////////////////////
@@ -202,6 +214,22 @@ public class PlayerHelper {
}
}
+ @AutoplayType
+ public static int getAutoplayType(@NonNull final Context context) {
+ final String defaultType = context.getString(R.string.autoplay_always_key);
+ final String wifi = context.getString(R.string.autoplay_wifi_key);
+ final String never = context.getString(R.string.autoplay_never_key);
+
+ final String type = getAutoplayType(context, defaultType);
+ if (type.equals(wifi)) {
+ return AUTOPLAY_TYPE_WIFI;
+ } else if (type.equals(never)) {
+ return AUTOPLAY_TYPE_NEVER;
+ } else {
+ return AUTOPLAY_TYPE_ALWAYS;
+ }
+ }
+
@NonNull
public static SeekParameters getSeekParameters(@NonNull final Context context) {
return isUsingInexactSeek(context) ?
@@ -351,6 +379,12 @@ public class PlayerHelper {
key);
}
+ private static String getAutoplayType(@NonNull final Context context,
+ final String key) {
+ return getPreferences(context).getString(context.getString(R.string.autoplay_key),
+ key);
+ }
+
private static SinglePlayQueue getAutoQueuedSinglePlayQueue(StreamInfoItem streamInfoItem) {
SinglePlayQueue singlePlayQueue = new SinglePlayQueue(streamInfoItem);
singlePlayQueue.getItem().setAutoQueued(true);
diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java
index eb950b1ed..d878a2b87 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java
@@ -445,7 +445,7 @@ public final class ListHelper {
* @param context App context
* @return {@code true} if connected to a metered network
*/
- private static boolean isMeteredNetwork(Context context)
+ public static boolean isMeteredNetwork(Context context)
{
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (manager == null || manager.getActiveNetworkInfo() == null) return false;
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 648894cc1..8b867a328 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -116,8 +116,8 @@ public class NavigationHelper {
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
}
- public static void playOnMainPlayer(final AppCompatActivity activity, final PlayQueue queue, final boolean resumePlayback) {
- playOnMainPlayer(activity.getSupportFragmentManager(), queue, resumePlayback);
+ public static void playOnMainPlayer(final AppCompatActivity activity, final PlayQueue queue, final boolean autoPlay) {
+ playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay);
}
public static void playOnMainPlayer(final FragmentManager fragmentManager, final PlayQueue queue, boolean autoPlay) {
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index d0602ed75..7fb1872cf 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -134,6 +134,12 @@
android:visibility="gone"
tools:visibility="visible">
+
+
-
-
+
+
-
-
@string/minimize_on_exit_popup_description
+
+ autoplay_key
+ @string/autoplay_always_key
+ autoplay_always_key
+ autoplay_wifi_key
+ autoplay_never_key
+
+ - @string/autoplay_always_key
+ - @string/autoplay_wifi_key
+ - @string/autoplay_never_key
+
+
+ - @string/autoplay_always_description
+ - @string/autoplay_wifi_description
+ - @string/autoplay_never_description
+
+
default_resolution
360p
show_higher_resolutions
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 19013322d..3a575ae25 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -411,6 +411,7 @@
Conferences
%1$s/%2$s
+ Play queue
Background player
Popup player
Remove
@@ -520,6 +521,11 @@
None
Minimize to background player
Minimize to popup player
+
+ Start playback automatically — %s
+ Always
+ Only on WiFi
+ Never
List view mode
List
Grid
diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml
index 0ff43ce90..447fa9018 100644
--- a/app/src/main/res/xml/video_audio_settings.xml
+++ b/app/src/main/res/xml/video_audio_settings.xml
@@ -105,6 +105,15 @@
android:summary="@string/minimize_on_exit_summary"
android:title="@string/minimize_on_exit_title"/>
+
+
Date: Thu, 9 Jan 2020 18:28:06 +0300
Subject: [PATCH 08/40] Autoplay enhancement and new button at the top left
corner - added a video close button to the top left corner - autoplay will
not work if stream plays in background or popup players
---
app/src/main/AndroidManifest.xml | 4 +-
.../fragments/detail/VideoDetailFragment.java | 37 +++++++++++++------
.../newpipe/player/VideoPlayerImpl.java | 9 +++++
.../activity_main_player.xml | 14 +++++++
.../main/res/layout/activity_main_player.xml | 14 +++++++
5 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0f8339f81..18e424524 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -38,9 +38,9 @@
-
+
{
if (l != ol || t != ot || r != or || b != ob) {
@@ -555,9 +560,11 @@ public class VideoPlayerImpl extends VideoPlayer
if (!isInFullscreen()) {
titleTextView.setVisibility(View.GONE);
channelTextView.setVisibility(View.GONE);
+ playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
} else {
titleTextView.setVisibility(View.VISIBLE);
channelTextView.setVisibility(View.VISIBLE);
+ playerCloseButton.setVisibility(View.GONE);
}
}
@@ -597,6 +604,8 @@ public class VideoPlayerImpl extends VideoPlayer
} else if (v.getId() == fullscreenButton.getId()) {
toggleFullscreen();
+ } else if (v.getId() == playerCloseButton.getId()) {
+ service.sendBroadcast(new Intent(VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER));
}
if (getCurrentState() != STATE_COMPLETED) {
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 7fb1872cf..df0ada0b0 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -175,6 +175,20 @@
android:paddingLeft="2dp"
tools:ignore="RtlHardcoded">
+
+
+
+
Date: Thu, 9 Jan 2020 19:27:10 +0300
Subject: [PATCH 09/40] Changed default autoplay type to "Only on WiFi"
---
.../org/schabi/newpipe/player/helper/PlayerHelper.java | 10 +++++-----
app/src/main/res/values/settings_keys.xml | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 80f38afb2..a0152c13a 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -216,17 +216,17 @@ public class PlayerHelper {
@AutoplayType
public static int getAutoplayType(@NonNull final Context context) {
- final String defaultType = context.getString(R.string.autoplay_always_key);
- final String wifi = context.getString(R.string.autoplay_wifi_key);
+ final String defaultType = context.getString(R.string.autoplay_wifi_key);
+ final String always = context.getString(R.string.autoplay_always_key);
final String never = context.getString(R.string.autoplay_never_key);
final String type = getAutoplayType(context, defaultType);
- if (type.equals(wifi)) {
- return AUTOPLAY_TYPE_WIFI;
+ if (type.equals(always)) {
+ return AUTOPLAY_TYPE_ALWAYS;
} else if (type.equals(never)) {
return AUTOPLAY_TYPE_NEVER;
} else {
- return AUTOPLAY_TYPE_ALWAYS;
+ return AUTOPLAY_TYPE_WIFI;
}
}
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index ed540ef41..404397450 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -65,7 +65,7 @@
autoplay_key
- @string/autoplay_always_key
+ @string/autoplay_wifi_key
autoplay_always_key
autoplay_wifi_key
autoplay_never_key
From 421b8214cb2d630f179d0786f9a7e2090fa9d10f Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Fri, 10 Jan 2020 17:32:05 +0300
Subject: [PATCH 10/40] Fixes of VideoDetailFragment
---
.../fragments/detail/VideoDetailFragment.java | 24 ++++++++++---------
.../newpipe/player/VideoPlayerImpl.java | 13 +++++-----
2 files changed, 19 insertions(+), 18 deletions(-)
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 b958a5a3f..22db9fadc 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
@@ -874,7 +874,7 @@ public class VideoDetailFragment
}
StackItem currentPeek = stack.peek();
- if (currentPeek != null && currentPeek.getPlayQueue() != playQueue) {
+ if (currentPeek != null && !currentPeek.getPlayQueue().equals(playQueue)) {
// When user selected a stream but didn't start playback this stream will not be added to backStack.
// Then he press Back and the last saved item from history will show up
setupFromHistoryItem(currentPeek);
@@ -1091,8 +1091,7 @@ public class VideoDetailFragment
startService(true);
return;
}
- if (currentInfo == null || playQueue == null)
- return;
+ if (currentInfo == null) return;
PlayQueue queue = setupPlayQueueForIntent(false);
@@ -1120,7 +1119,6 @@ public class VideoDetailFragment
// Size can be 0 because queue removes bad stream automatically when error occurs
if (playQueue == null || playQueue.size() == 0)
queue = new SinglePlayQueue(currentInfo);
- this.playQueue = queue;
return queue;
}
@@ -1404,8 +1402,7 @@ public class VideoDetailFragment
super.handleResult(info);
currentInfo = info;
- setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(),
- playQueue == null ? new SinglePlayQueue(info) : playQueue);
+ setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(), playQueue);
if(showRelatedStreams){
if(null == relatedStreamsLayout){ //phone
@@ -1665,8 +1662,13 @@ public class VideoDetailFragment
restoreDefaultOrientation();
break;
case BasePlayer.STATE_PLAYING:
- if (positionView.getAlpha() != 1f) animateView(positionView, true, 100);
- if (detailPositionView.getAlpha() != 1f) animateView(detailPositionView, true, 100);
+ if (positionView.getAlpha() != 1f
+ && player.getPlayQueue() != null
+ && player.getPlayQueue().getItem() != null
+ && player.getPlayQueue().getItem().getUrl().equals(url)) {
+ animateView(positionView, true, 100);
+ animateView(detailPositionView, true, 100);
+ }
setupOrientation();
break;
}
@@ -1677,7 +1679,8 @@ public class VideoDetailFragment
// Progress updates every second even if media is paused. It's useless until playing
if (!player.getPlayer().isPlaying() || playQueue == null) return;
- if (playQueue == player.getPlayQueue()) showPlaybackProgress(currentProgress, duration);
+ if (player.getPlayQueue().getItem().getUrl().equals(url))
+ showPlaybackProgress(currentProgress, duration);
// We don't want to interrupt playback and don't want to see notification if player is stopped
// since next lines of code will enable background playback if needed
@@ -1907,9 +1910,8 @@ public class VideoDetailFragment
if (player != null && player.isInFullscreen()) showSystemUi();
break;
case BottomSheetBehavior.STATE_DRAGGING:
- if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
- break;
case BottomSheetBehavior.STATE_SETTLING:
+ if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
break;
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index c773d719e..3bf734458 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -510,7 +510,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
protected void initPlayback(@NonNull PlayQueue queue, int repeatMode, float playbackSpeed, float playbackPitch, boolean playbackSkipSilence, boolean playOnReady) {
super.initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, playOnReady);
- updateQueue(queue);
+ updateQueue();
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1474,7 +1474,6 @@ public class VideoPlayerImpl extends VideoPlayer
public void setFragmentListener(PlayerServiceEventListener listener) {
fragmentListener = listener;
- updateMetadata();
updatePlayback();
triggerProgressUpdate();
}
@@ -1498,12 +1497,12 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
- private void updateQueue(PlayQueue queue) {
- if (fragmentListener != null) {
- fragmentListener.onQueueUpdate(queue);
+ private void updateQueue() {
+ if (fragmentListener != null && playQueue != null) {
+ fragmentListener.onQueueUpdate(playQueue);
}
- if (activityListener != null) {
- activityListener.onQueueUpdate(queue);
+ if (activityListener != null && playQueue != null) {
+ activityListener.onQueueUpdate(playQueue);
}
}
From 0c394b123c5dd2c480d4315c66d91d5a6faa3e6a Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Mon, 13 Jan 2020 19:24:28 +0300
Subject: [PATCH 11/40] Another fix of VideoDetailFragment
---
.../fragments/detail/VideoDetailFragment.java | 22 ++++++-------------
1 file changed, 7 insertions(+), 15 deletions(-)
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 22db9fadc..b1929891b 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
@@ -112,7 +112,6 @@ public class VideoDetailFragment
public static final String ACTION_SHOW_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
public static final String ACTION_HIDE_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
- private boolean autoPlayEnabled;
private boolean showRelatedStreams;
private boolean showComments;
private String selectedTabTag;
@@ -127,6 +126,8 @@ public class VideoDetailFragment
protected PlayQueue playQueue;
@State
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
+ @State
+ protected boolean autoPlayEnabled = true;
private StreamInfo currentInfo;
private Disposable currentWorker;
@@ -311,9 +312,6 @@ public class VideoDetailFragment
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
- // Let's play all streams automatically
- setAutoplay(true);
-
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(activity)
@@ -870,6 +868,7 @@ public class VideoDetailFragment
if (player != null && player.isInFullscreen()) {
player.onPause();
restoreDefaultOrientation();
+ setAutoplay(false);
return true;
}
@@ -1169,8 +1168,7 @@ public class VideoDetailFragment
// This method overrides default behaviour when setAutoplay() is called.
// Don't auto play if the user selected an external player or disabled it in settings
private boolean isAutoplayEnabled() {
- return playQueue != null && playQueue.getStreams().size() != 0
- && autoPlayEnabled
+ return autoPlayEnabled
&& !isExternalPlayerEnabled()
&& (player == null || player.videoPlayerSelected())
&& isAutoplayAllowedByUser();
@@ -1326,22 +1324,16 @@ public class VideoDetailFragment
if (player != null && player.isInFullscreen()) player.toggleFullscreen();
// This will show systemUI and pause the player.
// User can tap on Play button and video will be in fullscreen mode again
- if (globalScreenOrientationLocked()) removeVideoPlayerView();
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
private void setupOrientation() {
if (player == null || !player.videoPlayerSelected() || activity == null) return;
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
int newOrientation;
- if (globalScreenOrientationLocked()) {
- boolean lastOrientationWasLandscape
- = sharedPreferences.getBoolean(getString(R.string.last_orientation_landscape_key), false);
- newOrientation = lastOrientationWasLandscape
- ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
- : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
- } else
+ if (globalScreenOrientationLocked())
+ newOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+ else
newOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
if (newOrientation != activity.getRequestedOrientation())
From d1609cba90006e18a0dde03f09d133aedf73e985 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 15 Jan 2020 21:32:29 +0300
Subject: [PATCH 12/40] Enhancements to background playback and media button
handling
---
.../fragments/detail/VideoDetailFragment.java | 14 +++++-----
.../org/schabi/newpipe/player/MainPlayer.java | 26 ++++++++++++++++---
.../newpipe/player/VideoPlayerImpl.java | 10 +++----
3 files changed, 32 insertions(+), 18 deletions(-)
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 b1929891b..5493d05cc 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
@@ -21,6 +21,7 @@ import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.tabs.TabLayout;
@@ -237,9 +238,8 @@ public class VideoDetailFragment
if (!player.videoPlayerSelected()) return;
- if (currentInfo == null && !wasCleared()) selectAndLoadVideo(serviceId, url, name, playQueue);
-
- if (player.getPlayQueue() != null) addVideoPlayerView();
+ // STATE_IDLE means the player is stopped
+ if (player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE) addVideoPlayerView();
// If the video is playing but orientation changed let's make the video in fullscreen again
@@ -375,7 +375,7 @@ public class VideoDetailFragment
updateFlags = 0;
}
- // Check if it was loading when the fragment was stopped/paused,
+ // Check if it was loading when the fragment was stopped/paused
if (wasLoading.getAndSet(false) && !wasCleared()) {
selectAndLoadVideo(serviceId, url, name, playQueue);
} else if (currentInfo != null) {
@@ -537,7 +537,7 @@ public class VideoDetailFragment
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
break;
case R.id.overlay_play_pause_button:
- if (player != null) {
+ if (player != null && player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE) {
player.onPlayPause();
player.hideControls(0,0);
showSystemUi();
@@ -900,9 +900,9 @@ public class VideoDetailFragment
}
private void setupFromHistoryItem(StackItem item) {
+ setAutoplay(false);
hideMainPlayer();
- setAutoplay(false);
selectAndLoadVideo(
item.getServiceId(),
item.getUrl(),
@@ -1107,7 +1107,7 @@ public class VideoDetailFragment
return;
removeVideoPlayerView();
- playerService.stop();
+ playerService.stop(isAutoplayEnabled());
playerService.getView().setVisibility(View.GONE);
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 4e3c070a5..b7081f71e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -113,6 +113,10 @@ public final class MainPlayer extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (DEBUG) Log.d(TAG, "onStartCommand() called with: intent = [" + intent +
"], flags = [" + flags + "], startId = [" + startId + "]");
+
+ if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) || intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) != null)
+ showNotificationAndStartForeground();
+
playerImpl.handleIntent(intent);
if (playerImpl.mediaSessionManager != null) {
playerImpl.mediaSessionManager.handleMediaButtonIntent(intent);
@@ -120,15 +124,20 @@ public final class MainPlayer extends Service {
return START_NOT_STICKY;
}
- public void stop() {
- if (DEBUG)
- Log.d(TAG, "stop() called");
+ public void stop(boolean autoplayEnabled) {
+ if (DEBUG) Log.d(TAG, "stop() called");
if (playerImpl.getPlayer() != null) {
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
- // We can't pause the player here because it will make transition from one stream to a new stream not smooth
+ // Releases wifi & cpu, disables keepScreenOn, etc.
+ if (!autoplayEnabled) playerImpl.onPause();
+ // We can't just pause the player here because it will make transition from one stream to a new stream not smooth
playerImpl.getPlayer().stop(false);
playerImpl.setRecovery();
+ // Notification shows information about old stream but if a user selects a stream from backStack it's not actual anymore
+ // So we should hide the notification at all.
+ // When autoplay enabled such notification flashing is annoying so skip this case
+ if (!autoplayEnabled) stopForeground(true);
}
}
@@ -211,6 +220,15 @@ public final class MainPlayer extends Service {
}
}
+ private void showNotificationAndStartForeground() {
+ resetNotification();
+ if (getBigNotRemoteView() != null)
+ getBigNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
+ if (getNotRemoteView() != null)
+ getNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
+ startForeground(NOTIFICATION_ID, getNotBuilder().build());
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Notification
//////////////////////////////////////////////////////////////////////////*/
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 3bf734458..2da110712 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -189,12 +189,6 @@ public class VideoPlayerImpl extends VideoPlayer
reload();
}
- service.resetNotification();
- if (service.getBigNotRemoteView() != null)
- service.getBigNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
- if (service.getNotRemoteView() != null)
- service.getNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
- service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
setupElementsVisibility();
if (audioPlayerSelected()) {
@@ -1167,7 +1161,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void checkLandscape() {
AppCompatActivity parent = getParentActivity();
if (parent != null && service.isLandscape() != isInFullscreen()
- && getCurrentState() != STATE_COMPLETED && videoPlayerSelected())
+ && getCurrentState() != STATE_COMPLETED && videoPlayerSelected() && !audioOnly)
toggleFullscreen();
}
@@ -1197,6 +1191,8 @@ public class VideoPlayerImpl extends VideoPlayer
return;
audioOnly = !video;
+ // When a user returns from background controls could be hidden but systemUI will be shown 100%. Hide it
+ if (!audioOnly && !isControlsVisible()) hideSystemUIIfNeeded();
setRecovery();
reload();
}
From 92ff98d99ac0146db45c5f2cd1b4ebc7716c07a6 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Thu, 16 Jan 2020 14:20:22 +0300
Subject: [PATCH 13/40] New logic for handling global orientation - added a
button to manually change an orientation of a video - adapted UI for an
automatic global orientation too
---
.../fragments/detail/VideoDetailFragment.java | 61 ++++++++-----------
.../org/schabi/newpipe/player/MainPlayer.java | 2 +-
.../newpipe/player/VideoPlayerImpl.java | 41 ++++++++++++-
.../event/PlayerServiceEventListener.java | 2 +
.../newpipe/player/helper/PlayerHelper.java | 8 +++
.../settings/SettingsContentObserver.java | 29 ---------
.../activity_main_player.xml | 15 +++++
.../main/res/layout/activity_main_player.xml | 15 +++++
8 files changed, 106 insertions(+), 67 deletions(-)
delete mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java
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 5493d05cc..cfaf0aea4 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
@@ -4,6 +4,7 @@ import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.*;
import android.content.pm.ActivityInfo;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.os.Build;
@@ -69,7 +70,6 @@ import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.*;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.settings.SettingsContentObserver;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.views.AnimatedProgressBar;
@@ -97,8 +97,7 @@ public class VideoDetailFragment
View.OnClickListener,
View.OnLongClickListener,
PlayerEventListener,
- PlayerServiceEventListener,
- SettingsContentObserver.OnChangeListener {
+ PlayerServiceEventListener {
public static final String AUTO_PLAY = "auto_play";
private boolean isFragmentStopped;
@@ -203,7 +202,7 @@ public class VideoDetailFragment
private TabLayout tabLayout;
private FrameLayout relatedStreamsLayout;
- private SettingsContentObserver settingsContentObserver;
+ private ContentObserver settingsContentObserver;
private ServiceConnection serviceConnection;
private boolean bounded;
private MainPlayer playerService;
@@ -329,9 +328,15 @@ public class VideoDetailFragment
startService(false);
setupBroadcastReceiver();
- settingsContentObserver = new SettingsContentObserver(new Handler(), this);
+ settingsContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if(activity != null && !PlayerHelper.globalScreenOrientationLocked(activity))
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+ };
activity.getContentResolver().registerContentObserver(
- android.provider.Settings.System.CONTENT_URI, true,
+ Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
settingsContentObserver);
}
@@ -1311,13 +1316,6 @@ public class VideoDetailFragment
// Orientation listener
//////////////////////////////////////////////////////////////////////////*/
- private boolean globalScreenOrientationLocked() {
- // 1: Screen orientation changes using accelerometer
- // 0: Screen orientation is locked
- return !(android.provider.Settings.System.getInt(
- activity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
- }
-
private void restoreDefaultOrientation() {
if (player == null || !player.videoPlayerSelected() || activity == null) return;
@@ -1327,25 +1325,6 @@ public class VideoDetailFragment
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
- private void setupOrientation() {
- if (player == null || !player.videoPlayerSelected() || activity == null) return;
-
- int newOrientation;
- if (globalScreenOrientationLocked())
- newOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
- else
- newOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
- if (newOrientation != activity.getRequestedOrientation())
- activity.setRequestedOrientation(newOrientation);
- }
-
- @Override
- public void onSettingsChanged() {
- if(activity != null && !globalScreenOrientationLocked())
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@@ -1661,7 +1640,6 @@ public class VideoDetailFragment
animateView(positionView, true, 100);
animateView(detailPositionView, true, 100);
}
- setupOrientation();
break;
}
}
@@ -1742,6 +1720,15 @@ public class VideoDetailFragment
addVideoPlayerView();
}
+ @Override
+ public void onScreenRotationButtonClicked() {
+ int newOrientation = isLandscape() ?
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ : ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+
+ activity.setRequestedOrientation(newOrientation);
+ }
+
/*
* Will scroll down to description view after long click on moreOptionsButton
* */
@@ -1828,9 +1815,15 @@ public class VideoDetailFragment
if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null)
setAutoplay(true);
+ boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
// Let's give a user time to look at video information page if video is not playing
- if (player.isPlaying())
+ if (player.isPlaying()) {
player.checkLandscape();
+ } else if (orientationLocked) {
+ player.checkLandscape();
+ player.onPlay();
+ player.showControlsThenHide();
+ }
}
private boolean isLandscape() {
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index b7081f71e..63c2f195f 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -195,7 +195,7 @@ public final class MainPlayer extends Service {
boolean isLandscape() {
// DisplayMetrics from activity context knows about MultiWindow feature while DisplayMetrics from app context doesn't
- final DisplayMetrics metrics = playerImpl.getParentActivity() != null ?
+ final DisplayMetrics metrics = playerImpl != null && playerImpl.getParentActivity() != null ?
playerImpl.getParentActivity().getResources().getDisplayMetrics()
: getResources().getDisplayMetrics();
return metrics.heightPixels < metrics.widthPixels;
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 2da110712..66407936c 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -23,12 +23,15 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.content.*;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -88,7 +91,7 @@ import static org.schabi.newpipe.util.ListHelper.getResolutionIndex;
public class VideoPlayerImpl extends VideoPlayer
implements View.OnLayoutChangeListener,
PlaybackParameterDialog.Callback,
- View.OnLongClickListener{
+ View.OnLongClickListener {
private static final String TAG = ".VideoPlayerImpl";
private final float MAX_GESTURE_LENGTH = 0.75f;
@@ -109,6 +112,7 @@ public class VideoPlayerImpl extends VideoPlayer
private ImageButton openInBrowser;
private ImageButton fullscreenButton;
private ImageButton playerCloseButton;
+ private ImageButton screenRotationButton;
private ImageButton playPauseButton;
private ImageButton playPreviousButton;
@@ -139,6 +143,7 @@ public class VideoPlayerImpl extends VideoPlayer
private PlayerEventListener activityListener;
private GestureDetector gestureDetector;
private SharedPreferences defaultPreferences;
+ private ContentObserver settingsContentObserver;
@NonNull
final private AudioPlaybackResolver resolver;
@@ -233,6 +238,7 @@ public class VideoPlayerImpl extends VideoPlayer
this.playWithKodi = rootView.findViewById(R.id.playWithKodi);
this.openInBrowser = rootView.findViewById(R.id.openInBrowser);
this.fullscreenButton = rootView.findViewById(R.id.fullScreenButton);
+ this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
this.playerCloseButton = rootView.findViewById(R.id.playerCloseButton);
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
@@ -277,6 +283,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupElementsVisibility() {
if (popupPlayerSelected()) {
fullscreenButton.setVisibility(View.VISIBLE);
+ screenRotationButton.setVisibility(View.GONE);
getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.GONE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE);
queueButton.setVisibility(View.GONE);
@@ -292,6 +299,7 @@ public class VideoPlayerImpl extends VideoPlayer
playerCloseButton.setVisibility(View.GONE);
} else {
fullscreenButton.setVisibility(View.GONE);
+ setupScreenRotationButton(service.isLandscape());
getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.VISIBLE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE);
@@ -337,10 +345,19 @@ public class VideoPlayerImpl extends VideoPlayer
moreOptionsButton.setOnLongClickListener(this);
shareButton.setOnClickListener(this);
fullscreenButton.setOnClickListener(this);
+ screenRotationButton.setOnClickListener(this);
playWithKodi.setOnClickListener(this);
openInBrowser.setOnClickListener(this);
playerCloseButton.setOnClickListener(this);
+ settingsContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) { setupScreenRotationButton(service.isLandscape()); }
+ };
+ service.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
+ settingsContentObserver);
+
getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
if (l != ol || t != ot || r != or || b != ob) {
// Use smaller value to be consistent between screen orientations
@@ -560,6 +577,7 @@ public class VideoPlayerImpl extends VideoPlayer
channelTextView.setVisibility(View.VISIBLE);
playerCloseButton.setVisibility(View.GONE);
}
+ setupScreenRotationButton(isInFullscreen());
}
@Override
@@ -598,6 +616,9 @@ public class VideoPlayerImpl extends VideoPlayer
} else if (v.getId() == fullscreenButton.getId()) {
toggleFullscreen();
+ } else if (v.getId() == screenRotationButton.getId()) {
+ fragmentListener.onScreenRotationButtonClicked();
+
} else if (v.getId() == playerCloseButton.getId()) {
service.sendBroadcast(new Intent(VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER));
}
@@ -689,6 +710,13 @@ public class VideoPlayerImpl extends VideoPlayer
builder.create().show();
}
+ private void setupScreenRotationButton(boolean landscape) {
+ boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
+ screenRotationButton.setVisibility(orientationLocked && videoPlayerSelected() ? View.VISIBLE : View.GONE);
+ screenRotationButton.setImageDrawable(service.getResources().getDrawable(
+ landscape ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
+ }
+
@Override
public void onPlaybackSpeedClicked() {
if (videoPlayerSelected()) {
@@ -880,6 +908,13 @@ public class VideoPlayerImpl extends VideoPlayer
super.onCompleted();
}
+ @Override
+ public void destroy() {
+ super.destroy();
+
+ service.getContentResolver().unregisterContentObserver(settingsContentObserver);
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Broadcast Receiver
//////////////////////////////////////////////////////////////////////////*/
@@ -1282,7 +1317,7 @@ public class VideoPlayerImpl extends VideoPlayer
getLoadingPanel().setMinimumHeight(popupLayoutParams.height);
service.removeViewFromParent();
- windowManager.addView(service.getView(), popupLayoutParams);
+ windowManager.addView(getRootView(), popupLayoutParams);
if (getAspectRatioFrameLayout().getResizeMode() == AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
onResizeClicked();
@@ -1313,7 +1348,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void initVideoPlayer() {
- service.getView().setLayoutParams(new FrameLayout.LayoutParams(
+ getRootView().setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
index 7422f9442..eeff08b5c 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
@@ -5,6 +5,8 @@ import com.google.android.exoplayer2.ExoPlaybackException;
public interface PlayerServiceEventListener extends PlayerEventListener {
void onFullscreenStateChanged(boolean fullscreen);
+ void onScreenRotationButtonClicked();
+
void onMoreOptionsLongClicked();
void onPlayerError(ExoPlaybackException error);
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index a0152c13a..cfce6e678 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -321,6 +322,13 @@ public class PlayerHelper {
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
}
+ public static boolean globalScreenOrientationLocked(Context context) {
+ // 1: Screen orientation changes using accelerometer
+ // 0: Screen orientation is locked
+ return !(android.provider.Settings.System.getInt(
+ context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
+ }
+
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java
deleted file mode 100644
index 534fb26c3..000000000
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsContentObserver.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.schabi.newpipe.settings;
-
-import android.database.ContentObserver;
-import android.os.Handler;
-
-public class SettingsContentObserver extends ContentObserver {
- private OnChangeListener listener;
-
- public interface OnChangeListener {
- void onSettingsChanged();
- }
-
- public SettingsContentObserver(Handler handler, OnChangeListener listener) {
- super(handler);
- this.listener = listener;
- }
-
- @Override
- public boolean deliverSelfNotifications() {
- return super.deliverSelfNotifications();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- if (listener != null)
- listener.onSettingsChanged();
- }
-}
\ No newline at end of file
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index df0ada0b0..16a52cdb6 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -466,6 +466,21 @@
android:visibility="gone"
android:background="?attr/selectableItemBackground"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
+
+
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index b00a3a4d4..1a0fb292f 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -464,6 +464,21 @@
android:visibility="gone"
android:background="?attr/selectableItemBackground"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
+
+
From cc438fdb7babde33544e2baca2f3b59cb8e10b34 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Fri, 17 Jan 2020 17:37:53 +0300
Subject: [PATCH 14/40] Player's elements positioning is better for tablet and
in multiWindow mode - status bar got a fix for situation when a phone vendor
did not provide status bar height for landscape orientation - popup will not
be init'd twice - also fixed some non-reproduceable bugs
---
.../fragments/detail/VideoDetailFragment.java | 50 ++++++++++++-------
.../newpipe/player/VideoPlayerImpl.java | 46 +++++++++++++----
.../newpipe/player/helper/PlayerHelper.java | 6 +++
.../activity_main_player.xml | 2 -
.../fragment_video_detail.xml | 3 +-
.../main/res/layout/activity_main_player.xml | 2 -
.../main/res/layout/fragment_video_detail.xml | 3 +-
7 files changed, 74 insertions(+), 38 deletions(-)
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 cfaf0aea4..f4c79e73d 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
@@ -288,8 +288,8 @@ public class VideoDetailFragment
}
private void stopService() {
- getContext().stopService(new Intent(getContext(), MainPlayer.class));
unbind();
+ getContext().stopService(new Intent(getContext(), MainPlayer.class));
}
@@ -325,7 +325,6 @@ public class VideoDetailFragment
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
- startService(false);
setupBroadcastReceiver();
settingsContentObserver = new ContentObserver(new Handler()) {
@@ -415,6 +414,7 @@ public class VideoDetailFragment
positionSubscriber = null;
currentWorker = null;
disposables = null;
+ bottomSheetBehavior.setBottomSheetCallback(null);
}
@Override
@@ -688,6 +688,7 @@ public class VideoDetailFragment
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
setupBottomPlayer();
+ startService(false);
}
private View.OnTouchListener getOnControlsTouchListener() {
@@ -931,8 +932,6 @@ public class VideoDetailFragment
boolean streamIsTheSame = this.playQueue != null && this.playQueue.equals(playQueue);
// Situation when user switches from players to main player. All needed data is here, we can start watching
if (streamIsTheSame) {
- //TODO not sure about usefulness of this line in the case when user switches from one player to another
- // handleResult(currentInfo);
openVideoPlayer();
return;
}
@@ -1094,17 +1093,17 @@ public class VideoDetailFragment
if (playerService == null) {
startService(true);
return;
- }
- if (currentInfo == null) return;
+ }
+ if (currentInfo == null) return;
- PlayQueue queue = setupPlayQueueForIntent(false);
+ PlayQueue queue = setupPlayQueueForIntent(false);
- addVideoPlayerView();
- playerService.getView().setVisibility(View.GONE);
+ // Video view can have elements visible from popup, We hide it here but once it ready the view will be shown in handleIntent()
+ playerService.getView().setVisibility(View.GONE);
+ addVideoPlayerView();
- Intent playerIntent = NavigationHelper.getPlayerIntent(
- getContext(), MainPlayer.class, queue, null, true);
- activity.startService(playerIntent);
+ Intent playerIntent = NavigationHelper.getPlayerIntent(getContext(), MainPlayer.class, queue, null, true);
+ activity.startService(playerIntent);
}
private void hideMainPlayer() {
@@ -1194,7 +1193,7 @@ public class VideoDetailFragment
}
private void addVideoPlayerView() {
- if (player == null) return;
+ if (player == null || getView() == null) return;
FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
@@ -1213,6 +1212,8 @@ public class VideoDetailFragment
}
private void makeDefaultHeightForVideoPlaceholder() {
+ if (getView() == null) return;
+
FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
viewHolder.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT;
viewHolder.requestLayout();
@@ -1322,7 +1323,9 @@ public class VideoDetailFragment
if (player != null && player.isInFullscreen()) player.toggleFullscreen();
// This will show systemUI and pause the player.
// User can tap on Play button and video will be in fullscreen mode again
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ // Note for tablet: trying to avoid orientation changes since it's not easy to physically rotate the tablet every time
+ if (!PlayerHelper.isTablet(activity))
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1356,7 +1359,7 @@ public class VideoDetailFragment
if(relatedStreamsLayout != null){
if(showRelatedStreams){
- relatedStreamsLayout.setVisibility(View.INVISIBLE);
+ relatedStreamsLayout.setVisibility(player != null && player.isInFullscreen() ? View.GONE : View.INVISIBLE);
}else{
relatedStreamsLayout.setVisibility(View.GONE);
}
@@ -1383,7 +1386,7 @@ public class VideoDetailFragment
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout, RelatedVideosFragment.getInstance(info))
.commitNow();
- relatedStreamsLayout.setVisibility(View.VISIBLE);
+ relatedStreamsLayout.setVisibility(player != null && player.isInFullscreen() ? View.GONE : View.VISIBLE);
}
}
@@ -1722,6 +1725,13 @@ public class VideoDetailFragment
@Override
public void onScreenRotationButtonClicked() {
+ // In tablet user experience will be better if screen will not be rotated from landscape to portrait every time
+ // Just turn on fullscreen mode in landscape orientation
+ if (isLandscape() && PlayerHelper.isTablet(activity)) {
+ player.toggleFullscreen();
+ return;
+ }
+
int newOrientation = isLandscape() ?
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
@@ -1866,10 +1876,12 @@ public class VideoDetailFragment
final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
if (bottomSheetState != BottomSheetBehavior.STATE_HIDDEN) {
bottomSheetBehavior.setPeekHeight(peekHeight);
- if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED)
- setOverlayLook(appBarLayout, behavior, 1 - MAX_OVERLAY_ALPHA);
- else if (bottomSheetState == BottomSheetBehavior.STATE_EXPANDED)
+ if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED) {
+ overlay.setAlpha(MAX_OVERLAY_ALPHA);
+ } else if (bottomSheetState == BottomSheetBehavior.STATE_EXPANDED) {
+ overlay.setAlpha(0);
setOverlayElementsClickable(false);
+ }
}
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 66407936c..4c2740edc 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -1155,18 +1155,20 @@ public class VideoPlayerImpl extends VideoPlayer
// It doesn't include NavigationBar, notches, etc.
display.getSize(size);
- int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
+ int spaceBeforeTopControls = getRootView().findViewById(R.id.spaceBeforeControls).getWidth();
+ int widthForTopControls = isFullscreen ? size.x - spaceBeforeTopControls : ViewGroup.LayoutParams.MATCH_PARENT;
+ int widthForBottomControls = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
- primaryControls.getLayoutParams().width = width;
+ primaryControls.getLayoutParams().width = widthForTopControls;
((LinearLayout.LayoutParams) primaryControls.getLayoutParams()).gravity = gravity;
primaryControls.requestLayout();
- secondaryControls.getLayoutParams().width = width;
+ secondaryControls.getLayoutParams().width = widthForTopControls;
((LinearLayout.LayoutParams) secondaryControls.getLayoutParams()).gravity = gravity;
secondaryControls.requestLayout();
- getBottomControlsRoot().getLayoutParams().width = width;
+ getBottomControlsRoot().getLayoutParams().width = widthForBottomControls;
RelativeLayout.LayoutParams bottomParams = ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
@@ -1174,15 +1176,33 @@ public class VideoPlayerImpl extends VideoPlayer
getBottomControlsRoot().requestLayout();
ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackControlRoot);
- controlsRoot.getLayoutParams().height = isFullscreen ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
+ // In tablet navigationBar located at the bottom of the screen. And the only situation when we need to set custom height is
+ // in fullscreen mode in tablet in non-multiWindow mode. Other than that MATCH_PARENT is good
+ controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && PlayerHelper.isTablet(service)
+ ? size.y
+ : ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
+ int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
+ getRootView().findViewById(R.id.playbackWindowRoot).setPadding(0, topPadding, 0, 0);
+ getRootView().findViewById(R.id.playbackWindowRoot).requestLayout();
+ }
+
+ private int getStatusBarHeight() {
int statusBarHeight = 0;
int resourceId = service.getResources().getIdentifier("status_bar_height_landscape", "dimen", "android");
if (resourceId > 0) statusBarHeight = service.getResources().getDimensionPixelSize(resourceId);
+ if (statusBarHeight == 0) {
+ // Some devices provide wrong value for status bar height in landscape mode, this is workaround
+ DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
+ statusBarHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, metrics);
+ }
+ return statusBarHeight;
+ }
- getRootView().findViewById(R.id.playbackWindowRoot).setPadding(0, isFullscreen ? statusBarHeight : 0, 0, 0);
- getRootView().findViewById(R.id.playbackWindowRoot).requestLayout();
+ private boolean isInMultiWindow() {
+ AppCompatActivity parent = getParentActivity();
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && parent != null && parent.isInMultiWindowMode();
}
private void updatePlaybackButtons() {
@@ -1287,6 +1307,9 @@ public class VideoPlayerImpl extends VideoPlayer
private void initPopup() {
if (DEBUG) Log.d(TAG, "initPopup() called");
+ // Popup is already added to windowManager
+ if (getRootView().getLayoutParams() instanceof WindowManager.LayoutParams) return;
+
updateScreenSize();
final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(service);
@@ -1326,6 +1349,10 @@ public class VideoPlayerImpl extends VideoPlayer
@SuppressLint("RtlHardcoded")
private void initPopupCloseOverlay() {
if (DEBUG) Log.d(TAG, "initPopupCloseOverlay() called");
+
+ // closeOverlayView is already added to windowManager
+ if (closeOverlayView != null) return;
+
closeOverlayView = View.inflate(service, R.layout.player_popup_close_overlay, null);
closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
@@ -1493,6 +1520,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void end() {
windowManager.removeView(closeOverlayView);
+ closeOverlayView = null;
service.onDestroy();
}
@@ -1653,10 +1681,6 @@ public class VideoPlayerImpl extends VideoPlayer
return closeOverlayButton;
}
- public View getCloseOverlayView() {
- return closeOverlayView;
- }
-
public View getClosingOverlayView() {
return closingOverlayView;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index cfce6e678..ae1cac382 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -2,6 +2,7 @@ package org.schabi.newpipe.player.helper;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.Settings;
@@ -329,6 +330,11 @@ public class PlayerHelper {
context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
}
+ public static boolean isTablet(@NonNull final Context context) {
+ return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+ >= Configuration.SCREENLAYOUT_SIZE_LARGE;
+ }
+
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 16a52cdb6..73a1113c6 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -161,7 +161,6 @@
android:orientation="vertical"
android:gravity="top"
android:paddingTop="4dp"
- android:paddingEnd="6dp"
android:layout_toEndOf="@id/spaceBeforeControls"
android:baselineAligned="false">
@@ -172,7 +171,6 @@
android:baselineAligned="false"
android:gravity="top"
android:paddingBottom="7dp"
- android:paddingLeft="2dp"
tools:ignore="RtlHardcoded">
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 1a0fb292f..cf44d6bcb 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -159,7 +159,6 @@
android:orientation="vertical"
android:gravity="top"
android:paddingTop="4dp"
- android:paddingEnd="6dp"
android:layout_toEndOf="@id/spaceBeforeControls"
android:baselineAligned="false">
@@ -170,7 +169,6 @@
android:baselineAligned="false"
android:gravity="top"
android:paddingBottom="7dp"
- android:paddingLeft="2dp"
tools:ignore="RtlHardcoded">
Date: Sun, 26 Jan 2020 07:33:52 +0300
Subject: [PATCH 15/40] Hotfix
---
app/build.gradle | 2 +-
.../schabi/newpipe/fragments/detail/VideoDetailFragment.java | 1 +
app/src/main/res/layout/activity_error.xml | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index f7017a6df..424ed6211 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -62,7 +62,7 @@ dependencies {
exclude module: 'support-annotations'
})
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:8e53fda'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:ff61e284'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'
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 f4c79e73d..dca7126da 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
@@ -1175,6 +1175,7 @@ public class VideoDetailFragment
return autoPlayEnabled
&& !isExternalPlayerEnabled()
&& (player == null || player.videoPlayerSelected())
+ && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
&& isAutoplayAllowedByUser();
}
diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml
index c47077c73..f3fd9a956 100644
--- a/app/src/main/res/layout/activity_error.xml
+++ b/app/src/main/res/layout/activity_error.xml
@@ -100,6 +100,7 @@
android:id="@+id/errorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textIsSelectable="true"
android:typeface="monospace"/>
From f334a2740f95cd5fe093860e83483b6511b4542e Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 5 Feb 2020 08:59:30 +0300
Subject: [PATCH 16/40] Mini player, ExpandableSurfaceView with ZOOM support,
popup - mini player's title, image and author information will be updated in
many situations but the main idea is that the info will be the same as
currently playing stream. If nothing played then you'll see the info about
currently opened stream in fragment. When MainPlayer service stops the info
updates too - made ExpandableSurfaceView to replace AspectRatioFrameLayout.
The reason for that is to make possible to use aspect ratio mode ZOOM. It's
impossible to show a stream inside AspectRatioFrameLayout with ZOOM mode and
to fit the video view to a screen space at the same time. Now the new view
able to do that and to show vertical videos in a slightly wide space for them
- refactored some methods to make the code more understandable - made fixes
for player view for landscape-to-landscape orientation change - added Java
docs - adapted swipe tracking inside bottom sheet - fixed PlayQueue crashes
on clearing - paddings for popup player now as small as possible
---
.../material/appbar/FlingBehavior.java | 2 +-
.../fragments/detail/VideoDetailFragment.java | 79 +++++---
.../org/schabi/newpipe/player/BasePlayer.java | 17 +-
.../org/schabi/newpipe/player/MainPlayer.java | 1 +
.../schabi/newpipe/player/VideoPlayer.java | 32 ++-
.../newpipe/player/VideoPlayerImpl.java | 184 ++++++++++++------
.../event/CustomBottomSheetBehavior.java | 24 ++-
.../newpipe/player/playqueue/PlayQueue.java | 14 +-
.../newpipe/views/ExpandableSurfaceView.java | 102 ++++++++++
.../activity_main_player.xml | 81 +++-----
.../fragment_video_detail.xml | 2 +-
.../main/res/layout/activity_main_player.xml | 81 +++-----
.../main/res/layout/fragment_video_detail.xml | 2 +-
app/src/main/res/values/dimens.xml | 9 +
14 files changed, 405 insertions(+), 225 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
diff --git a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
index ff2860558..f1038faa1 100644
--- a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
+++ b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
@@ -30,7 +30,7 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
ViewGroup playQueue = child.findViewById(R.id.playQueue);
if (playQueue != null) {
playQueue.getGlobalVisibleRect(playQueueRect);
- if (playQueueRect.contains((int) ev.getX(), (int) ev.getY())) {
+ if (playQueueRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
allowScroll = false;
return false;
}
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 dca7126da..ce113a93d 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
@@ -5,7 +5,6 @@ import android.app.Activity;
import android.content.*;
import android.content.pm.ActivityInfo;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
@@ -108,6 +107,7 @@ public class VideoDetailFragment
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
private static final int COMMENTS_UPDATE_FLAG = 0x8;
private static final float MAX_OVERLAY_ALPHA = 0.9f;
+ private static final float MAX_PLAYER_HEIGHT = 0.7f;
public static final String ACTION_SHOW_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
public static final String ACTION_HIDE_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
@@ -235,7 +235,7 @@ public class VideoDetailFragment
// It will do nothing if the player is not in fullscreen mode
hideSystemUIIfNeeded();
- if (!player.videoPlayerSelected()) return;
+ if (!player.videoPlayerSelected() && !playAfterConnect) return;
// STATE_IDLE means the player is stopped
if (player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE) addVideoPlayerView();
@@ -282,6 +282,9 @@ public class VideoDetailFragment
}
private void startService(boolean playAfterConnect) {
+ // startService() can be called concurrently and it will give a random crashes and NullPointerExceptions
+ // inside the service because the service will be bound twice. Prevent it with unbinding first
+ unbind();
getContext().startService(new Intent(getContext(), MainPlayer.class));
serviceConnection = getServiceConnection(playAfterConnect);
bind();
@@ -708,7 +711,6 @@ public class VideoDetailFragment
private void initThumbnailViews(@NonNull StreamInfo info) {
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
- overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
final String infoServiceName = NewPipe.getNameOfService(info.getServiceId());
@@ -718,11 +720,6 @@ public class VideoDetailFragment
showSnackBarError(failReason.getCause(), UserAction.LOAD_IMAGE,
infoServiceName, imageUri, R.string.could_not_load_thumbnails);
}
-
- @Override
- public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
- overlayThumbnailImageView.setImageBitmap(loadedImage);
- }
};
imageLoader.displayImage(info.getThumbnailUrl(), thumbnailImageView,
@@ -855,7 +852,7 @@ public class VideoDetailFragment
*/
protected final LinkedList stack = new LinkedList<>();
- public void setTitleToUrl(int serviceId, String videoUrl, String name) {
+ /*public void setTitleToUrl(int serviceId, String videoUrl, String name) {
if (name != null && !name.isEmpty()) {
for (StackItem stackItem : stack) {
if (stack.peek().getServiceId() == serviceId
@@ -864,7 +861,7 @@ public class VideoDetailFragment
}
}
}
- }
+ }*/
@Override
public boolean onBackPressed() {
@@ -887,7 +884,9 @@ public class VideoDetailFragment
}
// If we have something in history of played items we replay it here
- if (player != null && player.getPlayQueue() != null && player.getPlayQueue().previous()) {
+ boolean isPreviousCanBePlayed = player != null && player.getPlayQueue() != null && player.videoPlayerSelected()
+ && player.getPlayQueue().previous();
+ if (isPreviousCanBePlayed) {
return true;
}
// That means that we are on the start of the stack,
@@ -914,6 +913,12 @@ public class VideoDetailFragment
item.getUrl(),
!TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "",
item.getPlayQueue());
+
+ PlayQueueItem playQueueItem = item.getPlayQueue().getItem();
+ // Update title, url, uploader from the last item in the stack (it's current now)
+ boolean isPlayerStopped = player == null || player.isPlayerStopped();
+ if (playQueueItem != null && isPlayerStopped)
+ updateOverlayData(playQueueItem.getTitle(), playQueueItem.getUploader(), playQueueItem.getThumbnailUrl());
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1199,7 +1204,7 @@ public class VideoDetailFragment
FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
// Check if viewHolder already contains a child
- if (player.getRootView() != viewHolder) removeVideoPlayerView();
+ if (player.getRootView().getParent() != viewHolder) removeVideoPlayerView();
setHeightThumbnail();
// Prevent from re-adding a view multiple times
@@ -1250,6 +1255,11 @@ public class VideoDetailFragment
}));
}
+ /**
+ * Method which controls the size of thumbnail and the size of main player inside a layout with thumbnail.
+ * It decides what height the player should have in both screen orientations. It knows about multiWindow feature
+ * and about videos with aspectRatio ZOOM (the height for them will be a bit higher, {@link #MAX_PLAYER_HEIGHT})
+ */
private void setHeightThumbnail() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
@@ -1260,11 +1270,14 @@ public class VideoDetailFragment
else
height = isPortrait
? (int) (metrics.widthPixels / (16.0f / 9.0f))
- : (int) (metrics.heightPixels / 2f);;
+ : (int) (metrics.heightPixels / 2f);
- thumbnailImageView.setLayoutParams(
- new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
+ thumbnailImageView.setLayoutParams(new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
+ if (player != null) {
+ int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
+ player.getSurfaceView().setHeights(height, player.isInFullscreen() ? height : maxHeight);
+ }
}
private void showContent() {
@@ -1393,13 +1406,11 @@ public class VideoDetailFragment
animateView(thumbnailPlayButton, true, 200);
videoTitleTextView.setText(name);
- overlayTitleTextView.setText(name);
if (!TextUtils.isEmpty(info.getUploaderName())) {
uploaderTextView.setText(info.getUploaderName());
uploaderTextView.setVisibility(View.VISIBLE);
uploaderTextView.setSelected(true);
- overlayChannelTextView.setText(info.getUploaderName());
} else {
uploaderTextView.setVisibility(View.GONE);
}
@@ -1481,8 +1492,9 @@ public class VideoDetailFragment
setupActionBar(info);
initThumbnailViews(info);
- setTitleToUrl(info.getServiceId(), info.getUrl(), info.getName());
- setTitleToUrl(info.getServiceId(), info.getOriginalUrl(), info.getName());
+ if (player == null || player.isPlayerStopped())
+ updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
+
if (!info.getErrors().isEmpty()) {
showSnackBarError(info.getErrors(),
@@ -1682,7 +1694,8 @@ public class VideoDetailFragment
peek.setUrl(info.getUrl());
}
- if (currentInfo == info) return;
+ updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
+ if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) return;
currentInfo = info;
setAutoplay(false);
@@ -1702,6 +1715,8 @@ public class VideoDetailFragment
public void onServiceStopped() {
unbind();
setOverlayPlayPauseImage();
+ if (currentInfo != null)
+ updateOverlayData(currentInfo.getName(), currentInfo.getUploaderName(), currentInfo.getThumbnailUrl());
}
@Override
@@ -1858,9 +1873,11 @@ public class VideoDetailFragment
private void cleanUp() {
// New beginning
stack.clear();
+ if (currentWorker != null) currentWorker.dispose();
stopService();
setInitialData(0,null,"", null);
currentInfo = null;
+ updateOverlayData(null, null, null);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1899,16 +1916,20 @@ public class VideoDetailFragment
// Disable click because overlay buttons located on top of buttons from the player
setOverlayElementsClickable(false);
hideSystemUIIfNeeded();
- if (isLandscape() && player != null && player.isPlaying() && !player.isInFullscreen())
- player.toggleFullscreen();
+ boolean needToExpand = isLandscape()
+ && player != null
+ && player.isPlaying()
+ && !player.isInFullscreen()
+ && player.videoPlayerSelected();
+ if (needToExpand) player.toggleFullscreen();
break;
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
setOverlayElementsClickable(true);
- if (player != null && player.isInFullscreen()) showSystemUi();
break;
case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_SETTLING:
+ if (player != null && player.isInFullscreen()) showSystemUi();
if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
break;
}
@@ -1925,6 +1946,15 @@ public class VideoDetailFragment
});
}
+ private void updateOverlayData(@Nullable String title, @Nullable String uploader, @Nullable String thumbnailUrl) {
+ overlayTitleTextView.setText(!TextUtils.isEmpty(title) ? title : "");
+ overlayChannelTextView.setText(!TextUtils.isEmpty(uploader) ? uploader : "");
+ overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
+ if (!TextUtils.isEmpty(thumbnailUrl))
+ imageLoader.displayImage(thumbnailUrl, overlayThumbnailImageView,
+ ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null);
+ }
+
private void setOverlayPlayPauseImage() {
boolean playing = player != null && player.getPlayer().getPlayWhenReady();
int attr = playing ? R.attr.pause : R.attr.play;
@@ -1935,7 +1965,8 @@ public class VideoDetailFragment
if (behavior != null) {
overlay.setAlpha(Math.min(MAX_OVERLAY_ALPHA, 1 - slideOffset));
- behavior.setTopAndBottomOffset((int)(-appBarLayout.getTotalScrollRange() * (1 - slideOffset) / 3));
+ // These numbers are not special. They just do a cool transition
+ behavior.setTopAndBottomOffset((int)(-thumbnailImageView.getHeight() * 2 * (1 - slideOffset) / 3));
appBarLayout.requestLayout();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 70ab82fb8..815cbb8ab 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -276,14 +276,6 @@ public abstract class BasePlayer implements
boolean same = playQueue != null && playQueue.equals(queue);
- // Do not re-init the same PlayQueue. Save time
- if (same && !playQueue.isDisposed()) {
- // Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
- if (simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE)
- simpleExoPlayer.retry();
- return;
- }
-
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
@@ -298,10 +290,17 @@ public abstract class BasePlayer implements
&& playQueue.getItem() != null
&& queue.getItem().getUrl().equals(playQueue.getItem().getUrl())
&& queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET
- && !same) {
+ && simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) {
+ // Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
+ if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) simpleExoPlayer.retry();
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
return;
+ } else if (same && !playQueue.isDisposed() && simpleExoPlayer != null) {
+ // Do not re-init the same PlayQueue. Save time
+ // Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
+ if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) simpleExoPlayer.retry();
+ return;
} else if (intent.getBooleanExtra(RESUME_PLAYBACK, false)
&& isPlaybackResumeEnabled()
&& !same) {
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 63c2f195f..771d6d7e1 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -179,6 +179,7 @@ public final class MainPlayer extends Service {
playerImpl.savePlaybackState();
playerImpl.stopActivityBinding();
+ playerImpl.removePopupFromView();
playerImpl.destroy();
}
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
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 07f485e7a..c29cfd19c 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
@@ -34,10 +34,7 @@ import android.os.Build;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SurfaceView;
-import android.view.View;
+import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull;
@@ -65,6 +62,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils;
+import org.schabi.newpipe.views.ExpandableSurfaceView;
import java.util.ArrayList;
import java.util.List;
@@ -109,8 +107,7 @@ public abstract class VideoPlayer extends BasePlayer
private View rootView;
- private AspectRatioFrameLayout aspectRatioFrameLayout;
- private SurfaceView surfaceView;
+ private ExpandableSurfaceView surfaceView;
private View surfaceForeground;
private View loadingPanel;
@@ -163,7 +160,6 @@ public abstract class VideoPlayer extends BasePlayer
public void initViews(View rootView) {
this.rootView = rootView;
- this.aspectRatioFrameLayout = rootView.findViewById(R.id.aspectRatioLayout);
this.surfaceView = rootView.findViewById(R.id.surfaceView);
this.surfaceForeground = rootView.findViewById(R.id.surfaceForeground);
this.loadingPanel = rootView.findViewById(R.id.loading_panel);
@@ -187,12 +183,10 @@ public abstract class VideoPlayer extends BasePlayer
setupSubtitleView(subtitleView, captionScale, captionStyle);
this.resizeView = rootView.findViewById(R.id.resizeTextView);
- resizeView.setText(PlayerHelper.resizeTypeOf(context, aspectRatioFrameLayout.getResizeMode()));
+ resizeView.setText(PlayerHelper.resizeTypeOf(context, getSurfaceView().getResizeMode()));
this.captionTextView = rootView.findViewById(R.id.captionTextView);
- //this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f);
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
this.playbackSeekBar.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
@@ -501,7 +495,7 @@ public abstract class VideoPlayer extends BasePlayer
if (DEBUG) {
Log.d(TAG, "onVideoSizeChanged() called with: width / height = [" + width + " / " + height + " = " + (((float) width) / height) + "], unappliedRotationDegrees = [" + unappliedRotationDegrees + "], pixelWidthHeightRatio = [" + pixelWidthHeightRatio + "]");
}
- aspectRatioFrameLayout.setAspectRatio(((float) width) / height);
+ getSurfaceView().setAspectRatio(((float) width) / height);
}
@Override
@@ -715,15 +709,15 @@ public abstract class VideoPlayer extends BasePlayer
}
void onResizeClicked() {
- if (getAspectRatioFrameLayout() != null) {
- final int currentResizeMode = getAspectRatioFrameLayout().getResizeMode();
+ if (getSurfaceView() != null) {
+ final int currentResizeMode = getSurfaceView().getResizeMode();
final int newResizeMode = nextResizeMode(currentResizeMode);
setResizeMode(newResizeMode);
}
}
protected void setResizeMode(@AspectRatioFrameLayout.ResizeMode final int resizeMode) {
- getAspectRatioFrameLayout().setResizeMode(resizeMode);
+ getSurfaceView().setResizeMode(resizeMode);
getResizeView().setText(PlayerHelper.resizeTypeOf(context, resizeMode));
}
@@ -894,11 +888,7 @@ public abstract class VideoPlayer extends BasePlayer
return resolver.getPlaybackQuality();
}
- public AspectRatioFrameLayout getAspectRatioFrameLayout() {
- return aspectRatioFrameLayout;
- }
-
- public SurfaceView getSurfaceView() {
+ public ExpandableSurfaceView getSurfaceView() {
return surfaceView;
}
@@ -969,6 +959,10 @@ public abstract class VideoPlayer extends BasePlayer
return qualityPopupMenu;
}
+ public TextView getPlaybackSpeedTextView() {
+ return playbackSpeedTextView;
+ }
+
public PopupMenu getPlaybackSpeedPopupMenu() {
return playbackSpeedPopupMenu;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 4c2740edc..72c3b71ee 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -46,6 +46,7 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
@@ -195,6 +196,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
setupElementsVisibility();
+ setupElementsSize();
if (audioPlayerSelected()) {
service.removeViewFromParent();
@@ -280,11 +282,16 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
+ /**
+ * This method ensures that popup and main players have different look. We use one layout for both players and
+ * need to decide what to show and what to hide. Additional measuring should be done inside {@link #setupElementsSize}.
+ * {@link #setControlsSize} is used to adapt the UI to fullscreen mode, multiWindow, navBar, etc
+ */
private void setupElementsVisibility() {
if (popupPlayerSelected()) {
fullscreenButton.setVisibility(View.VISIBLE);
screenRotationButton.setVisibility(View.GONE);
- getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.GONE);
+ getResizeView().setVisibility(View.GONE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE);
queueButton.setVisibility(View.GONE);
moreOptionsButton.setVisibility(View.GONE);
@@ -297,14 +304,16 @@ public class VideoPlayerImpl extends VideoPlayer
playWithKodi.setVisibility(View.GONE);
openInBrowser.setVisibility(View.GONE);
playerCloseButton.setVisibility(View.GONE);
+ getTopControlsRoot().bringToFront();
+ getBottomControlsRoot().bringToFront();
} else {
fullscreenButton.setVisibility(View.GONE);
setupScreenRotationButton(service.isLandscape());
- getRootView().findViewById(R.id.spaceBeforeControls).setVisibility(View.VISIBLE);
+ getResizeView().setVisibility(View.VISIBLE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE);
getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
- primaryControls.getLayoutParams().width = secondaryControls.getLayoutParams().width;
+ primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
secondaryControls.setVisibility(View.GONE);
moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
R.drawable.ic_expand_more_white_24dp));
@@ -325,6 +334,37 @@ public class VideoPlayerImpl extends VideoPlayer
animateRotation(moreOptionsButton, DEFAULT_CONTROLS_DURATION, 0);
}
+ /**
+ * Changes padding, size of elements based on player selected right now. Popup player has small padding in comparison with the
+ * main player
+ */
+ private void setupElementsSize() {
+ if (popupPlayerSelected()) {
+ int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_controls_padding);
+ int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_buttons_padding);
+ getTopControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
+ getBottomControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
+ getQualityTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getPlaybackSpeedTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getQualityTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getCaptionTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getQualityTextView().setMinimumWidth(0);
+ getPlaybackSpeedTextView().setMinimumWidth(0);
+ } else if (videoPlayerSelected()) {
+ int buttonsMinWidth = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_min_width);
+ int playerTopPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_top_padding);
+ int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_controls_padding);
+ int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_padding);
+ getTopControlsRoot().setPaddingRelative(controlsPadding, playerTopPadding, controlsPadding, 0);
+ getBottomControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
+ getQualityTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getPlaybackSpeedTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ getQualityTextView().setMinimumWidth(buttonsMinWidth);
+ getPlaybackSpeedTextView().setMinimumWidth(buttonsMinWidth);
+ getCaptionTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
+ }
+ }
+
@Override
public void initListeners() {
super.initListeners();
@@ -357,24 +397,7 @@ public class VideoPlayerImpl extends VideoPlayer
service.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
settingsContentObserver);
-
- getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
- if (l != ol || t != ot || r != or || b != ob) {
- // Use smaller value to be consistent between screen orientations
- // (and to make usage easier)
- int width = r - l, height = b - t;
- int min = Math.min(width, height);
- maxGestureLength = (int) (min * MAX_GESTURE_LENGTH);
-
- if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
-
- volumeProgressBar.setMax(maxGestureLength);
- brightnessProgressBar.setMax(maxGestureLength);
-
- setInitialGestureValues();
- queueLayout.getLayoutParams().height = min - queueLayout.getTop();
- }
- });
+ getRootView().addOnLayoutChangeListener(this);
}
public AppCompatActivity getParentActivity() {
@@ -553,19 +576,15 @@ public class VideoPlayerImpl extends VideoPlayer
intent.putExtra(Constants.KEY_URL, getVideoUrl());
intent.putExtra(Constants.KEY_TITLE, getVideoTitle());
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
+ service.onDestroy();
context.startActivity(intent);
+ return;
} else {
if (fragmentListener == null) return;
isFullscreen = !isFullscreen;
setControlsSize();
fragmentListener.onFullscreenStateChanged(isInFullscreen());
- // When user presses back button in landscape mode and in fullscreen and uses ZOOM mode
- // a video can be larger than screen. Prevent it like this
- if (getAspectRatioFrameLayout().getResizeMode() == AspectRatioFrameLayout.RESIZE_MODE_ZOOM
- && !isInFullscreen()
- && service.isLandscape())
- onResizeClicked();
}
if (!isInFullscreen()) {
@@ -741,18 +760,28 @@ public class VideoPlayerImpl extends VideoPlayer
}
@Override
- public void onLayoutChange(final View view, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (popupPlayerSelected()) {
- float widthDp = Math.abs(right - left) / service.getResources().getDisplayMetrics().density;
- final int visibility = widthDp > MINIMUM_SHOW_EXTRA_WIDTH_DP ? View.VISIBLE : View.GONE;
- secondaryControls.setVisibility(visibility);
- } else if (videoPlayerSelected()
- && !isInFullscreen()
- && getAspectRatioFrameLayout().getMeasuredHeight() > service.getResources().getDisplayMetrics().heightPixels * 0.8) {
- // Resize mode is ZOOM probably. In this mode video will grow down and it will be weird.
- // So let's open it in fullscreen
- toggleFullscreen();
+ public void onLayoutChange(final View view, int l, int t, int r, int b,
+ int ol, int ot, int or, int ob) {
+ if (l != ol || t != ot || r != or || b != ob) {
+ // Use smaller value to be consistent between screen orientations
+ // (and to make usage easier)
+ int width = r - l, height = b - t;
+ int min = Math.min(width, height);
+ maxGestureLength = (int) (min * MAX_GESTURE_LENGTH);
+
+ if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
+
+ volumeProgressBar.setMax(maxGestureLength);
+ brightnessProgressBar.setMax(maxGestureLength);
+
+ setInitialGestureValues();
+ queueLayout.getLayoutParams().height = min - queueLayout.getTop();
+
+ if (popupPlayerSelected()) {
+ float widthDp = Math.abs(r - l) / service.getResources().getDisplayMetrics().density;
+ final int visibility = widthDp > MINIMUM_SHOW_EXTRA_WIDTH_DP ? View.VISIBLE : View.GONE;
+ secondaryControls.setVisibility(visibility);
+ }
}
}
@@ -781,6 +810,11 @@ public class VideoPlayerImpl extends VideoPlayer
.apply();
}
+ private void restoreResizeMode() {
+ setResizeMode(defaultPreferences.getInt(
+ service.getString(R.string.last_resize_mode), AspectRatioFrameLayout.RESIZE_MODE_FIT));
+ }
+
@Override
protected VideoPlaybackResolver.QualityResolver getQualityResolver() {
return new VideoPlaybackResolver.QualityResolver() {
@@ -933,6 +967,7 @@ public class VideoPlayerImpl extends VideoPlayer
intentFilter.addAction(ACTION_FAST_REWIND);
intentFilter.addAction(ACTION_FAST_FORWARD);
+ intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -969,6 +1004,9 @@ public class VideoPlayerImpl extends VideoPlayer
case ACTION_REPEAT:
onRepeatClicked();
break;
+ case Intent.ACTION_CONFIGURATION_CHANGED:
+ setControlsSize();
+ break;
case Intent.ACTION_SCREEN_ON:
shouldUpdateOnProgress = true;
// Interrupt playback only when screen turns on and user is watching video in fragment
@@ -1054,6 +1092,10 @@ public class VideoPlayerImpl extends VideoPlayer
return playerType == MainPlayer.PlayerType.POPUP;
}
+ public boolean isPlayerStopped() {
+ return getPlayer() == null || getPlayer().getPlaybackState() == SimpleExoPlayer.STATE_IDLE;
+ }
+
private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
@@ -1143,32 +1185,29 @@ public class VideoPlayerImpl extends VideoPlayer
fragmentListener.hideSystemUIIfNeeded();
}
- /*
- * This method measures width and height of controls visible on screen. It ensures that controls will be side-by-side with
- * NavigationBar and notches but not under them. Tablets have only bottom NavigationBar
- * */
- private void setControlsSize() {
+ /**
+ * Measures width and height of controls visible on screen. It ensures that controls will be side-by-side with
+ * NavigationBar and notches but not under them. Tablets have only bottom NavigationBar
+ */
+ public void setControlsSize() {
Point size = new Point();
Display display = getRootView().getDisplay();
- if (display == null) return;
+ if (display == null || !videoPlayerSelected()) return;
// This method will give a correct size of a usable area of a window.
// It doesn't include NavigationBar, notches, etc.
display.getSize(size);
- int spaceBeforeTopControls = getRootView().findViewById(R.id.spaceBeforeControls).getWidth();
- int widthForTopControls = isFullscreen ? size.x - spaceBeforeTopControls : ViewGroup.LayoutParams.MATCH_PARENT;
- int widthForBottomControls = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
+ int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
- primaryControls.getLayoutParams().width = widthForTopControls;
- ((LinearLayout.LayoutParams) primaryControls.getLayoutParams()).gravity = gravity;
- primaryControls.requestLayout();
+ getTopControlsRoot().getLayoutParams().width = width;
+ RelativeLayout.LayoutParams topParams = ((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
+ topParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
+ topParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
+ topParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START);
+ getTopControlsRoot().requestLayout();
- secondaryControls.getLayoutParams().width = widthForTopControls;
- ((LinearLayout.LayoutParams) secondaryControls.getLayoutParams()).gravity = gravity;
- secondaryControls.requestLayout();
-
- getBottomControlsRoot().getLayoutParams().width = widthForBottomControls;
+ getBottomControlsRoot().getLayoutParams().width = width;
RelativeLayout.LayoutParams bottomParams = ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
@@ -1188,6 +1227,9 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().findViewById(R.id.playbackWindowRoot).requestLayout();
}
+ /**
+ * @return statusBar height that was found inside system resources or default value if no value was provided inside resources
+ */
private int getStatusBarHeight() {
int statusBarHeight = 0;
int resourceId = service.getResources().getIdentifier("status_bar_height_landscape", "dimen", "android");
@@ -1200,6 +1242,9 @@ public class VideoPlayerImpl extends VideoPlayer
return statusBarHeight;
}
+ /**
+ * @return true if main player is attached to activity and activity inside multiWindow mode
+ */
private boolean isInMultiWindow() {
AppCompatActivity parent = getParentActivity();
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && parent != null && parent.isInMultiWindowMode();
@@ -1308,7 +1353,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (DEBUG) Log.d(TAG, "initPopup() called");
// Popup is already added to windowManager
- if (getRootView().getLayoutParams() instanceof WindowManager.LayoutParams) return;
+ if (isPopupHasParent()) return;
updateScreenSize();
@@ -1316,18 +1361,19 @@ public class VideoPlayerImpl extends VideoPlayer
final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
-
+ popupHeight = getMinimumVideoHeight(popupWidth);
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
popupLayoutParams = new WindowManager.LayoutParams(
- (int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
+ (int) popupWidth, (int) popupHeight,
layoutParamType,
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ getSurfaceView().setHeights((int) popupHeight, (int) popupHeight);
int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
@@ -1342,8 +1388,8 @@ public class VideoPlayerImpl extends VideoPlayer
service.removeViewFromParent();
windowManager.addView(getRootView(), popupLayoutParams);
- if (getAspectRatioFrameLayout().getResizeMode() == AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
- onResizeClicked();
+ // Popup doesn't have aspectRatio selector, using FIT automatically
+ setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
}
@SuppressLint("RtlHardcoded")
@@ -1375,6 +1421,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void initVideoPlayer() {
+ restoreResizeMode();
getRootView().setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
}
@@ -1471,6 +1518,7 @@ public class VideoPlayerImpl extends VideoPlayer
popupLayoutParams.height = height;
popupWidth = width;
popupHeight = height;
+ getSurfaceView().setHeights((int) popupHeight, (int) popupHeight);
if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
windowManager.updateViewLayout(getRootView(), popupLayoutParams);
@@ -1499,6 +1547,14 @@ public class VideoPlayerImpl extends VideoPlayer
animateOverlayAndFinishService();
}
+ public void removePopupFromView() {
+ boolean isCloseOverlayHasParent = closeOverlayView != null && closeOverlayView.getParent() != null;
+ if (isPopupHasParent())
+ windowManager.removeView(getRootView());
+ if (isCloseOverlayHasParent)
+ windowManager.removeView(closeOverlayView);
+ }
+
private void animateOverlayAndFinishService() {
final int targetTranslationY = (int) (closeOverlayButton.getRootView().getHeight() - closeOverlayButton.getY());
@@ -1527,12 +1583,18 @@ public class VideoPlayerImpl extends VideoPlayer
}).start();
}
+ private boolean isPopupHasParent() {
+ View root = getRootView();
+ return root != null && root.getLayoutParams() instanceof WindowManager.LayoutParams && root.getParent() != null;
+ }
+
///////////////////////////////////////////////////////////////////////////
// Manipulations with listener
///////////////////////////////////////////////////////////////////////////
public void setFragmentListener(PlayerServiceEventListener listener) {
fragmentListener = listener;
+ updateMetadata();
updatePlayback();
triggerProgressUpdate();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
index 6f4cf41de..f0178853f 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -16,6 +16,8 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior {
super(context, attrs);
}
+ boolean skippingInterception = false;
+
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
// Behavior of globalVisibleRect is different on different APIs.
@@ -24,24 +26,40 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior {
boolean visible;
Rect rect = new Rect();
+ // Drop folowing when actions ends
+ if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP)
+ skippingInterception = false;
+
+ // Found that user still swipping, continue folowing
+ if (skippingInterception) return false;
+
// Without overriding scrolling will not work in detail_content_root_layout
ViewGroup controls = child.findViewById(R.id.detail_content_root_layout);
if (controls != null) {
visible = controls.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
+ if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
+ skippingInterception = true;
+ return false;
+ }
}
// Without overriding scrolling will not work on relatedStreamsLayout
ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout);
if (relatedStreamsLayout != null) {
visible = relatedStreamsLayout.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
+ if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
+ skippingInterception = true;
+ return false;
+ }
}
ViewGroup playQueue = child.findViewById(R.id.playQueue);
if (playQueue != null) {
visible = playQueue.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
+ if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
+ skippingInterception = true;
+ return false;
+ }
}
return super.onInterceptTouchEvent(parent, child, event);
diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
index 12454bde9..e739b8f33 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
@@ -59,7 +59,7 @@ public abstract class PlayQueue implements Serializable {
streams = new ArrayList<>();
streams.addAll(startWith);
history = new ArrayList<>();
- history.add(streams.get(index));
+ if (streams.size() > index) history.add(streams.get(index));
queueIndex = new AtomicInteger(index);
disposed = false;
@@ -305,7 +305,9 @@ public abstract class PlayQueue implements Serializable {
}
history.remove(streams.remove(removeIndex));
- history.add(streams.get(queueIndex.get()));
+ if (streams.size() > queueIndex.get()) {
+ history.add(streams.get(queueIndex.get()));
+ }
}
/**
@@ -379,7 +381,9 @@ public abstract class PlayQueue implements Serializable {
streams.add(0, streams.remove(newIndex));
}
queueIndex.set(0);
- history.add(streams.get(0));
+ if (streams.size() > 0) {
+ history.add(streams.get(0));
+ }
broadcast(new ReorderEvent(originIndex, queueIndex.get()));
}
@@ -407,7 +411,9 @@ public abstract class PlayQueue implements Serializable {
} else {
queueIndex.set(0);
}
- history.add(streams.get(queueIndex.get()));
+ if (streams.size() > queueIndex.get()) {
+ history.add(streams.get(queueIndex.get()));
+ }
broadcast(new ReorderEvent(originIndex, queueIndex.get()));
}
diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
new file mode 100644
index 000000000..df012eafd
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
@@ -0,0 +1,102 @@
+package org.schabi.newpipe.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.SurfaceView;
+import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
+
+import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.*;
+
+public class ExpandableSurfaceView extends SurfaceView {
+ private int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
+ private int baseHeight = 0;
+ private int maxHeight = 0;
+ private float videoAspectRatio = 0f;
+ private float scaleX = 1.0f;
+ private float scaleY = 1.0f;
+
+ public ExpandableSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (videoAspectRatio == 0f) return;
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ boolean verticalVideo = videoAspectRatio < 1;
+ // Use maxHeight only on non-fit resize mode and in vertical videos
+ int height = maxHeight != 0 && resizeMode != AspectRatioFrameLayout.RESIZE_MODE_FIT && verticalVideo ? maxHeight : baseHeight;
+
+ if (height == 0) return;
+
+ float viewAspectRatio = width / ((float) height);
+ float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
+ scaleX = 1.0f;
+ scaleY = 1.0f;
+
+ switch (resizeMode) {
+ case AspectRatioFrameLayout.RESIZE_MODE_FIT:
+ if (aspectDeformation > 0) {
+ height = (int) (width / videoAspectRatio);
+ } else {
+ width = (int) (height * videoAspectRatio);
+ }
+
+ break;
+ case RESIZE_MODE_ZOOM:
+ if (aspectDeformation < 0) {
+ scaleY = viewAspectRatio / videoAspectRatio;
+ } else {
+ scaleX = videoAspectRatio / viewAspectRatio;
+ }
+
+ break;
+ default:
+ break;
+ }
+ super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+
+ /**
+ * Scale view only in {@link #onLayout} to make transition for ZOOM mode as smooth as possible
+ */
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ }
+
+ /**
+ * @param base The height that will be used in every resize mode as a minimum height
+ * @param max The max height for vertical videos in non-FIT resize modes
+ */
+ public void setHeights(int base, int max) {
+ if (baseHeight == base && maxHeight == max) return;
+ baseHeight = base;
+ maxHeight = max;
+ requestLayout();
+ }
+
+ @AspectRatioFrameLayout.ResizeMode
+ public void setResizeMode(int newResizeMode) {
+ if (resizeMode == newResizeMode) return;
+
+ resizeMode = newResizeMode;
+ requestLayout();
+ }
+
+ @AspectRatioFrameLayout.ResizeMode
+ public int getResizeMode() {
+ return resizeMode;
+ }
+
+ public void setAspectRatio(float aspectRatio) {
+ if (videoAspectRatio == aspectRatio) return;
+
+ videoAspectRatio = aspectRatio;
+ requestLayout();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 73a1113c6..d434fe1ac 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -8,27 +8,18 @@
android:background="@color/black"
android:gravity="center">
-
+ android:layout_centerHorizontal="true"/>
-
-
-
-
-
-
+
-
-
-
@@ -235,11 +222,9 @@
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
- android:layout_marginStart="5dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
- android:minWidth="50dp"
android:text="720p"
android:textColor="@android:color/white"
android:textStyle="bold"
@@ -251,11 +236,10 @@
android:id="@+id/playbackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
android:minHeight="35dp"
- android:minWidth="40dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
@@ -266,7 +250,7 @@
android:id="@+id/queueButton"
android:layout_width="30dp"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -280,8 +264,7 @@
android:id="@+id/moreOptionsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="8dp"
- android:layout_marginEnd="8dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -304,7 +287,7 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
@@ -318,7 +301,7 @@
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
@@ -341,7 +324,7 @@
android:id="@+id/playWithKodi"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -355,7 +338,7 @@
android:id="@+id/openInBrowser"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -369,8 +352,7 @@
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
- android:layout_marginEnd="8dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -387,9 +369,7 @@
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
- android:layout_marginTop="2dp"
- android:layout_marginEnd="2dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
@@ -413,9 +393,8 @@
android:layout_alignParentBottom="true"
android:gravity="center"
android:orientation="horizontal"
- android:paddingBottom="6dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp">
+ android:paddingLeft="@dimen/player_main_controls_padding"
+ android:paddingRight="@dimen/player_main_controls_padding">
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index cf44d6bcb..d63108096 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -8,27 +8,18 @@
android:background="@color/black"
android:gravity="center">
-
+ android:layout_centerHorizontal="true"/>
-
-
-
-
-
-
+
-
-
-
@@ -233,11 +220,9 @@
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
- android:layout_marginStart="5dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
- android:minWidth="50dp"
android:text="720p"
android:textColor="@android:color/white"
android:textStyle="bold"
@@ -249,11 +234,10 @@
android:id="@+id/playbackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
android:minHeight="35dp"
- android:minWidth="40dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
@@ -264,7 +248,7 @@
android:id="@+id/queueButton"
android:layout_width="30dp"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -278,8 +262,7 @@
android:id="@+id/moreOptionsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="8dp"
- android:layout_marginEnd="8dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -302,7 +285,7 @@
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center"
android:minWidth="50dp"
@@ -316,7 +299,7 @@
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
@@ -339,7 +322,7 @@
android:id="@+id/playWithKodi"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -353,7 +336,7 @@
android:id="@+id/openInBrowser"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
@@ -367,8 +350,7 @@
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="35dp"
- android:padding="6dp"
- android:layout_marginEnd="8dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@@ -385,9 +367,7 @@
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
- android:layout_marginTop="2dp"
- android:layout_marginEnd="2dp"
- android:padding="6dp"
+ android:padding="@dimen/player_main_buttons_padding"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
@@ -411,9 +391,8 @@
android:layout_alignParentBottom="true"
android:gravity="center"
android:orientation="horizontal"
- android:paddingBottom="6dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp">
+ android:paddingLeft="@dimen/player_main_controls_padding"
+ android:paddingRight="@dimen/player_main_controls_padding">
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 07b71e5ee..56928b249 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -24,6 +24,15 @@
2dp
4dp
8dp
+
+
+ 16dp
+ 6dp
+ 4dp
+ 6dp
+ 1dp
+ 40dp
+
180dp
150dp
From a47e6dd8c542eefe88d1ea2bd814ea57f4fe1cdf Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 12 Feb 2020 22:33:23 +0300
Subject: [PATCH 17/40] AppBarLayout scrolling awesomeness, PlayQueue layout
touches interception, player's controls' margin - made scrolling in
appBarLayout awesome - PlayQueue layout was intercepting touches while it was
in GONE visibility state. Now it's not gonna happen - removed margin between
two lines of player's controls - when a user leaves the app with two back
presses the app will not stop MainPlayer service if popup or background
players play
---
.../material/appbar/FlingBehavior.java | 8 +--
.../fragments/detail/VideoDetailFragment.java | 5 +-
.../newpipe/player/VideoPlayerImpl.java | 6 +-
.../event/CustomBottomSheetBehavior.java | 61 +++++++------------
.../activity_main_player.xml | 1 -
.../fragment_video_detail.xml | 41 ++++++-------
.../main/res/layout/activity_main_player.xml | 1 -
.../main/res/layout/fragment_video_detail.xml | 48 ++++++---------
8 files changed, 69 insertions(+), 102 deletions(-)
diff --git a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
index f1038faa1..d8c5fd014 100644
--- a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
+++ b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
@@ -23,14 +23,14 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
}
private boolean allowScroll = true;
- private Rect playQueueRect = new Rect();
+ private Rect globalRect = new Rect();
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
- ViewGroup playQueue = child.findViewById(R.id.playQueue);
+ ViewGroup playQueue = child.findViewById(R.id.playQueuePanel);
if (playQueue != null) {
- playQueue.getGlobalVisibleRect(playQueueRect);
- if (playQueueRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
+ boolean visible = playQueue.getGlobalVisibleRect(globalRect);
+ if (visible && globalRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
allowScroll = false;
return false;
}
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 ce113a93d..6f65c34dc 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
@@ -403,8 +403,9 @@ public class VideoDetailFragment
public void onDestroy() {
super.onDestroy();
- if (!activity.isFinishing()) unbind();
- else stopService();
+ // Stop the service when user leaves the app with double back press if video player is selected. Otherwise unbind
+ if (activity.isFinishing() && player != null && player.videoPlayerSelected()) stopService();
+ else unbind();
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 72c3b71ee..78f6aa387 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -306,6 +306,7 @@ public class VideoPlayerImpl extends VideoPlayer
playerCloseButton.setVisibility(View.GONE);
getTopControlsRoot().bringToFront();
getBottomControlsRoot().bringToFront();
+ onQueueClosed();
} else {
fullscreenButton.setVisibility(View.GONE);
setupScreenRotationButton(service.isLandscape());
@@ -678,7 +679,10 @@ public class VideoPlayerImpl extends VideoPlayer
private void onQueueClosed() {
animateView(queueLayout, SLIDE_AND_ALPHA, /*visible=*/false,
- DEFAULT_CONTROLS_DURATION);
+ DEFAULT_CONTROLS_DURATION, 0, () -> {
+ // Even when queueLayout is GONE it receives touch events and ruins normal behavior of the app. This line fixes it
+ queueLayout.setTranslationY(-queueLayout.getHeight() * 5);
+ });
queueVisible = false;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
index f0178853f..c7acc0390 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -4,61 +4,46 @@ import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import org.schabi.newpipe.R;
-public class CustomBottomSheetBehavior extends BottomSheetBehavior {
+import java.util.Arrays;
+import java.util.List;
+
+public class CustomBottomSheetBehavior extends BottomSheetBehavior {
public CustomBottomSheetBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
- boolean skippingInterception = false;
+ boolean visible;
+ Rect globalRect = new Rect();
+ private boolean skippingInterception = false;
+ private List skipInterceptionOfElements = Arrays.asList(
+ R.id.detail_content_root_layout, R.id.relatedStreamsLayout, R.id.playQueuePanel, R.id.viewpager);
@Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
- // Behavior of globalVisibleRect is different on different APIs.
- // For example, on API 19 getGlobalVisibleRect returns a filled rect of a collapsed view while on the latest API
- // it returns empty rect in that case. So check visibility with return value too
- boolean visible;
- Rect rect = new Rect();
-
- // Drop folowing when actions ends
+ public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull FrameLayout child, MotionEvent event) {
+ // Drop following when action ends
if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP)
skippingInterception = false;
- // Found that user still swipping, continue folowing
+ // Found that user still swiping, continue following
if (skippingInterception) return false;
- // Without overriding scrolling will not work in detail_content_root_layout
- ViewGroup controls = child.findViewById(R.id.detail_content_root_layout);
- if (controls != null) {
- visible = controls.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
- skippingInterception = true;
- return false;
- }
- }
-
- // Without overriding scrolling will not work on relatedStreamsLayout
- ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout);
- if (relatedStreamsLayout != null) {
- visible = relatedStreamsLayout.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
- skippingInterception = true;
- return false;
- }
- }
-
- ViewGroup playQueue = child.findViewById(R.id.playQueue);
- if (playQueue != null) {
- visible = playQueue.getGlobalVisibleRect(rect);
- if (rect.contains((int) event.getRawX(), (int) event.getRawY()) && visible) {
- skippingInterception = true;
- return false;
+ // Without overriding scrolling will not work when user touches these elements
+ for (Integer element : skipInterceptionOfElements) {
+ ViewGroup viewGroup = child.findViewById(element);
+ if (viewGroup != null) {
+ visible = viewGroup.getGlobalVisibleRect(globalRect);
+ if (visible && globalRect.contains((int) event.getRawX(), (int) event.getRawY())) {
+ skippingInterception = true;
+ return false;
+ }
}
}
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index d434fe1ac..4a4879d7f 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -157,7 +157,6 @@
android:minHeight="50dp"
android:baselineAligned="false"
android:gravity="top"
- android:layout_marginBottom="7dp"
tools:ignore="RtlHardcoded">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
Date: Tue, 25 Feb 2020 02:15:22 +0300
Subject: [PATCH 18/40] Vertical videos in portrait & fullscreen, UI
enhancements for tablets and phones, fixes - vertical videos now work ok in
portrait and fullscreen mode at the same time - auto pause on back press is
disabled for large tablets - large dragable area for swipe to bottom in
fullscreen mode in place of top controls - appbar will be scrolled to top
when entering in fullscreen mode
---
.../fragments/detail/VideoDetailFragment.java | 11 +--
.../newpipe/player/VideoPlayerImpl.java | 75 ++++++++++++++-----
.../player/event/PlayerGestureListener.java | 5 +-
.../activity_main_player.xml | 44 +++++------
.../main/res/layout/activity_main_player.xml | 44 +++++------
5 files changed, 112 insertions(+), 67 deletions(-)
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 6f65c34dc..a946e3f99 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
@@ -870,7 +870,7 @@ public class VideoDetailFragment
// If we are in fullscreen mode just exit from it via first back press
if (player != null && player.isInFullscreen()) {
- player.onPause();
+ if (!PlayerHelper.isTablet(activity)) player.onPause();
restoreDefaultOrientation();
setAutoplay(false);
return true;
@@ -1699,6 +1699,7 @@ public class VideoDetailFragment
if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) return;
currentInfo = info;
+ setInitialData(info.getServiceId(), info.getUrl(),info.getName(), playQueue);
setAutoplay(false);
prepareAndHandleInfo(info, true);
}
@@ -1736,6 +1737,7 @@ public class VideoDetailFragment
}
if (relatedStreamsLayout != null) relatedStreamsLayout.setVisibility(fullscreen ? View.GONE : View.VISIBLE);
+ scrollToTop();
addVideoPlayerView();
}
@@ -1842,12 +1844,10 @@ public class VideoDetailFragment
if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null)
setAutoplay(true);
+ player.checkLandscape();
boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
// Let's give a user time to look at video information page if video is not playing
- if (player.isPlaying()) {
- player.checkLandscape();
- } else if (orientationLocked) {
- player.checkLandscape();
+ if (orientationLocked && !player.isPlaying()) {
player.onPlay();
player.showControlsThenHide();
}
@@ -1927,6 +1927,7 @@ public class VideoDetailFragment
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
setOverlayElementsClickable(true);
+ if (player != null) player.onQueueClosed();
break;
case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_SETTLING:
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 78f6aa387..ec490451b 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -74,9 +74,11 @@ import org.schabi.newpipe.util.*;
import java.util.List;
import static android.content.Context.WINDOW_SERVICE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static org.schabi.newpipe.player.MainPlayer.*;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
+import static org.schabi.newpipe.player.helper.PlayerHelper.isTablet;
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;
@@ -137,6 +139,7 @@ public class VideoPlayerImpl extends VideoPlayer
private boolean audioOnly = false;
private boolean isFullscreen = false;
+ private boolean isVerticalVideo = false;
boolean shouldUpdateOnProgress;
private MainPlayer service;
@@ -305,11 +308,13 @@ public class VideoPlayerImpl extends VideoPlayer
openInBrowser.setVisibility(View.GONE);
playerCloseButton.setVisibility(View.GONE);
getTopControlsRoot().bringToFront();
+ getTopControlsRoot().setClickable(false);
+ getTopControlsRoot().setFocusable(false);
getBottomControlsRoot().bringToFront();
onQueueClosed();
} else {
fullscreenButton.setVisibility(View.GONE);
- setupScreenRotationButton(service.isLandscape());
+ setupScreenRotationButton();
getResizeView().setVisibility(View.VISIBLE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE);
@@ -323,6 +328,10 @@ public class VideoPlayerImpl extends VideoPlayer
defaultPreferences.getBoolean(service.getString(R.string.show_play_with_kodi_key), false) ? View.VISIBLE : View.GONE);
openInBrowser.setVisibility(View.VISIBLE);
playerCloseButton.setVisibility(isFullscreen ? View.GONE : View.VISIBLE);
+ // Top controls have a large minHeight which is allows to drag the player down in fullscreen mode (just larger area
+ // to make easy to locate by finger)
+ getTopControlsRoot().setClickable(true);
+ getTopControlsRoot().setFocusable(true);
}
if (!isInFullscreen()) {
titleTextView.setVisibility(View.GONE);
@@ -393,7 +402,7 @@ public class VideoPlayerImpl extends VideoPlayer
settingsContentObserver = new ContentObserver(new Handler()) {
@Override
- public void onChange(boolean selfChange) { setupScreenRotationButton(service.isLandscape()); }
+ public void onChange(boolean selfChange) { setupScreenRotationButton(); }
};
service.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
@@ -442,6 +451,14 @@ public class VideoPlayerImpl extends VideoPlayer
setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
}
+ @Override
+ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
+ super.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
+ isVerticalVideo = width < height;
+ prepareOrientation();
+ setupScreenRotationButton();
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@@ -597,7 +614,7 @@ public class VideoPlayerImpl extends VideoPlayer
channelTextView.setVisibility(View.VISIBLE);
playerCloseButton.setVisibility(View.GONE);
}
- setupScreenRotationButton(isInFullscreen());
+ setupScreenRotationButton();
}
@Override
@@ -637,7 +654,8 @@ public class VideoPlayerImpl extends VideoPlayer
toggleFullscreen();
} else if (v.getId() == screenRotationButton.getId()) {
- fragmentListener.onScreenRotationButtonClicked();
+ if (!isVerticalVideo) fragmentListener.onScreenRotationButtonClicked();
+ else toggleFullscreen();
} else if (v.getId() == playerCloseButton.getId()) {
service.sendBroadcast(new Intent(VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER));
@@ -667,6 +685,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void onQueueClicked() {
queueVisible = true;
+ hideSystemUIIfNeeded();
buildQueue();
updatePlaybackButtons();
@@ -677,7 +696,9 @@ public class VideoPlayerImpl extends VideoPlayer
itemsList.scrollToPosition(playQueue.getIndex());
}
- private void onQueueClosed() {
+ public void onQueueClosed() {
+ if (!queueVisible) return;
+
animateView(queueLayout, SLIDE_AND_ALPHA, /*visible=*/false,
DEFAULT_CONTROLS_DURATION, 0, () -> {
// Even when queueLayout is GONE it receives touch events and ruins normal behavior of the app. This line fixes it
@@ -733,11 +754,18 @@ public class VideoPlayerImpl extends VideoPlayer
builder.create().show();
}
- private void setupScreenRotationButton(boolean landscape) {
+ private void setupScreenRotationButton() {
boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
- screenRotationButton.setVisibility(orientationLocked && videoPlayerSelected() ? View.VISIBLE : View.GONE);
+ boolean showButton = (orientationLocked || isVerticalVideo || isTablet(service)) && videoPlayerSelected();
+ screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(service.getResources().getDrawable(
- landscape ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
+ isInFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
+ }
+
+ private void prepareOrientation() {
+ boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
+ if (orientationLocked && isInFullscreen() && service.isLandscape() == isVerticalVideo && fragmentListener != null)
+ fragmentListener.onScreenRotationButtonClicked();
}
@Override
@@ -779,7 +807,7 @@ public class VideoPlayerImpl extends VideoPlayer
brightnessProgressBar.setMax(maxGestureLength);
setInitialGestureValues();
- queueLayout.getLayoutParams().height = min - queueLayout.getTop();
+ queueLayout.getLayoutParams().height = height - queueLayout.getTop();
if (popupPlayerSelected()) {
float widthDp = Math.abs(r - l) / service.getResources().getDisplayMetrics().density;
@@ -1009,7 +1037,16 @@ public class VideoPlayerImpl extends VideoPlayer
onRepeatClicked();
break;
case Intent.ACTION_CONFIGURATION_CHANGED:
- setControlsSize();
+ // The only situation I need to re-calculate elements sizes is when a user rotates a device from landscape to landscape
+ // because in that case the controls should be aligned to another side of a screen. The problem is when user leaves
+ // the app and returns back (while the app in landscape) Android reports via DisplayMetrics that orientation is
+ // portrait and it gives wrong sizes calculations. Let's skip re-calculation in every case but landscape
+ boolean reportedOrientationIsLandscape = service.isLandscape();
+ boolean actualOrientationIsLandscape = context.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ if (reportedOrientationIsLandscape && actualOrientationIsLandscape) setControlsSize();
+ // Close it because when changing orientation from portrait (in fullscreen mode) the size of queue layout can be
+ // larger than the screen size
+ onQueueClosed();
break;
case Intent.ACTION_SCREEN_ON:
shouldUpdateOnProgress = true;
@@ -1201,7 +1238,7 @@ public class VideoPlayerImpl extends VideoPlayer
// It doesn't include NavigationBar, notches, etc.
display.getSize(size);
- int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT;
+ int width = isFullscreen ? (service.isLandscape() ? size.x : size.y) : ViewGroup.LayoutParams.MATCH_PARENT;
int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
getTopControlsRoot().getLayoutParams().width = width;
@@ -1218,10 +1255,11 @@ public class VideoPlayerImpl extends VideoPlayer
bottomParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START);
getBottomControlsRoot().requestLayout();
- ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackControlRoot);
- // In tablet navigationBar located at the bottom of the screen. And the only situation when we need to set custom height is
- // in fullscreen mode in tablet in non-multiWindow mode. Other than that MATCH_PARENT is good
- controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && PlayerHelper.isTablet(service)
+ ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
+ // In tablet navigationBar located at the bottom of the screen. And the situations when we need to set custom height is
+ // in fullscreen mode in tablet in non-multiWindow mode or with vertical video. Other than that MATCH_PARENT is good
+ boolean navBarAtTheBottom = PlayerHelper.isTablet(service) || !service.isLandscape();
+ controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && navBarAtTheBottom
? size.y
: ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
@@ -1264,9 +1302,12 @@ public class VideoPlayerImpl extends VideoPlayer
public void checkLandscape() {
AppCompatActivity parent = getParentActivity();
- if (parent != null && service.isLandscape() != isInFullscreen()
- && getCurrentState() != STATE_COMPLETED && videoPlayerSelected() && !audioOnly)
+ boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isInFullscreen() && videoPlayerSelected() && !audioOnly;
+ boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED;
+ if (parent != null && videoInLandscapeButNotInFullscreen && playingState)
toggleFullscreen();
+
+ setControlsSize();
}
private void buildQueue() {
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 db7d8d615..bb9ede4d7 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
@@ -160,9 +160,8 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
isMovingInMain = true;
- boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
- boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2;
- boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea;
+ boolean acceptVolumeArea = initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0;
+ boolean acceptBrightnessArea = initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0;
if (isVolumeGestureEnabled && acceptVolumeArea) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 4a4879d7f..d87930371 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -12,7 +12,7 @@
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_centerHorizontal="true"/>
+ android:layout_centerInParent="true"/>
+
+
-
-
-
+
+ android:layout_centerInParent="true"/>
+
+
-
-
-
+
Date: Sat, 29 Feb 2020 02:57:54 +0300
Subject: [PATCH 19/40] Better implementation of old code
---
.../newpipe/fragments/detail/StackItem.java | 6 +-
.../fragments/detail/VideoDetailFragment.java | 66 +++++++------------
.../org/schabi/newpipe/player/MainPlayer.java | 1 +
.../newpipe/player/VideoPlayerImpl.java | 30 +++++++--
.../event/PlayerServiceEventListener.java | 4 +-
5 files changed, 55 insertions(+), 52 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
index 2e90a4fc9..38e41b2fb 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
@@ -8,7 +8,7 @@ class StackItem implements Serializable {
private final int serviceId;
private String title;
private String url;
- private final PlayQueue playQueue;
+ private PlayQueue playQueue;
StackItem(int serviceId, String url, String title, PlayQueue playQueue) {
this.serviceId = serviceId;
@@ -25,6 +25,10 @@ class StackItem implements Serializable {
this.url = url;
}
+ public void setPlayQueue(PlayQueue queue) {
+ this.playQueue = queue;
+ }
+
public int getServiceId() {
return serviceId;
}
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 a946e3f99..efb997d50 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
@@ -99,8 +99,6 @@ public class VideoDetailFragment
PlayerServiceEventListener {
public static final String AUTO_PLAY = "auto_play";
- private boolean isFragmentStopped;
-
private int updateFlags = 0;
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
@@ -109,8 +107,10 @@ public class VideoDetailFragment
private static final float MAX_OVERLAY_ALPHA = 0.9f;
private static final float MAX_PLAYER_HEIGHT = 0.7f;
- public static final String ACTION_SHOW_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
- public static final String ACTION_HIDE_MAIN_PLAYER = "org.schabi.newpipe.fragments.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
+ public static final String ACTION_SHOW_MAIN_PLAYER = "org.schabi.newpipe.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
+ public static final String ACTION_HIDE_MAIN_PLAYER = "org.schabi.newpipe.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
+ public static final String ACTION_VIDEO_FRAGMENT_RESUMED = "org.schabi.newpipe.VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED";
+ public static final String ACTION_VIDEO_FRAGMENT_STOPPED = "org.schabi.newpipe.VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED";
private boolean showRelatedStreams;
private boolean showComments;
@@ -233,15 +233,13 @@ public class VideoDetailFragment
startPlayerListener();
// It will do nothing if the player is not in fullscreen mode
- hideSystemUIIfNeeded();
+ hideSystemUiIfNeeded();
if (!player.videoPlayerSelected() && !playAfterConnect) return;
- // STATE_IDLE means the player is stopped
- if (player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE) addVideoPlayerView();
+ if (playerIsNotStopped()) addVideoPlayerView();
// If the video is playing but orientation changed let's make the video in fullscreen again
-
if (isLandscape()) checkLandscape();
else if (player.isInFullscreen()) player.toggleFullscreen();
@@ -363,7 +361,7 @@ public class VideoDetailFragment
public void onResume() {
super.onResume();
- isFragmentStopped = false;
+ activity.sendBroadcast(new Intent(ACTION_VIDEO_FRAGMENT_RESUMED));
setupBrightness(false);
@@ -383,20 +381,16 @@ public class VideoDetailFragment
}
// Check if it was loading when the fragment was stopped/paused
- if (wasLoading.getAndSet(false) && !wasCleared()) {
+ if (wasLoading.getAndSet(false) && !wasCleared())
selectAndLoadVideo(serviceId, url, name, playQueue);
- } else if (currentInfo != null) {
- updateProgressInfo(currentInfo);
- }
-
- if (player != null && player.videoPlayerSelected()) addVideoPlayerView();
}
@Override
public void onStop() {
super.onStop();
- isFragmentStopped = true;
+ if (!activity.isChangingConfigurations())
+ activity.sendBroadcast(new Intent(ACTION_VIDEO_FRAGMENT_STOPPED));
}
@Override
@@ -546,7 +540,7 @@ public class VideoDetailFragment
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
break;
case R.id.overlay_play_pause_button:
- if (player != null && player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE) {
+ if (playerIsNotStopped()) {
player.onPlayPause();
player.hideControls(0,0);
showSystemUi();
@@ -1632,8 +1626,14 @@ public class VideoDetailFragment
playQueue = queue;
// This should be the only place where we push data to stack. It will allow to have live instance of PlayQueue with actual
// information about deleted/added items inside Channel/Playlist queue and makes possible to have a history of played items
- if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue))
+ if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)) {
stack.push(new StackItem(serviceId, url, name, playQueue));
+ } else if (stack.peek().getPlayQueue().equals(queue)) {
+ // On every MainPlayer service's destroy() playQueue gets disposed and no longer able to track progress.
+ // That's why we update our cached disposed queue with the new one that is active and have the same history
+ // Without that the cached playQueue will have an old recovery position
+ stack.peek().setPlayQueue(queue);
+ }
if (DEBUG) {
Log.d(TAG, "onQueueUpdate() called with: serviceId = ["
@@ -1668,21 +1668,6 @@ public class VideoDetailFragment
if (player.getPlayQueue().getItem().getUrl().equals(url))
showPlaybackProgress(currentProgress, duration);
-
- // We don't want to interrupt playback and don't want to see notification if player is stopped
- // since next lines of code will enable background playback if needed
- if (!player.videoPlayerSelected()) return;
-
- // This will be called when user goes to another app
- if (isFragmentStopped) {
- // Video enabled. Let's think what to do with source in background
- if (player.backgroundPlaybackEnabled())
- player.useVideoSource(false);
- else if (player.minimizeOnPopupEnabled())
- NavigationHelper.playOnPopupPlayer(activity, playQueue, true);
- else player.onPause();
- }
- else player.useVideoSource(true);
}
@Override
@@ -1731,7 +1716,7 @@ public class VideoDetailFragment
if (parent == null) return;
if (fullscreen) {
- hideSystemUIIfNeeded();
+ hideSystemUiIfNeeded();
} else {
showSystemUi();
}
@@ -1776,11 +1761,6 @@ public class VideoDetailFragment
valueAnimator.start();
}
- @Override
- public boolean isFragmentStopped() {
- return isFragmentStopped;
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Player related utils
//////////////////////////////////////////////////////////////////////////*/
@@ -1811,11 +1791,15 @@ public class VideoDetailFragment
}
// Listener implementation
- public void hideSystemUIIfNeeded() {
+ public void hideSystemUiIfNeeded() {
if (player != null && player.isInFullscreen() && bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
hideSystemUi();
}
+ private boolean playerIsNotStopped() {
+ return player != null && player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
+ }
+
private void setupBrightness(boolean save) {
if (activity == null) return;
@@ -1916,7 +1900,7 @@ public class VideoDetailFragment
bottomSheetBehavior.setPeekHeight(peekHeight);
// Disable click because overlay buttons located on top of buttons from the player
setOverlayElementsClickable(false);
- hideSystemUIIfNeeded();
+ hideSystemUiIfNeeded();
boolean needToExpand = isLandscape()
&& player != null
&& player.isPlaying()
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 771d6d7e1..61b69ac5e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -177,6 +177,7 @@ public final class MainPlayer extends Service {
if (playerImpl != null) {
removeViewFromParent();
+ playerImpl.setRecovery();
playerImpl.savePlaybackState();
playerImpl.stopActivityBinding();
playerImpl.removePopupFromView();
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index ec490451b..4aa10fe9a 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -999,6 +999,9 @@ public class VideoPlayerImpl extends VideoPlayer
intentFilter.addAction(ACTION_FAST_REWIND);
intentFilter.addAction(ACTION_FAST_FORWARD);
+ intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED);
+ intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED);
+
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -1036,6 +1039,24 @@ public class VideoPlayerImpl extends VideoPlayer
case ACTION_REPEAT:
onRepeatClicked();
break;
+ case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED:
+ useVideoSource(true);
+ break;
+ case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED:
+ // This will be called when user goes to another app
+ // We don't want to interrupt playback and don't want to see notification if player is stopped
+ // since next lines of code will enable background playback if needed
+ if (videoPlayerSelected() && isPlaying()) {
+ if (backgroundPlaybackEnabled()) {
+ useVideoSource(false);
+ } else if (minimizeOnPopupEnabled()) {
+ setRecovery();
+ NavigationHelper.playOnPopupPlayer(getParentActivity(), playQueue, true);
+ } else {
+ onPause();
+ }
+ }
+ break;
case Intent.ACTION_CONFIGURATION_CHANGED:
// The only situation I need to re-calculate elements sizes is when a user rotates a device from landscape to landscape
// because in that case the controls should be aligned to another side of a screen. The problem is when user leaves
@@ -1223,7 +1244,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void hideSystemUIIfNeeded() {
if (fragmentListener != null)
- fragmentListener.hideSystemUIIfNeeded();
+ fragmentListener.hideSystemUiIfNeeded();
}
/**
@@ -1327,12 +1348,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
public void useVideoSource(boolean video) {
- // Return when: old value of audioOnly equals to the new value, audio player is selected,
- // video player is selected AND fragment is not shown
- if (playQueue == null
- || audioOnly == !video
- || audioPlayerSelected()
- || (video && videoPlayerSelected() && fragmentListener.isFragmentStopped()))
+ if (playQueue == null || audioOnly == !video || audioPlayerSelected())
return;
audioOnly = !video;
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
index eeff08b5c..69e23f8fb 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java
@@ -11,7 +11,5 @@ public interface PlayerServiceEventListener extends PlayerEventListener {
void onPlayerError(ExoPlaybackException error);
- boolean isFragmentStopped();
-
- void hideSystemUIIfNeeded();
+ void hideSystemUiIfNeeded();
}
\ No newline at end of file
From d87e488c236ed576675f25c3d27d815cab0e31f7 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 29 Feb 2020 22:13:07 +0300
Subject: [PATCH 20/40] Fix for a ripple effect on a button
---
.../java/org/schabi/newpipe/player/VideoPlayerImpl.java | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 4aa10fe9a..d026ff8cf 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -320,7 +320,7 @@ public class VideoPlayerImpl extends VideoPlayer
moreOptionsButton.setVisibility(View.VISIBLE);
getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
- secondaryControls.setVisibility(View.GONE);
+ secondaryControls.setVisibility(View.INVISIBLE);
moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
R.drawable.ic_expand_more_white_24dp));
shareButton.setVisibility(View.VISIBLE);
@@ -715,7 +715,12 @@ public class VideoPlayerImpl extends VideoPlayer
animateRotation(moreOptionsButton, DEFAULT_CONTROLS_DURATION,
isMoreControlsVisible ? 0 : 180);
animateView(secondaryControls, SLIDE_AND_ALPHA, !isMoreControlsVisible,
- DEFAULT_CONTROLS_DURATION);
+ DEFAULT_CONTROLS_DURATION, 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
+ if (isMoreControlsVisible) secondaryControls.setVisibility(View.INVISIBLE);
+ });
showControls(DEFAULT_CONTROLS_DURATION);
}
From 398cbe9284a10a2cf7fa9c7524edc89c26014a48 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Tue, 10 Mar 2020 12:06:38 +0300
Subject: [PATCH 21/40] Better backstack, better tablet support, switching
players confirmation, fix for background playback
---
.../fragments/detail/VideoDetailFragment.java | 118 ++++++++++++++----
.../newpipe/player/BackgroundPlayer.java | 2 +-
.../org/schabi/newpipe/player/BasePlayer.java | 4 +
.../newpipe/player/PopupVideoPlayer.java | 2 +-
.../newpipe/player/ServicePlayerActivity.java | 2 +-
.../newpipe/player/VideoPlayerImpl.java | 26 ++--
.../event/CustomBottomSheetBehavior.java | 19 +--
.../player/event/PlayerEventListener.java | 2 +-
.../newpipe/player/helper/PlayerHelper.java | 4 +
app/src/main/res/values/settings_keys.xml | 1 +
app/src/main/res/values/strings.xml | 3 +
app/src/main/res/xml/video_audio_settings.xml | 8 ++
12 files changed, 140 insertions(+), 51 deletions(-)
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 efb997d50..0b7ffddef 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
@@ -18,6 +18,7 @@ import android.widget.*;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
@@ -74,6 +75,7 @@ import org.schabi.newpipe.views.AnimatedProgressBar;
import java.io.Serializable;
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -86,6 +88,7 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
+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;
@@ -237,7 +240,7 @@ public class VideoDetailFragment
if (!player.videoPlayerSelected() && !playAfterConnect) return;
- if (playerIsNotStopped()) addVideoPlayerView();
+ if (playerIsNotStopped() && player.videoPlayerSelected()) addVideoPlayerView();
// If the video is playing but orientation changed let's make the video in fullscreen again
if (isLandscape()) checkLandscape();
@@ -382,7 +385,7 @@ public class VideoDetailFragment
// Check if it was loading when the fragment was stopped/paused
if (wasLoading.getAndSet(false) && !wasCleared())
- selectAndLoadVideo(serviceId, url, name, playQueue);
+ startLoading(false);
}
@Override
@@ -870,16 +873,10 @@ public class VideoDetailFragment
return true;
}
- StackItem currentPeek = stack.peek();
- if (currentPeek != null && !currentPeek.getPlayQueue().equals(playQueue)) {
- // When user selected a stream but didn't start playback this stream will not be added to backStack.
- // Then he press Back and the last saved item from history will show up
- setupFromHistoryItem(currentPeek);
- return true;
- }
-
// If we have something in history of played items we replay it here
- boolean isPreviousCanBePlayed = player != null && player.getPlayQueue() != null && player.videoPlayerSelected()
+ boolean isPreviousCanBePlayed = player != null
+ && player.getPlayQueue() != null
+ && player.videoPlayerSelected()
&& player.getPlayQueue().previous();
if (isPreviousCanBePlayed) {
return true;
@@ -903,11 +900,15 @@ public class VideoDetailFragment
setAutoplay(false);
hideMainPlayer();
- selectAndLoadVideo(
+ setInitialData(
item.getServiceId(),
item.getUrl(),
!TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "",
item.getPlayQueue());
+ startLoading(false);
+
+ // Maybe an item was deleted in background activity
+ if (item.getPlayQueue().getItem() == null) return;
PlayQueueItem playQueueItem = item.getPlayQueue().getItem();
// Update title, url, uploader from the last item in the stack (it's current now)
@@ -936,7 +937,7 @@ public class VideoDetailFragment
return;
}
setInitialData(serviceId, videoUrl, name, playQueue);
- startLoading(false);
+ startLoading(false, true);
}
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
@@ -965,6 +966,20 @@ public class VideoDetailFragment
currentInfo = null;
if (currentWorker != null) currentWorker.dispose();
+ runWorker(forceLoad, stack.isEmpty());
+ }
+
+ private void startLoading(boolean forceLoad, boolean addToBackStack) {
+ super.startLoading(false);
+
+ initTabs();
+ currentInfo = null;
+ if (currentWorker != null) currentWorker.dispose();
+
+ runWorker(forceLoad, addToBackStack);
+ }
+
+ private void runWorker(boolean forceLoad, boolean addToBackStack) {
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -973,12 +988,15 @@ public class VideoDetailFragment
hideMainPlayer();
handleResult(result);
showContent();
+ if (addToBackStack) {
+ if (playQueue == null) playQueue = new SinglePlayQueue(result);
+ stack.push(new StackItem(serviceId, url, name, playQueue));
+ }
if (isAutoplayEnabled()) openVideoPlayer();
}, (@NonNull Throwable throwable) -> {
isLoading.set(false);
onError(throwable);
});
-
}
private void initTabs() {
@@ -1063,7 +1081,9 @@ public class VideoDetailFragment
if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
} else {
- NavigationHelper.playOnPopupPlayer(activity, queue, true);
+ Runnable onAllow = () -> NavigationHelper.playOnPopupPlayer(activity, queue, true);
+ if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
+ else onAllow.run();
}
}
@@ -1073,7 +1093,9 @@ public class VideoDetailFragment
VideoStream selectedVideoStream = getSelectedVideoStream();
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
- openNormalPlayer();
+ Runnable onAllow = this::openNormalPlayer;
+ if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
+ else onAllow.run();
}
}
@@ -1085,7 +1107,9 @@ public class VideoDetailFragment
if (append) {
NavigationHelper.enqueueOnBackgroundPlayer(activity, queue, false);
} else {
- NavigationHelper.playOnBackgroundPlayer(activity, queue, true);
+ Runnable onAllow = () -> NavigationHelper.playOnBackgroundPlayer(activity, queue, true);
+ if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
+ else onAllow.run();
}
}
@@ -1579,7 +1603,7 @@ public class VideoDetailFragment
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
final boolean showPlaybackPosition = prefs.getBoolean(
activity.getString(R.string.enable_playback_state_lists_key), true);
- if (!playbackResumeEnabled || info.getDuration() <= 0) {
+ if (!playbackResumeEnabled) {
if (playQueue == null || playQueue.getStreams().isEmpty()
|| playQueue.getItem().getRecoveryPosition() == RECOVERY_UNSET || !showPlaybackPosition) {
positionView.setVisibility(View.INVISIBLE);
@@ -1605,7 +1629,8 @@ public class VideoDetailFragment
}, e -> {
if (DEBUG) e.printStackTrace();
}, () -> {
- // OnComplete, do nothing
+ animateView(positionView, false, 0);
+ animateView(detailPositionView, false, 0);
});
}
@@ -1615,6 +1640,10 @@ public class VideoDetailFragment
positionView.setMax(durationSeconds);
positionView.setProgress(progressSeconds);
detailPositionView.setText(Localization.getDurationString(progressSeconds));
+ if (positionView.getVisibility() != View.VISIBLE) {
+ animateView(positionView, true, 100);
+ animateView(detailPositionView, true, 100);
+ }
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1628,11 +1657,11 @@ public class VideoDetailFragment
// information about deleted/added items inside Channel/Playlist queue and makes possible to have a history of played items
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)) {
stack.push(new StackItem(serviceId, url, name, playQueue));
- } else if (stack.peek().getPlayQueue().equals(queue)) {
+ } else if (findQueueInStack(queue) != null) {
// On every MainPlayer service's destroy() playQueue gets disposed and no longer able to track progress.
// That's why we update our cached disposed queue with the new one that is active and have the same history
// Without that the cached playQueue will have an old recovery position
- stack.peek().setPlayQueue(queue);
+ findQueueInStack(queue).setPlayQueue(queue);
}
if (DEBUG) {
@@ -1671,20 +1700,23 @@ public class VideoDetailFragment
}
@Override
- public void onMetadataUpdate(StreamInfo info) {
- if (!stack.isEmpty()) {
+ public void onMetadataUpdate(StreamInfo info, PlayQueue queue) {
+ StackItem item = findQueueInStack(queue);
+ if (item != null) {
// When PlayQueue can have multiple streams (PlaylistPlayQueue or ChannelPlayQueue) every new played stream gives
// new title and url. StackItem contains information about first played stream. Let's update it here
- StackItem peek = stack.peek();
- peek.setTitle(info.getName());
- peek.setUrl(info.getUrl());
+ item.setTitle(info.getName());
+ item.setUrl(info.getUrl());
}
+ // They are not equal when user watches something in popup while browsing in fragment and then changes screen orientation
+ // In that case the fragment will set itself as a service listener and will receive initial call to onMetadataUpdate()
+ if (!queue.equals(playQueue)) return;
updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) return;
currentInfo = info;
- setInitialData(info.getServiceId(), info.getUrl(),info.getName(), playQueue);
+ setInitialData(info.getServiceId(), info.getUrl(),info.getName(), queue);
setAutoplay(false);
prepareAndHandleInfo(info, true);
}
@@ -1852,6 +1884,37 @@ public class VideoDetailFragment
return url == null;
}
+ private StackItem findQueueInStack(PlayQueue queue) {
+ StackItem item = null;
+ Iterator iterator = stack.descendingIterator();
+ while (iterator.hasNext()) {
+ StackItem next = iterator.next();
+ if (next.getPlayQueue().equals(queue)) {
+ item = next;
+ break;
+ }
+ }
+ return item;
+ }
+
+ private boolean shouldAskBeforeClearingQueue() {
+ PlayQueue activeQueue = player != null ? player.getPlayQueue() : null;
+ // Player will have STATE_IDLE when a user pressed back button
+ return isClearingQueueConfirmationRequired(activity) && playerIsNotStopped()
+ && activeQueue != null && !activeQueue.equals(playQueue) && activeQueue.getStreams().size() > 1;
+ }
+
+ private void showClearingQueueConfirmation(Runnable onAllow) {
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.confirm_prompt)
+ .setMessage(R.string.clear_queue_confirmation_description)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.yes, (dialog, which) -> {
+ onAllow.run();
+ dialog.dismiss();
+ }).show();
+ }
+
/*
* Remove unneeded information while waiting for a next task
* */
@@ -1905,6 +1968,7 @@ public class VideoDetailFragment
&& player != null
&& player.isPlaying()
&& !player.isInFullscreen()
+ && !PlayerHelper.isTablet(activity)
&& player.videoPlayerSelected();
if (needToExpand) player.toggleFullscreen();
break;
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
index ab07ded22..e408f49f6 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
@@ -451,7 +451,7 @@ public final class BackgroundPlayer extends Service {
private void updateMetadata() {
if (activityListener != null && getCurrentMetadata() != null) {
- activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata(), playQueue);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 815cbb8ab..490419c64 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -1236,6 +1236,10 @@ public abstract class BasePlayer implements
return simpleExoPlayer != null && simpleExoPlayer.isPlaying();
}
+ public boolean isLoading() {
+ return simpleExoPlayer != null && simpleExoPlayer.isLoading();
+ }
+
@Player.RepeatMode
public int getRepeatMode() {
return simpleExoPlayer == null
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 0a15e7169..22681d9ce 100644
--- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
@@ -673,7 +673,7 @@ public final class PopupVideoPlayer extends Service {
private void updateMetadata() {
if (activityListener != null && getCurrentMetadata() != null) {
- activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata(), playQueue);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
index 1c449c77e..619200727 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
@@ -596,7 +596,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
}
@Override
- public void onMetadataUpdate(StreamInfo info) {
+ public void onMetadataUpdate(StreamInfo info, PlayQueue queue) {
if (info != null) {
metadataTitle.setText(info.getName());
metadataArtist.setText(info.getUploaderName());
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index d026ff8cf..efbe06457 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -761,7 +761,8 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupScreenRotationButton() {
boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
- boolean showButton = (orientationLocked || isVerticalVideo || isTablet(service)) && videoPlayerSelected();
+ boolean tabletInLandscape = isTablet(service) && service.isLandscape();
+ boolean showButton = videoPlayerSelected() && (orientationLocked || isVerticalVideo || tabletInLandscape);
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(service.getResources().getDrawable(
isInFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
@@ -1048,10 +1049,10 @@ public class VideoPlayerImpl extends VideoPlayer
useVideoSource(true);
break;
case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED:
- // This will be called when user goes to another app
- // We don't want to interrupt playback and don't want to see notification if player is stopped
- // since next lines of code will enable background playback if needed
- if (videoPlayerSelected() && isPlaying()) {
+ // This will be called when user goes to another app/activity, turns off a screen.
+ // We don't want to interrupt playback and don't want to see notification if player is stopped.
+ // Next lines of code will enable background playback if needed
+ if (videoPlayerSelected() && (isPlaying() || isLoading())) {
if (backgroundPlaybackEnabled()) {
useVideoSource(false);
} else if (minimizeOnPopupEnabled()) {
@@ -1076,14 +1077,15 @@ public class VideoPlayerImpl extends VideoPlayer
break;
case Intent.ACTION_SCREEN_ON:
shouldUpdateOnProgress = true;
- // Interrupt playback only when screen turns on and user is watching video in fragment
- if (backgroundPlaybackEnabled() && getPlayer() != null && (isPlaying() || getPlayer().isLoading()))
+ // Interrupt playback only when screen turns on and user is watching video in popup player
+ // Same action for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED event
+ if (backgroundPlaybackEnabled() && popupPlayerSelected() && (isPlaying() || isLoading()))
useVideoSource(true);
break;
case Intent.ACTION_SCREEN_OFF:
shouldUpdateOnProgress = false;
- // Interrupt playback only when screen turns off with video working
- if (backgroundPlaybackEnabled() && getPlayer() != null && (isPlaying() || getPlayer().isLoading()))
+ // Interrupt playback only when screen turns off with popup player working
+ if (backgroundPlaybackEnabled() && popupPlayerSelected() && (isPlaying() || isLoading()))
useVideoSource(false);
break;
}
@@ -1330,7 +1332,7 @@ public class VideoPlayerImpl extends VideoPlayer
AppCompatActivity parent = getParentActivity();
boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isInFullscreen() && videoPlayerSelected() && !audioOnly;
boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED;
- if (parent != null && videoInLandscapeButNotInFullscreen && playingState)
+ if (parent != null && videoInLandscapeButNotInFullscreen && playingState && !PlayerHelper.isTablet(service))
toggleFullscreen();
setControlsSize();
@@ -1695,10 +1697,10 @@ public class VideoPlayerImpl extends VideoPlayer
private void updateMetadata() {
if (fragmentListener != null && getCurrentMetadata() != null) {
- fragmentListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ fragmentListener.onMetadataUpdate(getCurrentMetadata().getMetadata(), playQueue);
}
if (activityListener != null && getCurrentMetadata() != null) {
- activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
+ activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata(), playQueue);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
index c7acc0390..9d4707666 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -35,14 +35,17 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior
// Found that user still swiping, continue following
if (skippingInterception) return false;
- // Without overriding scrolling will not work when user touches these elements
- for (Integer element : skipInterceptionOfElements) {
- ViewGroup viewGroup = child.findViewById(element);
- if (viewGroup != null) {
- visible = viewGroup.getGlobalVisibleRect(globalRect);
- if (visible && globalRect.contains((int) event.getRawX(), (int) event.getRawY())) {
- skippingInterception = true;
- return false;
+ // Don't need to do anything if bottomSheet isn't expanded
+ if (getState() == BottomSheetBehavior.STATE_EXPANDED) {
+ // Without overriding scrolling will not work when user touches these elements
+ for (Integer element : skipInterceptionOfElements) {
+ ViewGroup viewGroup = child.findViewById(element);
+ if (viewGroup != null) {
+ visible = viewGroup.getGlobalVisibleRect(globalRect);
+ if (visible && globalRect.contains((int) event.getRawX(), (int) event.getRawY())) {
+ skippingInterception = true;
+ return false;
+ }
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
index 37ad9798f..8741f539f 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
@@ -10,6 +10,6 @@ public interface PlayerEventListener {
void onQueueUpdate(PlayQueue queue);
void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters);
void onProgressUpdate(int currentProgress, int duration, int bufferPercent);
- void onMetadataUpdate(StreamInfo info);
+ void onMetadataUpdate(StreamInfo info, PlayQueue queue);
void onServiceStopped();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index ae1cac382..6afb5a322 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -200,6 +200,10 @@ public class PlayerHelper {
return isAutoQueueEnabled(context, false);
}
+ public static boolean isClearingQueueConfirmationRequired(@NonNull final Context context) {
+ return getPreferences(context).getBoolean(context.getString(R.string.clear_queue_confirmation_key), false);
+ }
+
@MinimizeMode
public static int getMinimizeOnExitAction(@NonNull final Context context) {
final String defaultAction = context.getString(R.string.minimize_on_exit_none_key);
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index 404397450..f2e957a71 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -27,6 +27,7 @@
auto_queue_key
screen_brightness_key
screen_brightness_timestamp_key
+ clear_queue_confirmation_key
seek_duration
10000
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3a575ae25..388897ffd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -71,6 +71,9 @@
Use fast inexact seek
Inexact seek allows the player to seek to positions faster with reduced precision
Fast-forward/-rewind seek duration
+ Ask confirmation before clearing a queue
+ After switching from one player to another your queue may be replaced
+ Queue from the active player will be replaced
Load thumbnails
Show comments
Disable to stop showing comments
diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml
index 447fa9018..88dc071d0 100644
--- a/app/src/main/res/xml/video_audio_settings.xml
+++ b/app/src/main/res/xml/video_audio_settings.xml
@@ -164,5 +164,13 @@
android:key="@string/seek_duration_key"
android:summary="%s"
android:title="@string/seek_duration_title"/>
+
+
+
From a7fbe05a735b2019e938f1ca60e1196881ea35a4 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 27 Jun 2020 06:25:50 +0300
Subject: [PATCH 22/40] Changes for review
---
app/src/main/AndroidManifest.xml | 3 -
.../java/org/schabi/newpipe/MainActivity.java | 9 +-
.../org/schabi/newpipe/RouterActivity.java | 9 +-
.../fragments/detail/VideoDetailFragment.java | 278 +++++-------------
.../fragments/list/search/SearchFragment.java | 6 +-
.../newpipe/player/BackgroundPlayer.java | 2 +-
.../org/schabi/newpipe/player/BasePlayer.java | 33 ++-
.../org/schabi/newpipe/player/MainPlayer.java | 4 +-
.../schabi/newpipe/player/VideoPlayer.java | 10 +-
.../newpipe/player/VideoPlayerImpl.java | 72 ++---
.../player/event/PlayerGestureListener.java | 28 +-
.../newpipe/player/helper/AudioReactor.java | 2 +-
.../newpipe/player/helper/PlayerHelper.java | 18 +-
.../newpipe/streams/OggFromWebMWriter.java | 6 +-
.../newpipe/streams/SubtitleConverter.java | 2 +-
.../schabi/newpipe/streams/WebMWriter.java | 2 +-
.../schabi/newpipe/util/NavigationHelper.java | 23 +-
.../newpipe/views/ExpandableSurfaceView.java | 4 +-
.../giga/postprocessing/Postprocessing.java | 2 +-
.../giga/ui/adapter/MissionAdapter.java | 6 +-
.../giga/ui/common/ProgressDrawable.java | 4 +-
app/src/main/res/layout/toolbar_layout.xml | 9 -
22 files changed, 182 insertions(+), 350 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 18e424524..3136774e1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -38,9 +38,6 @@
-
- NavigationHelper.installKore(context))
- .setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
- });
- builder.create().show();
- }
-
- private void setupActionBarOnError(final String url) {
- if (DEBUG) Log.d(TAG, "setupActionBarHandlerOnError() called with: url = [" + url + "]");
- Log.e("-----", "missing code");
- }*/
-
- private void setupActionBar(final StreamInfo info) {
- if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
- boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
- .getBoolean(activity.getString(R.string.use_external_video_player_key), false);
-
- sortedVideoStreams = ListHelper.getSortedStreamVideosList(
- activity,
- info.getVideoStreams(),
- info.getVideoOnlyStreams(),
- false);
- selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
-
- /*final StreamItemAdapter streamsAdapter =
- new StreamItemAdapter<>(activity,
- new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
-
- spinnerToolbar.setAdapter(streamsAdapter);
- spinnerToolbar.setSelection(selectedVideoStreamIndex);
- spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView> parent, View view, int position, long id) {
- selectedVideoStreamIndex = position;
- }
-
- @Override
- public void onNothingSelected(AdapterView> parent) {
- }
- });*/
- }
-
/*//////////////////////////////////////////////////////////////////////////
// OwnStack
//////////////////////////////////////////////////////////////////////////*/
@@ -866,7 +730,7 @@ public class VideoDetailFragment
if (DEBUG) Log.d(TAG, "onBackPressed() called");
// If we are in fullscreen mode just exit from it via first back press
- if (player != null && player.isInFullscreen()) {
+ if (player != null && player.isFullscreen()) {
if (!PlayerHelper.isTablet(activity)) player.onPause();
restoreDefaultOrientation();
setAutoplay(false);
@@ -874,11 +738,10 @@ public class VideoDetailFragment
}
// If we have something in history of played items we replay it here
- boolean isPreviousCanBePlayed = player != null
+ if (player != null
&& player.getPlayQueue() != null
&& player.videoPlayerSelected()
- && player.getPlayQueue().previous();
- if (isPreviousCanBePlayed) {
+ && player.getPlayQueue().previous()) {
return true;
}
// That means that we are on the start of the stack,
@@ -930,9 +793,8 @@ public class VideoDetailFragment
}
public void selectAndLoadVideo(int serviceId, String videoUrl, String name, PlayQueue playQueue) {
- boolean streamIsTheSame = this.playQueue != null && this.playQueue.equals(playQueue);
// Situation when user switches from players to main player. All needed data is here, we can start watching
- if (streamIsTheSame) {
+ if (this.playQueue != null && this.playQueue.equals(playQueue)) {
openVideoPlayer();
return;
}
@@ -1056,7 +918,7 @@ public class VideoDetailFragment
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
// If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
- if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+ if (player != null && player.isFullscreen()) player.toggleFullscreen();
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
@@ -1072,18 +934,16 @@ public class VideoDetailFragment
}
// See UI changes while remote playQueue changes
- if (!bounded) startService(false);
+ if (!bound) startService(false);
// If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
- if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+ if (player != null && player.isFullscreen()) player.toggleFullscreen();
PlayQueue queue = setupPlayQueueForIntent(append);
if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
} else {
- Runnable onAllow = () -> NavigationHelper.playOnPopupPlayer(activity, queue, true);
- if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
- else onAllow.run();
+ replaceQueueIfUserConfirms(() -> NavigationHelper.playOnPopupPlayer(activity, queue, true));
}
}
@@ -1093,27 +953,23 @@ public class VideoDetailFragment
VideoStream selectedVideoStream = getSelectedVideoStream();
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
- Runnable onAllow = this::openNormalPlayer;
- if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
- else onAllow.run();
+ replaceQueueIfUserConfirms(this::openMainPlayer);
}
}
private void openNormalBackgroundPlayer(final boolean append) {
// See UI changes while remote playQueue changes
- if (!bounded) startService(false);
+ if (!bound) startService(false);
PlayQueue queue = setupPlayQueueForIntent(append);
if (append) {
NavigationHelper.enqueueOnBackgroundPlayer(activity, queue, false);
} else {
- Runnable onAllow = () -> NavigationHelper.playOnBackgroundPlayer(activity, queue, true);
- if (shouldAskBeforeClearingQueue()) showClearingQueueConfirmation(onAllow);
- else onAllow.run();
+ replaceQueueIfUserConfirms(() -> NavigationHelper.playOnBackgroundPlayer(activity, queue, true));
}
}
- private void openNormalPlayer() {
+ private void openMainPlayer() {
if (playerService == null) {
startService(true);
return;
@@ -1144,7 +1000,7 @@ public class VideoDetailFragment
PlayQueue queue = playQueue;
// Size can be 0 because queue removes bad stream automatically when error occurs
- if (playQueue == null || playQueue.size() == 0)
+ if (queue == null || queue.size() == 0)
queue = new SinglePlayQueue(currentInfo);
return queue;
@@ -1284,18 +1140,18 @@ public class VideoDetailFragment
boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
int height;
- if (player != null && player.isInFullscreen())
+ if (player != null && player.isFullscreen())
height = isInMultiWindow() ? getView().getHeight() : activity.getWindow().getDecorView().getHeight();
else
height = isPortrait
? (int) (metrics.widthPixels / (16.0f / 9.0f))
- : (int) (metrics.heightPixels / 2f);
+ : (int) (metrics.heightPixels / 2.0f);
thumbnailImageView.setLayoutParams(new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
if (player != null) {
int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
- player.getSurfaceView().setHeights(height, player.isInFullscreen() ? height : maxHeight);
+ player.getSurfaceView().setHeights(height, player.isFullscreen() ? height : maxHeight);
}
}
@@ -1353,7 +1209,7 @@ public class VideoDetailFragment
private void restoreDefaultOrientation() {
if (player == null || !player.videoPlayerSelected() || activity == null) return;
- if (player != null && player.isInFullscreen()) player.toggleFullscreen();
+ if (player != null && player.isFullscreen()) player.toggleFullscreen();
// This will show systemUI and pause the player.
// User can tap on Play button and video will be in fullscreen mode again
// Note for tablet: trying to avoid orientation changes since it's not easy to physically rotate the tablet every time
@@ -1375,7 +1231,6 @@ public class VideoDetailFragment
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
}
- //animateView(spinnerToolbar, false, 200);
animateView(thumbnailPlayButton, false, 50);
animateView(detailDurationView, false, 100);
animateView(detailPositionView, false, 100);
@@ -1392,7 +1247,7 @@ public class VideoDetailFragment
if(relatedStreamsLayout != null){
if(showRelatedStreams){
- relatedStreamsLayout.setVisibility(player != null && player.isInFullscreen() ? View.GONE : View.INVISIBLE);
+ relatedStreamsLayout.setVisibility(player != null && player.isFullscreen() ? View.GONE : View.INVISIBLE);
}else{
relatedStreamsLayout.setVisibility(View.GONE);
}
@@ -1419,7 +1274,7 @@ public class VideoDetailFragment
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout, RelatedVideosFragment.getInstance(info))
.commitNow();
- relatedStreamsLayout.setVisibility(player != null && player.isInFullscreen() ? View.GONE : View.VISIBLE);
+ relatedStreamsLayout.setVisibility(player != null && player.isFullscreen() ? View.GONE : View.VISIBLE);
}
}
@@ -1506,9 +1361,6 @@ public class VideoDetailFragment
prepareDescription(info.getDescription());
updateProgressInfo(info);
-
- //animateView(spinnerToolbar, true, 500);
- setupActionBar(info);
initThumbnailViews(info);
if (player == null || player.isPlayerStopped())
@@ -1527,7 +1379,6 @@ public class VideoDetailFragment
case LIVE_STREAM:
case AUDIO_LIVE_STREAM:
detailControlsDownload.setVisibility(View.GONE);
- spinnerToolbar.setVisibility(View.GONE);
break;
default:
if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
@@ -1535,7 +1386,6 @@ public class VideoDetailFragment
|| !info.getVideoOnlyStreams().isEmpty()) break;
detailControlsPopup.setVisibility(View.GONE);
- spinnerToolbar.setVisibility(View.GONE);
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp);
break;
}
@@ -1629,8 +1479,8 @@ public class VideoDetailFragment
}, e -> {
if (DEBUG) e.printStackTrace();
}, () -> {
- animateView(positionView, false, 0);
- animateView(detailPositionView, false, 0);
+ positionView.setVisibility(View.GONE);
+ detailPositionView.setVisibility(View.GONE);
});
}
@@ -1653,15 +1503,16 @@ public class VideoDetailFragment
@Override
public void onQueueUpdate(PlayQueue queue) {
playQueue = queue;
+ StackItem stackWithQueue;
// This should be the only place where we push data to stack. It will allow to have live instance of PlayQueue with actual
// information about deleted/added items inside Channel/Playlist queue and makes possible to have a history of played items
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)) {
stack.push(new StackItem(serviceId, url, name, playQueue));
- } else if (findQueueInStack(queue) != null) {
+ } else if ((stackWithQueue = findQueueInStack(queue)) != null) {
// On every MainPlayer service's destroy() playQueue gets disposed and no longer able to track progress.
// That's why we update our cached disposed queue with the new one that is active and have the same history
// Without that the cached playQueue will have an old recovery position
- findQueueInStack(queue).setPlayQueue(queue);
+ stackWithQueue.setPlayQueue(queue);
}
if (DEBUG) {
@@ -1679,7 +1530,7 @@ public class VideoDetailFragment
restoreDefaultOrientation();
break;
case BasePlayer.STATE_PLAYING:
- if (positionView.getAlpha() != 1f
+ if (positionView.getAlpha() != 1.0f
&& player.getPlayQueue() != null
&& player.getPlayQueue().getItem() != null
&& player.getPlayQueue().getItem().getUrl().equals(url)) {
@@ -1725,7 +1576,7 @@ public class VideoDetailFragment
public void onPlayerError(ExoPlaybackException error) {
if (error.type == ExoPlaybackException.TYPE_SOURCE || error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
hideMainPlayer();
- if (playerService != null && player.isInFullscreen())
+ if (playerService != null && player.isFullscreen())
player.toggleFullscreen();
}
}
@@ -1824,7 +1675,7 @@ public class VideoDetailFragment
// Listener implementation
public void hideSystemUiIfNeeded() {
- if (player != null && player.isInFullscreen() && bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
+ if (player != null && player.isFullscreen() && bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
hideSystemUi();
}
@@ -1897,11 +1748,19 @@ public class VideoDetailFragment
return item;
}
- private boolean shouldAskBeforeClearingQueue() {
- PlayQueue activeQueue = player != null ? player.getPlayQueue() : null;
+ private void replaceQueueIfUserConfirms(final Runnable onAllow) {
+ @Nullable final PlayQueue activeQueue = player == null ? null : player.getPlayQueue();
+
// Player will have STATE_IDLE when a user pressed back button
- return isClearingQueueConfirmationRequired(activity) && playerIsNotStopped()
- && activeQueue != null && !activeQueue.equals(playQueue) && activeQueue.getStreams().size() > 1;
+ if (isClearingQueueConfirmationRequired(activity)
+ && playerIsNotStopped()
+ && activeQueue != null
+ && !activeQueue.equals(playQueue)
+ && activeQueue.getStreams().size() > 1) {
+ showClearingQueueConfirmation(onAllow);
+ } else {
+ onAllow.run();
+ }
}
private void showClearingQueueConfirmation(Runnable onAllow) {
@@ -1964,13 +1823,13 @@ public class VideoDetailFragment
// Disable click because overlay buttons located on top of buttons from the player
setOverlayElementsClickable(false);
hideSystemUiIfNeeded();
- boolean needToExpand = isLandscape()
+ // Conditions when the player should be expanded to fullscreen
+ if (isLandscape()
&& player != null
&& player.isPlaying()
- && !player.isInFullscreen()
+ && !player.isFullscreen()
&& !PlayerHelper.isTablet(activity)
- && player.videoPlayerSelected();
- if (needToExpand) player.toggleFullscreen();
+ && player.videoPlayerSelected()) player.toggleFullscreen();
break;
case BottomSheetBehavior.STATE_COLLAPSED:
// Re-enable clicks
@@ -1979,7 +1838,7 @@ public class VideoDetailFragment
break;
case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_SETTLING:
- if (player != null && player.isInFullscreen()) showSystemUi();
+ if (player != null && player.isFullscreen()) showSystemUi();
if (player != null && player.isControlsVisible()) player.hideControls(0, 0);
break;
}
@@ -1997,8 +1856,8 @@ public class VideoDetailFragment
}
private void updateOverlayData(@Nullable String title, @Nullable String uploader, @Nullable String thumbnailUrl) {
- overlayTitleTextView.setText(!TextUtils.isEmpty(title) ? title : "");
- overlayChannelTextView.setText(!TextUtils.isEmpty(uploader) ? uploader : "");
+ overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title);
+ overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader);
overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(thumbnailUrl))
imageLoader.displayImage(thumbnailUrl, overlayThumbnailImageView,
@@ -2006,8 +1865,7 @@ public class VideoDetailFragment
}
private void setOverlayPlayPauseImage() {
- boolean playing = player != null && player.getPlayer().getPlayWhenReady();
- int attr = playing ? R.attr.pause : R.attr.play;
+ int attr = player != null && player.getPlayer().getPlayWhenReady() ? R.attr.pause : R.attr.play;
overlayPlayPauseButton.setImageResource(ThemeHelper.resolveResourceIdFromAttr(activity, attr));
}
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 f2e8aa244..5fd470745 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
@@ -436,16 +436,16 @@ public class SearchFragment
if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) {
searchToolbarContainer.setTranslationX(100);
- searchToolbarContainer.setAlpha(0f);
+ searchToolbarContainer.setAlpha(0.0f);
searchToolbarContainer.setVisibility(View.VISIBLE);
searchToolbarContainer.animate()
.translationX(0)
- .alpha(1f)
+ .alpha(1.0f)
.setDuration(200)
.setInterpolator(new DecelerateInterpolator()).start();
} else {
searchToolbarContainer.setTranslationX(0);
- searchToolbarContainer.setAlpha(1f);
+ searchToolbarContainer.setAlpha(1.0f);
searchToolbarContainer.setVisibility(View.VISIBLE);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
index e408f49f6..99b38aae7 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
@@ -337,7 +337,7 @@ public final class BackgroundPlayer extends Service {
@Override
public void onPrepared(boolean playWhenReady) {
super.onPrepared(playWhenReady);
- simpleExoPlayer.setVolume(1f);
+ simpleExoPlayer.setVolume(1.0f);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 490419c64..c31654473 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -158,7 +158,7 @@ public abstract class BasePlayer implements
// Playback
//////////////////////////////////////////////////////////////////////////*/
- protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f};
+ protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f};
protected PlayQueue playQueue;
protected PlayQueueAdapter playQueueAdapter;
@@ -227,7 +227,7 @@ public abstract class BasePlayer implements
public void setup() {
if (simpleExoPlayer == null) {
- initPlayer(/*playOnInit=*/true);
+ initPlayer(true);
}
initListeners();
}
@@ -274,7 +274,7 @@ public abstract class BasePlayer implements
return;
}
- boolean same = playQueue != null && playQueue.equals(queue);
+ boolean samePlayQueue = playQueue != null && playQueue.equals(queue);
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
@@ -282,6 +282,14 @@ public abstract class BasePlayer implements
final boolean playbackSkipSilence = intent.getBooleanExtra(PLAYBACK_SKIP_SILENCE,
getPlaybackSkipSilence());
+ /*
+ * There are 3 situations when playback shouldn't be started from scratch (zero timestamp):
+ * 1. User pressed on a timestamp link and the same video should be rewound to that timestamp
+ * 2. User changed a player from, for example. main to popup, or from audio to main, etc
+ * 3. User chose to resume a video based on a saved timestamp from history of played videos
+ * In those cases time will be saved because re-init of the play queue is a not an instant task
+ * and requires network calls
+ * */
// seek to timestamp if stream is already playing
if (simpleExoPlayer != null
&& queue.size() == 1
@@ -289,21 +297,20 @@ public abstract class BasePlayer implements
&& playQueue.size() == 1
&& playQueue.getItem() != null
&& queue.getItem().getUrl().equals(playQueue.getItem().getUrl())
- && queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET
- && simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) {
+ && queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
// Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) simpleExoPlayer.retry();
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
return;
- } else if (same && !playQueue.isDisposed() && simpleExoPlayer != null) {
+ } else if (samePlayQueue && !playQueue.isDisposed() && simpleExoPlayer != null) {
// Do not re-init the same PlayQueue. Save time
// Player can have state = IDLE when playback is stopped or failed and we should retry() in this case
if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) simpleExoPlayer.retry();
return;
} else if (intent.getBooleanExtra(RESUME_PLAYBACK, false)
&& isPlaybackResumeEnabled()
- && !same) {
+ && !samePlayQueue) {
final PlayQueueItem item = queue.getItem();
if (item != null && item.getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET) {
stateLoader = recordManager.loadStreamState(item)
@@ -313,19 +320,16 @@ public abstract class BasePlayer implements
.subscribe(
state -> {
queue.setRecovery(queue.getIndex(), state.getProgressTime());
- initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
- /*playOnInit=*/true);
+ initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, true);
},
error -> {
if (DEBUG) error.printStackTrace();
// In case any error we can start playback without history
- initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
- /*playOnInit=*/true);
+ initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, true);
},
() -> {
// Completed but not found in history
- initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
- /*playOnInit=*/true);
+ initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, true);
}
);
databaseUpdateReactor.add(stateLoader);
@@ -334,8 +338,7 @@ public abstract class BasePlayer implements
}
// Good to go...
// In a case of equal PlayQueues we can re-init old one but only when it is disposed
- initPlayback(same ? playQueue : queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
- /*playOnInit=*/true);
+ initPlayback(samePlayQueue ? playQueue : queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, true);
}
protected void initPlayback(@NonNull final PlayQueue queue,
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 61b69ac5e..f1f9b51da 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -184,8 +184,6 @@ public final class MainPlayer extends Service {
playerImpl.destroy();
}
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
- playerImpl = null;
- lockManager = null;
stopForeground(true);
stopSelf();
@@ -197,7 +195,7 @@ public final class MainPlayer extends Service {
boolean isLandscape() {
// DisplayMetrics from activity context knows about MultiWindow feature while DisplayMetrics from app context doesn't
- final DisplayMetrics metrics = playerImpl != null && playerImpl.getParentActivity() != null ?
+ final DisplayMetrics metrics = (playerImpl != null && playerImpl.getParentActivity() != null) ?
playerImpl.getParentActivity().getResources().getDisplayMetrics()
: getResources().getDisplayMetrics();
return metrics.heightPixels < metrics.widthPixels;
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 c29cfd19c..632044b06 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
@@ -797,9 +797,9 @@ public abstract class VideoPlayer extends BasePlayer
if (drawableId == -1) {
if (controlAnimationView.getVisibility() == View.VISIBLE) {
controlViewAnimator = ObjectAnimator.ofPropertyValuesHolder(controlAnimationView,
- PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
- PropertyValuesHolder.ofFloat(View.SCALE_X, 1.4f, 1f),
- PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.4f, 1f)
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1.0f, 0.0f),
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 1.4f, 1.0f),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.4f, 1.0f)
).setDuration(DEFAULT_CONTROLS_DURATION);
controlViewAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -812,8 +812,8 @@ public abstract class VideoPlayer extends BasePlayer
return;
}
- float scaleFrom = goneOnEnd ? 1f : 1f, scaleTo = goneOnEnd ? 1.8f : 1.4f;
- float alphaFrom = goneOnEnd ? 1f : 0f, alphaTo = goneOnEnd ? 0f : 1f;
+ float scaleFrom = goneOnEnd ? 1.0f : 1.0f, scaleTo = goneOnEnd ? 1.8f : 1.4f;
+ float alphaFrom = goneOnEnd ? 1.0f : 0.0f, alphaTo = goneOnEnd ? 0.0f : 1.0f;
controlViewAnimator = ObjectAnimator.ofPropertyValuesHolder(controlAnimationView,
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index efbe06457..aad20b0ad 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -270,14 +270,14 @@ public class VideoPlayerImpl extends VideoPlayer
final float captionScale,
@NonNull final CaptionStyleCompat captionStyle) {
if (popupPlayerSelected()) {
- float captionRatio = (captionScale - 1f) / 5f + 1f;
+ float captionRatio = (captionScale - 1.0f) / 5.0f + 1.0f;
view.setFractionalTextSize(SubtitleView.DEFAULT_TEXT_SIZE_FRACTION * captionRatio);
view.setApplyEmbeddedStyles(captionStyle.equals(CaptionStyleCompat.DEFAULT));
view.setStyle(captionStyle);
} else {
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final int minimumLength = Math.min(metrics.heightPixels, metrics.widthPixels);
- final float captionRatioInverse = 20f + 4f * (1f - captionScale);
+ final float captionRatioInverse = 20f + 4f * (1.0f - captionScale);
view.setFixedTextSize(TypedValue.COMPLEX_UNIT_PX,
(float) minimumLength / captionRatioInverse);
view.setApplyEmbeddedStyles(captionStyle.equals(CaptionStyleCompat.DEFAULT));
@@ -300,7 +300,7 @@ public class VideoPlayerImpl extends VideoPlayer
moreOptionsButton.setVisibility(View.GONE);
getTopControlsRoot().setOrientation(LinearLayout.HORIZONTAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
- secondaryControls.setAlpha(1f);
+ secondaryControls.setAlpha(1.0f);
secondaryControls.setVisibility(View.VISIBLE);
secondaryControls.setTranslationY(0);
shareButton.setVisibility(View.GONE);
@@ -333,7 +333,7 @@ public class VideoPlayerImpl extends VideoPlayer
getTopControlsRoot().setClickable(true);
getTopControlsRoot().setFocusable(true);
}
- if (!isInFullscreen()) {
+ if (!isFullscreen()) {
titleTextView.setVisibility(View.GONE);
channelTextView.setVisibility(View.GONE);
} else {
@@ -602,10 +602,10 @@ public class VideoPlayerImpl extends VideoPlayer
isFullscreen = !isFullscreen;
setControlsSize();
- fragmentListener.onFullscreenStateChanged(isInFullscreen());
+ fragmentListener.onFullscreenStateChanged(isFullscreen());
}
- if (!isInFullscreen()) {
+ if (!isFullscreen()) {
titleTextView.setVisibility(View.GONE);
channelTextView.setVisibility(View.GONE);
playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
@@ -674,7 +674,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public boolean onLongClick(View v) {
- if (v.getId() == moreOptionsButton.getId() && isInFullscreen()) {
+ if (v.getId() == moreOptionsButton.getId() && isFullscreen()) {
fragmentListener.onMoreOptionsLongClicked();
hideControls(0, 0);
hideSystemUIIfNeeded();
@@ -690,7 +690,7 @@ public class VideoPlayerImpl extends VideoPlayer
updatePlaybackButtons();
getControlsRoot().setVisibility(View.INVISIBLE);
- animateView(queueLayout, SLIDE_AND_ALPHA, /*visible=*/true,
+ animateView(queueLayout, SLIDE_AND_ALPHA,true,
DEFAULT_CONTROLS_DURATION);
itemsList.scrollToPosition(playQueue.getIndex());
@@ -699,7 +699,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void onQueueClosed() {
if (!queueVisible) return;
- animateView(queueLayout, SLIDE_AND_ALPHA, /*visible=*/false,
+ animateView(queueLayout, SLIDE_AND_ALPHA,false,
DEFAULT_CONTROLS_DURATION, 0, () -> {
// Even when queueLayout is GONE it receives touch events and ruins normal behavior of the app. This line fixes it
queueLayout.setTranslationY(-queueLayout.getHeight() * 5);
@@ -765,12 +765,12 @@ public class VideoPlayerImpl extends VideoPlayer
boolean showButton = videoPlayerSelected() && (orientationLocked || isVerticalVideo || tabletInLandscape);
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(service.getResources().getDrawable(
- isInFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
+ isFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
}
private void prepareOrientation() {
boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
- if (orientationLocked && isInFullscreen() && service.isLandscape() == isVerticalVideo && fragmentListener != null)
+ if (orientationLocked && isFullscreen() && service.isLandscape() == isVerticalVideo && fragmentListener != null)
fragmentListener.onScreenRotationButtonClicked();
}
@@ -893,7 +893,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void onBlocked() {
super.onBlocked();
- playPauseButton.setImageResource(R.drawable.ic_pause_white);
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
animatePlayButtons(false, 100);
getRootView().setKeepScreenOn(false);
@@ -1185,7 +1185,7 @@ public class VideoPlayerImpl extends VideoPlayer
return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
}
- public boolean isInFullscreen() {
+ public boolean isFullscreen() {
return isFullscreen;
}
@@ -1216,8 +1216,7 @@ public class VideoPlayerImpl extends VideoPlayer
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
getControlsVisibilityHandler().postDelayed(() ->
animateView(getControlsRoot(), false, duration, 0,
- this::hideSystemUIIfNeeded),
- /*delayMillis=*/delay
+ this::hideSystemUIIfNeeded), delay
);
}
@@ -1225,24 +1224,13 @@ public class VideoPlayerImpl extends VideoPlayer
if (playQueue == null)
return;
- if (playQueue.getIndex() == 0)
- playPreviousButton.setVisibility(View.INVISIBLE);
- else
- playPreviousButton.setVisibility(View.VISIBLE);
-
- if (playQueue.getIndex() + 1 == playQueue.getStreams().size())
- playNextButton.setVisibility(View.INVISIBLE);
- else
- playNextButton.setVisibility(View.VISIBLE);
-
- if (playQueue.getStreams().size() <= 1 || popupPlayerSelected())
- queueButton.setVisibility(View.GONE);
- else
- queueButton.setVisibility(View.VISIBLE);
+ playPreviousButton.setVisibility(playQueue.getIndex() == 0 ? View.INVISIBLE : View.VISIBLE);
+ playNextButton.setVisibility(playQueue.getIndex() + 1 == playQueue.getStreams().size() ? View.INVISIBLE : View.VISIBLE);
+ queueButton.setVisibility(playQueue.getStreams().size() <= 1 || popupPlayerSelected() ? View.GONE : View.VISIBLE);
}
private void showSystemUIPartially() {
- if (isInFullscreen() && getParentActivity() != null) {
+ if (isFullscreen() && getParentActivity() != null) {
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
}
@@ -1330,7 +1318,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void checkLandscape() {
AppCompatActivity parent = getParentActivity();
- boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isInFullscreen() && videoPlayerSelected() && !audioOnly;
+ boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isFullscreen() && videoPlayerSelected() && !audioOnly;
boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED;
if (parent != null && videoInLandscapeButNotInFullscreen && playingState && !PlayerHelper.isTablet(service))
toggleFullscreen();
@@ -1421,7 +1409,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (DEBUG) Log.d(TAG, "initPopup() called");
// Popup is already added to windowManager
- if (isPopupHasParent()) return;
+ if (popupHasParent()) return;
updateScreenSize();
@@ -1430,13 +1418,10 @@ public class VideoPlayerImpl extends VideoPlayer
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
popupHeight = getMinimumVideoHeight(popupWidth);
- final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
- WindowManager.LayoutParams.TYPE_PHONE :
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
popupLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) popupHeight,
- layoutParamType,
+ popupLayoutParamType(),
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
@@ -1470,15 +1455,12 @@ public class VideoPlayerImpl extends VideoPlayer
closeOverlayView = View.inflate(service, R.layout.player_popup_close_overlay, null);
closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
- final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
- WindowManager.LayoutParams.TYPE_PHONE :
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
- layoutParamType,
+ popupLayoutParamType(),
flags,
PixelFormat.TRANSLUCENT);
closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
@@ -1600,6 +1582,12 @@ public class VideoPlayerImpl extends VideoPlayer
windowManager.updateViewLayout(getRootView(), popupLayoutParams);
}
+ private int popupLayoutParamType() {
+ return Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_PHONE :
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Misc
//////////////////////////////////////////////////////////////////////////*/
@@ -1617,7 +1605,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void removePopupFromView() {
boolean isCloseOverlayHasParent = closeOverlayView != null && closeOverlayView.getParent() != null;
- if (isPopupHasParent())
+ if (popupHasParent())
windowManager.removeView(getRootView());
if (isCloseOverlayHasParent)
windowManager.removeView(closeOverlayView);
@@ -1651,7 +1639,7 @@ public class VideoPlayerImpl extends VideoPlayer
}).start();
}
- private boolean isPopupHasParent() {
+ private boolean popupHasParent() {
View root = getRootView();
return root != null && root.getLayoutParams() instanceof WindowManager.LayoutParams && root.getParent() != null;
}
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 bb9ede4d7..f8b402103 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
@@ -160,10 +160,7 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
isMovingInMain = true;
- boolean acceptVolumeArea = initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0;
- boolean acceptBrightnessArea = initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0;
-
- if (isVolumeGestureEnabled && acceptVolumeArea) {
+ if (isVolumeGestureEnabled && initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
float currentProgressPercent =
(float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
@@ -172,14 +169,11 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
- final int resId =
- currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
+ playerImpl.getVolumeImageView().setImageDrawable(
+ AppCompatResources.getDrawable(service, currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
: currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp
: currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp
- : R.drawable.ic_volume_up_white_72dp;
-
- playerImpl.getVolumeImageView().setImageDrawable(
- AppCompatResources.getDrawable(service, resId)
+ : R.drawable.ic_volume_up_white_72dp)
);
if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) {
@@ -188,7 +182,7 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
}
- } else if (isBrightnessGestureEnabled && acceptBrightnessArea) {
+ } else if (isBrightnessGestureEnabled && initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0) {
Activity parent = playerImpl.getParentActivity();
if (parent == null) return true;
@@ -203,13 +197,11 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
- final int resId =
- currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
- : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
- : R.drawable.ic_brightness_high_white_72dp;
-
playerImpl.getBrightnessImageView().setImageDrawable(
- AppCompatResources.getDrawable(service, resId)
+ AppCompatResources.getDrawable(service,
+ currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
+ : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
+ : R.drawable.ic_brightness_high_white_72dp)
);
if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) {
@@ -247,7 +239,7 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
- v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isInFullscreen());
+ v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isFullscreen());
return true;
case MotionEvent.ACTION_UP:
v.getParent().requestDisallowInterceptTouchEvent(false);
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java
index 8f344390a..ff9d0c477 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java
@@ -114,7 +114,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener,
private void onAudioFocusGain() {
Log.d(TAG, "onAudioFocusGain() called");
player.setVolume(DUCK_AUDIO_TO);
- animateAudio(DUCK_AUDIO_TO, 1f);
+ animateAudio(DUCK_AUDIO_TO, 1.0f);
if (PlayerHelper.isResumeAfterAudioFocusGain(context)) {
player.setPlayWhenReady(true);
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 6afb5a322..82003231d 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -222,14 +222,10 @@ public class PlayerHelper {
@AutoplayType
public static int getAutoplayType(@NonNull final Context context) {
- final String defaultType = context.getString(R.string.autoplay_wifi_key);
- final String always = context.getString(R.string.autoplay_always_key);
- final String never = context.getString(R.string.autoplay_never_key);
-
- final String type = getAutoplayType(context, defaultType);
- if (type.equals(always)) {
+ final String type = getAutoplayType(context, context.getString(R.string.autoplay_wifi_key));
+ if (type.equals(context.getString(R.string.autoplay_always_key))) {
return AUTOPLAY_TYPE_ALWAYS;
- } else if (type.equals(never)) {
+ } else if (type.equals(context.getString(R.string.autoplay_never_key))) {
return AUTOPLAY_TYPE_NEVER;
} else {
return AUTOPLAY_TYPE_WIFI;
@@ -307,12 +303,12 @@ public class PlayerHelper {
* Very small - 0.25f, Small - 0.5f, Normal - 1.0f, Large - 1.5f, Very Large - 2.0f
* */
public static float getCaptionScale(@NonNull final Context context) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return 1f;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return 1.0f;
final CaptioningManager captioningManager = (CaptioningManager)
context.getSystemService(Context.CAPTIONING_SERVICE);
if (captioningManager == null || !captioningManager.isEnabled()) {
- return 1f;
+ return 1.0f;
}
return captioningManager.getFontScale();
@@ -330,8 +326,8 @@ public class PlayerHelper {
public static boolean globalScreenOrientationLocked(Context context) {
// 1: Screen orientation changes using accelerometer
// 0: Screen orientation is locked
- return !(android.provider.Settings.System.getInt(
- context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
+ return android.provider.Settings.System.getInt(
+ context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 0;
}
public static boolean isTablet(@NonNull final Context context) {
diff --git a/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java b/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java
index 16bffea9a..5a5a9e1fd 100644
--- a/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java
+++ b/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java
@@ -158,7 +158,7 @@ public class OggFromWebMWriter implements Closeable {
switch (webm_track.kind) {
case Audio:
resolution = getSampleFrequencyFromTrack(webm_track.bMetadata);
- if (resolution == 0f) {
+ if (resolution == 0.0f) {
throw new RuntimeException("cannot get the audio sample rate");
}
break;
@@ -167,7 +167,7 @@ public class OggFromWebMWriter implements Closeable {
if (webm_track.defaultDuration == 0) {
throw new RuntimeException("missing default frame time");
}
- resolution = 1000f / ((float) webm_track.defaultDuration / webm_segment.info.timecodeScale);
+ resolution = 1000.0f / ((float) webm_track.defaultDuration / webm_segment.info.timecodeScale);
break;
default:
throw new RuntimeException("not implemented");
@@ -358,7 +358,7 @@ public class OggFromWebMWriter implements Closeable {
}
}
- return 0f;
+ return 0.0f;
}
private void clearSegmentTable() {
diff --git a/app/src/main/java/org/schabi/newpipe/streams/SubtitleConverter.java b/app/src/main/java/org/schabi/newpipe/streams/SubtitleConverter.java
index c41db4373..9c6fa977d 100644
--- a/app/src/main/java/org/schabi/newpipe/streams/SubtitleConverter.java
+++ b/app/src/main/java/org/schabi/newpipe/streams/SubtitleConverter.java
@@ -292,7 +292,7 @@ public class SubtitleConverter {
time += Integer.parseInt(units[0]) * 3600000;// hours
time += Integer.parseInt(units[1]) * 60000;//minutes
- time += Float.parseFloat(units[2]) * 1000f;// seconds and milliseconds (if present)
+ time += Float.parseFloat(units[2]) * 1000.0f;// seconds and milliseconds (if present)
// frames and sub-frames are ignored (not implemented)
// time += units[3] * fps;
diff --git a/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java b/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java
index 8525fabd2..fa2cc43e2 100644
--- a/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java
+++ b/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java
@@ -612,7 +612,7 @@ public class WebMWriter implements Closeable {
int offset = withLength ? 1 : 0;
byte[] buffer = new byte[offset + length];
- long marker = (long) Math.floor((length - 1f) / 8f);
+ long marker = (long) Math.floor((length - 1.0f) / 8.0f);
float mul = 1;
for (int i = length - 1; i >= 0; i--, mul *= 0x100) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 8b867a328..f0fd7e41b 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -73,9 +73,7 @@ public class NavigationHelper {
if (cacheKey != null) intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback);
-
- int playerType = intent.getIntExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO);
- intent.putExtra(VideoPlayer.PLAYER_TYPE, playerType);
+ intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO);
return intent;
}
@@ -122,7 +120,24 @@ public class NavigationHelper {
public static void playOnMainPlayer(final FragmentManager fragmentManager, final PlayQueue queue, boolean autoPlay) {
PlayQueueItem currentStream = queue.getItem();
- NavigationHelper.openVideoDetailFragment(fragmentManager, currentStream.getServiceId(), currentStream.getUrl(), currentStream.getTitle(), autoPlay, queue);
+ openVideoDetailFragment(fragmentManager, currentStream.getServiceId(), currentStream.getUrl(), currentStream.getTitle(), autoPlay, queue);
+ }
+
+ public static void playOnMainPlayer(@NonNull final Context context,
+ @NonNull final PlayQueue queue,
+ @NonNull final StreamingService.LinkType linkType,
+ @NonNull final String url,
+ @NonNull final String title,
+ final boolean autoPlay,
+ final boolean resumePlayback) {
+
+ Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Constants.KEY_LINK_TYPE, linkType);
+ intent.putExtra(Constants.KEY_URL, url);
+ intent.putExtra(Constants.KEY_TITLE, title);
+ intent.putExtra(VideoDetailFragment.AUTO_PLAY, autoPlay);
+ context.startActivity(intent);
}
public static void playOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
index df012eafd..f5a7df471 100644
--- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
+++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
@@ -11,7 +11,7 @@ public class ExpandableSurfaceView extends SurfaceView {
private int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
private int baseHeight = 0;
private int maxHeight = 0;
- private float videoAspectRatio = 0f;
+ private float videoAspectRatio = 0.0f;
private float scaleX = 1.0f;
private float scaleY = 1.0f;
@@ -22,7 +22,7 @@ public class ExpandableSurfaceView extends SurfaceView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (videoAspectRatio == 0f) return;
+ if (videoAspectRatio == 0.0f) return;
int width = MeasureSpec.getSize(widthMeasureSpec);
boolean verticalVideo = videoAspectRatio < 1;
diff --git a/app/src/main/java/us/shandian/giga/postprocessing/Postprocessing.java b/app/src/main/java/us/shandian/giga/postprocessing/Postprocessing.java
index 773ff92d1..bf9202a75 100644
--- a/app/src/main/java/us/shandian/giga/postprocessing/Postprocessing.java
+++ b/app/src/main/java/us/shandian/giga/postprocessing/Postprocessing.java
@@ -89,7 +89,7 @@ public abstract class Postprocessing implements Serializable {
}
public void setTemporalDir(@NonNull File directory) {
- long rnd = (int) (Math.random() * 100000f);
+ long rnd = (int) (Math.random() * 100000.0f);
tempFile = new File(directory, rnd + "_" + System.nanoTime() + ".tmp");
}
diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
index 8420e343b..aa7e42abc 100644
--- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
+++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
@@ -198,7 +198,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
} else {
h.progress.setMarquee(false);
h.status.setText("100%");
- h.progress.setProgress(1f);
+ h.progress.setProgress(1.0f);
h.size.setText(Utility.formatBytes(item.mission.length));
}
}
@@ -231,7 +231,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
double progress;
if (mission.unknownLength) {
progress = Double.NaN;
- h.progress.setProgress(0f);
+ h.progress.setProgress(0.0f);
} else {
progress = done / length;
}
@@ -298,7 +298,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
for (int i = 0; i < h.lastSpeed.length; i++) {
averageSpeed += h.lastSpeed[i];
}
- averageSpeed /= h.lastSpeed.length + 1f;
+ averageSpeed /= h.lastSpeed.length + 1.0f;
}
String speedStr = Utility.formatSpeed(averageSpeed);
diff --git a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java
index 3f638d418..bec947540 100644
--- a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java
+++ b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java
@@ -26,7 +26,7 @@ public class ProgressDrawable extends Drawable {
public ProgressDrawable() {
mMarqueeLine = null;// marquee disabled
- mMarqueeProgress = 0f;
+ mMarqueeProgress = 0.0f;
mMarqueeSize = 0;
mMarqueeNext = 0;
}
@@ -122,7 +122,7 @@ public class ProgressDrawable extends Drawable {
}
private void setupMarquee(int width, int height) {
- mMarqueeSize = (int) ((width * 10f) / 100f);// the size is 10% of the width
+ mMarqueeSize = (int) ((width * 10.0f) / 100.0f);// the size is 10% of the width
mMarqueeLine.rewind();
mMarqueeLine.moveTo(-mMarqueeSize, -mMarqueeSize);
diff --git a/app/src/main/res/layout/toolbar_layout.xml b/app/src/main/res/layout/toolbar_layout.xml
index 5d224bda8..318d16ff5 100644
--- a/app/src/main/res/layout/toolbar_layout.xml
+++ b/app/src/main/res/layout/toolbar_layout.xml
@@ -19,15 +19,6 @@
app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"
app:titleTextAppearance="@style/Toolbar.Title">
-
-
Date: Sun, 12 Jul 2020 03:59:47 +0300
Subject: [PATCH 23/40] Marked many (too many) variables as final
---
.../material/appbar/FlingBehavior.java | 6 +-
.../java/org/schabi/newpipe/MainActivity.java | 9 +-
.../newpipe/fragments/detail/StackItem.java | 2 +-
.../fragments/detail/VideoDetailFragment.java | 278 +++++++++---------
.../org/schabi/newpipe/player/BasePlayer.java | 2 +-
.../org/schabi/newpipe/player/MainPlayer.java | 14 +-
.../newpipe/player/ServicePlayerActivity.java | 2 +-
.../newpipe/player/VideoPlayerImpl.java | 121 ++++----
.../event/CustomBottomSheetBehavior.java | 8 +-
.../player/event/PlayerGestureListener.java | 31 +-
.../newpipe/player/playqueue/PlayQueue.java | 17 +-
.../schabi/newpipe/util/NavigationHelper.java | 65 ++--
.../newpipe/views/ExpandableSurfaceView.java | 13 +-
13 files changed, 295 insertions(+), 273 deletions(-)
diff --git a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
index d8c5fd014..95137414e 100644
--- a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
+++ b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
@@ -23,13 +23,13 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
}
private boolean allowScroll = true;
- private Rect globalRect = new Rect();
+ private final Rect globalRect = new Rect();
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
- ViewGroup playQueue = child.findViewById(R.id.playQueuePanel);
+ final ViewGroup playQueue = child.findViewById(R.id.playQueuePanel);
if (playQueue != null) {
- boolean visible = playQueue.getGlobalVisibleRect(globalRect);
+ final boolean visible = playQueue.getGlobalVisibleRect(globalRect);
if (visible && globalRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
allowScroll = false;
return false;
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index f63003661..2fb5d616a 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -45,7 +45,6 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.navigation.NavigationView;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
@@ -453,21 +452,21 @@ public class MainActivity extends AppCompatActivity {
public void onBackPressed() {
if (DEBUG) Log.d(TAG, "onBackPressed() called");
- FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder);
- BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
+ final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder);
+ final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
final int sheetState = bottomSheetBehavior.getState();
// In case bottomSheet is not visible on the screen or collapsed we can assume that the user interacts with a fragment
// inside fragment_holder so all back presses should be handled by it
if (sheetState == BottomSheetBehavior.STATE_HIDDEN || sheetState == BottomSheetBehavior.STATE_COLLAPSED) {
- Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
+ final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it
if (fragment instanceof BackPressable) {
if (((BackPressable) fragment).onBackPressed()) return;
}
} else {
- Fragment fragmentPlayer = getSupportFragmentManager().findFragmentById(R.id.fragment_player_holder);
+ final Fragment fragmentPlayer = getSupportFragmentManager().findFragmentById(R.id.fragment_player_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it
if (fragmentPlayer instanceof BackPressable) {
if (!((BackPressable) fragmentPlayer).onBackPressed())
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
index 38e41b2fb..ca96b5f96 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
@@ -10,7 +10,7 @@ class StackItem implements Serializable {
private String url;
private PlayQueue playQueue;
- StackItem(int serviceId, String url, String title, PlayQueue playQueue) {
+ StackItem(final int serviceId, final String url, final String title, final PlayQueue playQueue) {
this.serviceId = serviceId;
this.url = url;
this.title = title;
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 9d6d20c0d..060f95b68 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
@@ -73,10 +73,7 @@ import org.schabi.newpipe.util.*;
import org.schabi.newpipe.views.AnimatedProgressBar;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
import java.util.concurrent.TimeUnit;
import icepick.State;
@@ -143,7 +140,7 @@ public class VideoDetailFragment
private List sortedVideoStreams;
private int selectedVideoStreamIndex = -1;
- private BottomSheetBehavior bottomSheetBehavior;
+ private BottomSheetBehavior bottomSheetBehavior;
private BroadcastReceiver broadcastReceiver;
/*//////////////////////////////////////////////////////////////////////////
@@ -194,7 +191,7 @@ public class VideoDetailFragment
private ImageButton overlayCloseButton;
private AppBarLayout appBarLayout;
- private ViewPager viewPager;
+ private ViewPager viewPager;
private TabAdaptor pageAdapter;
private TabLayout tabLayout;
private FrameLayout relatedStreamsLayout;
@@ -210,19 +207,19 @@ public class VideoDetailFragment
// Service management
//////////////////////////////////////////////////////////////////////////*/
- private ServiceConnection getServiceConnection(boolean playAfterConnect) {
+ private ServiceConnection getServiceConnection(final boolean playAfterConnect) {
return new ServiceConnection() {
@Override
- public void onServiceDisconnected(ComponentName name) {
+ public void onServiceDisconnected(final ComponentName name) {
if (DEBUG) Log.d(TAG, "Player service is disconnected");
unbind();
}
@Override
- public void onServiceConnected(ComponentName compName, IBinder service) {
+ public void onServiceConnected(final ComponentName compName, final IBinder service) {
if (DEBUG) Log.d(TAG, "Player service is connected");
- MainPlayer.LocalBinder localBinder = (MainPlayer.LocalBinder) service;
+ final MainPlayer.LocalBinder localBinder = (MainPlayer.LocalBinder) service;
playerService = localBinder.getService();
player = localBinder.getPlayer();
@@ -255,18 +252,16 @@ public class VideoDetailFragment
private void bind() {
if (DEBUG) Log.d(TAG, "bind() called");
- Intent serviceIntent = new Intent(getContext(), MainPlayer.class);
- final boolean success = getContext().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
-
- if (!success) getContext().unbindService(serviceConnection);
- bound = success;
+ final Intent serviceIntent = new Intent(getContext(), MainPlayer.class);
+ bound = requireContext().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+ if (!bound) requireContext().unbindService(serviceConnection);
}
private void unbind() {
if (DEBUG) Log.d(TAG, "unbind() called");
if (bound) {
- getContext().unbindService(serviceConnection);
+ requireContext().unbindService(serviceConnection);
bound = false;
stopPlayerListener();
playerService = null;
@@ -286,20 +281,20 @@ public class VideoDetailFragment
// startService() can be called concurrently and it will give a random crashes and NullPointerExceptions
// inside the service because the service will be bound twice. Prevent it with unbinding first
unbind();
- getContext().startService(new Intent(getContext(), MainPlayer.class));
+ requireContext().startService(new Intent(getContext(), MainPlayer.class));
serviceConnection = getServiceConnection(playAfterConnect);
bind();
}
private void stopService() {
unbind();
- getContext().stopService(new Intent(getContext(), MainPlayer.class));
+ requireContext().stopService(new Intent(getContext(), MainPlayer.class));
}
/*////////////////////////////////////////////////////////////////////////*/
- public static VideoDetailFragment getInstance(int serviceId, String videoUrl, String name, PlayQueue playQueue) {
+ public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, final String name, final PlayQueue playQueue) {
VideoDetailFragment instance = new VideoDetailFragment();
instance.setInitialData(serviceId, videoUrl, name, playQueue);
return instance;
@@ -330,8 +325,8 @@ public class VideoDetailFragment
settingsContentObserver = new ContentObserver(new Handler()) {
@Override
- public void onChange(boolean selfChange) {
- if(activity != null && !PlayerHelper.globalScreenOrientationLocked(activity))
+ public void onChange(final boolean selfChange) {
+ if (activity != null && !PlayerHelper.globalScreenOrientationLocked(activity))
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
};
@@ -402,10 +397,9 @@ public class VideoDetailFragment
if (positionSubscriber != null) positionSubscriber.dispose();
if (currentWorker != null) currentWorker.dispose();
- if (disposables != null) disposables.clear();
+ disposables.clear();
positionSubscriber = null;
currentWorker = null;
- disposables = null;
bottomSheetBehavior.setBottomSheetCallback(null);
}
@@ -521,10 +515,9 @@ public class VideoDetailFragment
case R.id.overlay_play_pause_button:
if (playerIsNotStopped()) {
player.onPlayPause();
- player.hideControls(0,0);
+ player.hideControls(0, 0);
showSystemUi();
- }
- else openVideoPlayer();
+ } else openVideoPlayer();
setOverlayPlayPauseImage();
break;
@@ -754,12 +747,13 @@ public class VideoDetailFragment
// Remove top
stack.pop();
// Get stack item from the new top
+ assert stack.peek() != null;
setupFromHistoryItem(stack.peek());
return true;
}
- private void setupFromHistoryItem(StackItem item) {
+ private void setupFromHistoryItem(final StackItem item) {
setAutoplay(false);
hideMainPlayer();
@@ -773,9 +767,9 @@ public class VideoDetailFragment
// Maybe an item was deleted in background activity
if (item.getPlayQueue().getItem() == null) return;
- PlayQueueItem playQueueItem = item.getPlayQueue().getItem();
+ final PlayQueueItem playQueueItem = item.getPlayQueue().getItem();
// Update title, url, uploader from the last item in the stack (it's current now)
- boolean isPlayerStopped = player == null || player.isPlayerStopped();
+ final boolean isPlayerStopped = player == null || player.isPlayerStopped();
if (playQueueItem != null && isPlayerStopped)
updateOverlayData(playQueueItem.getTitle(), playQueueItem.getUploader(), playQueueItem.getThumbnailUrl());
}
@@ -792,7 +786,7 @@ public class VideoDetailFragment
else prepareAndHandleInfo(currentInfo, false);
}
- public void selectAndLoadVideo(int serviceId, String videoUrl, String name, PlayQueue playQueue) {
+ public void selectAndLoadVideo(final int serviceId, final String videoUrl, final String name, final PlayQueue playQueue) {
// Situation when user switches from players to main player. All needed data is here, we can start watching
if (this.playQueue != null && this.playQueue.equals(playQueue)) {
openVideoPlayer();
@@ -802,7 +796,7 @@ public class VideoDetailFragment
startLoading(false, true);
}
- public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
+ public void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) {
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = ["
+ info + "], scrollToTop = [" + scrollToTop + "]");
@@ -821,7 +815,7 @@ public class VideoDetailFragment
}
@Override
- public void startLoading(boolean forceLoad) {
+ public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
initTabs();
@@ -831,7 +825,7 @@ public class VideoDetailFragment
runWorker(forceLoad, stack.isEmpty());
}
- private void startLoading(boolean forceLoad, boolean addToBackStack) {
+ private void startLoading(final boolean forceLoad, final boolean addToBackStack) {
super.startLoading(false);
initTabs();
@@ -841,7 +835,7 @@ public class VideoDetailFragment
runWorker(forceLoad, addToBackStack);
}
- private void runWorker(boolean forceLoad, boolean addToBackStack) {
+ private void runWorker(final boolean forceLoad, final boolean addToBackStack) {
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -867,26 +861,26 @@ public class VideoDetailFragment
}
pageAdapter.clearAllItems();
- if(shouldShowComments()){
+ if (shouldShowComments()) {
pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG);
}
- if(showRelatedStreams && null == relatedStreamsLayout){
+ if (showRelatedStreams && null == relatedStreamsLayout) {
//temp empty fragment. will be updated in handleResult
pageAdapter.addFragment(new Fragment(), RELATED_TAB_TAG);
}
- if(pageAdapter.getCount() == 0){
+ if (pageAdapter.getCount() == 0) {
pageAdapter.addFragment(new EmptyFragment(), EMPTY_TAB_TAG);
}
pageAdapter.notifyDataSetUpdate();
- if(pageAdapter.getCount() < 2){
+ if (pageAdapter.getCount() < 2) {
tabLayout.setVisibility(View.GONE);
- }else{
- int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
- if(position != -1) viewPager.setCurrentItem(position);
+ } else {
+ final int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
+ if (position != -1) viewPager.setCurrentItem(position);
tabLayout.setVisibility(View.VISIBLE);
}
}
@@ -911,10 +905,10 @@ public class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/
private void openBackgroundPlayer(final boolean append) {
- AudioStream audioStream = currentInfo.getAudioStreams()
+ final AudioStream audioStream = currentInfo.getAudioStreams()
.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
- boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
+ final boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
// If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
@@ -939,7 +933,7 @@ public class VideoDetailFragment
// If a user watched video inside fullscreen mode and than chose another player return to non-fullscreen mode
if (player != null && player.isFullscreen()) player.toggleFullscreen();
- PlayQueue queue = setupPlayQueueForIntent(append);
+ final PlayQueue queue = setupPlayQueueForIntent(append);
if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
} else {
@@ -950,7 +944,8 @@ public class VideoDetailFragment
private void openVideoPlayer() {
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
- VideoStream selectedVideoStream = getSelectedVideoStream();
+ final VideoStream selectedVideoStream = getSelectedVideoStream();
+ if (selectedVideoStream == null) return;
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
replaceQueueIfUserConfirms(this::openMainPlayer);
@@ -961,7 +956,7 @@ public class VideoDetailFragment
// See UI changes while remote playQueue changes
if (!bound) startService(false);
- PlayQueue queue = setupPlayQueueForIntent(append);
+ final PlayQueue queue = setupPlayQueueForIntent(append);
if (append) {
NavigationHelper.enqueueOnBackgroundPlayer(activity, queue, false);
} else {
@@ -971,18 +966,18 @@ public class VideoDetailFragment
private void openMainPlayer() {
if (playerService == null) {
- startService(true);
- return;
+ startService(true);
+ return;
}
if (currentInfo == null) return;
- PlayQueue queue = setupPlayQueueForIntent(false);
+ final PlayQueue queue = setupPlayQueueForIntent(false);
// Video view can have elements visible from popup, We hide it here but once it ready the view will be shown in handleIntent()
- playerService.getView().setVisibility(View.GONE);
+ Objects.requireNonNull(playerService.getView()).setVisibility(View.GONE);
addVideoPlayerView();
- Intent playerIntent = NavigationHelper.getPlayerIntent(getContext(), MainPlayer.class, queue, null, true);
+ final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(), MainPlayer.class, queue, null, true);
activity.startService(playerIntent);
}
@@ -995,7 +990,7 @@ public class VideoDetailFragment
playerService.getView().setVisibility(View.GONE);
}
- private PlayQueue setupPlayQueueForIntent(boolean append) {
+ private PlayQueue setupPlayQueueForIntent(final boolean append) {
if (append) return new SinglePlayQueue(currentInfo);
PlayQueue queue = playQueue;
@@ -1026,7 +1021,7 @@ public class VideoDetailFragment
// Utils
//////////////////////////////////////////////////////////////////////////*/
- public void setAutoplay(boolean autoplay) {
+ public void setAutoplay(final boolean autoplay) {
this.autoPlayEnabled = autoplay;
}
@@ -1059,7 +1054,7 @@ public class VideoDetailFragment
&& isAutoplayAllowedByUser();
}
- private boolean isAutoplayAllowedByUser () {
+ private boolean isAutoplayAllowedByUser() {
if (activity == null) return false;
switch (PlayerHelper.getAutoplayType(activity)) {
@@ -1076,7 +1071,7 @@ public class VideoDetailFragment
private void addVideoPlayerView() {
if (player == null || getView() == null) return;
- FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
+ final FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
// Check if viewHolder already contains a child
if (player.getRootView().getParent() != viewHolder) removeVideoPlayerView();
@@ -1095,7 +1090,7 @@ public class VideoDetailFragment
private void makeDefaultHeightForVideoPlaceholder() {
if (getView() == null) return;
- FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
+ final FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
viewHolder.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT;
viewHolder.requestLayout();
}
@@ -1113,7 +1108,7 @@ public class VideoDetailFragment
disposables.add(Single.just(descriptionHtml)
.map((@io.reactivex.annotations.NonNull String description) -> {
- Spanned parsedDescription;
+ final Spanned parsedDescription;
if (Build.VERSION.SDK_INT >= 24) {
parsedDescription = Html.fromHtml(description, 0);
} else {
@@ -1137,11 +1132,11 @@ public class VideoDetailFragment
*/
private void setHeightThumbnail() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
- boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
+ final boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
- int height;
+ final int height;
if (player != null && player.isFullscreen())
- height = isInMultiWindow() ? getView().getHeight() : activity.getWindow().getDecorView().getHeight();
+ height = isInMultiWindow() ? Objects.requireNonNull(getView()).getHeight() : activity.getWindow().getDecorView().getHeight();
else
height = isPortrait
? (int) (metrics.widthPixels / (16.0f / 9.0f))
@@ -1150,7 +1145,7 @@ public class VideoDetailFragment
thumbnailImageView.setLayoutParams(new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
if (player != null) {
- int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
+ final int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
player.getSurfaceView().setHeights(height, player.isFullscreen() ? height : maxHeight);
}
}
@@ -1159,7 +1154,7 @@ public class VideoDetailFragment
contentRootLayoutHiding.setVisibility(View.VISIBLE);
}
- protected void setInitialData(int serviceId, String url, String name, PlayQueue playQueue) {
+ protected void setInitialData(final int serviceId, final String url, final String name, final PlayQueue playQueue) {
this.serviceId = serviceId;
this.url = url;
this.name = !TextUtils.isEmpty(name) ? name : "";
@@ -1188,9 +1183,9 @@ public class VideoDetailFragment
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if(intent.getAction().equals(ACTION_SHOW_MAIN_PLAYER)) {
+ if (intent.getAction().equals(ACTION_SHOW_MAIN_PLAYER)) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
- } else if(intent.getAction().equals(ACTION_HIDE_MAIN_PLAYER)) {
+ } else if (intent.getAction().equals(ACTION_HIDE_MAIN_PLAYER)) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
@@ -1227,7 +1222,7 @@ public class VideoDetailFragment
super.showLoading();
//if data is already cached, transition from VISIBLE -> INVISIBLE -> VISIBLE is not required
- if(!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)){
+ if (!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)) {
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
}
@@ -1245,10 +1240,10 @@ public class VideoDetailFragment
videoTitleToggleArrow.setVisibility(View.GONE);
videoTitleRoot.setClickable(false);
- if(relatedStreamsLayout != null){
- if(showRelatedStreams){
+ if (relatedStreamsLayout != null) {
+ if (showRelatedStreams) {
relatedStreamsLayout.setVisibility(player != null && player.isFullscreen() ? View.GONE : View.INVISIBLE);
- }else{
+ } else {
relatedStreamsLayout.setVisibility(View.GONE);
}
}
@@ -1266,11 +1261,11 @@ public class VideoDetailFragment
currentInfo = info;
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(), playQueue);
- if(showRelatedStreams){
- if(null == relatedStreamsLayout){ //phone
+ if (showRelatedStreams) {
+ if (null == relatedStreamsLayout) { //phone
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedVideosFragment.getInstance(info));
pageAdapter.notifyDataSetUpdate();
- }else{ //tablet
+ } else { //tablet
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout, RelatedVideosFragment.getInstance(info))
.commitNow();
@@ -1359,6 +1354,12 @@ public class VideoDetailFragment
videoUploadDateView.setVisibility(View.GONE);
}
+ sortedVideoStreams = ListHelper.getSortedStreamVideosList(
+ activity,
+ info.getVideoStreams(),
+ info.getVideoOnlyStreams(),
+ false);
+ selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
prepareDescription(info.getDescription());
updateProgressInfo(info);
initThumbnailViews(info);
@@ -1381,7 +1382,7 @@ public class VideoDetailFragment
detailControlsDownload.setVisibility(View.GONE);
break;
default:
- if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
+ if (info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
if (!info.getVideoStreams().isEmpty()
|| !info.getVideoOnlyStreams().isEmpty()) break;
@@ -1393,28 +1394,28 @@ public class VideoDetailFragment
public void openDownloadDialog() {
- try {
- DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
- downloadDialog.setVideoStreams(sortedVideoStreams);
- downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
- downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
- downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
+ try {
+ final DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
+ downloadDialog.setVideoStreams(sortedVideoStreams);
+ downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
+ downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
+ downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
- downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
- } catch (Exception e) {
- ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
- ServiceList.all()
- .get(currentInfo
- .getServiceId())
- .getServiceInfo()
- .getName(), "",
- R.string.could_not_setup_download_menu);
+ downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
+ } catch (Exception e) {
+ final ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
+ ServiceList.all()
+ .get(currentInfo
+ .getServiceId())
+ .getServiceInfo()
+ .getName(), "",
+ R.string.could_not_setup_download_menu);
- ErrorActivity.reportError(activity,
- e,
- activity.getClass(),
- activity.findViewById(android.R.id.content), info);
- }
+ ErrorActivity.reportError(activity,
+ e,
+ activity.getClass(),
+ activity.findViewById(android.R.id.content), info);
+ }
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1428,7 +1429,7 @@ public class VideoDetailFragment
else if (exception instanceof ContentNotAvailableException) {
showError(getString(R.string.content_not_available), false);
} else {
- int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
+ final int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
? R.string.youtube_signature_decryption_error
: exception instanceof ParsingException
? R.string.parsing_error
@@ -1484,7 +1485,7 @@ public class VideoDetailFragment
});
}
- private void showPlaybackProgress(long progress, long duration) {
+ private void showPlaybackProgress(final long progress, final long duration) {
final int progressSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(progress);
final int durationSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(duration);
positionView.setMax(durationSeconds);
@@ -1501,9 +1502,9 @@ public class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/
@Override
- public void onQueueUpdate(PlayQueue queue) {
+ public void onQueueUpdate(final PlayQueue queue) {
playQueue = queue;
- StackItem stackWithQueue;
+ final StackItem stackWithQueue;
// This should be the only place where we push data to stack. It will allow to have live instance of PlayQueue with actual
// information about deleted/added items inside Channel/Playlist queue and makes possible to have a history of played items
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)) {
@@ -1522,7 +1523,7 @@ public class VideoDetailFragment
}
@Override
- public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) {
+ public void onPlaybackUpdate(final int state, final int repeatMode, final boolean shuffled, final PlaybackParameters parameters) {
setOverlayPlayPauseImage();
switch (state) {
@@ -1542,7 +1543,7 @@ public class VideoDetailFragment
}
@Override
- public void onProgressUpdate(int currentProgress, int duration, int bufferPercent) {
+ public void onProgressUpdate(final int currentProgress, final int duration, final int bufferPercent) {
// Progress updates every second even if media is paused. It's useless until playing
if (!player.getPlayer().isPlaying() || playQueue == null) return;
@@ -1551,8 +1552,8 @@ public class VideoDetailFragment
}
@Override
- public void onMetadataUpdate(StreamInfo info, PlayQueue queue) {
- StackItem item = findQueueInStack(queue);
+ public void onMetadataUpdate(final StreamInfo info, final PlayQueue queue) {
+ final StackItem item = findQueueInStack(queue);
if (item != null) {
// When PlayQueue can have multiple streams (PlaylistPlayQueue or ChannelPlayQueue) every new played stream gives
// new title and url. StackItem contains information about first played stream. Let's update it here
@@ -1567,13 +1568,13 @@ public class VideoDetailFragment
if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) return;
currentInfo = info;
- setInitialData(info.getServiceId(), info.getUrl(),info.getName(), queue);
+ setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue);
setAutoplay(false);
prepareAndHandleInfo(info, true);
}
@Override
- public void onPlayerError(ExoPlaybackException error) {
+ public void onPlayerError(final ExoPlaybackException error) {
if (error.type == ExoPlaybackException.TYPE_SOURCE || error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
hideMainPlayer();
if (playerService != null && player.isFullscreen())
@@ -1590,12 +1591,12 @@ public class VideoDetailFragment
}
@Override
- public void onFullscreenStateChanged(boolean fullscreen) {
+ public void onFullscreenStateChanged(final boolean fullscreen) {
if (playerService.getView() == null || player.getParentActivity() == null)
return;
- View view = playerService.getView();
- ViewGroup parent = (ViewGroup) view.getParent();
+ final View view = playerService.getView();
+ final ViewGroup parent = (ViewGroup) view.getParent();
if (parent == null) return;
if (fullscreen) {
@@ -1619,7 +1620,7 @@ public class VideoDetailFragment
return;
}
- int newOrientation = isLandscape() ?
+ final int newOrientation = isLandscape() ?
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
@@ -1627,13 +1628,13 @@ public class VideoDetailFragment
}
/*
- * Will scroll down to description view after long click on moreOptionsButton
- * */
+ * Will scroll down to description view after long click on moreOptionsButton
+ * */
@Override
public void onMoreOptionsLongClicked() {
- CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
- AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
- ValueAnimator valueAnimator = ValueAnimator.ofInt(0, -getView().findViewById(R.id.player_placeholder).getHeight());
+ final CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
+ final ValueAnimator valueAnimator = ValueAnimator.ofInt(0, -getView().findViewById(R.id.player_placeholder).getHeight());
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(animation -> {
behavior.setTopAndBottomOffset((int) animation.getAnimatedValue());
@@ -1662,7 +1663,7 @@ public class VideoDetailFragment
if (activity == null) return;
- int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
@@ -1683,12 +1684,10 @@ public class VideoDetailFragment
return player != null && player.getPlayer() != null && player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
}
- private void setupBrightness(boolean save) {
+ private void setupBrightness(final boolean save) {
if (activity == null) return;
- WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
- float brightnessLevel;
-
+ final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
if (save) {
// Save current brightness level
PlayerHelper.setScreenBrightness(activity, lp.screenBrightness);
@@ -1698,7 +1697,7 @@ public class VideoDetailFragment
lp.screenBrightness = -1;
} else {
// Restore already saved brightness level
- brightnessLevel = PlayerHelper.getScreenBrightness(activity);
+ final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
if (brightnessLevel <= 0.0f && brightnessLevel > 1.0f)
return;
@@ -1712,7 +1711,7 @@ public class VideoDetailFragment
setAutoplay(true);
player.checkLandscape();
- boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
+ final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
// Let's give a user time to look at video information page if video is not playing
if (orientationLocked && !player.isPlaying()) {
player.onPlay();
@@ -1729,17 +1728,17 @@ public class VideoDetailFragment
}
/*
- * Means that the player fragment was swiped away via BottomSheetLayout and is empty but ready for any new actions. See cleanUp()
- * */
+ * Means that the player fragment was swiped away via BottomSheetLayout and is empty but ready for any new actions. See cleanUp()
+ * */
private boolean wasCleared() {
return url == null;
}
- private StackItem findQueueInStack(PlayQueue queue) {
+ private StackItem findQueueInStack(final PlayQueue queue) {
StackItem item = null;
- Iterator iterator = stack.descendingIterator();
+ final Iterator iterator = stack.descendingIterator();
while (iterator.hasNext()) {
- StackItem next = iterator.next();
+ final StackItem next = iterator.next();
if (next.getPlayQueue().equals(queue)) {
item = next;
break;
@@ -1763,7 +1762,7 @@ public class VideoDetailFragment
}
}
- private void showClearingQueueConfirmation(Runnable onAllow) {
+ private void showClearingQueueConfirmation(final Runnable onAllow) {
new AlertDialog.Builder(activity)
.setTitle(R.string.confirm_prompt)
.setMessage(R.string.clear_queue_confirmation_description)
@@ -1775,14 +1774,14 @@ public class VideoDetailFragment
}
/*
- * Remove unneeded information while waiting for a next task
- * */
+ * Remove unneeded information while waiting for a next task
+ * */
private void cleanUp() {
// New beginning
stack.clear();
if (currentWorker != null) currentWorker.dispose();
stopService();
- setInitialData(0,null,"", null);
+ setInitialData(0, null, "", null);
currentInfo = null;
updateOverlayData(null, null, null);
}
@@ -1792,10 +1791,10 @@ public class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/
private void setupBottomPlayer() {
- CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
- AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
+ final CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
- FrameLayout bottomSheetLayout = activity.findViewById(R.id.fragment_player_holder);
+ final FrameLayout bottomSheetLayout = activity.findViewById(R.id.fragment_player_holder);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
bottomSheetBehavior.setState(bottomSheetState);
final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
@@ -1810,7 +1809,8 @@ public class VideoDetailFragment
}
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
- @Override public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ @Override
+ public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
bottomSheetState = newState;
switch (newState) {
@@ -1843,7 +1843,9 @@ public class VideoDetailFragment
break;
}
}
- @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+
+ @Override
+ public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
setOverlayLook(appBarLayout, behavior, slideOffset);
}
});
@@ -1855,31 +1857,31 @@ public class VideoDetailFragment
});
}
- private void updateOverlayData(@Nullable String title, @Nullable String uploader, @Nullable String thumbnailUrl) {
+ private void updateOverlayData(@Nullable final String title, @Nullable final String uploader, @Nullable final String thumbnailUrl) {
overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title);
overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader);
overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(thumbnailUrl))
imageLoader.displayImage(thumbnailUrl, overlayThumbnailImageView,
- ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null);
+ ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null);
}
private void setOverlayPlayPauseImage() {
- int attr = player != null && player.getPlayer().getPlayWhenReady() ? R.attr.pause : R.attr.play;
+ final int attr = player != null && player.getPlayer().getPlayWhenReady() ? R.attr.pause : R.attr.play;
overlayPlayPauseButton.setImageResource(ThemeHelper.resolveResourceIdFromAttr(activity, attr));
}
- private void setOverlayLook(AppBarLayout appBarLayout, AppBarLayout.Behavior behavior, float slideOffset) {
+ private void setOverlayLook(final AppBarLayout appBarLayout, final AppBarLayout.Behavior behavior, final float slideOffset) {
if (behavior != null) {
overlay.setAlpha(Math.min(MAX_OVERLAY_ALPHA, 1 - slideOffset));
// These numbers are not special. They just do a cool transition
- behavior.setTopAndBottomOffset((int)(-thumbnailImageView.getHeight() * 2 * (1 - slideOffset) / 3));
+ behavior.setTopAndBottomOffset((int) (-thumbnailImageView.getHeight() * 2 * (1 - slideOffset) / 3));
appBarLayout.requestLayout();
}
}
- private void setOverlayElementsClickable(boolean enable) {
+ private void setOverlayElementsClickable(final boolean enable) {
overlayThumbnailImageView.setClickable(enable);
overlayThumbnailImageView.setLongClickable(enable);
overlayMetadata.setClickable(enable);
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index c31654473..634ce7779 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -274,7 +274,7 @@ public abstract class BasePlayer implements
return;
}
- boolean samePlayQueue = playQueue != null && playQueue.equals(queue);
+ final boolean samePlayQueue = playQueue != null && playQueue.equals(queue);
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index f1f9b51da..5bfa4732e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -102,7 +102,7 @@ public final class MainPlayer extends Service {
}
private void createView() {
- View layout = View.inflate(this, R.layout.activity_main_player, null);
+ final View layout = View.inflate(this, R.layout.activity_main_player, null);
playerImpl = new VideoPlayerImpl(this);
playerImpl.setup(layout);
@@ -124,7 +124,7 @@ public final class MainPlayer extends Service {
return START_NOT_STICKY;
}
- public void stop(boolean autoplayEnabled) {
+ public void stop(final boolean autoplayEnabled) {
if (DEBUG) Log.d(TAG, "stop() called");
if (playerImpl.getPlayer() != null) {
@@ -212,7 +212,7 @@ public final class MainPlayer extends Service {
if (getView().getParent() != null) {
if (playerImpl.getParentActivity() != null) {
// This means view was added to fragment
- ViewGroup parent = (ViewGroup) getView().getParent();
+ final ViewGroup parent = (ViewGroup) getView().getParent();
parent.removeView(getView());
} else
// This means view was added by windowManager for popup player
@@ -244,7 +244,7 @@ public final class MainPlayer extends Service {
setupNotification(notRemoteView);
setupNotification(bigNotRemoteView);
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@@ -254,7 +254,7 @@ public final class MainPlayer extends Service {
return builder;
}
- private void setupNotification(RemoteViews remoteViews) {
+ private void setupNotification(final RemoteViews remoteViews) {
// Don't show anything until player is playing
if (playerImpl == null) return;
@@ -298,7 +298,7 @@ public final class MainPlayer extends Service {
*
* @param drawableId if != -1, sets the drawable with that id on the play/pause button
*/
- synchronized void updateNotification(int drawableId) {
+ synchronized void updateNotification(final int drawableId) {
//if (DEBUG) Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
if (notBuilder == null) return;
if (drawableId != -1) {
@@ -329,7 +329,7 @@ public final class MainPlayer extends Service {
}
private Intent getIntentForNotification() {
- Intent intent;
+ final Intent intent;
if (playerImpl.audioPlayerSelected() || playerImpl.popupPlayerSelected()) {
// Means we play in popup or audio only. Let's show BackgroundPlayerActivity
intent = NavigationHelper.getBackgroundPlayerActivityIntent(getApplicationContext());
diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
index 619200727..9b5838a40 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
@@ -186,7 +186,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
}
Intent getSwitchIntent(final Class clazz, final MainPlayer.PlayerType playerType) {
- Intent intent = NavigationHelper.getPlayerIntent(
+ final Intent intent = NavigationHelper.getPlayerIntent(
getApplicationContext(),
clazz,
this.player.getPlayQueue(),
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index aad20b0ad..6c1843763 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -97,7 +97,7 @@ public class VideoPlayerImpl extends VideoPlayer
View.OnLongClickListener {
private static final String TAG = ".VideoPlayerImpl";
- private final float MAX_GESTURE_LENGTH = 0.75f;
+ private static final float MAX_GESTURE_LENGTH = 0.75f;
private TextView titleTextView;
private TextView channelTextView;
@@ -142,11 +142,11 @@ public class VideoPlayerImpl extends VideoPlayer
private boolean isVerticalVideo = false;
boolean shouldUpdateOnProgress;
- private MainPlayer service;
+ private final MainPlayer service;
private PlayerServiceEventListener fragmentListener;
private PlayerEventListener activityListener;
private GestureDetector gestureDetector;
- private SharedPreferences defaultPreferences;
+ private final SharedPreferences defaultPreferences;
private ContentObserver settingsContentObserver;
@NonNull
final private AudioPlaybackResolver resolver;
@@ -184,7 +184,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void handleIntent(Intent intent) {
if (intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) == null) return;
- MainPlayer.PlayerType oldPlayerType = playerType;
+ final MainPlayer.PlayerType oldPlayerType = playerType;
choosePlayerTypeFromIntent(intent);
audioOnly = audioPlayerSelected();
@@ -350,8 +350,8 @@ public class VideoPlayerImpl extends VideoPlayer
*/
private void setupElementsSize() {
if (popupPlayerSelected()) {
- int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_controls_padding);
- int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_buttons_padding);
+ final int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_controls_padding);
+ final int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_popup_buttons_padding);
getTopControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
getBottomControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
getQualityTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
@@ -361,10 +361,10 @@ public class VideoPlayerImpl extends VideoPlayer
getQualityTextView().setMinimumWidth(0);
getPlaybackSpeedTextView().setMinimumWidth(0);
} else if (videoPlayerSelected()) {
- int buttonsMinWidth = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_min_width);
- int playerTopPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_top_padding);
- int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_controls_padding);
- int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_padding);
+ final int buttonsMinWidth = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_min_width);
+ final int playerTopPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_top_padding);
+ final int controlsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_controls_padding);
+ final int buttonsPadding = service.getResources().getDimensionPixelSize(R.dimen.player_main_buttons_padding);
getTopControlsRoot().setPaddingRelative(controlsPadding, playerTopPadding, controlsPadding, 0);
getBottomControlsRoot().setPaddingRelative(controlsPadding, 0, controlsPadding, 0);
getQualityTextView().setPadding(buttonsPadding, buttonsPadding, buttonsPadding, buttonsPadding);
@@ -379,7 +379,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void initListeners() {
super.initListeners();
- PlayerGestureListener listener = new PlayerGestureListener(this, service);
+ final PlayerGestureListener listener = new PlayerGestureListener(this, service);
gestureDetector = new GestureDetector(context, listener);
getRootView().setOnTouchListener(listener);
@@ -415,7 +415,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (getRootView() == null || getRootView().getParent() == null || !(getRootView().getParent() instanceof ViewGroup))
return null;
- ViewGroup parent = (ViewGroup) getRootView().getParent();
+ final ViewGroup parent = (ViewGroup) getRootView().getParent();
return (AppCompatActivity) parent.getContext();
}
@@ -509,7 +509,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
@Override
- public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
+ public void onUpdateProgress(final int currentProgress, final int duration, final int bufferPercent) {
super.onUpdateProgress(currentProgress, duration, bufferPercent);
updateProgress(currentProgress, duration, bufferPercent);
@@ -560,7 +560,8 @@ public class VideoPlayerImpl extends VideoPlayer
}
@Override
- protected void initPlayback(@NonNull PlayQueue queue, int repeatMode, float playbackSpeed, float playbackPitch, boolean playbackSkipSilence, boolean playOnReady) {
+ protected void initPlayback(@NonNull PlayQueue queue, int repeatMode, float playbackSpeed,
+ float playbackPitch, boolean playbackSkipSilence, boolean playOnReady) {
super.initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, playOnReady);
updateQueue();
}
@@ -760,16 +761,16 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void setupScreenRotationButton() {
- boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
- boolean tabletInLandscape = isTablet(service) && service.isLandscape();
- boolean showButton = videoPlayerSelected() && (orientationLocked || isVerticalVideo || tabletInLandscape);
+ final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
+ final boolean tabletInLandscape = isTablet(service) && service.isLandscape();
+ final boolean showButton = videoPlayerSelected() && (orientationLocked || isVerticalVideo || tabletInLandscape);
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(service.getResources().getDrawable(
isFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white));
}
private void prepareOrientation() {
- boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
+ final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
if (orientationLocked && isFullscreen() && service.isLandscape() == isVerticalVideo && fragmentListener != null)
fragmentListener.onScreenRotationButtonClicked();
}
@@ -1068,8 +1069,8 @@ public class VideoPlayerImpl extends VideoPlayer
// because in that case the controls should be aligned to another side of a screen. The problem is when user leaves
// the app and returns back (while the app in landscape) Android reports via DisplayMetrics that orientation is
// portrait and it gives wrong sizes calculations. Let's skip re-calculation in every case but landscape
- boolean reportedOrientationIsLandscape = service.isLandscape();
- boolean actualOrientationIsLandscape = context.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ final boolean reportedOrientationIsLandscape = service.isLandscape();
+ final boolean actualOrientationIsLandscape = context.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
if (reportedOrientationIsLandscape && actualOrientationIsLandscape) setControlsSize();
// Close it because when changing orientation from portrait (in fullscreen mode) the size of queue layout can be
// larger than the screen size
@@ -1130,7 +1131,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
- private void choosePlayerTypeFromIntent(Intent intent) {
+ private void choosePlayerTypeFromIntent(final Intent intent) {
// If you want to open popup from the app just include Constants.POPUP_ONLY into an extra
if (intent.getIntExtra(PLAYER_TYPE, PLAYER_TYPE_VIDEO) == PLAYER_TYPE_AUDIO) {
playerType = MainPlayer.PlayerType.AUDIO;
@@ -1165,12 +1166,12 @@ public class VideoPlayerImpl extends VideoPlayer
return getPlayer() == null || getPlayer().getPlaybackState() == SimpleExoPlayer.STATE_IDLE;
}
- private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
+ private int distanceFromCloseButton(final MotionEvent popupMotionEvent) {
final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
- float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
- float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
+ final float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
+ final float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2));
}
@@ -1181,7 +1182,7 @@ public class VideoPlayerImpl extends VideoPlayer
return buttonRadius * 1.2f;
}
- public boolean isInsideClosingRadius(MotionEvent popupMotionEvent) {
+ public boolean isInsideClosingRadius(final MotionEvent popupMotionEvent) {
return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
}
@@ -1199,7 +1200,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
@Override
- public void showControls(long duration) {
+ public void showControls(final long duration) {
if (queueVisible) return;
showOrHideButtons();
@@ -1208,7 +1209,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
@Override
- public void hideControls(final long duration, long delay) {
+ public void hideControls(final long duration, final long delay) {
if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
showOrHideButtons();
@@ -1231,7 +1232,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void showSystemUIPartially() {
if (isFullscreen() && getParentActivity() != null) {
- int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
}
}
@@ -1247,40 +1248,40 @@ public class VideoPlayerImpl extends VideoPlayer
* NavigationBar and notches but not under them. Tablets have only bottom NavigationBar
*/
public void setControlsSize() {
- Point size = new Point();
- Display display = getRootView().getDisplay();
+ final Point size = new Point();
+ final Display display = getRootView().getDisplay();
if (display == null || !videoPlayerSelected()) return;
// This method will give a correct size of a usable area of a window.
// It doesn't include NavigationBar, notches, etc.
display.getSize(size);
- int width = isFullscreen ? (service.isLandscape() ? size.x : size.y) : ViewGroup.LayoutParams.MATCH_PARENT;
- int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
+ final int width = isFullscreen ? (service.isLandscape() ? size.x : size.y) : ViewGroup.LayoutParams.MATCH_PARENT;
+ final int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP;
getTopControlsRoot().getLayoutParams().width = width;
- RelativeLayout.LayoutParams topParams = ((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
+ final RelativeLayout.LayoutParams topParams = ((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
topParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
topParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
topParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START);
getTopControlsRoot().requestLayout();
getBottomControlsRoot().getLayoutParams().width = width;
- RelativeLayout.LayoutParams bottomParams = ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
+ final RelativeLayout.LayoutParams bottomParams = ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
bottomParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START);
getBottomControlsRoot().requestLayout();
- ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
+ final ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
// In tablet navigationBar located at the bottom of the screen. And the situations when we need to set custom height is
// in fullscreen mode in tablet in non-multiWindow mode or with vertical video. Other than that MATCH_PARENT is good
- boolean navBarAtTheBottom = PlayerHelper.isTablet(service) || !service.isLandscape();
+ final boolean navBarAtTheBottom = PlayerHelper.isTablet(service) || !service.isLandscape();
controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && navBarAtTheBottom
? size.y
: ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
- int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
+ final int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
getRootView().findViewById(R.id.playbackWindowRoot).setPadding(0, topPadding, 0, 0);
getRootView().findViewById(R.id.playbackWindowRoot).requestLayout();
}
@@ -1290,11 +1291,11 @@ public class VideoPlayerImpl extends VideoPlayer
*/
private int getStatusBarHeight() {
int statusBarHeight = 0;
- int resourceId = service.getResources().getIdentifier("status_bar_height_landscape", "dimen", "android");
+ final int resourceId = service.getResources().getIdentifier("status_bar_height_landscape", "dimen", "android");
if (resourceId > 0) statusBarHeight = service.getResources().getDimensionPixelSize(resourceId);
if (statusBarHeight == 0) {
// Some devices provide wrong value for status bar height in landscape mode, this is workaround
- DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
+ final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
statusBarHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, metrics);
}
return statusBarHeight;
@@ -1304,7 +1305,7 @@ public class VideoPlayerImpl extends VideoPlayer
* @return true if main player is attached to activity and activity inside multiWindow mode
*/
private boolean isInMultiWindow() {
- AppCompatActivity parent = getParentActivity();
+ final AppCompatActivity parent = getParentActivity();
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && parent != null && parent.isInMultiWindowMode();
}
@@ -1317,9 +1318,9 @@ public class VideoPlayerImpl extends VideoPlayer
}
public void checkLandscape() {
- AppCompatActivity parent = getParentActivity();
- boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isFullscreen() && videoPlayerSelected() && !audioOnly;
- boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED;
+ final AppCompatActivity parent = getParentActivity();
+ final boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isFullscreen() && videoPlayerSelected() && !audioOnly;
+ final boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED;
if (parent != null && videoInLandscapeButNotInFullscreen && playingState && !PlayerHelper.isTablet(service))
toggleFullscreen();
@@ -1342,7 +1343,7 @@ public class VideoPlayerImpl extends VideoPlayer
itemsListCloseButton.setOnClickListener(view -> onQueueClosed());
}
- public void useVideoSource(boolean video) {
+ public void useVideoSource(final boolean video) {
if (playQueue == null || audioOnly == !video || audioPlayerSelected())
return;
@@ -1415,7 +1416,7 @@ public class VideoPlayerImpl extends VideoPlayer
final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(service);
final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width);
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
+ final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
popupHeight = getMinimumVideoHeight(popupWidth);
@@ -1428,8 +1429,8 @@ public class VideoPlayerImpl extends VideoPlayer
popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
getSurfaceView().setHeights((int) popupHeight, (int) popupHeight);
- int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
- int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
+ final int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
+ final int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
popupLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
popupLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
@@ -1458,7 +1459,7 @@ public class VideoPlayerImpl extends VideoPlayer
final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams(
+ final WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
popupLayoutParamType(),
flags,
@@ -1523,19 +1524,19 @@ public class VideoPlayerImpl extends VideoPlayer
}
public void savePositionAndSize() {
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
+ final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service);
sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply();
sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply();
sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply();
}
- private float getMinimumVideoHeight(float width) {
+ private float getMinimumVideoHeight(final float width) {
//if (DEBUG) Log.d(TAG, "getMinimumVideoHeight() called with: width = [" + width + "], returned: " + height);
return width / (16.0f / 9.0f); // Respect the 16:9 ratio that most videos have
}
public void updateScreenSize() {
- DisplayMetrics metrics = new DisplayMetrics();
+ final DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
@@ -1604,7 +1605,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
public void removePopupFromView() {
- boolean isCloseOverlayHasParent = closeOverlayView != null && closeOverlayView.getParent() != null;
+ final boolean isCloseOverlayHasParent = closeOverlayView != null && closeOverlayView.getParent() != null;
if (popupHasParent())
windowManager.removeView(getRootView());
if (isCloseOverlayHasParent)
@@ -1640,7 +1641,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
private boolean popupHasParent() {
- View root = getRootView();
+ final View root = getRootView();
return root != null && root.getLayoutParams() instanceof WindowManager.LayoutParams && root.getParent() != null;
}
@@ -1648,27 +1649,27 @@ public class VideoPlayerImpl extends VideoPlayer
// Manipulations with listener
///////////////////////////////////////////////////////////////////////////
- public void setFragmentListener(PlayerServiceEventListener listener) {
+ public void setFragmentListener(final PlayerServiceEventListener listener) {
fragmentListener = listener;
updateMetadata();
updatePlayback();
triggerProgressUpdate();
}
- public void removeFragmentListener(PlayerServiceEventListener listener) {
+ public void removeFragmentListener(final PlayerServiceEventListener listener) {
if (fragmentListener == listener) {
fragmentListener = null;
}
}
- /*package-private*/ void setActivityListener(PlayerEventListener listener) {
+ void setActivityListener(final PlayerEventListener listener) {
activityListener = listener;
updateMetadata();
updatePlayback();
triggerProgressUpdate();
}
- /*package-private*/ void removeActivityListener(PlayerEventListener listener) {
+ void removeActivityListener(final PlayerEventListener listener) {
if (activityListener == listener) {
activityListener = null;
}
@@ -1703,7 +1704,7 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
- private void updateProgress(int currentProgress, int duration, int bufferPercent) {
+ private void updateProgress(final int currentProgress, final int duration, final int bufferPercent) {
if (fragmentListener != null) {
fragmentListener.onProgressUpdate(currentProgress, duration, bufferPercent);
}
@@ -1787,11 +1788,11 @@ public class VideoPlayerImpl extends VideoPlayer
return popupHeight;
}
- public void setPopupWidth(float width) {
+ public void setPopupWidth(final float width) {
popupWidth = width;
}
- public void setPopupHeight(float height) {
+ public void setPopupHeight(final float height) {
popupHeight = height;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
index 9d4707666..24ef5dffc 100644
--- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
+++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java
@@ -16,14 +16,14 @@ import java.util.List;
public class CustomBottomSheetBehavior extends BottomSheetBehavior {
- public CustomBottomSheetBehavior(Context context, AttributeSet attrs) {
+ public CustomBottomSheetBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
boolean visible;
Rect globalRect = new Rect();
private boolean skippingInterception = false;
- private List skipInterceptionOfElements = Arrays.asList(
+ private final List skipInterceptionOfElements = Arrays.asList(
R.id.detail_content_root_layout, R.id.relatedStreamsLayout, R.id.playQueuePanel, R.id.viewpager);
@Override
@@ -38,8 +38,8 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior
// Don't need to do anything if bottomSheet isn't expanded
if (getState() == BottomSheetBehavior.STATE_EXPANDED) {
// Without overriding scrolling will not work when user touches these elements
- for (Integer element : skipInterceptionOfElements) {
- ViewGroup viewGroup = child.findViewById(element);
+ for (final Integer element : skipInterceptionOfElements) {
+ final ViewGroup viewGroup = child.findViewById(element);
if (viewGroup != null) {
visible = viewGroup.getGlobalVisibleRect(globalRect);
if (visible && globalRect.contains((int) event.getRawX(), (int) event.getRawY())) {
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 f8b402103..8e4a5c2ac 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
@@ -20,8 +20,8 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
private static final String TAG = ".PlayerGestureListener";
private static final boolean DEBUG = BasePlayer.DEBUG;
- private VideoPlayerImpl playerImpl;
- private MainPlayer service;
+ private final VideoPlayerImpl playerImpl;
+ private final MainPlayer service;
private int initialPopupX, initialPopupY;
@@ -29,7 +29,7 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
private boolean isResizing;
- private int tossFlingVelocity;
+ private final int tossFlingVelocity;
private final boolean isVolumeGestureEnabled;
private final boolean isBrightnessGestureEnabled;
@@ -114,9 +114,9 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
//////////////////////////////////////////////////////////////////////////*/
private boolean onDoubleTapInMain(MotionEvent e) {
- if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
+ if (e.getX() > playerImpl.getRootView().getWidth() * 2.0 / 3.0) {
playerImpl.onFastForward();
- } else if (e.getX() < playerImpl.getRootView().getWidth() / 3) {
+ } else if (e.getX() < playerImpl.getRootView().getWidth() / 3.0) {
playerImpl.onFastRewind();
} else {
playerImpl.getPlayPauseButton().performClick();
@@ -143,7 +143,8 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
return true;
}
- private boolean onScrollInMain(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
+ private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent movingEvent,
+ final float distanceX, final float distanceY) {
if (!isVolumeGestureEnabled && !isBrightnessGestureEnabled) return false;
//noinspection PointlessBooleanExpression
@@ -162,9 +163,9 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
if (isVolumeGestureEnabled && initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
- float currentProgressPercent =
+ final float currentProgressPercent =
(float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
- int currentVolume = (int) (maxVolume * currentProgressPercent);
+ final int currentVolume = (int) (maxVolume * currentProgressPercent);
playerImpl.getAudioReactor().setVolume(currentVolume);
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
@@ -183,15 +184,15 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
}
} else if (isBrightnessGestureEnabled && initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0) {
- Activity parent = playerImpl.getParentActivity();
+ final Activity parent = playerImpl.getParentActivity();
if (parent == null) return true;
- Window window = parent.getWindow();
+ final Window window = parent.getWindow();
playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
- float currentProgressPercent =
+ final float currentProgressPercent =
(float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();
- WindowManager.LayoutParams layoutParams = window.getAttributes();
+ final WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.screenBrightness = currentProgressPercent;
window.setAttributes(layoutParams);
@@ -306,8 +307,10 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
isMovingInPopup = true;
- float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX);
- float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY);
+ final float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX());
+ float posX = (int) (initialPopupX + diffX);
+ final float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY());
+ float posY = (int) (initialPopupY + diffY);
if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth());
else if (posX < 0) posX = 0;
diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
index e739b8f33..482b36100 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
@@ -46,7 +46,7 @@ public abstract class PlayQueue implements Serializable {
private ArrayList backup;
private ArrayList streams;
- private ArrayList history;
+ private final ArrayList history;
@NonNull private final AtomicInteger queueIndex;
private transient BehaviorSubject eventBroadcast;
@@ -132,7 +132,7 @@ public abstract class PlayQueue implements Serializable {
* Returns the item at the given index.
* May throw {@link IndexOutOfBoundsException}.
* */
- public PlayQueueItem getItem(int index) {
+ public PlayQueueItem getItem(final int index) {
if (index < 0 || index >= streams.size() || streams.get(index) == null) return null;
return streams.get(index);
}
@@ -235,7 +235,7 @@ public abstract class PlayQueue implements Serializable {
* Will emit a {@link AppendEvent} on any given context.
* */
public synchronized void append(@NonNull final List items) {
- List itemList = new ArrayList<>(items);
+ final List itemList = new ArrayList<>(items);
if (isShuffled()) {
backup.addAll(itemList);
@@ -300,8 +300,7 @@ public abstract class PlayQueue implements Serializable {
}
if (backup != null) {
- final int backupIndex = backup.indexOf(getItem(removeIndex));
- backup.remove(backupIndex);
+ backup.remove(getItem(removeIndex));
}
history.remove(streams.remove(removeIndex));
@@ -332,7 +331,7 @@ public abstract class PlayQueue implements Serializable {
queueIndex.incrementAndGet();
}
- PlayQueueItem playQueueItem = streams.remove(source);
+ final PlayQueueItem playQueueItem = streams.remove(source);
playQueueItem.setAutoQueued(false);
streams.add(target, playQueueItem);
broadcast(new MoveEvent(source, target));
@@ -431,7 +430,7 @@ public abstract class PlayQueue implements Serializable {
history.remove(history.size() - 1);
- PlayQueueItem last = history.remove(history.size() - 1);
+ final PlayQueueItem last = history.remove(history.size() - 1);
setIndex(indexOf(last));
return true;
@@ -443,11 +442,11 @@ public abstract class PlayQueue implements Serializable {
* VideoDetailFragment without duplicating items from two identical queues
* */
@Override
- public boolean equals(@Nullable Object obj) {
+ public boolean equals(@Nullable final Object obj) {
if (!(obj instanceof PlayQueue) || getStreams().size() != ((PlayQueue) obj).getStreams().size())
return false;
- PlayQueue other = (PlayQueue) obj;
+ final PlayQueue other = (PlayQueue) obj;
for (int i = 0; i < getStreams().size(); i++) {
if (!getItem(i).getUrl().equals(other.getItem(i).getUrl()))
return false;
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index f0fd7e41b..e897e827f 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -114,13 +114,25 @@ public class NavigationHelper {
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
}
- public static void playOnMainPlayer(final AppCompatActivity activity, final PlayQueue queue, final boolean autoPlay) {
+ public static void playOnMainPlayer(
+ final AppCompatActivity activity,
+ final PlayQueue queue,
+ final boolean autoPlay) {
playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay);
}
- public static void playOnMainPlayer(final FragmentManager fragmentManager, final PlayQueue queue, boolean autoPlay) {
- PlayQueueItem currentStream = queue.getItem();
- openVideoDetailFragment(fragmentManager, currentStream.getServiceId(), currentStream.getUrl(), currentStream.getTitle(), autoPlay, queue);
+ public static void playOnMainPlayer(
+ final FragmentManager fragmentManager,
+ final PlayQueue queue,
+ final boolean autoPlay) {
+ final PlayQueueItem currentStream = queue.getItem();
+ openVideoDetailFragment(
+ fragmentManager,
+ currentStream.getServiceId(),
+ currentStream.getUrl(),
+ currentStream.getTitle(),
+ autoPlay,
+ queue);
}
public static void playOnMainPlayer(@NonNull final Context context,
@@ -131,7 +143,7 @@ public class NavigationHelper {
final boolean autoPlay,
final boolean resumePlayback) {
- Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback);
+ final Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_LINK_TYPE, linkType);
intent.putExtra(Constants.KEY_URL, url);
@@ -147,14 +159,14 @@ public class NavigationHelper {
}
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
- Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
+ final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
startService(context, intent);
}
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
- Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
+ final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
startService(context, intent);
}
@@ -170,7 +182,7 @@ public class NavigationHelper {
}
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
- Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
+ final Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
startService(context, intent);
}
@@ -179,9 +191,9 @@ public class NavigationHelper {
enqueueOnBackgroundPlayer(context, queue, false, resumePlayback);
}
- public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend, final boolean resumePlayback) {
+ public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean selectOnAppend, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
- Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
+ final Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
startService(context, intent);
}
@@ -307,25 +319,31 @@ public class NavigationHelper {
openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null);
}
- public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title, boolean autoPlay, PlayQueue playQueue) {
- Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder);
+ public static void openVideoDetailFragment(
+ FragmentManager fragmentManager,
+ int serviceId,
+ String url,
+ String title,
+ boolean autoPlay,
+ PlayQueue playQueue) {
+ final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder);
if (title == null) title = "";
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
- expandMainPlayer(fragment.getActivity());
- VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
+ expandMainPlayer(fragment.requireActivity());
+ final VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
detailFragment.setAutoplay(autoPlay);
detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue);
detailFragment.scrollToTop();
return;
}
- VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title, playQueue);
+ final VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title, playQueue);
instance.setAutoplay(autoPlay);
defaultTransaction(fragmentManager)
.replace(R.id.fragment_player_holder, instance)
- .runOnCommit(() -> expandMainPlayer(instance.getActivity()))
+ .runOnCommit(() -> expandMainPlayer(instance.requireActivity()))
.commit();
}
@@ -335,9 +353,9 @@ public class NavigationHelper {
}
public static void openChannelFragment(
- FragmentManager fragmentManager,
- int serviceId,
- String url,
+ final FragmentManager fragmentManager,
+ final int serviceId,
+ final String url,
String name) {
if (name == null) name = "";
defaultTransaction(fragmentManager)
@@ -525,7 +543,7 @@ public class NavigationHelper {
case STREAM:
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(context)
- .getBoolean(context.getString(R.string.autoplay_through_intent_key), false));
+ .getBoolean(context.getString(R.string.autoplay_through_intent_key), false));
break;
}
@@ -558,6 +576,7 @@ public class NavigationHelper {
/**
* Start an activity to install Kore
+ *
* @param context the context
*/
public static void installKore(Context context) {
@@ -566,13 +585,13 @@ public class NavigationHelper {
/**
* Start Kore app to show a video on Kodi
- *
+ *
* For a list of supported urls see the
*
- * Kore source code
+ * Kore source code
* .
*
- * @param context the context to use
+ * @param context the context to use
* @param videoURL the url to the video
*/
public static void playWithKore(Context context, Uri videoURL) {
diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
index f5a7df471..3d69e9cdc 100644
--- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
+++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java
@@ -25,14 +25,14 @@ public class ExpandableSurfaceView extends SurfaceView {
if (videoAspectRatio == 0.0f) return;
int width = MeasureSpec.getSize(widthMeasureSpec);
- boolean verticalVideo = videoAspectRatio < 1;
+ final boolean verticalVideo = videoAspectRatio < 1;
// Use maxHeight only on non-fit resize mode and in vertical videos
int height = maxHeight != 0 && resizeMode != AspectRatioFrameLayout.RESIZE_MODE_FIT && verticalVideo ? maxHeight : baseHeight;
if (height == 0) return;
- float viewAspectRatio = width / ((float) height);
- float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
+ final float viewAspectRatio = width / ((float) height);
+ final float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
scaleX = 1.0f;
scaleY = 1.0f;
@@ -73,15 +73,14 @@ public class ExpandableSurfaceView extends SurfaceView {
* @param base The height that will be used in every resize mode as a minimum height
* @param max The max height for vertical videos in non-FIT resize modes
*/
- public void setHeights(int base, int max) {
+ public void setHeights(final int base, final int max) {
if (baseHeight == base && maxHeight == max) return;
baseHeight = base;
maxHeight = max;
requestLayout();
}
- @AspectRatioFrameLayout.ResizeMode
- public void setResizeMode(int newResizeMode) {
+ public void setResizeMode(@AspectRatioFrameLayout.ResizeMode final int newResizeMode) {
if (resizeMode == newResizeMode) return;
resizeMode = newResizeMode;
@@ -93,7 +92,7 @@ public class ExpandableSurfaceView extends SurfaceView {
return resizeMode;
}
- public void setAspectRatio(float aspectRatio) {
+ public void setAspectRatio(final float aspectRatio) {
if (videoAspectRatio == aspectRatio) return;
videoAspectRatio = aspectRatio;
From bff238774ec3a92fe84289d300e7c476b0e9daab Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Mon, 13 Jul 2020 23:28:39 +0300
Subject: [PATCH 24/40] Small fixes of issues
---
.../fragments/detail/VideoDetailFragment.java | 9 ++-------
.../schabi/newpipe/player/VideoPlayerImpl.java | 17 +++++++++--------
.../player/event/PlayerGestureListener.java | 8 ++++++--
.../layout-large-land/activity_main_player.xml | 18 +++++++-----------
.../main/res/layout/activity_main_player.xml | 4 ++--
5 files changed, 26 insertions(+), 30 deletions(-)
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 ce6bd72f5..396363d31 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
@@ -470,11 +470,6 @@ public class VideoDetailFragment
@Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
-
- // Check if the next video label and video is visible,
- // if it is, include the two elements in the next check
- int nextCount = currentInfo != null && currentInfo.getNextVideo() != null ? 2 : 0;
-
if (!isLoading.get() && currentInfo != null && isVisible()) {
outState.putSerializable(INFO_KEY, currentInfo);
}
@@ -947,12 +942,12 @@ public class VideoDetailFragment
getString(R.string.show_age_restricted_content), false)) {
hideAgeRestrictedContent();
} else {
- currentInfo = result;
handleResult(result);
showContent();
if (addToBackStack) {
if (playQueue == null) playQueue = new SinglePlayQueue(result);
- stack.push(new StackItem(serviceId, url, name, playQueue));
+ if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue))
+ stack.push(new StackItem(serviceId, url, name, playQueue));
}
if (isAutoplayEnabled()) openVideoPlayer();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 1b72d7a96..d102a93ec 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -351,6 +351,7 @@ public class VideoPlayerImpl extends VideoPlayer
titleTextView.setVisibility(View.VISIBLE);
channelTextView.setVisibility(View.VISIBLE);
}
+ setMuteButton(muteButton, isMuted());
animateRotation(moreOptionsButton, DEFAULT_CONTROLS_DURATION, 0);
}
@@ -960,12 +961,12 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void onBlocked() {
super.onBlocked();
- playPauseButton.setImageResource(R.drawable.exo_controls_play);
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
animatePlayButtons(false, 100);
getRootView().setKeepScreenOn(false);
service.resetNotification();
- service.updateNotification(R.drawable.exo_controls_play);
+ service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
}
@Override
@@ -974,14 +975,14 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.exo_controls_play);
+ service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
}
@Override
public void onPlaying() {
super.onPlaying();
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
- playPauseButton.setImageResource(R.drawable.exo_controls_pause);
+ playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp);
animatePlayButtons(true, 200);
playPauseButton.requestFocus();
});
@@ -991,7 +992,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.exo_controls_pause);
+ service.updateNotification(R.drawable.ic_pause_white_24dp);
service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
}
@@ -1000,7 +1001,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void onPaused() {
super.onPaused();
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
- playPauseButton.setImageResource(R.drawable.exo_controls_play);
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
animatePlayButtons(true, 200);
playPauseButton.requestFocus();
});
@@ -1008,7 +1009,7 @@ public class VideoPlayerImpl extends VideoPlayer
updateWindowFlags(IDLE_WINDOW_FLAGS);
service.resetNotification();
- service.updateNotification(R.drawable.exo_controls_play);
+ service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
// Remove running notification when user don't want music (or video in popup) to be played in background
if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected())
@@ -1024,7 +1025,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.exo_controls_play);
+ service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
}
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 d7bc3a6a0..559a4d02b 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
@@ -175,7 +175,11 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
isMovingInMain = true;
- if (isVolumeGestureEnabled && initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0) {
+ boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
+ boolean acceptVolumeArea = acceptAnyArea
+ || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0;
+
+ if (isVolumeGestureEnabled && acceptVolumeArea) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
final float currentProgressPercent =
(float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
@@ -197,7 +201,7 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
}
- } else if (isBrightnessGestureEnabled && initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0) {
+ } else {
final Activity parent = playerImpl.getParentActivity();
if (parent == null) return true;
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 1ee2538c0..1a4e6b4e4 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -42,7 +42,6 @@
android:id="@+id/playQueuePanel"
android:layout_width="380dp"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_height="match_parent"
android:visibility="gone"
android:background="?attr/queue_background_color"
@@ -58,9 +57,7 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
- android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
- android:layout_marginRight="40dp"
android:layout_marginEnd="40dp"
android:padding="10dp"
android:clickable="true"
@@ -88,7 +85,6 @@
android:src="@drawable/exo_controls_repeat_off"
android:background="?android:selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
-
@@ -223,9 +218,10 @@
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 26a2ebc8b..92288933b 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -217,7 +217,6 @@
+ android:visibility="gone"
+ tools:visibility="visible"/>
-
@@ -504,7 +503,7 @@
app:srcCompat="@drawable/ic_pause_white_24dp"
tools:ignore="ContentDescription"/>
-
@@ -660,7 +659,6 @@
-
@@ -504,7 +504,7 @@
app:srcCompat="@drawable/ic_pause_white_24dp"
tools:ignore="ContentDescription"/>
-
diff --git a/app/src/main/res/layout/activity_player_queue_control.xml b/app/src/main/res/layout/activity_player_queue_control.xml
index c5b8e5743..c5d3b0eb8 100644
--- a/app/src/main/res/layout/activity_player_queue_control.xml
+++ b/app/src/main/res/layout/activity_player_queue_control.xml
@@ -184,7 +184,7 @@
app:srcCompat="@drawable/ic_repeat_white_24dp"
tools:ignore="ContentDescription"/>
-
-
Date: Tue, 21 Jul 2020 01:43:49 +0300
Subject: [PATCH 29/40] AndroidTvUtils -> DeviceUtils
---
.../java/org/schabi/newpipe/MainActivity.java | 6 +++---
.../org/schabi/newpipe/RouterActivity.java | 4 ++--
.../newpipe/download/DownloadActivity.java | 4 ++--
.../fragments/detail/VideoDetailFragment.java | 12 +++++------
.../fragments/list/search/SearchFragment.java | 4 ++--
.../holder/CommentsMiniInfoItemHolder.java | 4 ++--
.../subscription/dialog/FeedGroupDialog.kt | 4 ++--
.../newpipe/player/MainVideoPlayer.java | 10 +++++-----
.../newpipe/player/VideoPlayerImpl.java | 11 +++++-----
.../newpipe/player/helper/PlayerHelper.java | 7 -------
.../newpipe/settings/SettingsActivity.java | 4 ++--
.../{AndroidTvUtils.java => DeviceUtils.java} | 20 +++++++++++++------
.../newpipe/views/FocusAwareSeekBar.java | 4 ++--
13 files changed, 47 insertions(+), 47 deletions(-)
rename app/src/main/java/org/schabi/newpipe/util/{AndroidTvUtils.java => DeviceUtils.java} (81%)
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index ad5741503..fb1ca2342 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -68,7 +68,7 @@ import org.schabi.newpipe.player.VideoPlayer;
import org.schabi.newpipe.player.event.OnKeyDownListener;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization;
@@ -144,7 +144,7 @@ public class MainActivity extends AppCompatActivity {
ErrorActivity.reportUiError(this, e);
}
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this);
}
}
@@ -545,7 +545,7 @@ public class MainActivity extends AppCompatActivity {
Log.d(TAG, "onBackPressed() called");
}
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
View drawerPanel = findViewById(R.id.navigation);
if (drawer.isDrawerOpen(drawerPanel)) {
drawer.closeDrawers();
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index 423e88b76..e9e166c22 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -44,7 +44,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
@@ -347,7 +347,7 @@ public class RouterActivity extends AppCompatActivity {
alertDialog.show();
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(alertDialog);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java
index e46ded40d..5415c4ff8 100644
--- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java
@@ -13,7 +13,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import org.schabi.newpipe.R;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
@@ -57,7 +57,7 @@ public class DownloadActivity extends AppCompatActivity {
}
});
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this);
}
}
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 a87864a63..5310570c8 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
@@ -92,7 +92,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -767,7 +767,7 @@ public class VideoDetailFragment
thumbnailBackgroundButton.requestFocus();
- if (AndroidTvUtils.isTv(getContext())) {
+ if (DeviceUtils.isTv(getContext())) {
// remove ripple effects from detail controls
final int transparent = getResources().getColor(R.color.transparent_background_color);
detailControlsAddToPlaylist.setBackgroundColor(transparent);
@@ -880,7 +880,7 @@ public class VideoDetailFragment
// If we are in fullscreen mode just exit from it via first back press
if (player != null && player.isFullscreen()) {
- if (!PlayerHelper.isTablet(activity)) {
+ if (!DeviceUtils.isTablet(activity)) {
player.onPause();
}
restoreDefaultOrientation();
@@ -1449,7 +1449,7 @@ public class VideoDetailFragment
// User can tap on Play button and video will be in fullscreen mode again
// Note for tablet: trying to avoid orientation changes since it's not easy
// to physically rotate the tablet every time
- if (!PlayerHelper.isTablet(activity)) {
+ if (!DeviceUtils.isTablet(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
@@ -1945,7 +1945,7 @@ public class VideoDetailFragment
// In tablet user experience will be better if screen will not be rotated
// from landscape to portrait every time.
// Just turn on fullscreen mode in landscape orientation
- if (isLandscape() && PlayerHelper.isTablet(activity)) {
+ if (isLandscape() && DeviceUtils.isTablet(activity)) {
player.toggleFullscreen();
return;
}
@@ -2185,7 +2185,7 @@ public class VideoDetailFragment
&& player != null
&& player.isPlaying()
&& !player.isFullscreen()
- && !PlayerHelper.isTablet(activity)
+ && !DeviceUtils.isTablet(activity)
&& player.videoPlayerSelected()) {
player.toggleFullscreen();
}
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 33ab001c7..12abc29ae 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
@@ -48,7 +48,7 @@ import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -525,7 +525,7 @@ public class SearchFragment extends BaseListFragment {
- if (AndroidTvUtils.isTv(itemBuilder.getContext())) {
+ if (DeviceUtils.isTv(itemBuilder.getContext())) {
openCommentAuthor(item);
} else {
ShareUtils.copyToClipboard(itemBuilder.getContext(), commentText);
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
index 66387d298..80036cd4a 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
@@ -40,7 +40,7 @@ import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialogViewModel.Dia
import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem
import org.schabi.newpipe.local.subscription.item.PickerIconItem
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
-import org.schabi.newpipe.util.AndroidTvUtils
+import org.schabi.newpipe.util.DeviceUtils
import org.schabi.newpipe.util.ThemeHelper
class FeedGroupDialog : DialogFragment(), BackPressable {
@@ -237,7 +237,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
}
toolbar_search_edit_text.setOnClickListener {
- if (AndroidTvUtils.isTv(context)) {
+ if (DeviceUtils.isTv(context)) {
showKeyboardSearch()
}
}
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 51e159469..f87887685 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
@@ -80,7 +80,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.KoreUtil;
import org.schabi.newpipe.util.ListHelper;
@@ -179,7 +179,7 @@ public final class MainVideoPlayer extends AppCompatActivity
final String orientKey = getString(R.string.last_orientation_landscape_key);
final boolean lastOrientationWasLandscape = defaultPreferences
- .getBoolean(orientKey, AndroidTvUtils.isTv(getApplicationContext()));
+ .getBoolean(orientKey, DeviceUtils.isTv(getApplicationContext()));
setLandscape(lastOrientationWasLandscape);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
@@ -191,7 +191,7 @@ public final class MainVideoPlayer extends AppCompatActivity
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
false, rotationObserver);
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this);
}
}
@@ -223,7 +223,7 @@ public final class MainVideoPlayer extends AppCompatActivity
default:
break;
case KeyEvent.KEYCODE_BACK:
- if (AndroidTvUtils.isTv(getApplicationContext())
+ if (DeviceUtils.isTv(getApplicationContext())
&& playerImpl.isControlsVisible()) {
playerImpl.hideControls(0, 0);
hideSystemUi();
@@ -272,7 +272,7 @@ public final class MainVideoPlayer extends AppCompatActivity
final String orientKey = getString(R.string.last_orientation_landscape_key);
boolean lastOrientationWasLandscape = defaultPreferences
- .getBoolean(orientKey, AndroidTvUtils.isTv(getApplicationContext()));
+ .getBoolean(orientKey, DeviceUtils.isTv(getApplicationContext()));
setLandscape(lastOrientationWasLandscape);
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index ec01ef11e..fd3f38ab7 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -94,7 +94,7 @@ 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.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.KoreUtil;
@@ -117,7 +117,6 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
import static org.schabi.newpipe.player.MainPlayer.NOTIFICATION_ID;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
-import static org.schabi.newpipe.player.helper.PlayerHelper.isTablet;
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;
@@ -481,7 +480,7 @@ public class VideoPlayerImpl extends VideoPlayer
default:
break;
case KeyEvent.KEYCODE_BACK:
- if (AndroidTvUtils.isTv(service) && isControlsVisible()) {
+ if (DeviceUtils.isTv(service) && isControlsVisible()) {
hideControls(0, 0);
hideSystemUIIfNeeded();
return true;
@@ -930,7 +929,7 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupScreenRotationButton() {
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
- final boolean tabletInLandscape = isTablet(service) && service.isLandscape();
+ final boolean tabletInLandscape = DeviceUtils.isTablet(service) && service.isLandscape();
final boolean showButton = videoPlayerSelected()
&& (orientationLocked || isVerticalVideo || tabletInLandscape);
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
@@ -1542,7 +1541,7 @@ public class VideoPlayerImpl extends VideoPlayer
// And the situations when we need to set custom height is
// in fullscreen mode in tablet in non-multiWindow mode or with vertical video.
// Other than that MATCH_PARENT is good
- final boolean navBarAtTheBottom = PlayerHelper.isTablet(service) || !service.isLandscape();
+ final boolean navBarAtTheBottom = DeviceUtils.isTablet(service) || !service.isLandscape();
controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow()
&& navBarAtTheBottom ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
@@ -1612,7 +1611,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (parent != null
&& videoInLandscapeButNotInFullscreen
&& playingState
- && !PlayerHelper.isTablet(service)) {
+ && !DeviceUtils.isTablet(service)) {
toggleFullscreen();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index bd681ec44..58732bda2 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -362,13 +362,6 @@ public final class PlayerHelper {
context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 0;
}
- public static boolean isTablet(@NonNull final Context context) {
- return (context
- .getResources()
- .getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
- >= Configuration.SCREENLAYOUT_SIZE_LARGE;
- }
-
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 18cbece6f..26a33917f 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -13,7 +13,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import org.schabi.newpipe.R;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
@@ -62,7 +62,7 @@ public class SettingsActivity extends AppCompatActivity
.commit();
}
- if (AndroidTvUtils.isTv(this)) {
+ if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/AndroidTvUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
similarity index 81%
rename from app/src/main/java/org/schabi/newpipe/util/AndroidTvUtils.java
rename to app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
index db2ab4aa7..0afa0663c 100644
--- a/app/src/main/java/org/schabi/newpipe/util/AndroidTvUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
@@ -8,22 +8,23 @@ import android.os.BatteryManager;
import android.os.Build;
import android.view.KeyEvent;
+import androidx.annotation.NonNull;
import org.schabi.newpipe.App;
import static android.content.Context.BATTERY_SERVICE;
import static android.content.Context.UI_MODE_SERVICE;
-public final class AndroidTvUtils {
+public final class DeviceUtils {
private static final String AMAZON_FEATURE_FIRE_TV = "amazon.hardware.fire_tv";
private static Boolean isTV = null;
- private AndroidTvUtils() {
+ private DeviceUtils() {
}
public static boolean isTv(final Context context) {
- if (AndroidTvUtils.isTV != null) {
- return AndroidTvUtils.isTV;
+ if (isTV != null) {
+ return isTV;
}
PackageManager pm = App.getApp().getPackageManager();
@@ -48,8 +49,15 @@ public final class AndroidTvUtils {
isTv = isTv || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
- AndroidTvUtils.isTV = isTv;
- return AndroidTvUtils.isTV;
+ DeviceUtils.isTV = isTv;
+ return DeviceUtils.isTV;
+ }
+
+ public static boolean isTablet(@NonNull final Context context) {
+ return (context
+ .getResources()
+ .getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+ >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
public static boolean isConfirmKey(final int keyCode) {
diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java b/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java
index 6dbcded48..a50d5a64c 100644
--- a/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java
+++ b/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java
@@ -26,7 +26,7 @@ import android.widget.SeekBar;
import androidx.appcompat.widget.AppCompatSeekBar;
-import org.schabi.newpipe.util.AndroidTvUtils;
+import org.schabi.newpipe.util.DeviceUtils;
/**
* SeekBar, adapted for directional navigation. It emulates touch-related callbacks
@@ -60,7 +60,7 @@ public final class FocusAwareSeekBar extends AppCompatSeekBar {
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
- if (!isInTouchMode() && AndroidTvUtils.isConfirmKey(keyCode)) {
+ if (!isInTouchMode() && DeviceUtils.isConfirmKey(keyCode)) {
releaseTrack();
}
From 77cd3182f10f616eba93768fbefd9001de09d22f Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Tue, 21 Jul 2020 01:53:59 +0300
Subject: [PATCH 30/40] Removed unused line
---
.../main/java/org/schabi/newpipe/player/helper/PlayerHelper.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 58732bda2..0a4dd83ac 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -2,7 +2,6 @@ package org.schabi.newpipe.player.helper;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.Settings;
From 3ecbbea7cbcb5feba59e543835b4ff3edbf55edf Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 22 Jul 2020 01:20:30 +0300
Subject: [PATCH 31/40] Better TV support, icons, activity_main refactoring -
on Android TV you'll be able to navigate with D-pad in main fragment and in
the player. But not between them for now - play/pause/next/previous buttons
are smaller now - replaced ic_list with previous version of it -
activity_main looks better which helps with Android TV support
---
.../fragments/detail/VideoDetailFragment.java | 10 +++---
.../schabi/newpipe/player/VideoPlayer.java | 2 +-
.../newpipe/player/VideoPlayerImpl.java | 15 +--------
.../main/res/drawable/ic_list_black_24dp.xml | 11 ++++---
.../main/res/drawable/ic_list_white_24dp.xml | 12 +++----
.../activity_main_player.xml | 18 ++++++-----
app/src/main/res/layout/activity_main.xml | 31 +++++++------------
.../main/res/layout/activity_main_player.xml | 18 ++++++-----
.../layout/activity_player_queue_control.xml | 8 ++---
app/src/main/res/values/strings.xml | 2 +-
10 files changed, 53 insertions(+), 74 deletions(-)
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 5310570c8..a83acd476 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
@@ -570,10 +570,6 @@ public class VideoDetailFragment
@Override
public void onClick(final View v) {
- if (isLoading.get() || currentInfo == null) {
- return;
- }
-
switch (v.getId()) {
case R.id.detail_controls_background:
openBackgroundPlayer(false);
@@ -2168,6 +2164,7 @@ public class VideoDetailFragment
@Override
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
bottomSheetState = newState;
+ ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
@@ -2175,6 +2172,7 @@ public class VideoDetailFragment
cleanUp();
break;
case BottomSheetBehavior.STATE_EXPANDED:
+ mainFragment.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
bottomSheetBehavior.setPeekHeight(peekHeight);
// Disable click because overlay buttons located on top of buttons
// from the player
@@ -2191,6 +2189,8 @@ public class VideoDetailFragment
}
break;
case BottomSheetBehavior.STATE_COLLAPSED:
+ mainFragment.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ mainFragment.requestFocus();
// Re-enable clicks
setOverlayElementsClickable(true);
if (player != null) {
@@ -2236,7 +2236,7 @@ public class VideoDetailFragment
}
private void setOverlayPlayPauseImage() {
- final int attr = player != null && player.getPlayer().getPlayWhenReady()
+ final int attr = player != null && player.isPlaying()
? R.attr.ic_pause
: R.attr.ic_play_arrow;
overlayPlayPauseButton.setImageResource(
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 51f9f3ca2..e621f9f33 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
@@ -204,7 +204,7 @@ public abstract class VideoPlayer extends BasePlayer
final CaptionStyleCompat captionStyle = PlayerHelper.getCaptionStyle(context);
setupSubtitleView(subtitleView, captionScale, captionStyle);
- this.resizeView = view.findViewById(R.id.resizeTextView);
+ this.resizeView = view.findViewById(R.id.resizeTextView);
resizeView.setText(PlayerHelper
.resizeTypeOf(context, getSurfaceView().getResizeMode()));
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index fd3f38ab7..4eea23a0b 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -22,8 +22,6 @@ package org.schabi.newpipe.player;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
@@ -60,7 +58,6 @@ import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.ItemTouchHelper;
@@ -892,7 +889,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (DEBUG) {
Log.i(TAG, "Failed to start kore", e);
}
- showInstallKoreDialog(getParentActivity());
+ KoreUtil.showInstallKoreDialog(getParentActivity());
}
}
@@ -917,16 +914,6 @@ public class VideoPlayerImpl extends VideoPlayer
? View.VISIBLE : View.GONE);
}
- private static void showInstallKoreDialog(final Context context) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setMessage(R.string.kore_not_found)
- .setPositiveButton(R.string.install, (DialogInterface dialog, int which) ->
- NavigationHelper.installKore(context))
- .setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
- });
- builder.create().show();
- }
-
private void setupScreenRotationButton() {
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
final boolean tabletInLandscape = DeviceUtils.isTablet(service) && service.isLandscape();
diff --git a/app/src/main/res/drawable/ic_list_black_24dp.xml b/app/src/main/res/drawable/ic_list_black_24dp.xml
index 7020c1c56..4c2fb8834 100644
--- a/app/src/main/res/drawable/ic_list_black_24dp.xml
+++ b/app/src/main/res/drawable/ic_list_black_24dp.xml
@@ -1,8 +1,9 @@
-
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
diff --git a/app/src/main/res/drawable/ic_list_white_24dp.xml b/app/src/main/res/drawable/ic_list_white_24dp.xml
index c9ae06017..f47037629 100644
--- a/app/src/main/res/drawable/ic_list_white_24dp.xml
+++ b/app/src/main/res/drawable/ic_list_white_24dp.xml
@@ -1,9 +1,5 @@
-
-
+
+
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 92288933b..91627f2d4 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -247,9 +247,12 @@
-
-
-
+ android:layout_height="match_parent">
+
-
-
-
+ android:id="@+id/fragment_player_holder"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ app:behavior_hideable="true"
+ app:behavior_peekHeight="0dp"
+ app:layout_behavior="org.schabi.newpipe.player.event.CustomBottomSheetBehavior">
-
-
+
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index e4a1e7c0c..fabb46bcc 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -250,9 +250,12 @@
-
Use fast inexact seek
Inexact seek allows the player to seek to positions faster with reduced precision. Seeking for 5, 15 or 25 seconds doesn\'t work with this.
Fast-forward/-rewind seek duration
- Ask confirmation before clearing a queue
+ Ask for confirmation before clearing a queue
After switching from one player to another your queue may be replaced
Queue from the active player will be replaced
Load thumbnails
From 7aa8a5c368e0142ff0008c9c0c7c876477b8935f Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 22 Jul 2020 02:20:58 +0300
Subject: [PATCH 32/40] Fixed a situation when background playback could use a
video stream instead of an audio stream
---
.../fragments/detail/VideoDetailFragment.java | 4 +-
.../newpipe/player/VideoPlayerImpl.java | 42 ++++++++++++-------
2 files changed, 30 insertions(+), 16 deletions(-)
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 a83acd476..c920a103a 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
@@ -2164,10 +2164,10 @@ public class VideoDetailFragment
@Override
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
bottomSheetState = newState;
- ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
-
+ final ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
+ mainFragment.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
bottomSheetBehavior.setPeekHeight(0);
cleanUp();
break;
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 4eea23a0b..435396fd6 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -187,6 +187,7 @@ public class VideoPlayerImpl extends VideoPlayer
private boolean audioOnly = false;
private boolean isFullscreen = false;
private boolean isVerticalVideo = false;
+ private boolean fragmentIsVisible = false;
boolean shouldUpdateOnProgress;
int timesNotificationUpdated;
@@ -1224,28 +1225,22 @@ public class VideoPlayerImpl extends VideoPlayer
break;
case ACTION_PLAY_PAUSE:
onPlayPause();
+ if (!fragmentIsVisible) {
+ // Ensure that we have audio-only stream playing when a user
+ // started to play from notification's play button from outside of the app
+ onFragmentStopped();
+ }
break;
case ACTION_REPEAT:
onRepeatClicked();
break;
case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED:
+ fragmentIsVisible = true;
useVideoSource(true);
break;
case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED:
- // This will be called when user goes to another app/activity, turns off a screen.
- // We don't want to interrupt playback and don't want to see notification
- // if player is stopped.
- // Next lines of code will enable background playback if needed
- if (videoPlayerSelected() && (isPlaying() || isLoading())) {
- if (backgroundPlaybackEnabled()) {
- useVideoSource(false);
- } else if (minimizeOnPopupEnabled()) {
- setRecovery();
- NavigationHelper.playOnPopupPlayer(getParentActivity(), playQueue, true);
- } else {
- onPause();
- }
- }
+ fragmentIsVisible = false;
+ onFragmentStopped();
break;
case Intent.ACTION_CONFIGURATION_CHANGED:
assureCorrectAppLanguage(service);
@@ -1998,6 +1993,7 @@ public class VideoPlayerImpl extends VideoPlayer
public void setFragmentListener(final PlayerServiceEventListener listener) {
fragmentListener = listener;
+ fragmentIsVisible = true;
updateMetadata();
updatePlayback();
triggerProgressUpdate();
@@ -2072,6 +2068,24 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
+ /**
+ * This will be called when a user goes to another app/activity, turns off a screen.
+ * We don't want to interrupt playback and don't want to see notification so
+ * next lines of code will enable audio-only playback only if needed
+ * */
+ private void onFragmentStopped() {
+ if (videoPlayerSelected() && (isPlaying() || isLoading())) {
+ if (backgroundPlaybackEnabled()) {
+ useVideoSource(false);
+ } else if (minimizeOnPopupEnabled()) {
+ setRecovery();
+ NavigationHelper.playOnPopupPlayer(getParentActivity(), playQueue, true);
+ } else {
+ onPause();
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////
From 91a0257c8f1a4b190936bc933ed18f8ee081d876 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 22 Jul 2020 17:19:32 +0300
Subject: [PATCH 33/40] Fixes for Android API <21
---
.../schabi/newpipe/player/VideoPlayerImpl.java | 18 +++++++++---------
.../fragment_video_detail.xml | 2 +-
.../main/res/layout/fragment_video_detail.xml | 2 +-
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 435396fd6..70642481e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -370,7 +370,7 @@ public class VideoPlayerImpl extends VideoPlayer
getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
secondaryControls.setVisibility(View.INVISIBLE);
- moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
+ moreOptionsButton.setImageDrawable(AppCompatResources.getDrawable(service,
R.drawable.ic_expand_more_white_24dp));
shareButton.setVisibility(View.VISIBLE);
showHideKodiButton();
@@ -921,9 +921,9 @@ public class VideoPlayerImpl extends VideoPlayer
final boolean showButton = videoPlayerSelected()
&& (orientationLocked || isVerticalVideo || tabletInLandscape);
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
- screenRotationButton.setImageDrawable(service.getResources().getDrawable(isFullscreen()
- ? R.drawable.ic_fullscreen_exit_white_24dp
- : R.drawable.ic_fullscreen_white_24dp));
+ screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen()
+ ? R.drawable.ic_fullscreen_exit_white_24dp
+ : R.drawable.ic_fullscreen_white_24dp));
}
private void prepareOrientation() {
@@ -1077,7 +1077,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(false);
service.resetNotification();
- service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
+ service.updateNotification(R.drawable.exo_controls_play);
}
@Override
@@ -1086,7 +1086,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
+ service.updateNotification(R.drawable.exo_controls_play);
}
@Override
@@ -1103,7 +1103,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.ic_pause_white_24dp);
+ service.updateNotification(R.drawable.exo_controls_pause);
service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
}
@@ -1120,7 +1120,7 @@ public class VideoPlayerImpl extends VideoPlayer
updateWindowFlags(IDLE_WINDOW_FLAGS);
service.resetNotification();
- service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
+ service.updateNotification(R.drawable.exo_controls_play);
// Remove running notification when user don't want music (or video in popup)
// to be played in background
@@ -1138,7 +1138,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setKeepScreenOn(true);
service.resetNotification();
- service.updateNotification(R.drawable.ic_play_arrow_white_24dp);
+ service.updateNotification(R.drawable.exo_controls_play);
}
diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml
index 192993c82..e2a611b08 100644
--- a/app/src/main/res/layout-large-land/fragment_video_detail.xml
+++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml
@@ -675,7 +675,7 @@
android:layout_marginRight="2dp"
android:padding="10dp"
android:scaleType="center"
- android:src="?attr/ic_play_arrow"
+ app:srcCompat="?attr/ic_play_arrow"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index cc1f25646..1dd5a7135 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -648,7 +648,7 @@
android:layout_marginRight="2dp"
android:padding="10dp"
android:scaleType="center"
- android:src="?attr/ic_play_arrow"
+ app:srcCompat="?attr/ic_play_arrow"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
From 7c79d421e8928d0596e13ab043b87f2fd3946a3a Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Fri, 24 Jul 2020 00:43:09 +0300
Subject: [PATCH 34/40] Quality selector for external playback and better
fullscreen mode for old devices
---
.../fragments/detail/VideoDetailFragment.java | 41 ++++++++++++-------
.../newpipe/player/VideoPlayerImpl.java | 4 +-
2 files changed, 30 insertions(+), 15 deletions(-)
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 c920a103a..be7d0316a 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
@@ -370,7 +370,7 @@ public class VideoDetailFragment
/*////////////////////////////////////////////////////////////////////////*/
public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl,
- final String name, final PlayQueue playQueue) {
+ final String name, final PlayQueue playQueue) {
VideoDetailFragment instance = new VideoDetailFragment();
instance.setInitialData(serviceId, videoUrl, name, playQueue);
return instance;
@@ -1142,11 +1142,7 @@ public class VideoDetailFragment
private void openVideoPlayer() {
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
- final VideoStream selectedVideoStream = getSelectedVideoStream();
- if (selectedVideoStream == null) {
- return;
- }
- startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
+ showExternalPlaybackDialog();
} else {
replaceQueueIfUserConfirms(this::openMainPlayer);
}
@@ -1302,12 +1298,6 @@ public class VideoDetailFragment
viewHolder.requestLayout();
}
-
- @Nullable
- private VideoStream getSelectedVideoStream() {
- return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;
- }
-
private void prepareDescription(final Description description) {
if (description == null || TextUtils.isEmpty(description.getContent())
|| description == Description.emptyDescription) {
@@ -2113,8 +2103,7 @@ public class VideoDetailFragment
private void showClearingQueueConfirmation(final Runnable onAllow) {
new AlertDialog.Builder(activity)
- .setTitle(R.string.confirm_prompt)
- .setMessage(R.string.clear_queue_confirmation_description)
+ .setTitle(R.string.clear_queue_confirmation_description)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
onAllow.run();
@@ -2122,6 +2111,30 @@ public class VideoDetailFragment
}).show();
}
+ private void showExternalPlaybackDialog() {
+ if (sortedVideoStreams == null) {
+ return;
+ }
+ CharSequence[] resolutions = new CharSequence[sortedVideoStreams.size()];
+ for (int i = 0; i < sortedVideoStreams.size(); i++) {
+ resolutions[i] = sortedVideoStreams.get(i).getResolution();
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setNeutralButton(R.string.open_in_browser, (dialog, i) ->
+ ShareUtils.openUrlInBrowser(requireActivity(), url)
+ );
+ // Maybe there are no video streams available, show just `open in browser` button
+ if (resolutions.length > 0) {
+ builder.setSingleChoiceItems(resolutions, selectedVideoStreamIndex, (dialog, i) -> {
+ dialog.dismiss();
+ startOnExternalPlayer(activity, currentInfo, sortedVideoStreams.get(i));
+ }
+ );
+ }
+ builder.show();
+ }
+
/*
* Remove unneeded information while waiting for a next task
* */
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 70642481e..0c5bfd8f6 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -1463,7 +1463,9 @@ public class VideoPlayerImpl extends VideoPlayer
private void showSystemUIPartially() {
if (isFullscreen() && getParentActivity() != null) {
- final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
}
}
From 08db1d59e57d5c0db46911e2894b85b7c1de613e Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 25 Jul 2020 04:14:29 +0300
Subject: [PATCH 35/40] Android TV: ability to select all buttons in the main
player, as well as in the main fragment
---
.../fragments/detail/VideoDetailFragment.java | 37 ++++-
.../newpipe/player/VideoPlayerImpl.java | 28 ++--
.../newpipe/views/FocusOverlayView.java | 15 +-
.../activity_main_player.xml | 153 +++++++++---------
.../fragment_video_detail.xml | 3 +
.../main/res/layout/activity_main_player.xml | 148 +++++++++--------
6 files changed, 216 insertions(+), 168 deletions(-)
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 be7d0316a..1d83094dc 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
@@ -34,6 +34,7 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
@@ -2154,6 +2155,30 @@ public class VideoDetailFragment
// Bottom mini player
//////////////////////////////////////////////////////////////////////////*/
+ /**
+ * That's for Android TV support. Move focus from main fragment to the player or back
+ * based on what is currently selected
+ * @param toMain if true than the main fragment will be focused or the player otherwise
+ * */
+ private void moveFocusToMainFragment(final boolean toMain) {
+ final ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
+ // Hamburger button steels a focus even under bottomSheet
+ final Toolbar toolbar = requireActivity().findViewById(R.id.toolbar);
+ final int afterDescendants = ViewGroup.FOCUS_AFTER_DESCENDANTS;
+ final int blockDescendants = ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+ if (toMain) {
+ mainFragment.setDescendantFocusability(afterDescendants);
+ toolbar.setDescendantFocusability(afterDescendants);
+ ((ViewGroup) requireView()).setDescendantFocusability(blockDescendants);
+ mainFragment.requestFocus();
+ } else {
+ mainFragment.setDescendantFocusability(blockDescendants);
+ toolbar.setDescendantFocusability(blockDescendants);
+ ((ViewGroup) requireView()).setDescendantFocusability(afterDescendants);
+ thumbnailBackgroundButton.requestFocus();
+ }
+ }
+
private void setupBottomPlayer() {
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
@@ -2177,15 +2202,17 @@ public class VideoDetailFragment
@Override
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
bottomSheetState = newState;
- final ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
+
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
- mainFragment.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ moveFocusToMainFragment(true);
+
bottomSheetBehavior.setPeekHeight(0);
cleanUp();
break;
case BottomSheetBehavior.STATE_EXPANDED:
- mainFragment.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ moveFocusToMainFragment(false);
+
bottomSheetBehavior.setPeekHeight(peekHeight);
// Disable click because overlay buttons located on top of buttons
// from the player
@@ -2202,8 +2229,8 @@ public class VideoDetailFragment
}
break;
case BottomSheetBehavior.STATE_COLLAPSED:
- mainFragment.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mainFragment.requestFocus();
+ moveFocusToMainFragment(true);
+
// Re-enable clicks
setOverlayElementsClickable(true);
if (player != null) {
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 0c5bfd8f6..8a4f7fdc5 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -480,7 +480,6 @@ public class VideoPlayerImpl extends VideoPlayer
case KeyEvent.KEYCODE_BACK:
if (DeviceUtils.isTv(service) && isControlsVisible()) {
hideControls(0, 0);
- hideSystemUIIfNeeded();
return true;
}
break;
@@ -499,7 +498,9 @@ public class VideoPlayerImpl extends VideoPlayer
}
if (!isControlsVisible()) {
- playPauseButton.requestFocus();
+ if (!queueVisible) {
+ playPauseButton.requestFocus();
+ }
showControlsThenHide();
showSystemUIPartially();
return true;
@@ -805,7 +806,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (v.getId() == playPauseButton.getId()) {
hideControls(0, 0);
} else {
- safeHideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
+ hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
}
}
});
@@ -830,6 +831,7 @@ public class VideoPlayerImpl extends VideoPlayer
updatePlaybackButtons();
getControlsRoot().setVisibility(View.INVISIBLE);
+ queueLayout.requestFocus();
animateView(queueLayout, SLIDE_AND_ALPHA, true,
DEFAULT_CONTROLS_DURATION);
@@ -848,6 +850,7 @@ public class VideoPlayerImpl extends VideoPlayer
queueLayout.setTranslationY(-queueLayout.getHeight() * 5);
});
queueVisible = false;
+ playPauseButton.requestFocus();
}
private void onMoreOptionsClicked() {
@@ -1095,7 +1098,9 @@ public class VideoPlayerImpl extends VideoPlayer
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp);
animatePlayButtons(true, 200);
- playPauseButton.requestFocus();
+ if (!queueVisible) {
+ playPauseButton.requestFocus();
+ }
});
updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS);
@@ -1114,7 +1119,9 @@ public class VideoPlayerImpl extends VideoPlayer
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
animatePlayButtons(true, 200);
- playPauseButton.requestFocus();
+ if (!queueVisible) {
+ playPauseButton.requestFocus();
+ }
});
updateWindowFlags(IDLE_WINDOW_FLAGS);
@@ -1401,12 +1408,10 @@ public class VideoPlayerImpl extends VideoPlayer
return isFullscreen;
}
- @Override
public void showControlsThenHide() {
- if (queueVisible) {
- return;
+ if (DEBUG) {
+ Log.d(TAG, "showControlsThenHide() called");
}
-
showOrHideButtons();
showSystemUIPartially();
super.showControlsThenHide();
@@ -1414,10 +1419,9 @@ public class VideoPlayerImpl extends VideoPlayer
@Override
public void showControls(final long duration) {
- if (queueVisible) {
- return;
+ if (DEBUG) {
+ Log.d(TAG, "showControls() called with: duration = [" + duration + "]");
}
-
showOrHideButtons();
showSystemUIPartially();
super.showControls(duration);
diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java
index 1c868f66a..b23f98d79 100644
--- a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java
+++ b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java
@@ -38,6 +38,7 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.view.WindowCallbackWrapper;
@@ -113,7 +114,9 @@ public final class FocusOverlayView extends Drawable implements
if (focusedView != null) {
focusedView.getGlobalVisibleRect(focusRect);
- } else {
+ }
+
+ if (shouldClearFocusRect(focusedView, focusRect)) {
focusRect.setEmpty();
}
@@ -184,6 +187,16 @@ public final class FocusOverlayView extends Drawable implements
public void setColorFilter(final ColorFilter colorFilter) {
}
+ /*
+ * When any view in the player looses it's focus (after setVisibility(GONE)) the focus gets
+ * added to the whole fragment which has a width and height equal to the window frame.
+ * The easiest way to avoid the unneeded frame is to skip highlighting of rect that is
+ * equal to the overlayView bounds
+ * */
+ private boolean shouldClearFocusRect(@Nullable final View focusedView, final Rect focusedRect) {
+ return focusedView == null || focusedRect.equals(getBounds());
+ }
+
public static void setupFocusObserver(final Dialog dialog) {
Rect displayRect = new Rect();
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index 91627f2d4..46edda8b7 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -38,81 +38,6 @@
tools:ignore="ContentDescription"
tools:visibility="visible"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -431,6 +358,7 @@
android:paddingBottom="4dp"
android:paddingTop="8dp"
tools:progress="25"
+ android:nextFocusDown="@id/screenRotationButton"
tools:secondaryProgress="50"/>
@@ -522,6 +451,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -602,6 +604,7 @@
android:alpha="0.9"
android:paddingLeft="@dimen/video_item_search_padding"
android:paddingRight="@dimen/video_item_search_padding"
+ android:descendantFocusability="blocksDescendants"
android:background="?attr/windowBackground" >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Date: Sat, 25 Jul 2020 07:00:53 +0300
Subject: [PATCH 36/40] Another part of UI improvements for Android TV - focus
will be hidden right after start of a video; fullscreen works like this too -
back button will not needed to be pressed one more time like before - prev &
next buttons for playqueue will be hidden with play/pause button before video
be ready to play
---
.../fragments/detail/VideoDetailFragment.java | 3 ++-
.../java/org/schabi/newpipe/player/MainPlayer.java | 3 +++
.../org/schabi/newpipe/player/VideoPlayerImpl.java | 7 +++++--
.../res/layout-large-land/fragment_video_detail.xml | 12 ++++++------
4 files changed, 16 insertions(+), 9 deletions(-)
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 1d83094dc..6c459ffe9 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
@@ -2158,8 +2158,9 @@ public class VideoDetailFragment
/**
* That's for Android TV support. Move focus from main fragment to the player or back
* based on what is currently selected
+ *
* @param toMain if true than the main fragment will be focused or the player otherwise
- * */
+ */
private void moveFocusToMainFragment(final boolean toMain) {
final ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
// Hamburger button steels a focus even under bottomSheet
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 53296d915..703be346e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -163,6 +163,9 @@ public final class MainPlayer extends Service {
// from one stream to a new stream not smooth
playerImpl.getPlayer().stop(false);
playerImpl.setRecovery();
+ // Android TV will handle back button in case controls will be visible
+ // (one more additional unneeded click while the player is hidden)
+ playerImpl.hideControls(0, 0);
// Notification shows information about old stream but if a user selects
// a stream from backStack it's not actual anymore
// So we should hide the notification at all.
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index 8a4f7fdc5..d2eb591f4 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -253,9 +253,12 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().setVisibility(View.VISIBLE);
initPopup();
initPopupCloseOverlay();
+ playPauseButton.requestFocus();
} else {
getRootView().setVisibility(View.VISIBLE);
initVideoPlayer();
+ // Android TV: without it focus will frame the whole player
+ playPauseButton.requestFocus();
}
onPlay();
@@ -1057,10 +1060,10 @@ public class VideoPlayerImpl extends VideoPlayer
private void animatePlayButtons(final boolean show, final int duration) {
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
- if (playQueue.getIndex() > 0) {
+ if (playQueue.getIndex() > 0 || !show) {
animateView(playPreviousButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
}
- if (playQueue.getIndex() + 1 < playQueue.getStreams().size()) {
+ if (playQueue.getIndex() + 1 < playQueue.getStreams().size() || !show) {
animateView(playNextButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
}
diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml
index 95d2f3c00..f69832b81 100644
--- a/app/src/main/res/layout-large-land/fragment_video_detail.xml
+++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml
@@ -153,12 +153,12 @@
tools:visibility="visible" />
-
+
From 5293d17e32afa13386ce2e90db4455532c1bc487 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 25 Jul 2020 09:39:42 +0300
Subject: [PATCH 37/40] Removed unused files, translations, styles, settings
key
---
app/src/main/AndroidManifest.xml | 23 +-
.../newpipe/player/BackgroundPlayer.java | 684 --------
.../org/schabi/newpipe/player/MainPlayer.java | 2 +-
.../newpipe/player/MainVideoPlayer.java | 1478 -----------------
.../newpipe/player/PopupVideoPlayer.java | 1315 ---------------
.../player/PopupVideoPlayerActivity.java | 72 -
.../newpipe/player/VideoPlayerImpl.java | 2 +-
.../player/event/PlayerGestureListener.java | 4 +-
app/src/main/res/layout/player_popup.xml | 299 ----
.../res/layout/player_popup_notification.xml | 88 -
.../main/res/menu/menu_play_queue_popup.xml | 10 -
app/src/main/res/menu/menu_videooptions.xml | 22 -
app/src/main/res/values-ar/strings.xml | 2 -
.../main/res/values-b+zh+HANS+CN/strings.xml | 2 -
app/src/main/res/values-be/strings.xml | 2 -
app/src/main/res/values-bg/strings.xml | 2 -
app/src/main/res/values-ca/strings.xml | 2 -
app/src/main/res/values-ckb/strings.xml | 2 -
app/src/main/res/values-cs/strings.xml | 2 -
app/src/main/res/values-da/strings.xml | 2 -
app/src/main/res/values-de/strings.xml | 2 -
app/src/main/res/values-el/strings.xml | 2 -
app/src/main/res/values-eo/strings.xml | 2 -
app/src/main/res/values-es/strings.xml | 2 -
app/src/main/res/values-et/strings.xml | 2 -
app/src/main/res/values-eu/strings.xml | 2 -
app/src/main/res/values-fa/strings.xml | 2 -
app/src/main/res/values-fi/strings.xml | 2 -
app/src/main/res/values-fr/strings.xml | 2 -
app/src/main/res/values-gl/strings.xml | 2 -
app/src/main/res/values-he/strings.xml | 2 -
app/src/main/res/values-hi/strings.xml | 2 -
app/src/main/res/values-hr/strings.xml | 2 -
app/src/main/res/values-hu/strings.xml | 1 -
app/src/main/res/values-in/strings.xml | 2 -
app/src/main/res/values-it/strings.xml | 2 -
app/src/main/res/values-ja/strings.xml | 2 -
app/src/main/res/values-ko/strings.xml | 2 -
app/src/main/res/values-ku/strings.xml | 2 -
app/src/main/res/values-lt/strings.xml | 2 -
app/src/main/res/values-mk/strings.xml | 2 -
app/src/main/res/values-ml/strings.xml | 2 -
app/src/main/res/values-ms/strings.xml | 2 -
app/src/main/res/values-nb-rNO/strings.xml | 2 -
app/src/main/res/values-ne/strings.xml | 2 -
app/src/main/res/values-nl-rBE/strings.xml | 2 -
app/src/main/res/values-nl/strings.xml | 2 -
app/src/main/res/values-pa/strings.xml | 2 -
app/src/main/res/values-pl/strings.xml | 2 -
app/src/main/res/values-pt-rBR/strings.xml | 2 -
app/src/main/res/values-pt/strings.xml | 2 -
app/src/main/res/values-ro/strings.xml | 2 -
app/src/main/res/values-ru/strings.xml | 2 -
app/src/main/res/values-sk/strings.xml | 2 -
app/src/main/res/values-sl/strings.xml | 2 -
app/src/main/res/values-sq/strings.xml | 2 -
app/src/main/res/values-sr/strings.xml | 2 -
app/src/main/res/values-sv/strings.xml | 2 -
app/src/main/res/values-th/strings.xml | 2 -
app/src/main/res/values-tr/strings.xml | 2 -
app/src/main/res/values-uk/strings.xml | 2 -
app/src/main/res/values-ur/strings.xml | 2 -
app/src/main/res/values-v28/styles.xml | 8 -
app/src/main/res/values-vi/strings.xml | 2 -
app/src/main/res/values-zh-rCN/strings.xml | 2 -
app/src/main/res/values-zh-rTW/strings.xml | 2 -
app/src/main/res/values/settings_keys.xml | 2 -
app/src/main/res/values/strings.xml | 2 -
app/src/main/res/values/styles.xml | 1 -
69 files changed, 5 insertions(+), 4112 deletions(-)
delete mode 100644 app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
delete mode 100644 app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
delete mode 100644 app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
delete mode 100644 app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayerActivity.java
delete mode 100644 app/src/main/res/layout/player_popup.xml
delete mode 100644 app/src/main/res/layout/player_popup_notification.xml
delete mode 100644 app/src/main/res/menu/menu_play_queue_popup.xml
delete mode 100644 app/src/main/res/menu/menu_videooptions.xml
delete mode 100644 app/src/main/res/values-v28/styles.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 60a7f3cb8..16ed422e0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -42,11 +42,6 @@
-
-
-
@@ -57,25 +52,9 @@
-
-
-
-
-
-
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
deleted file mode 100644
index a75ea7de8..000000000
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * Copyright 2017 Mauricio Colli
- * BackgroundPlayer.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.player;
-
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.core.app.NotificationCompat;
-
-import com.google.android.exoplayer2.PlaybackParameters;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.nostra13.universalimageloader.core.assist.FailReason;
-
-import org.schabi.newpipe.BuildConfig;
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.stream.StreamInfo;
-import org.schabi.newpipe.player.event.PlayerEventListener;
-import org.schabi.newpipe.player.playqueue.PlayQueueItem;
-import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
-import org.schabi.newpipe.player.resolver.MediaSourceTag;
-import org.schabi.newpipe.util.BitmapUtils;
-import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.ThemeHelper;
-
-import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
-import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
-
-/**
- * Service Background Player implementing {@link VideoPlayer}.
- *
- * @author mauriciocolli
- */
-public final class BackgroundPlayer extends Service {
- public static final String ACTION_CLOSE
- = "org.schabi.newpipe.player.BackgroundPlayer.CLOSE";
- public static final String ACTION_PLAY_PAUSE
- = "org.schabi.newpipe.player.BackgroundPlayer.PLAY_PAUSE";
- public static final String ACTION_REPEAT
- = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
- public static final String ACTION_PLAY_NEXT
- = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_NEXT";
- public static final String ACTION_PLAY_PREVIOUS
- = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_PREVIOUS";
- public static final String ACTION_FAST_REWIND
- = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND";
- public static final String ACTION_FAST_FORWARD
- = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD";
-
- public static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
- private static final String TAG = "BackgroundPlayer";
- private static final boolean DEBUG = BasePlayer.DEBUG;
- private static final int NOTIFICATION_ID = 123789;
- private static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60;
- private BasePlayerImpl basePlayerImpl;
-
- /*//////////////////////////////////////////////////////////////////////////
- // Service-Activity Binder
- //////////////////////////////////////////////////////////////////////////*/
- private SharedPreferences sharedPreferences;
-
- /*//////////////////////////////////////////////////////////////////////////
- // Notification
- //////////////////////////////////////////////////////////////////////////*/
- private PlayerEventListener activityListener;
- private IBinder mBinder;
- private NotificationManager notificationManager;
- private NotificationCompat.Builder notBuilder;
- private RemoteViews notRemoteView;
- private RemoteViews bigNotRemoteView;
- private boolean shouldUpdateOnProgress;
- private int timesNotificationUpdated;
-
- /*//////////////////////////////////////////////////////////////////////////
- // Service's LifeCycle
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void onCreate() {
- if (DEBUG) {
- Log.d(TAG, "onCreate() called");
- }
- notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- assureCorrectAppLanguage(this);
- ThemeHelper.setTheme(this);
- basePlayerImpl = new BasePlayerImpl(this);
- basePlayerImpl.setup();
-
- mBinder = new PlayerServiceBinder(basePlayerImpl);
- shouldUpdateOnProgress = true;
- }
-
- @Override
- public int onStartCommand(final Intent intent, final int flags, final int startId) {
- if (DEBUG) {
- Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], "
- + "flags = [" + flags + "], startId = [" + startId + "]");
- }
- basePlayerImpl.handleIntent(intent);
- if (basePlayerImpl.mediaSessionManager != null) {
- basePlayerImpl.mediaSessionManager.handleMediaButtonIntent(intent);
- }
- return START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- if (DEBUG) {
- Log.d(TAG, "destroy() called");
- }
- onClose();
- }
-
- @Override
- protected void attachBaseContext(final Context base) {
- super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base));
- }
-
- @Override
- public IBinder onBind(final Intent intent) {
- return mBinder;
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Actions
- //////////////////////////////////////////////////////////////////////////*/
- private void onClose() {
- if (DEBUG) {
- Log.d(TAG, "onClose() called");
- }
-
- if (basePlayerImpl != null) {
- basePlayerImpl.savePlaybackState();
- basePlayerImpl.stopActivityBinding();
- basePlayerImpl.destroy();
- }
- if (notificationManager != null) {
- notificationManager.cancel(NOTIFICATION_ID);
- }
- mBinder = null;
- basePlayerImpl = null;
-
- stopForeground(true);
- stopSelf();
- }
-
- private void onScreenOnOff(final boolean on) {
- if (DEBUG) {
- Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
- }
- shouldUpdateOnProgress = on;
- basePlayerImpl.triggerProgressUpdate();
- if (on) {
- basePlayerImpl.startProgressLoop();
- } else {
- basePlayerImpl.stopProgressLoop();
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Notification
- //////////////////////////////////////////////////////////////////////////*/
-
- private void resetNotification() {
- notBuilder = createNotification();
- timesNotificationUpdated = 0;
- }
-
- private NotificationCompat.Builder createNotification() {
- notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
- R.layout.player_background_notification);
- bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
- R.layout.player_background_notification_expanded);
-
- setupNotification(notRemoteView);
- setupNotification(bigNotRemoteView);
-
- NotificationCompat.Builder builder = new NotificationCompat
- .Builder(this, getString(R.string.notification_channel_id))
- .setOngoing(true)
- .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setCustomContentView(notRemoteView)
- .setCustomBigContentView(bigNotRemoteView);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- setLockScreenThumbnail(builder);
- }
-
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
- builder.setPriority(NotificationCompat.PRIORITY_MAX);
- }
- return builder;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- private void setLockScreenThumbnail(final NotificationCompat.Builder builder) {
- boolean isLockScreenThumbnailEnabled = sharedPreferences.getBoolean(
- getString(R.string.enable_lock_screen_video_thumbnail_key), true);
-
- if (isLockScreenThumbnailEnabled) {
- basePlayerImpl.mediaSessionManager.setLockScreenArt(
- builder,
- getCenteredThumbnailBitmap()
- );
- } else {
- basePlayerImpl.mediaSessionManager.clearLockScreenArt(builder);
- }
- }
-
- @Nullable
- private Bitmap getCenteredThumbnailBitmap() {
- final int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
- final int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
-
- return BitmapUtils.centerCrop(basePlayerImpl.getThumbnail(), screenWidth, screenHeight);
- }
-
- private void setupNotification(final RemoteViews remoteViews) {
- if (basePlayerImpl == null) {
- return;
- }
-
- remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
- remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
-
- remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationStop,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
-
- // Starts background player activity -- attempts to unlock lockscreen
- final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(this);
- remoteViews.setOnClickPendingIntent(R.id.notificationContent,
- PendingIntent.getActivity(this, NOTIFICATION_ID, intent,
- PendingIntent.FLAG_UPDATE_CURRENT));
-
- if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
- remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_previous);
- remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_next);
- remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT));
- } else {
- remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_rewind);
- remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_fastforward);
- remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID,
- new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT));
- }
-
- setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode());
- }
-
- /**
- * Updates the notification, and the play/pause button in it.
- * Used for changes on the remoteView
- *
- * @param drawableId if != -1, sets the drawable with that id on the play/pause button
- */
- private synchronized void updateNotification(final int drawableId) {
-// if (DEBUG) {
-// Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
-// }
- if (notBuilder == null) {
- return;
- }
- if (drawableId != -1) {
- if (notRemoteView != null) {
- notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
- }
- if (bigNotRemoteView != null) {
- bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
- }
- }
- notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
- timesNotificationUpdated++;
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
-
- private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
- switch (repeatMode) {
- case Player.REPEAT_MODE_OFF:
- remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_repeat_off);
- break;
- case Player.REPEAT_MODE_ONE:
- remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_repeat_one);
- break;
- case Player.REPEAT_MODE_ALL:
- remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD,
- R.drawable.exo_controls_repeat_all);
- break;
- }
- }
- //////////////////////////////////////////////////////////////////////////
-
- protected class BasePlayerImpl extends BasePlayer {
- @NonNull
- private final AudioPlaybackResolver resolver;
- private int cachedDuration;
- private String cachedDurationString;
-
- BasePlayerImpl(final Context context) {
- super(context);
- this.resolver = new AudioPlaybackResolver(context, dataSource);
- }
-
- @Override
- public void initPlayer(final boolean playOnReady) {
- super.initPlayer(playOnReady);
- }
-
- @Override
- public void handleIntent(final Intent intent) {
- super.handleIntent(intent);
-
- resetNotification();
- if (bigNotRemoteView != null) {
- bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
- }
- if (notRemoteView != null) {
- notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
- }
- startForeground(NOTIFICATION_ID, notBuilder.build());
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Thumbnail Loading
- //////////////////////////////////////////////////////////////////////////*/
-
- private void updateNotificationThumbnail() {
- if (basePlayerImpl == null) {
- return;
- }
- if (notRemoteView != null) {
- notRemoteView.setImageViewBitmap(R.id.notificationCover,
- basePlayerImpl.getThumbnail());
- }
- if (bigNotRemoteView != null) {
- bigNotRemoteView.setImageViewBitmap(R.id.notificationCover,
- basePlayerImpl.getThumbnail());
- }
- }
-
- @Override
- public void onLoadingComplete(final String imageUri, final View view,
- final Bitmap loadedImage) {
- super.onLoadingComplete(imageUri, view, loadedImage);
- resetNotification();
- updateNotificationThumbnail();
- updateNotification(-1);
- }
-
- @Override
- public void onLoadingFailed(final String imageUri, final View view,
- final FailReason failReason) {
- super.onLoadingFailed(imageUri, view, failReason);
- resetNotification();
- updateNotificationThumbnail();
- updateNotification(-1);
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // States Implementation
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void onPrepared(final boolean playWhenReady) {
- super.onPrepared(playWhenReady);
- }
-
- @Override
- public void onShuffleClicked() {
- super.onShuffleClicked();
- updatePlayback();
- }
-
- @Override
- public void onMuteUnmuteButtonClicked() {
- super.onMuteUnmuteButtonClicked();
- updatePlayback();
- }
-
- @Override
- public void onUpdateProgress(final int currentProgress, final int duration,
- final int bufferPercent) {
- updateProgress(currentProgress, duration, bufferPercent);
-
- if (!shouldUpdateOnProgress) {
- return;
- }
- if (timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET) {
- resetNotification();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /*Oreo*/) {
- updateNotificationThumbnail();
- }
- }
- if (bigNotRemoteView != null) {
- if (cachedDuration != duration) {
- cachedDuration = duration;
- cachedDurationString = getTimeString(duration);
- }
- bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration,
- currentProgress, false);
- bigNotRemoteView.setTextViewText(R.id.notificationTime,
- getTimeString(currentProgress) + " / " + cachedDurationString);
- }
- if (notRemoteView != null) {
- notRemoteView.setProgressBar(R.id.notificationProgressBar, duration,
- currentProgress, false);
- }
- updateNotification(-1);
- }
-
- @Override
- public void onPlayPrevious() {
- super.onPlayPrevious();
- triggerProgressUpdate();
- }
-
- @Override
- public void onPlayNext() {
- super.onPlayNext();
- triggerProgressUpdate();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- if (notRemoteView != null) {
- notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
- }
- if (bigNotRemoteView != null) {
- bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, null);
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // ExoPlayer Listener
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void onPlaybackParametersChanged(final PlaybackParameters playbackParameters) {
- super.onPlaybackParametersChanged(playbackParameters);
- updatePlayback();
- }
-
- @Override
- public void onLoadingChanged(final boolean isLoading) {
- // Disable default behavior
- }
-
- @Override
- public void onRepeatModeChanged(final int i) {
- resetNotification();
- updateNotification(-1);
- updatePlayback();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Playback Listener
- //////////////////////////////////////////////////////////////////////////*/
-
- protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
- super.onMetadataChanged(tag);
- resetNotification();
- updateNotificationThumbnail();
- updateNotification(-1);
- updateMetadata();
- }
-
- @Override
- @Nullable
- public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
- return resolver.resolve(info);
- }
-
- @Override
- public void onPlaybackShutdown() {
- super.onPlaybackShutdown();
- onClose();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Activity Event Listener
- //////////////////////////////////////////////////////////////////////////*/
-
- /*package-private*/ void setActivityListener(final PlayerEventListener listener) {
- activityListener = listener;
- updateMetadata();
- updatePlayback();
- triggerProgressUpdate();
- }
-
- /*package-private*/ void removeActivityListener(final PlayerEventListener listener) {
- if (activityListener == listener) {
- activityListener = null;
- }
- }
-
- private void updateMetadata() {
- if (activityListener != null && getCurrentMetadata() != null) {
- activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata(), playQueue);
- }
- }
-
- private void updatePlayback() {
- if (activityListener != null && simpleExoPlayer != null && playQueue != null) {
- activityListener.onPlaybackUpdate(currentState, getRepeatMode(),
- playQueue.isShuffled(), getPlaybackParameters());
- }
- }
-
- private void updateProgress(final int currentProgress, final int duration,
- final int bufferPercent) {
- if (activityListener != null) {
- activityListener.onProgressUpdate(currentProgress, duration, bufferPercent);
- }
- }
-
- private void stopActivityBinding() {
- if (activityListener != null) {
- activityListener.onServiceStopped();
- activityListener = null;
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Broadcast Receiver
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- protected void setupBroadcastReceiver(final IntentFilter intentFltr) {
- super.setupBroadcastReceiver(intentFltr);
- intentFltr.addAction(ACTION_CLOSE);
- intentFltr.addAction(ACTION_PLAY_PAUSE);
- intentFltr.addAction(ACTION_REPEAT);
- intentFltr.addAction(ACTION_PLAY_PREVIOUS);
- intentFltr.addAction(ACTION_PLAY_NEXT);
- intentFltr.addAction(ACTION_FAST_REWIND);
- intentFltr.addAction(ACTION_FAST_FORWARD);
-
- intentFltr.addAction(Intent.ACTION_SCREEN_ON);
- intentFltr.addAction(Intent.ACTION_SCREEN_OFF);
-
- intentFltr.addAction(Intent.ACTION_HEADSET_PLUG);
- }
-
- @Override
- public void onBroadcastReceived(final Intent intent) {
- super.onBroadcastReceived(intent);
- if (intent == null || intent.getAction() == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
- }
- switch (intent.getAction()) {
- case ACTION_CLOSE:
- onClose();
- break;
- case ACTION_PLAY_PAUSE:
- onPlayPause();
- break;
- case ACTION_REPEAT:
- onRepeatClicked();
- break;
- case ACTION_PLAY_NEXT:
- onPlayNext();
- break;
- case ACTION_PLAY_PREVIOUS:
- onPlayPrevious();
- break;
- case ACTION_FAST_FORWARD:
- onFastForward();
- break;
- case ACTION_FAST_REWIND:
- onFastRewind();
- break;
- case Intent.ACTION_SCREEN_ON:
- onScreenOnOff(true);
- break;
- case Intent.ACTION_SCREEN_OFF:
- onScreenOnOff(false);
- break;
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // States
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void changeState(final int state) {
- super.changeState(state);
- updatePlayback();
- }
-
- @Override
- public void onPlaying() {
- super.onPlaying();
- resetNotification();
- updateNotificationThumbnail();
- updateNotification(R.drawable.exo_controls_pause);
- }
-
- @Override
- public void onPaused() {
- super.onPaused();
- resetNotification();
- updateNotificationThumbnail();
- updateNotification(R.drawable.exo_controls_play);
- }
-
- @Override
- public void onCompleted() {
- super.onCompleted();
- resetNotification();
- if (bigNotRemoteView != null) {
- bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
- }
- if (notRemoteView != null) {
- notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
- }
- updateNotificationThumbnail();
- updateNotification(R.drawable.ic_replay_white_24dp);
- }
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
index 703be346e..d3bb90660 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -1,6 +1,6 @@
/*
* Copyright 2017 Mauricio Colli
- * BackgroundPlayer.java is part of NewPipe
+ * Part of NewPipe
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
deleted file mode 100644
index f87887685..000000000
--- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
+++ /dev/null
@@ -1,1478 +0,0 @@
-/*
- * Copyright 2017 Mauricio Colli
- * Copyright 2019 Eltex ltd
- * MainVideoPlayer.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.player;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.DisplayCutout;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.PopupMenu;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.app.ActivityCompat;
-import androidx.recyclerview.widget.ItemTouchHelper;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.text.CaptionStyleCompat;
-import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
-import com.google.android.exoplayer2.ui.SubtitleView;
-
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.stream.VideoStream;
-import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
-import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
-import org.schabi.newpipe.player.helper.PlayerHelper;
-import org.schabi.newpipe.player.playqueue.PlayQueueItem;
-import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
-import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
-import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
-import org.schabi.newpipe.player.resolver.MediaSourceTag;
-import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
-import org.schabi.newpipe.util.DeviceUtils;
-import org.schabi.newpipe.util.AnimationUtils;
-import org.schabi.newpipe.util.KoreUtil;
-import org.schabi.newpipe.util.ListHelper;
-import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.PermissionHelper;
-import org.schabi.newpipe.util.ShareUtils;
-import org.schabi.newpipe.util.StateSaver;
-import org.schabi.newpipe.util.ThemeHelper;
-import org.schabi.newpipe.views.FocusOverlayView;
-
-import java.util.List;
-import java.util.Queue;
-import java.util.UUID;
-
-import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING;
-import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
-import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
-import static org.schabi.newpipe.player.VideoPlayer.DPAD_CONTROLS_HIDE_TIME;
-import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
-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.Localization.assureCorrectAppLanguage;
-import static org.schabi.newpipe.util.StateSaver.KEY_SAVED_STATE;
-
-/**
- * Activity Player implementing {@link VideoPlayer}.
- *
- * @author mauriciocolli
- */
-public final class MainVideoPlayer extends AppCompatActivity
- implements StateSaver.WriteRead, PlaybackParameterDialog.Callback {
- private static final String TAG = ".MainVideoPlayer";
- private static final boolean DEBUG = BasePlayer.DEBUG;
-
- private GestureDetector gestureDetector;
-
- private VideoPlayerImpl playerImpl;
-
- private SharedPreferences defaultPreferences;
-
- @Nullable
- private PlayerState playerState;
- private boolean isInMultiWindow;
- private boolean isBackPressed;
-
- private ContentObserver rotationObserver;
-
- /*//////////////////////////////////////////////////////////////////////////
- // Activity LifeCycle
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- protected void onCreate(@Nullable final Bundle savedInstanceState) {
- assureCorrectAppLanguage(this);
- super.onCreate(savedInstanceState);
- if (DEBUG) {
- Log.d(TAG, "onCreate() called with: "
- + "savedInstanceState = [" + savedInstanceState + "]");
- }
- defaultPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- ThemeHelper.setTheme(this);
- getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- getWindow().setStatusBarColor(Color.BLACK);
- }
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- WindowManager.LayoutParams lp = getWindow().getAttributes();
- lp.screenBrightness = PlayerHelper.getScreenBrightness(getApplicationContext());
- getWindow().setAttributes(lp);
-
- hideSystemUi();
- setContentView(R.layout.activity_main_player);
-
- playerImpl = new VideoPlayerImpl(this);
- playerImpl.setup(findViewById(android.R.id.content));
-
- if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) {
- return; // We have saved states, stop here to restore it
- }
-
- final Intent intent = getIntent();
- if (intent != null) {
- playerImpl.handleIntent(intent);
- } else {
- Toast.makeText(this, R.string.general_error, Toast.LENGTH_SHORT).show();
- finish();
- }
-
- rotationObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(final boolean selfChange) {
- super.onChange(selfChange);
- if (globalScreenOrientationLocked()) {
- final String orientKey = getString(R.string.last_orientation_landscape_key);
-
- final boolean lastOrientationWasLandscape = defaultPreferences
- .getBoolean(orientKey, DeviceUtils.isTv(getApplicationContext()));
- setLandscape(lastOrientationWasLandscape);
- } else {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }
- };
-
- getContentResolver().registerContentObserver(
- Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
- false, rotationObserver);
-
- if (DeviceUtils.isTv(this)) {
- FocusOverlayView.setupFocusObserver(this);
- }
- }
-
- @Override
- protected void onRestoreInstanceState(@NonNull final Bundle bundle) {
- if (DEBUG) {
- Log.d(TAG, "onRestoreInstanceState() called");
- }
- super.onRestoreInstanceState(bundle);
- StateSaver.tryToRestore(bundle, this);
- }
-
- @Override
- protected void onNewIntent(final Intent intent) {
- if (DEBUG) {
- Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
- }
- super.onNewIntent(intent);
- if (intent != null) {
- playerState = null;
- playerImpl.handleIntent(intent);
- }
- }
-
- @Override
- public boolean onKeyDown(final int keyCode, final KeyEvent event) {
- switch (event.getKeyCode()) {
- default:
- break;
- case KeyEvent.KEYCODE_BACK:
- if (DeviceUtils.isTv(getApplicationContext())
- && playerImpl.isControlsVisible()) {
- playerImpl.hideControls(0, 0);
- hideSystemUi();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- View playerRoot = playerImpl.getRootView();
- View controls = playerImpl.getControlsRoot();
- if (playerRoot.hasFocus() && !controls.hasFocus()) {
- // do not interfere with focus in playlist etc.
- return super.onKeyDown(keyCode, event);
- }
-
- if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) {
- return true;
- }
-
- if (!playerImpl.isControlsVisible()) {
- playerImpl.playPauseButton.requestFocus();
- playerImpl.showControlsThenHide();
- showSystemUi();
- return true;
- } else {
- playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DPAD_CONTROLS_HIDE_TIME);
- }
- break;
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- protected void onResume() {
- if (DEBUG) {
- Log.d(TAG, "onResume() called");
- }
- assureCorrectAppLanguage(this);
- super.onResume();
-
- if (globalScreenOrientationLocked()) {
- final String orientKey = getString(R.string.last_orientation_landscape_key);
-
- boolean lastOrientationWasLandscape = defaultPreferences
- .getBoolean(orientKey, DeviceUtils.isTv(getApplicationContext()));
- setLandscape(lastOrientationWasLandscape);
- }
-
- final int lastResizeMode = defaultPreferences.getInt(
- getString(R.string.last_resize_mode), AspectRatioFrameLayout.RESIZE_MODE_FIT);
- playerImpl.setResizeMode(lastResizeMode);
-
- // Upon going in or out of multiwindow mode, isInMultiWindow will always be false,
- // since the first onResume needs to restore the player.
- // Subsequent onResume calls while multiwindow mode remains the same and the player is
- // prepared should be ignored.
- if (isInMultiWindow) {
- return;
- }
- isInMultiWindow = isInMultiWindow();
-
- if (playerState != null) {
- playerImpl.setPlaybackQuality(playerState.getPlaybackQuality());
- playerImpl.initPlayback(playerState.getPlayQueue(), playerState.getRepeatMode(),
- playerState.getPlaybackSpeed(), playerState.getPlaybackPitch(),
- playerState.isPlaybackSkipSilence(), playerState.wasPlaying(),
- playerImpl.isMuted());
- }
- }
-
- @Override
- public void onConfigurationChanged(final Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- assureCorrectAppLanguage(this);
-
- if (playerImpl.isSomePopupMenuVisible()) {
- playerImpl.getQualityPopupMenu().dismiss();
- playerImpl.getPlaybackSpeedPopupMenu().dismiss();
- }
- }
-
- @Override
- public void onBackPressed() {
- super.onBackPressed();
- isBackPressed = true;
- }
-
- @Override
- protected void onSaveInstanceState(final Bundle outState) {
- if (DEBUG) {
- Log.d(TAG, "onSaveInstanceState() called");
- }
- super.onSaveInstanceState(outState);
- if (playerImpl == null) {
- return;
- }
-
- playerImpl.setRecovery();
- if (!playerImpl.gotDestroyed()) {
- playerState = createPlayerState();
- }
- StateSaver.tryToSave(isChangingConfigurations(), null, outState, this);
- }
-
- @Override
- protected void onStop() {
- if (DEBUG) {
- Log.d(TAG, "onStop() called");
- }
- super.onStop();
- PlayerHelper.setScreenBrightness(getApplicationContext(),
- getWindow().getAttributes().screenBrightness);
-
- if (playerImpl == null) {
- return;
- }
- if (!isBackPressed) {
- playerImpl.minimize();
- }
- playerState = createPlayerState();
- playerImpl.destroy();
-
- if (rotationObserver != null) {
- getContentResolver().unregisterContentObserver(rotationObserver);
- }
-
- isInMultiWindow = false;
- isBackPressed = false;
- }
-
- @Override
- protected void attachBaseContext(final Context newBase) {
- super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(newBase));
- }
-
- @Override
- protected void onPause() {
- playerImpl.savePlaybackState();
- super.onPause();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // State Saving
- //////////////////////////////////////////////////////////////////////////*/
-
- private PlayerState createPlayerState() {
- return new PlayerState(playerImpl.getPlayQueue(), playerImpl.getRepeatMode(),
- playerImpl.getPlaybackSpeed(), playerImpl.getPlaybackPitch(),
- playerImpl.getPlaybackQuality(), playerImpl.getPlaybackSkipSilence(),
- playerImpl.isPlaying());
- }
-
- @Override
- public String generateSuffix() {
- return "." + UUID.randomUUID().toString() + ".player";
- }
-
- @Override
- public void writeTo(final Queue