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