Merge pull request #4272 from avently/small-fixes2

Small fixes of issues with old devices support, brightness, etc
This commit is contained in:
Tobias Groza 2020-09-27 15:31:02 +02:00 committed by GitHub
commit 541eb70b9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 443 additions and 364 deletions

View File

@ -11,6 +11,7 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
@ -102,13 +103,12 @@ import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.SerializedCache;
import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.AnimatedProgressBar;
import org.schabi.newpipe.views.LargeTextMovementMethod;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@ -125,6 +125,7 @@ import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -337,7 +338,7 @@ public class VideoDetailFragment
stopPlayerListener();
playerService = null;
player = null;
saveCurrentAndRestoreDefaultBrightness();
restoreDefaultBrightness();
}
}
@ -404,7 +405,7 @@ public class VideoDetailFragment
settingsContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(final boolean selfChange) {
if (activity != null && !PlayerHelper.globalScreenOrientationLocked(activity)) {
if (activity != null && !globalScreenOrientationLocked(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
@ -426,7 +427,7 @@ public class VideoDetailFragment
if (currentWorker != null) {
currentWorker.dispose();
}
saveCurrentAndRestoreDefaultBrightness();
restoreDefaultBrightness();
PreferenceManager.getDefaultSharedPreferences(requireContext())
.edit()
.putString(getString(R.string.stream_info_selected_tab_key),
@ -538,31 +539,51 @@ public class VideoDetailFragment
super.onSaveInstanceState(outState);
if (!isLoading.get() && currentInfo != null && isVisible()) {
outState.putSerializable(INFO_KEY, currentInfo);
final String infoCacheKey = SerializedCache.getInstance()
.put(currentInfo, StreamInfo.class);
if (infoCacheKey != null) {
outState.putString(INFO_KEY, infoCacheKey);
}
}
if (playQueue != null) {
outState.putSerializable(VideoPlayer.PLAY_QUEUE_KEY, playQueue);
final String queueCacheKey = SerializedCache.getInstance()
.put(playQueue, PlayQueue.class);
if (queueCacheKey != null) {
outState.putString(VideoPlayer.PLAY_QUEUE_KEY, queueCacheKey);
}
}
final String stackCacheKey = SerializedCache.getInstance().put(stack, LinkedList.class);
if (stackCacheKey != null) {
outState.putString(STACK_KEY, stackCacheKey);
}
outState.putSerializable(STACK_KEY, stack);
}
@Override
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
super.onRestoreInstanceState(savedState);
Serializable serializable = savedState.getSerializable(INFO_KEY);
if (serializable instanceof StreamInfo) {
currentInfo = (StreamInfo) serializable;
InfoCache.getInstance().putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
final String infoCacheKey = savedState.getString(INFO_KEY);
if (infoCacheKey != null) {
currentInfo = SerializedCache.getInstance().take(infoCacheKey, StreamInfo.class);
if (currentInfo != null) {
InfoCache.getInstance()
.putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
}
}
serializable = savedState.getSerializable(STACK_KEY);
if (serializable instanceof Collection) {
//noinspection unchecked
stack.addAll((Collection<? extends StackItem>) serializable);
final String stackCacheKey = savedState.getString(STACK_KEY);
if (stackCacheKey != null) {
final LinkedList<StackItem> cachedStack =
SerializedCache.getInstance().take(stackCacheKey, LinkedList.class);
if (cachedStack != null) {
stack.addAll(cachedStack);
}
}
final String queueCacheKey = savedState.getString(VideoPlayer.PLAY_QUEUE_KEY);
if (queueCacheKey != null) {
playQueue = SerializedCache.getInstance().take(queueCacheKey, PlayQueue.class);
}
playQueue = (PlayQueue) savedState.getSerializable(VideoPlayer.PLAY_QUEUE_KEY);
}
/*//////////////////////////////////////////////////////////////////////////
@ -1808,9 +1829,6 @@ public class VideoDetailFragment
setOverlayPlayPauseImage();
switch (state) {
case BasePlayer.STATE_COMPLETED:
restoreDefaultOrientation();
break;
case BasePlayer.STATE_PLAYING:
if (positionView.getAlpha() != 1.0f
&& player.getPlayQueue() != null
@ -1869,10 +1887,11 @@ public class VideoDetailFragment
public void onPlayerError(final ExoPlaybackException error) {
if (error.type == ExoPlaybackException.TYPE_SOURCE
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
hideMainPlayer();
// Properly exit from fullscreen
if (playerService != null && player.isFullscreen()) {
player.toggleFullscreen();
}
hideMainPlayer();
}
}
@ -1911,7 +1930,13 @@ public class VideoDetailFragment
}
scrollToTop();
addVideoPlayerView();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addVideoPlayerView();
} else {
// KitKat needs a delay before addVideoPlayerView call or it reports wrong height in
// activity.getWindow().getDecorView().getHeight()
new Handler().post(this::addVideoPlayerView);
}
}
@Override
@ -1919,13 +1944,15 @@ public class VideoDetailFragment
// In tablet user experience will be better if screen will not be rotated
// from landscape to portrait every time.
// Just turn on fullscreen mode in landscape orientation
if (isLandscape() && DeviceUtils.isTablet(activity)) {
// or portrait & unlocked global orientation
if (DeviceUtils.isTablet(activity)
&& (!globalScreenOrientationLocked(activity) || isLandscape())) {
player.toggleFullscreen();
return;
}
final int newOrientation = isLandscape()
? ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
activity.setRequestedOrientation(newOrientation);
@ -1970,7 +1997,11 @@ public class VideoDetailFragment
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
}
activity.getWindow().getDecorView().setSystemUiVisibility(0);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(ThemeHelper.resolveColorFromAttr(
requireContext(), android.R.attr.colorPrimary));
}
}
private void hideSystemUi() {
@ -1985,18 +2016,26 @@ public class VideoDetailFragment
// Prevent jumping of the player on devices with cutout
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
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;
// In multiWindow mode status bar is not transparent for devices with cutout
// if I include this flag. So without it is better in this case
if (!isInMultiWindow()) {
visibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
activity.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& (isInMultiWindow() || (player != null && player.isFullscreen()))) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
// Listener implementation
@ -2014,13 +2053,11 @@ public class VideoDetailFragment
&& player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
}
private void saveCurrentAndRestoreDefaultBrightness() {
private void restoreDefaultBrightness() {
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
if (lp.screenBrightness == -1) {
return;
}
// Save current brightness level
PlayerHelper.setScreenBrightness(activity, lp.screenBrightness);
// Restore the old brightness when fragment.onPause() called or
// when a player is in portrait
@ -2039,7 +2076,7 @@ public class VideoDetailFragment
|| !player.isFullscreen()
|| bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) {
// Apply system brightness when the player is not in fullscreen
saveCurrentAndRestoreDefaultBrightness();
restoreDefaultBrightness();
} else {
// Restore already saved brightness level
final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
@ -2058,11 +2095,9 @@ public class VideoDetailFragment
}
player.checkLandscape();
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
// Let's give a user time to look at video information page if video is not playing
if (orientationLocked && !player.isPlaying()) {
if (globalScreenOrientationLocked(activity) && !player.isPlaying()) {
player.onPlay();
player.showControlsThenHide();
}
}
@ -2265,6 +2300,7 @@ public class VideoDetailFragment
&& player.videoPlayerSelected()) {
player.toggleFullscreen();
}
setOverlayLook(appBarLayout, behavior, 1);
break;
case BottomSheetBehavior.STATE_COLLAPSED:
moveFocusToMainFragment(true);
@ -2274,6 +2310,7 @@ public class VideoDetailFragment
if (player != null) {
player.onQueueClosed();
}
setOverlayLook(appBarLayout, behavior, 0);
break;
case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_SETTLING:

View File

@ -147,6 +147,7 @@ public final class MainPlayer extends Service {
// Android TV will handle back button in case controls will be visible
// (one more additional unneeded click while the player is hidden)
playerImpl.hideControls(0, 0);
playerImpl.onQueueClosed();
// 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.
@ -195,6 +196,10 @@ public final class MainPlayer extends Service {
}
if (playerImpl != null) {
// Exit from fullscreen when user closes the player via notification
if (playerImpl.isFullscreen()) {
playerImpl.toggleFullscreen();
}
removeViewFromParent();
playerImpl.setRecovery();

View File

@ -128,6 +128,8 @@ public abstract class VideoPlayer extends BasePlayer
private View controlsRoot;
private TextView currentDisplaySeek;
private View playerTopShadow;
private View playerBottomShadow;
private View bottomControlsRoot;
private SeekBar playbackSeekBar;
@ -190,6 +192,8 @@ public abstract class VideoPlayer extends BasePlayer
this.controlAnimationView = view.findViewById(R.id.controlAnimationView);
this.controlsRoot = view.findViewById(R.id.playbackControlRoot);
this.currentDisplaySeek = view.findViewById(R.id.currentDisplaySeek);
this.playerTopShadow = view.findViewById(R.id.playerTopShadow);
this.playerBottomShadow = view.findViewById(R.id.playerBottomShadow);
this.playbackSeekBar = view.findViewById(R.id.playbackSeekBar);
this.playbackCurrentTime = view.findViewById(R.id.playbackCurrentTime);
this.playbackEndTime = view.findViewById(R.id.playbackEndTime);
@ -356,11 +360,11 @@ public abstract class VideoPlayer extends BasePlayer
return true;
});
// apply caption language from previous user preference
if (userPreferredLanguage != null && (captionLanguage.equals(userPreferredLanguage)
|| searchForAutogenerated && captionLanguage.startsWith(userPreferredLanguage)
|| userPreferredLanguage.contains("(") && captionLanguage.startsWith(
userPreferredLanguage
.substring(0, userPreferredLanguage.indexOf('('))))) {
if (userPreferredLanguage != null
&& (captionLanguage.equals(userPreferredLanguage)
|| (searchForAutogenerated && captionLanguage.startsWith(userPreferredLanguage))
|| (userPreferredLanguage.contains("(") && captionLanguage.startsWith(
userPreferredLanguage.substring(0, userPreferredLanguage.indexOf('(')))))) {
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
if (textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setPreferredTextLanguage(captionLanguage);
@ -754,7 +758,6 @@ public abstract class VideoPlayer extends BasePlayer
}
qualityPopupMenu.show();
isSomePopupMenuVisible = true;
showControls(DEFAULT_CONTROLS_DURATION);
final VideoStream videoStream = getSelectedVideoStream();
if (videoStream != null) {
@ -772,7 +775,6 @@ public abstract class VideoPlayer extends BasePlayer
}
playbackSpeedPopupMenu.show();
isSomePopupMenuVisible = true;
showControls(DEFAULT_CONTROLS_DURATION);
}
private void onCaptionClicked() {
@ -781,7 +783,6 @@ public abstract class VideoPlayer extends BasePlayer
}
captionPopupMenu.show();
isSomePopupMenuVisible = true;
showControls(DEFAULT_CONTROLS_DURATION);
}
void onResizeClicked() {
@ -958,6 +959,7 @@ public abstract class VideoPlayer extends BasePlayer
? DEFAULT_CONTROLS_HIDE_TIME
: DPAD_CONTROLS_HIDE_TIME;
showHideShadow(true, DEFAULT_CONTROLS_DURATION, 0);
animateView(controlsRoot, true, DEFAULT_CONTROLS_DURATION, 0,
() -> hideControls(DEFAULT_CONTROLS_DURATION, hideTime));
}
@ -967,6 +969,7 @@ public abstract class VideoPlayer extends BasePlayer
Log.d(TAG, "showControls() called");
}
controlsVisibilityHandler.removeCallbacksAndMessages(null);
showHideShadow(true, duration, 0);
animateView(controlsRoot, true, duration);
}
@ -986,8 +989,10 @@ public abstract class VideoPlayer extends BasePlayer
Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
}
controlsVisibilityHandler.removeCallbacksAndMessages(null);
controlsVisibilityHandler.postDelayed(() ->
animateView(controlsRoot, false, duration), delay);
controlsVisibilityHandler.postDelayed(() -> {
showHideShadow(false, duration, 0);
animateView(controlsRoot, false, duration);
}, delay);
}
public void hideControlsAndButton(final long duration, final long delay, final View button) {
@ -1006,6 +1011,11 @@ public abstract class VideoPlayer extends BasePlayer
};
}
void showHideShadow(final boolean show, final long duration, final long delay) {
animateView(playerTopShadow, show, duration, delay, null);
animateView(playerBottomShadow, show, duration, delay, null);
}
public abstract void hideSystemUIIfNeeded();
/*//////////////////////////////////////////////////////////////////////////

View File

@ -27,22 +27,21 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.view.DisplayCutout;
import androidx.preference.PreferenceManager;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@ -104,7 +103,6 @@ import org.schabi.newpipe.util.ShareUtils;
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.ACTION_CLOSE;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
@ -116,6 +114,7 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION;
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
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;
@ -138,7 +137,6 @@ public class VideoPlayerImpl extends VideoPlayer
static final String POPUP_SAVED_WIDTH = "popup_saved_width";
static final String POPUP_SAVED_X = "popup_saved_x";
static final String POPUP_SAVED_Y = "popup_saved_y";
private static final int MINIMUM_SHOW_EXTRA_WIDTH_DP = 300;
private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS
@ -254,6 +252,7 @@ public class VideoPlayerImpl extends VideoPlayer
} else {
getRootView().setVisibility(View.VISIBLE);
initVideoPlayer();
onQueueClosed();
// Android TV: without it focus will frame the whole player
playPauseButton.requestFocus();
}
@ -310,6 +309,9 @@ public class VideoPlayerImpl extends VideoPlayer
titleTextView.setSelected(true);
channelTextView.setSelected(true);
// Prevent hiding of bottom sheet via swipe inside queue
this.itemsList.setNestedScrollingEnabled(false);
}
@Override
@ -334,7 +336,6 @@ 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()) {
@ -469,6 +470,17 @@ public class VideoPlayerImpl extends VideoPlayer
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
settingsContentObserver);
getRootView().addOnLayoutChangeListener(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
queueLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> {
final DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout != null) {
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
}
return windowInsets;
});
}
}
public boolean onKeyDown(final int keyCode) {
@ -688,53 +700,33 @@ public class VideoPlayerImpl extends VideoPlayer
}
/*//////////////////////////////////////////////////////////////////////////
// Player Overrides
//////////////////////////////////////////////////////////////////////////*/
// Player Overrides
//////////////////////////////////////////////////////////////////////////*/
@Override
public void toggleFullscreen() {
if (DEBUG) {
Log.d(TAG, "toggleFullscreen() called");
}
if (simpleExoPlayer == null || getCurrentMetadata() == null) {
if (popupPlayerSelected()
|| simpleExoPlayer == null
|| getCurrentMetadata() == null
|| fragmentListener == null) {
return;
}
if (popupPlayerSelected()) {
setRecovery();
service.removeViewFromParent();
final Intent intent = NavigationHelper.getPlayerIntent(
service,
MainActivity.class,
this.getPlayQueue(),
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
null,
true,
!isPlaying(),
isMuted()
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_SERVICE_ID,
getCurrentMetadata().getMetadata().getServiceId());
intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
intent.putExtra(Constants.KEY_URL, getVideoUrl());
intent.putExtra(Constants.KEY_TITLE, getVideoTitle());
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
service.onDestroy();
context.startActivity(intent);
return;
isFullscreen = !isFullscreen;
if (!isFullscreen) {
// Apply window insets because Android will not do it when orientation changes
// from landscape to portrait (open vertical video to reproduce)
getControlsRoot().setPadding(0, 0, 0, 0);
} else {
if (fragmentListener == null) {
return;
}
isFullscreen = !isFullscreen;
setControlsSize();
fragmentListener.onFullscreenStateChanged(isFullscreen());
// Android needs tens milliseconds to send new insets but a user is able to see
// how controls changes it's position from `0` to `nav bar height` padding.
// So just hide the controls to hide this visual inconsistency
hideControls(0, 0);
}
fragmentListener.onFullscreenStateChanged(isFullscreen());
if (!isFullscreen()) {
titleTextView.setVisibility(View.GONE);
@ -748,6 +740,40 @@ public class VideoPlayerImpl extends VideoPlayer
setupScreenRotationButton();
}
public void switchFromPopupToMain() {
if (DEBUG) {
Log.d(TAG, "switchFromPopupToMain() called");
}
if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) {
return;
}
setRecovery();
service.removeViewFromParent();
final Intent intent = NavigationHelper.getPlayerIntent(
service,
MainActivity.class,
this.getPlayQueue(),
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
null,
true,
!isPlaying(),
isMuted()
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_SERVICE_ID,
getCurrentMetadata().getMetadata().getServiceId());
intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
intent.putExtra(Constants.KEY_URL, getVideoUrl());
intent.putExtra(Constants.KEY_TITLE, getVideoTitle());
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
service.onDestroy();
context.startActivity(intent);
}
@Override
public void onClick(final View v) {
super.onClick(v);
@ -775,9 +801,12 @@ public class VideoPlayerImpl extends VideoPlayer
} else if (v.getId() == openInBrowser.getId()) {
onOpenInBrowserClicked();
} else if (v.getId() == fullscreenButton.getId()) {
toggleFullscreen();
switchFromPopupToMain();
} else if (v.getId() == screenRotationButton.getId()) {
if (!isVerticalVideo) {
// Only if it's not a vertical video or vertical video but in landscape with locked
// orientation a screen orientation can be changed automatically
if (!isVerticalVideo
|| (service.isLandscape() && globalScreenOrientationLocked(service))) {
fragmentListener.onScreenRotationButtonClicked();
} else {
toggleFullscreen();
@ -790,9 +819,12 @@ public class VideoPlayerImpl extends VideoPlayer
if (getCurrentState() != STATE_COMPLETED) {
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
showHideShadow(true, DEFAULT_CONTROLS_DURATION, 0);
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
if (v.getId() == playPauseButton.getId()) {
if (v.getId() == playPauseButton.getId()
// Hide controls in fullscreen immediately
|| (v.getId() == screenRotationButton.getId() && isFullscreen)) {
hideControls(0, 0);
} else {
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
@ -819,7 +851,7 @@ public class VideoPlayerImpl extends VideoPlayer
buildQueue();
updatePlaybackButtons();
getControlsRoot().setVisibility(View.INVISIBLE);
hideControls(0, 0);
queueLayout.requestFocus();
animateView(queueLayout, SLIDE_AND_ALPHA, true,
DEFAULT_CONTROLS_DURATION);
@ -911,9 +943,8 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupScreenRotationButton() {
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
final boolean tabletInLandscape = DeviceUtils.isTablet(service) && service.isLandscape();
final boolean showButton = videoPlayerSelected()
&& (orientationLocked || isVerticalVideo || tabletInLandscape);
&& (orientationLocked || isVerticalVideo || DeviceUtils.isTablet(service));
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen()
? R.drawable.ic_fullscreen_exit_white_24dp
@ -925,6 +956,8 @@ public class VideoPlayerImpl extends VideoPlayer
if (orientationLocked
&& isFullscreen()
&& service.isLandscape() == isVerticalVideo
&& !DeviceUtils.isTv(service)
&& !DeviceUtils.isTablet(service)
&& fragmentListener != null) {
fragmentListener.onScreenRotationButtonClicked();
}
@ -955,6 +988,7 @@ public class VideoPlayerImpl extends VideoPlayer
super.onDismiss(menu);
if (isPlaying()) {
hideControls(DEFAULT_CONTROLS_DURATION, 0);
hideSystemUIIfNeeded();
}
}
@ -979,15 +1013,6 @@ public class VideoPlayerImpl extends VideoPlayer
setInitialGestureValues();
queueLayout.getLayoutParams().height = height - queueLayout.getTop();
if (popupPlayerSelected()) {
final 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);
}
}
}
@ -1146,6 +1171,9 @@ public class VideoPlayerImpl extends VideoPlayer
updateWindowFlags(IDLE_WINDOW_FLAGS);
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
if (isFullscreen) {
toggleFullscreen();
}
super.onCompleted();
}
@ -1255,20 +1283,6 @@ public class VideoPlayerImpl extends VideoPlayer
updatePopupSize(getPopupLayoutParams().width, -1);
checkPopupPositionBounds();
}
// 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
final boolean reportedOrientationIsLandscape = service.isLandscape();
final boolean actualOrientationIsLandscape = context.getResources()
.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
if (reportedOrientationIsLandscape && actualOrientationIsLandscape) {
setControlsSize();
}
// Close it because when changing orientation from portrait
// (in fullscreen mode) the size of queue layout can be larger than the screen size
onQueueClosed();
@ -1278,18 +1292,14 @@ public class VideoPlayerImpl extends VideoPlayer
// Interrupt playback only when screen turns on
// and user is watching video in popup player.
// Same actions for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED
if (backgroundPlaybackEnabled()
&& popupPlayerSelected()
&& (isPlaying() || isLoading())) {
if (popupPlayerSelected() && (isPlaying() || isLoading())) {
useVideoSource(true);
}
break;
case Intent.ACTION_SCREEN_OFF:
shouldUpdateOnProgress = false;
// Interrupt playback only when screen turns off with popup player working
if (backgroundPlaybackEnabled()
&& popupPlayerSelected()
&& (isPlaying() || isLoading())) {
if (popupPlayerSelected() && (isPlaying() || isLoading())) {
useVideoSource(false);
}
break;
@ -1426,9 +1436,10 @@ public class VideoPlayerImpl extends VideoPlayer
showOrHideButtons();
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
getControlsVisibilityHandler().postDelayed(() ->
animateView(getControlsRoot(), false, duration, 0,
this::hideSystemUIIfNeeded), delay
getControlsVisibilityHandler().postDelayed(() -> {
showHideShadow(false, duration, 0);
animateView(getControlsRoot(), false, duration, 0, this::hideSystemUIIfNeeded);
}, delay
);
}
@ -1456,11 +1467,17 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void showSystemUIPartially() {
if (isFullscreen() && getParentActivity() != null) {
final AppCompatActivity activity = getParentActivity();
if (isFullscreen() && activity != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
@ -1475,92 +1492,6 @@ public class VideoPlayerImpl extends VideoPlayer
getLoadController().disablePreloadingOfCurrentTrack();
}
/**
* 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() {
final Point size = new Point();
final Display display = getRootView().getDisplay();
if (display == null || !videoPlayerSelected()) {
return;
}
// This method will give a correct size of a usable area of a window.
// It doesn't include NavigationBar, notches, etc.
display.getSize(size);
final boolean isLandscape = service.isLandscape();
final int width = isFullscreen
? (isLandscape ? size.x : size.y)
: ViewGroup.LayoutParams.MATCH_PARENT;
final int gravity = isFullscreen
? (display.getRotation() == Surface.ROTATION_90
? Gravity.START : Gravity.END)
: Gravity.TOP;
getTopControlsRoot().getLayoutParams().width = width;
final RelativeLayout.LayoutParams topParams =
((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
topParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
topParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
topParams.addRule(gravity == Gravity.END
? RelativeLayout.ALIGN_PARENT_END
: RelativeLayout.ALIGN_PARENT_START);
getTopControlsRoot().requestLayout();
getBottomControlsRoot().getLayoutParams().width = width;
final RelativeLayout.LayoutParams bottomParams =
((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
bottomParams.addRule(gravity == Gravity.END
? RelativeLayout.ALIGN_PARENT_END
: RelativeLayout.ALIGN_PARENT_START);
getBottomControlsRoot().requestLayout();
final ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
// In tablet navigationBar located at the bottom of the screen.
// And the situations when we need to set custom height is
// in fullscreen mode in tablet in non-multiWindow mode or with vertical video.
// Other than that MATCH_PARENT is good
final boolean navBarAtTheBottom = DeviceUtils.isTablet(service) || !isLandscape;
controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow()
&& navBarAtTheBottom ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
controlsRoot.requestLayout();
final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
topPadding = !isLandscape && DeviceUtils.hasCutout(topPadding, metrics) ? 0 : topPadding;
getRootView().findViewById(R.id.playbackWindowRoot).setTranslationY(topPadding);
getBottomControlsRoot().setTranslationY(-topPadding);
}
/**
* @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;
final int resourceId = service.isLandscape()
? service.getResources().getIdentifier(
"status_bar_height_landscape", "dimen", "android")
: service.getResources().getIdentifier(
"status_bar_height", "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
final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
statusBarHeight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 24, metrics);
}
return statusBarHeight;
}
protected void setMuteButton(final ImageButton button, final boolean isMuted) {
button.setImageDrawable(AppCompatResources.getDrawable(service, isMuted
? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp));
@ -1603,8 +1534,6 @@ public class VideoPlayerImpl extends VideoPlayer
&& !DeviceUtils.isTablet(service)) {
toggleFullscreen();
}
setControlsSize();
}
private void buildQueue() {
@ -2001,6 +1930,12 @@ public class VideoPlayerImpl extends VideoPlayer
public void setFragmentListener(final PlayerServiceEventListener listener) {
fragmentListener = listener;
fragmentIsVisible = true;
// Apply window insets because Android will not do it when orientation changes
// from landscape to portrait
if (!isFullscreen) {
getControlsRoot().setPadding(0, 0, 0, 0);
}
queueLayout.setPadding(0, 0, 0, 0);
updateMetadata();
updatePlayback();
triggerProgressUpdate();

View File

@ -39,12 +39,13 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout>
}
// Found that user still swiping, continue following
if (skippingInterception) {
if (skippingInterception || getState() == BottomSheetBehavior.STATE_SETTLING) {
return false;
}
// Don't need to do anything if bottomSheet isn't expanded
if (getState() == BottomSheetBehavior.STATE_EXPANDED) {
if (getState() == BottomSheetBehavior.STATE_EXPANDED
&& event.getAction() == MotionEvent.ACTION_DOWN) {
// Without overriding scrolling will not work when user touches these elements
for (final Integer element : skipInterceptionOfElements) {
final ViewGroup viewGroup = child.findViewById(element);

View File

@ -9,6 +9,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ProgressBar;
import androidx.appcompat.content.res.AppCompatResources;
import org.schabi.newpipe.R;
import org.schabi.newpipe.player.BasePlayer;
@ -264,14 +265,19 @@ public class PlayerGestureListener
}
final Window window = parent.getWindow();
playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
final float currentProgressPercent = (float) playerImpl.getBrightnessProgressBar()
.getProgress() / playerImpl.getMaxGestureLength();
final WindowManager.LayoutParams layoutParams = window.getAttributes();
final ProgressBar bar = playerImpl.getBrightnessProgressBar();
final float oldBrightness = layoutParams.screenBrightness;
bar.setProgress((int) (bar.getMax() * Math.max(0, Math.min(1, oldBrightness))));
bar.incrementProgressBy((int) distanceY);
final float currentProgressPercent = (float) bar.getProgress() / bar.getMax();
layoutParams.screenBrightness = currentProgressPercent;
window.setAttributes(layoutParams);
// Save current brightness level
PlayerHelper.setScreenBrightness(parent, currentProgressPercent);
if (DEBUG) {
Log.d(TAG, "onScroll().brightnessControl, "
+ "currentBrightness = " + currentProgressPercent);

View File

@ -6,8 +6,6 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.BatteryManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@ -74,17 +72,4 @@ public final class DeviceUtils {
return false;
}
}
/*
* Compares current status bar height with default status bar height in Android and decides,
* does the device has cutout or not
* */
public static boolean hasCutout(final float statusBarHeight, final DisplayMetrics metrics) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
final float defaultStatusBarHeight = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 25, metrics);
return statusBarHeight > defaultStatusBarHeight;
}
return false;
}
}

View File

@ -0,0 +1,39 @@
package org.schabi.newpipe.views;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.google.android.material.appbar.CollapsingToolbarLayout;
public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
public CustomCollapsingToolbarLayout(@NonNull final Context context) {
super(context);
overrideListener();
}
public CustomCollapsingToolbarLayout(@NonNull final Context context,
@Nullable final AttributeSet attrs) {
super(context, attrs);
overrideListener();
}
public CustomCollapsingToolbarLayout(@NonNull final Context context,
@Nullable final AttributeSet attrs,
final int defStyleAttr) {
super(context, attrs, defStyleAttr);
overrideListener();
}
/**
* CollapsingToolbarLayout sets it's own setOnApplyInsetsListener which consumes
* system insets {@link CollapsingToolbarLayout#onWindowInsetChanged(WindowInsetsCompat)}
* so we will not receive them in subviews with fitsSystemWindows = true.
* Override Google's behavior
* */
public void overrideListener() {
ViewCompat.setOnApplyWindowInsetsListener(this, (v, insets) -> insets);
}
}

View File

@ -1,14 +1,17 @@
package org.schabi.newpipe.views;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.SurfaceView;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
public class ExpandableSurfaceView extends SurfaceView {
private int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
private int resizeMode = RESIZE_MODE_FIT;
private int baseHeight = 0;
private int maxHeight = 0;
private float videoAspectRatio = 0.0f;
@ -30,7 +33,7 @@ public class ExpandableSurfaceView extends SurfaceView {
final boolean verticalVideo = videoAspectRatio < 1;
// Use maxHeight only on non-fit resize mode and in vertical videos
int height = maxHeight != 0
&& resizeMode != AspectRatioFrameLayout.RESIZE_MODE_FIT
&& resizeMode != RESIZE_MODE_FIT
&& verticalVideo ? maxHeight : baseHeight;
if (height == 0) {
@ -42,26 +45,22 @@ public class ExpandableSurfaceView extends SurfaceView {
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;
if (resizeMode == RESIZE_MODE_FIT
// KitKat doesn't work well when a view has a scale like needed for ZOOM
|| (resizeMode == RESIZE_MODE_ZOOM && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)) {
if (aspectDeformation > 0) {
height = (int) (width / videoAspectRatio);
} else {
width = (int) (height * videoAspectRatio);
}
} else if (resizeMode == RESIZE_MODE_ZOOM) {
if (aspectDeformation < 0) {
scaleY = viewAspectRatio / videoAspectRatio;
} else {
scaleX = videoAspectRatio / viewAspectRatio;
}
}
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}

View File

@ -17,15 +17,19 @@
*/
package org.schabi.newpipe.views;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import org.schabi.newpipe.R;
public final class FocusAwareCoordinator extends CoordinatorLayout {
private final Rect childFocus = new Rect();
@ -63,4 +67,41 @@ public final class FocusAwareCoordinator extends CoordinatorLayout {
requestChildRectangleOnScreen(child, childFocus, false);
}
}
/**
* Applies window insets to all children, not just for the first who consume the insets.
* Makes possible for multiple fragments to co-exist. Without this code
* the first ViewGroup who consumes will be the last who receive the insets
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WindowInsets dispatchApplyWindowInsets(final WindowInsets insets) {
boolean consumed = false;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
final WindowInsets res = child.dispatchApplyWindowInsets(insets);
if (res.isConsumed()) {
consumed = true;
}
}
if (consumed) {
insets.consumeSystemWindowInsets();
}
return insets;
}
/**
* Adjusts player's controls manually because fitsSystemWindows doesn't work when multiple
* receivers adjust its bounds. So when two listeners are present (like in profile page)
* the player's controls will not receive insets. This method fixes it
*/
@Override
protected boolean fitSystemWindows(final Rect insets) {
final ViewGroup controls = findViewById(R.id.playbackControlRoot);
if (controls != null) {
controls.setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
return super.fitSystemWindows(insets);
}
}

View File

@ -20,7 +20,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="5"
android:fitsSystemWindows="true"
android:isScrollContainer="true">
<com.google.android.material.appbar.AppBarLayout
@ -28,12 +27,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
app:elevation="0dp"
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
@ -162,7 +160,7 @@
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
<!-- CONTENT -->
<RelativeLayout

View File

@ -28,6 +28,22 @@
android:layout_centerInParent="true"
android:layout_gravity="center"/>
<View
android:id="@+id/playerTopShadow"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_top_background"
android:layout_alignParentTop="true"
android:visibility="gone"/>
<View
android:id="@+id/playerBottomShadow"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_background"
android:layout_alignParentBottom="true"
android:visibility="gone"/>
<ImageView
android:id="@+id/endScreen"
android:layout_width="match_parent"
@ -42,28 +58,16 @@
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="@color/video_overlay_color"
android:visibility="gone"
tools:visibility="visible">
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_top_background"
android:layout_alignParentTop="true" />
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_background"
android:layout_alignParentBottom="true" />
<!-- All top controls in this layout -->
<RelativeLayout
android:id="@+id/playbackWindowRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/topControls"
@ -228,29 +232,29 @@
tools:ignore="HardcodedText,RtlHardcoded"
tools:text="FIT"/>
<Button
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/player_main_buttons_padding"
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"
android:textAllCaps="false"
android:background="?attr/selectableItemBackground"
tools:ignore="RelativeOverlap,RtlHardcoded"
tools:text="English"/>
<Space
android:id="@+id/spaceBeforeButton"
<FrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="3"/>
android:layout_height="wrap_content"
android:layout_weight="3">
<TextView
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/player_main_buttons_padding"
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"
android:background="?attr/selectableItemBackground"
tools:ignore="RelativeOverlap,RtlHardcoded"
tools:text="English"/>
</FrameLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/playWithKodi"
@ -307,25 +311,24 @@
android:contentDescription="@string/mute"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="@dimen/player_main_buttons_padding"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
tools:ignore="ContentDescription,RtlHardcoded"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="@dimen/player_main_buttons_padding"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
tools:ignore="ContentDescription,RtlHardcoded"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:id="@+id/bottomControls"
android:layout_width="match_parent"
@ -353,10 +356,10 @@
android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:paddingBottom="4dp"
android:paddingTop="8dp"
android:layout_marginTop="2dp"
tools:progress="25"
android:nextFocusDown="@id/screenRotationButton"
tools:secondaryProgress="50"/>

View File

@ -4,8 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:layout_height="match_parent">
<org.schabi.newpipe.views.FocusAwareCoordinator
android:layout_width="match_parent"

View File

@ -18,11 +18,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
@ -148,8 +147,7 @@
/>
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
<!-- CONTENT -->
<RelativeLayout

View File

@ -28,6 +28,22 @@
android:layout_centerInParent="true"
android:layout_gravity="center"/>
<View
android:id="@+id/playerTopShadow"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_top_background"
android:layout_alignParentTop="true"
android:visibility="gone"/>
<View
android:id="@+id/playerBottomShadow"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_background"
android:layout_alignParentBottom="true"
android:visibility="gone"/>
<ImageView
android:id="@+id/endScreen"
android:layout_width="match_parent"
@ -42,28 +58,16 @@
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="@color/video_overlay_color"
android:visibility="gone"
tools:visibility="visible">
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_top_background"
android:layout_alignParentTop="true" />
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_background"
android:layout_alignParentBottom="true" />
<!-- All top controls in this layout -->
<RelativeLayout
android:id="@+id/playbackWindowRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/topControls"
@ -227,6 +231,11 @@
tools:ignore="HardcodedText,RtlHardcoded"
tools:text="FIT"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3">
<TextView
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
@ -235,7 +244,6 @@
android:layout_marginEnd="8dp"
android:gravity="center|left"
android:minHeight="35dp"
android:maxWidth="100dp"
android:lines="1"
android:ellipsize="end"
android:minWidth="50dp"
@ -245,11 +253,7 @@
tools:ignore="RelativeOverlap,RtlHardcoded"
tools:text="English"/>
<Space
android:id="@+id/spaceBeforeButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="3"/>
</FrameLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/playWithKodi"
@ -306,25 +310,24 @@
android:contentDescription="@string/mute"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="@dimen/player_main_buttons_padding"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
tools:ignore="ContentDescription,RtlHardcoded"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fullScreenButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="@dimen/player_main_buttons_padding"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
tools:ignore="ContentDescription,RtlHardcoded"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:id="@+id/bottomControls"
android:layout_width="match_parent"
@ -352,10 +355,10 @@
android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:paddingBottom="4dp"
android:paddingTop="8dp"
android:layout_marginTop="2dp"
tools:progress="25"
tools:secondaryProgress="50"/>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Base.V29.LightTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
</style>
<style name="Base.LightTheme" parent="Base.V29.LightTheme" />
<style name="Base.V29.DarkTheme" parent="Base.V7.DarkTheme">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
</style>
<style name="Base.DarkTheme" parent="Base.V29.DarkTheme" />
<style name="Base.V29.BlackTheme" parent="Base.V7.BlackTheme">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
</style>
<style name="Base.BlackTheme" parent="Base.V29.BlackTheme" />
</resources>

View File

@ -9,8 +9,8 @@
</style>
<!-- Base themes -->
<style name="LightTheme" parent="Theme.AppCompat.Light.NoActionBar">
<style name="Base.LightTheme" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="LightTheme" parent="Base.LightTheme">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_primary_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>