mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Fix some crashes / issues after player refactor
This commit is contained in:
		| @@ -240,10 +240,6 @@ public final class VideoDetailFragment | ||||
|             playerUi.ifPresent(MainPlayerUi::toggleFullscreen); | ||||
|         } | ||||
|  | ||||
|         if (playerIsNotStopped() && player.videoPlayerSelected()) { | ||||
|             addVideoPlayerView(); | ||||
|         } | ||||
|  | ||||
|         //noinspection SimplifyOptionalCallChains | ||||
|         if (playAfterConnect | ||||
|                 || (currentInfo != null | ||||
| @@ -335,6 +331,9 @@ public final class VideoDetailFragment | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "onResume() called"); | ||||
|         } | ||||
|  | ||||
|         activity.sendBroadcast(new Intent(ACTION_VIDEO_FRAGMENT_RESUMED)); | ||||
|  | ||||
| @@ -1310,22 +1309,14 @@ public final class VideoDetailFragment | ||||
|         if (!isPlayerAvailable()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         final Optional<View> root = player.UIs().get(VideoPlayerUi.class) | ||||
|                 .map(VideoPlayerUi::getBinding) | ||||
|                 .map(ViewBinding::getRoot); | ||||
|  | ||||
|         // Check if viewHolder already contains a child TODO TODO whaat | ||||
|         /*if (playerService != null | ||||
|                 && root.map(View::getParent).orElse(null) != binding.playerPlaceholder) { | ||||
|             playerService.removeViewFromParent(); | ||||
|         }*/ | ||||
|         setHeightThumbnail(); | ||||
|  | ||||
|         // Prevent from re-adding a view multiple times | ||||
|         if (root.isPresent() && root.get().getParent() == null) { | ||||
|             binding.playerPlaceholder.addView(root.get()); | ||||
|         } | ||||
|         new Handler().post(() -> player.UIs().get(MainPlayerUi.class).ifPresent(playerUi -> { | ||||
|             playerUi.removeViewFromParent(); | ||||
|             binding.playerPlaceholder.addView(playerUi.getBinding().getRoot()); | ||||
|             playerUi.setupVideoSurfaceIfNeeded(); | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     private void removeVideoPlayerView() { | ||||
| @@ -1793,9 +1784,6 @@ public final class VideoDetailFragment | ||||
|  | ||||
|     @Override | ||||
|     public void onViewCreated() { | ||||
|         // Video view can have elements visible from popup, | ||||
|         // We hide it here but once it ready the view will be shown in handleIntent() | ||||
|         getRoot().ifPresent(view -> view.setVisibility(View.GONE)); | ||||
|         addVideoPlayerView(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -485,6 +485,10 @@ public final class Player implements PlaybackListener, Listener { | ||||
|             // make sure UIs know whether a service is connected or not | ||||
|             UIs.call(PlayerUi::onFragmentListenerSet); | ||||
|         } | ||||
|         if (!exoPlayerIsNull()) { | ||||
|             UIs.call(PlayerUi::initPlayer); | ||||
|             UIs.call(PlayerUi::initPlayback); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void initPlayback(@NonNull final PlayQueue queue, | ||||
| @@ -599,7 +603,7 @@ public final class Player implements PlaybackListener, Listener { | ||||
|         progressUpdateDisposable.set(null); | ||||
|         PicassoHelper.cancelTag(PicassoHelper.PLAYER_THUMBNAIL_TAG); // cancel thumbnail loading | ||||
|  | ||||
|         UIs.call(PlayerUi::destroy); | ||||
|         UIs.destroyAll(Object.class); // destroy every UI: obviously every UI extends Object | ||||
|     } | ||||
|  | ||||
|     public void setRecovery() { | ||||
| @@ -737,7 +741,7 @@ public final class Player implements PlaybackListener, Listener { | ||||
|             case Intent.ACTION_CONFIGURATION_CHANGED: | ||||
|                 assureCorrectAppLanguage(service); | ||||
|                 if (DEBUG) { | ||||
|                     Log.d(TAG, "onConfigurationChanged() called"); | ||||
|                     Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received"); | ||||
|                 } | ||||
|                 break; | ||||
|             case Intent.ACTION_HEADSET_PLUG: //FIXME | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| package org.schabi.newpipe.player.gesture | ||||
|  | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.util.Log | ||||
| import android.view.MotionEvent | ||||
| import android.view.View | ||||
| import android.view.View.OnTouchListener | ||||
| import android.widget.ProgressBar | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.appcompat.content.res.AppCompatResources | ||||
| import org.schabi.newpipe.MainActivity | ||||
| import org.schabi.newpipe.R | ||||
| @@ -29,8 +29,6 @@ import kotlin.math.min | ||||
| class MainPlayerGestureListener( | ||||
|     private val playerUi: MainPlayerUi | ||||
| ) : BasePlayerGestureListener(playerUi), OnTouchListener { | ||||
|     private val maxVolume: Int = player.audioReactor.maxVolume | ||||
|  | ||||
|     private var isMoving = false | ||||
|  | ||||
|     override fun onTouch(v: View, event: MotionEvent): Boolean { | ||||
| @@ -41,11 +39,11 @@ class MainPlayerGestureListener( | ||||
|         } | ||||
|         return when (event.action) { | ||||
|             MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { | ||||
|                 v.parent.requestDisallowInterceptTouchEvent(playerUi.isFullscreen) | ||||
|                 v.parent?.requestDisallowInterceptTouchEvent(playerUi.isFullscreen) | ||||
|                 true | ||||
|             } | ||||
|             MotionEvent.ACTION_UP -> { | ||||
|                 v.parent.requestDisallowInterceptTouchEvent(false) | ||||
|                 v.parent?.requestDisallowInterceptTouchEvent(false) | ||||
|                 false | ||||
|             } | ||||
|             else -> true | ||||
| @@ -68,14 +66,15 @@ class MainPlayerGestureListener( | ||||
|     private fun onScrollVolume(distanceY: Float) { | ||||
|         // If we just started sliding, change the progress bar to match the system volume | ||||
|         if (binding.volumeRelativeLayout.visibility != View.VISIBLE) { | ||||
|             val volumePercent: Float = player.audioReactor.volume / maxVolume.toFloat() | ||||
|             val volumePercent: Float = | ||||
|                 player.audioReactor.volume / player.audioReactor.maxVolume.toFloat() | ||||
|             binding.volumeProgressBar.progress = (volumePercent * MAX_GESTURE_LENGTH).toInt() | ||||
|         } | ||||
|  | ||||
|         binding.volumeProgressBar.incrementProgressBy(distanceY.toInt()) | ||||
|         val currentProgressPercent: Float = | ||||
|             binding.volumeProgressBar.progress.toFloat() / MAX_GESTURE_LENGTH | ||||
|         val currentVolume = (maxVolume * currentProgressPercent).toInt() | ||||
|         val currentVolume = (player.audioReactor.maxVolume * currentProgressPercent).toInt() | ||||
|         player.audioReactor.volume = currentVolume | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "onScroll().volumeControl, currentVolume = $currentVolume") | ||||
| @@ -102,7 +101,7 @@ class MainPlayerGestureListener( | ||||
|     } | ||||
|  | ||||
|     private fun onScrollBrightness(distanceY: Float) { | ||||
|         val parent: Activity = playerUi.parentActivity | ||||
|         val parent: AppCompatActivity = playerUi.parentActivity.orElse(null) ?: return | ||||
|         val window = parent.window | ||||
|         val layoutParams = window.attributes | ||||
|         val bar: ProgressBar = binding.brightnessProgressBar | ||||
|   | ||||
| @@ -13,12 +13,13 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAct | ||||
| import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; | ||||
| import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.res.Resources; | ||||
| import android.database.ContentObserver; | ||||
| import android.graphics.Bitmap; | ||||
| import android.graphics.Color; | ||||
| import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.provider.Settings; | ||||
| import android.util.DisplayMetrics; | ||||
| @@ -28,7 +29,6 @@ import android.view.KeyEvent; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.ViewParent; | ||||
| import android.view.Window; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.FrameLayout; | ||||
| import android.widget.LinearLayout; | ||||
| @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.appcompat.content.res.AppCompatResources; | ||||
| import androidx.fragment.app.FragmentActivity; | ||||
| import androidx.recyclerview.widget.ItemTouchHelper; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| @@ -68,8 +69,9 @@ import org.schabi.newpipe.util.external_communication.KoreUtils; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.Optional; | ||||
|  | ||||
| public final class MainPlayerUi extends VideoPlayerUi { | ||||
| public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutChangeListener { | ||||
|     private static final String TAG = MainPlayerUi.class.getSimpleName(); | ||||
|  | ||||
|     private boolean isFullscreen = false; | ||||
| @@ -113,7 +115,6 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|  | ||||
|         super.setupAfterIntent(); | ||||
|  | ||||
|         binding.getRoot().setVisibility(View.VISIBLE); | ||||
|         initVideoPlayer(); | ||||
|         // Android TV: without it focus will frame the whole player | ||||
|         binding.playPauseButton.requestFocus(); | ||||
| @@ -139,7 +140,8 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|         binding.segmentsButton.setOnClickListener(v -> onSegmentsClicked()); | ||||
|  | ||||
|         binding.addToPlaylistButton.setOnClickListener(v -> | ||||
|                 player.onAddToPlaylistClicked(getParentActivity().getSupportFragmentManager())); | ||||
|                 getParentActivity().map(FragmentActivity::getSupportFragmentManager) | ||||
|                         .ifPresent(player::onAddToPlaylistClicked)); | ||||
|  | ||||
|         settingsContentObserver = new ContentObserver(new Handler()) { | ||||
|             @Override | ||||
| @@ -151,7 +153,20 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|                 Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false, | ||||
|                 settingsContentObserver); | ||||
|  | ||||
|         binding.getRoot().addOnLayoutChangeListener(this::onLayoutChange); | ||||
|         binding.getRoot().addOnLayoutChangeListener(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void deinitListeners() { | ||||
|         super.deinitListeners(); | ||||
|  | ||||
|         binding.queueButton.setOnClickListener(null); | ||||
|         binding.segmentsButton.setOnClickListener(null); | ||||
|         binding.addToPlaylistButton.setOnClickListener(null); | ||||
|  | ||||
|         context.getContentResolver().unregisterContentObserver(settingsContentObserver); | ||||
|  | ||||
|         binding.getRoot().removeOnLayoutChangeListener(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -178,7 +193,6 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|     @Override | ||||
|     public void destroy() { | ||||
|         super.destroy(); | ||||
|         context.getContentResolver().unregisterContentObserver(settingsContentObserver); | ||||
|  | ||||
|         // Exit from fullscreen when user closes the player via notification | ||||
|         if (isFullscreen) { | ||||
| @@ -324,9 +338,10 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|                     player.useVideoSource(false); | ||||
|                     break; | ||||
|                 case MINIMIZE_ON_EXIT_MODE_POPUP: | ||||
|                     getParentActivity().ifPresent(activity -> { | ||||
|                         player.setRecovery(); | ||||
|                     NavigationHelper.playOnPopupPlayer(getParentActivity(), | ||||
|                             player.getPlayQueue(), true); | ||||
|                         NavigationHelper.playOnPopupPlayer(activity, player.getPlayQueue(), true); | ||||
|                     }); | ||||
|                     break; | ||||
|                 case MINIMIZE_ON_EXIT_MODE_NONE: default: | ||||
|                     player.pause(); | ||||
| @@ -385,7 +400,7 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|     @Override | ||||
|     public void showSystemUIPartially() { | ||||
|         if (isFullscreen) { | ||||
|             final Window window = getParentActivity().getWindow(); | ||||
|             getParentActivity().map(Activity::getWindow).ifPresent(window -> { | ||||
|                 window.setStatusBarColor(Color.TRANSPARENT); | ||||
|                 window.setNavigationBarColor(Color.TRANSPARENT); | ||||
|                 final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | ||||
| @@ -393,6 +408,7 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; | ||||
|                 window.getDecorView().setSystemUiVisibility(visibility); | ||||
|                 window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -476,7 +492,8 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|     //region Gestures | ||||
|  | ||||
|     @SuppressWarnings("checkstyle:ParameterNumber") | ||||
|     private void onLayoutChange(final View view, final int l, final int t, final int r, final int b, | ||||
|     @Override | ||||
|     public void onLayoutChange(final View view, final int l, final int t, final int r, final int b, | ||||
|                                final int ol, final int ot, final int or, final int ob) { | ||||
|         if (l != ol || t != ot || r != or || b != ob) { | ||||
|             // Use smaller value to be consistent between screen orientations | ||||
| @@ -501,8 +518,7 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|  | ||||
|     private void setInitialGestureValues() { | ||||
|         if (player.getAudioReactor() != null) { | ||||
|             final float currentVolumeNormalized = | ||||
|                     (float) player.getAudioReactor().getVolume() | ||||
|             final float currentVolumeNormalized = (float) player.getAudioReactor().getVolume() | ||||
|                     / player.getAudioReactor().getMaxVolume(); | ||||
|             binding.volumeProgressBar.setProgress( | ||||
|                     (int) (binding.volumeProgressBar.getMax() * currentVolumeNormalized)); | ||||
| @@ -714,7 +730,7 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|             @Override | ||||
|             public void held(final PlayQueueItem item, final View view) { | ||||
|                 @Nullable final PlayQueue playQueue = player.getPlayQueue(); | ||||
|                 @Nullable final AppCompatActivity parentActivity = getParentActivity(); | ||||
|                 @Nullable final AppCompatActivity parentActivity = getParentActivity().orElse(null); | ||||
|                 if (playQueue != null && parentActivity != null && playQueue.indexOf(item) != -1) { | ||||
|                     openPopupMenu(player.getPlayQueue(), item, view, true, | ||||
|                             parentActivity.getSupportFragmentManager(), context); | ||||
| @@ -801,10 +817,15 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|  | ||||
|     @Override | ||||
|     protected void onPlaybackSpeedClicked() { | ||||
|         final AppCompatActivity activity = getParentActivity().orElse(null); | ||||
|         if (activity == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(), | ||||
|                 player.getPlaybackSkipSilence(), (speed, pitch, skipSilence) | ||||
|                         -> player.setPlaybackParameters(speed, pitch, skipSilence)) | ||||
|                 .show(getParentActivity().getSupportFragmentManager(), null); | ||||
|                 .show(activity.getSupportFragmentManager(), null); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -876,15 +897,15 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|             binding.playbackControlRoot.setPadding(0, 0, 0, 0); | ||||
|         } else { | ||||
|         if (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); | ||||
|         } else { | ||||
|             // Apply window insets because Android will not do it when orientation changes | ||||
|             // from landscape to portrait (open vertical video to reproduce) | ||||
|             binding.playbackControlRoot.setPadding(0, 0, 0, 0); | ||||
|         } | ||||
|         fragmentListener.onFullscreenStateChanged(isFullscreen); | ||||
|  | ||||
| @@ -924,14 +945,22 @@ public final class MainPlayerUi extends VideoPlayerUi { | ||||
|         return binding; | ||||
|     } | ||||
|  | ||||
|     public AppCompatActivity getParentActivity() { | ||||
|         return (AppCompatActivity) ((ViewGroup) binding.getRoot().getParent()).getContext(); | ||||
|     public Optional<AppCompatActivity> getParentActivity() { | ||||
|         final ViewParent rootParent = binding.getRoot().getParent(); | ||||
|         if (rootParent instanceof ViewGroup) { | ||||
|             final Context activity = ((ViewGroup) rootParent).getContext(); | ||||
|             if (activity instanceof AppCompatActivity) { | ||||
|                 return Optional.of((AppCompatActivity) activity); | ||||
|             } | ||||
|         } | ||||
|         return Optional.empty(); | ||||
|     } | ||||
|  | ||||
|     public boolean isLandscape() { | ||||
|         // DisplayMetrics from activity context knows about MultiWindow feature | ||||
|         // while DisplayMetrics from app context doesn't | ||||
|         return DeviceUtils.isLandscape(getParentActivity()); | ||||
|         return DeviceUtils.isLandscape( | ||||
|                 getParentActivity().map(Context.class::cast).orElse(player.getService())); | ||||
|     } | ||||
|     //endregion | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,6 @@ import org.schabi.newpipe.player.Player; | ||||
| import java.util.List; | ||||
|  | ||||
| public abstract class PlayerUi { | ||||
|     private static final String TAG = PlayerUi.class.getSimpleName(); | ||||
|  | ||||
|     @NonNull protected Context context; | ||||
|     @NonNull protected Player player; | ||||
|   | ||||
| @@ -69,11 +69,9 @@ public final class PopupPlayerUi extends VideoPlayerUi { | ||||
|  | ||||
|     @Override | ||||
|     public void setupAfterIntent() { | ||||
|         setupElementsVisibility(); | ||||
|         binding.getRoot().setVisibility(View.VISIBLE); | ||||
|         super.setupAfterIntent(); | ||||
|         initPopup(); | ||||
|         initPopupCloseOverlay(); | ||||
|         binding.playPauseButton.requestFocus(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -103,6 +101,7 @@ public final class PopupPlayerUi extends VideoPlayerUi { | ||||
|         binding.loadingPanel.setMinimumHeight(popupLayoutParams.height); | ||||
|  | ||||
|         windowManager.addView(binding.getRoot(), popupLayoutParams); | ||||
|         setupVideoSurfaceIfNeeded(); // now there is a parent, we can setup video surface | ||||
|  | ||||
|         // Popup doesn't have aspectRatio selector, using FIT automatically | ||||
|         setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); | ||||
| @@ -304,7 +303,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { | ||||
|     } | ||||
|  | ||||
|     public void removePopupFromView() { | ||||
|         if (windowManager != null) { | ||||
|         // wrap in try-catch since it could sometimes generate errors randomly | ||||
|         try { | ||||
|             if (popupHasParent()) { | ||||
| @@ -324,7 +322,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { | ||||
|             Log.w(TAG, "Failed to remove popup overlay from window manager", e); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     private void animatePopupOverlayAndFinishService() { | ||||
|         final int targetTranslationY = | ||||
|   | ||||
| @@ -32,7 +32,6 @@ import android.view.Gravity; | ||||
| import android.view.KeyEvent; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.Surface; | ||||
| import android.view.View; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.RelativeLayout; | ||||
| @@ -107,6 +106,7 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|     protected PlayerBinding binding; | ||||
|     private final Handler controlsVisibilityHandler = new Handler(); | ||||
|     @Nullable private SurfaceHolderCallback surfaceHolderCallback; | ||||
|     boolean surfaceIsSetup = false; | ||||
|     @Nullable private Bitmap thumbnail = null; | ||||
|  | ||||
|  | ||||
| @@ -130,6 +130,7 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|  | ||||
|     private GestureDetector gestureDetector; | ||||
|     private BasePlayerGestureListener playerGestureListener; | ||||
|     @Nullable private View.OnLayoutChangeListener onLayoutChangeListener = null; | ||||
|  | ||||
|     @NonNull private final SeekbarPreviewThumbnailHolder seekbarPreviewThumbnailHolder = | ||||
|             new SeekbarPreviewThumbnailHolder(); | ||||
| @@ -138,6 +139,7 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|                          @NonNull final PlayerBinding playerBinding) { | ||||
|         super(player); | ||||
|         binding = playerBinding; | ||||
|         setupFromView(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -222,8 +224,8 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|  | ||||
|         // PlaybackControlRoot already consumed window insets but we should pass them to | ||||
|         // player_overlays and fast_seek_overlay too. Without it they will be off-centered. | ||||
|         binding.playbackControlRoot.addOnLayoutChangeListener( | ||||
|                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { | ||||
|         onLayoutChangeListener | ||||
|                 = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { | ||||
|                     binding.playerOverlays.setPadding( | ||||
|                             v.getPaddingLeft(), | ||||
|                             v.getPaddingTop(), | ||||
| @@ -240,7 +242,43 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|                     fastSeekParams.topMargin = -v.getPaddingBottom(); | ||||
|                     fastSeekParams.rightMargin = -v.getPaddingLeft(); | ||||
|                     fastSeekParams.bottomMargin = -v.getPaddingTop(); | ||||
|                 }); | ||||
|                 }; | ||||
|         binding.playbackControlRoot.addOnLayoutChangeListener(onLayoutChangeListener); | ||||
|     } | ||||
|  | ||||
|     protected void deinitListeners() { | ||||
|         binding.qualityTextView.setOnClickListener(null); | ||||
|         binding.playbackSpeed.setOnClickListener(null); | ||||
|         binding.playbackSeekBar.setOnSeekBarChangeListener(null); | ||||
|         binding.captionTextView.setOnClickListener(null); | ||||
|         binding.resizeTextView.setOnClickListener(null); | ||||
|         binding.playbackLiveSync.setOnClickListener(null); | ||||
|  | ||||
|         binding.getRoot().setOnTouchListener(null); | ||||
|         playerGestureListener = null; | ||||
|         gestureDetector = null; | ||||
|  | ||||
|         binding.repeatButton.setOnClickListener(null); | ||||
|         binding.shuffleButton.setOnClickListener(null); | ||||
|  | ||||
|         binding.playPauseButton.setOnClickListener(null); | ||||
|         binding.playPreviousButton.setOnClickListener(null); | ||||
|         binding.playNextButton.setOnClickListener(null); | ||||
|  | ||||
|         binding.moreOptionsButton.setOnClickListener(null); | ||||
|         binding.moreOptionsButton.setOnLongClickListener(null); | ||||
|         binding.share.setOnClickListener(null); | ||||
|         binding.share.setOnLongClickListener(null); | ||||
|         binding.fullScreenButton.setOnClickListener(null); | ||||
|         binding.screenRotationButton.setOnClickListener(null); | ||||
|         binding.playWithKodi.setOnClickListener(null); | ||||
|         binding.openInBrowser.setOnClickListener(null); | ||||
|         binding.playerCloseButton.setOnClickListener(null); | ||||
|         binding.switchMute.setOnClickListener(null); | ||||
|  | ||||
|         ViewCompat.setOnApplyWindowInsetsListener(binding.itemsListPanel, null); | ||||
|  | ||||
|         binding.playbackControlRoot.removeOnLayoutChangeListener(onLayoutChangeListener); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -304,18 +342,25 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|         playerGestureListener.doubleTapControls(binding.fastSeekOverlay); | ||||
|     } | ||||
|  | ||||
|     public void deinitPlayerSeekOverlay() { | ||||
|         binding.fastSeekOverlay | ||||
|                 .seekSecondsSupplier(null) | ||||
|                 .performListener(null); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setupAfterIntent() { | ||||
|         super.setupAfterIntent(); | ||||
|         setupElementsVisibility(); | ||||
|         setupElementsSize(context.getResources()); | ||||
|         binding.getRoot().setVisibility(View.VISIBLE); | ||||
|         binding.playPauseButton.requestFocus(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void initPlayer() { | ||||
|         super.initPlayer(); | ||||
|         setupVideoSurface(); | ||||
|         setupFromView(); | ||||
|         setupVideoSurfaceIfNeeded(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -331,7 +376,7 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|     @Override | ||||
|     public void destroyPlayer() { | ||||
|         super.destroyPlayer(); | ||||
|         cleanupVideoSurface(); | ||||
|         clearVideoSurface(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -340,6 +385,8 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|         if (binding != null) { | ||||
|             binding.endScreen.setImageBitmap(null); | ||||
|         } | ||||
|         deinitPlayerSeekOverlay(); | ||||
|         deinitListeners(); | ||||
|     } | ||||
|  | ||||
|     protected void setupElementsVisibility() { | ||||
| @@ -1470,40 +1517,50 @@ public abstract class VideoPlayerUi extends PlayerUi | ||||
|     // SurfaceHolderCallback helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     //region SurfaceHolderCallback helpers | ||||
|     private void setupVideoSurface() { | ||||
|  | ||||
|     /** | ||||
|      * Connects the video surface to the exo player. This can be called anytime without the risk for | ||||
|      * issues to occur, since the player will run just fine when no surface is connected. Therefore | ||||
|      * the video surface will be setup only when all of these conditions are true: it is not already | ||||
|      * setup (this just prevents wasting resources to setup the surface again), there is an exo | ||||
|      * player, the root view is attached to a parent and the surface view is valid/unreleased (the | ||||
|      * latter two conditions prevent "The surface has been released" errors). So this function can | ||||
|      * be called many times and even while the UI is in unready states. | ||||
|      */ | ||||
|     public void setupVideoSurfaceIfNeeded() { | ||||
|         if (!surfaceIsSetup && player.getExoPlayer() != null | ||||
|                 && binding.getRoot().getParent() != null) { | ||||
|             // make sure there is nothing left over from previous calls | ||||
|         cleanupVideoSurface(); | ||||
|             clearVideoSurface(); | ||||
|  | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // >=API23 | ||||
|                 surfaceHolderCallback = new SurfaceHolderCallback(context, player.getExoPlayer()); | ||||
|                 binding.surfaceView.getHolder().addCallback(surfaceHolderCallback); | ||||
|             final Surface surface = binding.surfaceView.getHolder().getSurface(); | ||||
|  | ||||
|                 // ensure player is using an unreleased surface, which the surfaceView might not be | ||||
|                 // when starting playback on background or during player switching | ||||
|             if (surface.isValid()) { | ||||
|                 if (binding.surfaceView.getHolder().getSurface().isValid()) { | ||||
|                     // initially set the surface manually otherwise | ||||
|                     // onRenderedFirstFrame() will not be called | ||||
|                 player.getExoPlayer().setVideoSurface(surface); | ||||
|                     player.getExoPlayer().setVideoSurfaceHolder(binding.surfaceView.getHolder()); | ||||
|                 } | ||||
|  | ||||
|             } else { | ||||
|                 player.getExoPlayer().setVideoSurfaceView(binding.surfaceView); | ||||
|             } | ||||
|  | ||||
|             surfaceIsSetup = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void cleanupVideoSurface() { | ||||
|         final Optional<ExoPlayer> exoPlayer = Optional.ofNullable(player.getExoPlayer()); | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // >=API23 | ||||
|             if (surfaceHolderCallback != null) { | ||||
|     private void clearVideoSurface() { | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M // >=API23 | ||||
|                 && surfaceHolderCallback != null) { | ||||
|             binding.surfaceView.getHolder().removeCallback(surfaceHolderCallback); | ||||
|             surfaceHolderCallback.release(); | ||||
|             surfaceHolderCallback = null; | ||||
|         } | ||||
|             exoPlayer.ifPresent(simpleExoPlayer -> simpleExoPlayer.setVideoSurface(null)); | ||||
|         } else { | ||||
|             exoPlayer.ifPresent(simpleExoPlayer -> simpleExoPlayer.setVideoSurfaceView(null)); | ||||
|         } | ||||
|         Optional.ofNullable(player.getExoPlayer()).ifPresent(ExoPlayer::clearVideoSurface); | ||||
|         surfaceIsSetup = false; | ||||
|     } | ||||
|     //endregion | ||||
|  | ||||
|   | ||||
| @@ -38,14 +38,14 @@ class PlayerFastSeekOverlay(context: Context, attrs: AttributeSet?) : | ||||
|  | ||||
|     private var performListener: PerformListener? = null | ||||
|  | ||||
|     fun performListener(listener: PerformListener) = apply { | ||||
|     fun performListener(listener: PerformListener?) = apply { | ||||
|         performListener = listener | ||||
|     } | ||||
|  | ||||
|     private var seekSecondsSupplier: () -> Int = { 0 } | ||||
|  | ||||
|     fun seekSecondsSupplier(supplier: () -> Int) = apply { | ||||
|         seekSecondsSupplier = supplier | ||||
|     fun seekSecondsSupplier(supplier: (() -> Int)?) = apply { | ||||
|         seekSecondsSupplier = supplier ?: { 0 } | ||||
|     } | ||||
|  | ||||
|     // Indicates whether this (double) tap is the first of a series | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stypox
					Stypox