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 002/202] 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 003/202] 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 004/202] 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 005/202] 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 006/202] 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 007/202] 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 008/202] 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 009/202] 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 010/202] 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 011/202] 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 012/202] 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 013/202] 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 014/202] 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 015/202] 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 016/202] 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 017/202] 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 018/202] 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 019/202] 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 020/202] 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 021/202] 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 4c6f7238dde4fc66b8ce61d67b89dd98c2c2f080 Mon Sep 17 00:00:00 2001
From: Terry Louwers
Date: Fri, 29 May 2020 05:23:52 +0000
Subject: [PATCH 022/202] Translated using Weblate (Dutch)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-nl/strings.xml | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 6ee12f9ed..31f855c82 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -240,10 +240,10 @@
Houd ingedrukt om toe te voegen aan de wachtrij
[Onbekend]
Toevoegen aan wachtrij in de achtergrond
- Toevoegen aan wachtrij in nieuwe pop-up
+ Toevoegen aan wachtrij in pop-up
Begin hier met afspelen
Begin hier met afspelen in de achtergrond
- Begin met afspelen in nieuwe pop-up
+ Begin met afspelen in pop-up
Doneren
NewPipe wordt door vrijwilligers in hun vrije tijd ontwikkeld om jou de beste ervaring te brengen. Geef wat terug zodat onze ontwikkelaars NewPipe nóg beter kunnen maken terwijl ze van hun kopje koffie genieten.
Teruggeven
@@ -427,7 +427,7 @@
Raster
Auto
Wissel van weergave
- NewPipe-update beschikbaar!
+ NewPipe-update is beschikbaar!
Tik om te downloaden
Voltooid
In afwachting van
@@ -615,4 +615,7 @@
Originele teksten van services zijn zichtbaar in stream-items
YouTube beperkte modus
Laat orginele tijd geleden zien
+ De avatar-miniatuur van het kanaal
+ Door %s
+ Gecreëerd door %s
\ No newline at end of file
From 0cff10e02e95930fa5d5c205fc85481df2ee44ff Mon Sep 17 00:00:00 2001
From: Edoardo Regni
Date: Fri, 29 May 2020 05:29:23 +0000
Subject: [PATCH 023/202] Translated using Weblate (Dutch)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-nl/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 31f855c82..3a6b2372f 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -152,7 +152,7 @@
Over
Bijdragers
Licenties
- Vrij en licht streamen voor Android.
+ open source en lichtgewicht streaming voor Android.
Bekijken op GitHub
Licentie van NewPipe
Hulp is altijd welkom. Of je nu nieuwe ideeën hebt, vertalingen kan aanleveren, wijzigingen in het ontwerp kan verrichten, code kan opschonen of van grote wijzigingen voorzien. Hoe meer hulp, hoe beter het wordt!
From bef579ec262a1daeeba550217082fe2b3817a1fb Mon Sep 17 00:00:00 2001
From: Terry Louwers
Date: Fri, 29 May 2020 05:29:41 +0000
Subject: [PATCH 024/202] Translated using Weblate (Dutch)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-nl/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 3a6b2372f..a7882b845 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -152,7 +152,7 @@
Over
Bijdragers
Licenties
- open source en lichtgewicht streaming voor Android.
+ Open source en lichtgewicht streaming voor Android.
Bekijken op GitHub
Licentie van NewPipe
Hulp is altijd welkom. Of je nu nieuwe ideeën hebt, vertalingen kan aanleveren, wijzigingen in het ontwerp kan verrichten, code kan opschonen of van grote wijzigingen voorzien. Hoe meer hulp, hoe beter het wordt!
From 16d4fa03a53ba8b367f60977ffcd546058cf632e Mon Sep 17 00:00:00 2001
From: TotalCaesar659
Date: Sun, 24 May 2020 18:22:03 +0000
Subject: [PATCH 025/202] Translated using Weblate (English)
Currently translated at 99.8% (576 of 577 strings)
---
app/src/main/res/values/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ac79a77a4..0fa83e0bd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -540,7 +540,7 @@
Auto
Switch View
- NewPipe Update Available!
+ NewPipe update is available!
Tap to download
Finished
Pending
@@ -654,4 +654,4 @@
Channel\'s avatar thumbnail
Created by %s
By %s
-
+
\ No newline at end of file
From 27bf3901defc0ec06882b5769b7f15d95fa3f675 Mon Sep 17 00:00:00 2001
From: AioiLight
Date: Tue, 26 May 2020 06:29:16 +0000
Subject: [PATCH 026/202] Translated using Weblate (Japanese)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-ja/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 269c0b1fa..d7974fd35 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -412,7 +412,7 @@
グリッド
自動
スイッチビュー
- NewPipeのアップデートがあります!
+ NewPipe のアップデートがあります!
タップでダウンロード
完了
保留中
From 5f2d2a64d246dd293dde50ff049d879bc0424937 Mon Sep 17 00:00:00 2001
From: ssantos
Date: Mon, 25 May 2020 15:58:04 +0000
Subject: [PATCH 027/202] Translated using Weblate (Portuguese)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-pt/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 32c1a2b16..c4160f15e 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -233,7 +233,7 @@
Definições de áudio
Iniciar reprodução aqui
Iniciar reprodução em segundo plano
- Iniciar reprodução numa nova janela
+ Iniciar reprodução num popup
Mostrar informação
Listas de reprodução favoritas
Serviço
@@ -370,7 +370,7 @@
Também deseja importar as definições\?
Toque longo para enfileirar
Colocar em fila em segundo plano
- Colocar em fila num nova janela
+ Pôr na fila num popup
Ação de \'abrir\' preferida
Ação predefinida ao abrir o conteúdo — %s
Definir como Miniatura da Lista de Reprodução
From e8cf71f41c4152660780ee50b56371d7b9a8c28d Mon Sep 17 00:00:00 2001
From: B0pol
Date: Thu, 28 May 2020 19:51:52 +0000
Subject: [PATCH 028/202] Translated using Weblate (Esperanto)
Currently translated at 97.5% (563 of 577 strings)
---
app/src/main/res/values-eo/strings.xml | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index f985931e5..1b3f2f4c6 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -171,7 +171,7 @@
Teni por viciĝi
[Nekonata]
Viciĝi en la fona ludilo
- Viciĝi en nova ŝprucfenestro
+ Viciĝi en ŝprucfenestro
Komenci ludi en nova ŝprucfenestro
Preferata enhavlando
Ŝangi Orientiĝon
@@ -587,4 +587,9 @@
Malmutigi
Helpo
Tio enhavo ne estas ankoraŭ subtenata per NewPipe.\n\nĜi espereble estos en sekvanta versio.
+ ∞ filmetoj
+ 100+ filmetoj
+ Artistoj
+ Albumoj
+ Kantoj
\ No newline at end of file
From 73b72ab01c743ad0f4d9d9ca1a14dbdc6bce8888 Mon Sep 17 00:00:00 2001
From: Rex_sa
Date: Sat, 23 May 2020 08:48:28 +0000
Subject: [PATCH 029/202] Translated using Weblate (Arabic)
Currently translated at 99.8% (576 of 577 strings)
---
app/src/main/res/values-ar/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index c70ae5a92..ae984969f 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -234,7 +234,7 @@
التفاصيل
الإعدادات الصوتية
تشغيل هنا
- بدأ التشغيل في نافذة منبثقة جديدة
+ بدأ التشغيل في نافذة منبثقة
تحدي الكابتشا
ضغط مطول للإدراج الى قائمة الانتظار
From 7d98a700282cc3f7cc15a575ef4fca75550405ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emin=20Tufan=20=C3=87etin?=
Date: Sun, 24 May 2020 22:20:28 +0000
Subject: [PATCH 030/202] Translated using Weblate (Turkish)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-tr/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 1ea1b5ce2..6d6187886 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -423,7 +423,7 @@
Izgara
Otomatik
Görünümü Değiştir
- NewPipe Güncellemesi Var!
+ NewPipe güncellemesi var!
İndirmek için dokunun
Tamamlandı
durduruldu
From 0baa5a2f04f61b640d98d920fc10ba6c77f64f8a Mon Sep 17 00:00:00 2001
From: WaldiS
Date: Sat, 23 May 2020 14:32:57 +0000
Subject: [PATCH 031/202] Translated using Weblate (Polish)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-pl/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index f6a528d80..908ad6121 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -248,10 +248,10 @@
Ustawienia dźwięku
Przytrzymaj, aby zakolejkować
Kolejkuj w tle
- Kolejkuj w nowym wyskakującym okienku
+ Kolejkuj w wyskakującym okienku
Zacznij odtwarzać tutaj
Zacznij odtwarzać w tle
- Zacznij odtwarzać w nowym wyskakującym okienku
+ Zacznij odtwarzać w wyskakującym okienku
Nie znaleziono odtwarzacza strumieniowego (żeby odtworzyć, możesz zainstalować VLC).
Domyślny kraj treści
Usługa
From 123bdbd13b0bbe6f690c39446af04bc4909f05d7 Mon Sep 17 00:00:00 2001
From: Mostafa Ahangarha
Date: Tue, 26 May 2020 16:15:49 +0000
Subject: [PATCH 032/202] Translated using Weblate (Persian)
Currently translated at 96.1% (555 of 577 strings)
---
app/src/main/res/values-fa/strings.xml | 94 +++++++++++++++++++++++---
1 file changed, 85 insertions(+), 9 deletions(-)
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 0e0195833..4ccf3a6fb 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -47,7 +47,7 @@
پخش
محتوا
محتوای محدود شده بر اساس سن
- نمایش ویدیوهای دارای محدودیت سنی. اجازه دادن به چنین محتوای از بخش تنظیمات ممکن است.
+ نمایش ویدیوهای دارای محدودیت سنی. تغییرات آتی از طریق تنظیمات ممکن است.
زنده
بارگیریها
بارگیریها
@@ -63,7 +63,7 @@
جریانهای زنده هنوز پشتیبانی نمیشوند
نمیتوان هیچ جریانی را گرفت
ببخشید! نباید این اتّفاق زخ میداد.
- گزارش خطا با رایانامه
+ این خطا را با رایانامه گزارش کنید
ببخشید، چند مشکل رخ داد.
گزارش
اطّلاعات:
@@ -77,14 +77,14 @@
نپسندیدنها
استفاده از تور
(آزمایشی) اجبار ترافیک بارگیری از مسیر تور برای محرمانگی بیشتر (هنوز پخش جریانی پشتیبانی نمیشود).
- گزارش یک خطا
+ گزارش خطا
گزارش کاربر
نمیتوان شاخهٔ بارگیری «%1$s» را ایجاد کرد
شاخهٔ بارگیری «%1$s» ایجاد شد
ویدیو
صدا
تلاش دوباره
- اجازهٔ دسترسی به فضای ذخیره رد شد
+ ابتدا اجازهٔ دسترسی به فضای ذخیره را بدهید
شروع
مکث
پخش
@@ -288,7 +288,7 @@
اشکالزدایی
بهروزرسانیها
در پنجره جداگانه باز شود
- حالت پنجره مجزا
+ حالت تصویر در تصویر
اندازه پیش فرض پنجره جداگانه
پنجره جداگانه
به یاد داشتن اندازه و موقعیت پنجره جداگانه
@@ -382,7 +382,7 @@
شروع پخش در اینجا
شروع پخش در پسزمینه
شروع پخش در حالت تصویر در تصویر
- نیوپایپ توسط داوطلبانی توسعه داده میشود که با صرف زمان، بهترین تجربه را برای شما به ارمغان میآورند. حمایت از توسعهدهندگان، به آنها کمک میکند تا حین نوشیدن یک فنجان قهوه، نیوپایپ را به مراتب بهتر کنند.
+ نیوپایپ توسط داوطلبانی توسعه داده میشود که با صرف زمان آزادشان، بهترین تجربه کاربری را برای شما به ارمغان میآورند. حمایت از توسعهدهندگان، به آنها کمک میکند تا حین لذت بردن از نوشیدن یک فنجان قهوه، نیوپایپ را به مراتب بهتر کنند.
حمایت
برای اطلاعات و اخبار بیشتر، به وبسایت نیوپایپ سر بزنید.
تغییر وضعیت به تصویر در تصویر
@@ -475,7 +475,7 @@
فضایی روی دستگاه باقی نمانده است
پیشرفت کار متوفق شد زیرا پرونده پاک شده است
پایان زمان اتصال
- مطمئنید؟
+ میخواهید تاریخچه بارگیری را پاک کنید یا همه پروندههایی که بارگیری شدهاند؟
محدود کردن صف بارگیری
یک بارگیری در هر زمان اجرا شود
شروع بارگیریها
@@ -485,8 +485,8 @@
از شما پرسیده خواهد شد که هر بارگیری کجا ذخیره شود.
\nاگر میخواهید بارگیری در کارت SD خارجی ذخیره شود، گزینه SAF را انتخاب کنید
استفاده از SAF
- چارچوب دسترسی حافظه اجازه دخیره بارگیریها در کارت SD خارجی را میدهد.
-\nنکته: برخی دستگاهها با این قابلیت سازگاری ندارند
+ «چارچوب دسترسی حافظه» اجازه دخیره بارگیریها در کارت SD خارجی را میدهد.
+\nبرخی دستگاهها با این قابلیت سازگاری ندارند
حذف موقعیتهای پخش
حذف تمام موقعیتهای پخش
همه موقعیتهای پخش حذف شوند؟
@@ -513,4 +513,80 @@
به کمک پخشکننده پسزمینه، تصویر بندانگشتی ویدئو در حالت قفل صفحه نمایش یابد
تصویر ویدئو در حالت قفل صفحه
کمینه کردن به هنگام تغییر برنامه
+ توسط %s
+ ایجاد شده توسط %s
+ این محتوا هنوز توسط نیوپایپ پشتیبانی میشود.
+\n
+\nامیدواریم در نسخههای آینده مورد پشتیبانی قرار گیرد.
+ غیرفعالسازی حالت سریع
+ فعالسازی حالت سریع
+ خوراک
+ جدید
+ میخواهید این گروه را پاک کنید؟
+ نام
+
+ - %d مورد انتخاب شده
+ - %d مورد انتخاب شده
+
+ پردازش خوراک…
+ بارگیری خوراک…
+ بارگیری نشده: %d
+ آخرین بهروزرسانی خوراک: %s
+
+ - %d روز
+ - %d روز
+
+
+ - %d ساعت
+ - %d ساعت
+
+
+ - %d دقیقه
+ - %d دقیقه
+
+
+ - %d ثانیه
+ - %d ثانیه
+
+ بله، به همراه ویدئوهایی که ناقص دیده شدهاند
+ ویدئوهای دیده شده پاک شوند؟
+ پاک کردن دیده شدهها
+ پیشفرض دستگاه
+ زبان برنامه
+ نمونهای را انتخاب کنید
+ تعداد %1$s بارگیری، پاک شد
+ پاک کردن پروندههای بارگیری شده
+ پاک کردن تاریخچه بارگیری
+ ناتوانی در بازیابی این بارگیری
+ در حال بازیابی
+ کمینهسازی به پخشکننده تصویر در تصویر
+ نمایش زمان سپریشده اصلی هر مورد
+ صدادار
+ بیصدا
+ مورد پسندترینها
+ اخیرا اضافه شده
+ محلی
+ زبان، با راهاندازی مجدد برنامه تغییر خواهد یافت.
+ کیوسک پیشفرض
+ انجام شد
+ وقتی انجام شد، «Done» یا «انجام شد» را بفشارید
+ بینهایت ویدئو
+ بیش از ۱۰۰ ویدئو
+
+ - %s شنونده
+ - %s شنونده
+
+ کسی در حال شنیدن نیست
+
+ - %s تماشاچی
+ - %s تماشاچی
+
+ کسی در حال مشاهده نیست
+ تغییر خدمت، خدمت جاری:
+ اجازه نمایش روی دیگر برنامهها را بدهید
+ راهنما
+ هنرمندان
+ آلبومها
+ موسیقیها
+ حالت محدودیت یوتیوب
\ No newline at end of file
From 927ea723370d82caf9cf768097fd72902be6b735 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Samuel=20Carvalho=20de=20Ara=C3=BAjo?=
Date: Mon, 25 May 2020 21:51:38 +0000
Subject: [PATCH 033/202] Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-pt-rBR/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 471c595cc..e3981a308 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -430,7 +430,7 @@
Grade
Automático
Mudar exibição
- Atualização do NewPipe disponível!
+ Atualização do NewPipe está disponível!
Toque para baixar
Finalizado
pausado
From 57c9b29ba3dc9595fceca38ec0eda4a230a2d3d8 Mon Sep 17 00:00:00 2001
From: Eric
Date: Sun, 24 May 2020 10:30:53 +0000
Subject: [PATCH 034/202] Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-b+zh+HANS+CN/strings.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
index 4e68feb68..338739b2d 100644
--- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml
+++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
@@ -71,7 +71,7 @@
无法得知订阅人数
发布新版本时,通知我升级应用
网格
- 新版 NewPipe 发布!
+ NewPipe有更新!
服务器不接受 接收 multi-threaded 下载, 以 @string/msg_threads = 1 重试
自动播放
清除数据
@@ -282,10 +282,10 @@
长按队列
[未知]
添加到后台部分队列
- 添加至新悬浮窗列表
+ 加入悬浮窗队列
开始在此处开始播放
开始后台播放
- 开始在新悬浮窗中播放
+ 开始在悬浮窗中播放
捐赠
NewPipe 是由志愿者花费时间为您带来最佳体验开发的。回馈帮助开发人员在享用一杯咖啡的同时,让 NewPipe 变得更好。
反馈
From ee2a1593742d9efdc2faeace85605aebc98a4098 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Allan=20Nordh=C3=B8y?=
Date: Tue, 26 May 2020 10:28:20 +0000
Subject: [PATCH 035/202] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?=
=?UTF-8?q?an=20Bokm=C3=A5l)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently translated at 89.0% (514 of 577 strings)
---
app/src/main/res/values-nb-rNO/strings.xml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 94c527949..ed07c22d3 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -587,4 +587,7 @@
Artister
Album
Sanger
+ Ja, og delvist sette videoer
+ Fjern sette videoer\?
+ Fjern sette
\ No newline at end of file
From 3984fc075fea6523bcf74350d02af1a85a473b8c Mon Sep 17 00:00:00 2001
From: Ishwor Ghimire
Date: Sun, 24 May 2020 09:07:30 +0000
Subject: [PATCH 036/202] Translated using Weblate (Nepali)
Currently translated at 99.8% (576 of 577 strings)
---
app/src/main/res/values-ne/strings.xml | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml
index fea27093d..e749ebe24 100644
--- a/app/src/main/res/values-ne/strings.xml
+++ b/app/src/main/res/values-ne/strings.xml
@@ -349,7 +349,7 @@
नयाँ पपअपमा लामबद्ध
यहाँ प्ले सुरु
पृष्ठभूमिमा बजाउन सुरु गर्नुहोस
- नयाँ पपअपमा बजाउन सुरु गर्नुहोस
+ पपअपमा बजाउन सुरु गर्नुहोस
ड्रअर खोल्नुहोस
ड्रअर बन्द
यहाँ केही चाँडै प्रकट हुनेछ ;D
@@ -597,4 +597,28 @@
- %d मिनेट
- %d मिनेट
+ च्यानल अवतार थम्बनेल
+ भिडियोहरू जुन प्लेलिस्टमा थपिनु अघि र पछि हेरिएको थियो हटाइनेछ।
+\n…
+\n के तपाईँ निश्चित हुनुहुन्छ\? यो उल्ट्याउन सकिदैन!
+ हेरिएका भिडियोहरू हटाउने हो\?
+ हेरिएको हटाउनुहोस्
+ सेवाहरूबाट मूल पाठहरू स्ट्रिम वस्तुहरूमा देखिने छन्
+ आईटमहरूमा मूल समय पहिले देखाउनुहोस्
+ आनंत भिडियोहरू
+ १००+ भिडियोहरू
+ कलाकारहरू
+ एल्बमहरू
+ गित
+ यो भिडियो उमेर प्रतिबन्धित छ।…
+\n…
+\nएदी तपैंलाई हेर्न परेमा, सेटिंगमा गई “उमेर प्रतिबन्धित समाग्री” सक्षम गर्नुहोस्।
+ यूट्यूब प्रतिबन्धित मोड
+ %s द्वरा
+ %s द्वरा सिर्जना गरिएको
+ यो सामग्री अझै NewPipeमा समर्थित छैन।
+\n…
+\n…
+\nआशा छ कि भविष्यको संस्करणमा समर्थित हुनेछ।
+ हो र आंशिक रूपमा हेरिएको भिडियोहरू
\ No newline at end of file
From 3ae71c73c420f609aa4e5ac3c975a8bdc091f499 Mon Sep 17 00:00:00 2001
From: notramo
Date: Sun, 31 May 2020 17:08:22 +0000
Subject: [PATCH 037/202] Translated using Weblate (Hungarian)
Currently translated at 60.6% (350 of 577 strings)
---
app/src/main/res/values-hu/strings.xml | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 1f74f79e6..4d384822a 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -19,7 +19,7 @@
Válassza ki a videófájlok letöltési helyét
Alapértelmezett felbontás
Lejátszás Kodi-val
- A Kore alkalmazás nem található. Telepíted a Kore lejátszót?
+ Telepíted a Kore lejátszót\?
\"Lejátszás Kodi-val\" opció mutatása
Opció mutatása a videók Kodi médiaközponttal való lejátszására
Hang
@@ -78,7 +78,7 @@
Felhasználói jelentés
Videó
Audio
- A kezdéshez koppints a keresésre
+ A kezdéshez koppints a \"Keresés\"-re!
Nem sikerült az összes előnézeti kép betöltése
Nem sikerült a weblap betöltése
Nem sikerült a weblap teljes betöltése
@@ -115,13 +115,13 @@
Nem található stream lejátszó alkalmazás (feltelepítheted a VLC-t a lejátszáshoz).
Megnyitás felugró ablakként
Néhány felbontásnál eltávolítja a hangot
- NewPipe felugró ablak
+ Felugró ablak
Feliratkozás
Feliratkozva
Több infó
Alapértelmezett felugró ablak felbontása
Mutassa a magasabb felbontásokat
- Csak néhány eszköz támogatja a 2K/4K videók lejátszását
+ Csak néhány eszköz tud lejátszani 2K/4K videókat
Alapértelmezett videó formátum
Fekete
Jegyezze meg a felugró ablak helyét és méretét
@@ -173,7 +173,7 @@
Adatfolyam fájl letöltése
Hozzáadás ehhez
Gyorsabb, de pontatlan tekerés használata
- A pontatlan tekerés lehetővé teszi, hogy gyorsabban ugorjon a pozíciókra, de kevesebb pontossággal
+ A pontatlan tekerés lehetővé teszi, hogy gyorsabban ugorjon a pozíciókra, de kevesebb pontossággal. Az 5, 15, vagy 25 másodperces tekerés nem működik ebben a módban.
Bélyegképek betöltése
Kapcsold ki, hogy a megelőzzed bélyegképek betöltését, így csökkentve az adat és memória használatot. Ennek az értéknek a megváltoztatása törli a memóriában és a meghajtón lévő bélyegkép gyorsítótárat.
A bélyegkép gyorsítótár törölve
@@ -375,7 +375,7 @@
Új fül
Válassz fület
Kommentek mutatása
- Kapcsold ki, hogy ne mutassa a kommenteket
+ Kapcsold ki a kommentek elrejtéséhez
Tartalmak alapértelmezett országa
Folytatás főnézetben
Figyelmen kívül hagy
@@ -384,5 +384,9 @@
Sor
a fájl nem írható felül
Az előre- és visszatekerés időtartama
- Videó előnézet megjelenítése a lezárási képernyőn
+ Videó előnézet a lezárási képernyőn
+ Legutóbbi lejátszási pozíció visszaállítása
+ Lejátszás folytatása
+ Megjeleníti a videó bélyegképét a képernyőzáron, amikor a háttér lejátszó van használva
+ Változtasd meg a letöltési helyet, hogy érvénybe lépjen
\ No newline at end of file
From c3c8d80919171b05acaadf752fa9c89868b0a59d Mon Sep 17 00:00:00 2001
From: zeritti
Date: Sat, 30 May 2020 20:17:16 +0000
Subject: [PATCH 038/202] Translated using Weblate (Czech)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-cs/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 1f532ffe7..ce5f9c88f 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -434,7 +434,7 @@ otevření ve vyskakovacím okně
Mřížka
Automaticky
Přepnout zobrazení
- K dispozici je aktualizace aplikace NewPipe!
+ Aktualizace NewPipe je k dispozici!
Klepněte pro stažení
Hotovo
Vyčkávání
From 4063221313973949388a8b14a77f680e5c40a08a Mon Sep 17 00:00:00 2001
From: Anxhelo Lushka
Date: Sat, 30 May 2020 19:17:54 +0000
Subject: [PATCH 039/202] Translated using Weblate (Albanian)
Currently translated at 100.0% (577 of 577 strings)
---
app/src/main/res/values-sq/strings.xml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index aa1238c0d..d16b32ead 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -611,4 +611,7 @@
Tekstet origjinale nga shërbimet do të jenë të dukshme në objektet e stream
Shfaq titullin origjinal \"kohë më parë\" në objekte
Modaliteti i kufizuar i NewPipe
+ Pamja statike e avatarit të kanalit
+ Nga %s
+ Krijuar nga %s
\ No newline at end of file
From a4d8388b2ea493bf093f2d9513faea9f72e64696 Mon Sep 17 00:00:00 2001
From: zmni
Date: Wed, 3 Jun 2020 00:26:02 +0000
Subject: [PATCH 040/202] Translated using Weblate (Indonesian)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-in/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index c567b4479..a18a2b93f 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -422,7 +422,7 @@
Kisi
Otomatis
Alihkan Tampilan
- Pembaruan NewPipe Tersedia!
+ Pembaruan NewPipe telah tersedia!
Ketuk untuk mengunduh
Selesai
Tertunda
From 903308d28572d5e3878c6cfda828a387e9f73a1c Mon Sep 17 00:00:00 2001
From: WaldiS
Date: Tue, 2 Jun 2020 15:27:51 +0000
Subject: [PATCH 041/202] Translated using Weblate (Polish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-pl/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 1206159eb..9101c063c 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -315,7 +315,7 @@
Wypełnij
Powiększ
Wygenerowane automatycznie
- LeakCanary
+ Wyciek danych
Monitorowanie wycieków pamięci może powodować niestabilność aplikacji podczas zrzutu pamięci
Zgłaszanie błędów poza cyklem życia
Wymusza raportowanie niedostarczonych wyjątków Rx poza cyklem życia fragmentu lub aktywności
From e68e787e7a10ed17962c3936961d5b3140daa8c3 Mon Sep 17 00:00:00 2001
From: pitachips
Date: Thu, 4 Jun 2020 16:58:41 +0000
Subject: [PATCH 042/202] Translated using Weblate (Korean)
Currently translated at 84.3% (486 of 576 strings)
---
app/src/main/res/values-ko/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 190c13b71..849b5aaf2 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -523,7 +523,7 @@
이 다운로드를 복구할 수 없습니다
인스턴스를 선택하세요
비디어 썸네일 화면 고정을 가능하게 한다
- 백그라운드 플레이어를 사용하는 경우 비디오 썸네일은 고정된 스크린 위에 표시됩니다.
+ 백그라운드 플레이어를 사용하는 경우 비디오 썸네일은 잠금화면 상에 표시됩니다.
다운로드 기록 삭제
다운로드된 파일 삭제
%1$s 다운로드 삭제
From 7092577482d719b837bf9d6feb2ce574767d5895 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Freddy=20Mor=C3=A1n=20Jr?=
Date: Sat, 6 Jun 2020 18:35:42 +0000
Subject: [PATCH 043/202] Translated using Weblate (Spanish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-es/strings.xml | 42 +++++++++++++-------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index db9c65046..6cc4b1ffe 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -2,7 +2,7 @@
%1$s vistas
Publicado el %1$s
- No se encontró reproductor. ¿Instalar VLC\?
+ No se encontró ningún reproductor de retransmisiones. ¿Instalar VLC\?
Instalar
Cancelar
Abrir en el navegador
@@ -15,14 +15,14 @@
Elegir navegador
giro
Carpeta de descarga de vídeo
- Aquí se almacenan los vídeos descargados
- Elegir carpeta de descarga para vídeos
- Cambie las carpetas de descarga para que tenga efecto
+ Los archivos de vídeo descargados se almacenan aquí
+ Elija la carpeta de descarga para los archivos de vídeo
+ Cambie las carpetas de descarga para que surtan efecto
Resolución predeterminada
Reproducir con Kodi
- ¿Instalar aplicación Kore faltante\?
+ ¿Instalar la app Kore que falta\?
Mostrar opción «Reproducir con Kodi»
- Mostrar una opción para reproducir vídeo en el centro multimedia Kodi
+ Mostrar opción para reproducir vídeo a través del centro de medios Kodi
Audio
Formato de audio predeterminado
Descargar
@@ -41,8 +41,8 @@
(Experimental) Forzar la descarga a través de Tor para una mayor privacidad (transmisión de vídeos aún no compatible).
No se puede crear la carpeta de descargas «%1$s»
Se creó la carpeta de descargas «%1$s»
- Aquí se almacenan los audios descargados
- Elegir carpeta de descarga para archivos de audio
+ Los archivos de audio descargados se almacenan aquí
+ Elija la carpeta de descarga para los archivos de audio
Carpeta de descarga de audio
Vídeo y audio
Reproducir
@@ -62,7 +62,7 @@
Contenido
Contenido restringido por edad
Mostrar vídeo restringido por edad. Se pueden realizar más cambios desde los ajustes.
- Toque \"Buscar\" para comenzar
+ Toque «Buscar» para empezar
Reproducción automática
Reproducir un vídeo cuando NewPipe es llamado desde otra app
En directo
@@ -120,20 +120,20 @@
\npara abrir en modo emergente
Reto reCAPTCHA
Reto reCAPTCHA requerido
- Modo emergente de NewPipe
+ Modo emergente
Reproduciendo en modo emergente
Formato de vídeo predeterminado
Desactivado
Mostrar resoluciones más altas
- Solo algunos dispositivos pueden reproducir vídeos en 2K/4K
+ Sólo algunos dispositivos pueden reproducir vídeos en 2K / 4K
Resolución predeterminada de emergente
Segundo plano
- Popup
+ Emergente
Filtro
Actualizar
Limpiar
- Recordar tamaño y posición del repr. emerg.
- Recordar el último tamaño y posición del repr. emerg.
+ Recordar tamaño y posición del reproductor emergente
+ Recordar el último tamaño y posición del reproductor emergente
Emergente
Redimensionando
Elimina el audio en algunas resoluciones
@@ -280,7 +280,7 @@
Esto reemplazará su configuración actual.
Descargar archivo de retransmisión
Mostrar información
- "Listas de reproducción en marcadores "
+ Playlists en marcadores
Añadir a
Arrastrar para reordenar
Crear
@@ -317,7 +317,7 @@
La monitorización de fugas de memoria puede causar que la app no responda cuando hay Heap Dump
Reportar errores fuera del ciclo de duración
Forzar reporte de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte
- Usar búsqueda rápida inexacta
+ Usar búsqueda rápida e inexacta
La búsqueda inexacta permite al reproductor buscar posiciones más rápido con menor precisión. Buscar de a 5, 15 o 25 segundos no funciona.
Poner en cola vídeo relacionado siguiente
Continuar reproducción sin repetir al añadir de forma automática un vídeo relacionado con el último visto
@@ -354,10 +354,10 @@
\n
\n¿Quiere continuar\?
Cargar miniaturas
- Desactívela para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la caché de imágenes en la memoria volátil y en el disco.
+ Desactive esta opción para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la caché de imágenes en la memoria volátil y en el disco.
Se vació la caché de imágenes
- Eliminar metadatos en memoria caché
- Eliminar todos los datos de páginas web en memoria caché
+ Vaciar metadatos en la caché
+ Eliminar todos los datos de páginas web en la caché
Se vació la caché de metadatos
Controles de velocidad de reproducción
Tiempo
@@ -527,8 +527,8 @@
Más gustados
Generado automáticamente (no se encontró creador)
Elige una instancia
- Miniatura de vídeo en la pantalla de bloqueo
- Se mostrará una miniatura del video en la pantalla de bloqueo al usar el reproductor de fondo
+ Miniatura de vídeo en pantalla de bloqueo
+ Se mostrará una miniatura del vídeo en la pantalla de bloqueo al usar el reproductor en segundo plano
Limpiar historial de descargas
Eliminar archivos descargados
Eliminadas %1$s descargas
From 77147510fbf13e34bf79df0c368189320864d97b Mon Sep 17 00:00:00 2001
From: Ville Rantanen
Date: Sun, 7 Jun 2020 12:15:44 +0000
Subject: [PATCH 044/202] Translated using Weblate (Finnish)
Currently translated at 83.5% (481 of 576 strings)
---
app/src/main/res/values-fi/strings.xml | 264 ++++++++++++++++---------
1 file changed, 175 insertions(+), 89 deletions(-)
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 44ffc5126..b2f27295c 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -3,11 +3,11 @@
Napauta hakua aloittaaksesi
%1$s näyttökertaa
Julkaistu %1$s
- Ei löytynyt soitinta. Haluatko asentaa VLC:n?
+ Ei löytynyt suoratoistosoitinta. Asennetaanko VLC\?
Asenna
Peruuta
Avaa selaimessa
- Avaa ponnahdusikkunassa
+ Avaa ponnahdusikkunatilassa
Jaa
Lataus
Haku
@@ -19,31 +19,31 @@
Käytä ulkoista videosoitinta
Poistaa äänen joillakin resoluutioilla
Käytä ulkoista äänisoitinta
- NewPipe ponnahdusikkuna
+ Ponnahdusikkunatila
Tilaa
Tilattu
Kanavan tilaus peruttu
- Ei pystytty vaihtamaan tilausta
+ Tilauksen vaihtaminen epäonnistui
Ei pystytty päivittämään tilausta
Päävalikko
Tilaukset
Uudet
Taustatoisto
- Ikkuna
- Videolatausten sijainti
- Sijainti ladatuille videoille
- Aseta videoiden tallennussijainti
- Äänilatausten sijaintikansio
- Sijainti ladatuille äänitiedostoille
- Aseta sijainti minne äänitiedostot tallennetaan
+ Ponnahdusikkuna
+ Videon latauskansio
+ Ladatut videotiedostot tallennetaan tänne
+ Valitse videotiedostojen latauskansio
+ Äänen latauskansio
+ Ladatut äänitiedostot tallennetaan tänne
+ Valitse äänitiedostojen latauskansio
Automaattitoisto
- Toistaa automaattisesti videon, kun NewPipe avataan toisesta ohjelmasta
+ Toistaa videon automaattisesti, kun NewPipe avataan toisesta ohjelmasta
Oletusresoluutio
Ponnahdusikkunan oletusresoluutio
Näytä korkeampia resoluutioita
- Vain jotkin laitteet tukevat 2K/4K videotoistoa
+ Vain jotkin laitteet voivat toistaa 2K/4K-videoa
Toista Kodi:ssa
- Kore sovellusta ei löytynyt. Asennetaanko se?
+ Asennetaanko puuttuva Kore-sovellus\?
Näytä \"Toista Kodi:ssa\" vaihtoehto
Näyttää painikkeen, jolla voi toistaa videon Kodi media center:llä
Ääni
@@ -54,35 +54,35 @@
Tumma
Musta
Muista ponnahdusikkunan koko ja sijainti
- Muista viimeisin ponnahdusikkunan koko ja sijainti
+ Muista ponnahdusikkunan viimeisin koko ja sijainti
Soittimen eleohjaus
- Käytä eleitä ohjataksesi kirkkautta ja äänentasoa
+ Käytä eleitä ohjataksesi soittimen kirkkautta ja äänentasoa
Hakuehdotukset
Näytä ehdotuksia hakiessa
Hakuhistoria
Tallentaa hakutietokannan paikallisesti
- Historia ja Välimuisti
+ Katseluhistoria
Pidä muistissa katsotut videot
Jatka toistoa keskeytysten jälkeen (esim. puhelut)
Lataus
- Seuraava video
+ Seuraava
Näytä seuraavia ja samankaltaisia videoita
- URL ei tueta
+ URL ei tuettu
Oletus-sisällon kieli
Soitin
Käyttäytyminen
- Video & Ääni
- Historia ja välimuisti
+ Video & ääni
+ Historia & välimuisti
Ponnahdusikkuna
Ulkoasu
Muu
Toistaa taustalla
- Toistaa ponnahdusikkunassa
+ Toistetaan ponnahdusikkunatilassa
Toista
Sisältö
- Näytä ikärajoitettua sisältöä
- Ikärajoitettu video. Ikärajoituksen voi sallia asetuksista.
- suora
+ Ikärajoitettu sisältö
+ Ikärajoitettu video. Muuttaminen on mahdollista asetuksissa.
+ Suora
Lataukset
Lataukset
Virheraportti
@@ -99,7 +99,7 @@
Paras resoluutio
Peru
NewPipe Ilmoitus
- Ilmoitukset Newpipe taustasoitolle ja ponnahdusikkunalle
+ Ilmoitukset NewPipen tausta- ja ponnahdusikkunasoittimille
Virhe
Verkkovirhe
Ei pystytty lataamaan kaikkia esikatselukuvia
@@ -108,24 +108,24 @@
Ei pystytty jäsentämään websivua kokonaan
Sisältö ei ole saatavilla
Ei pystytty asettamaan latausvalikkoa
- Tämä on LIVE LÄHETYS, mitä ei vielä tueta.
- Ei saatu mitään suoratoistoa
+ Live-suoratoistoa ei vielä tueta
+ Suoratoistosisältöä ei saatu
Kuvan lataus epäonnistui
Sovellus/UI kaatui
Pahoittelut, noin ei olisi pitänyt käydä.
- Raportoi virhe sähköpostin kautta
+ Raportoi tämä virhe sähköpostin kautta
Pahoittelut, joitain virheitä tapahtui.
- RAPORTTI
+ Raportti
Mitä tapahtui:
Sinun viesti (englanniksi):
Yksityiskohdat:
Videon esikatselukuva
- Videon esikatselukuva
- Jakajan avatar esikatselukuva
+ Toista video, kesto:
+ Lataajan hahmokuvake
Tykkäykset
Ei-tykkäykset
Käytä Tor
- (Kokeellinen) Pakota latausliikenne Tor:n kautta saadaksesi parempaa yksityisyyttä (videoiden suoratoistoa ei vielä tueta).
+ (Kokeellinen) Pakota latausliikenne Tor:n kautta parantaaksesi yksityisyyttä (videoiden suoratoistoa ei vielä tueta).
Raportoi virhe
Käyttäjäraportti
Ei tuloksia
@@ -135,7 +135,7 @@
Video
Ääni
Toista uudelleen
- Oikeus tallennustilan hallintaan evätty
+ Myönnä ensin käyttöoikeus tallennustilaan
t.
milj.
bilj.
@@ -164,17 +164,18 @@
Tiedostonimi
Säikeet
Virhe
- Serveriä ei tueta
+ Palvelinta ei tueta
Tiedosto on jo olemassa
Epämuodostunut URL tai Internet-yhteys ei ole saatavilla
NewPipe Lataus käynnissä
Napauta nähdäksesi lisää
Odota…
Kopioitu leikepöydälle
- Valitse saatavilla oleva latauskansio
- Tämä käyttöoikeus tarvitaan ponnahdusikkunan käytölle
+ Määritä latauskansio myöhemmin asetuksissa
+ Tämä käyttöoikeus tarvitaan
+\nponnahdusikkunatilan käyttämiseksi
reCAPTCHA Haaste
- reCAPTCHA Haaste pyydetty
+ reCAPTCHA-haaste pyydetty
Lataus
Sallitut merkit tiedostonimissä
Epäkelvot merkit korvataan tällä arvolla
@@ -190,22 +191,22 @@
Tietoja
Hyväntekijät
Lisenssit
- Vapaa ja kevyt nettivideo- ja audiostriimitoistin Androidille.
+ Vapaata ja kevyttä suoratoistoa Androidilla.
Näytä GitHub:ssa
NewPipe:n Lisenssi
- Olkoon sinulla ideoita; käännöksistä, design muutoksista, koodin siivoamisesta tai raskaista koodimuutoksista—apu on aina tervetullutta. Mitä enemmän saadaan tehtyä, sen paremmaksi sovellus tulee!
+ Olkoon sinulla ideoita; käännöksistä, designmuutoksista, koodin siivoamisesta tai raskaista koodimuutoksista—apu on aina tervetullutta. Mitä enemmän saadaan tehtyä, sen paremmaksi sovellus tulee!
Lue lisenssi
Osallistu
Historia
Haettu
Katsottu
- Historia on poistettu käytöstä
+ Historia on pois käytöstä
Historia
Historia on tyhjä
Historia pyyhitty
Poistettu
Haluatko poistaa tämän hakuhistoriasta?
- Jatka toistoa ohjelman palatessa päällimmäiseksi
+ Jatka toistoa
Info:
Mikä:\\nPyyntö:\\nSisällön kieli:\\nPalvelu:\\nGMT Aika:\\nPaketti:\\nVersio:\\nOS versio:
© %1$s %2$s %3$s alla
@@ -216,51 +217,51 @@
Syötteet
Kanavat
Valitse kanava
- Ei yhtään tilattua kanavaa vielä
+ Yhtään kanavaa ei ole vielä tilattu
Valitse kioski
Kioski
Nousussa
Top 50
Uudet & kuumat
- Näytä vihje kun taustasoitto tai popup painiketta painetaan pohjassa
+ Näytä vihje, kun taustasoitto- tai ponnahdusikkunapainiketta painetaan videon yksityiskohtanäkymässä
Lisätty taustasoittojonoon
- Lisätty ikkunajonoon
+ Lisätty ponnahdusikkunasoittimen jonoon
Toista kaikki
- Tätä ei voitu toistaa
+ Tätä suoratoistosisältöä ei voitu toistaa
Palautuskelvoton soittimen virhe
Palaudutaan soittimen virheestä
- Taustatoisto
- Ikkuna
+ Taustasoitin
+ Ponnahdusikkunasoitin
Poista
Yksityiskohdat
Ääniasetukset
Pidä pohjassa lisätäksesi jonoon
- "Näytä vihje soittolistaan lisäämiseen "
+ Näytä vihje soittolistaan lisäämiseksi
[Tuntematon]
Lisää taustatoistojonoon
- Lisää ikkunajonoon
- Aloita toistaminen
+ Lisää ponnahdusikkunajonoon
+ Aloita toistaminen tässä
Aloita toisto taustalla
- Aloita toisto ikkunassa
+ Aloita toisto ponnahdusikkunassa
Lahjoita
- NewPipe kehitetään vapaaehtoisten toimesta, jotka käyttävät vapaa-aikaansa tuottaakseen parhaan mahdollisen käyttökokemuksen sinulle. Nyt on aika antaa takaisin, jotta kehittäjät voivat tehdä NewPipe:sta vielä paremman nauttiessaan kupin kahvia.
+ NewPipe on vapaaehtoisten vapaa-ajallaan kehittämä tuoden sinulle parhaan mahdollisen käyttäjäkokemuksen. Nyt on aika auttaa kehittäjiä tekemään NewPipesta vielä parempi heidän nauttiessaan kupin kahvia.
Anna takaisin
Websivu
Käy verkkosivuillamme saadaksesi lisää tietoa ja uusimmat uutiset NewPipe:stä.
- Soitinta ei löytynyt (voit asentaa VLC:n toistaaksesi).
- Lataa toistotiedosto
+ Suoratoistosoitinta ei löytynyt (voit asentaa VLC:n toistaaksesi).
+ Lataa suoratoistotiedosto
Näytä lisätietoja
- Kirjamerkityt soittolistat
+ Kirjanmerkityt soittolistat
Lisää soittolistaan
Käytä nopeampaa epätarkkaa pikakelausta
- Epätarkka kelaus mahdollistaa videon kelauksen nopeammin, huonommalla tarkkuudella
- Näytä videoiden pikkukuvat
- Poista käytöstä lopettaaksesi kaikkien pikkukuvien lataus säästääksesi mobiilidataa ja muistinkäyttöä. Tämän asetuksen muuttaminen poistaa kaikki pikkukuvat välimuistista
+ Epätarkka kelaus mahdollistaa videon kelauksen nopeammin huonommalla tarkkuudella. Kelaaminen 5, 15 tai 25 sekuntia ei toimi tämän kanssa.
+ Lataa esikatselukuvat
+ Poista käytöstä estääksesi esikatselukuvien lataus säästääksesi dataa ja muistinkäyttöä. Asetuksen muuttaminen poistaa sekä muistissa että levyllä olevan kuvavälimuistin.
Kuvavälimuisti tyhjennetty
Poista tallennettu metatieto
Poista kaikki tallennettu sivutieto
Metatiedot poistettu
- Automaattisesti lisää seuraavan youtuben ehdottaman videon soittolistaan, sen viimeisen kappaleen soidessa (Youtuben Autoplay)
+ Jatka päättyvää (ei uudelleentoistettavaa) toistojonoa lisäämällä siihen aiheeseen liittyvä suoratoistosisältö
Sisällön oletusmaa
Palvelu
"Virheenkorjaus "
@@ -273,27 +274,27 @@
Tiedosto
Vaihda orientaatio
Vaihda taustalle
- Vaihda ponnahdusikkunatoistoon
+ Vaihda ponnahdusikkunaan
Vaihda normaalitoistoon
Tuo tietokanta
Vie tietokanta
Kirjoittaa yli tämänhetkisen historian ja tilaukset
Vie historia, tilaukset tai soittolistat
Poista katseluhistoria
- Poistaa toistohistorian
- Poista koko katseluhistoria.
+ Poistaa toistohistorian ja toistokohdat
+ Poista koko katseluhistoria\?
Katseluhistoria onnistuneesti poistettu.
Poista hakuhistoria
Poistaa historian haetuista videoista
- Poista koko hakuhistoria.
+ Poista koko hakuhistoria\?
Hakuhistoria onnistuneesti poistettu.
Ulkoiset soittimet eivät tue tämänkaltaisia linkkejä
Viallinen osoite
- Videojonoja ei löytynyt
- Audiojonoja ei löytynyt
- Virheellinen kansio
- Virheellinen tiedosto/lähde
- Tiedosta ei löydy tai sen kirjoittamiseen/lukemiseen ei ole lupaa
+ Suoratoistovideoita ei löytynyt
+ Suoratoistoääntä ei löytynyt
+ Kansiota ei ole olemassa
+ Tiedoston/sisällön lähdettä ei ole
+ Tiedoa ei ole tai lupa sen kirjoittamiseen tai lukemiseen puuttuu
Tiedostonimi ei voi olla tyhjä
Hups! Tapahtui virhe: %1$s
Vedä järjestelläksesi
@@ -307,9 +308,9 @@
NewPipen tietosuojakäytäntö
NewPipe ottaa yksityisyytesi tosissaan. Siksi se ei kerää sinulta mitään tietoja ilman lupaasi. NewPipen tietosuojakäytännössä selitetään tarkasti mitä tietoja lähetetään tai tallennetaan virheraportin yhteydessä.
Lue tietosuojakäytäntö
- Automaattisesti lisää ehdotettu video soittolistaan
- Videostriimejä ei löytynyt ladattavaksi
- NewPipe on vapaata softaa. Voit käyttää sitä, katsella sekä muokata sen koodia ja parannella sitä ilmaiseksi mielesi mukaan. Tarkemmin sanottuna, voit jakaa sitä edelleen ja/tai muokata sitä Free Software Foundationin julkaiseman GNU GPL:n, joko versio kolmosen (GPLv3), tai uudemman, ehdoilla.
+ Lisää ehdotettu suoratoistosisältö automaattisesti soittolistaan
+ Suoratoistosisältöä ei saatavilla ladattavaksi
+ NewPipe on vapaata ohjelmistoa. Voit käyttää, opiskella, jakaa ja parantaa sitä mielesi mukaan. Tarkemmin sanottuna voit jakaa sitä edelleen ja/tai muokata sitä Free Software Foundationin julkaiseman GNU General Public Licensen, version 3 tai uudemman, ehdoilla.
Haluatko poistaa tämän katseluhistoriasta?
Oletko varma että haluat poistaa kaiken katseluhistoriasta?
Viimeksi toistettu
@@ -331,31 +332,31 @@
Kysy aina
Haetaan infoa…
Ladataan pyydettyä sisältöä
- Luo uusi soittolista
- Poista soittolista
- Uudelleennimeä soittolista
+ Uusi soittolista
+ Poista
+ Uudelleennimeä
Nimi
Lisää soittolistaan
Aseta soittolistan kuvakkeeksi
Tallenna soittolista kirjanmerkkeihin
Poista kirjanmerkki
- Haluatko poistaa tämän soittolistan?
+ Poistetaanko tämä soittolista\?
Soittolista luotu
Lisätty soittolistaan
- Soittolistan kuvake muutettu
- Soittolistaa ei voitu poistaa
+ Soittolistan kuvake muutettu.
+ Soittolistaa ei voitu poistaa.
Ei tekstityksiä
Sovita
Täytä
Suurenna
Automaattisesti luotu
Tekstitykset
- Muokkaa tekstitysten kokoa ja taustaa. Asetusten päivittämiseksi sinun tulee käynnistää ohjelma uudelleen
- Laita päälle LeakCanary-muistivuodonpaljastin (virheenkorjausta varten)
+ Muokkaa soittimen tekstitysten kokoa ja taustatyylejä. Asetusten käyttöönotto vaatii uudelleenkäynnistyksen.
+ LeakCanary
Muistivuotojen valvonta voi aiheuttaa ohjelman hidastumisen virhetilanteissa
- Raportoi yhteensopimattomuusvirheitä jotka aiheutuvat vanhoista ohjelmista
- Pakota raportointi kuljetuskelvottomille Rx-poikkeuksille jotka ovat muistisirpaleiden tai aktiviteettielämänkaaren ulkopuolella poiston jälkeen
- Tuo/Vie
+ Raportoi yhteensopivuusvirheitä, jotka aiheutuvat vanhoista ohjelmista
+ Pakota raportointi toimituskelvottomille Rx-poikkeuksille, jotka ovat poiston jälkeen muistisirpaleiden tai aktiviteettielämänkaaren ulkopuolella
+ Tuo/vie
Tuo
Tuo kohteesta
Vie kohteeseen
@@ -379,10 +380,10 @@
sinun käyttäjänimesi, soundcloud.com/<sinun käyttäjänimesi>
Ota huomioon että tämä operaatio voi käyttää paljon dataa.
\nHaluatko jatkaa?
- Toiston nopeuden säätimet
- Musiikkitempo
+ Toistonopeuden säätimet
+ Tempo
Sävelkorkeus
- Irroita tempo ja nopeus toisistaan (saattaa aiheuttaa säröä äänessä)
+ Irrota toisistaan (saattaa aiheuttaa säröytymistä)
Kelaa eteenpäin hiljaisissa kohdissa
Askel
Nollaa
@@ -391,14 +392,99 @@
Hylkää
Ei rajaa
Rajoita resoluutiota kun mobiilidata on käytössä
- Pienennä vaihtaessa ohjelmaa
- Toiminto kun vaihdetaan toiseen ohjelmaan päävideosoittimesta — %s
+ Pienennä vaihdettaessa ohjelmaa
+ Toiminto vaihdettaessa toiseen ohjelmaan päävideosoittimesta — %s
Ei koskaan
Pienennä taustasoittimeksi
- Muuta ponnahdusikkunaksi
+ Pienennä ponnahdusikkunasoittimeksi
Peru tilaus
Uusi välilehti
Valitse välilehti
Valmis
Jono
+ Eteen-/taaksepäinkelauksen aika-askeleen pituus
+ Hiljattain lisätyt
+ Paikalliset
+ Pidetyimmät
+ Kieli vaihtuu, kun sovellus uudelleenkäynnistetään.
+ Kommentteja ei voitu ladata
+ Valinta
+ \@string/app_name
+ Mitkä välilehdet näytetään pääsivulla
+ Valmis
+ Paina \"Valmis\", kun ratkaistu
+ ∞ videota
+ 100+ videota
+
+ - %s katselija
+ - %s katselijaa
+
+
+ - %s kuuntelija
+ - %s kuuntelijaa
+
+ Ei kuuntelijoita
+ Ei katselijoita
+ Tilaajien lukumäärä ei saatavilla
+ Kytke palvelu, tällä hetkellä valittuna:
+ Myönnä käyttöoikeus näyttää muiden sovellusten päällä
+ Haluatko palauttaa oletusarvot\?
+ Palauta oletusarvot
+ Ei voitu lukea talletettuja välilehtiä, joten käytetään oletusvälilehtiä
+ Tiedosto siirretty tai poistettu
+ Lataaminen ulkoiselle SD-kortille ei ole mahdollista. Nollataanko latauskansion sijainti\?
+ Ulkoinen tallennustila ei käytettävissä
+ Ohje
+ Ilmoitukset uudesta NewPipe-versiosta
+ Sovelluspäivitysilmoitus
+ Tiedosto poistettu
+ Esittäjät
+ Albumit
+ Kappaleet
+ Tapahtumat
+ Videot
+ Tämä video on ikärajoitettu.
+\n
+\nSalli ikärajoitettu sisältö asetuksissa katsoaksesi.
+ YouTuben rajoitettu tila
+ Päivitykset
+ Instanssi on jo olemassa
+ Vain HTTPS-URL:t ovat tuettuja
+ Instanssia ei voitu vahvistaa
+ Syötä instanssin URL
+ Lisää instanssi
+ Hae instansseja, joista pidät, osoitteesta %s
+ Valitse PeerTube-suosikki-instanssisi
+ PeerTube-instanssit
+ Automaattinen toisto
+ Tietojen tyhjennys
+ Jatka toistoa
+ Vaihda latauskansioita, että muutokset tulevat voimaan
+ Palvelujen alkuperäiset tekstit näkyvät suoratoistettavassa sisällössä
+ Käytä eleitä ohjataksesi soittimen kirkkautta
+ Kirkkauden eleohjaus
+ Käytä eleitä ohjataksesi soittimen äänenvoimakkuutta
+ Äänenvoimakkuuden eleohjaus
+ Poista käytöstä piilottaaksesi kommentit
+ Toistokohdat poistettu.
+ Poistetaanko kaikki toistokohdat\?
+ Poistaa kaikki toistokohdat
+ Poista toistokohdat
+ Näytä toistokohtien osoittimet listoissa
+ Kohdat listoissa
+ Palauta edellinen toistokohta
+ Aiemmin katsotut ja soittolistaan lisätyt videot poistetaan.
+\nOletko varma\? Tätä ei voi peruuttaa!
+ Poistetaanko katsotut videot\?
+ Kyllä ja osittain katsotut videot
+ Pysäytä
+ Tyhjennä lataushistoria
+ Sulje
+ Poista ladatut tiedostot
+ Haluatko tyhjentää lataushistoriasi vai poistaa kaikki ladatut tiedostot\?
+ Ei kommentteja
+ Näytä kommentit
+ Kanavan hahmokuvake
+ Videon esikatselukuva näytetään lukitusnäytöllä, kun käytetään taustasoitinta
+ Videon esikatselukuva lukitusnäytöllä
\ No newline at end of file
From 5b8fc25da61f587097adb9cfd476eb0cdaeb2d60 Mon Sep 17 00:00:00 2001
From: thami simo
Date: Mon, 8 Jun 2020 22:12:05 +0000
Subject: [PATCH 045/202] Translated using Weblate (Arabic)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-ar/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 20589e9f5..3d03e88fd 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -440,7 +440,7 @@
الشبكة
تلقائي
تبديل طريقة العرض
- يتوفر تحديث ل newpipe!
+ تحديث NewPipe متاح!
اضغط لتنزيل
انتهى
ريثما
From d9ce25a72140d3fcf090eb9533c0c0f8922d84b0 Mon Sep 17 00:00:00 2001
From: Ville Rantanen
Date: Mon, 8 Jun 2020 17:13:01 +0000
Subject: [PATCH 046/202] Translated using Weblate (Finnish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-fi/strings.xml | 126 ++++++++++++++++++++++++-
1 file changed, 125 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index b2f27295c..0627d7e10 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -155,7 +155,7 @@
- %s videoita
Aloita
- Keskeytä
+ Tauota
Toista
Poista
Tarkistus-suma
@@ -487,4 +487,128 @@
Kanavan hahmokuvake
Videon esikatselukuva näytetään lukitusnäytöllä, kun käytetään taustasoitinta
Videon esikatselukuva lukitusnäytöllä
+ Kenen toimesta: %s
+ Onko syötteen lataaminen mielestäsi liian hidas\? Voit koittaa ottaa käyttöön nopean lataamisen (voit vaihtaa sen asetuksista tai painamalla alla olevaa painiketta).
+\n
+\nNewPipe tarjoaa kaksi tapaa ladata syöte:
+\n• Koko tilatun kanavan lataaminen, mikä on hidasta, mutta lataa syötteen kokonaisuudessaan.
+\n• Erityisen palvelu-endpointin käyttö, mikä on nopeaa, mutta yleensä ei lataa syötettä kokonaisuudessaan.
+\n
+\nNäiden kahden välinen ero on, että nopean lataamista tiedoista yleensä puuttuu esim. sisällön kesto tai tyyppi (ei voi erotella livevideoita ja tavallisia) tai se ei lataa kaikkea sisältöä.
+\n
+\nYouTuve on esimerkki palvelusta, joka tarjoaa nopean tavan RSS-syötteen avulla.
+\n
+\nValinta riippuu siitä, mitä halutaan: nopeutta vai tarkkoja tietoja.
+ Oletuskioski
+ %s:n luoma
+ NewPipe ei vielä tue tätä sisältöä.
+\n
+\nToivottavasti sitä tuetaan myöhemmässä versiossa.
+ Poista nopea tila käytöstä
+ Ota nopea tila käyttöön
+ Saatavilla joissakin palveluissa, on yleensä paljon nopeampi, mutta voi palauttaa rajallisen määrän sisältöä ja usein epätäydelliset tiedot (esim. ei kestoa, ei sisällön tyyppiä, ei livestatusta).
+ Hae erityisestä syötteestä, kun sellainen on saatavilla
+ Päivitä aina
+ Edellisestä päivityksestä kulunut aika, jonka jälkeen tilaus katsotaan vanhentuneeksi
+ Syötteen päivitysvälin kynnysarvo
+ Syöte
+ Uusi
+ Haluatko poistaa tämän ryhmän\?
+ Nimi
+ Tyhjä ryhmän nimi
+
+ - %d valittu
+ - %d valittu
+
+ Tilausta ei valittuna
+ Valitse tilaukset
+ Käsitellään syötettä…
+ Ladataan syötettä…
+ Ei ladattu: %d
+ Syöte viimeksi päivitetty: %s
+ Kanavaryhmät
+
+ - %d päivä
+ - %d päivää
+
+
+ - %d tunti
+ - %d tuntia
+
+
+ - %d minuutti
+ - %d minuuttia
+
+
+ - %d sekunti
+ - %d sekuntia
+
+ ExoPlayerin rajoitusten takia kelauksen aika-askel asetettiin %d sekuntiin
+ Poista katsotut
+ Järjestelmän oletus
+ Sovelluksen kieli
+ Valitse instanssi
+ \'Storage Access Framework\' sallii lataukset ulkoiselle SD-kortille.
+\nJotkin laitteet eivät ole yhteensopivia
+ Käytä SAF:ää
+ Jokaisen latauksen kohde kysytään.
+\nValitse SAF, jos haluat ladata ulkoiselle SD-kortille
+ Jokaisen latauksen kohde kysytään
+ Kysy mihin ladataan
+ Aloita lataukset
+ Yksi lataus kerrallaan on käynnissä
+ Rajoita latausjonon kokoa
+ Suurin määrä yrityksiä ennen kuin lataus perutaan
+ Uudelleenyritysten maksimimäärä
+ Poistettiin %1$s latausta
+ Tätä latausta ei voi palauttaa
+ Yhteys aikakatkaistiin
+ Eteneminen menetettiin, koska tiedosto poistettiin
+ Laitteella ei ole tilaa
+ NewPipe suljettiin, kun se käsitteli tiedostoa
+ Jälkikäsittely epäonnistui
+ Ei löytynyt
+ Palvelin ei hyväksy monisäikeisiä latauksia, yritä uudelleen asetuksella @string/msg_threads = 1
+ Palvelin ei lähetä dataa
+ Palvelimelle ei voi yhdistää
+ Palvelinta ei löytynyt
+ Turvallista yhteyttä ei voitu muodostaa
+ Järjestelmä esti käyttöoikeuden
+ Kohdekansiota ei voi luoda
+ Tiedostoa ei voi luoda
+ Koodi
+ Näytä virhe
+ Tällä nimellä on jo lataus odottamassa
+ Tällä nimellä on jo lataus käynnissä
+ tiedostoa ei voi ylikirjoittaa
+ Ladattu tiedosto, jolla on tämä nimi, on jo olemassa
+ Tämän niminen tiedosto on jo olemassa
+ Ylikirjoita
+ Luo uniikki nimi
+ %s latausta valmiina
+ Lataus valmis
+ Lataus epäonnistui
+ Järjestelmä kieltäytyi toiminnosta
+ palautetaan
+ jälkikäsitellään
+ lisätty jonoon
+ Odottaa
+ Näytä alkuperäinen aika sisällölle
+ Tauota lataukset
+ Hyödyllinen vaihdettaessa mobiilidataan, vaikka joitakin latauksia ei voi pysäyttää
+ Keskeytä käytön mukaan laskutettavilla yhteyksillä
+ tauotettu
+ Napauta ladataksesi
+ NewPipe-päivitys on saatavilla!
+ Vaihda näkymää
+ Listanäkymän ulkoasu
+ Automaattinen
+ Ruudukko
+ Lista
+ Näytä ilmoitus sovelluspäivityksestä, kun uusi versio on saatavilla
+ Päivitykset
+ Automaattisesti luotu (lataajaa ei löytynyt)
+ Poista mykistys
+ Mykistä
+ Konferenssit
\ No newline at end of file
From 1fa609e53926e5def55eb857fc514dcaedb1054b Mon Sep 17 00:00:00 2001
From: Theophine Savio Theodore
Date: Tue, 9 Jun 2020 18:58:11 +0000
Subject: [PATCH 047/202] Translated using Weblate (Malayalam)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-ml/strings.xml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index aa5362b80..ce9958c73 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -606,4 +606,10 @@
ഡൗൺലോഡ് പൂർത്തിയായി
ഡൗൺലോഡ് പരാജയപ്പെട്ടു
സിസ്റ്റം പ്രവർത്തനം തടഞ്ഞു
+ % മുഖാന്തരം
+ %s സൃഷ്ടിച്ചത്
+ ചാനൽ ലഘുചിത്രം
+ സേവനങ്ങളിൽ നിന്നുള്ള യഥാർത്ഥ വാചകങ്ങൾ സ്ട്രീം ഇനങ്ങളിൽ ദൃശ്യമാകും
+ ഇനങ്ങളിൽ യഥാർത്ഥ സമയം മുമ്പ് കാണിക്കുക
+ യുട്യൂബ് നിയന്ത്രിത മോഡ്
\ No newline at end of file
From 19334b4f96bee89e127be600a45ea30ff0f36482 Mon Sep 17 00:00:00 2001
From: Theophine Savio Theodore
Date: Tue, 9 Jun 2020 19:04:57 +0000
Subject: [PATCH 048/202] Translated using Weblate (Malayalam)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-ml/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index ce9958c73..7f95a81cf 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -224,7 +224,7 @@
വീഡിയോ
\'%1$s\' ഡൗൺലോഡ് പട്ടിക നിലവിൽ വന്നു
ഡൗൺലോഡ് പട്ടിക ഉണ്ടാക്കാൻ സാധിച്ചില്ല
- വലിച്ചിഴയ്ക്കൂ!
+ പുനക്രമീകരിക്കാൻ വലിച്ചിടുക
¡ഇബടെ ഒരു കുന്തോമില്ല!
ഫലങ്ങൾ ലഭ്യമല്ല
ഉപയോക്താവിന്റെ റിപോർട്ട്
From 7d6e226c2b374d483126f295f42a3f004eb141e9 Mon Sep 17 00:00:00 2001
From: Jasper Eames Palmer
Date: Thu, 11 Jun 2020 19:29:15 -0700
Subject: [PATCH 049/202] Update status color to match toolbar color
---
.../java/org/schabi/newpipe/MainActivity.java | 8 --------
app/src/main/res/values/colors_services.xml | 16 ++++++++--------
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 3fc3121b1..37d6d62f5 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -34,8 +34,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
@@ -127,12 +125,6 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- Window w = getWindow();
- w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
- WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- }
-
if (getSupportFragmentManager() != null
&& getSupportFragmentManager().getBackStackEntryCount() == 0) {
initFragments();
diff --git a/app/src/main/res/values/colors_services.xml b/app/src/main/res/values/colors_services.xml
index 0126ee9ae..a09878fae 100644
--- a/app/src/main/res/values/colors_services.xml
+++ b/app/src/main/res/values/colors_services.xml
@@ -4,44 +4,44 @@
#e53935
#992722
#000000
- #ff4336
+ #e53935
#CD322E
#992722
#FFFFFF
- #ff4336
+ #CD322E
#f57c00
#995700
#000000
- #ff9100
+ #f57c00
#f57c00
#995700
#FFFFFF
- #ff9100
+ #f57c00
#ff6f00
#c43e00
#000000
- #ff833a
+ #ff6f00
#ff6f00
#c43e00
#FFFFFF
- #ff833a
+ #ff6f00
#9e9e9e
#616161
#000000
- #afafaf
+ #9e9e9e
#9e9e9e
#616161
#FFFFFF
- #afafaf
+ #9e9e9e
\ No newline at end of file
From 6c6ee4134649e92d1c6f10fdcfd7712890cad55f Mon Sep 17 00:00:00 2001
From: nautilusx
Date: Fri, 12 Jun 2020 14:32:38 +0000
Subject: [PATCH 050/202] Translated using Weblate (German)
Currently translated at 99.8% (575 of 576 strings)
---
app/src/main/res/values-de/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 4f5c6e018..d8e577d55 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -427,7 +427,7 @@
Gitter
Auto
Ansicht wechseln
- NewPipe-Update verfügbar!
+ NewPipe-Update ist verfügbar!
Zum Herunterladen antippen
Fertig
Ausstehend
From ae4d9c7f8069c36fe92ba8109984872281b8a541 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Freddy=20Mor=C3=A1n=20Jr?=
Date: Wed, 10 Jun 2020 23:00:03 +0000
Subject: [PATCH 051/202] Translated using Weblate (Spanish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-es/strings.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 6cc4b1ffe..54ab846ee 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -165,7 +165,7 @@
No se pudo actualizar la suscripción
Principal
Suscripciones
- Qué hay de nuevo
+ Novedades
Reanudar reproducción
Continuar reproduciendo después de las interrupciones (ej. llamadas telefónicas)
Descargar
@@ -213,7 +213,7 @@
¿Quiere eliminar este elemento del historial de búsquedas\?
Contenido de la página principal
Página en blanco
- Página del kiosco
+ Página del quiosco
Página de suscripción
Página de novedades
Página del canal
@@ -354,10 +354,10 @@
\n
\n¿Quiere continuar\?
Cargar miniaturas
- Desactive esta opción para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la caché de imágenes en la memoria volátil y en el disco.
+ Desactivar para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la caché de imágenes en la memoria volátil y en el disco.
Se vació la caché de imágenes
- Vaciar metadatos en la caché
- Eliminar todos los datos de páginas web en la caché
+ Vaciar metadatos en memoria caché
+ Eliminar todos los datos de páginas web en antememoria
Se vació la caché de metadatos
Controles de velocidad de reproducción
Tiempo
@@ -501,7 +501,7 @@
Elimina todas las posiciones de reproducción
¿Quiere eliminar todas las posiciones de reproducción\?
Activar/desactivar servicio, seleccionados actualmente:
- kiosco predeterminado
+ Quiosco predeterminado
Nadie está viendo
- %s viendo
From 367e6258046bf7df9af8dce106a83d1e1bc6d491 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?O=C4=9Fuz=20Ersen?=
Date: Sat, 13 Jun 2020 10:01:12 +0000
Subject: [PATCH 052/202] Translated using Weblate (Turkish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-tr/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 2327858ab..f7843968f 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -5,7 +5,7 @@
Yayınlanma: %1$s
Akış oynatıcısı bulunamadı. VLC yüklensin mi\?
Yükle
- Vazgeç
+ İptal
Tarayıcıda aç
Paylaş
İndir
From 858111e62342ca7379b4cb54cbc4a311747004ac Mon Sep 17 00:00:00 2001
From: Oymate
Date: Thu, 11 Jun 2020 14:17:51 +0000
Subject: [PATCH 053/202] Translated using Weblate (Bengali (Bangladesh))
Currently translated at 30.5% (176 of 576 strings)
---
app/src/main/res/values-bn-rBD/strings.xml | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml
index 6ad5e7ad0..29906c8f9 100644
--- a/app/src/main/res/values-bn-rBD/strings.xml
+++ b/app/src/main/res/values-bn-rBD/strings.xml
@@ -185,8 +185,13 @@
থাম্বনেইল লোড করুন
থাম্বনেইল প্রদর্শন বন্ধ করার মাধ্যমে, ডাটা এবং মেমোরি সংরক্ষণ করুন। অপশনটি পরিবর্তনে ইন-মেমোরি এবং অন-ডিস্ক ইমেজ ক্যাশ উভয়ই মুছে যাবে।
ছবির ক্যাশ মুছে ফেলা হয়েছে
- ওয়েবপৃষ্ঠার সকল ক্যাশড তথ্য মুছুন
- ক্যাশড মেটাডেটা মুছুন
+ সব ক্যাশড ওয়েবপেজ ডেটা মুছে ফেলো
+ ক্যাশ করা মেটাডেটা মুছো
মেটাডেটা ক্যাশ মুছে ফেলা হয়েছে
পরবর্তী স্ট্রিম স্বয়ংক্রিয়ংভাবে সংযোজন করুন
+ প্লেয়ারের নিয়ন্ত্রণ সংকেত
+ প্লেয়ারের উজ্জ্বলতা নিয়ন্ত্রণ করতে সংকেত ব্যবহার করো
+ উজ্জ্বলতার নিয়ন্ত্রণ সংকেত
+ প্লেয়ারের ভলিউম নিয়ন্ত্রণ করতে সংকেত ব্যবহার করো
+ ভলিউম সংকেত নিয়ন্ত্রণ
\ No newline at end of file
From 58e177b3e48eb8e014a19ba0fa7a271feb324893 Mon Sep 17 00:00:00 2001
From: ButterflyOfFire
Date: Wed, 17 Jun 2020 12:25:33 +0000
Subject: [PATCH 054/202] Added translation using Weblate (Kabyle)
---
app/src/main/res/values-kab/strings.xml | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 app/src/main/res/values-kab/strings.xml
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-kab/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
From 746cab92f0f4663899bd60cff3487fa0a0141aed Mon Sep 17 00:00:00 2001
From: Ayoub Rejal
Date: Thu, 18 Jun 2020 15:17:40 +0000
Subject: [PATCH 055/202] Added translation using Weblate (Arabic (Libya))
---
app/src/main/res/values-ar-rLY/strings.xml | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 app/src/main/res/values-ar-rLY/strings.xml
diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-ar-rLY/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
From c796e2ae3c4d103c01dec6d172a6c6601061f682 Mon Sep 17 00:00:00 2001
From: Ethan Budd
Date: Thu, 18 Jun 2020 22:43:06 -0500
Subject: [PATCH 056/202] Fixes crash when a file is deleted then redownloaded
---
app/src/main/java/us/shandian/giga/io/StoredFileHelper.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java b/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
index ad3ceec3d..eba9437e1 100644
--- a/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
+++ b/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
@@ -315,6 +315,7 @@ public class StoredFileHelper implements Serializable {
return false;
if (this.isInvalid() || storage.isInvalid()) {
+ if (this.srcName == null || storage.srcName == null || this.srcType == null || storage.srcType == null) return false;
return this.srcName.equalsIgnoreCase(storage.srcName) && this.srcType.equalsIgnoreCase(storage.srcType);
}
From 487c9ebbd450b7632ef19db4d7d926bfe1dfda3e Mon Sep 17 00:00:00 2001
From: Marian Hanzel
Date: Wed, 17 Jun 2020 17:17:23 +0000
Subject: [PATCH 057/202] Translated using Weblate (Slovak)
Currently translated at 96.1% (554 of 576 strings)
---
app/src/main/res/values-sk/strings.xml | 33 ++++++++++++++------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 17ba3f3f3..57bf49544 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -63,15 +63,15 @@
Obsah
Vekovo obmedzený obsah
Toto video je vekovo obmedzené. Povoľte zobrazenie obsahu v nastavení.
- NAŽIVO
+ Naživo
Nemožno kompletne zanalyzovať web
Nemožno nastaviť menu preberania
Živé vysielanie zatiaľ nie je podporované
Nemožno získať žiadny stream
Pardon, toto sa nemalo stať.
- Nahlásiť chybu mejlom
+ Nahlásiť túto chybu mejlom
Pardon, nastala chyba.
- SPRÁVA
+ Nahlásiť
Info:
Čo sa stalo:
Váš komentár (v Angličtine):
@@ -81,7 +81,7 @@
Video
Zvuk
Znova
- Prístup na úložisko bol zakázaný
+ Najskôr povoľte prístup na úložisko
Štart
Pauza
Prehrať
@@ -153,7 +153,7 @@
Hľadané výrazy ukladať lokálne
História pozretí
Ukladať históriu pozretých videí
- Pokračovať po prepnutí zobrazenia
+ Pokračovať v prehrávaní
Pokračovať po prerušeniach (napr. hlasový hovor)
NewPipe notifikácie
Notifikácie pre NewPipe na pozadí a v mini okne
@@ -267,8 +267,8 @@
Vyberte si kanál
Zatiaľ nemáte žiadne odbery kanálov
Vyberte si kiosk
- Export je dokončený
- Import je dokončený
+ Vyexportované
+ Naimportované
Neplatný ZIP súbor
Upozornenie: Nemožno importovať všetky súbory.
Toto prepíše vaše aktuálne nastavenie.
@@ -313,7 +313,7 @@
Vyplniť
Zväčšiť
Používať rýchly posun
- Rýchly posun umožňuje prejsť na novú pozíciu rýchlejšie, ale s menšou presnosťou
+ Rýchly posun umožňuje prejsť na novú pozíciu rýchlejšie, ale s menšou presnosťou. Posúvanie o 5,15 alebo 25 sekúnd s týmto nastavením nefunguje.
Načítanie miniatúr
Vypnutím tejto funkcie sa nebudú vytvárať miniatúry a tým sa ušetrí miesto a pamäť. Zmena nastavení spôsobuje vyčistenie vyrovnávacej pamäte.
Vyrovnávacia pamäť obrázkov vymazaná
@@ -321,7 +321,7 @@
Odstrániť všetky údaje webových stránok vo vyrovnávacej pamäti
Vyrovnávacia pamäť metadát bola vymazaná
Automaticky zaradiť daľší stream
- Automaticky zaradí súvisiaci stream, keď prehrávanie začne na poslednom streame v neopakujúcich sa zoznamoch prehrávania
+ Pokračovať v prehrávaní súvisiacich streamov
Ladenie
Súbor
Neplatný adresár
@@ -337,7 +337,7 @@
Automaticky vygenerované
Titulky
Upravte mierku textu titulkov prehrávača a štýly pozadia. Vyžaduje sa reštart aplikácie.
- Povoliť službu LeakCanary
+ LeakCanary
Monitorovanie pretečenia pamäte môže spôsobiť, že aplikácia nebude reagovať
Nahlásiť mimo-cyklické chyby
Vynútiť hlásenie výnimiek nedoručiteľných Rx mimo časového cyklu fragmentov alebo aktivity po zneškodnení
@@ -419,10 +419,10 @@
Upozornenia na novú verziu NewPipe
Externé úložisko je nedostupné
Sťahovanie na externú SD kartu nie je možné. Obnoviť umiestnenie priečinka na sťahovanie\?
- Chyba pri načítavaní uložených kariet, použijú sa predvolené
+ Nemožno načítať uložené karty, použijú sa predvolené
Obnoviť predvolené nastavenia
Chcete obnoviť predvolené hodnoty\?
- Počet účastníkov nie je k dispozícii
+ Počet odberateľov nie je k dispozícii
Karty, ktoré sa zobrazujú na hlavnej stránke
Výber
Konferencie
@@ -455,7 +455,7 @@
Adresár nemožno vytvoriť
Nemožno vytvoriť súbor
Povolenie odmietnuté systémom
- Bezpečnostné pripojenie zlyhalo
+ Nepodarilo sa vytvoriť zabezpečené pripojenie
Server sa nepodarilo nájsť
Nepodarilo sa pripojiť k serveru
Server neposiela údaje
@@ -568,8 +568,8 @@
- %d dni
- %d dní
- Skupiny zdrojov
- Najstaršia aktualizácia odberu: %s
+ Skupiny kanálov
+ Aktualizované: % s
Nenačítané: %d
Načítavanie zdroja…
Spracovávanie zdroja…
@@ -622,4 +622,7 @@
\n
\nPokiaľ ho chcete pozerať, musíte povoliť \"Vekovo obmedzený obsah\" v nastaveniach.
YouTube v obmedzenom režime
+ %s
+ Vytvoril %s
+ Minuatúrny avatar kanála
\ No newline at end of file
From a470a4af9b625350f63a61854a8965d0a74b4153 Mon Sep 17 00:00:00 2001
From: Andreas Westrell
Date: Wed, 17 Jun 2020 13:42:56 +0000
Subject: [PATCH 058/202] Translated using Weblate (Swedish)
Currently translated at 99.1% (571 of 576 strings)
---
app/src/main/res/values-sv/strings.xml | 80 ++++++++++++++++++++++++--
1 file changed, 76 insertions(+), 4 deletions(-)
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 3a83b8676..9ac4dc5be 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -239,10 +239,10 @@
Ljudinställningar
Håll för att placera i kön
Placera i bakgrunds-kön
- Placera i popup-kön
+ Bifoga i en popup
Börja spela här
Börja spela i bakgrunden
- Börja spela i en ny popup
+ Börja spela i en popup
Donera
NewPipe utvecklas av frivilliga som spenderar sin fritid på att ge dig den bästa användarupplevelsen. Nu är det tid att ge tillbaka för att säkerställa att utvecklarna kan göra NewPipe ännu bättre medan de njuter av en kopp kaffe.
Ge tillbaka
@@ -352,7 +352,7 @@
Autogenererade
Textning
Ändra spelarens textskala och bakgrundsstil. Kräver att appen startar om för att träder i kraft.
- Aktivera LeakCanary
+ LeakCanary
Minnesläcka övervakning kan orsaka att appen inte svarar under heap dumpning
Rapportera out-of-lifecycle fel
Tvinga rapportering av otillåtna Rx-undantag utanför fragment eller aktivitetslivscykel efter uppstädning
@@ -439,7 +439,7 @@
Det finns en pågående nedladdning med det här namnet
Visa fel
Kod
- Säker anslutning misslyckades
+ Kunde inte fastställa en säker anslutning
Kunde inte hitta servern
Kunde inte ansluta till servern
Servern skickar ingen data
@@ -522,4 +522,76 @@
YouTube begränsat läge
Språket ändras när appen har startats om.
Det gick inte att ladda kommentarer
+ Inaktivera snabbläge
+ Aktivera snabbläge
+ Tillgängligt i vissa tjänster det är vanligtvis mycket snabbare men kan returnera en begränsad mängd artiklar och ofta ofullständig information (t.ex. ingen varaktighet, objekttyp, ingen live-status).
+ Hämta från dedikerat flöde när det finns tillgängligt
+ Uppdatera alltid
+ Tid efter senaste uppdatering innan en prenumeration anses vara föråldrad — %s
+ Tröskel för uppdatering av flöde
+ Flöde
+ Ny
+ Vill du ta bort den här gruppen\?
+ Namn
+ Tomt gruppnamn
+
+ - %d vald
+ - %d valda
+
+ Ingen prenumeration vald
+ Välj prenumerationer
+ Bearbetar flöde …
+ Läser in flöde …
+ Ej laddad: %d
+ Flödet senast uppdaterat: %s
+ Kanalgrupper
+
+ - %d dag
+ - %d dagar
+
+
+ - %d timme
+ - &d timmar
+
+
+ - %d minut
+ - %d minuter
+
+
+ - %d sekund
+ - %d sekunder
+
+ På grund av ExoPlayer-begränsningar sattes söktiden till %d sekunder
+ Ja, och delvis tittade videor
+ Videor som har tittats före och efter att de har lagts till i spellistan kommer att tas bort.
+\nÄr du säker\? Detta kan inte göras ogjort!
+ Ta bort tittade videor\?
+ Ta bort tittade
+ Systems standard
+ Appspråk
+ Välj en instans
+ \"Storage Access Framework\" tillåter nedladdningar till ett externt SD-kort.
+\nVissa enheter är inkompatibla
+ Använd SAF
+ Du blir frågad var du vill spara varje nedladdning.
+\nVälj SAF om du vill ladda ner till ett externt SD-kort
+ Du blir frågad var du vill spara varje nedladdning
+ Fråga var du ska ladda ner
+ Pausa nedladdningar
+ Starta nedladdningar
+ En nedladdning körs samtidigt
+ Begränsa nedladdningskön
+ Stäng ner
+ Användbart när du byter till mobildata, även om vissa nedladdningar inte kan avbrytas
+ Avbryt i en uppmätt nätverk
+ Rensa %1$s nedladdningarna
+ Radera nedladdade filer
+ Vill du rensa din nedladdningshistorik eller radera alla nerladdade filer\?
+ Rensa nedladdningshistorik
+ Kunde inte återfinna nedladdningen
+ Anslutnings avbrott
+ Framsteg förlorat, För att filen blev borttagen
+ Inget utrymme kvar på enhet
+ NewPipe stängdes under arbete med en fil
+ Tillåtelse nekat av systemet
\ No newline at end of file
From 1275b26ba060916444cbdaff444368aed04b365b Mon Sep 17 00:00:00 2001
From: ButterflyOfFire
Date: Wed, 17 Jun 2020 12:27:08 +0000
Subject: [PATCH 059/202] Translated using Weblate (Kabyle)
Currently translated at 4.3% (25 of 576 strings)
---
app/src/main/res/values-kab/strings.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index a6b3daec9..98d8d8edc 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -1,2 +1,4 @@
-
\ No newline at end of file
+
+ Iccer amaynut
+
\ No newline at end of file
From 76bb1fd61ece23fdfa5e7bd135504a9095bdc954 Mon Sep 17 00:00:00 2001
From: Ayoub Rejal
Date: Thu, 18 Jun 2020 15:18:17 +0000
Subject: [PATCH 060/202] Translated using Weblate (Arabic (Libya))
Currently translated at 6.7% (39 of 576 strings)
---
app/src/main/res/values-ar-rLY/strings.xml | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml
index a6b3daec9..d0c5e2605 100644
--- a/app/src/main/res/values-ar-rLY/strings.xml
+++ b/app/src/main/res/values-ar-rLY/strings.xml
@@ -1,2 +1,21 @@
-
\ No newline at end of file
+
+ استخدم مشغل فيديو خارجي
+ دوران
+ اختر المتصفح
+ مشاركة مع
+ اعدادات
+ بحث
+ تنزيل ملف البث
+ تحميل
+ مشاركه
+ فتح في نافدة منبثقة
+ افتح في المتصفح
+ إلغاء
+ تثبيت
+ لم يتم العثور على مشغل بث (يمكنك تثبيت VLC لتشغيله).
+ لم يتم العثور على مشغل بث. يرجى تثبيت VLC؟
+ تم النشر في %1$s
+ %1$s من المشاهدات
+ انقر على \"بحث\" للبدء
+
\ No newline at end of file
From c9a1eb55b560ebb6d0a4f8190c0a91be55055a57 Mon Sep 17 00:00:00 2001
From: Zackz
Date: Sun, 21 Jun 2020 16:02:36 +0000
Subject: [PATCH 061/202] Added translation using Weblate (Javanese)
---
app/src/main/res/values-jv/strings.xml | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 app/src/main/res/values-jv/strings.xml
diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-jv/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
From 2ecf8044d70b52331744ba4f7fe4d2d331745585 Mon Sep 17 00:00:00 2001
From: Zackz
Date: Sun, 21 Jun 2020 16:06:46 +0000
Subject: [PATCH 062/202] Translated using Weblate (Javanese)
Currently translated at 10.9% (63 of 576 strings)
---
app/src/main/res/values-jv/strings.xml | 88 +++++++++++++++++++++++++-
1 file changed, 87 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml
index a6b3daec9..78d2cd6c0 100644
--- a/app/src/main/res/values-jv/strings.xml
+++ b/app/src/main/res/values-jv/strings.xml
@@ -1,2 +1,88 @@
-
\ No newline at end of file
+
+ Posisi nang daftar
+ Teruske, loske wae lur
+ Delok sejarah mantan
+ Riwayatmu biyen cuk
+ Duduhke saran nalika nggoleki
+ Goleki saran
+ Gunake gestur gawe ngontrol kepadhangan lan volum
+ Ngontrol kepadhangan ngaggo gesture
+ Kontrol kepadhangan nganggo gestur
+ Gunake gesture gawe ngontrol volum
+ Pokoke teruske muter suara/video
+ Antri otomatis stream bare
+ Sampah metadata wes dibusak
+ Busak kabeh sampah ora kanggo
+ Pateni ben gambar cilik ora ketok, ora boros data lan memori. Iku bakal ngresiki sampah gambar.
+ Sampah gambar wes resik
+ Pateni gawe ngumpetke komentar
+ Duduhke komentar
+ Duduhke gambar cilik
+ Durasi cepet maju/mundure
+ Eling-eling ukuran lan posisi ngambang terakhir
+ Eling-eling ukuran lan posisi ngambang
+ Ireng
+ Peteng
+ Padhang
+ Tema
+ Format video standar
+ Format audio standar
+ Suara ati
+ Gambar cilik video diduduhke nang layar kegembok nalika nganggo pemuter mburi
+ Duduhke pilihan muter video nganggo Kodi
+ Gambar cilik nang lock screen
+ Duduhke pilihan \"Puter nganggo Kodi\"
+ Aplikasi Kore rung ono, pasang\?
+ Puter nganggo Kodi
+ Mung piranti tertentu sing iso muter video 2K/4K
+ Duduhke resolusi luwih gedhe
+ Resolusi ngambang standar
+ Resolusi standar
+ Puter video nalika NewPipe diarani seko aplikasi liyane
+ Puter otomatis
+ Ganti tempat unduhan ben ngefek
+ Pilih tempat kanggo nyimpen suara
+ Suara sing diunduh disimpen nang kene
+ Tempat nyimpen suara
+ Pilih tempat nyimpen video
+ Video unduhan disimpen nang kene
+ Folder video unduhan
+ Tambah nang
+ Ngambang
+ Latar mburi
+ Pilih Tab
+ Tab anyar
+ Daftar sing ditandai
+ Langganan
+ Utama
+ Ndeloke info
+ Ora iso nganyari langganan
+ Ora iso ngubah langganan
+ Wes ora langganan saluran
+ Rasido langganan
+ Pelanggan
+ Langganan
+ Mode ngambang
+ Gunake pemuter suara liyane
+ Busak suara ing resolusi cuk
+ Gunake pemuter video liyane
+ puter
+ Pilih browser
+ Bagi nganggo
+ Maksude: %1$s\?
+ Setelan
+ Goleki
+ Unduh file
+ Unduh
+ Bagi
+ Buka nang mode nyembul
+ Buka nang Browser
+ Orasido
+ Pasang
+ Ra ono pemuter (kowe iso pasang VLC gae muter iku).
+ Raono pemuter. Pasang VLC\?
+ Diterbitke %1$s
+ %1$s penonton
+ Pencet \"Cari\" nek meh mulai
+
\ No newline at end of file
From 33f3a4f455908402e8d0ff860af95e27c8162edb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89frit?=
Date: Tue, 23 Jun 2020 01:28:34 +0000
Subject: [PATCH 063/202] Translated using Weblate (French)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-fr/strings.xml | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index dc4ca619e..1d6ff3f8b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -562,7 +562,7 @@
Dernière mise à jour du flux : %s
Pas chargés : %d
Chargement du flux…
- Préparation du flux…
+ Traitement du flux…
Choisir les abonnements
Aucun abonnement sélectionné
@@ -577,21 +577,21 @@
Seuil de mise à jour du flux
Temps depuis la dernière mise à jour pour que le flux ne soit plus considéré comme à jour — %s
Toujours mettre à jour
- Charger depuis un service dédié quand c\'est possible
- Disponible pour quelques services, c\'est généralement bien plus rapide mais peut retourner un nombre d\'élements limité et des informations souvent incomplètes (ex : pas de durée, pas de statut « En direct ».
+ Récupérer depuis un flux dédié lorsque disponible
+ Disponible dans certains services, c’est généralement beaucoup plus rapide mais cela peut renvoyer un nombre limité d’éléments et souvent avec des informations incomplètes (p. ex. aucune durée, de type d’élément ou de statut en direct).
Activer le mode rapide
Désactiver le mode rapide
- Pensez-vous que la chargement du flux est trop lent \? Si c\'est le cas, essayez le mode rapide (vous pouvez changer le mode dans les paramètres ou en pressant le bouton en dessous).
+ Pensez-vous que le chargement du flux est trop lent \? Si c’est le cas, essayez le chargement rapide (vous pouvez le modifier dans les paramètres ou en pressant le bouton ci-dessous).
\n
-\n NewPipe propose deux manières de charger le flux :
-\n • Charger toutes les chaines auquelles vous êtes abonnés, cette manière est lente mais complète.
-\n • Charger depuis un service dédié, cette manière est plus rapide mais souvent incomplète.
+\nNewPipe propose deux stratégies de chargement de flux :
+\n• Récupérer la totalité de la chaîne d’abonnement, ce qui est lent mais complet.
+\n• Utiliser un service dédié, ce qui est rapide mais souvent incomplet.
\n
-\n La différence entre les deux est que la méthode rapide manque souvent de quelques informations, comme la durée d\'un élément ou le type (nous ne pouvons pas différencier les vidéos en direct des vidéos normales) et pourrait retourner moins d\'éléments.
+\nLa différence entre les deux est que la méthode rapide manque souvent de quelques informations, comme la durée d’un élément ou le type (pas de distinction possible entre les vidéos en direct et les autres), et elle pourrait retourner moins d’éléments.
\n
-\n YouTube est un exemple qui offre cette méthode rapide avec le flux RSS.
+\nYouTube est un exemple de service qui offre une méthode rapide avec son flux RSS.
\n
-\n Donc le choix vous revient : Préferez-vous la vitesse ou des informations précises \?
+\nDonc le choix se résume à ce que vous préférez : la rapidité ou des informations précises.
Aide
Ce contenu n\'est pas encore supporté par NewPipe.\n\nIl le sera peut-être dans une version future.
Albums
From 8c823f3a2d419a8e2394aa1ed571b29673ab9f5d Mon Sep 17 00:00:00 2001
From: Jwtiyar Nariman
Date: Wed, 24 Jun 2020 09:12:52 +0000
Subject: [PATCH 064/202] Added translation using Weblate (Central Kurdish)
---
app/src/main/res/values-ckb/strings.xml | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 app/src/main/res/values-ckb/strings.xml
diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-ckb/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
From 039b47b872cbf9b965b1fc2629f569b34a124937 Mon Sep 17 00:00:00 2001
From: Jwtiyar Nariman
Date: Wed, 24 Jun 2020 11:57:03 +0000
Subject: [PATCH 065/202] Translated using Weblate (Central Kurdish)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-ckb/strings.xml | 616 +++++++++++++++++++++++-
1 file changed, 615 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml
index a6b3daec9..2a386c262 100644
--- a/app/src/main/res/values-ckb/strings.xml
+++ b/app/src/main/res/values-ckb/strings.xml
@@ -1,2 +1,616 @@
-
\ No newline at end of file
+
+ ئایا توانای بارکردن لاوازە؟ گەر وایە ئەوا بارکردنی خێرا تاقی بکەرەوە (دەتوانی بیگۆڕیت لە بەشی ڕێکخستنەکان لەڕێگای گرتەکردن لەم دوگمەیەی خوارەوە).
+\n
+\nئەم داوانامەیە دوو شێوازی بارکردنت بۆ پێشنیاز دەکات:
+\n- بارکردنی تەواوی کەناڵە بەشدارییەکانت، ئەمەیان خاوە بەڵام تەواوە.
+\n- تەرخانکردنی خزمەتگوزارییەکان ئەمەیان خێرایە بەڵام زۆر تەواو نییە.
+\n
+\nجیاوازی نێوان ئەم دووانە ئەوەیە کە خێراکە چەند زانیارییەکی وونە، وەکوو ماوەی هەر بڕگەیەک یان جۆرەکەی (ناتوانێت جیاوازی بکات لە نێوان ڤیدیۆی ڕاستەوخۆ و ئاسایی) لەوانەشە بڕگەی کەمتر باربکات.
+\n
+\nیوتیوب نموونەیە لەم خزمەتگوزارییە کە ڕێگەی خێرا بەکاردەبات بەهۆی پیشاندەری RSS.
+\n
+\nبۆیە هەڵژرادن بۆ خۆت دەگەڕێتەوە: زانیاری تەواو یان خێرا.
+ ئەم داوانامەیە سەرچاوە کراوەیە : دەتوانی بەکاریبهێنی، بیخوێنیتەوە و هاوبەشی پێبکەیت و بەرەوپێشی ببەیت. بەتایبەتی دەتوانی دابەشیبکەیتەوە یاخوود بگۆڕیت بەپێی مەرجەکانی GNU مۆڵەتنامەی گشتی وەک داوانامەیەکی بڵاوی خۆڕایی, بەهۆی وەشانی ٣ ی مۆڵەتنامە، یان هەر وەشانێک کە دوواتر.
+ چی:\\nداواکراو:\\nزمانی ناوەڕۆک:\\nخزمەتگوزاری:\\GMTnکات:\\nگورزە:\\nوەشان:\\nوەشانی سیستەم:
+ پڕۆژەی ئەپەکە زانیارییە تایبەتییەکانت بەوردی دەپارێزێ. هەروەها داوانامەکە هیچ زانایارییەکت بەبێ ئاگاداری تۆ بەکارنابات.
+\nسیاسەتی تایبەتی داوانامە بەوردی ڕوونکردنەوەت دەداتێ لەسەر ئەو زانیاریانەی وەریاندەگرێ و بەکاریاندەبات.
+ داگرتن لە بیرگەی دەرەکی ناکرێت. شوێنی بوخچەی داگرتنەکان دابنرێتەوە؟
+ ناچالاكی بكه بۆ ڕاگرتنی وێنۆچكهكان له باركردن و پاشهكهوتبوون لهسهر بیرگهی ئامێرهكهت, گۆڕینی ئهمه دهبێته هۆی سڕینهوهیان لهسهر بیرگهی مۆبایلهكهت.
+ مەبەستت ئەمەیە: %1$s\?
+ ماوەی نوێکاری
+ هێڵەکی
+ پاشکۆی خۆکاری پەخشێکی بەستراوە لەکاتی کارپێکردنی کۆتا پەخشدا
+ سنوری ڕیزبوونی داگرتنەکان
+ بیرگەی ناوەکیت پڕبووە
+ ژمارەی بەشداربووان نادیارە
+ ناتوانرێ لەسەر ئەو پەڕگەیە جێگیربکرێ
+ بازدەر هەڵبژێرە
+ ملیۆن
+ +١٠٠ ڤیدیۆ
+ کارپێکەر
+ هێنانەوە
+ سکاڵا لەسەر کێشە لەڕێگای ئیمێڵ
+ ناو
+ چارەسەرکردن شکستی هێنا
+ بچوکبوونەوە لەکاتی گۆڕینی داوانامە
+ پەڕەی نوێترینەکان
+ |(تاقیکاری) داگرتنی خێرا بەبەکارهێنانی Tor بۆ زیادکردنی تایبەتێتی (پشتگیری پەخشە ڕاستەوخۆکان ناکات) .
+ پەڕگەی ڤیدیۆ داگیراوەکان لێرەدا هەڵدەگیرێن
+ هەناردەکردنی مێژوو و بەشداربوون و لیستەکان
+ بردنەپێشی ناتەواوی خێرا وا لە کارپێکەرەکە داکات کە بەخێرایی شوێنەکە بگۆڕێت. بردنەپێشی ٥ یان ١٥ یان ٢٥ چرکەیی لەگەڵ ئەمەدا کارناکات.
+ سکاڵاکردن لەسەر نەگەیاندنی Rx ی پەسەندنەکرا لە دەرەوەی پارچە یان چالاکی لەدوای پوختەکردن
+ قەبارە دانانەوە
+ پیشاندانی ’دواتر’ و ڤیدیۆ ’هاوشێوەکان’
+ هێنانەپێش
+ پەڕە بنەڕەتییەکان بەکاردەبردرێن, ناتوانرێ پەڕە پاشەکەوتکراوەکان بخوێنرێنەوە
+ سڕینەوەی تەواوی مێژووی تەماشاکردن؟
+ سڕینەوەی مێژووی گەڕان
+ هەڵە
+ ئەمە لەسەر ڕێکخستنەکانی ئێستات جێگیردەبێت.
+ ئاگانامەکانی داوانامە
+ ئەم ئەپە لەلایەن چەند خۆبەخشێکەوە دروستکراوە کەکاتی خۆیان پێ بەخشیووە تاکو باشترین خزمەتگوزاریت پێشکەش بکەن. هیچ نەبێت بە کڕینی کوپێک قاوە یارمەتی گەشەپێدەرەکانمان بدە بۆ ئەوەی کاتی زیاتر تەرخان بکەین بۆ بەرەوپێشبردنی NewPipe.
+ بلیۆن
+ گەڕانی پێشنیارکراوەکان
+ خێراییەکان
+ پەڕگە سڕایەوە
+ کۆمەڵەی کەناڵەکان
+ ئەم دەسەڵاتە پێویستە بۆ
+\nکردنەوەی پەنجەرەی بچووک
+ پەڕگە بوونی هەیە
+ ڕاگیراوە
+ لە چاوەڕوانیدایە
+ هەناردەکرا
+ هەڵەدۆزی
+ پاشماوەی داتاکان سڕانەوە
+ کیۆسکی بنەڕەتی
+ باشترین پیشاندان
+
+ - %s بەشداربوو
+ - %s بەشداربوون
+
+ سڕینەوەی یەک دانە
+ پیشاندانی وردی بهرزتر
+ ئەرکی نوێ
+ لەڕیزدایە
+ دانانی ناوی نوێ
+ لەسەر مێژوو و بەشداربووەکانی ئێستات جێگیردەبێت
+ هیچ پەخشێ نەدۆزرایەوە
+ تەمەنت بۆ تەماشاکردنی ئەم ڤیدیۆیە ڕێپێنەدراوە.
+\n
+\nگەر دەتەوێت بیبینیت ئەوا ناوەڕۆکە ڕێپێنەدراوەکانی تەمەن لە ڕێکخستنەکان چالاک بکە.
+ سڕینەوەی مێژووی داگرتن
+ تەنها یەکجار
+ دۆخی قەدەغەکراوی یوتیوب
+ reCAPTCHA داواکاری
+ نوێکارییەکان
+ سکاڵا لەبەکاربەر
+ ئەم ڕاژەیە ناتوانێ چەندین داگرتن لەیەک کاتدا بکات
+ چالاککردنی دۆخی خێرا
+ بچوککردنەوە بۆ کارپێکەری پاشبنەما
+ پەخشی ڕاستەوخۆ پشتگیری ناکرێ لەئێستادا
+ کاتێ کارپێکەری پاشبنەما کاردەکات ئەوا وێنۆچکەی ڤیدیۆکە لە ڕوونما داخراوەکەدا نیشاندەدرێت
+ بهشداریت نەما له كهناڵ
+ ناتوانرێ ئەم پەخشە کارپێبکرێ
+ بیرهاتنهوهی شوێن و قهبارهی پهنجهرهی بچووک
+ گێڕانەوەی کارپێکەر بۆکاتی پێش کێشە
+ هیچیان
+ بەسوودە بۆ کاتی گۆڕینی هێڵ بۆ داتای مۆبایل, لەگەڵ ئەوەشدا زۆربەی داگرتنەکان ڕاناگرێت
+ ماڵپەڕ
+ لە ڕیز
+ كارپێكردن به Kodi
+ ناتوانرێ لێدوانەکان باربکرێ
+ بەستەری دۆخ دابنێ
+ پەڕگەی دەنگە داگیراوەکان لێرەدا هەڵدەگیرێن
+ ببورە، هەندێ کێشە ڕوویدا.
+ هەناردەکردن بۆ
+ ڕەفتار
+ هەڵبژاردنی کەناڵ
+ پرسیاربکرێ لەکوێ دابگیرێ
+ © %1$s لەلایەن %2$s لەژێر %3$s
+ مۆڵەتنامەی لایەنی سێهەم
+ مۆڵەتنامەی داوانامە
+ پیشاندانی ڕێنمایی ”داگرتن تا پاشکۆ”
+ بەڵێ
+ دابەشکراوەکان
+ زۆرترین کارپێکردن
+ لابردنی دڵخواز
+ مۆڵەتەکان
+ ناتوانرێت بهشداریكردنهكه نوێبكرێتهوه
+ بازدەرێکی نوێ
+ جوڵەی پەنجەت لەسەر ڕوونما بەکاربهێنە بۆ گۆڕینی ئاستی دەنگ و ڕووناکی
+ پاشبنەما
+ هیچ ئەنجامێک نییە
+ زمان دەگۆڕدرێ لەدوای داخستن و پاشان کردنەوەی داوانامە.
+ سڕینەوەی تەماشاکراوەکان
+ پیشاندانی نیشانەکەری شوێنی کارپێکراو لە لیستەکان
+ باركردنی وێنۆچكهكان
+ شوێنەکان لە لیستدا
+ بهشداربوویت
+ بەهۆی گۆڕانکاری لە شێوەی ژێرنووسکردنەکە. پێویستە داوانامە دابخەیت و دیسانەوە بیکەیتەوە.
+
+ - %d دیاریکراو
+ - %d هەڵبژێردراو
+
+ ناتوانرێ ڕاژە بدۆزرێتەوە
+ مێژوو & کاش
+ دیار
+ سڕینەوە
+
+ - %s گوێی لێدەگرێ
+ - %s گوێی لێدەگرن
+
+ داگرتنەکانی داوانامە
+ ناو
+ کەناڵەکان
+ مێژوو
+ ناتوانرێ بەشدارییەکان پاشەکەوت بکرێن
+
+ - %s بینراو
+ - %s بینراو
+
+ ڕاگرتن
+ فۆڵدەری داگرتنی پەڕگە دەنگییەکان هەڵبژێرە
+ نوێ
+ سڕینەوەی مێژووی تەماشاکردن
+ کارپێکردنەوەی لیست
+ دەستکەوتنی زانیاری…
+ مێژووی گەڕانەکانت دەسڕێتەوە
+ چی ڕوویدا:
+ کارپێکردن لە پاشبنەما
+ بێ سنوور
+ ڕەتکردنەوە
+ پیشاندانی کێشە
+ کارپێکردن
+ هەمیشە نوێکردنەوە
+ ناتوانرێ بەشدارییەکان بهێنرێتەوە
+ کۆنترۆڵی ڕووناکی بەجوڵەی پەنجە
+ دەرکەوتن
+ هێنانەوەی بنکەی زانیاریەکان
+ گەڕان بەناو مێژوو
+ پەڕگەیەکی داگیراو بەم ناوەوە هەیە
+ ڕاکێشان بۆ دووبارە ڕێکخستنەوە
+ سڕینەوە
+ دۆخی پهنجهرهی بچووک
+ هەموو کات
+ مۆڵەتنامە بخوێنەوە
+ داوانامە/ڕووکار ڕاوەستا
+ هەمووی کارپێبکە
+ کارپێکردن
+ هەمان پەڕگە/بابەت بوونی نییە
+ دەستپێکردن
+ بهشداربوون
+ بژاردەی ”کارپێکردن بە Kodi“ پیشانبدرێت
+ بهشدارییهكان
+ پەڕەی بەتاڵ
+ جۆری بنەڕەتی دهنگ
+ هێما تایبەتییەکان
+ ڕووكار
+ گۆڕین بۆ سەرەکی
+ دەقە بنچینەییەکان لە خزمەتگوزارییەکانەوە لە بابەتی پەخشەکاندا دیار دەبن
+ کارپێکەری پەنجەرەی بچووک
+ داگرتن
+ ژێرنووسەکان
+ بەستەر هەڵەیە
+ ڕیزنەبوون لە پاشبنەما
+ کۆنترۆڵی دەنگ بەجوڵەی پەنجە
+ ڕیزکردنی خۆکاری کارپێکردنی دواتر
+ کارپێکەرە ڤیدیۆییە دەرەکییەکان پشتگیری ئەم جۆرە بەستەرانە ناکەن
+ کردار ڕەتکرایەوە لەلایەن سیستەمەوە
+ پەڕەی بەشدارییەکان
+ وێنۆچکەی پێشبینینی ڤیدیۆ
+ پهنجهرهی بچووک
+ ڕهش
+ قهبارهی بنەڕەتی پهنجهرهی بچووک
+ ئاگاداربە: ناتوانرێ هەموو پەڕگەکان بهێنرێنەوە.
+ ئارەزوومەندییەکانی دۆخی پێرتووبی ڕێکبخە
+ دواین نوێکردنەوە: %s
+ هەڵبژاردن
+ ڕاگرتن
+ لیست
+ دەستپێکردنەوەی داگرتنەکان
+ هیچ ژێرنووسێک نییە
+ %s داگرتن تەواوبوون
+ کارپێکردن لە پەنجەرەی بچووک
+ دواتر
+ هیچ بینراوێک نییە
+ ئەو پەڕگەیە بوونی نییە یان دەسەڵاتی خوێندنەوە و نوسینی لاوازە
+ هیتر
+ ناتوانرێ ماڵپەڕ شیبکرێتەوە
+ نووسە ڕێگەپێدراوەکان لە پەڕگەیەکی ناویدا
+ نازنامەکەت , soundcloud.com/yourid
+ یارمەتی
+ چی نوێ ههیه
+ پەنجەرەی بچووک
+ گرتەبکە بۆ وردەکاری
+ شوێنی هەموو کارپێکراوەکان بسڕدرێتەوە؟
+ باشترین ٥٠
+ لیستی کارپێکردن
+ تەنها بەستەرەکانی https پشتگیریکراون
+ پەیوەستبوونی پارێزراو شکستی هێنا
+ ڕاژە پشتگیرینەکراوە
+ ئەو ڤیدیۆیانەی پێشتر سەیرت کردوون و دواتر زیادت کردوون بۆ خشتەی کارکردن دەسڕێنەوە.
+\nئایا دڵنیایت؟ ئەمە ناگەڕێنرێتەوە!
+ بابەت سڕایەوە
+ وێنۆچکەی کەناڵ
+ ئایا دەتەوێت ڕێکخستنەکانیش بهێنرێنەوە؟
+ هیچ كارپێكهرێكی ڤیدیۆیی نهدۆزرایهوه. دهتهوێت VLC دابمەزرێنیت؟
+ بوخچەی داگرتنی ڤیدیۆ
+ پاڵاوتن
+ کردنەوەی پلیکانە
+ کاڵ
+ پیشاندانی پێشنیارەکان لەکاتی گەڕان
+ کردارەکە شکستی هێنا, چونکە ئەو پەڕگەیە سڕاوەتەوە
+ زیادکردن بۆ
+ هیچ بەشداربوویەک نییە
+ دۆخی پێرتووبی
+ خشتەی کارپێکردن دروستکرا
+ سڕینەوەی بەستەر (ڕەنگە ببێتە هۆی تێکدان)
+ پەڕەی کەناڵەکان
+ گۆڕین بۆ پاشبنەما
+ ∞ ڤیدیۆ
+ چالاککردنی LeakCanary
+ بەکارهێنانی بردنەپێشی ناتەواوی خێرا
+ هەڵەیەک ڕوویدا : %1$s
+ بوخچەی داگرتن بۆ پەڕگەی ڤیدیۆکان هەڵبژێرە
+ دروستکراو لەلایەن %s
+ بەکارهێنەران
+ ناوەڕۆک
+ بهكارهێنانی كارپێكهری ڤیدیۆی دهرهكی
+ چالاککردنی وێنۆچکەی ڤیدیۆی داخستنی ڕوونما
+ دهنگ
+ سهرهكی
+ بەکارهێنانی Tor
+ پیشاندانی کاتی بنچینەیی پێشوو لەسەر بابەتەکان
+ وردەکارییەکان
+ هەمان دۆخ کاراکراوە
+ زمانی بنەڕەتی داوانامە
+ خشتەی کارپێکردنەکان نیشانەکران
+ سیاسەتی تایبەتی داوانامە
+ داگرتن
+ مێژوو بەتاڵە
+ چالاک نەکردنی دۆخی خێرا
+ كردنهوه له وێبگهر
+ ڕاژەکە هیچ داتایەک نانێرێت
+ شوێنی کارپێکراوەکان سڕانەوە.
+ ئاگانامەکانی وەشانی نوێی داوانامە
+ داگرتن تەواوبوو
+ تەنها چەند مۆبایلێک پشتگیری کارپێکردنی ڤیدیۆی 2K/4K دەکەن
+ ئاگانامەکانی داوانامە بۆ پاشبنەما و کارپێکردنەکانی پەنجەرەی بچووک
+ لەهەندێ خزمەتگوزاریدا بەردەستە، هەمیشە خێرایە بەڵام ڕەنگە هەندێ لە بابەتەکان زانیارییەکانیان ناتەواو بێت (وەک نەبوونی ماوە، جۆری بابەت ، نەبوونی پەخش).
+
+ - %d چرکە
+ - %d چرکە
+
+ دەگەڕێنرێتەوە
+ دواتر
+ کردنەوەی ماڵپەڕ
+ ڤیدیۆ کارپێبکرێ کاتێ ئەپەکە لە ئەپێکیتر کرایەوە
+ کارپێکردنی خۆکاری
+ زیاترین هەوڵدانەکان
+ خزمەتگوزاری
+ نوێ & چالاک
+ داگرتنێکی ترت هەیە بەهەمان ناو
+ کیۆسک
+
+ - %s ڤیدیۆ
+ - %s ڤیدیۆکان
+
+ هەوڵدانەوە
+ ڤیدیۆ تەماشاکراوەکان بسڕێنەوە؟
+ لیستی کارپێکردنەکان
+ دۆخێک هەڵبژێرە
+ بردنەپێشەوەی خێرا لەکاتی بێدەنگکردن
+ لێدوانەکەت (بە ئینگلیزی):
+ پیشاندانی بژاردهی كارپێكردنی ڤیدیۆ به Kodi
+ پەڕگەیەک بەهەمان ناو هەیە
+
+ - %d ڕۆژ
+ - %d ڕۆژەکان
+
+ ناولێنان
+ داگرتن
+ باشە
+ خاوێنکردنەوەی پاشماوەی داتا
+ ناتوانرێ ئەم داگرتنە بهێنرێتەوە
+ بەشدارنەبوون
+ گۆڕینی پیشاندان
+ هیچ پەخشێکی ڤیدیۆیی نەدۆزرایەوە
+ هێنانەوە لە
+ گرتە بکە لەسەر ”تەواو” کاتێ کە چارەسەرکرا
+ ئاگانامەی نوێکاری داوانامە
+ ناتوانرێ هەموو وێنۆچکەکان باربکرێن
+ %1$s بینراو
+ هەناردەکردنی پێشووتر
+ پەڕگەی ZIP دروست نییە
+ ناچالاککراوە
+ بەشداریکردن
+ ناتوانرێ شوێنی داگرتن دروستبکرێ \'%1$s\'
+ پشتگوێخستن
+ داگرتنەکان
+ هەرکاتێ بیرۆکەیەکت هەبوو وەک ; وەرگێڕان، گۆڕینی دیزاین ، سڕینەوەی کۆد ،یان هەر گۆڕانکارییەکیتر ئەوا یارمەتییەکەت لەسەرچاوانمانە. ئێمە هەمیشە دەمانەوێ ئەپەکە زیاتر بەرەوپێش ببەین!
+ خشتەی کارپێکردن دانرا
+ سنوردانانی تەمەن
+ ههڵبژاردنی وێبگهر
+ هیچ لێدوانێک نییە
+ ماوەی خێرا بردنە پێشەوە\\ گێڕانەوە بۆ دواوە
+ گەڕانەوە
+ چوونەجێگەی
+ بوخچەی مەبەست ناتوانرێ دروست بکرێ
+ هێڵی ئینتەرنێت نەما
+ مێژوو سڕایەوە
+ شوێنی هەموو کارپێکراوەکان دەسڕێتەوە
+ ئایا دەتەوێ ئەم بابەتە لە مێژووی گەڕان بسڕدرێتەوە؟
+ ئایا دەتەوێ مێژووی داگرتنەکانت بسڕدرێنەوە یان هەموو پەڕگە داگیراوەکان بسڕدرێنەوە؟
+ پیشاندانی زانیاری
+ داگرتنێکیتر هەیە بەهەمان ناو
+ كاركردنی خۆكارانه
+ هیچ بەشدارییەک دیاری نەکراوە
+ ناوی کۆمەڵە بەتاڵە
+ تەواو
+ بەدڵبوون
+ ناتوانرێ مۆڵەت باربکرێ
+ بیرهاتنهوهی كۆتا قهباره و شوێنی پهنجهرهی بچووك
+ دروستکردن
+ ئەوە بزانە ئەم کردارە پێویستی بە هێڵێکی گران هەیە.
+\n
+\nدەتەوێ بەردەوامبیت؟
+ هەمووی
+ داخستن
+ خۆکاری
+ ناتوانرێ وێنە باربکرێ
+ دەنگ
+ پرسیارت لێ دەکرێت بۆ شوێنی داگرتنی هەر پەڕگەیەک
+\nدەتوانیت SAF بەکاربهێنیت گەر دەتەوێ لە بیرگەی دەرەکیدا پەڕگەکان دابگریت
+ هەناردەدەکرێت…
+ وردەکارییەکان:
+ هیچ پەخشێک نییە بۆ داگرتن
+ بارنەکراو : %d
+ بارکردنی ناوەڕۆکی داواکراو
+ ڕێگەپێدان ڕەتکرایەوە لەلایەن سیستەمەوە
+ مێژوو دەسڕێتەوە لەگەڵ ڤیدیۆ کارپێکراوەکان و شوێنی لیستە ڤیدیۆییەکان
+ دانانی خۆکاری
+ ١ بابەت سڕایەوە.
+ کرداری بنەڕەتی لەکاتی کردنەوەی بابەت — %s
+ هەڵبژاردنی کیۆسک
+ کۆنفرانسەکان
+ هیچ داوانامەیەک دانەمەزراوە بۆ کارپێکردنی ئەم پەڕگەیە
+ كردنهوه له پهنجهرهی بچووک
+ سنووری قەبارە لەکاتی بەکارهێنانی داتای مۆبایل
+ ڕیزنەبوون لە پەنجەرەی بچووک
+ داخستنی پلیکانە
+ کەناڵ
+ کۆد
+ سڕینەوەی داتا
+ ڕێکخستنەکان
+ زیندوو
+ سکاڵا لەبوونی کێشە
+ هیچ کەناڵێکی بەشداری نییە
+ ڕاوەستا لەسەر کێشەی هێڵ
+ ناتوانیت گۆڕانكاری لهم بهشدارییهدا بكهیت
+ وردی بنەڕەتی
+ بچووککردنەوە بۆ پەنجەرەی بچووک
+ جوڵەی پەنجەت لەسەر ڕوونما بەکاربهێنە بۆ گۆڕینی ئاستی ڕووناکی ڕوونما
+ گۆرانییەکان
+ داگرتنی پەڕگەی پەخش
+ بەستەر هەڵەیە یاخوود بەئینتەرنێتەوە پەیوەست نەبوویت
+ شێوازی پیشاندانی خشتە
+ زیادکردنی دۆخ
+ پەسەند کردن
+ بەمزووانە شتێک لێرەدا دەردەکەوێ :D
+ تەماشاکراوە
+ ئەم خشتەی کارپێکردنە بسڕدرێتەوە؟
+ ڕیزکرا لە کارپێکردن لە پەنجەرەی بچووک
+ بێدەنگکردن
+ سەردانی ماڵپەڕی داوانامەکەمان بکە بۆ زانیاری و هەواڵی نوێ.
+ ئایا دڵنیای لە سڕینەوەی هەموو بابەتەکان لە مێژوودا؟
+ ئایا دەتەوێ ئەم بابەتە لە مێژووی تەماشاکردن بسڕدرێتەوە؟
+ داونامایەکی خۆڕایی و کێشی کەم بۆ پەخشی ڕاستەوخۆ لەسەر ئەندرۆید.
+ ناوەڕۆک بوونی نییە
+ ئەو دۆخانە بدۆزەرەوە کە لەگەڵ خۆتدا دەگونجێن لە %s
+ پەڕگە
+ نەدۆزرایەوە
+ گەڕا
+ چارەسەردەکرێت
+ جوڵەی پەنجەت لەسەر ڕوونما بەکاربهێنە بۆ گۆڕینی ئاستی دەنگ
+ ناوەڕۆکی پەڕەی سەرەکی
+ دیاریکردنی بەشدارییەکان
+ هێنانەوەی پەڕگە
+ بوخچەی داگرتنی دهنگ
+ ههنێ له قهبارهكان دهنگیان تێدا نابێت
+ ڕووداوەکان
+ وێنۆچکەی کەسی بەرزکەرەوە
+ گرتەبکە بۆ داگرتن
+ هێنرایەوە
+ داگرتنەکان
+ کۆنترۆڵی خێرایی کارپێکەر
+ ئایا دەتەوێ ئەم کۆمەڵەیە بسڕیتەوە؟
+ پەنجەت داگرە بۆ ڕیزنەبوون
+ زۆرترین ژمارەی هەوڵدان پێش پاشگەزبوونەوە لە داگرتنەکە
+ هەڵەیەک ڕوویدا
+ هەمووی بسڕەوە
+ بەڵێ، لەگەڵ ڤیدیۆ تەماشاکراوەکانەوە
+ دەستپێکردنی لێدان لە پەنجەرەی بچووکەوە
+ بەدڵنەبوون
+ مێژوو
+ تاقیکردنەوەی هێڵێک
+ دەسەڵاتی پێبدە بۆ پیشاندان لەسەرووی داوانامەکانی ترەوە
+ مێژووی تەماشاکردن سڕایەوە.
+ هەنگاو
+ تکایە چاوەڕێبکە…
+ خوێندنەوەی سیاسەتی تایبەتی
+ سڕینەوەی پەڕگە داگیراوەکان
+ ببوورە، ناتوانرێ ئەوە ڕووبدات.
+ پڕ بە ڕونما
+ کارپێکەری پەنجەرەی بچووک
+ ڕێکخستنەوە
+ هەناردەکردنی بنکەی زانیارییەکان
+ تاریک
+ تێکەڵکردن لە دیاری تەرخانکراوەوە کاتێ بەردەست بوو
+ داگرتن شکستی هێنا
+ ناتوانرێ بە ڕاژەوە پەیوەست بیت
+ دەستپێکردنی لێدان لێرەوە
+ کارپێکردنی ڤیدیۆ، ماوەی:
+ هەزار
+ زۆرترین بەدڵبوون
+ سڕینەوە
+ جۆری بنەڕەتی ڤیدیۆ
+ هیچ شتێک لێرەدا نییە
+ سڕینەوە
+ هەمان فۆڵدەر بوونی نییە
+ دڵخوازکردنی لیستی کارپێکردن
+ ناوی پەڕگە ناکرێ بەتاڵ بێت
+ ناتوانرێ واژووی بەستەری ڤیدیۆ بخوێنرێتەوە
+ پیشاندانی ئاگانامەیەک بۆ ئامادەبوونی داوانامە لەکاتی بەردەست بوونی وەشانی نوێ
+ بەشداربووەکان
+ مێژووی تەماشاکردن
+ سکاڵا لەسەر کێشەکان
+ خۆکاری دانرا (هیچ بەرزکەرەوەیەک نەدۆزرایەوە)
+ دەستپێکردنی لێدان لە پاشبنەماوە
+ ناوی پەڕگە
+ دانان لەسەر وێنۆچکەی خشتەی کارپێکردن
+ دەربارەی داوانامە
+ زیادکردن بۆ خشتەی کارپێکردن
+ (نەزانراو)
+ زمانی داوانامە
+ پەڕەی کیۆسک
+ دەستپێکردنەوە کارپێکردن
+ شوێنی پەڕگە گۆڕدراوە یان سڕاوەتەوە
+ هێما نادروستەکان بەم بەهایە جێگۆڕکێ دەکرێن
+ نوێکردنەوە
+ گهڕان
+ گێڕانەوە بۆ بنەڕەتی
+ کۆنتڕۆڵی کارپێکەر بەجوڵەی پەنجە
+ reCAPTCHA داواکراوە
+ گۆڕین بۆ پەنجەرەی بچووک
+ کرداری ’کردنەوە’ی پێشنیارکراو
+
+ - %d خولەک
+ - %d خولەك
+
+ ئامادەکردن…
+ ئەلبوومەکان
+ هیچ ڤیدیۆیەک نییە
+ لەلایەن %s
+ دانانی ئاڕاستە
+ چ پەڕەیەک نیشانبدرێ لە پەڕەی سەرەکی
+ ڕاپۆرت
+ هەمیشە بپرسە
+ هێشتنەوەی تراکی ڤیدیۆ کارپێکراوەکان
+ بهكارهێنانی كارپێكهری دهنگی دهرهكی
+ بینین لە GitHub
+ زانیاری:
+ هیچ کارپێکەرێکی ڤیدیۆیی نەدۆزرایەوە (دەتوانی کارپێکەری VLC دامەزرێنی) .
+ نەگێڕانەوەی کارپێکەر بۆ پێش کێشە ڕوویدا
+ دەهێنرێتەوە…
+ هاوبهشپێكردن لهگهڵ
+ خزمەتگوزاری چەسپاو، ئێستا هەڵبژێردراو:
+ تەواوی گەڕانەکانت بسڕدرێنەوە؟
+ بەردەوام بوونی ڤیدیۆ لەدوای هەبوونی هەر بڕینێک (وەک پەیوەندی تەلەفۆنی)
+ گرته له ”گهڕان” بكه بۆ دهستپێكردن
+ کۆگای گەڕانی نێوخۆیی
+ ڤیدیۆ & دەنگ
+ هاوبهشپێكردن
+ بوخچەی داگرتن بگۆڕە بۆ ئەنجامدانی کاریگەری
+ پیشاندانی ڕێنمایی کاتێ لە پاشبنەما یاخوود پەنجەرەی بچووکدا گرتە دەکرێ لەسەر” وردەکاری:” ڤیدیۆیەک
+ %1$ لە داگرتنەکان سڕانەوە
+ پەخشی هیچ دەنگێک نەدۆزرایەوە
+ هونەرمەندەکان
+ هیچ کەسێک تەماشای ناکات
+ دەربارە
+ تکایە بوخچەیەک بۆ شوێنی داگرتن دیاریبکە لە ڕێکخستنەکان
+ بارکردن…
+ بەهۆی ExoPlayer ەوە ماوەی بردنەپێش و بردنەپاش لە %d چرکەدا دیاریکرا
+ سڕینەوەی شوێنی کارپێکراوەکان
+ لەم ماوەیەدا بڵاوکرابێتەوە
+ ناولێنان
+ کارپێکەری پاشبنەما
+ پەڕگە ناتوانرێ دروستبکرێ
+ پێدانەوە
+ خشتەی کارپێکردنی نوێ
+
+ - %d کاتژمێر
+ - %d کات ژمێر
+
+ بڵاوكراوهتهوه له %1$s
+ مێژوو ناچالاکە
+ کاشی وێنەكان سڕایهوه
+ هێمای جێگۆڕین
+ ڕێكخستنهكان
+ تەواوبوو
+ بۆ هێنانەوەی بەشداربوونەکانی یوتوب پێویستە پەڕگەی خەزن بوو بگەڕێنیتەوە:
+\n
+\n1. ئەم بەستەرە بکەوە: %1$
+\n2. بچۆرەژوورەوە گەر داوای کرد
+\n3. داگرتنێک دەست پێدەکات (ئەمە پەڕگەی خەزنکراوە)
+ بنەڕەتی سیستەم
+ کێشە لە هێڵەکەتدا هەیە
+
+ - %s تەماشا دەکات
+ - %s تەماشا دەکەن
+
+ هەڵە لە ڕاپۆرتکردن
+ سڕینەوەی پاشماوەی هەموو داتاکان
+ داوانامەکە نهدۆزرایهوه. دابمهزرێت؟
+ ناتوانرێ پشتگیری دۆخەکە بکرێ
+ ڕیزکرا لە کارپێکردن لە پاشبنەما
+ دامەزراندن
+ شوێنی داگرتن دانرا \'%1$s\'
+ ڤیدیۆکان
+ بەستەرەکە پشتگیری نەکراوە
+ شەپۆلی دەنگ
+ کرداری کاتی گۆڕین بۆ داوانامەیەکی تر لە کارپێکەری ڤیدیۆییەوە — %s
+ لەبەرگیرایەوە
+ چوارچێوەی گەیشتن بە بیرگە ڕێگەدەدات بە داگرتنی پەڕگەکان لە بیرگەی دەرەکیدا.
+\nتێبینی: هەندێ لە مۆبایلەکان پشتگیری ناکرێن
+ گونجاو بە ڕونما
+ چاودێری دزەکردنی بیرگە ڕەنگە ببێتە هۆی وەڵامنەدانەوەی لەکاتی گەرمبوون
+ بێدەنگ نەکردن
+ ناوخۆ
+ پڕبینەرەکان
+ تەنها یەک داگرتن کاردەکات لەیەک کاتدا
+ ئایا دەتەوێ بگەڕێنرێتەوە بۆ شێوازی بنەڕەتی؟
+ دەسەڵاتی گەیشتن بە بیرگە نەدرا
+ ڕاگرتنی داگرتنەکان
+ دەربارە
+ خولانەوە
+ ناتوانی ئەم خشتەی کارپێکردنە بسڕیتەوە.
+ پیشاندانی لێدوانەکان
+ بۆ جێبەجێکردنی فرمانەکان لەگەڵ یاسای پاراستنی داتای گشتی ئەوروپیدا (GDPR) , ئێمە سەرنجت ڕادەکێشین بۆ سیاسەتە تایبەتییەکانی ئەپەکەمان. تکایە بەئاگادارییەوە بیخوێنەوە.
+\nپێویستە قبوڵی بکەیت بۆ ناردنی سکاڵاکانت.
+ ئەم ناوەڕۆکە پشتگیری نەکراوە لەلایەن ئەپەکەمانەوە.
+\n
+\nهیوادارین بتوانین لە وەشانەکانی داهاتوودا پشتگیری بکەین.
+ بەخشین
+ هیچ کەسێ گوێی لێ ناگرێ
+ مێژووی گەڕانەکانت سڕانەوە.
+ ڤیدیۆ
+ ئەپ داخرا لەکاتی کارکردن لەسەر ئەو پەڕگەیە
+ کارپێکەری ڤیدیۆیی
+ ناچالاککردن بۆ پیشان نەدانی لێدوانەکان
+ ناتوانرێ ماڵپەڕ بەتەواوی شیبکرێتەوە
+ بەکارهێنانی SAF
+ کاتی پاش دواین نوێکردنەوە دوای بەشداربوون ڕەچاوکراوە — %s
+ بیرگەی دەرەکی بەردەست نییە
+ گێڕانەوەی لیست بۆ شوێنی پێشووتر
+ پاشگهزبوونهوه
+ تراکەکان
+ ڕێکخستنەکانی دەنگ
+ پیشاندانی ئەو ڤیدیۆیانەی سنوری تەمەنیان بۆ دانراوە. لە ڕێکخستنەکانەوە ڕێگەی پێدەدرێت.
+ پرسیارت لێ دەکرێت بۆ شوێنی داگرتنی هەر پەڕگەیەک
+ دواین کارپێکراو
+ ناتوانرێ لیستی داگرتن دابنرێ
+ هێنانەوە/هاوردەکردن
+ کارپێکەری پاشبنەما
+ وەشانی نوێی داوانامە بەردەستە!
+ وێنۆچکەی خشتەی کارپێکردن گۆڕدرا.
+ هێنانەوەی پەڕەی کەسی SoundCloud بەدانانی بەستەر یاخوود ئایدی:
+\n
+\n1. دۆخی ”Desktop mode” لە وێبگەرەکەتدا چالاک بکە (ئەم ماڵپەڕە بۆ وێبگەری مۆبایلەکان بەردەست نییە)
+\n2. ئەم بەستەرە بکەرەوە : %1$s
+\n3. بچۆرە ژوورەوە گەر داواکرا
+\n4. بەستەری پەڕەی کەسییەکەت دابنێ.
+ پیت و ژمارەکان
+ وڵاتی بنەڕەتی
+ نوێکارییەکان
+
\ No newline at end of file
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 066/202] 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: Sat, 27 Jun 2020 16:11:25 +0200
Subject: [PATCH 067/202] Fix queue channel name color for some devices
---
app/src/main/res/layout/activity_player_queue_control.xml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
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 628c8f56c..c5b8e5743 100644
--- a/app/src/main/res/layout/activity_player_queue_control.xml
+++ b/app/src/main/res/layout/activity_player_queue_control.xml
@@ -55,7 +55,6 @@
From c24dfc63dce8caa161d76b7cb5743a082b33caa0 Mon Sep 17 00:00:00 2001
From: Mauricio Colli
Date: Sun, 5 Apr 2020 16:25:44 -0300
Subject: [PATCH 068/202] Add search for subscription picker in the feed group
dialog
---
.../database/subscription/SubscriptionDAO.kt | 7 +
.../local/subscription/SubscriptionManager.kt | 2 +
.../subscription/dialog/FeedGroupDialog.kt | 336 ++++++++++++------
.../dialog/FeedGroupDialogViewModel.kt | 77 ++--
.../subscription/item/EmptyPlaceholderItem.kt | 1 +
.../item/PickerSubscriptionItem.kt | 37 +-
.../res/layout/dialog_feed_group_create.xml | 68 ++--
.../main/res/layout/toolbar_search_layout.xml | 10 +-
.../main/res/menu/menu_feed_group_dialog.xml | 10 +
9 files changed, 363 insertions(+), 185 deletions(-)
create mode 100644 app/src/main/res/menu/menu_feed_group_dialog.xml
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
index 573fa4b90..aa34a2867 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
@@ -20,6 +20,13 @@ abstract class SubscriptionDAO : BasicDAO {
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
abstract override fun getAll(): Flowable>
+ @Query("""
+ SELECT * FROM subscriptions
+ WHERE name LIKE '%' || :filter || '%'
+ ORDER BY name COLLATE NOCASE ASC
+ """)
+ abstract fun filterByName(filter: String): Flowable>
+
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable>
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
index 92ab8cb0c..ce272c856 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
@@ -21,6 +21,8 @@ class SubscriptionManager(context: Context) {
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
fun subscriptions() = subscriptionTable.all
+ fun filterByName(filter: String) = subscriptionTable.filterByName(filter)
+
fun upsertAll(infoList: List): List {
val listEntities = subscriptionTable.upsertAll(
infoList.map { SubscriptionEntity.from(it) })
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 e9d9ac5b3..0b77ec1d8 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
@@ -5,6 +5,7 @@ import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.text.Editable
+import android.text.TextUtils
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
@@ -13,34 +14,22 @@ import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
+import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.xwray.groupie.GroupAdapter
+import com.xwray.groupie.OnItemClickListener
import com.xwray.groupie.Section
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.Icepick
import icepick.State
import java.io.Serializable
-import kotlinx.android.synthetic.main.dialog_feed_group_create.cancel_button
-import kotlinx.android.synthetic.main.dialog_feed_group_create.confirm_button
-import kotlinx.android.synthetic.main.dialog_feed_group_create.delete_button
-import kotlinx.android.synthetic.main.dialog_feed_group_create.delete_screen_message
-import kotlinx.android.synthetic.main.dialog_feed_group_create.group_name_input
-import kotlinx.android.synthetic.main.dialog_feed_group_create.group_name_input_container
-import kotlinx.android.synthetic.main.dialog_feed_group_create.icon_preview
-import kotlinx.android.synthetic.main.dialog_feed_group_create.icon_selector
-import kotlinx.android.synthetic.main.dialog_feed_group_create.options_root
-import kotlinx.android.synthetic.main.dialog_feed_group_create.select_channel_button
-import kotlinx.android.synthetic.main.dialog_feed_group_create.selected_subscription_count_view
-import kotlinx.android.synthetic.main.dialog_feed_group_create.separator
-import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector
-import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector_header_info
-import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector_list
+import kotlin.collections.contains
+import kotlinx.android.synthetic.main.dialog_feed_group_create.*
+import kotlinx.android.synthetic.main.toolbar_search_layout.*
import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
-import org.schabi.newpipe.database.subscription.SubscriptionEntity
+import org.schabi.newpipe.fragments.BackPressable
import org.schabi.newpipe.local.subscription.FeedGroupIcon
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.DeleteScreen
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.IconPickerScreen
@@ -51,9 +40,10 @@ 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.ThemeHelper
-class FeedGroupDialog : DialogFragment() {
+class FeedGroupDialog : DialogFragment(), BackPressable {
private lateinit var viewModel: FeedGroupDialogViewModel
private var groupId: Long = NO_GROUP_SELECTED
private var groupIcon: FeedGroupIcon? = null
@@ -66,22 +56,19 @@ class FeedGroupDialog : DialogFragment() {
object DeleteScreen : ScreenState()
}
- @State
- @JvmField
- var selectedIcon: FeedGroupIcon? = null
- @State
- @JvmField
- var selectedSubscriptions: HashSet = HashSet()
- @State
- @JvmField
- var currentScreen: ScreenState = InitialScreen
+ @State @JvmField var selectedIcon: FeedGroupIcon? = null
+ @State @JvmField var selectedSubscriptions: HashSet = HashSet()
+ @State @JvmField var wasSubscriptionSelectionChanged: Boolean = false
+ @State @JvmField var currentScreen: ScreenState = InitialScreen
- @State
- @JvmField
- var subscriptionsListState: Parcelable? = null
- @State
- @JvmField
- var iconsListState: Parcelable? = null
+ @State @JvmField var subscriptionsListState: Parcelable? = null
+ @State @JvmField var iconsListState: Parcelable? = null
+ @State @JvmField var wasSearchSubscriptionsVisible = false
+ @State @JvmField var subscriptionsCurrentSearchQuery = ""
+
+ private val subscriptionMainSection = Section()
+ private val subscriptionEmptyFooter = Section()
+ private lateinit var subscriptionGroupAdapter: GroupAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -91,22 +78,30 @@ class FeedGroupDialog : DialogFragment() {
groupId = arguments?.getLong(KEY_GROUP_ID, NO_GROUP_SELECTED) ?: NO_GROUP_SELECTED
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
return inflater.inflate(R.layout.dialog_feed_group_create, container)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireActivity(), theme) {
override fun onBackPressed() {
- if (currentScreen !is InitialScreen) {
- showScreen(InitialScreen)
- } else {
+ if (!this@FeedGroupDialog.onBackPressed()) {
super.onBackPressed()
}
}
}
}
+ override fun onPause() {
+ super.onPause()
+
+ wasSearchSubscriptionsVisible = isSearchVisible()
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
@@ -119,11 +114,15 @@ class FeedGroupDialog : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewModel = ViewModelProviders.of(this, FeedGroupDialogViewModel.Factory(requireContext(), groupId))
- .get(FeedGroupDialogViewModel::class.java)
+ viewModel = ViewModelProvider(this,
+ FeedGroupDialogViewModel.Factory(requireContext(),
+ groupId, subscriptionsCurrentSearchQuery)
+ ).get(FeedGroupDialogViewModel::class.java)
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
- viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer { setupSubscriptionPicker(it.first, it.second) })
+ viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer {
+ setupSubscriptionPicker(it.first, it.second)
+ })
viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
when (it) {
ProcessingEvent -> disableInput()
@@ -131,15 +130,54 @@ class FeedGroupDialog : DialogFragment() {
}
})
+ subscriptionGroupAdapter = GroupAdapter().apply {
+ add(subscriptionMainSection)
+ add(subscriptionEmptyFooter)
+ spanCount = 4
+ }
+ subscriptions_selector_list.apply {
+ // Disable animations, too distracting.
+ itemAnimator = null
+ adapter = subscriptionGroupAdapter
+ layoutManager = GridLayoutManager(requireContext(), subscriptionGroupAdapter.spanCount,
+ RecyclerView.VERTICAL, false).apply {
+ spanSizeLookup = subscriptionGroupAdapter.spanSizeLookup
+ }
+ }
+
setupIconPicker()
setupListeners()
showScreen(currentScreen)
+
+ if (currentScreen == SubscriptionsPickerScreen && wasSearchSubscriptionsVisible) {
+ showSearch()
+ } else if (currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED) {
+ showKeyboard()
+ }
}
- // /////////////////////////////////////////////////////////////////////////
+ override fun onDestroyView() {
+ super.onDestroyView()
+ subscriptions_selector_list?.adapter = null
+ icon_selector?.adapter = null
+ }
+
+ /*///////////////////////////////////////////////////////////////////////////
// Setup
- // /////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////// */
+
+ override fun onBackPressed(): Boolean {
+ if (currentScreen is SubscriptionsPickerScreen && isSearchVisible()) {
+ hideSearch()
+ return true
+ } else if (currentScreen !is InitialScreen) {
+ showScreen(InitialScreen)
+ return true
+ }
+
+ return false
+ }
private fun setupListeners() {
delete_button.setOnClickListener { showScreen(DeleteScreen) }
@@ -163,13 +201,54 @@ class FeedGroupDialog : DialogFragment() {
}
})
- confirm_button.setOnClickListener {
- when (currentScreen) {
- InitialScreen -> handlePositiveButtonInitialScreen()
- DeleteScreen -> viewModel.deleteGroup()
- else -> showScreen(InitialScreen)
+ confirm_button.setOnClickListener { handlePositiveButton() }
+
+ select_channel_button.setOnClickListener {
+ subscriptions_selector_list.scrollToPosition(0)
+ showScreen(SubscriptionsPickerScreen)
+ }
+
+ val headerMenu = subscriptions_header_toolbar.menu
+ requireActivity().menuInflater.inflate(R.menu.menu_feed_group_dialog, headerMenu)
+
+ headerMenu.findItem(R.id.action_search).setOnMenuItemClickListener {
+ showSearch()
+ true
+ }
+
+ toolbar_search_clear.setOnClickListener {
+ if (TextUtils.isEmpty(toolbar_search_edit_text.text)) {
+ hideSearch()
+ return@setOnClickListener
+ }
+ resetSearch()
+ showKeyboardSearch()
+ }
+
+ toolbar_search_edit_text.setOnClickListener {
+ if (AndroidTvUtils.isTv(context)) {
+ showKeyboardSearch()
}
}
+
+ toolbar_search_edit_text.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
+ override fun afterTextChanged(s: Editable) = Unit
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+ val newQuery: String = toolbar_search_edit_text.text.toString()
+ subscriptionsCurrentSearchQuery = newQuery
+ viewModel.filterSubscriptionsBy(newQuery)
+ }
+ })
+
+ subscriptionGroupAdapter?.setOnItemClickListener(subscriptionPickerItemListener)
+ }
+
+ private fun handlePositiveButton() = when {
+ currentScreen is InitialScreen -> handlePositiveButtonInitialScreen()
+ currentScreen is DeleteScreen -> viewModel.deleteGroup()
+ currentScreen is SubscriptionsPickerScreen && isSearchVisible() -> hideSearch()
+ else -> showScreen(InitialScreen)
}
private fun handlePositiveButtonInitialScreen() {
@@ -202,80 +281,73 @@ class FeedGroupDialog : DialogFragment() {
groupIcon = feedGroupEntity?.icon
groupSortOrder = feedGroupEntity?.sortOrder ?: -1
- icon_preview.setImageResource((if (selectedIcon == null) icon else selectedIcon!!).getDrawableRes(requireContext()))
+ val feedGroupIcon = if (selectedIcon == null) icon else selectedIcon!!
+ icon_preview.setImageResource(feedGroupIcon.getDrawableRes(requireContext()))
if (group_name_input.text.isNullOrBlank()) {
group_name_input.setText(name)
}
}
- private fun setupSubscriptionPicker(subscriptions: List, selectedSubscriptions: Set) {
- this.selectedSubscriptions.addAll(selectedSubscriptions)
- val useGridLayout = subscriptions.isNotEmpty()
+ private val subscriptionPickerItemListener = OnItemClickListener { item, view ->
+ if (item is PickerSubscriptionItem) {
+ val subscriptionId = item.subscriptionEntity.uid
+ wasSubscriptionSelectionChanged = true
- val groupAdapter = GroupAdapter()
- groupAdapter.spanCount = if (useGridLayout) 4 else 1
-
- val subscriptionsCount = this.selectedSubscriptions.size
- val selectedCountText = resources.getQuantityString(R.plurals.feed_group_dialog_selection_count, subscriptionsCount, subscriptionsCount)
- selected_subscription_count_view.text = selectedCountText
- subscriptions_selector_header_info.text = selectedCountText
-
- Section().apply {
- addAll(subscriptions.map {
- val isSelected = this@FeedGroupDialog.selectedSubscriptions.contains(it.uid)
- PickerSubscriptionItem(it, isSelected)
- })
- setPlaceholder(EmptyPlaceholderItem())
-
- groupAdapter.add(this)
- }
-
- subscriptions_selector_list.apply {
- layoutManager = if (useGridLayout) {
- GridLayoutManager(requireContext(), groupAdapter.spanCount, RecyclerView.VERTICAL, false)
+ val isSelected = if (this.selectedSubscriptions.contains(subscriptionId)) {
+ this.selectedSubscriptions.remove(subscriptionId)
+ false
} else {
- LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
+ this.selectedSubscriptions.add(subscriptionId)
+ true
}
- adapter = groupAdapter
+ item.updateSelected(view, isSelected)
+ updateSubscriptionSelectedCount()
+ }
+ }
- if (subscriptionsListState != null) {
- layoutManager?.onRestoreInstanceState(subscriptionsListState)
- subscriptionsListState = null
- }
+ private fun setupSubscriptionPicker(
+ subscriptions: List,
+ selectedSubscriptions: Set
+ ) {
+ if (!wasSubscriptionSelectionChanged) {
+ this.selectedSubscriptions.addAll(selectedSubscriptions)
}
- groupAdapter.setOnItemClickListener { item, _ ->
- when (item) {
- is PickerSubscriptionItem -> {
- val subscriptionId = item.subscriptionEntity.uid
+ updateSubscriptionSelectedCount()
- val isSelected = if (this.selectedSubscriptions.contains(subscriptionId)) {
- this.selectedSubscriptions.remove(subscriptionId)
- false
- } else {
- this.selectedSubscriptions.add(subscriptionId)
- true
- }
-
- item.isSelected = isSelected
- item.notifyChanged(PickerSubscriptionItem.UPDATE_SELECTED)
-
- val subscriptionsCount = this.selectedSubscriptions.size
- val updateSelectedCountText = resources.getQuantityString(R.plurals.feed_group_dialog_selection_count, subscriptionsCount, subscriptionsCount)
- selected_subscription_count_view.text = updateSelectedCountText
- subscriptions_selector_header_info.text = updateSelectedCountText
- }
- }
+ if (subscriptions.isEmpty()) {
+ subscriptionEmptyFooter.clear()
+ subscriptionEmptyFooter.add(EmptyPlaceholderItem())
+ } else {
+ subscriptionEmptyFooter.clear()
}
- select_channel_button.setOnClickListener {
+ subscriptions.forEach {
+ it.isSelected = this@FeedGroupDialog.selectedSubscriptions
+ .contains(it.subscriptionEntity.uid)
+ }
+
+ subscriptionMainSection.update(subscriptions, false)
+
+ if (subscriptionsListState != null) {
+ subscriptions_selector_list.layoutManager?.onRestoreInstanceState(subscriptionsListState)
+ subscriptionsListState = null
+ } else {
subscriptions_selector_list.scrollToPosition(0)
- showScreen(SubscriptionsPickerScreen)
}
}
+ private fun updateSubscriptionSelectedCount() {
+ val selectedCount = this.selectedSubscriptions.size
+ val selectedCountText = resources.getQuantityString(
+ R.plurals.feed_group_dialog_selection_count,
+ selectedCount, selectedCount)
+ selected_subscription_count_view.text = selectedCountText
+ subscriptions_header_info.text = selectedCountText
+ }
+
private fun setupIconPicker() {
val groupAdapter = GroupAdapter()
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(requireContext(), it) })
@@ -311,9 +383,9 @@ class FeedGroupDialog : DialogFragment() {
}
}
- // /////////////////////////////////////////////////////////////////////////
+ /*///////////////////////////////////////////////////////////////////////////
// Screen Selector
- // /////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////// */
private fun showScreen(screen: ScreenState) {
currentScreen = screen
@@ -337,7 +409,8 @@ class FeedGroupDialog : DialogFragment() {
else -> View.VISIBLE
}
- if (currentScreen != InitialScreen) hideKeyboard()
+ hideKeyboard()
+ hideSearch()
}
private fun View.onlyVisibleIn(vararg screens: ScreenState) {
@@ -347,13 +420,58 @@ class FeedGroupDialog : DialogFragment() {
}
}
- // /////////////////////////////////////////////////////////////////////////
+ /*///////////////////////////////////////////////////////////////////////////
// Utils
- // /////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////// */
+
+ private fun isSearchVisible() = subscriptions_header_search_container?.visibility == View.VISIBLE
+
+ private fun resetSearch() {
+ toolbar_search_edit_text.setText("")
+ subscriptionsCurrentSearchQuery = ""
+ viewModel.clearSubscriptionsFilter()
+ }
+
+ private fun hideSearch() {
+ resetSearch()
+ subscriptions_header_search_container.visibility = View.GONE
+ subscriptions_header_info_container.visibility = View.VISIBLE
+ subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = true
+ hideKeyboardSearch()
+ }
+
+ private fun showSearch() {
+ subscriptions_header_search_container.visibility = View.VISIBLE
+ subscriptions_header_info_container.visibility = View.GONE
+ subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = false
+ showKeyboardSearch()
+ }
+
+ private val inputMethodManager by lazy {
+ requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ }
+
+ private fun showKeyboardSearch() {
+ if (toolbar_search_edit_text.requestFocus()) {
+ inputMethodManager.showSoftInput(toolbar_search_edit_text, InputMethodManager.SHOW_IMPLICIT)
+ }
+ }
+
+ private fun hideKeyboardSearch() {
+ inputMethodManager.hideSoftInputFromWindow(toolbar_search_edit_text.windowToken,
+ InputMethodManager.RESULT_UNCHANGED_SHOWN)
+ toolbar_search_edit_text.clearFocus()
+ }
+
+ private fun showKeyboard() {
+ if (group_name_input.requestFocus()) {
+ inputMethodManager.showSoftInput(group_name_input, InputMethodManager.SHOW_IMPLICIT)
+ }
+ }
private fun hideKeyboard() {
- val inputMethodManager = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
+ inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken,
+ InputMethodManager.RESULT_UNCHANGED_SHOWN)
group_name_input.clearFocus()
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
index ac00245e6..a7ab300f2 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
@@ -9,42 +9,55 @@ import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.disposables.Disposable
import io.reactivex.functions.BiFunction
+import io.reactivex.processors.BehaviorProcessor
import io.reactivex.schedulers.Schedulers
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
-import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.local.feed.FeedDatabaseManager
import org.schabi.newpipe.local.subscription.FeedGroupIcon
import org.schabi.newpipe.local.subscription.SubscriptionManager
+import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
-class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModel() {
- class Factory(val context: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModelProvider.Factory {
- @Suppress("UNCHECKED_CAST")
- override fun create(modelClass: Class): T {
- return FeedGroupDialogViewModel(context.applicationContext, groupId) as T
- }
- }
+class FeedGroupDialogViewModel(
+ applicationContext: Context,
+ private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
+ initialQuery: String = ""
+) : ViewModel() {
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
private var subscriptionManager = SubscriptionManager(applicationContext)
+ private var filterSubscriptions = BehaviorProcessor.create()
+ private var allSubscriptions = subscriptionManager.subscriptions()
+
+ private var subscriptionsFlowable = filterSubscriptions
+ .startWith(initialQuery)
+ .distinctUntilChanged()
+ .switchMap { query ->
+ if (query.isEmpty()) {
+ allSubscriptions
+ } else {
+ subscriptionManager.filterByName(query)
+ }
+ }.map { list -> list.map { PickerSubscriptionItem(it) } }
+
private val mutableGroupLiveData = MutableLiveData()
- private val mutableSubscriptionsLiveData = MutableLiveData, Set>>()
+ private val mutableSubscriptionsLiveData = MutableLiveData, Set>>()
private val mutableDialogEventLiveData = MutableLiveData()
val groupLiveData: LiveData = mutableGroupLiveData
- val subscriptionsLiveData: LiveData, Set>> = mutableSubscriptionsLiveData
+ val subscriptionsLiveData: LiveData, Set>> = mutableSubscriptionsLiveData
val dialogEventLiveData: LiveData = mutableDialogEventLiveData
private var actionProcessingDisposable: Disposable? = null
private var feedGroupDisposable = feedDatabaseManager.getGroup(groupId)
- .subscribeOn(Schedulers.io())
- .subscribe(mutableGroupLiveData::postValue)
+ .subscribeOn(Schedulers.io())
+ .subscribe(mutableGroupLiveData::postValue)
private var subscriptionsDisposable = Flowable
- .combineLatest(subscriptionManager.subscriptions(), feedDatabaseManager.subscriptionIdsForGroup(groupId),
- BiFunction { t1: List, t2: List -> t1 to t2.toSet() })
- .subscribeOn(Schedulers.io())
- .subscribe(mutableSubscriptionsLiveData::postValue)
+ .combineLatest(subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
+ BiFunction { t1: List, t2: List -> t1 to t2.toSet() })
+ .subscribeOn(Schedulers.io())
+ .subscribe(mutableSubscriptionsLiveData::postValue)
override fun onCleared() {
super.onCleared()
@@ -55,14 +68,14 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
fun createGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set) {
doAction(feedDatabaseManager.createGroup(name, selectedIcon)
- .flatMapCompletable {
- feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
- })
+ .flatMapCompletable {
+ feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
+ })
}
fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set, sortOrder: Long) {
doAction(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
- .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
+ .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
}
fun deleteGroup() {
@@ -74,13 +87,33 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
mutableDialogEventLiveData.value = DialogEvent.ProcessingEvent
actionProcessingDisposable = completable
- .subscribeOn(Schedulers.io())
- .subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
+ .subscribeOn(Schedulers.io())
+ .subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
}
}
+ fun filterSubscriptionsBy(query: String) {
+ filterSubscriptions.onNext(query)
+ }
+
+ fun clearSubscriptionsFilter() {
+ filterSubscriptions.onNext("")
+ }
+
sealed class DialogEvent {
object ProcessingEvent : DialogEvent()
object SuccessEvent : DialogEvent()
}
+
+ class Factory(
+ private val context: Context,
+ private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
+ private val initialQuery: String = ""
+ ) : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun create(modelClass: Class): T {
+ return FeedGroupDialogViewModel(context.applicationContext,
+ groupId, initialQuery) as T
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/EmptyPlaceholderItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/EmptyPlaceholderItem.kt
index c806277ee..ef7eb93cd 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/EmptyPlaceholderItem.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/EmptyPlaceholderItem.kt
@@ -7,4 +7,5 @@ import org.schabi.newpipe.R
class EmptyPlaceholderItem : Item() {
override fun getLayout(): Int = R.layout.list_empty_view
override fun bind(viewHolder: GroupieViewHolder, position: Int) {}
+ override fun getSpanSize(spanCount: Int, position: Int): Int = spanCount
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt
index d90ac0d82..7d33da71f 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt
@@ -1,39 +1,28 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
-import com.nostra13.universalimageloader.core.DisplayImageOptions
import com.nostra13.universalimageloader.core.ImageLoader
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
-import kotlinx.android.synthetic.main.picker_subscription_item.selected_highlight
-import kotlinx.android.synthetic.main.picker_subscription_item.thumbnail_view
-import kotlinx.android.synthetic.main.picker_subscription_item.title_view
+import kotlinx.android.synthetic.main.picker_subscription_item.*
+import kotlinx.android.synthetic.main.picker_subscription_item.view.*
import org.schabi.newpipe.R
import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.util.AnimationUtils
import org.schabi.newpipe.util.AnimationUtils.animateView
import org.schabi.newpipe.util.ImageDisplayConstants
-data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, var isSelected: Boolean = false) : Item() {
- companion object {
- const val UPDATE_SELECTED = 123
-
- val IMAGE_LOADING_OPTIONS: DisplayImageOptions = ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS
- }
-
+data class PickerSubscriptionItem(
+ val subscriptionEntity: SubscriptionEntity,
+ var isSelected: Boolean = false
+) : Item() {
+ override fun getId(): Long = subscriptionEntity.uid
override fun getLayout(): Int = R.layout.picker_subscription_item
-
- override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList) {
- if (payloads.contains(UPDATE_SELECTED)) {
- animateView(viewHolder.selected_highlight, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
- return
- }
-
- super.bind(viewHolder, position, payloads)
- }
+ override fun getSpanSize(spanCount: Int, position: Int): Int = 1
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
- ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl, viewHolder.thumbnail_view, IMAGE_LOADING_OPTIONS)
+ ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl,
+ viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
viewHolder.title_view.text = subscriptionEntity.name
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
@@ -47,7 +36,9 @@ data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, va
viewHolder.selected_highlight.alpha = 1F
}
- override fun getId(): Long {
- return subscriptionEntity.uid
+ fun updateSelected(containerView: View, isSelected: Boolean) {
+ this.isSelected = isSelected
+ animateView(containerView.selected_highlight,
+ AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
}
}
diff --git a/app/src/main/res/layout/dialog_feed_group_create.xml b/app/src/main/res/layout/dialog_feed_group_create.xml
index 2bd0e1141..17893fecc 100644
--- a/app/src/main/res/layout/dialog_feed_group_create.xml
+++ b/app/src/main/res/layout/dialog_feed_group_create.xml
@@ -102,42 +102,56 @@
android:id="@+id/subscriptions_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
+ android:orientation="vertical">
-
+ android:layout_height="?attr/actionBarSize"
+ android:gravity="center_vertical"
+ android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
+ app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"
+ app:titleTextAppearance="@style/Toolbar.Title">
-
+ android:orientation="vertical"
+ android:gravity="center_vertical">
-
-
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/toolbar_search_layout.xml b/app/src/main/res/layout/toolbar_search_layout.xml
index 34e659ece..9a7d56a6e 100644
--- a/app/src/main/res/layout/toolbar_search_layout.xml
+++ b/app/src/main/res/layout/toolbar_search_layout.xml
@@ -1,10 +1,11 @@
-
+ tools:background="?attr/colorPrimary">
diff --git a/app/src/main/res/menu/menu_feed_group_dialog.xml b/app/src/main/res/menu/menu_feed_group_dialog.xml
new file mode 100644
index 000000000..390088b39
--- /dev/null
+++ b/app/src/main/res/menu/menu_feed_group_dialog.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
From 9f3b35634a66ad07776145020c30a553482af478 Mon Sep 17 00:00:00 2001
From: Mauricio Colli
Date: Sun, 5 Apr 2020 16:27:53 -0300
Subject: [PATCH 069/202] Fix subscription picker items flickering in the feed
group dialog
The adapter could not tell the items were the same because the
subscription class was missing some methods (i.e. equals and hashcode),
so a full rebind was being done.
---
.../subscription/SubscriptionEntity.java | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
index cc7219543..a47f17d13 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
@@ -130,4 +130,55 @@ public class SubscriptionEntity {
item.setDescription(getDescription());
return item;
}
+
+
+ // TODO: Remove these generated methods by migrating this class to a data class from Kotlin.
+ @Override
+ @SuppressWarnings("EqualsReplaceableByObjectsCall")
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final SubscriptionEntity that = (SubscriptionEntity) o;
+
+ if (uid != that.uid) {
+ return false;
+ }
+ if (serviceId != that.serviceId) {
+ return false;
+ }
+ if (!url.equals(that.url)) {
+ return false;
+ }
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+ if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) {
+ return false;
+ }
+ if (subscriberCount != null
+ ? !subscriberCount.equals(that.subscriberCount)
+ : that.subscriberCount != null) {
+ return false;
+ }
+ return description != null
+ ? description.equals(that.description)
+ : that.description == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (uid ^ (uid >>> 32));
+ result = 31 * result + serviceId;
+ result = 31 * result + url.hashCode();
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0);
+ result = 31 * result + (subscriberCount != null ? subscriberCount.hashCode() : 0);
+ result = 31 * result + (description != null ? description.hashCode() : 0);
+ return result;
+ }
}
From 2e6e75cd4ef5d6a52544b003ae25fe6ac0bd5dbd Mon Sep 17 00:00:00 2001
From: Mauricio Colli
Date: Thu, 9 Apr 2020 13:07:02 -0300
Subject: [PATCH 070/202] Add filter to the feed group dialog to show only
ungrouped subscriptions
---
.../database/subscription/SubscriptionDAO.kt | 36 ++++++++++++++++-
.../local/subscription/SubscriptionManager.kt | 39 ++++++++++++++-----
.../subscription/dialog/FeedGroupDialog.kt | 13 ++++++-
.../dialog/FeedGroupDialogViewModel.kt | 32 +++++++++------
.../main/res/menu/menu_feed_group_dialog.xml | 13 ++++++-
app/src/main/res/values/strings.xml | 1 +
6 files changed, 107 insertions(+), 27 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
index aa34a2867..60dd343b9 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt
@@ -22,10 +22,42 @@ abstract class SubscriptionDAO : BasicDAO {
@Query("""
SELECT * FROM subscriptions
+
WHERE name LIKE '%' || :filter || '%'
+
ORDER BY name COLLATE NOCASE ASC
""")
- abstract fun filterByName(filter: String): Flowable>
+ abstract fun getSubscriptionsFiltered(filter: String): Flowable>
+
+ @Query("""
+ SELECT * FROM subscriptions s
+
+ LEFT JOIN feed_group_subscription_join fgs
+ ON s.uid = fgs.subscription_id
+
+ WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
+
+ ORDER BY name COLLATE NOCASE ASC
+ """)
+ abstract fun getSubscriptionsOnlyUngrouped(
+ currentGroupId: Long
+ ): Flowable>
+
+ @Query("""
+ SELECT * FROM subscriptions s
+
+ LEFT JOIN feed_group_subscription_join fgs
+ ON s.uid = fgs.subscription_id
+
+ WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
+ AND s.name LIKE '%' || :filter || '%'
+
+ ORDER BY name COLLATE NOCASE ASC
+ """)
+ abstract fun getSubscriptionsOnlyUngroupedFiltered(
+ currentGroupId: Long,
+ filter: String
+ ): Flowable>
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable>
@@ -59,7 +91,7 @@ abstract class SubscriptionDAO : BasicDAO {
entity.uid = uidFromInsert
} else {
val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url)
- ?: throw IllegalStateException("Subscription cannot be null just after insertion.")
+ ?: throw IllegalStateException("Subscription cannot be null just after insertion.")
entity.uid = subscriptionIdFromDb
update(entity)
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
index ce272c856..2740591e6 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
@@ -2,9 +2,11 @@ package org.schabi.newpipe.local.subscription
import android.content.Context
import io.reactivex.Completable
+import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.schabi.newpipe.NewPipeDatabase
+import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.database.subscription.SubscriptionDAO
import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.extractor.ListInfo
@@ -21,11 +23,28 @@ class SubscriptionManager(context: Context) {
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
fun subscriptions() = subscriptionTable.all
- fun filterByName(filter: String) = subscriptionTable.filterByName(filter)
+ fun getSubscriptions(
+ currentGroupId: Long = FeedGroupEntity.GROUP_ALL_ID,
+ filterQuery: String = "",
+ showOnlyUngrouped: Boolean = false
+ ): Flowable> {
+ return when {
+ filterQuery.isNotEmpty() -> {
+ return if (showOnlyUngrouped) {
+ subscriptionTable.getSubscriptionsOnlyUngroupedFiltered(
+ currentGroupId, filterQuery)
+ } else {
+ subscriptionTable.getSubscriptionsFiltered(filterQuery)
+ }
+ }
+ showOnlyUngrouped -> subscriptionTable.getSubscriptionsOnlyUngrouped(currentGroupId)
+ else -> subscriptionTable.all
+ }
+ }
fun upsertAll(infoList: List): List {
val listEntities = subscriptionTable.upsertAll(
- infoList.map { SubscriptionEntity.from(it) })
+ infoList.map { SubscriptionEntity.from(it) })
database.runInTransaction {
infoList.forEachIndexed { index, info ->
@@ -37,13 +56,13 @@ class SubscriptionManager(context: Context) {
}
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
- .flatMapCompletable {
- Completable.fromRunnable {
- it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
- subscriptionTable.update(it)
- feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
- }
+ .flatMapCompletable {
+ Completable.fromRunnable {
+ it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
+ subscriptionTable.update(it)
+ feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
}
+ }
fun updateFromInfo(subscriptionId: Long, info: ListInfo) {
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId)
@@ -59,8 +78,8 @@ class SubscriptionManager(context: Context) {
fun deleteSubscription(serviceId: Int, url: String): Completable {
return Completable.fromCallable { subscriptionTable.deleteSubscription(serviceId, url) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) {
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 0b77ec1d8..66387d298 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
@@ -65,6 +65,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
@State @JvmField var iconsListState: Parcelable? = null
@State @JvmField var wasSearchSubscriptionsVisible = false
@State @JvmField var subscriptionsCurrentSearchQuery = ""
+ @State @JvmField var subscriptionsShowOnlyUngrouped = false
private val subscriptionMainSection = Section()
private val subscriptionEmptyFooter = Section()
@@ -116,7 +117,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
viewModel = ViewModelProvider(this,
FeedGroupDialogViewModel.Factory(requireContext(),
- groupId, subscriptionsCurrentSearchQuery)
+ groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped)
).get(FeedGroupDialogViewModel::class.java)
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
@@ -216,6 +217,16 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
true
}
+ headerMenu.findItem(R.id.feed_group_toggle_show_only_ungrouped_subscriptions).apply {
+ isChecked = subscriptionsShowOnlyUngrouped
+ setOnMenuItemClickListener {
+ subscriptionsShowOnlyUngrouped = !subscriptionsShowOnlyUngrouped
+ it.isChecked = subscriptionsShowOnlyUngrouped
+ viewModel.toggleShowOnlyUngrouped(subscriptionsShowOnlyUngrouped)
+ true
+ }
+ }
+
toolbar_search_clear.setOnClickListener {
if (TextUtils.isEmpty(toolbar_search_edit_text.text)) {
hideSearch()
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
index a7ab300f2..e9a7e4eb7 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt
@@ -20,24 +20,25 @@ import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
class FeedGroupDialogViewModel(
applicationContext: Context,
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
- initialQuery: String = ""
+ initialQuery: String = "",
+ initialShowOnlyUngrouped: Boolean = false
) : ViewModel() {
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
private var subscriptionManager = SubscriptionManager(applicationContext)
private var filterSubscriptions = BehaviorProcessor.create()
- private var allSubscriptions = subscriptionManager.subscriptions()
+ private var toggleShowOnlyUngrouped = BehaviorProcessor.create()
- private var subscriptionsFlowable = filterSubscriptions
- .startWith(initialQuery)
+ private var subscriptionsFlowable = Flowable
+ .combineLatest(
+ filterSubscriptions.startWith(initialQuery),
+ toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
+ BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
+ )
.distinctUntilChanged()
- .switchMap { query ->
- if (query.isEmpty()) {
- allSubscriptions
- } else {
- subscriptionManager.filterByName(query)
- }
+ .switchMap { filter ->
+ subscriptionManager.getSubscriptions(groupId, filter.query, filter.showOnlyUngrouped)
}.map { list -> list.map { PickerSubscriptionItem(it) } }
private val mutableGroupLiveData = MutableLiveData()
@@ -100,20 +101,27 @@ class FeedGroupDialogViewModel(
filterSubscriptions.onNext("")
}
+ fun toggleShowOnlyUngrouped(showOnlyUngrouped: Boolean) {
+ toggleShowOnlyUngrouped.onNext(showOnlyUngrouped)
+ }
+
sealed class DialogEvent {
object ProcessingEvent : DialogEvent()
object SuccessEvent : DialogEvent()
}
+ data class Filter(val query: String, val showOnlyUngrouped: Boolean)
+
class Factory(
private val context: Context,
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
- private val initialQuery: String = ""
+ private val initialQuery: String = "",
+ private val initialShowOnlyUngrouped: Boolean = false
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun create(modelClass: Class): T {
return FeedGroupDialogViewModel(context.applicationContext,
- groupId, initialQuery) as T
+ groupId, initialQuery, initialShowOnlyUngrouped) as T
}
}
}
diff --git a/app/src/main/res/menu/menu_feed_group_dialog.xml b/app/src/main/res/menu/menu_feed_group_dialog.xml
index 390088b39..af9be1c65 100644
--- a/app/src/main/res/menu/menu_feed_group_dialog.xml
+++ b/app/src/main/res/menu/menu_feed_group_dialog.xml
@@ -1,10 +1,19 @@
-
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f2e79a79b..a4dfd255d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -637,6 +637,7 @@
Name
Do you want to delete this group?
New
+ Show only ungrouped subscriptions
Feed
Feed update threshold
Time after last update before a subscription is considered outdated — %s
From a7e8f5087ee10a814fe0148657e35c85392b93f8 Mon Sep 17 00:00:00 2001
From: wb9688
Date: Sun, 28 Jun 2020 14:59:44 +0200
Subject: [PATCH 071/202] Do not include Checkstyle in any APK
---
app/build.gradle | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6e0801049..9d1ac22a2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -91,6 +91,11 @@ ext {
markwonVersion = '4.3.1'
}
+configurations {
+ checkstyle
+ ktlint
+}
+
checkstyle {
configFile rootProject.file('checkstyle.xml')
ignoreFailures false
@@ -106,8 +111,7 @@ task runCheckstyle(type: Checkstyle) {
exclude '**/BuildConfig.java'
exclude 'main/java/us/shandian/giga/**'
- // empty classpath
- classpath = files()
+ classpath = configurations.checkstyle
showViolations true
@@ -117,10 +121,6 @@ task runCheckstyle(type: Checkstyle) {
}
}
-configurations {
- ktlint
-}
-
task runKtlint(type: JavaExec) {
main = "com.pinterest.ktlint.Main"
classpath = configurations.ktlint
@@ -143,7 +143,7 @@ dependencies {
implementation "frankiesardo:icepick:${icepickVersion}"
kapt "frankiesardo:icepick-processor:${icepickVersion}"
- debugImplementation "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
+ checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
ktlint "com.pinterest:ktlint:0.35.0"
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
From 3de5afc68e2ddf98c26b11c229a457d82a5ae3d0 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Sun, 28 Jun 2020 16:07:17 +0200
Subject: [PATCH 072/202] Add Markdown export of crash logs
Add app language as additional debug information to reports
---
.../schabi/newpipe/report/ErrorActivity.java | 176 +++++++++++++-----
app/src/main/res/layout/activity_error.xml | 22 ++-
app/src/main/res/values-de/strings.xml | 4 +-
app/src/main/res/values/strings.xml | 5 +-
4 files changed, 159 insertions(+), 48 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java
index 20724c02b..52761e467 100644
--- a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java
@@ -11,7 +11,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
-import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -20,6 +19,7 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -37,6 +37,8 @@ import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
+import org.schabi.newpipe.util.Localization;
+import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.PrintWriter;
@@ -45,7 +47,6 @@ import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
-import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;
@@ -81,6 +82,10 @@ public class ErrorActivity extends AppCompatActivity {
public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org";
public static final String ERROR_EMAIL_SUBJECT
= "Exception in NewPipe " + BuildConfig.VERSION_NAME;
+
+ public static final String ERROR_GITHUB_ISSUE_URL
+ = "https://github.com/TeamNewPipe/NewPipe/issues";
+
private String[] errorList;
private ErrorInfo errorInfo;
private Class returnActivity;
@@ -193,7 +198,10 @@ public class ErrorActivity extends AppCompatActivity {
actionBar.setDisplayShowTitleEnabled(true);
}
- Button reportButton = findViewById(R.id.errorReportButton);
+ final Button reportEmailButton = findViewById(R.id.errorReportEmailButton);
+ final Button copyButton = findViewById(R.id.errorReportCopyButton);
+ final Button reportGithubButton = findViewById(R.id.errorReportGitHubButton);
+
userCommentBox = findViewById(R.id.errorCommentBox);
TextView errorView = findViewById(R.id.errorView);
TextView infoView = findViewById(R.id.errorInfosView);
@@ -205,40 +213,23 @@ public class ErrorActivity extends AppCompatActivity {
errorList = intent.getStringArrayExtra(ERROR_LIST);
// important add guru meditation
- addGuruMeditaion();
+ addGuruMeditation();
currentTimeStamp = getCurrentTimeStamp();
- reportButton.setOnClickListener((View v) -> {
- Context context = this;
- new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.privacy_policy_title)
- .setMessage(R.string.start_accept_privacy_policy)
- .setCancelable(false)
- .setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> {
- Intent webIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(context.getString(R.string.privacy_policy_url))
- );
- context.startActivity(webIntent);
- })
- .setPositiveButton(R.string.accept, (dialog, which) -> {
- final Intent i = new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse("mailto:")) // only email apps should handle this
- .putExtra(Intent.EXTRA_EMAIL, new String[]{ERROR_EMAIL_ADDRESS})
- .putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
- .putExtra(Intent.EXTRA_TEXT, buildJson());
- if (i.resolveActivity(getPackageManager()) != null) {
- startActivity(i);
- }
-
- })
- .setNegativeButton(R.string.decline, (dialog, which) -> {
- // do nothing
- })
- .show();
-
+ reportEmailButton.setOnClickListener((View v) -> {
+ openPrivacyPolicyDialog(this, "EMAIL");
});
+ copyButton.setOnClickListener((View v) -> {
+ ShareUtils.copyToClipboard(this, buildMarkdown());
+ Toast.makeText(this, R.string.msg_copied, Toast.LENGTH_SHORT).show();
+ });
+
+ reportGithubButton.setOnClickListener((View v) -> {
+ openPrivacyPolicyDialog(this, "GITHUB");
+ });
+
+
// normal bugreport
buildInfo(errorInfo);
if (errorInfo.message != 0) {
@@ -250,7 +241,7 @@ public class ErrorActivity extends AppCompatActivity {
errorView.setText(formErrorText(errorList));
- //print stack trace once again for debugging:
+ // print stack trace once again for debugging:
for (String e : errorList) {
Log.e(TAG, e);
}
@@ -281,6 +272,37 @@ public class ErrorActivity extends AppCompatActivity {
return false;
}
+ private void openPrivacyPolicyDialog(final Context context, final String action) {
+ new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.privacy_policy_title)
+ .setMessage(R.string.start_accept_privacy_policy)
+ .setCancelable(false)
+ .setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> {
+ ShareUtils.openUrlInBrowser(context,
+ context.getString(R.string.privacy_policy_url));
+ })
+ .setPositiveButton(R.string.accept, (dialog, which) -> {
+ if (action.equals("EMAIL")) { // send on email
+ final Intent i = new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse("mailto:")) // only email apps should handle this
+ .putExtra(Intent.EXTRA_EMAIL, new String[]{ERROR_EMAIL_ADDRESS})
+ .putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
+ .putExtra(Intent.EXTRA_TEXT, buildJson());
+ if (i.resolveActivity(getPackageManager()) != null) {
+ startActivity(i);
+ }
+ } else if (action.equals("GITHUB")) { // open the NewPipe issue page on GitHub
+ ShareUtils.openUrlInBrowser(this, ERROR_GITHUB_ISSUE_URL);
+ }
+
+ })
+ .setNegativeButton(R.string.decline, (dialog, which) -> {
+ // do nothing
+ })
+ .show();
+ }
+
private String formErrorText(final String[] el) {
StringBuilder text = new StringBuilder();
if (el != null) {
@@ -331,7 +353,9 @@ public class ErrorActivity extends AppCompatActivity {
text += getUserActionString(info.userAction) + "\n"
+ info.request + "\n"
- + getContentLangString() + "\n"
+ + getContentLanguageString() + "\n"
+ + getContentCountryString() + "\n"
+ + getAppLanguage() + "\n"
+ info.serviceName + "\n"
+ currentTimeStamp + "\n"
+ getPackageName() + "\n"
@@ -347,7 +371,9 @@ public class ErrorActivity extends AppCompatActivity {
.object()
.value("user_action", getUserActionString(errorInfo.userAction))
.value("request", errorInfo.request)
- .value("content_language", getContentLangString())
+ .value("content_language", getContentLanguageString())
+ .value("content_country", getContentCountryString())
+ .value("app_language", getAppLanguage())
.value("service", errorInfo.serviceName)
.value("package", getPackageName())
.value("version", BuildConfig.VERSION_NAME)
@@ -365,6 +391,63 @@ public class ErrorActivity extends AppCompatActivity {
return "";
}
+ private String buildMarkdown() {
+ try {
+ final StringBuilder htmlErrorReport = new StringBuilder();
+
+ final String userComment = userCommentBox.getText().toString();
+ if (!userComment.isEmpty()) {
+ htmlErrorReport.append(userComment).append("\n");
+ }
+
+ // basic error info
+ htmlErrorReport
+ .append("## Exception")
+ .append("\n* __User Action:__ ")
+ .append(getUserActionString(errorInfo.userAction))
+ .append("\n* __Request:__ ").append(errorInfo.request)
+ .append("\n* __Content Country:__ ").append(getContentCountryString())
+ .append("\n* __Content Language:__ ").append(getContentLanguageString())
+ .append("\n* __App Language:__ ").append(getAppLanguage())
+ .append("\n* __Service:__ ").append(errorInfo.serviceName)
+ .append("\n* __Version:__ ").append(BuildConfig.VERSION_NAME)
+ .append("\n* __OS:__ ").append(getOsString()).append("\n");
+
+
+ // Collapse all logs to a single paragraph when there are more than one
+ // to keep the GitHub issue clean.
+ if (errorList.length > 1) {
+ htmlErrorReport
+ .append("Exceptions (")
+ .append(errorList.length)
+ .append(")
\n");
+ }
+
+ // add the logs
+ for (int i = 0; i < errorList.length; i++) {
+ htmlErrorReport.append("Crash log ");
+ if (errorList.length > 1) {
+ htmlErrorReport.append(i + 1);
+ }
+ htmlErrorReport.append("")
+ .append("
\n")
+ .append("\n```\n").append(errorList[i]).append("\n```\n")
+ .append("
\n");
+ }
+
+ // make sure to close everything
+ if (errorList.length > 1) {
+ htmlErrorReport.append("
\n");
+ }
+ htmlErrorReport.append("
\n");
+ return htmlErrorReport.toString();
+ } catch (Throwable e) {
+ Log.e(TAG, "Error while erroring: Could not build markdown");
+ e.printStackTrace();
+ return "";
+ }
+ }
+
private String getUserActionString(final UserAction userAction) {
if (userAction == null) {
return "Your description is in another castle.";
@@ -373,24 +456,27 @@ public class ErrorActivity extends AppCompatActivity {
}
}
- private String getContentLangString() {
- String contentLanguage = PreferenceManager.getDefaultSharedPreferences(this)
- .getString(this.getString(R.string.content_country_key), "none");
- if (contentLanguage.equals(getString(R.string.default_localization_key))) {
- contentLanguage = Locale.getDefault().toString();
- }
- return contentLanguage;
+ private String getContentCountryString() {
+ return Localization.getPreferredContentCountry(this).getCountryCode();
+ }
+
+ private String getContentLanguageString() {
+ return Localization.getPreferredLocalization(this).getLocalizationCode();
+ }
+
+ private String getAppLanguage() {
+ return Localization.getAppLocale(getApplicationContext()).toString();
}
private String getOsString() {
- String osBase = Build.VERSION.SDK_INT >= 23 ? Build.VERSION.BASE_OS : "Android";
+ final String osBase = Build.VERSION.SDK_INT >= 23 ? Build.VERSION.BASE_OS : "Android";
return System.getProperty("os.name")
+ " " + (osBase.isEmpty() ? "Android" : osBase)
+ " " + Build.VERSION.RELEASE
+ " - " + Build.VERSION.SDK_INT;
}
- private void addGuruMeditaion() {
+ private void addGuruMeditation() {
//just an easter egg
TextView sorryView = findViewById(R.id.errorSorryView);
String text = sorryView.getText().toString();
diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml
index c47077c73..12237f918 100644
--- a/app/src/main/res/layout/activity_error.xml
+++ b/app/src/main/res/layout/activity_error.xml
@@ -118,11 +118,31 @@
android:inputType="" />
+
+
+
+
+
+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 4f5c6e018..113f6025f 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -65,10 +65,12 @@
Live-Streams werden noch nicht unterstützt
Konnte Webseite nicht vollständig analysieren
Fehler via E-Mail melden
+ Formatierten Fehlerbericht kopieren
+ Fehler auf GitHub melden
Melden
Info:
Dies ist passiert:
- Was:\\nAnfrage:\\nSprache des Inhalts:\\nDienst:\\nZeit (GMT):\\nPaket:\\nVersion:\\nOS-Version:
+ Was:\\nAnfrage:\\nSprache des Inhalts:\\nLand des Inhalts:\\nSprache der App:\\nDienst:\\nZeit (GMT):\\nPaket:\\nVersion:\\nOS-Version:
Details:
Video
Audio
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f2e79a79b..0441a242d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -234,11 +234,14 @@
Sorry, that should not have happened.
Guru Meditation.
Report this error via e-mail
+ Copy formatted report
+ Report error on GitHub
+ Please check whether an issue discussing your crash already exists. When creating duplicate tickets, you take time from us which we could spend with fixing the actual bug.
Sorry, some errors occurred.
Report
Info:
What happened:
- What:\\nRequest:\\nContent Lang:\\nService:\\nGMT Time:\\nPackage:\\nVersion:\\nOS version:
+ What:\\nRequest:\\nContent Language:\\nContent Country:\\nApp Language:\\nService:\\nGMT Time:\\nPackage:\\nVersion:\\nOS version:
Your comment (in English):
Details:
From 16ae90dc9f395d6a9bb50afbc7572f0d389ad8d7 Mon Sep 17 00:00:00 2001
From: ssantos
Date: Sat, 27 Jun 2020 21:54:50 +0000
Subject: [PATCH 073/202] Translated using Weblate (German)
Currently translated at 100.0% (576 of 576 strings)
---
app/src/main/res/values-de/strings.xml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d8e577d55..15b82d5dd 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -4,8 +4,7 @@
Veröffentlicht am %1$s
Keinen Stream-Player gefunden. VLC installieren\?
Installieren
- Abbrechen
-\n
+ Abbrechen
Im Browser öffnen
Teilen
Herunterladen
@@ -240,7 +239,7 @@
„Zum Anhängen gedrückt halten“ Tipp anzeigen
[Unbekannt]
In Warteschlange für Hintergrundwiedergabe
- In Warteschlange in neuem Pop-up
+ In Warteschlange in einem Pop-up
Ab hier wiedergeben
Wiedergabe im Hintergrund starten
Wiedergabe in einem Pop-up starten
From 078ae157940ebc3086c43f532c5b4446fcfec7e2 Mon Sep 17 00:00:00 2001
From: Anonim Qwerty
Date: Sun, 28 Jun 2020 19:35:09 +0000
Subject: [PATCH 074/202] Translated using Weblate (Ukrainian)
Currently translated at 98.9% (570 of 576 strings)
---
app/src/main/res/values-uk/strings.xml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 594a6e224..f12c19cd2 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -619,4 +619,7 @@
\n
\nЩоб побачити його потрібно включите \"Контент 18+\" в налаштуваннях.
Видалено %1$s завантажень
+ Ескіз аватара каналу
+ Оригінальні тексти сервісів будуть видимі в потокових елементах
+ Режим обмеженого доступу YouTube
\ No newline at end of file
From 26c5f69161e079ba3ccb2dd12de6bc1936ff15e5 Mon Sep 17 00:00:00 2001
From: Enol P
Date: Sat, 27 Jun 2020 14:28:29 +0000
Subject: [PATCH 075/202] Translated using Weblate (Asturian)
Currently translated at 51.0% (294 of 576 strings)
---
app/src/main/res/values-b+ast/strings.xml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 247c76b3f..06f554db1 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -130,7 +130,7 @@
Novedaes
[Desconozse]
Reproducir en segundu planu
- Reproducir nun ventanu
+
Donación
Donar
@@ -210,7 +210,7 @@
Nun hai comentarios
Llimpieza de datos
Amosar comentarios
-
+ Desactiva esta opción p\'anubrir los comentarios
Pa cumplir cola GDPR (Regulación Xeneral de Proteición de Datos) europea, pidímoste que revises la política de privacidá de NewPipe. Lléila con procuru.
\nHas aceutala unviándonos un informe de fallos.
@@ -304,4 +304,8 @@
Llipióse la caché de metadatos
Desanicia los datos de les páxines web na caché
Llimpiar los metadatos de la caché
+ Llimpióse la caché d\'imáxenes
+ Amuésase una miniatura de videu na pantalla de bloquéu al usar el reproductor en segundu planu
+ ¿Instalar l\'aplicación Kore\?
+ Reproduz un videu cuando s\'invoca a NewPipe dende otra aplicación
\ No newline at end of file
From 4e8407ed8fead74b2f0a4eff28a8393aeaa12fb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Allan=20Nordh=C3=B8y?=
Date: Sun, 28 Jun 2020 21:45:56 +0000
Subject: [PATCH 076/202] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?=
=?UTF-8?q?an=20Bokm=C3=A5l)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently translated at 89.2% (514 of 576 strings)
---
app/src/main/res/values-nb-rNO/strings.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index faf1841d0..532eb4aca 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -585,4 +585,6 @@
Ja, og delvist sette videoer
Fjern sette videoer\?
Fjern sette
+ Opprettet av %s
+ Av %s
\ No newline at end of file
From e14ba482442ab41682bb3920aca1d6239444f7d7 Mon Sep 17 00:00:00 2001
From: Jasper Eames Palmer
Date: Mon, 29 Jun 2020 22:30:09 -0700
Subject: [PATCH 077/202] Update non-service status bar colors to match toolbar
colors
---
app/src/main/res/values-v21/styles_services.xml | 6 +++---
app/src/main/res/values/styles.xml | 14 +++++++-------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/app/src/main/res/values-v21/styles_services.xml b/app/src/main/res/values-v21/styles_services.xml
index 1c725f887..d40065059 100644
--- a/app/src/main/res/values-v21/styles_services.xml
+++ b/app/src/main/res/values-v21/styles_services.xml
@@ -34,19 +34,19 @@
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index fae608074..9018a2d2a 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -12,7 +12,7 @@
-
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 1795dd607..d77d1564c 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -307,8 +307,6 @@
Trang chủ
Xu hướng
Mới & hot
- Trình phát nền
- Trình phát popup
Tẩy xoá
Chi tiết
Cài đặt âm thanh
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 8b87e9b10..76fb80795 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -273,8 +273,6 @@
无法播放此串流
发生无法恢复播放器错误
恢复播放器错误
- 后台播放
- 悬浮窗播放器
移除
详情
音频设置
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index b71d64a1f..0a10f68a0 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -252,8 +252,6 @@
動向
前 50
最新和熱門
- 背景播放
- 懸浮視窗播放
移除
詳細資訊
音訊設定
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index cdb8293af..bce920722 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -144,8 +144,6 @@
- @string/audio_webm_key
- last_orientation_landscape_key
-
last_resize_mode
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2da2a9b1c..be856ef01 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -429,8 +429,6 @@
%1$s/%2$s
Play queue
- Background player
- Popup player
Remove
Details
Audio Settings
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 9018a2d2a..9a81d01e1 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -330,5 +330,4 @@
- @null
-
From 68ed738dcd02e1c32a92aa787ece1447245f0b76 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 25 Jul 2020 09:45:33 +0300
Subject: [PATCH 154/202] Renamed files
---
app/src/main/java/org/schabi/newpipe/player/MainPlayer.java | 6 +++---
.../{activity_main_player.xml => player.xml} | 0
.../res/layout/{activity_main_player.xml => player.xml} | 0
..._background_notification.xml => player_notification.xml} | 0
...cation_expanded.xml => player_notification_expanded.xml} | 0
5 files changed, 3 insertions(+), 3 deletions(-)
rename app/src/main/res/layout-large-land/{activity_main_player.xml => player.xml} (100%)
rename app/src/main/res/layout/{activity_main_player.xml => player.xml} (100%)
rename app/src/main/res/layout/{player_background_notification.xml => player_notification.xml} (100%)
rename app/src/main/res/layout/{player_background_notification_expanded.xml => player_notification_expanded.xml} (100%)
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 d3bb90660..fe1c95b9c 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
@@ -122,7 +122,7 @@ public final class MainPlayer extends Service {
}
private void createView() {
- final View layout = View.inflate(this, R.layout.activity_main_player, null);
+ final View layout = View.inflate(this, R.layout.player, null);
playerImpl = new VideoPlayerImpl(this);
playerImpl.setup(layout);
@@ -284,9 +284,9 @@ public final class MainPlayer extends Service {
private NotificationCompat.Builder createNotification() {
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
- R.layout.player_background_notification);
+ R.layout.player_notification);
bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
- R.layout.player_background_notification_expanded);
+ R.layout.player_notification_expanded);
setupNotification(notRemoteView);
setupNotification(bigNotRemoteView);
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/player.xml
similarity index 100%
rename from app/src/main/res/layout-large-land/activity_main_player.xml
rename to app/src/main/res/layout-large-land/player.xml
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/player.xml
similarity index 100%
rename from app/src/main/res/layout/activity_main_player.xml
rename to app/src/main/res/layout/player.xml
diff --git a/app/src/main/res/layout/player_background_notification.xml b/app/src/main/res/layout/player_notification.xml
similarity index 100%
rename from app/src/main/res/layout/player_background_notification.xml
rename to app/src/main/res/layout/player_notification.xml
diff --git a/app/src/main/res/layout/player_background_notification_expanded.xml b/app/src/main/res/layout/player_notification_expanded.xml
similarity index 100%
rename from app/src/main/res/layout/player_background_notification_expanded.xml
rename to app/src/main/res/layout/player_notification_expanded.xml
From 03685db2fc4bdbe3f334e50d1b415746165d5acc Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sun, 26 Jul 2020 12:04:40 +0200
Subject: [PATCH 155/202] Improve playlist header layout: align with info items
---
.../main/res/layout/local_playlist_header.xml | 14 ++++----------
app/src/main/res/layout/playlist_header.xml | 18 +++++++-----------
2 files changed, 11 insertions(+), 21 deletions(-)
diff --git a/app/src/main/res/layout/local_playlist_header.xml b/app/src/main/res/layout/local_playlist_header.xml
index 04fe32ab0..f4e2fe07d 100644
--- a/app/src/main/res/layout/local_playlist_header.xml
+++ b/app/src/main/res/layout/local_playlist_header.xml
@@ -1,23 +1,18 @@
-
-
@@ -46,8 +42,8 @@
android:id="@+id/uploader_avatar_view"
android:layout_width="@dimen/playlist_detail_uploader_image_size"
android:layout_height="@dimen/playlist_detail_uploader_image_size"
+ android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
- android:layout_margin="1dp"
android:src="@drawable/buddy"
app:civ_border_color="#ffffff"
app:civ_border_width="1dp"/>
@@ -56,7 +52,7 @@
android:id="@+id/uploader_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginLeft="4dp"
+ android:paddingHorizontal="6dp"
android:layout_toRightOf="@+id/uploader_avatar_view"
android:ellipsize="end"
android:gravity="left|center_vertical"
@@ -73,7 +69,7 @@
android:layout_alignBottom="@+id/uploader_layout"
android:layout_alignTop="@+id/uploader_layout"
android:layout_alignParentRight="true"
- android:layout_marginRight="6dp"
+ android:layout_marginLeft="6dp"
android:ellipsize="end"
android:gravity="right|center_vertical"
android:maxLines="1"
From b0685c153a0341da0f94c98d0fd12363d009e7b9 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Sun, 26 Jul 2020 14:26:01 +0200
Subject: [PATCH 156/202] Update extractor version
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b97958edc..b5e4c9b83 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -163,7 +163,7 @@ dependencies {
exclude module: 'support-annotations'
}
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:df28a087de3d9ccb9eb180e001b1d8ca7f7cf544'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:b4481dfec21cf39aabbb791290d30130235aeabd'
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
implementation "org.jsoup:jsoup:1.13.1"
From 17edd1c3d4cc80d4c5f9077ba6cb910f8b1a0315 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Sun, 26 Jul 2020 14:38:41 +0200
Subject: [PATCH 157/202] Add changelog
---
fastlane/metadata/android/en-US/changelogs/952.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 fastlane/metadata/android/en-US/changelogs/952.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/952.txt b/fastlane/metadata/android/en-US/changelogs/952.txt
new file mode 100644
index 000000000..c72818bc5
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/952.txt
@@ -0,0 +1,7 @@
+Improved
+• Auto-play is available for all services (instead of only for YouTube)
+
+Fixed
+• Fixed related streams by supporting YouTube's new continuations
+• Fixed age restricted YouTube videos
+• [Android TV] Fixed lingering focus highlight overlay
\ No newline at end of file
From f53a0f0d07b9d2ff6cd8a712eda806ecdd5bb587 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Sun, 26 Jul 2020 14:39:18 +0200
Subject: [PATCH 158/202] Update version to 0.19.7 (952)
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index b5e4c9b83..cfd1a5a00 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdkVersion 19
targetSdkVersion 29
- versionCode 951
- versionName "0.19.6"
+ versionCode 952
+ versionName "0.19.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
From 7918e3a1aa4d0c5632c18a553f8813de1fa9d855 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Tue, 28 Jul 2020 01:07:13 +0200
Subject: [PATCH 159/202] Update version to 0.19.8 (953)
Update extractot version.
Fix extraction of YouTube's decryption function.
---
app/build.gradle | 6 +++---
fastlane/metadata/android/en-US/changelogs/953.txt | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
create mode 100644 fastlane/metadata/android/en-US/changelogs/953.txt
diff --git a/app/build.gradle b/app/build.gradle
index afca85646..2082d72f8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdkVersion 19
targetSdkVersion 29
- versionCode 952
- versionName "0.19.7"
+ versionCode 953
+ versionName "0.19.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -163,7 +163,7 @@ dependencies {
exclude module: 'support-annotations'
}
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:b4481dfec21cf39aabbb791290d30130235aeabd'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:5ac80624a40f4c600ae493e66881b5bf008f0ddb'
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
implementation "org.jsoup:jsoup:1.13.1"
diff --git a/fastlane/metadata/android/en-US/changelogs/953.txt b/fastlane/metadata/android/en-US/changelogs/953.txt
new file mode 100644
index 000000000..127ea0686
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/953.txt
@@ -0,0 +1 @@
+Fix extraction of YouTube's decryption function.
\ No newline at end of file
From 1b9f5989efff651c3a99eacfacab2015cc75a5a4 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Tue, 28 Jul 2020 10:45:48 +0200
Subject: [PATCH 160/202] Fix empty stacktrace in bug report
ACRA has to be initialized after MultiDex
https://github.com/ACRA/acra/issues/619
https://github.com/ACRA/acra/wiki/Troubleshooting-Guide#legacy-multidex
---
app/src/debug/java/org/schabi/newpipe/DebugApp.kt | 12 ++++++------
app/src/main/java/org/schabi/newpipe/App.java | 7 +++++--
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
index 5cfde80b8..9ea3bdabe 100644
--- a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
+++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
@@ -1,6 +1,5 @@
package org.schabi.newpipe
-import android.content.Context
import androidx.multidex.MultiDex
import androidx.preference.PreferenceManager
import com.facebook.stetho.Stetho
@@ -11,11 +10,6 @@ import okhttp3.OkHttpClient
import org.schabi.newpipe.extractor.downloader.Downloader
class DebugApp : App() {
- override fun attachBaseContext(base: Context) {
- super.attachBaseContext(base)
- MultiDex.install(this)
- }
-
override fun onCreate() {
super.onCreate()
initStetho()
@@ -34,6 +28,12 @@ class DebugApp : App() {
return downloader
}
+ override fun initACRA() {
+ // install MultiDex before initializing ACRA
+ MultiDex.install(this)
+ super.initACRA()
+ }
+
private fun initStetho() {
// Create an InitializerBuilder
val initializerBuilder = Stetho.newInitializerBuilder(this)
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 33d77ea47..64c1fdb1c 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -77,7 +77,6 @@ public class App extends Application {
@Override
protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
-
initACRA();
}
@@ -200,7 +199,11 @@ public class App extends Application {
.build();
}
- private void initACRA() {
+ /**
+ * Called in {@link #attachBaseContext(Context)} after calling the {@code super} method.
+ * Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA.
+ */
+ protected void initACRA() {
try {
final CoreConfiguration acraConfig = new CoreConfigurationBuilder(this)
.setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES)
From 21d1f69d6d7697c1eae6b4b0d5f0a441ec857de7 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Tue, 28 Jul 2020 10:48:54 +0200
Subject: [PATCH 161/202] Do not init ACRA if inside its own process
https://github.com/ACRA/acra/wiki/Troubleshooting-Guide#applicationoncreate
---
app/src/main/java/org/schabi/newpipe/App.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 64c1fdb1c..75228cefe 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -204,6 +204,10 @@ public class App extends Application {
* Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA.
*/
protected void initACRA() {
+ if (ACRA.isACRASenderServiceProcess()) {
+ return;
+ }
+
try {
final CoreConfiguration acraConfig = new CoreConfigurationBuilder(this)
.setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES)
From d3b6781bb8414895ed2785a55aa82644931232b0 Mon Sep 17 00:00:00 2001
From: Tobias Groza
Date: Tue, 28 Jul 2020 10:56:06 +0200
Subject: [PATCH 162/202] Update bug report template
DO NOT REPORT THAT CRASH ANY MORE. WE ARE GETTING SPAMMED11!!!1
---
.github/ISSUE_TEMPLATE/bug_report.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index dbc1c05a5..655a1b96c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,6 +6,8 @@ assignees: ''
---
+# We have fixed the decrpytion exception and a new release is on its wait. Please do not report the bug, that no videos can be played.
+If you are about to report something else, please remove this and the above line. Thank you :)
+
+
+
+
From 70b643e7bace4a7da94c5a46c7964619eb575363 Mon Sep 17 00:00:00 2001
From: ButterflyOfFire <42316180+BoFFire@users.noreply.github.com>
Date: Mon, 3 Aug 2020 19:01:51 +0200
Subject: [PATCH 173/202] Fixing name for Taqbaylit language
Fixing name for Taqbaylit language
---
app/src/main/res/values/settings_keys.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index bce920722..737830f60 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -1085,7 +1085,7 @@
- Italiano
- 日本語
- ꦧꦱꦗꦮ
- - Tamaziɣt Taqbaylit
+ - Taqbaylit
- 한국어
- کوردی
- Lietuvių kalba
From d7574973e97fa7a3a21aa59967311f6091941830 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Wed, 5 Aug 2020 12:46:25 +0300
Subject: [PATCH 174/202] Reduced CPU usage when playing a video by 7-10%
---
.../fragments/detail/VideoDetailFragment.java | 28 ++++++++++++-------
1 file changed, 18 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 66077f3ee..d014c5b38 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
@@ -202,6 +202,7 @@ public class VideoDetailFragment
private ImageView thumbnailImageView;
private ImageView thumbnailPlayButton;
private AnimatedProgressBar positionView;
+ private ViewGroup playerPlaceholder;
private View videoTitleRoot;
private TextView videoTitleTextView;
@@ -705,6 +706,7 @@ public class VideoDetailFragment
thumbnailBackgroundButton = rootView.findViewById(R.id.detail_thumbnail_root_layout);
thumbnailImageView = rootView.findViewById(R.id.detail_thumbnail_image_view);
thumbnailPlayButton = rootView.findViewById(R.id.detail_thumbnail_play_button);
+ playerPlaceholder = rootView.findViewById(R.id.player_placeholder);
contentRootLayoutHiding = rootView.findViewById(R.id.detail_content_root_hiding);
@@ -1265,17 +1267,15 @@ public class VideoDetailFragment
return;
}
- final FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
-
// Check if viewHolder already contains a child
- if (player.getRootView().getParent() != viewHolder) {
+ if (player.getRootView().getParent() != playerPlaceholder) {
removeVideoPlayerView();
}
setHeightThumbnail();
// Prevent from re-adding a view multiple times
if (player.getRootView().getParent() == null) {
- viewHolder.addView(player.getRootView());
+ playerPlaceholder.addView(player.getRootView());
}
}
@@ -1290,9 +1290,8 @@ public class VideoDetailFragment
return;
}
- final FrameLayout viewHolder = getView().findViewById(R.id.player_placeholder);
- viewHolder.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT;
- viewHolder.requestLayout();
+ playerPlaceholder.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT;
+ playerPlaceholder.requestLayout();
}
private void prepareDescription(final Description description) {
@@ -1771,8 +1770,17 @@ public class VideoDetailFragment
final int progressSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(progress);
final int durationSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(duration);
positionView.setMax(durationSeconds);
- positionView.setProgressAnimated(progressSeconds);
- detailPositionView.setText(Localization.getDurationString(progressSeconds));
+ // If there is no player inside fragment use animation, otherwise don't because
+ // it affects CPU
+ if (playerPlaceholder.getChildCount() == 0) {
+ positionView.setProgressAnimated(progressSeconds);
+ } else {
+ positionView.setProgress(progressSeconds);
+ }
+ final String position = Localization.getDurationString(progressSeconds);
+ if (position != detailPositionView.getText()) {
+ detailPositionView.setText(position);
+ }
if (positionView.getVisibility() != View.VISIBLE) {
animateView(positionView, true, 100);
animateView(detailPositionView, true, 100);
@@ -1949,7 +1957,7 @@ public class VideoDetailFragment
(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());
+ .ofInt(0, -playerPlaceholder.getHeight());
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(animation -> {
behavior.setTopAndBottomOffset((int) animation.getAnimatedValue());
From 04bb070afa7f86b0f24f01315964ec7725afc758 Mon Sep 17 00:00:00 2001
From: nmurali94
Date: Tue, 11 Aug 2020 16:53:36 -0400
Subject: [PATCH 175/202] Remove timestamp when sharing a live stream
---
.../main/java/org/schabi/newpipe/player/VideoPlayerImpl.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
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 0f98b2296..44bdc1108 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -880,9 +880,11 @@ public class VideoPlayerImpl extends VideoPlayer
private void onShareClicked() {
// share video at the current time (youtube.com/watch?v=ID&t=SECONDS)
+ // Timestamp doesn't make sense in a live stream so drop it
+ final String ts = isLive() ? "" : ("&t=" + (getPlaybackSeekBar().getProgress() / 1000));
ShareUtils.shareUrl(service,
getVideoTitle(),
- getVideoUrl() + "&t=" + getPlaybackSeekBar().getProgress() / 1000);
+ getVideoUrl() + ts);
}
private void onPlayWithKodiClicked() {
From fb9905a89e943182bdf0d1d667b5cadd96e25fed Mon Sep 17 00:00:00 2001
From: Keegan <32858528+gkeegan@users.noreply.github.com>
Date: Fri, 14 Aug 2020 11:27:06 -0500
Subject: [PATCH 176/202] Add need for contributors to discuss possible changes
This was discussed in IRC, and should help prevent the occurrence of problems where people spend hours on a feature only for it to be rejected for miscellaneous reasons.
---
.github/CONTRIBUTING.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index f61e320c9..93af9d872 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -33,6 +33,7 @@ with your GitHub account.
## Code contribution
+* If you want to add a feature or change one, please open an issue describing your change. This gives the team and community a chance to give feedback before you spend any time on something that could be done differently or not done at all. It also prevents two contributors from working on the same thing and one being disappointed when only one user's code can be added.
* Stick to NewPipe's style conventions: follow [checkStyle](https://github.com/checkstyle/checkstyle). It will run each time you build the project.
* Do not bring non-free software (e.g. binary blobs) into the project. Also, make sure you do not introduce Google
libraries.
From 12ac5ef781bfc6b31ef264259e03676b5c2f5675 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sat, 15 Aug 2020 15:58:25 +0200
Subject: [PATCH 177/202] [regression] Close player in onPlaybackShutdown()
---
.../main/java/org/schabi/newpipe/player/BasePlayer.java | 8 --------
.../java/org/schabi/newpipe/player/VideoPlayerImpl.java | 2 +-
2 files changed, 1 insertion(+), 9 deletions(-)
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 1a8c98fd2..36484e4b0 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -1068,14 +1068,6 @@ public abstract class BasePlayer implements
registerView();
}
- @Override
- public void onPlaybackShutdown() {
- if (DEBUG) {
- Log.d(TAG, "Shutting down...");
- }
- destroy();
- }
-
/*//////////////////////////////////////////////////////////////////////////
// General Player
//////////////////////////////////////////////////////////////////////////*/
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 44bdc1108..8fdc67be3 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -622,7 +622,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (DEBUG) {
Log.d(TAG, "onPlaybackShutdown() called");
}
- // Override it because we don't want playerImpl destroyed
+ service.onDestroy();
}
@Override
From d3d65c8e3a6074a2ca0a697f2c5ebdbe61256783 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Sat, 15 Aug 2020 20:51:52 +0300
Subject: [PATCH 178/202] Set bottom padding of the main fragment when the mini
player is visible
---
.../fragments/detail/VideoDetailFragment.java | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
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 66077f3ee..39eb74a58 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
@@ -2176,6 +2176,30 @@ public class VideoDetailFragment
}
}
+ /**
+ * When the mini player exists the view underneath it is not touchable.
+ * Bottom padding should be equal to the mini player's height in this case
+ *
+ * @param showMore whether main fragment should be expanded or not
+ * */
+ private void manageSpaceAtTheBottom(final boolean showMore) {
+ final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
+ final ViewGroup holder = requireActivity().findViewById(R.id.fragment_holder);
+ final int newBottomPadding;
+ if (showMore) {
+ newBottomPadding = 0;
+ } else {
+ newBottomPadding = peekHeight;
+ }
+ if (holder.getPaddingBottom() == newBottomPadding) {
+ return;
+ }
+ holder.setPadding(holder.getPaddingLeft(),
+ holder.getPaddingTop(),
+ holder.getPaddingRight(),
+ newBottomPadding);
+ }
+
private void setupBottomPlayer() {
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
@@ -2186,6 +2210,7 @@ public class VideoDetailFragment
bottomSheetBehavior.setState(bottomSheetState);
final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
if (bottomSheetState != BottomSheetBehavior.STATE_HIDDEN) {
+ manageSpaceAtTheBottom(false);
bottomSheetBehavior.setPeekHeight(peekHeight);
if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED) {
overlay.setAlpha(MAX_OVERLAY_ALPHA);
@@ -2203,12 +2228,14 @@ public class VideoDetailFragment
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
moveFocusToMainFragment(true);
+ manageSpaceAtTheBottom(true);
bottomSheetBehavior.setPeekHeight(0);
cleanUp();
break;
case BottomSheetBehavior.STATE_EXPANDED:
moveFocusToMainFragment(false);
+ manageSpaceAtTheBottom(false);
bottomSheetBehavior.setPeekHeight(peekHeight);
// Disable click because overlay buttons located on top of buttons
From d30651331922db62cd13371e79a42bfb39102fea Mon Sep 17 00:00:00 2001
From: Xiang Rong Lin <41164160+XiangRongLin@users.noreply.github.com>
Date: Sun, 16 Aug 2020 08:42:56 +0200
Subject: [PATCH 179/202] Add checkstyle rule to show final local variable
violations as warning
---
checkstyle.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/checkstyle.xml b/checkstyle.xml
index c61b92247..af8c5480f 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -154,6 +154,10 @@
+
+
+
+
From 87228673b45225a57b53937ab2c9715d7bf205ca Mon Sep 17 00:00:00 2001
From: wb9688
Date: Sun, 16 Aug 2020 10:24:58 +0200
Subject: [PATCH 180/202] Use final where possible
---
.../schabi/newpipe/report/ErrorInfoTest.java | 6 +-
...agmentStatePagerAdapterMenuWorkaround.java | 28 +++---
.../material/appbar/FlingBehavior.java | 28 +++---
app/src/main/java/org/schabi/newpipe/App.java | 12 +--
.../newpipe/CheckForNewAppVersionTask.java | 10 +--
.../org/schabi/newpipe/DownloaderImpl.java | 38 ++++-----
.../java/org/schabi/newpipe/ExitActivity.java | 2 +-
.../java/org/schabi/newpipe/MainActivity.java | 72 ++++++++--------
.../org/schabi/newpipe/NewPipeDatabase.java | 2 +-
.../newpipe/PanicResponderActivity.java | 2 +-
.../org/schabi/newpipe/ReCaptchaActivity.java | 20 ++---
.../org/schabi/newpipe/RouterActivity.java | 32 +++----
.../schabi/newpipe/about/AboutActivity.java | 20 ++---
.../schabi/newpipe/about/LicenseFragment.java | 4 +-
.../newpipe/about/LicenseFragmentHelper.java | 2 +-
.../schabi/newpipe/database/Converters.java | 2 +-
.../subscription/SubscriptionEntity.java | 4 +-
.../newpipe/download/DownloadActivity.java | 10 +--
.../newpipe/download/DownloadDialog.java | 56 ++++++------
.../newpipe/fragments/BaseStateFragment.java | 2 +-
.../newpipe/fragments/MainFragment.java | 8 +-
.../fragments/OnScrollBelowItemsListener.java | 8 +-
.../newpipe/fragments/detail/TabAdaptor.java | 2 +-
.../fragments/detail/VideoDetailFragment.java | 52 ++++++------
.../fragments/list/BaseListFragment.java | 10 +--
.../list/channel/ChannelFragment.java | 32 +++----
.../list/comments/CommentsFragment.java | 2 +-
.../list/kiosk/DefaultKioskFragment.java | 2 +-
.../fragments/list/kiosk/KioskFragment.java | 10 +--
.../list/playlist/PlaylistFragment.java | 8 +-
.../fragments/list/search/SearchFragment.java | 48 ++++++-----
.../list/search/SuggestionListAdapter.java | 6 +-
.../list/videos/RelatedVideosFragment.java | 8 +-
.../newpipe/info_list/InfoItemBuilder.java | 3 +-
.../newpipe/info_list/InfoItemDialog.java | 4 +-
.../newpipe/info_list/InfoListAdapter.java | 14 +--
.../holder/ChannelInfoItemHolder.java | 4 +-
.../holder/CommentsMiniInfoItemHolder.java | 15 ++--
.../holder/StreamMiniInfoItemHolder.java | 5 +-
.../newpipe/local/LocalItemListAdapter.java | 8 +-
.../local/bookmark/BookmarkFragment.java | 6 +-
.../local/dialog/PlaylistAppendDialog.java | 10 +--
.../local/dialog/PlaylistCreationDialog.java | 6 +-
.../local/history/HistoryRecordManager.java | 14 +--
.../history/StatisticsPlaylistFragment.java | 2 +-
.../holder/LocalPlaylistStreamItemHolder.java | 4 +-
.../LocalStatisticStreamItemHolder.java | 4 +-
.../local/playlist/LocalPlaylistFragment.java | 14 +--
.../local/playlist/LocalPlaylistManager.java | 6 +-
.../local/playlist/RemotePlaylistManager.java | 2 +-
.../SubscriptionsImportFragment.java | 6 +-
.../services/ImportExportJsonHelper.java | 16 ++--
.../services/SubscriptionsExportService.java | 4 +-
.../services/SubscriptionsImportService.java | 8 +-
.../org/schabi/newpipe/player/BasePlayer.java | 8 +-
.../newpipe/player/ServicePlayerActivity.java | 4 +-
.../schabi/newpipe/player/VideoPlayer.java | 24 +++---
.../newpipe/player/VideoPlayerImpl.java | 14 +--
.../player/event/PlayerGestureListener.java | 18 ++--
.../newpipe/player/helper/AudioReactor.java | 2 +-
.../newpipe/player/helper/CacheFactory.java | 4 +-
.../newpipe/player/helper/LoadController.java | 2 +-
.../player/helper/MediaSessionManager.java | 4 +-
.../helper/PlaybackParameterDialog.java | 20 ++---
.../newpipe/player/helper/PlayerHelper.java | 16 ++--
.../mediasession/PlayQueueNavigator.java | 10 +--
.../playback/BasePlayerMediaSession.java | 5 +-
.../player/playback/CustomTrackSelector.java | 8 +-
.../playqueue/AbstractInfoPlayQueue.java | 2 +-
.../player/playqueue/SinglePlayQueue.java | 2 +-
.../resolver/VideoPlaybackResolver.java | 2 +-
.../schabi/newpipe/report/ErrorActivity.java | 52 ++++++------
.../settings/AppearanceSettingsFragment.java | 4 +-
.../settings/BasePreferenceFragment.java | 2 +-
.../settings/ContentSettingsFragment.java | 58 ++++++-------
.../settings/DownloadSettingsFragment.java | 25 +++---
.../newpipe/settings/NewPipeSettings.java | 6 +-
.../PeertubeInstanceListFragment.java | 30 +++----
.../settings/SelectChannelFragment.java | 12 +--
.../newpipe/settings/SelectKioskFragment.java | 12 +--
.../settings/SelectPlaylistFragment.java | 2 +-
.../newpipe/settings/SettingsActivity.java | 8 +-
.../settings/UpdateSettingsFragment.java | 2 +-
.../settings/VideoAudioSettingsFragment.java | 8 +-
.../settings/tabs/ChooseTabsFragment.java | 12 +--
.../org/schabi/newpipe/settings/tabs/Tab.java | 4 +-
.../newpipe/settings/tabs/TabsJsonHelper.java | 6 +-
.../newpipe/settings/tabs/TabsManager.java | 2 +-
.../schabi/newpipe/streams/DataReader.java | 19 +++--
.../schabi/newpipe/streams/Mp4DashReader.java | 82 +++++++++---------
.../newpipe/streams/Mp4FromDashWriter.java | 85 ++++++++++---------
.../newpipe/streams/OggFromWebMWriter.java | 22 ++---
.../newpipe/streams/SrtFromTtmlWriter.java | 16 ++--
.../schabi/newpipe/streams/WebMReader.java | 38 ++++-----
.../schabi/newpipe/streams/WebMWriter.java | 71 ++++++++--------
.../schabi/newpipe/util/AnimationUtils.java | 12 +--
.../org/schabi/newpipe/util/BitmapUtils.java | 25 +++---
.../util/CommentTextOnTouchListener.java | 36 ++++----
.../org/schabi/newpipe/util/CookieUtils.java | 4 +-
.../org/schabi/newpipe/util/DeviceUtils.java | 5 +-
.../schabi/newpipe/util/ExtractorHelper.java | 12 +--
.../schabi/newpipe/util/FilenameUtils.java | 4 +-
.../org/schabi/newpipe/util/InfoCache.java | 2 +-
.../util/LayoutManagerSmoothScroller.java | 2 +-
.../org/schabi/newpipe/util/ListHelper.java | 77 +++++++++--------
.../org/schabi/newpipe/util/Localization.java | 30 +++----
.../schabi/newpipe/util/NavigationHelper.java | 44 +++++-----
.../schabi/newpipe/util/PeertubeHelper.java | 27 +++---
.../schabi/newpipe/util/PermissionHelper.java | 9 +-
.../newpipe/util/RelatedStreamInfo.java | 6 +-
.../newpipe/util/SecondaryStreamHelper.java | 6 +-
.../schabi/newpipe/util/ServiceHelper.java | 22 ++---
.../org/schabi/newpipe/util/ShareUtils.java | 2 +-
.../org/schabi/newpipe/util/StateSaver.java | 32 +++----
.../newpipe/util/StreamDialogEntry.java | 4 +-
.../newpipe/util/StreamItemAdapter.java | 11 +--
.../newpipe/util/TLSSocketFactoryCompat.java | 4 +-
.../org/schabi/newpipe/util/ThemeHelper.java | 28 +++---
.../org/schabi/newpipe/util/ZipHelper.java | 16 ++--
.../newpipe/views/AnimatedProgressBar.java | 2 +-
.../schabi/newpipe/views/CollapsibleView.java | 2 +-
.../newpipe/views/FocusAwareDrawerLayout.java | 10 ++-
.../newpipe/views/FocusOverlayView.java | 36 ++++----
.../views/LargeTextMovementMethod.java | 64 +++++++-------
.../newpipe/views/NewPipeRecyclerView.java | 12 +--
.../newpipe/views/ScrollableTabLayout.java | 2 +-
.../views/SuperScrollLayoutManager.java | 16 ++--
.../services/ImportExportJsonHelperTest.java | 18 ++--
.../schabi/newpipe/settings/tabs/TabTest.java | 2 +-
.../settings/tabs/TabsJsonHelperTest.java | 9 +-
.../schabi/newpipe/util/ListHelperTest.java | 8 +-
checkstyle-suppressions.xml | 2 +-
132 files changed, 1024 insertions(+), 1005 deletions(-)
diff --git a/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java b/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
index 55e747cd5..972737d25 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
+++ b/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
@@ -21,13 +21,13 @@ public class ErrorInfoTest {
@Test
public void errorInfoTestParcelable() {
- ErrorInfo info = ErrorInfo.make(UserAction.USER_REPORT, "youtube", "request",
+ final ErrorInfo info = ErrorInfo.make(UserAction.USER_REPORT, "youtube", "request",
R.string.general_error);
// Obtain a Parcel object and write the parcelable object to it:
- Parcel parcel = Parcel.obtain();
+ final Parcel parcel = Parcel.obtain();
info.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- ErrorInfo infoFromParcel = ErrorInfo.CREATOR.createFromParcel(parcel);
+ final ErrorInfo infoFromParcel = ErrorInfo.CREATOR.createFromParcel(parcel);
assertEquals(UserAction.USER_REPORT, infoFromParcel.userAction);
assertEquals("youtube", infoFromParcel.serviceName);
diff --git a/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java
index 11f457b6c..5840b0f7e 100644
--- a/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java
+++ b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java
@@ -150,7 +150,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
- Fragment f = mFragments.get(position);
+ final Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
@@ -160,12 +160,12 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
mCurTransaction = mFragmentManager.beginTransaction();
}
- Fragment fragment = getItem(position);
+ final Fragment fragment = getItem(position);
if (DEBUG) {
Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
}
if (mSavedState.size() > position) {
- Fragment.SavedState fss = mSavedState.get(position);
+ final Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
@@ -191,7 +191,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
@Override
public void destroyItem(@NonNull final ViewGroup container, final int position,
@NonNull final Object object) {
- Fragment fragment = (Fragment) object;
+ final Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
@@ -217,7 +217,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull final ViewGroup container, final int position,
@NonNull final Object object) {
- Fragment fragment = (Fragment) object;
+ final Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
@@ -267,17 +267,17 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
- Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
+ final Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i = 0; i < mFragments.size(); i++) {
- Fragment f = mFragments.get(i);
+ final Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
- String key = "f" + i;
+ final String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -294,9 +294,9 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
@Override
public void restoreState(@Nullable final Parcelable state, @Nullable final ClassLoader loader) {
if (state != null) {
- Bundle bundle = (Bundle) state;
+ final Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
- Parcelable[] fss = bundle.getParcelableArray("states");
+ final Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
if (fss != null) {
@@ -304,11 +304,11 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
mSavedState.add((Fragment.SavedState) fss[i]);
}
}
- Iterable keys = bundle.keySet();
- for (String key: keys) {
+ final Iterable keys = bundle.keySet();
+ for (final String key : keys) {
if (key.startsWith("f")) {
- int index = Integer.parseInt(key.substring(1));
- Fragment f = mFragmentManager.getFragment(bundle, key);
+ final int index = Integer.parseInt(key.substring(1));
+ final Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
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 90e5edcd3..174631fde 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,19 +30,18 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
public boolean onRequestChildRectangleOnScreen(
@NonNull final CoordinatorLayout coordinatorLayout, @NonNull final AppBarLayout child,
@NonNull final Rect rectangle, final boolean immediate) {
-
focusScrollRect.set(rectangle);
coordinatorLayout.offsetDescendantRectToMyCoords(child, focusScrollRect);
- int height = coordinatorLayout.getHeight();
+ final int height = coordinatorLayout.getHeight();
if (focusScrollRect.top <= 0 && focusScrollRect.bottom >= height) {
// the child is too big to fit inside ourselves completely, ignore request
return false;
}
- int dy;
+ final int dy;
if (focusScrollRect.bottom > height) {
dy = focusScrollRect.top;
@@ -54,7 +53,7 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
return false;
}
- int consumed = scroll(coordinatorLayout, child, dy, getMaxDragOffset(child), 0);
+ final int consumed = scroll(coordinatorLayout, child, dy, getMaxDragOffset(child), 0);
return consumed == dy;
}
@@ -106,14 +105,14 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
@Nullable
private OverScroller getScrollerField() {
try {
- Class> headerBehaviorType = this.getClass()
+ final Class> headerBehaviorType = this.getClass()
.getSuperclass().getSuperclass().getSuperclass();
if (headerBehaviorType != null) {
- Field field = headerBehaviorType.getDeclaredField("scroller");
+ final Field field = headerBehaviorType.getDeclaredField("scroller");
field.setAccessible(true);
return ((OverScroller) field.get(this));
}
- } catch (NoSuchFieldException | IllegalAccessException e) {
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
// ?
}
return null;
@@ -122,34 +121,35 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
@Nullable
private Field getLastNestedScrollingChildRefField() {
try {
- Class> headerBehaviorType = this.getClass().getSuperclass().getSuperclass();
+ final Class> headerBehaviorType = this.getClass().getSuperclass().getSuperclass();
if (headerBehaviorType != null) {
- Field field = headerBehaviorType.getDeclaredField("lastNestedScrollingChildRef");
+ final Field field
+ = headerBehaviorType.getDeclaredField("lastNestedScrollingChildRef");
field.setAccessible(true);
return field;
}
- } catch (NoSuchFieldException e) {
+ } catch (final NoSuchFieldException e) {
// ?
}
return null;
}
private void resetNestedScrollingChild() {
- Field field = getLastNestedScrollingChildRefField();
+ final Field field = getLastNestedScrollingChildRefField();
if (field != null) {
try {
- Object value = field.get(this);
+ final Object value = field.get(this);
if (value != null) {
field.set(this, null);
}
- } catch (IllegalAccessException e) {
+ } catch (final IllegalAccessException e) {
// ?
}
}
}
private void stopAppBarLayoutFling() {
- OverScroller scroller = getScrollerField();
+ final OverScroller scroller = getScrollerField();
if (scroller != null) {
scroller.forceFinished(true);
}
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 0071d2efe..00842ec46 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -104,7 +104,7 @@ public class App extends Application {
}
protected Downloader getDownloader() {
- DownloaderImpl downloader = DownloaderImpl.init(null);
+ final DownloaderImpl downloader = DownloaderImpl.init(null);
setCookiesToDownloader(downloader);
return downloader;
}
@@ -208,7 +208,7 @@ public class App extends Application {
.setBuildConfigClass(BuildConfig.class)
.build();
ACRA.init(this, acraConfig);
- } catch (ACRAConfigurationException ace) {
+ } catch (final ACRAConfigurationException ace) {
ace.printStackTrace();
ErrorActivity.reportError(this,
ace,
@@ -231,10 +231,10 @@ public class App extends Application {
// Keep this below DEFAULT to avoid making noise on every notification update
final int importance = NotificationManager.IMPORTANCE_LOW;
- NotificationChannel mChannel = new NotificationChannel(id, name, importance);
+ final NotificationChannel mChannel = new NotificationChannel(id, name, importance);
mChannel.setDescription(description);
- NotificationManager mNotificationManager =
+ final NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
@@ -255,11 +255,11 @@ public class App extends Application {
final String appUpdateDescription
= getString(R.string.app_update_notification_channel_description);
- NotificationChannel appUpdateChannel
+ final NotificationChannel appUpdateChannel
= new NotificationChannel(appUpdateId, appUpdateName, importance);
appUpdateChannel.setDescription(appUpdateDescription);
- NotificationManager appUpdateNotificationManager
+ final NotificationManager appUpdateNotificationManager
= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
}
diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
index 625f514e9..0d211fdfd 100644
--- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
+++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
@@ -62,7 +62,7 @@ public class CheckForNewAppVersionTask extends AsyncTask {
try {
packageInfo = pm.getPackageInfo(packageName, flags);
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (final PackageManager.NameNotFoundException e) {
ErrorActivity.reportError(APP, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
@@ -77,7 +77,7 @@ public class CheckForNewAppVersionTask extends AsyncTask {
try {
final CertificateFactory cf = CertificateFactory.getInstance("X509");
c = (X509Certificate) cf.generateCertificate(input);
- } catch (CertificateException e) {
+ } catch (final CertificateException e) {
ErrorActivity.reportError(APP, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
@@ -86,7 +86,7 @@ public class CheckForNewAppVersionTask extends AsyncTask {
String hexString = null;
try {
- MessageDigest md = MessageDigest.getInstance("SHA1");
+ final MessageDigest md = MessageDigest.getInstance("SHA1");
final byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
@@ -167,7 +167,7 @@ public class CheckForNewAppVersionTask extends AsyncTask {
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
- } catch (JsonParserException e) {
+ } catch (final JsonParserException e) {
// connectivity problems, do not alarm user and fail silently
if (DEBUG) {
Log.w(TAG, Log.getStackTraceString(e));
@@ -187,7 +187,7 @@ public class CheckForNewAppVersionTask extends AsyncTask {
private void compareAppVersionAndShowNotification(final String versionName,
final String apkLocationUrl,
final int versionCode) {
- int notificationId = 2000;
+ final int notificationId = 2000;
if (BuildConfig.VERSION_CODE < versionCode) {
diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
index 95d3c2b7c..9cba0667b 100644
--- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
@@ -94,18 +94,18 @@ public final class DownloaderImpl extends Downloader {
private static void enableModernTLS(final OkHttpClient.Builder builder) {
try {
// get the default TrustManager
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
+ final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
- TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
- X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+ final X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
// insert our own TLSSocketFactory
- SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance();
+ final SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance();
builder.sslSocketFactory(sslSocketFactory, trustManager);
@@ -114,16 +114,16 @@ public final class DownloaderImpl extends Downloader {
// Necessary because some servers (e.g. Framatube.org)
// don't support the old cipher suites.
// https://github.com/square/okhttp/issues/4053#issuecomment-402579554
- List cipherSuites = new ArrayList<>();
+ final List cipherSuites = new ArrayList<>();
cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
- ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ final ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
.build();
builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT));
- } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ } catch (final KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
if (DEBUG) {
e.printStackTrace();
}
@@ -131,15 +131,15 @@ public final class DownloaderImpl extends Downloader {
}
public String getCookies(final String url) {
- List resultCookies = new ArrayList<>();
+ final List resultCookies = new ArrayList<>();
if (url.contains(YOUTUBE_DOMAIN)) {
- String youtubeCookie = getCookie(YOUTUBE_RESTRICTED_MODE_COOKIE_KEY);
+ final String youtubeCookie = getCookie(YOUTUBE_RESTRICTED_MODE_COOKIE_KEY);
if (youtubeCookie != null) {
resultCookies.add(youtubeCookie);
}
}
// Recaptcha cookie is always added TODO: not sure if this is necessary
- String recaptchaCookie = getCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY);
+ final String recaptchaCookie = getCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY);
if (recaptchaCookie != null) {
resultCookies.add(recaptchaCookie);
}
@@ -159,9 +159,9 @@ public final class DownloaderImpl extends Downloader {
}
public void updateYoutubeRestrictedModeCookies(final Context context) {
- String restrictedModeEnabledKey =
+ final String restrictedModeEnabledKey =
context.getString(R.string.youtube_restricted_mode_enabled);
- boolean restrictedModeEnabled = PreferenceManager.getDefaultSharedPreferences(context)
+ final boolean restrictedModeEnabled = PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(restrictedModeEnabledKey, false);
updateYoutubeRestrictedModeCookies(restrictedModeEnabled);
}
@@ -186,9 +186,9 @@ public final class DownloaderImpl extends Downloader {
try {
final Response response = head(url);
return Long.parseLong(response.getHeader("Content-Length"));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
throw new IOException("Invalid content length", e);
- } catch (ReCaptchaException e) {
+ } catch (final ReCaptchaException e) {
throw new IOException(e);
}
}
@@ -199,7 +199,7 @@ public final class DownloaderImpl extends Downloader {
.method("GET", null).url(siteUrl)
.addHeader("User-Agent", USER_AGENT);
- String cookies = getCookies(siteUrl);
+ final String cookies = getCookies(siteUrl);
if (!cookies.isEmpty()) {
requestBuilder.addHeader("Cookie", cookies);
}
@@ -218,7 +218,7 @@ public final class DownloaderImpl extends Downloader {
}
return body.byteStream();
- } catch (ReCaptchaException e) {
+ } catch (final ReCaptchaException e) {
throw new IOException(e.getMessage(), e.getCause());
}
}
@@ -240,18 +240,18 @@ public final class DownloaderImpl extends Downloader {
.method(httpMethod, requestBody).url(url)
.addHeader("User-Agent", USER_AGENT);
- String cookies = getCookies(url);
+ final String cookies = getCookies(url);
if (!cookies.isEmpty()) {
requestBuilder.addHeader("Cookie", cookies);
}
- for (Map.Entry> pair : headers.entrySet()) {
+ for (final Map.Entry> pair : headers.entrySet()) {
final String headerName = pair.getKey();
final List headerValueList = pair.getValue();
if (headerValueList.size() > 1) {
requestBuilder.removeHeader(headerName);
- for (String headerValue : headerValueList) {
+ for (final String headerValue : headerValueList) {
requestBuilder.addHeader(headerName, headerValue);
}
} else if (headerValueList.size() == 1) {
diff --git a/app/src/main/java/org/schabi/newpipe/ExitActivity.java b/app/src/main/java/org/schabi/newpipe/ExitActivity.java
index 94eff9560..d4a4e3125 100644
--- a/app/src/main/java/org/schabi/newpipe/ExitActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/ExitActivity.java
@@ -27,7 +27,7 @@ import android.os.Bundle;
public class ExitActivity extends Activity {
public static void exitAndRemoveFromRecentApps(final Activity activity) {
- Intent intent = new Intent(activity, ExitActivity.class);
+ final Intent intent = new Intent(activity, ExitActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index fb1ca2342..c3086d02c 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -140,7 +140,7 @@ public class MainActivity extends AppCompatActivity {
setSupportActionBar(findViewById(R.id.toolbar));
try {
setupDrawer();
- } catch (Exception e) {
+ } catch (final Exception e) {
ErrorActivity.reportUiError(this, e);
}
@@ -155,8 +155,8 @@ public class MainActivity extends AppCompatActivity {
drawerItems = findViewById(R.id.navigation);
//Tabs
- int currentServiceId = ServiceHelper.getSelectedServiceId(this);
- StreamingService service = NewPipe.getService(currentServiceId);
+ final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
+ final StreamingService service = NewPipe.getService(currentServiceId);
int kioskId = 0;
@@ -228,7 +228,7 @@ public class MainActivity extends AppCompatActivity {
case R.id.menu_tabs_group:
try {
tabSelected(item);
- } catch (Exception e) {
+ } catch (final Exception e) {
ErrorActivity.reportUiError(this, e);
}
break;
@@ -269,8 +269,8 @@ public class MainActivity extends AppCompatActivity {
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
break;
default:
- int currentServiceId = ServiceHelper.getSelectedServiceId(this);
- StreamingService service = NewPipe.getService(currentServiceId);
+ final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
+ final StreamingService service = NewPipe.getService(currentServiceId);
String serviceName = "";
int kioskId = 0;
@@ -299,8 +299,8 @@ public class MainActivity extends AppCompatActivity {
}
private void setupDrawerHeader() {
- NavigationView navigationView = findViewById(R.id.navigation);
- View hView = navigationView.getHeaderView(0);
+ final NavigationView navigationView = findViewById(R.id.navigation);
+ final View hView = navigationView.getHeaderView(0);
serviceArrow = hView.findViewById(R.id.drawer_arrow);
headerServiceIcon = hView.findViewById(R.id.drawer_header_service_icon);
@@ -335,7 +335,7 @@ public class MainActivity extends AppCompatActivity {
} else {
try {
showTabs();
- } catch (Exception e) {
+ } catch (final Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
@@ -344,11 +344,11 @@ public class MainActivity extends AppCompatActivity {
private void showServices() {
serviceArrow.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp);
- for (StreamingService s : NewPipe.getServices()) {
+ for (final StreamingService s : NewPipe.getServices()) {
final String title = s.getServiceInfo().getName()
+ (ServiceHelper.isBeta(s) ? " (beta)" : "");
- MenuItem menuItem = drawerItems.getMenu()
+ final MenuItem menuItem = drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
@@ -362,20 +362,20 @@ public class MainActivity extends AppCompatActivity {
}
private void enhancePeertubeMenu(final StreamingService s, final MenuItem menuItem) {
- PeertubeInstance currentInstace = PeertubeHelper.getCurrentInstance();
+ final PeertubeInstance currentInstace = PeertubeHelper.getCurrentInstance();
menuItem.setTitle(currentInstace.getName() + (ServiceHelper.isBeta(s) ? " (beta)" : ""));
- Spinner spinner = (Spinner) LayoutInflater.from(this)
+ final Spinner spinner = (Spinner) LayoutInflater.from(this)
.inflate(R.layout.instance_spinner_layout, null);
- List instances = PeertubeHelper.getInstanceList(this);
- List items = new ArrayList<>();
+ final List instances = PeertubeHelper.getInstanceList(this);
+ final List items = new ArrayList<>();
int defaultSelect = 0;
- for (PeertubeInstance instance : instances) {
+ for (final PeertubeInstance instance : instances) {
items.add(instance.getName());
if (instance.getUrl().equals(currentInstace.getUrl())) {
defaultSelect = items.size() - 1;
}
}
- ArrayAdapter adapter = new ArrayAdapter<>(this,
+ final ArrayAdapter adapter = new ArrayAdapter<>(this,
R.layout.instance_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
@@ -384,7 +384,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onItemSelected(final AdapterView> parent, final View view,
final int position, final long id) {
- PeertubeInstance newInstance = instances.get(position);
+ final PeertubeInstance newInstance = instances.get(position);
if (newInstance.getUrl().equals(PeertubeHelper.getCurrentInstance().getUrl())) {
return;
}
@@ -410,8 +410,8 @@ public class MainActivity extends AppCompatActivity {
serviceArrow.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp);
//Tabs
- int currentServiceId = ServiceHelper.getSelectedServiceId(this);
- StreamingService service = NewPipe.getService(currentServiceId);
+ final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
+ final StreamingService service = NewPipe.getService(currentServiceId);
int kioskId = 0;
@@ -476,11 +476,12 @@ public class MainActivity extends AppCompatActivity {
headerServiceView.post(() -> headerServiceView.setSelected(true));
toggleServiceButton.setContentDescription(
getString(R.string.drawer_header_description) + selectedServiceName);
- } catch (Exception e) {
+ } catch (final Exception e) {
ErrorActivity.reportUiError(this, e);
}
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ final SharedPreferences sharedPreferences
+ = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences.getBoolean(Constants.KEY_THEME_CHANGE, false)) {
if (DEBUG) {
Log.d(TAG, "Theme has changed, recreating activity...");
@@ -513,7 +514,7 @@ public class MainActivity extends AppCompatActivity {
if (intent != null) {
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
// to not destroy the already created backstack
- String action = intent.getAction();
+ final String action = intent.getAction();
if ((action != null && action.equals(Intent.ACTION_MAIN))
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
return;
@@ -546,7 +547,7 @@ public class MainActivity extends AppCompatActivity {
}
if (DeviceUtils.isTv(this)) {
- View drawerPanel = findViewById(R.id.navigation);
+ final View drawerPanel = findViewById(R.id.navigation);
if (drawer.isDrawerOpen(drawerPanel)) {
drawer.closeDrawers();
return;
@@ -594,7 +595,7 @@ public class MainActivity extends AppCompatActivity {
public void onRequestPermissionsResult(final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults) {
- for (int i : grantResults) {
+ for (final int i : grantResults) {
if (i == PackageManager.PERMISSION_DENIED) {
return;
}
@@ -604,7 +605,7 @@ public class MainActivity extends AppCompatActivity {
NavigationHelper.openDownloads(this);
break;
case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE:
- Fragment fragment = getSupportFragmentManager()
+ final Fragment fragment = getSupportFragmentManager()
.findFragmentById(R.id.fragment_player_holder);
if (fragment instanceof VideoDetailFragment) {
((VideoDetailFragment) fragment).openDownloadDialog();
@@ -656,13 +657,14 @@ public class MainActivity extends AppCompatActivity {
}
super.onCreateOptionsMenu(menu);
- Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
+ final Fragment fragment
+ = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
if (!(fragment instanceof SearchFragment)) {
findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container)
.setVisibility(View.GONE);
}
- ActionBar actionBar = getSupportActionBar();
+ final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
}
@@ -677,7 +679,7 @@ public class MainActivity extends AppCompatActivity {
if (DEBUG) {
Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
}
- int id = item.getItemId();
+ final int id = item.getItemId();
switch (id) {
case android.R.id.home:
@@ -745,13 +747,13 @@ public class MainActivity extends AppCompatActivity {
}
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
- String url = intent.getStringExtra(Constants.KEY_URL);
- int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
- String title = intent.getStringExtra(Constants.KEY_TITLE);
+ final String url = intent.getStringExtra(Constants.KEY_URL);
+ final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
+ final String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent
.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
- boolean autoPlay = intent
+ final boolean autoPlay = intent
.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
final String intentCacheKey = intent
.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY);
@@ -780,7 +782,7 @@ public class MainActivity extends AppCompatActivity {
if (searchString == null) {
searchString = "";
}
- int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
+ final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(
getSupportFragmentManager(),
serviceId,
@@ -789,7 +791,7 @@ public class MainActivity extends AppCompatActivity {
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
}
- } catch (Exception e) {
+ } catch (final Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
index c59c48367..988a5ed98 100644
--- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
+++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
@@ -46,7 +46,7 @@ public final class NewPipeDatabase {
if (databaseInstance == null) {
throw new IllegalStateException("database is not initialized");
}
- Cursor c = databaseInstance.query("pragma wal_checkpoint(full)", null);
+ final Cursor c = databaseInstance.query("pragma wal_checkpoint(full)", null);
if (c.moveToFirst() && c.getInt(0) == 1) {
throw new RuntimeException("Checkpoint was blocked from completing");
}
diff --git a/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java b/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
index 2e1abd598..75304a2b9 100644
--- a/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
@@ -31,7 +31,7 @@ public class PanicResponderActivity extends Activity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Intent intent = getIntent();
+ final Intent intent = getIntent();
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
// TODO: Explicitly clear the search results
// once they are restored when the app restarts
diff --git a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
index 40ea4fd58..c962ed99d 100644
--- a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
@@ -61,7 +61,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recaptcha);
- Toolbar toolbar = findViewById(R.id.toolbar);
+ final Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
String url = getIntent().getStringExtra(RECAPTCHA_URL_EXTRA);
@@ -76,7 +76,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
webView = findViewById(R.id.reCaptchaWebView);
// enable Javascript
- WebSettings webSettings = webView.getSettings();
+ final WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@@ -84,7 +84,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
@Override
public boolean shouldOverrideUrlLoading(final WebView view,
final WebResourceRequest request) {
- String url = request.getUrl().toString();
+ final String url = request.getUrl().toString();
if (MainActivity.DEBUG) {
Log.d(TAG, "shouldOverrideUrlLoading: request.url=" + url);
}
@@ -113,7 +113,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
// cleaning cache, history and cookies from webView
webView.clearCache(true);
webView.clearHistory();
- android.webkit.CookieManager cookieManager = CookieManager.getInstance();
+ final android.webkit.CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.removeAllCookies(aBoolean -> {
});
@@ -128,7 +128,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu_recaptcha, menu);
- ActionBar actionBar = getSupportActionBar();
+ final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setTitle(R.string.title_activity_recaptcha);
@@ -145,7 +145,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
- int id = item.getItemId();
+ final int id = item.getItemId();
switch (id) {
case R.id.menu_item_done:
saveCookiesAndFinish();
@@ -173,7 +173,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
setResult(RESULT_OK);
}
- Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class);
+ final Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
}
@@ -188,13 +188,13 @@ public class ReCaptchaActivity extends AppCompatActivity {
return;
}
- String cookies = CookieManager.getInstance().getCookie(url);
+ final String cookies = CookieManager.getInstance().getCookie(url);
handleCookies(cookies);
// sometimes cookies are inside the url
- int abuseStart = url.indexOf("google_abuse=");
+ final int abuseStart = url.indexOf("google_abuse=");
if (abuseStart != -1) {
- int abuseEnd = url.indexOf("+path");
+ final int abuseEnd = url.indexOf("+path");
try {
String abuseCookie = url.substring(abuseStart + 13, abuseEnd);
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index e9e166c22..70fceaf07 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -310,7 +310,7 @@ public class RouterActivity extends AppCompatActivity {
};
int id = 12345;
- for (AdapterChoiceItem item : choices) {
+ for (final AdapterChoiceItem item : choices) {
final RadioButton radioButton
= (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
radioButton.setText(item.description);
@@ -330,7 +330,7 @@ public class RouterActivity extends AppCompatActivity {
getString(R.string.preferred_open_action_last_selected_key), null);
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
for (int i = 0; i < choices.size(); i++) {
- AdapterChoiceItem c = choices.get(i);
+ final AdapterChoiceItem c = choices.get(i);
if (lastSelectedPlayer.equals(c.key)) {
selectedRadioPosition = i;
break;
@@ -362,9 +362,9 @@ public class RouterActivity extends AppCompatActivity {
final SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(this);
- boolean isExtVideoEnabled = preferences.getBoolean(
+ final boolean isExtVideoEnabled = preferences.getBoolean(
getString(R.string.use_external_video_player_key), false);
- boolean isExtAudioEnabled = preferences.getBoolean(
+ final boolean isExtAudioEnabled = preferences.getBoolean(
getString(R.string.use_external_audio_player_key), false);
returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key),
@@ -410,9 +410,9 @@ public class RouterActivity extends AppCompatActivity {
}
private void handleText() {
- String searchString = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- int serviceId = getIntent().getIntExtra(Constants.KEY_SERVICE_ID, 0);
- Intent intent = new Intent(getThemeWrapperContext(), MainActivity.class);
+ final String searchString = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+ final int serviceId = getIntent().getIntExtra(Constants.KEY_SERVICE_ID, 0);
+ final Intent intent = new Intent(getThemeWrapperContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
NavigationHelper.openSearch(getThemeWrapperContext(), serviceId, searchString);
@@ -479,14 +479,14 @@ public class RouterActivity extends AppCompatActivity {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> {
- List sortedVideoStreams = ListHelper
+ final List sortedVideoStreams = ListHelper
.getSortedStreamVideosList(this, result.getVideoStreams(),
result.getVideoOnlyStreams(), false);
- int selectedVideoStreamIndex = ListHelper
+ final int selectedVideoStreamIndex = ListHelper
.getDefaultResolutionIndex(this, sortedVideoStreams);
- FragmentManager fm = getSupportFragmentManager();
- DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
+ final FragmentManager fm = getSupportFragmentManager();
+ final DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(result.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
@@ -504,7 +504,7 @@ public class RouterActivity extends AppCompatActivity {
public void onRequestPermissionsResult(final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults) {
- for (int i : grantResults) {
+ for (final int i : grantResults) {
if (i == PackageManager.PERMISSION_DENIED) {
finish();
return;
@@ -634,7 +634,7 @@ public class RouterActivity extends AppCompatActivity {
if (!(serializable instanceof Choice)) {
return;
}
- Choice playerChoice = (Choice) serializable;
+ final Choice playerChoice = (Choice) serializable;
handleChoice(playerChoice);
}
@@ -682,13 +682,13 @@ public class RouterActivity extends AppCompatActivity {
final SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(this);
- boolean isExtVideoEnabled = preferences.getBoolean(
+ final boolean isExtVideoEnabled = preferences.getBoolean(
getString(R.string.use_external_video_player_key), false);
- boolean isExtAudioEnabled = preferences.getBoolean(
+ final boolean isExtAudioEnabled = preferences.getBoolean(
getString(R.string.use_external_audio_player_key), false);
PlayQueue playQueue;
- String playerChoice = choice.playerChoice;
+ final String playerChoice = choice.playerChoice;
if (info instanceof StreamInfo) {
if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
index b5be2dde6..3041c3d6c 100644
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
@@ -88,7 +88,7 @@ public class AboutActivity extends AppCompatActivity {
setContentView(R.layout.activity_about);
- Toolbar toolbar = findViewById(R.id.toolbar);
+ final Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Create the adapter that will return a fragment for each of the three
@@ -99,13 +99,13 @@ public class AboutActivity extends AppCompatActivity {
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
- TabLayout tabLayout = findViewById(R.id.tabs);
+ final TabLayout tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
- int id = item.getItemId();
+ final int id = item.getItemId();
switch (id) {
case android.R.id.home:
@@ -134,25 +134,25 @@ public class AboutActivity extends AppCompatActivity {
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_about, container, false);
- Context context = this.getContext();
+ final View rootView = inflater.inflate(R.layout.fragment_about, container, false);
+ final Context context = this.getContext();
- TextView version = rootView.findViewById(R.id.app_version);
+ final TextView version = rootView.findViewById(R.id.app_version);
version.setText(BuildConfig.VERSION_NAME);
- View githubLink = rootView.findViewById(R.id.github_link);
+ final View githubLink = rootView.findViewById(R.id.github_link);
githubLink.setOnClickListener(nv ->
openUrlInBrowser(context, context.getString(R.string.github_url)));
- View donationLink = rootView.findViewById(R.id.donation_link);
+ final View donationLink = rootView.findViewById(R.id.donation_link);
donationLink.setOnClickListener(v ->
openUrlInBrowser(context, context.getString(R.string.donation_url)));
- View websiteLink = rootView.findViewById(R.id.website_link);
+ final View websiteLink = rootView.findViewById(R.id.website_link);
websiteLink.setOnClickListener(nv ->
openUrlInBrowser(context, context.getString(R.string.website_url)));
- View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
+ final View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
privacyPolicyLink.setOnClickListener(v ->
openUrlInBrowser(context, context.getString(R.string.privacy_policy_url)));
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
index bc6310601..404f12602 100644
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
@@ -31,8 +31,8 @@ public class LicenseFragment extends Fragment {
if (softwareComponents == null) {
throw new NullPointerException("softwareComponents is null");
}
- LicenseFragment fragment = new LicenseFragment();
- Bundle bundle = new Bundle();
+ final LicenseFragment fragment = new LicenseFragment();
+ final Bundle bundle = new Bundle();
bundle.putParcelableArray(ARG_COMPONENTS, softwareComponents);
fragment.setArguments(bundle);
return fragment;
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
index 1c425567f..01a01bc88 100644
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
@@ -51,7 +51,7 @@ public class LicenseFragmentHelper extends AsyncTask