Merge pull request #4288 from avently/performance-increase
Performance increase
This commit is contained in:
commit
160a04c3c7
|
@ -3,11 +3,9 @@ package org.schabi.newpipe.fragments.detail;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
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;
|
||||||
|
@ -16,7 +14,8 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.Looper;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import androidx.core.text.HtmlCompat;
|
import androidx.core.text.HtmlCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import android.provider.Settings;
|
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.ImageLoadingListener;
|
||||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.ReCaptchaActivity;
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
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.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.BasePlayer;
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
import org.schabi.newpipe.player.MainPlayer;
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
import org.schabi.newpipe.player.VideoPlayer;
|
|
||||||
import org.schabi.newpipe.player.VideoPlayerImpl;
|
import org.schabi.newpipe.player.VideoPlayerImpl;
|
||||||
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
|
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
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.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
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.DeviceUtils;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.InfoCache;
|
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
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;
|
||||||
|
@ -136,8 +133,7 @@ public class VideoDetailFragment
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
View.OnClickListener,
|
View.OnClickListener,
|
||||||
View.OnLongClickListener,
|
View.OnLongClickListener,
|
||||||
PlayerEventListener,
|
PlayerServiceExtendedEventListener,
|
||||||
PlayerServiceEventListener,
|
|
||||||
OnKeyDownListener {
|
OnKeyDownListener {
|
||||||
public static final String AUTO_PLAY = "auto_play";
|
public static final String AUTO_PLAY = "auto_play";
|
||||||
|
|
||||||
|
@ -159,9 +155,6 @@ public class VideoDetailFragment
|
||||||
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
||||||
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
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 showRelatedStreams;
|
||||||
private boolean showComments;
|
private boolean showComments;
|
||||||
private String selectedTabTag;
|
private String selectedTabTag;
|
||||||
|
@ -174,14 +167,13 @@ public class VideoDetailFragment
|
||||||
protected String name;
|
protected String name;
|
||||||
@State
|
@State
|
||||||
protected String url;
|
protected String url;
|
||||||
@State
|
protected static PlayQueue playQueue;
|
||||||
protected PlayQueue playQueue;
|
|
||||||
@State
|
@State
|
||||||
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
|
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
|
||||||
@State
|
@State
|
||||||
protected boolean autoPlayEnabled = true;
|
protected boolean autoPlayEnabled = true;
|
||||||
|
|
||||||
private StreamInfo currentInfo;
|
private static StreamInfo currentInfo;
|
||||||
private Disposable currentWorker;
|
private Disposable currentWorker;
|
||||||
@NonNull
|
@NonNull
|
||||||
private CompositeDisposable disposables = new CompositeDisposable();
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
@ -250,8 +242,6 @@ public class VideoDetailFragment
|
||||||
private FrameLayout relatedStreamsLayout;
|
private FrameLayout relatedStreamsLayout;
|
||||||
|
|
||||||
private ContentObserver settingsContentObserver;
|
private ContentObserver settingsContentObserver;
|
||||||
private ServiceConnection serviceConnection;
|
|
||||||
private boolean bound;
|
|
||||||
private MainPlayer playerService;
|
private MainPlayer playerService;
|
||||||
private VideoPlayerImpl player;
|
private VideoPlayerImpl player;
|
||||||
|
|
||||||
|
@ -259,30 +249,12 @@ public class VideoDetailFragment
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service management
|
// Service management
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@Override
|
||||||
private ServiceConnection getServiceConnection(final Context context,
|
public void onServiceConnected(final VideoPlayerImpl connectedPlayer,
|
||||||
|
final MainPlayer connectedPlayerService,
|
||||||
final boolean playAfterConnect) {
|
final boolean playAfterConnect) {
|
||||||
return new ServiceConnection() {
|
player = connectedPlayer;
|
||||||
@Override
|
playerService = connectedPlayerService;
|
||||||
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();
|
|
||||||
|
|
||||||
// It will do nothing if the player is not in fullscreen mode
|
// It will do nothing if the player is not in fullscreen mode
|
||||||
hideSystemUiIfNeeded();
|
hideSystemUiIfNeeded();
|
||||||
|
@ -291,20 +263,20 @@ public class VideoDetailFragment
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerIsNotStopped() && player.videoPlayerSelected()) {
|
|
||||||
addVideoPlayerView();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLandscape()) {
|
if (isLandscape()) {
|
||||||
// If the video is playing but orientation changed
|
// If the video is playing but orientation changed
|
||||||
// let's make the video in fullscreen again
|
// let's make the video in fullscreen again
|
||||||
checkLandscape();
|
checkLandscape();
|
||||||
} else if (player.isFullscreen()) {
|
} else if (player.isFullscreen() && !player.isVerticalVideo()) {
|
||||||
// Device is in portrait orientation after rotation but UI is in fullscreen.
|
// Device is in portrait orientation after rotation but UI is in fullscreen.
|
||||||
// Return back to non-fullscreen state
|
// Return back to non-fullscreen state
|
||||||
player.toggleFullscreen();
|
player.toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playerIsNotStopped() && player.videoPlayerSelected()) {
|
||||||
|
addVideoPlayerView();
|
||||||
|
}
|
||||||
|
|
||||||
if (playAfterConnect
|
if (playAfterConnect
|
||||||
|| (currentInfo != null
|
|| (currentInfo != null
|
||||||
&& isAutoplayEnabled()
|
&& isAutoplayEnabled()
|
||||||
|
@ -312,70 +284,21 @@ public class VideoDetailFragment
|
||||||
openVideoPlayer();
|
openVideoPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(final Context context) {
|
@Override
|
||||||
if (DEBUG) {
|
public void onServiceDisconnected() {
|
||||||
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();
|
|
||||||
playerService = null;
|
playerService = null;
|
||||||
player = null;
|
player = null;
|
||||||
restoreDefaultBrightness();
|
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,
|
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();
|
final VideoDetailFragment instance = new VideoDetailFragment();
|
||||||
instance.setInitialData(serviceId, videoUrl, name, playQueue);
|
instance.setInitialData(serviceId, videoUrl, name, queue);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,9 +401,9 @@ public class VideoDetailFragment
|
||||||
// Stop the service when user leaves the app with double back press
|
// Stop the service when user leaves the app with double back press
|
||||||
// if video player is selected. Otherwise unbind
|
// if video player is selected. Otherwise unbind
|
||||||
if (activity.isFinishing() && player != null && player.videoPlayerSelected()) {
|
if (activity.isFinishing() && player != null && player.videoPlayerSelected()) {
|
||||||
stopService(requireContext());
|
PlayerHolder.stopService(App.getApp());
|
||||||
} else {
|
} else {
|
||||||
unbind(requireContext());
|
PlayerHolder.removeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
@ -498,6 +421,12 @@ public class VideoDetailFragment
|
||||||
positionSubscriber = null;
|
positionSubscriber = null;
|
||||||
currentWorker = null;
|
currentWorker = null;
|
||||||
bottomSheetBehavior.setBottomSheetCallback(null);
|
bottomSheetBehavior.setBottomSheetCallback(null);
|
||||||
|
|
||||||
|
if (activity.isFinishing()) {
|
||||||
|
playQueue = null;
|
||||||
|
currentInfo = null;
|
||||||
|
stack = new LinkedList<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -530,62 +459,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
|
// OnClick
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -780,8 +653,6 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
relatedStreamsLayout = rootView.findViewById(R.id.relatedStreamsLayout);
|
relatedStreamsLayout = rootView.findViewById(R.id.relatedStreamsLayout);
|
||||||
|
|
||||||
setHeightThumbnail();
|
|
||||||
|
|
||||||
thumbnailBackgroundButton.requestFocus();
|
thumbnailBackgroundButton.requestFocus();
|
||||||
|
|
||||||
if (DeviceUtils.isTv(getContext())) {
|
if (DeviceUtils.isTv(getContext())) {
|
||||||
|
@ -827,7 +698,11 @@ public class VideoDetailFragment
|
||||||
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
||||||
|
|
||||||
setupBottomPlayer();
|
setupBottomPlayer();
|
||||||
startService(requireContext(), false);
|
if (!PlayerHolder.bound) {
|
||||||
|
setHeightThumbnail();
|
||||||
|
} else {
|
||||||
|
PlayerHolder.startService(App.getApp(), false, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private View.OnTouchListener getOnControlsTouchListener() {
|
private View.OnTouchListener getOnControlsTouchListener() {
|
||||||
|
@ -882,7 +757,7 @@ public class VideoDetailFragment
|
||||||
* Stack that contains the "navigation history".<br>
|
* Stack that contains the "navigation history".<br>
|
||||||
* The peek is the current video.
|
* The peek is the current video.
|
||||||
*/
|
*/
|
||||||
protected final LinkedList<StackItem> stack = new LinkedList<>();
|
private static LinkedList<StackItem> stack = new LinkedList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(final int keyCode) {
|
public boolean onKeyDown(final int keyCode) {
|
||||||
|
@ -966,7 +841,7 @@ public class VideoDetailFragment
|
||||||
if (currentInfo == null) {
|
if (currentInfo == null) {
|
||||||
prepareAndLoadInfo();
|
prepareAndLoadInfo();
|
||||||
} else {
|
} else {
|
||||||
prepareAndHandleInfo(currentInfo, false);
|
prepareAndHandleInfoIfNeededAfterDelay(currentInfo, false, 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,6 +860,21 @@ public class VideoDetailFragment
|
||||||
startLoading(false, true);
|
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) {
|
private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "prepareAndHandleInfo() called with: "
|
Log.d(TAG, "prepareAndHandleInfo() called with: "
|
||||||
|
@ -1144,8 +1034,8 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
// See UI changes while remote playQueue changes
|
// See UI changes while remote playQueue changes
|
||||||
if (!bound) {
|
if (player == null) {
|
||||||
startService(requireContext(), false);
|
PlayerHolder.startService(App.getApp(), false, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a user watched video inside fullscreen mode and than chose another player
|
// If a user watched video inside fullscreen mode and than chose another player
|
||||||
|
@ -1174,8 +1064,8 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
private void openNormalBackgroundPlayer(final boolean append) {
|
private void openNormalBackgroundPlayer(final boolean append) {
|
||||||
// See UI changes while remote playQueue changes
|
// See UI changes while remote playQueue changes
|
||||||
if (!bound) {
|
if (player == null) {
|
||||||
startService(requireContext(), false);
|
PlayerHolder.startService(App.getApp(), false, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
final PlayQueue queue = setupPlayQueueForIntent(append);
|
final PlayQueue queue = setupPlayQueueForIntent(append);
|
||||||
|
@ -1189,7 +1079,7 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
private void openMainPlayer() {
|
private void openMainPlayer() {
|
||||||
if (playerService == null) {
|
if (playerService == null) {
|
||||||
startService(requireContext(), true);
|
PlayerHolder.startService(App.getApp(), true, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentInfo == null) {
|
if (currentInfo == null) {
|
||||||
|
@ -1278,7 +1168,7 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
// Check if viewHolder already contains a child
|
// Check if viewHolder already contains a child
|
||||||
if (player.getRootView().getParent() != playerPlaceholder) {
|
if (player.getRootView().getParent() != playerPlaceholder) {
|
||||||
removeVideoPlayerView();
|
playerService.removeViewFromParent();
|
||||||
}
|
}
|
||||||
setHeightThumbnail();
|
setHeightThumbnail();
|
||||||
|
|
||||||
|
@ -1334,6 +1224,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
|
* 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
|
* a layout with thumbnail. It decides what height the player should have in both
|
||||||
|
@ -1344,24 +1251,35 @@ public class VideoDetailFragment
|
||||||
private void setHeightThumbnail() {
|
private void setHeightThumbnail() {
|
||||||
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||||
final boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
|
final boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
|
||||||
|
requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
|
||||||
|
|
||||||
final int height;
|
|
||||||
if (player != null && player.isFullscreen()) {
|
if (player != null && player.isFullscreen()) {
|
||||||
height = isInMultiWindow()
|
final int height = isInMultiWindow()
|
||||||
? requireView().getHeight()
|
? requireView().getHeight()
|
||||||
: activity.getWindow().getDecorView().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 {
|
} else {
|
||||||
height = isPortrait
|
requireView().getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final int height = isPortrait
|
||||||
? (int) (metrics.widthPixels / (16.0f / 9.0f))
|
? (int) (metrics.widthPixels / (16.0f / 9.0f))
|
||||||
: (int) (metrics.heightPixels / 2.0f);
|
: (int) (metrics.heightPixels / 2.0f);
|
||||||
|
setHeightThumbnail(height, metrics);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setHeightThumbnail(final int newHeight, final DisplayMetrics metrics) {
|
||||||
thumbnailImageView.setLayoutParams(
|
thumbnailImageView.setLayoutParams(
|
||||||
new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
|
new FrameLayout.LayoutParams(
|
||||||
thumbnailImageView.setMinimumHeight(height);
|
RelativeLayout.LayoutParams.MATCH_PARENT, newHeight));
|
||||||
|
thumbnailImageView.setMinimumHeight(newHeight);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
final int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1880,7 +1798,10 @@ public class VideoDetailFragment
|
||||||
currentInfo = info;
|
currentInfo = info;
|
||||||
setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue);
|
setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue);
|
||||||
setAutoplay(false);
|
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
|
@Override
|
||||||
|
@ -1897,7 +1818,6 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceStopped() {
|
public void onServiceStopped() {
|
||||||
unbind(requireContext());
|
|
||||||
setOverlayPlayPauseImage();
|
setOverlayPlayPauseImage();
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
updateOverlayData(currentInfo.getName(),
|
updateOverlayData(currentInfo.getName(),
|
||||||
|
@ -2189,7 +2109,7 @@ public class VideoDetailFragment
|
||||||
if (currentWorker != null) {
|
if (currentWorker != null) {
|
||||||
currentWorker.dispose();
|
currentWorker.dispose();
|
||||||
}
|
}
|
||||||
stopService(requireContext());
|
PlayerHolder.stopService(App.getApp());
|
||||||
setInitialData(0, null, "", null);
|
setInitialData(0, null, "", null);
|
||||||
currentInfo = null;
|
currentInfo = null;
|
||||||
updateOverlayData(null, null, null);
|
updateOverlayData(null, null, null);
|
||||||
|
|
|
@ -500,12 +500,19 @@ public abstract class BasePlayer implements
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingComplete(final String imageUri, final View view,
|
public void onLoadingComplete(final String imageUri, final View view,
|
||||||
final Bitmap loadedImage) {
|
final Bitmap loadedImage) {
|
||||||
|
final float width = Math.min(
|
||||||
|
context.getResources().getDimension(R.dimen.player_notification_thumbnail_width),
|
||||||
|
loadedImage.getWidth());
|
||||||
|
currentThumbnail = Bitmap.createScaledBitmap(loadedImage,
|
||||||
|
(int) width,
|
||||||
|
(int) (loadedImage.getHeight() / (loadedImage.getWidth() / width)), true);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Thumbnail - onLoadingComplete() called with: "
|
Log.d(TAG, "Thumbnail - onLoadingComplete() called with: "
|
||||||
+ "imageUri = [" + imageUri + "], view = [" + view + "], "
|
+ "imageUri = [" + imageUri + "], view = [" + view + "], "
|
||||||
+ "loadedImage = [" + loadedImage + "]");
|
+ "loadedImage = [" + loadedImage + "], "
|
||||||
|
+ loadedImage.getWidth() + "x" + loadedImage.getHeight()
|
||||||
|
+ ", scaled width = " + width);
|
||||||
}
|
}
|
||||||
currentThumbnail = loadedImage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2107,4 +2107,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
public View getClosingOverlayView() {
|
public View getClosingOverlayView() {
|
||||||
return closingOverlayView;
|
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -48,6 +48,7 @@
|
||||||
<dimen name="player_main_buttons_padding">6dp</dimen>
|
<dimen name="player_main_buttons_padding">6dp</dimen>
|
||||||
<dimen name="player_popup_buttons_padding">1dp</dimen>
|
<dimen name="player_popup_buttons_padding">1dp</dimen>
|
||||||
<dimen name="player_main_buttons_min_width">40dp</dimen>
|
<dimen name="player_main_buttons_min_width">40dp</dimen>
|
||||||
|
<dimen name="player_notification_thumbnail_width">200dp</dimen>
|
||||||
|
|
||||||
<!-- Miscellaneous -->
|
<!-- Miscellaneous -->
|
||||||
<dimen name="popup_default_width">180dp</dimen>
|
<dimen name="popup_default_width">180dp</dimen>
|
||||||
|
|
Loading…
Reference in New Issue