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.SharedPreferences;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; 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.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.SerializedCache;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.AnimatedProgressBar; import org.schabi.newpipe.views.AnimatedProgressBar;
import org.schabi.newpipe.views.LargeTextMovementMethod; import org.schabi.newpipe.views.LargeTextMovementMethod;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; 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.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; 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.helper.PlayerHelper.isClearingQueueConfirmationRequired;
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -337,7 +338,7 @@ public class VideoDetailFragment
stopPlayerListener(); stopPlayerListener();
playerService = null; playerService = null;
player = null; player = null;
saveCurrentAndRestoreDefaultBrightness(); restoreDefaultBrightness();
} }
} }
@ -404,7 +405,7 @@ public class VideoDetailFragment
settingsContentObserver = new ContentObserver(new Handler()) { settingsContentObserver = new ContentObserver(new Handler()) {
@Override @Override
public void onChange(final boolean selfChange) { public void onChange(final boolean selfChange) {
if (activity != null && !PlayerHelper.globalScreenOrientationLocked(activity)) { if (activity != null && !globalScreenOrientationLocked(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} }
} }
@ -426,7 +427,7 @@ public class VideoDetailFragment
if (currentWorker != null) { if (currentWorker != null) {
currentWorker.dispose(); currentWorker.dispose();
} }
saveCurrentAndRestoreDefaultBrightness(); restoreDefaultBrightness();
PreferenceManager.getDefaultSharedPreferences(requireContext()) PreferenceManager.getDefaultSharedPreferences(requireContext())
.edit() .edit()
.putString(getString(R.string.stream_info_selected_tab_key), .putString(getString(R.string.stream_info_selected_tab_key),
@ -538,31 +539,51 @@ public class VideoDetailFragment
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
if (!isLoading.get() && currentInfo != null && isVisible()) { 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) { 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 @Override
protected void onRestoreInstanceState(@NonNull final Bundle savedState) { protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
super.onRestoreInstanceState(savedState); super.onRestoreInstanceState(savedState);
Serializable serializable = savedState.getSerializable(INFO_KEY); final String infoCacheKey = savedState.getString(INFO_KEY);
if (serializable instanceof StreamInfo) { if (infoCacheKey != null) {
currentInfo = (StreamInfo) serializable; currentInfo = SerializedCache.getInstance().take(infoCacheKey, StreamInfo.class);
InfoCache.getInstance().putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM); if (currentInfo != null) {
InfoCache.getInstance()
.putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
}
} }
serializable = savedState.getSerializable(STACK_KEY); final String stackCacheKey = savedState.getString(STACK_KEY);
if (serializable instanceof Collection) { if (stackCacheKey != null) {
//noinspection unchecked final LinkedList<StackItem> cachedStack =
stack.addAll((Collection<? extends StackItem>) serializable); 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(); setOverlayPlayPauseImage();
switch (state) { switch (state) {
case BasePlayer.STATE_COMPLETED:
restoreDefaultOrientation();
break;
case BasePlayer.STATE_PLAYING: case BasePlayer.STATE_PLAYING:
if (positionView.getAlpha() != 1.0f if (positionView.getAlpha() != 1.0f
&& player.getPlayQueue() != null && player.getPlayQueue() != null
@ -1869,10 +1887,11 @@ public class VideoDetailFragment
public void onPlayerError(final ExoPlaybackException error) { public void onPlayerError(final ExoPlaybackException error) {
if (error.type == ExoPlaybackException.TYPE_SOURCE if (error.type == ExoPlaybackException.TYPE_SOURCE
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) { || error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
hideMainPlayer(); // Properly exit from fullscreen
if (playerService != null && player.isFullscreen()) { if (playerService != null && player.isFullscreen()) {
player.toggleFullscreen(); player.toggleFullscreen();
} }
hideMainPlayer();
} }
} }
@ -1911,7 +1930,13 @@ public class VideoDetailFragment
} }
scrollToTop(); scrollToTop();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addVideoPlayerView(); 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 @Override
@ -1919,13 +1944,15 @@ public class VideoDetailFragment
// In tablet user experience will be better if screen will not be rotated // In tablet user experience will be better if screen will not be rotated
// from landscape to portrait every time. // from landscape to portrait every time.
// Just turn on fullscreen mode in landscape orientation // 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(); player.toggleFullscreen();
return; return;
} }
final int newOrientation = isLandscape() final int newOrientation = isLandscape()
? ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; : ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
activity.setRequestedOrientation(newOrientation); activity.setRequestedOrientation(newOrientation);
@ -1970,7 +1997,11 @@ public class VideoDetailFragment
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
} }
activity.getWindow().getDecorView().setSystemUiVisibility(0); 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() { private void hideSystemUi() {
@ -1985,18 +2016,26 @@ public class VideoDetailFragment
// Prevent jumping of the player on devices with cutout // Prevent jumping of the player on devices with cutout
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
activity.getWindow().getAttributes().layoutInDisplayCutoutMode = 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_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; | 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().getDecorView().setSystemUiVisibility(visibility);
activity.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); && (isInMultiWindow() || (player != null && player.isFullscreen()))) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} }
// Listener implementation // Listener implementation
@ -2014,13 +2053,11 @@ public class VideoDetailFragment
&& player.getPlayer().getPlaybackState() != Player.STATE_IDLE; && player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
} }
private void saveCurrentAndRestoreDefaultBrightness() { private void restoreDefaultBrightness() {
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes(); final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
if (lp.screenBrightness == -1) { if (lp.screenBrightness == -1) {
return; return;
} }
// Save current brightness level
PlayerHelper.setScreenBrightness(activity, lp.screenBrightness);
// Restore the old brightness when fragment.onPause() called or // Restore the old brightness when fragment.onPause() called or
// when a player is in portrait // when a player is in portrait
@ -2039,7 +2076,7 @@ public class VideoDetailFragment
|| !player.isFullscreen() || !player.isFullscreen()
|| bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) { || bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) {
// Apply system brightness when the player is not in fullscreen // Apply system brightness when the player is not in fullscreen
saveCurrentAndRestoreDefaultBrightness(); restoreDefaultBrightness();
} else { } else {
// Restore already saved brightness level // Restore already saved brightness level
final float brightnessLevel = PlayerHelper.getScreenBrightness(activity); final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
@ -2058,11 +2095,9 @@ public class VideoDetailFragment
} }
player.checkLandscape(); 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 // 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.onPlay();
player.showControlsThenHide();
} }
} }
@ -2265,6 +2300,7 @@ public class VideoDetailFragment
&& player.videoPlayerSelected()) { && player.videoPlayerSelected()) {
player.toggleFullscreen(); player.toggleFullscreen();
} }
setOverlayLook(appBarLayout, behavior, 1);
break; break;
case BottomSheetBehavior.STATE_COLLAPSED: case BottomSheetBehavior.STATE_COLLAPSED:
moveFocusToMainFragment(true); moveFocusToMainFragment(true);
@ -2274,6 +2310,7 @@ public class VideoDetailFragment
if (player != null) { if (player != null) {
player.onQueueClosed(); player.onQueueClosed();
} }
setOverlayLook(appBarLayout, behavior, 0);
break; break;
case BottomSheetBehavior.STATE_DRAGGING: case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_SETTLING: 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 // Android TV will handle back button in case controls will be visible
// (one more additional unneeded click while the player is hidden) // (one more additional unneeded click while the player is hidden)
playerImpl.hideControls(0, 0); playerImpl.hideControls(0, 0);
playerImpl.onQueueClosed();
// Notification shows information about old stream but if a user selects // Notification shows information about old stream but if a user selects
// a stream from backStack it's not actual anymore // a stream from backStack it's not actual anymore
// So we should hide the notification at all. // So we should hide the notification at all.
@ -195,6 +196,10 @@ public final class MainPlayer extends Service {
} }
if (playerImpl != null) { if (playerImpl != null) {
// Exit from fullscreen when user closes the player via notification
if (playerImpl.isFullscreen()) {
playerImpl.toggleFullscreen();
}
removeViewFromParent(); removeViewFromParent();
playerImpl.setRecovery(); playerImpl.setRecovery();

View File

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

View File

@ -27,22 +27,21 @@ import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.view.DisplayCutout;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Display;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.Gravity; import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
@ -104,7 +103,6 @@ import org.schabi.newpipe.util.ShareUtils;
import java.util.List; import java.util.List;
import static android.content.Context.WINDOW_SERVICE; 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_CLOSE;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND; 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_REPEAT;
import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE; 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.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.Type.SLIDE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.animateRotation; import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView; 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_WIDTH = "popup_saved_width";
static final String POPUP_SAVED_X = "popup_saved_x"; static final String POPUP_SAVED_X = "popup_saved_x";
static final String POPUP_SAVED_Y = "popup_saved_y"; 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 private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS
@ -254,6 +252,7 @@ public class VideoPlayerImpl extends VideoPlayer
} else { } else {
getRootView().setVisibility(View.VISIBLE); getRootView().setVisibility(View.VISIBLE);
initVideoPlayer(); initVideoPlayer();
onQueueClosed();
// Android TV: without it focus will frame the whole player // Android TV: without it focus will frame the whole player
playPauseButton.requestFocus(); playPauseButton.requestFocus();
} }
@ -310,6 +309,9 @@ public class VideoPlayerImpl extends VideoPlayer
titleTextView.setSelected(true); titleTextView.setSelected(true);
channelTextView.setSelected(true); channelTextView.setSelected(true);
// Prevent hiding of bottom sheet via swipe inside queue
this.itemsList.setNestedScrollingEnabled(false);
} }
@Override @Override
@ -334,7 +336,6 @@ public class VideoPlayerImpl extends VideoPlayer
* This method ensures that popup and main players have different look. * 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. * 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}. * 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() { private void setupElementsVisibility() {
if (popupPlayerSelected()) { if (popupPlayerSelected()) {
@ -469,6 +470,17 @@ public class VideoPlayerImpl extends VideoPlayer
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false, Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
settingsContentObserver); settingsContentObserver);
getRootView().addOnLayoutChangeListener(this); 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) { public boolean onKeyDown(final int keyCode) {
@ -696,11 +708,46 @@ public class VideoPlayerImpl extends VideoPlayer
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "toggleFullscreen() called"); Log.d(TAG, "toggleFullscreen() called");
} }
if (simpleExoPlayer == null || getCurrentMetadata() == null) { if (popupPlayerSelected()
|| simpleExoPlayer == null
|| getCurrentMetadata() == null
|| fragmentListener == null) {
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 {
// 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);
channelTextView.setVisibility(View.GONE);
playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
} else {
titleTextView.setVisibility(View.VISIBLE);
channelTextView.setVisibility(View.VISIBLE);
playerCloseButton.setVisibility(View.GONE);
}
setupScreenRotationButton();
}
public void switchFromPopupToMain() {
if (DEBUG) {
Log.d(TAG, "switchFromPopupToMain() called");
}
if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) {
return; return;
} }
if (popupPlayerSelected()) {
setRecovery(); setRecovery();
service.removeViewFromParent(); service.removeViewFromParent();
final Intent intent = NavigationHelper.getPlayerIntent( final Intent intent = NavigationHelper.getPlayerIntent(
@ -725,27 +772,6 @@ public class VideoPlayerImpl extends VideoPlayer
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true); intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
service.onDestroy(); service.onDestroy();
context.startActivity(intent); context.startActivity(intent);
return;
} else {
if (fragmentListener == null) {
return;
}
isFullscreen = !isFullscreen;
setControlsSize();
fragmentListener.onFullscreenStateChanged(isFullscreen());
}
if (!isFullscreen()) {
titleTextView.setVisibility(View.GONE);
channelTextView.setVisibility(View.GONE);
playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
} else {
titleTextView.setVisibility(View.VISIBLE);
channelTextView.setVisibility(View.VISIBLE);
playerCloseButton.setVisibility(View.GONE);
}
setupScreenRotationButton();
} }
@Override @Override
@ -775,9 +801,12 @@ public class VideoPlayerImpl extends VideoPlayer
} else if (v.getId() == openInBrowser.getId()) { } else if (v.getId() == openInBrowser.getId()) {
onOpenInBrowserClicked(); onOpenInBrowserClicked();
} else if (v.getId() == fullscreenButton.getId()) { } else if (v.getId() == fullscreenButton.getId()) {
toggleFullscreen(); switchFromPopupToMain();
} else if (v.getId() == screenRotationButton.getId()) { } 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(); fragmentListener.onScreenRotationButtonClicked();
} else { } else {
toggleFullscreen(); toggleFullscreen();
@ -790,9 +819,12 @@ public class VideoPlayerImpl extends VideoPlayer
if (getCurrentState() != STATE_COMPLETED) { if (getCurrentState() != STATE_COMPLETED) {
getControlsVisibilityHandler().removeCallbacksAndMessages(null); getControlsVisibilityHandler().removeCallbacksAndMessages(null);
showHideShadow(true, DEFAULT_CONTROLS_DURATION, 0);
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> { animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) { 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); hideControls(0, 0);
} else { } else {
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
@ -819,7 +851,7 @@ public class VideoPlayerImpl extends VideoPlayer
buildQueue(); buildQueue();
updatePlaybackButtons(); updatePlaybackButtons();
getControlsRoot().setVisibility(View.INVISIBLE); hideControls(0, 0);
queueLayout.requestFocus(); queueLayout.requestFocus();
animateView(queueLayout, SLIDE_AND_ALPHA, true, animateView(queueLayout, SLIDE_AND_ALPHA, true,
DEFAULT_CONTROLS_DURATION); DEFAULT_CONTROLS_DURATION);
@ -911,9 +943,8 @@ public class VideoPlayerImpl extends VideoPlayer
private void setupScreenRotationButton() { private void setupScreenRotationButton() {
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service); final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
final boolean tabletInLandscape = DeviceUtils.isTablet(service) && service.isLandscape();
final boolean showButton = videoPlayerSelected() final boolean showButton = videoPlayerSelected()
&& (orientationLocked || isVerticalVideo || tabletInLandscape); && (orientationLocked || isVerticalVideo || DeviceUtils.isTablet(service));
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE); screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen() screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen()
? R.drawable.ic_fullscreen_exit_white_24dp ? R.drawable.ic_fullscreen_exit_white_24dp
@ -925,6 +956,8 @@ public class VideoPlayerImpl extends VideoPlayer
if (orientationLocked if (orientationLocked
&& isFullscreen() && isFullscreen()
&& service.isLandscape() == isVerticalVideo && service.isLandscape() == isVerticalVideo
&& !DeviceUtils.isTv(service)
&& !DeviceUtils.isTablet(service)
&& fragmentListener != null) { && fragmentListener != null) {
fragmentListener.onScreenRotationButtonClicked(); fragmentListener.onScreenRotationButtonClicked();
} }
@ -955,6 +988,7 @@ public class VideoPlayerImpl extends VideoPlayer
super.onDismiss(menu); super.onDismiss(menu);
if (isPlaying()) { if (isPlaying()) {
hideControls(DEFAULT_CONTROLS_DURATION, 0); hideControls(DEFAULT_CONTROLS_DURATION, 0);
hideSystemUIIfNeeded();
} }
} }
@ -979,15 +1013,6 @@ public class VideoPlayerImpl extends VideoPlayer
setInitialGestureValues(); setInitialGestureValues();
queueLayout.getLayoutParams().height = height - queueLayout.getTop(); 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); updateWindowFlags(IDLE_WINDOW_FLAGS);
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
if (isFullscreen) {
toggleFullscreen();
}
super.onCompleted(); super.onCompleted();
} }
@ -1255,20 +1283,6 @@ public class VideoPlayerImpl extends VideoPlayer
updatePopupSize(getPopupLayoutParams().width, -1); updatePopupSize(getPopupLayoutParams().width, -1);
checkPopupPositionBounds(); 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 // Close it because when changing orientation from portrait
// (in fullscreen mode) the size of queue layout can be larger than the screen size // (in fullscreen mode) the size of queue layout can be larger than the screen size
onQueueClosed(); onQueueClosed();
@ -1278,18 +1292,14 @@ public class VideoPlayerImpl extends VideoPlayer
// Interrupt playback only when screen turns on // Interrupt playback only when screen turns on
// and user is watching video in popup player. // and user is watching video in popup player.
// Same actions for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED // Same actions for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED
if (backgroundPlaybackEnabled() if (popupPlayerSelected() && (isPlaying() || isLoading())) {
&& popupPlayerSelected()
&& (isPlaying() || isLoading())) {
useVideoSource(true); useVideoSource(true);
} }
break; break;
case Intent.ACTION_SCREEN_OFF: case Intent.ACTION_SCREEN_OFF:
shouldUpdateOnProgress = false; shouldUpdateOnProgress = false;
// Interrupt playback only when screen turns off with popup player working // Interrupt playback only when screen turns off with popup player working
if (backgroundPlaybackEnabled() if (popupPlayerSelected() && (isPlaying() || isLoading())) {
&& popupPlayerSelected()
&& (isPlaying() || isLoading())) {
useVideoSource(false); useVideoSource(false);
} }
break; break;
@ -1426,9 +1436,10 @@ public class VideoPlayerImpl extends VideoPlayer
showOrHideButtons(); showOrHideButtons();
getControlsVisibilityHandler().removeCallbacksAndMessages(null); getControlsVisibilityHandler().removeCallbacksAndMessages(null);
getControlsVisibilityHandler().postDelayed(() -> getControlsVisibilityHandler().postDelayed(() -> {
animateView(getControlsRoot(), false, duration, 0, showHideShadow(false, duration, 0);
this::hideSystemUIIfNeeded), delay animateView(getControlsRoot(), false, duration, 0, this::hideSystemUIIfNeeded);
}, delay
); );
} }
@ -1456,11 +1467,17 @@ public class VideoPlayerImpl extends VideoPlayer
} }
private void showSystemUIPartially() { 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 final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; | 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(); 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) { protected void setMuteButton(final ImageButton button, final boolean isMuted) {
button.setImageDrawable(AppCompatResources.getDrawable(service, isMuted button.setImageDrawable(AppCompatResources.getDrawable(service, isMuted
? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp)); ? 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)) { && !DeviceUtils.isTablet(service)) {
toggleFullscreen(); toggleFullscreen();
} }
setControlsSize();
} }
private void buildQueue() { private void buildQueue() {
@ -2001,6 +1930,12 @@ public class VideoPlayerImpl extends VideoPlayer
public void setFragmentListener(final PlayerServiceEventListener listener) { public void setFragmentListener(final PlayerServiceEventListener listener) {
fragmentListener = listener; fragmentListener = listener;
fragmentIsVisible = true; 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(); updateMetadata();
updatePlayback(); updatePlayback();
triggerProgressUpdate(); triggerProgressUpdate();

View File

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

View File

@ -9,6 +9,7 @@ import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ProgressBar;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.BasePlayer;
@ -264,14 +265,19 @@ public class PlayerGestureListener
} }
final Window window = parent.getWindow(); 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 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; layoutParams.screenBrightness = currentProgressPercent;
window.setAttributes(layoutParams); window.setAttributes(layoutParams);
// Save current brightness level
PlayerHelper.setScreenBrightness(parent, currentProgressPercent);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "onScroll().brightnessControl, " Log.d(TAG, "onScroll().brightnessControl, "
+ "currentBrightness = " + currentProgressPercent); + "currentBrightness = " + currentProgressPercent);

View File

@ -6,8 +6,6 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.Build; import android.os.Build;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.KeyEvent; import android.view.KeyEvent;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -74,17 +72,4 @@ public final class DeviceUtils {
return false; 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; package org.schabi.newpipe.views;
import android.content.Context; import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.SurfaceView; import android.view.SurfaceView;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; 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; import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
public class ExpandableSurfaceView extends SurfaceView { public class ExpandableSurfaceView extends SurfaceView {
private int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT; private int resizeMode = RESIZE_MODE_FIT;
private int baseHeight = 0; private int baseHeight = 0;
private int maxHeight = 0; private int maxHeight = 0;
private float videoAspectRatio = 0.0f; private float videoAspectRatio = 0.0f;
@ -30,7 +33,7 @@ public class ExpandableSurfaceView extends SurfaceView {
final boolean verticalVideo = videoAspectRatio < 1; final boolean verticalVideo = videoAspectRatio < 1;
// Use maxHeight only on non-fit resize mode and in vertical videos // Use maxHeight only on non-fit resize mode and in vertical videos
int height = maxHeight != 0 int height = maxHeight != 0
&& resizeMode != AspectRatioFrameLayout.RESIZE_MODE_FIT && resizeMode != RESIZE_MODE_FIT
&& verticalVideo ? maxHeight : baseHeight; && verticalVideo ? maxHeight : baseHeight;
if (height == 0) { if (height == 0) {
@ -42,26 +45,22 @@ public class ExpandableSurfaceView extends SurfaceView {
scaleX = 1.0f; scaleX = 1.0f;
scaleY = 1.0f; scaleY = 1.0f;
switch (resizeMode) { if (resizeMode == RESIZE_MODE_FIT
case AspectRatioFrameLayout.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) { if (aspectDeformation > 0) {
height = (int) (width / videoAspectRatio); height = (int) (width / videoAspectRatio);
} else { } else {
width = (int) (height * videoAspectRatio); width = (int) (height * videoAspectRatio);
} }
} else if (resizeMode == RESIZE_MODE_ZOOM) {
break;
case RESIZE_MODE_ZOOM:
if (aspectDeformation < 0) { if (aspectDeformation < 0) {
scaleY = viewAspectRatio / videoAspectRatio; scaleY = viewAspectRatio / videoAspectRatio;
} else { } else {
scaleX = videoAspectRatio / viewAspectRatio; scaleX = videoAspectRatio / viewAspectRatio;
} }
break;
default:
break;
} }
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
} }

View File

@ -17,15 +17,19 @@
*/ */
package org.schabi.newpipe.views; package org.schabi.newpipe.views;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import org.schabi.newpipe.R;
public final class FocusAwareCoordinator extends CoordinatorLayout { public final class FocusAwareCoordinator extends CoordinatorLayout {
private final Rect childFocus = new Rect(); private final Rect childFocus = new Rect();
@ -63,4 +67,41 @@ public final class FocusAwareCoordinator extends CoordinatorLayout {
requestChildRectangleOnScreen(child, childFocus, false); 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_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="5" android:layout_weight="5"
android:fitsSystemWindows="true"
android:isScrollContainer="true"> android:isScrollContainer="true">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
@ -28,12 +27,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false" android:touchscreenBlocksFocus="false"
app:elevation="0dp" app:elevation="0dp"
app:layout_behavior="com.google.android.material.appbar.FlingBehavior"> 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"> app:layout_scrollFlags="scroll">
@ -162,7 +160,7 @@
</FrameLayout> </FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
<!-- CONTENT --> <!-- CONTENT -->
<RelativeLayout <RelativeLayout

View File

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

View File

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

View File

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

View File

@ -28,6 +28,22 @@
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_gravity="center"/> 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 <ImageView
android:id="@+id/endScreen" android:id="@+id/endScreen"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -42,28 +58,16 @@
android:id="@+id/playbackControlRoot" android:id="@+id/playbackControlRoot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="@color/video_overlay_color" android:background="@color/video_overlay_color"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> 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 --> <!-- All top controls in this layout -->
<RelativeLayout <RelativeLayout
android:id="@+id/playbackWindowRoot" android:id="@+id/playbackWindowRoot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:fitsSystemWindows="true">
<LinearLayout <LinearLayout
android:id="@+id/topControls" android:id="@+id/topControls"
@ -227,6 +231,11 @@
tools:ignore="HardcodedText,RtlHardcoded" tools:ignore="HardcodedText,RtlHardcoded"
tools:text="FIT"/> tools:text="FIT"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3">
<TextView <TextView
android:id="@+id/captionTextView" android:id="@+id/captionTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -235,7 +244,6 @@
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:gravity="center|left" android:gravity="center|left"
android:minHeight="35dp" android:minHeight="35dp"
android:maxWidth="100dp"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:minWidth="50dp" android:minWidth="50dp"
@ -245,11 +253,7 @@
tools:ignore="RelativeOverlap,RtlHardcoded" tools:ignore="RelativeOverlap,RtlHardcoded"
tools:text="English"/> tools:text="English"/>
<Space </FrameLayout>
android:id="@+id/spaceBeforeButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="3"/>
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/playWithKodi" android:id="@+id/playWithKodi"
@ -306,16 +310,11 @@
android:contentDescription="@string/mute" android:contentDescription="@string/mute"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</LinearLayout>
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fullScreenButton" android:id="@+id/fullScreenButton"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:padding="@dimen/player_main_buttons_padding" android:padding="@dimen/player_main_buttons_padding"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
@ -325,6 +324,10 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/bottomControls" android:id="@+id/bottomControls"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -352,10 +355,10 @@
android:id="@+id/playbackSeekBar" android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar" style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" android:layout_weight="1"
android:paddingBottom="4dp" android:layout_marginTop="2dp"
android:paddingTop="8dp"
tools:progress="25" tools:progress="25"
tools:secondaryProgress="50"/> 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> </style>
<!-- Base themes --> <!-- Base themes -->
<style name="Base.LightTheme" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="LightTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="LightTheme" parent="Base.LightTheme">
<item name="colorPrimary">@color/light_youtube_primary_color</item> <item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_primary_color</item> <item name="colorPrimaryDark">@color/light_youtube_primary_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item> <item name="colorAccent">@color/light_youtube_accent_color</item>