mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Increased performance of the UI. main thread is not as busy as before
This commit is contained in:
		| @@ -3,11 +3,9 @@ package org.schabi.newpipe.fragments.detail; | ||||
| import android.animation.ValueAnimator; | ||||
| import android.app.Activity; | ||||
| import android.content.BroadcastReceiver; | ||||
| import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import android.content.ServiceConnection; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.pm.ActivityInfo; | ||||
| import android.database.ContentObserver; | ||||
| @@ -16,7 +14,8 @@ import android.graphics.drawable.Drawable; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.os.IBinder; | ||||
| import android.os.Looper; | ||||
| import android.view.ViewTreeObserver; | ||||
| import androidx.core.text.HtmlCompat; | ||||
| import androidx.preference.PreferenceManager; | ||||
| import android.provider.Settings; | ||||
| @@ -60,6 +59,7 @@ import com.nostra13.universalimageloader.core.assist.FailReason; | ||||
| import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; | ||||
| import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; | ||||
|  | ||||
| import org.schabi.newpipe.App; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.ReCaptchaActivity; | ||||
| import org.schabi.newpipe.download.DownloadDialog; | ||||
| @@ -83,12 +83,11 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||
| import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||
| import org.schabi.newpipe.player.BasePlayer; | ||||
| import org.schabi.newpipe.player.MainPlayer; | ||||
| import org.schabi.newpipe.player.VideoPlayer; | ||||
| import org.schabi.newpipe.player.VideoPlayerImpl; | ||||
| import org.schabi.newpipe.player.event.OnKeyDownListener; | ||||
| import org.schabi.newpipe.player.event.PlayerEventListener; | ||||
| import org.schabi.newpipe.player.event.PlayerServiceEventListener; | ||||
| import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener; | ||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | ||||
| import org.schabi.newpipe.player.helper.PlayerHolder; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||
| @@ -98,12 +97,10 @@ import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.DeviceUtils; | ||||
| import org.schabi.newpipe.util.ExtractorHelper; | ||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | ||||
| import org.schabi.newpipe.util.InfoCache; | ||||
| import org.schabi.newpipe.util.ListHelper; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
| import org.schabi.newpipe.util.SerializedCache; | ||||
| import org.schabi.newpipe.util.ShareUtils; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
| import org.schabi.newpipe.views.AnimatedProgressBar; | ||||
| @@ -135,8 +132,7 @@ public class VideoDetailFragment | ||||
|         SharedPreferences.OnSharedPreferenceChangeListener, | ||||
|         View.OnClickListener, | ||||
|         View.OnLongClickListener, | ||||
|         PlayerEventListener, | ||||
|         PlayerServiceEventListener, | ||||
|         PlayerServiceExtendedEventListener, | ||||
|         OnKeyDownListener { | ||||
|     public static final String AUTO_PLAY = "auto_play"; | ||||
|  | ||||
| @@ -158,9 +154,6 @@ public class VideoDetailFragment | ||||
|     private static final String RELATED_TAB_TAG = "NEXT VIDEO"; | ||||
|     private static final String EMPTY_TAB_TAG = "EMPTY TAB"; | ||||
|  | ||||
|     private static final String INFO_KEY = "info_key"; | ||||
|     private static final String STACK_KEY = "stack_key"; | ||||
|  | ||||
|     private boolean showRelatedStreams; | ||||
|     private boolean showComments; | ||||
|     private String selectedTabTag; | ||||
| @@ -173,14 +166,13 @@ public class VideoDetailFragment | ||||
|     protected String name; | ||||
|     @State | ||||
|     protected String url; | ||||
|     @State | ||||
|     protected PlayQueue playQueue; | ||||
|     protected static PlayQueue playQueue; | ||||
|     @State | ||||
|     int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; | ||||
|     @State | ||||
|     protected boolean autoPlayEnabled = true; | ||||
|  | ||||
|     private StreamInfo currentInfo; | ||||
|     private static StreamInfo currentInfo; | ||||
|     private Disposable currentWorker; | ||||
|     @NonNull | ||||
|     private CompositeDisposable disposables = new CompositeDisposable(); | ||||
| @@ -249,8 +241,6 @@ public class VideoDetailFragment | ||||
|     private FrameLayout relatedStreamsLayout; | ||||
|  | ||||
|     private ContentObserver settingsContentObserver; | ||||
|     private ServiceConnection serviceConnection; | ||||
|     private boolean bound; | ||||
|     private MainPlayer playerService; | ||||
|     private VideoPlayerImpl player; | ||||
|  | ||||
| @@ -258,30 +248,12 @@ public class VideoDetailFragment | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Service management | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private ServiceConnection getServiceConnection(final Context context, | ||||
|     @Override | ||||
|     public void onServiceConnected(final VideoPlayerImpl connectedPlayer, | ||||
|                                    final MainPlayer connectedPlayerService, | ||||
|                                    final boolean playAfterConnect) { | ||||
|         return new ServiceConnection() { | ||||
|             @Override | ||||
|             public void onServiceDisconnected(final ComponentName compName) { | ||||
|                 if (DEBUG) { | ||||
|                     Log.d(TAG, "Player service is disconnected"); | ||||
|                 } | ||||
|  | ||||
|                 unbind(context); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onServiceConnected(final ComponentName compName, final IBinder service) { | ||||
|                 if (DEBUG) { | ||||
|                     Log.d(TAG, "Player service is connected"); | ||||
|                 } | ||||
|                 final MainPlayer.LocalBinder localBinder = (MainPlayer.LocalBinder) service; | ||||
|  | ||||
|                 playerService = localBinder.getService(); | ||||
|                 player = localBinder.getPlayer(); | ||||
|  | ||||
|                 startPlayerListener(); | ||||
|         player = connectedPlayer; | ||||
|         playerService = connectedPlayerService; | ||||
|  | ||||
|         // It will do nothing if the player is not in fullscreen mode | ||||
|         hideSystemUiIfNeeded(); | ||||
| @@ -290,20 +262,20 @@ public class VideoDetailFragment | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|                 if (playerIsNotStopped() && player.videoPlayerSelected()) { | ||||
|                     addVideoPlayerView(); | ||||
|                 } | ||||
|  | ||||
|         if (isLandscape()) { | ||||
|             // If the video is playing but orientation changed | ||||
|             // let's make the video in fullscreen again | ||||
|             checkLandscape(); | ||||
|                 } else if (player.isFullscreen()) { | ||||
|         } else if (player.isFullscreen() && !player.isVerticalVideo()) { | ||||
|             // Device is in portrait orientation after rotation but UI is in fullscreen. | ||||
|             // Return back to non-fullscreen state | ||||
|             player.toggleFullscreen(); | ||||
|         } | ||||
|  | ||||
|         if (playerIsNotStopped() && player.videoPlayerSelected()) { | ||||
|             addVideoPlayerView(); | ||||
|         } | ||||
|  | ||||
|         if (playAfterConnect | ||||
|                 || (currentInfo != null | ||||
|                 && isAutoplayEnabled() | ||||
| @@ -311,70 +283,21 @@ public class VideoDetailFragment | ||||
|             openVideoPlayer(); | ||||
|         } | ||||
|     } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     private void bind(final Context context) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "bind() called"); | ||||
|         } | ||||
|  | ||||
|         final Intent serviceIntent = new Intent(context, MainPlayer.class); | ||||
|         bound = context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); | ||||
|         if (!bound) { | ||||
|             context.unbindService(serviceConnection); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void unbind(final Context context) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "unbind() called"); | ||||
|         } | ||||
|  | ||||
|         if (bound) { | ||||
|             context.unbindService(serviceConnection); | ||||
|             bound = false; | ||||
|             stopPlayerListener(); | ||||
|     @Override | ||||
|     public void onServiceDisconnected() { | ||||
|         playerService = null; | ||||
|         player = null; | ||||
|         restoreDefaultBrightness(); | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     private void startPlayerListener() { | ||||
|         if (player != null) { | ||||
|             player.setFragmentListener(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void stopPlayerListener() { | ||||
|         if (player != null) { | ||||
|             player.removeFragmentListener(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void startService(final Context context, final boolean playAfterConnect) { | ||||
|         // startService() can be called concurrently and it will give a random crashes | ||||
|         // and NullPointerExceptions inside the service because the service will be | ||||
|         // bound twice. Prevent it with unbinding first | ||||
|         unbind(context); | ||||
|         context.startService(new Intent(context, MainPlayer.class)); | ||||
|         serviceConnection = getServiceConnection(context, playAfterConnect); | ||||
|         bind(context); | ||||
|     } | ||||
|  | ||||
|     private void stopService(final Context context) { | ||||
|         unbind(context); | ||||
|         context.stopService(new Intent(context, MainPlayer.class)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, | ||||
|                                                   final String name, final PlayQueue playQueue) { | ||||
|                                                   final String name, final PlayQueue queue) { | ||||
|         final VideoDetailFragment instance = new VideoDetailFragment(); | ||||
|         instance.setInitialData(serviceId, videoUrl, name, playQueue); | ||||
|         instance.setInitialData(serviceId, videoUrl, name, queue); | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
| @@ -477,9 +400,9 @@ public class VideoDetailFragment | ||||
|         // Stop the service when user leaves the app with double back press | ||||
|         // if video player is selected. Otherwise unbind | ||||
|         if (activity.isFinishing() && player != null && player.videoPlayerSelected()) { | ||||
|             stopService(requireContext()); | ||||
|             PlayerHolder.stopService(App.getApp()); | ||||
|         } else { | ||||
|             unbind(requireContext()); | ||||
|             PlayerHolder.removeListener(); | ||||
|         } | ||||
|  | ||||
|         PreferenceManager.getDefaultSharedPreferences(activity) | ||||
| @@ -497,6 +420,12 @@ public class VideoDetailFragment | ||||
|         positionSubscriber = null; | ||||
|         currentWorker = null; | ||||
|         bottomSheetBehavior.setBottomSheetCallback(null); | ||||
|  | ||||
|         if (activity.isFinishing()) { | ||||
|             playQueue = null; | ||||
|             currentInfo = null; | ||||
|             stack = new LinkedList<>(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -529,62 +458,6 @@ public class VideoDetailFragment | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onSaveInstanceState(final Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
|  | ||||
|         if (!isLoading.get() && currentInfo != null && isVisible()) { | ||||
|             final String infoCacheKey = SerializedCache.getInstance() | ||||
|                     .put(currentInfo, StreamInfo.class); | ||||
|             if (infoCacheKey != null) { | ||||
|                 outState.putString(INFO_KEY, infoCacheKey); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (playQueue != null) { | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onRestoreInstanceState(@NonNull final Bundle savedState) { | ||||
|         super.onRestoreInstanceState(savedState); | ||||
|  | ||||
|         final String infoCacheKey = savedState.getString(INFO_KEY); | ||||
|         if (infoCacheKey != null) { | ||||
|             currentInfo = SerializedCache.getInstance().take(infoCacheKey, StreamInfo.class); | ||||
|             if (currentInfo != null) { | ||||
|                 InfoCache.getInstance() | ||||
|                         .putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         final String stackCacheKey = savedState.getString(STACK_KEY); | ||||
|         if (stackCacheKey != null) { | ||||
|             final LinkedList<StackItem> cachedStack = | ||||
|                     SerializedCache.getInstance().take(stackCacheKey, LinkedList.class); | ||||
|             if (cachedStack != null) { | ||||
|                 stack.addAll(cachedStack); | ||||
|             } | ||||
|         } | ||||
|         final String queueCacheKey = savedState.getString(VideoPlayer.PLAY_QUEUE_KEY); | ||||
|         if (queueCacheKey != null) { | ||||
|             playQueue = SerializedCache.getInstance().take(queueCacheKey, PlayQueue.class); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OnClick | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
| @@ -779,8 +652,6 @@ public class VideoDetailFragment | ||||
|  | ||||
|         relatedStreamsLayout = rootView.findViewById(R.id.relatedStreamsLayout); | ||||
|  | ||||
|         setHeightThumbnail(); | ||||
|  | ||||
|         thumbnailBackgroundButton.requestFocus(); | ||||
|  | ||||
|         if (DeviceUtils.isTv(getContext())) { | ||||
| @@ -826,7 +697,11 @@ public class VideoDetailFragment | ||||
|         detailControlsPopup.setOnTouchListener(getOnControlsTouchListener()); | ||||
|  | ||||
|         setupBottomPlayer(); | ||||
|         startService(requireContext(), false); | ||||
|         if (!PlayerHolder.bound) { | ||||
|             setHeightThumbnail(); | ||||
|         } else { | ||||
|             PlayerHolder.startService(App.getApp(), false, this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private View.OnTouchListener getOnControlsTouchListener() { | ||||
| @@ -881,7 +756,7 @@ public class VideoDetailFragment | ||||
|      * Stack that contains the "navigation history".<br> | ||||
|      * The peek is the current video. | ||||
|      */ | ||||
|     protected final LinkedList<StackItem> stack = new LinkedList<>(); | ||||
|     private static LinkedList<StackItem> stack = new LinkedList<>(); | ||||
|  | ||||
|     @Override | ||||
|     public boolean onKeyDown(final int keyCode) { | ||||
| @@ -965,7 +840,7 @@ public class VideoDetailFragment | ||||
|         if (currentInfo == null) { | ||||
|             prepareAndLoadInfo(); | ||||
|         } else { | ||||
|             prepareAndHandleInfo(currentInfo, false); | ||||
|             prepareAndHandleInfoIfNeededAfterDelay(currentInfo, false, 50); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -981,6 +856,21 @@ public class VideoDetailFragment | ||||
|         startLoading(false, true); | ||||
|     } | ||||
|  | ||||
|     private void prepareAndHandleInfoIfNeededAfterDelay(final StreamInfo info, | ||||
|                                                         final boolean scrollToTop, | ||||
|                                                         final long delay) { | ||||
|         new Handler(Looper.getMainLooper()).postDelayed(() -> { | ||||
|             if (activity == null) { | ||||
|                 return; | ||||
|             } | ||||
|             // Data can already be drawn, don't spend time twice | ||||
|             if (info.getName().equals(videoTitleTextView.getText().toString())) { | ||||
|                 return; | ||||
|             } | ||||
|             prepareAndHandleInfo(info, scrollToTop); | ||||
|         }, delay); | ||||
|     } | ||||
|  | ||||
|     private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "prepareAndHandleInfo() called with: " | ||||
| @@ -1140,8 +1030,8 @@ public class VideoDetailFragment | ||||
|         } | ||||
|  | ||||
|         // See UI changes while remote playQueue changes | ||||
|         if (!bound) { | ||||
|             startService(requireContext(), false); | ||||
|         if (player == null) { | ||||
|             PlayerHolder.startService(App.getApp(), false, this); | ||||
|         } | ||||
|  | ||||
|         //  If a user watched video inside fullscreen mode and than chose another player | ||||
| @@ -1170,8 +1060,8 @@ public class VideoDetailFragment | ||||
|  | ||||
|     private void openNormalBackgroundPlayer(final boolean append) { | ||||
|         // See UI changes while remote playQueue changes | ||||
|         if (!bound) { | ||||
|             startService(requireContext(), false); | ||||
|         if (player == null) { | ||||
|             PlayerHolder.startService(App.getApp(), false, this); | ||||
|         } | ||||
|  | ||||
|         final PlayQueue queue = setupPlayQueueForIntent(append); | ||||
| @@ -1185,7 +1075,7 @@ public class VideoDetailFragment | ||||
|  | ||||
|     private void openMainPlayer() { | ||||
|         if (playerService == null) { | ||||
|             startService(requireContext(), true); | ||||
|             PlayerHolder.startService(App.getApp(), true, this); | ||||
|             return; | ||||
|         } | ||||
|         if (currentInfo == null) { | ||||
| @@ -1290,7 +1180,7 @@ public class VideoDetailFragment | ||||
|  | ||||
|         // Check if viewHolder already contains a child | ||||
|         if (player.getRootView().getParent() != playerPlaceholder) { | ||||
|             removeVideoPlayerView(); | ||||
|             playerService.removeViewFromParent(); | ||||
|         } | ||||
|         setHeightThumbnail(); | ||||
|  | ||||
| @@ -1346,6 +1236,23 @@ public class VideoDetailFragment | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private final ViewTreeObserver.OnPreDrawListener preDrawListener = | ||||
|             new ViewTreeObserver.OnPreDrawListener() { | ||||
|                 @Override | ||||
|                 public boolean onPreDraw() { | ||||
|                     final DisplayMetrics metrics = getResources().getDisplayMetrics(); | ||||
|  | ||||
|                     if (getView() != null) { | ||||
|                         final int height = isInMultiWindow() | ||||
|                                 ? requireView().getHeight() | ||||
|                                 : activity.getWindow().getDecorView().getHeight(); | ||||
|                         setHeightThumbnail(height, metrics); | ||||
|                         getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); | ||||
|                     } | ||||
|                     return false; | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|     /** | ||||
|      * Method which controls the size of thumbnail and the size of main player inside | ||||
|      * a layout with thumbnail. It decides what height the player should have in both | ||||
| @@ -1356,24 +1263,35 @@ public class VideoDetailFragment | ||||
|     private void setHeightThumbnail() { | ||||
|         final DisplayMetrics metrics = getResources().getDisplayMetrics(); | ||||
|         final boolean isPortrait = metrics.heightPixels > metrics.widthPixels; | ||||
|         requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); | ||||
|  | ||||
|         final int height; | ||||
|         if (player != null && player.isFullscreen()) { | ||||
|             height = isInMultiWindow() | ||||
|             final int height = isInMultiWindow() | ||||
|                     ? requireView().getHeight() | ||||
|                     : activity.getWindow().getDecorView().getHeight(); | ||||
|             // Height is zero when the view is not yet displayed like after orientation change | ||||
|             if (height != 0) { | ||||
|                 setHeightThumbnail(height, metrics); | ||||
|             } else { | ||||
|             height = isPortrait | ||||
|                 requireView().getViewTreeObserver().addOnPreDrawListener(preDrawListener); | ||||
|             } | ||||
|         } else { | ||||
|             final int height = isPortrait | ||||
|                     ? (int) (metrics.widthPixels / (16.0f / 9.0f)) | ||||
|                     : (int) (metrics.heightPixels / 2.0f); | ||||
|             setHeightThumbnail(height, metrics); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void setHeightThumbnail(final int newHeight, final DisplayMetrics metrics) { | ||||
|         thumbnailImageView.setLayoutParams( | ||||
|                 new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height)); | ||||
|         thumbnailImageView.setMinimumHeight(height); | ||||
|                 new FrameLayout.LayoutParams( | ||||
|                         RelativeLayout.LayoutParams.MATCH_PARENT, newHeight)); | ||||
|         thumbnailImageView.setMinimumHeight(newHeight); | ||||
|         if (player != null) { | ||||
|             final int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT); | ||||
|             player.getSurfaceView().setHeights(height, player.isFullscreen() ? height : maxHeight); | ||||
|             player.getSurfaceView() | ||||
|                     .setHeights(newHeight, player.isFullscreen() ? newHeight : maxHeight); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1895,7 +1813,10 @@ public class VideoDetailFragment | ||||
|         currentInfo = info; | ||||
|         setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue); | ||||
|         setAutoplay(false); | ||||
|         prepareAndHandleInfo(info, true); | ||||
|         // Delay execution just because it freezes the main thread, and while playing | ||||
|         // next/previous video you see visual glitches | ||||
|         // (when non-vertical video goes after vertical video) | ||||
|         prepareAndHandleInfoIfNeededAfterDelay(info, true, 200); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -1912,7 +1833,6 @@ public class VideoDetailFragment | ||||
|  | ||||
|     @Override | ||||
|     public void onServiceStopped() { | ||||
|         unbind(requireContext()); | ||||
|         setOverlayPlayPauseImage(); | ||||
|         if (currentInfo != null) { | ||||
|             updateOverlayData(currentInfo.getName(), | ||||
| @@ -2197,7 +2117,7 @@ public class VideoDetailFragment | ||||
|         if (currentWorker != null) { | ||||
|             currentWorker.dispose(); | ||||
|         } | ||||
|         stopService(requireContext()); | ||||
|         PlayerHolder.stopService(App.getApp()); | ||||
|         setInitialData(0, null, "", null); | ||||
|         currentInfo = null; | ||||
|         updateOverlayData(null, null, null); | ||||
|   | ||||
| @@ -2107,4 +2107,8 @@ public class VideoPlayerImpl extends VideoPlayer | ||||
|     public View getClosingOverlayView() { | ||||
|         return closingOverlayView; | ||||
|     } | ||||
|  | ||||
|     public boolean isVerticalVideo() { | ||||
|         return isVerticalVideo; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,11 @@ | ||||
| package org.schabi.newpipe.player.event; | ||||
|  | ||||
| import org.schabi.newpipe.player.MainPlayer; | ||||
| import org.schabi.newpipe.player.VideoPlayerImpl; | ||||
|  | ||||
| public interface PlayerServiceExtendedEventListener extends PlayerServiceEventListener { | ||||
|     void onServiceConnected(VideoPlayerImpl player, | ||||
|                             MainPlayer playerService, | ||||
|                             boolean playAfterConnect); | ||||
|     void onServiceDisconnected(); | ||||
| } | ||||
| @@ -0,0 +1,219 @@ | ||||
| package org.schabi.newpipe.player.helper; | ||||
|  | ||||
| import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.ServiceConnection; | ||||
| import android.os.IBinder; | ||||
| import android.util.Log; | ||||
| import com.google.android.exoplayer2.ExoPlaybackException; | ||||
| import com.google.android.exoplayer2.PlaybackParameters; | ||||
| import org.schabi.newpipe.App; | ||||
| import org.schabi.newpipe.MainActivity; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.player.MainPlayer; | ||||
| import org.schabi.newpipe.player.VideoPlayerImpl; | ||||
| import org.schabi.newpipe.player.event.PlayerServiceEventListener; | ||||
| import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
|  | ||||
| public final class PlayerHolder { | ||||
|     private PlayerHolder() { | ||||
|     } | ||||
|  | ||||
|     private static final boolean DEBUG = MainActivity.DEBUG; | ||||
|     private static final String TAG = "PlayerHolder"; | ||||
|  | ||||
|     private static PlayerServiceExtendedEventListener listener; | ||||
|  | ||||
|     private static ServiceConnection serviceConnection; | ||||
|     public static boolean bound; | ||||
|     private static MainPlayer playerService; | ||||
|     private static VideoPlayerImpl player; | ||||
|  | ||||
|     public static void setListener(final PlayerServiceExtendedEventListener newListener) { | ||||
|         listener = newListener; | ||||
|         // Force reload data from service | ||||
|         if (player != null) { | ||||
|             listener.onServiceConnected(player, playerService, false); | ||||
|             startPlayerListener(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void removeListener() { | ||||
|         listener = null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static void startService(final Context context, | ||||
|                                     final boolean playAfterConnect, | ||||
|                                     final PlayerServiceExtendedEventListener newListener) { | ||||
|         setListener(newListener); | ||||
|         if (bound) { | ||||
|             return; | ||||
|         } | ||||
|         // startService() can be called concurrently and it will give a random crashes | ||||
|         // and NullPointerExceptions inside the service because the service will be | ||||
|         // bound twice. Prevent it with unbinding first | ||||
|         unbind(context); | ||||
|         context.startService(new Intent(context, MainPlayer.class)); | ||||
|         serviceConnection = getServiceConnection(context, playAfterConnect); | ||||
|         bind(context); | ||||
|     } | ||||
|  | ||||
|     public static void stopService(final Context context) { | ||||
|         unbind(context); | ||||
|         context.stopService(new Intent(context, MainPlayer.class)); | ||||
|     } | ||||
|  | ||||
|     private static ServiceConnection getServiceConnection(final Context context, | ||||
|                                                           final boolean playAfterConnect) { | ||||
|         return new ServiceConnection() { | ||||
|             @Override | ||||
|             public void onServiceDisconnected(final ComponentName compName) { | ||||
|                 if (DEBUG) { | ||||
|                     Log.d(TAG, "Player service is disconnected"); | ||||
|                 } | ||||
|  | ||||
|                 unbind(context); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onServiceConnected(final ComponentName compName, final IBinder service) { | ||||
|                 if (DEBUG) { | ||||
|                     Log.d(TAG, "Player service is connected"); | ||||
|                 } | ||||
|                 final MainPlayer.LocalBinder localBinder = (MainPlayer.LocalBinder) service; | ||||
|  | ||||
|                 playerService = localBinder.getService(); | ||||
|                 player = localBinder.getPlayer(); | ||||
|                 if (listener != null) { | ||||
|                     listener.onServiceConnected(player, playerService, playAfterConnect); | ||||
|                 } | ||||
|                 startPlayerListener(); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     private static void bind(final Context context) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "bind() called"); | ||||
|         } | ||||
|  | ||||
|         final Intent serviceIntent = new Intent(context, MainPlayer.class); | ||||
|         bound = context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); | ||||
|         if (!bound) { | ||||
|             context.unbindService(serviceConnection); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void unbind(final Context context) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "unbind() called"); | ||||
|         } | ||||
|  | ||||
|         if (bound) { | ||||
|             context.unbindService(serviceConnection); | ||||
|             bound = false; | ||||
|             stopPlayerListener(); | ||||
|             playerService = null; | ||||
|             player = null; | ||||
|             if (listener != null) { | ||||
|                 listener.onServiceDisconnected(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static void startPlayerListener() { | ||||
|         if (player != null) { | ||||
|             player.setFragmentListener(INNER_LISTENER); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void stopPlayerListener() { | ||||
|         if (player != null) { | ||||
|             player.removeFragmentListener(INNER_LISTENER); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static final PlayerServiceEventListener INNER_LISTENER = | ||||
|             new PlayerServiceEventListener() { | ||||
|                 @Override | ||||
|                 public void onFullscreenStateChanged(final boolean fullscreen) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onFullscreenStateChanged(fullscreen); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onScreenRotationButtonClicked() { | ||||
|                     if (listener != null) { | ||||
|                         listener.onScreenRotationButtonClicked(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onMoreOptionsLongClicked() { | ||||
|                     if (listener != null) { | ||||
|                         listener.onMoreOptionsLongClicked(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onPlayerError(final ExoPlaybackException error) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onPlayerError(error); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void hideSystemUiIfNeeded() { | ||||
|                     if (listener != null) { | ||||
|                         listener.hideSystemUiIfNeeded(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onQueueUpdate(final PlayQueue queue) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onQueueUpdate(queue); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onPlaybackUpdate(final int state, | ||||
|                                              final int repeatMode, | ||||
|                                              final boolean shuffled, | ||||
|                                              final PlaybackParameters parameters) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onPlaybackUpdate(state, repeatMode, shuffled, parameters); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onProgressUpdate(final int currentProgress, | ||||
|                                              final int duration, | ||||
|                                              final int bufferPercent) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onProgressUpdate(currentProgress, duration, bufferPercent); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onMetadataUpdate(final StreamInfo info, final PlayQueue queue) { | ||||
|                     if (listener != null) { | ||||
|                         listener.onMetadataUpdate(info, queue); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onServiceStopped() { | ||||
|                     if (listener != null) { | ||||
|                         listener.onServiceStopped(); | ||||
|                     } | ||||
|                     unbind(App.getApp()); | ||||
|                 } | ||||
|             }; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Avently
					Avently