1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-01-11 01:40:59 +00:00

Merge pull request #4562 from Stypox/fix-detail-open

Fix opening VideoDetailFragment and much more
This commit is contained in:
Tobias Groza 2020-11-09 22:04:39 +01:00 committed by GitHub
commit c4a739bef6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 365 additions and 550 deletions

View File

@ -30,9 +30,7 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import androidx.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -46,6 +44,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.ActionBarDrawerToggle;
@ -55,6 +54,7 @@ import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.navigation.NavigationView; import com.google.android.material.navigation.NavigationView;
@ -69,10 +69,11 @@ import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.player.VideoPlayer;
import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.event.OnKeyDownListener;
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.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
@ -87,6 +88,7 @@ import org.schabi.newpipe.views.FocusOverlayView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
@ -152,7 +154,7 @@ public class MainActivity extends AppCompatActivity {
if (DeviceUtils.isTv(this)) { if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this); FocusOverlayView.setupFocusObserver(this);
} }
setupBroadcastReceiver(); openMiniPlayerUponPlayerStarted();
} }
private void setupDrawer() throws Exception { private void setupDrawer() throws Exception {
@ -758,32 +760,36 @@ public class MainActivity extends AppCompatActivity {
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
final String url = intent.getStringExtra(Constants.KEY_URL); final String url = intent.getStringExtra(Constants.KEY_URL);
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
final String title = intent.getStringExtra(Constants.KEY_TITLE); String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent if (title == null) {
.getSerializableExtra(Constants.KEY_LINK_TYPE))) { title = "";
}
final StreamingService.LinkType linkType = ((StreamingService.LinkType) intent
.getSerializableExtra(Constants.KEY_LINK_TYPE));
assert linkType != null;
switch (linkType) {
case STREAM: case STREAM:
final boolean autoPlay = intent final String intentCacheKey = intent.getStringExtra(
.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); VideoPlayer.PLAY_QUEUE_KEY);
final String intentCacheKey = intent
.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY);
final PlayQueue playQueue = intentCacheKey != null final PlayQueue playQueue = intentCacheKey != null
? SerializedCache.getInstance() ? SerializedCache.getInstance()
.take(intentCacheKey, PlayQueue.class) .take(intentCacheKey, PlayQueue.class)
: null; : null;
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(),
serviceId, url, title, autoPlay, playQueue); final boolean switchingPlayers = intent.getBooleanExtra(
VideoDetailFragment.KEY_SWITCHING_PLAYERS, false);
NavigationHelper.openVideoDetailFragment(
getApplicationContext(), getSupportFragmentManager(),
serviceId, url, title, playQueue, switchingPlayers);
break; break;
case CHANNEL: case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(), NavigationHelper.openChannelFragment(getSupportFragmentManager(),
serviceId, serviceId, url, title);
url,
title);
break; break;
case PLAYLIST: case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), NavigationHelper.openPlaylistFragment(getSupportFragmentManager(),
serviceId, serviceId, url, title);
url,
title);
break; break;
} }
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
@ -805,26 +811,38 @@ public class MainActivity extends AppCompatActivity {
} }
} }
private void setupBroadcastReceiver() { private void openMiniPlayerIfMissing() {
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(VideoDetailFragment.ACTION_PLAYER_STARTED)) {
final Fragment fragmentPlayer = getSupportFragmentManager() final Fragment fragmentPlayer = getSupportFragmentManager()
.findFragmentById(R.id.fragment_player_holder); .findFragmentById(R.id.fragment_player_holder);
if (fragmentPlayer == null) { if (fragmentPlayer == null) {
/* // We still don't have a fragment attached to the activity. It can happen when a user
* We still don't have a fragment attached to the activity. // started popup or background players without opening a stream inside the fragment.
* It can happen when a user started popup or background players // Adding it in a collapsed state (only mini player will be visible).
* without opening a stream inside the fragment.
* Adding it in a collapsed state (only mini player will be visible)
* */
NavigationHelper.showMiniPlayer(getSupportFragmentManager()); NavigationHelper.showMiniPlayer(getSupportFragmentManager());
} }
/* }
* At this point the player is added 100%, we can unregister.
* Other actions are useless since the fragment will not be removed after that private void openMiniPlayerUponPlayerStarted() {
* */ if (getIntent().getSerializableExtra(Constants.KEY_LINK_TYPE)
== StreamingService.LinkType.STREAM) {
// handleIntent() already takes care of opening video detail fragment
// due to an intent containing a STREAM link
return;
}
if (PlayerHolder.isPlayerOpen()) {
// if the player is already open, no need for a broadcast receiver
openMiniPlayerIfMissing();
} else {
// listen for player start intent being sent around
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (Objects.equals(intent.getAction(),
VideoDetailFragment.ACTION_PLAYER_STARTED)) {
openMiniPlayerIfMissing();
// At this point the player is added 100%, we can unregister. Other actions
// are useless since the fragment will not be removed after that.
unregisterReceiver(broadcastReceiver); unregisterReceiver(broadcastReceiver);
broadcastReceiver = null; broadcastReceiver = null;
} }
@ -834,6 +852,7 @@ public class MainActivity extends AppCompatActivity {
intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED);
registerReceiver(broadcastReceiver, intentFilter); registerReceiver(broadcastReceiver, intentFilter);
} }
}
private boolean bottomSheetHiddenOrCollapsed() { private boolean bottomSheetHiddenOrCollapsed() {
final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder); final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder);

View File

@ -40,7 +40,9 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.MainPlayer;
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.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
@ -60,8 +62,6 @@ import org.schabi.newpipe.views.FocusOverlayView;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import icepick.Icepick; import icepick.Icepick;
@ -116,8 +116,6 @@ public class RouterActivity extends AppCompatActivity {
} }
} }
internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false);
setTheme(ThemeHelper.isLightThemeSelected(this) setTheme(ThemeHelper.isLightThemeSelected(this)
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
} }
@ -398,15 +396,23 @@ public class RouterActivity extends AppCompatActivity {
// show both "show info" and "video player", they are two different activities // show both "show info" and "video player", they are two different activities
returnList.add(showInfo); returnList.add(showInfo);
returnList.add(videoPlayer); returnList.add(videoPlayer);
} else if (capabilities.contains(VIDEO)
&& PlayerHelper.isAutoplayAllowedByUser(context)) {
// show only "video player" since the details activity will be opened and the video
// will be autoplayed there and "show info" would do the exact same thing
returnList.add(videoPlayer);
} else { } else {
// show only "show info" if video player is not applicable or autoplay is disabled final MainPlayer.PlayerType playerType = PlayerHolder.getType();
if (capabilities.contains(VIDEO)
&& PlayerHelper.isAutoplayAllowedByUser(context)
&& playerType == null || playerType == MainPlayer.PlayerType.VIDEO) {
// show only "video player" since the details activity will be opened and the
// video will be auto played there. Since "show info" would do the exact same
// thing, use that as a key to let VideoDetailFragment load the stream instead
// of using FetcherService (see comment in handleChoice())
returnList.add(new AdapterChoiceItem(
showInfo.key, videoPlayer.description, videoPlayer.icon));
} else {
// show only "show info" if video player is not applicable, auto play is
// disabled or a video is playing in a player different than the main one
returnList.add(showInfo); returnList.add(showInfo);
} }
}
if (capabilities.contains(VIDEO)) { if (capabilities.contains(VIDEO)) {
returnList.add(popupPlayer); returnList.add(popupPlayer);
@ -492,12 +498,7 @@ public class RouterActivity extends AppCompatActivity {
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> { .subscribe(intent -> {
if (!internalRoute) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
startActivity(intent); startActivity(intent);
finish(); finish();
}, throwable -> handleError(throwable, currentUrl)) }, throwable -> handleError(throwable, currentUrl))
); );
@ -515,7 +516,7 @@ public class RouterActivity extends AppCompatActivity {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private void openDownloadDialog() { private void openDownloadDialog() {
ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> { .subscribe((@NonNull StreamInfo result) -> {
@ -532,10 +533,10 @@ public class RouterActivity extends AppCompatActivity {
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.show(fm, "downloadDialog"); downloadDialog.show(fm, "downloadDialog");
fm.executePendingTransactions(); fm.executePendingTransactions();
downloadDialog.getDialog().setOnDismissListener(dialog -> finish()); downloadDialog.requireDialog().setOnDismissListener(dialog -> finish());
}, (@NonNull Throwable throwable) -> { }, (@NonNull Throwable throwable) -> {
showUnsupportedUrlDialog(currentUrl); showUnsupportedUrlDialog(currentUrl);
}); }));
} }
@Override @Override
@ -553,66 +554,6 @@ public class RouterActivity extends AppCompatActivity {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Service Fetcher
//////////////////////////////////////////////////////////////////////////*/
private String removeHeadingGibberish(final String input) {
int start = 0;
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
if (!input.substring(i, i + 1).matches("\\p{L}")) {
start = i + 1;
break;
}
}
return input.substring(start);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private String trim(final String input) {
if (input == null || input.length() < 1) {
return input;
} else {
String output = input;
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
output = output.substring(1);
}
while (output.length() > 0
&& output.substring(output.length() - 1).matches(REGEX_REMOVE_FROM_URL)) {
output = output.substring(0, output.length() - 1);
}
return output;
}
}
/**
* Retrieves all Strings which look remotely like URLs from a text.
* Used if NewPipe was called through share menu.
*
* @param sharedText text to scan for URLs.
* @return potential URLs
*/
protected String[] getUris(final String sharedText) {
final Collection<String> result = new HashSet<>();
if (sharedText != null) {
final String[] array = sharedText.split("\\p{Space}");
for (String s : array) {
s = trim(s);
if (s.length() != 0) {
if (s.matches(".+://.+")) {
result.add(removeHeadingGibberish(s));
} else if (s.matches(".+\\..+")) {
result.add("http://" + s);
}
}
}
}
return result.toArray(new String[0]);
}
private static class AdapterChoiceItem { private static class AdapterChoiceItem {
final String description; final String description;
final String key; final String key;
@ -725,50 +666,34 @@ public class RouterActivity extends AppCompatActivity {
final boolean isExtAudioEnabled = preferences.getBoolean( final boolean isExtAudioEnabled = preferences.getBoolean(
getString(R.string.use_external_audio_player_key), false); getString(R.string.use_external_audio_player_key), false);
PlayQueue playQueue; final PlayQueue playQueue;
final String playerChoice = choice.playerChoice;
if (info instanceof StreamInfo) { if (info instanceof StreamInfo) {
if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { if (choice.playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info); NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
return;
} else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { } else if (choice.playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info); NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
return;
} else { }
playQueue = new SinglePlayQueue((StreamInfo) info); playQueue = new SinglePlayQueue((StreamInfo) info);
} else if (info instanceof ChannelInfo) {
if (playerChoice.equals(videoPlayerKey)) { playQueue = new ChannelPlayQueue((ChannelInfo) info);
openMainPlayer(playQueue, choice); } else if (info instanceof PlaylistInfo) {
} else if (playerChoice.equals(backgroundPlayerKey)) { playQueue = new PlaylistPlayQueue((PlaylistInfo) info);
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); } else {
} else if (playerChoice.equals(popupPlayerKey)) { return;
NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
}
}
} }
if (info instanceof ChannelInfo || info instanceof PlaylistInfo) { if (choice.playerChoice.equals(videoPlayerKey)) {
playQueue = info instanceof ChannelInfo NavigationHelper.playOnMainPlayer(this, playQueue, false);
? new ChannelPlayQueue((ChannelInfo) info) } else if (choice.playerChoice.equals(backgroundPlayerKey)) {
: new PlaylistPlayQueue((PlaylistInfo) info);
if (playerChoice.equals(videoPlayerKey)) {
openMainPlayer(playQueue, choice);
} else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
} else if (playerChoice.equals(popupPlayerKey)) { } else if (choice.playerChoice.equals(popupPlayerKey)) {
NavigationHelper.playOnPopupPlayer(this, playQueue, true); NavigationHelper.playOnPopupPlayer(this, playQueue, true);
} }
}
}; };
} }
private void openMainPlayer(final PlayQueue playQueue, final Choice choice) {
NavigationHelper.playOnMainPlayer(this, playQueue, choice.linkType,
choice.url, "", true, true);
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();

View File

@ -110,6 +110,7 @@ import org.schabi.newpipe.views.LargeTextMovementMethod;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import icepick.State; import icepick.State;
@ -128,7 +129,7 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfi
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class VideoDetailFragment public final class VideoDetailFragment
extends BaseStateFragment<StreamInfo> extends BaseStateFragment<StreamInfo>
implements BackPressable, implements BackPressable,
SharedPreferences.OnSharedPreferenceChangeListener, SharedPreferences.OnSharedPreferenceChangeListener,
@ -136,7 +137,7 @@ public class VideoDetailFragment
View.OnLongClickListener, View.OnLongClickListener,
PlayerServiceExtendedEventListener, PlayerServiceExtendedEventListener,
OnKeyDownListener { OnKeyDownListener {
public static final String AUTO_PLAY = "auto_play"; public static final String KEY_SWITCHING_PLAYERS = "switching_players";
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1; private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int COMMENTS_UPDATE_FLAG = 0x2; private static final int COMMENTS_UPDATE_FLAG = 0x2;
@ -167,19 +168,23 @@ public class VideoDetailFragment
@State @State
protected int serviceId = Constants.NO_SERVICE_ID; protected int serviceId = Constants.NO_SERVICE_ID;
@State @State
protected String name; @NonNull
protected String title = "";
@State @State
protected String url; @Nullable
protected static PlayQueue playQueue; protected String url = null;
@Nullable
protected PlayQueue playQueue = null;
@State @State
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
@State @State
protected boolean autoPlayEnabled = true; protected boolean autoPlayEnabled = true;
private static StreamInfo currentInfo; @Nullable
private StreamInfo currentInfo = null;
private Disposable currentWorker; private Disposable currentWorker;
@NonNull @NonNull
private CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
@Nullable @Nullable
private Disposable positionSubscriber = null; private Disposable positionSubscriber = null;
@ -284,6 +289,7 @@ public class VideoDetailFragment
|| (currentInfo != null || (currentInfo != null
&& isAutoplayEnabled() && isAutoplayEnabled()
&& player.getParentActivity() == null)) { && player.getParentActivity() == null)) {
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer(); openVideoPlayer();
} }
} }
@ -298,8 +304,10 @@ public class VideoDetailFragment
/*////////////////////////////////////////////////////////////////////////*/ /*////////////////////////////////////////////////////////////////////////*/
public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, public static VideoDetailFragment getInstance(final int serviceId,
final String name, final PlayQueue queue) { @Nullable final String videoUrl,
@NonNull final String name,
@Nullable final PlayQueue queue) {
final VideoDetailFragment instance = new VideoDetailFragment(); final VideoDetailFragment instance = new VideoDetailFragment();
instance.setInitialData(serviceId, videoUrl, name, queue); instance.setInitialData(serviceId, videoUrl, name, queue);
return instance; return instance;
@ -444,8 +452,8 @@ public class VideoDetailFragment
switch (requestCode) { switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST: case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
NavigationHelper NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
.openVideoDetailFragment(getFM(), serviceId, url, name); serviceId, url, title, null, false);
} else { } else {
Log.e(TAG, "ReCaptcha failed"); Log.e(TAG, "ReCaptcha failed");
} }
@ -514,6 +522,7 @@ public class VideoDetailFragment
} }
break; break;
case R.id.detail_thumbnail_root_layout: case R.id.detail_thumbnail_root_layout:
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer(); openVideoPlayer();
break; break;
case R.id.detail_title_root_layout: case R.id.detail_title_root_layout:
@ -530,6 +539,7 @@ public class VideoDetailFragment
player.hideControls(0, 0); player.hideControls(0, 0);
showSystemUi(); showSystemUi();
} else { } else {
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer(); openVideoPlayer();
} }
@ -791,7 +801,7 @@ public class VideoDetailFragment
player.onPause(); player.onPause();
} }
restoreDefaultOrientation(); restoreDefaultOrientation();
setAutoplay(false); setAutoPlay(false);
return true; return true;
} }
@ -819,14 +829,11 @@ public class VideoDetailFragment
} }
private void setupFromHistoryItem(final StackItem item) { private void setupFromHistoryItem(final StackItem item) {
setAutoplay(false); setAutoPlay(false);
hideMainPlayer(); hideMainPlayer();
setInitialData( setInitialData(item.getServiceId(), item.getUrl(),
item.getServiceId(), item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue());
item.getUrl(),
!TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "",
item.getPlayQueue());
startLoading(false); startLoading(false);
// Maybe an item was deleted in background activity // Maybe an item was deleted in background activity
@ -860,18 +867,17 @@ public class VideoDetailFragment
} }
} }
public void selectAndLoadVideo(final int sid, final String videoUrl, final String title, public void selectAndLoadVideo(final int newServiceId,
final PlayQueue queue) { @Nullable final String newUrl,
// Situation when user switches from players to main player. @NonNull final String newTitle,
// All needed data is here, we can start watching @Nullable final PlayQueue newQueue) {
if (this.playQueue != null && this.playQueue.equals(queue)) { if (player != null && newQueue != null && playQueue != null
openVideoPlayer(); && !Objects.equals(newQueue.getItem(), playQueue.getItem())) {
return; // Preloading can be disabled since playback is surely being replaced.
}
setInitialData(sid, videoUrl, title, queue);
if (player != null) {
player.disablePreloadingOfCurrentTrack(); player.disablePreloadingOfCurrentTrack();
} }
setInitialData(newServiceId, newUrl, newTitle, newQueue);
startLoading(false, true); startLoading(false, true);
} }
@ -956,7 +962,7 @@ public class VideoDetailFragment
playQueue = new SinglePlayQueue(result); playQueue = new SinglePlayQueue(result);
} }
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue)) { if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue)) {
stack.push(new StackItem(serviceId, url, name, playQueue)); stack.push(new StackItem(serviceId, url, title, playQueue));
} }
} }
if (isAutoplayEnabled()) { if (isAutoplayEnabled()) {
@ -977,7 +983,7 @@ public class VideoDetailFragment
if (shouldShowComments()) { if (shouldShowComments()) {
pageAdapter.addFragment( pageAdapter.addFragment(
CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG); CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
} }
if (showRelatedStreams && null == relatedStreamsLayout) { if (showRelatedStreams && null == relatedStreamsLayout) {
@ -1068,7 +1074,7 @@ public class VideoDetailFragment
} }
} }
private void openVideoPlayer() { public void openVideoPlayer() {
if (PreferenceManager.getDefaultSharedPreferences(activity) if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) { .getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
showExternalPlaybackDialog(); showExternalPlaybackDialog();
@ -1094,7 +1100,7 @@ public class VideoDetailFragment
private void openMainPlayer() { private void openMainPlayer() {
if (playerService == null) { if (playerService == null) {
PlayerHolder.startService(App.getApp(), true, this); PlayerHolder.startService(App.getApp(), autoPlayEnabled, this);
return; return;
} }
if (currentInfo == null) { if (currentInfo == null) {
@ -1105,11 +1111,13 @@ public class VideoDetailFragment
// Video view can have elements visible from popup, // Video view can have elements visible from popup,
// We hide it here but once it ready the view will be shown in handleIntent() // We hide it here but once it ready the view will be shown in handleIntent()
if (playerService.getView() != null) {
playerService.getView().setVisibility(View.GONE); playerService.getView().setVisibility(View.GONE);
}
addVideoPlayerView(); addVideoPlayerView();
final Intent playerIntent = NavigationHelper final Intent playerIntent = NavigationHelper
.getPlayerIntent(requireContext(), MainPlayer.class, queue, null, true); .getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled);
activity.startService(playerIntent); activity.startService(playerIntent);
} }
@ -1143,8 +1151,8 @@ public class VideoDetailFragment
// Utils // Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public void setAutoplay(final boolean autoplay) { public void setAutoPlay(final boolean autoPlay) {
this.autoPlayEnabled = autoplay; this.autoPlayEnabled = autoPlay;
} }
private void startOnExternalPlayer(@NonNull final Context context, private void startOnExternalPlayer(@NonNull final Context context,
@ -1166,7 +1174,7 @@ public class VideoDetailFragment
.getBoolean(getString(R.string.use_external_video_player_key), false); .getBoolean(getString(R.string.use_external_video_player_key), false);
} }
// This method overrides default behaviour when setAutoplay() is called. // This method overrides default behaviour when setAutoPlay() is called.
// Don't auto play if the user selected an external player or disabled it in settings // Don't auto play if the user selected an external player or disabled it in settings
private boolean isAutoplayEnabled() { private boolean isAutoplayEnabled() {
return autoPlayEnabled return autoPlayEnabled
@ -1302,12 +1310,14 @@ public class VideoDetailFragment
contentRootLayoutHiding.setVisibility(View.VISIBLE); contentRootLayoutHiding.setVisibility(View.VISIBLE);
} }
protected void setInitialData(final int sid, final String u, final String title, protected void setInitialData(final int newServiceId,
final PlayQueue queue) { @Nullable final String newUrl,
this.serviceId = sid; @NonNull final String newTitle,
this.url = u; @Nullable final PlayQueue newPlayQueue) {
this.name = !TextUtils.isEmpty(title) ? title : ""; this.serviceId = newServiceId;
this.playQueue = queue; this.url = newUrl;
this.title = newTitle;
this.playQueue = newPlayQueue;
} }
private void setErrorImage(final int imageResource) { private void setErrorImage(final int imageResource) {
@ -1400,7 +1410,7 @@ public class VideoDetailFragment
animateView(detailPositionView, false, 100); animateView(detailPositionView, false, 100);
animateView(positionView, false, 50); animateView(positionView, false, 50);
videoTitleTextView.setText(name != null ? name : ""); videoTitleTextView.setText(title);
videoTitleTextView.setMaxLines(1); videoTitleTextView.setMaxLines(1);
animateView(videoTitleTextView, true, 0); animateView(videoTitleTextView, true, 0);
@ -1445,7 +1455,7 @@ public class VideoDetailFragment
} }
} }
animateView(thumbnailPlayButton, true, 200); animateView(thumbnailPlayButton, true, 200);
videoTitleTextView.setText(name); videoTitleTextView.setText(title);
if (!TextUtils.isEmpty(info.getSubChannelName())) { if (!TextUtils.isEmpty(info.getSubChannelName())) {
displayBothUploaderAndSubChannel(info); displayBothUploaderAndSubChannel(info);
@ -1738,7 +1748,7 @@ public class VideoDetailFragment
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "onQueueUpdate() called with: serviceId = [" Log.d(TAG, "onQueueUpdate() called with: serviceId = ["
+ serviceId + "], videoUrl = [" + url + "], name = [" + serviceId + "], videoUrl = [" + url + "], name = ["
+ name + "], playQueue = [" + playQueue + "]"); + title + "], playQueue = [" + playQueue + "]");
} }
// This should be the only place where we push data to stack. // This should be the only place where we push data to stack.
@ -1823,7 +1833,7 @@ 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);
// Delay execution just because it freezes the main thread, and while playing // Delay execution just because it freezes the main thread, and while playing
// next/previous video you see visual glitches // next/previous video you see visual glitches
// (when non-vertical video goes after vertical video) // (when non-vertical video goes after vertical video)
@ -2037,7 +2047,7 @@ public class VideoDetailFragment
private void checkLandscape() { private void checkLandscape() {
if ((!player.isPlaying() && player.getPlayQueue() != playQueue) if ((!player.isPlaying() && player.getPlayQueue() != playQueue)
|| player.getPlayQueue() == null) { || player.getPlayQueue() == null) {
setAutoplay(true); setAutoPlay(true);
} }
player.checkLandscape(); player.checkLandscape();
@ -2287,10 +2297,10 @@ public class VideoDetailFragment
}); });
} }
private void updateOverlayData(@Nullable final String title, private void updateOverlayData(@Nullable final String overlayTitle,
@Nullable final String uploader, @Nullable final String uploader,
@Nullable final String thumbnailUrl) { @Nullable final String thumbnailUrl) {
overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title); overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle);
overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader);
overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(thumbnailUrl)) { if (!TextUtils.isEmpty(thumbnailUrl)) {

View File

@ -321,8 +321,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
private void onStreamSelected(final StreamInfoItem selectedItem) { private void onStreamSelected(final StreamInfoItem selectedItem) {
onItemSelected(selectedItem); onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(getFM(), NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
null, false);
} }
protected void onScrollToBottom() { protected void onScrollToBottom() {

View File

@ -517,7 +517,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
monitorSubscription(result); monitorSubscription(result);
headerPlayAllButton.setOnClickListener(view -> NavigationHelper headerPlayAllButton.setOnClickListener(view -> NavigationHelper
.playOnMainPlayer(activity, getPlayQueue(), true)); .playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> NavigationHelper headerPopupButton.setOnClickListener(view -> NavigationHelper
.playOnPopupPlayer(activity, getPlayQueue(), false)); .playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> NavigationHelper headerBackgroundButton.setOnClickListener(view -> NavigationHelper

View File

@ -319,7 +319,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
.subscribe(getPlaylistBookmarkSubscriber()); .subscribe(getPlaylistBookmarkSubscriber());
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->

View File

@ -25,6 +25,7 @@ import org.reactivestreams.Subscription;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
@ -149,11 +150,10 @@ public class StatisticsPlaylistFragment
@Override @Override
public void selected(final LocalItem selectedItem) { public void selected(final LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) { if (selectedItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; final StreamEntity item =
NavigationHelper.openVideoDetailFragment(getFM(), ((StreamStatisticsEntry) selectedItem).getStreamEntity();
item.getStreamEntity().getServiceId(), NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
item.getStreamEntity().getUrl(), item.getServiceId(), item.getUrl(), item.getTitle(), null, false);
item.getStreamEntity().getTitle());
} }
} }
@ -325,7 +325,7 @@ public class StatisticsPlaylistFragment
} }
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->

View File

@ -30,6 +30,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
@ -178,10 +179,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override @Override
public void selected(final LocalItem selectedItem) { public void selected(final LocalItem selectedItem) {
if (selectedItem instanceof PlaylistStreamEntry) { if (selectedItem instanceof PlaylistStreamEntry) {
final PlaylistStreamEntry item = (PlaylistStreamEntry) selectedItem; final StreamEntity item =
NavigationHelper.openVideoDetailFragment(getFM(), ((PlaylistStreamEntry) selectedItem).getStreamEntity();
item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(), NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
item.getStreamEntity().getTitle()); item.getServiceId(), item.getUrl(), item.getTitle(), null, false);
} }
} }
@ -494,7 +495,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
setVideoCount(itemListAdapter.getItemsList().size()); setVideoCount(itemListAdapter.getItemsList().size());
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->

View File

@ -2,11 +2,8 @@ package org.schabi.newpipe.player;
import android.content.Intent; import android.content.Intent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
public final class BackgroundPlayerActivity extends ServicePlayerActivity { public final class BackgroundPlayerActivity extends ServicePlayerActivity {
@ -46,31 +43,6 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity {
return R.menu.menu_play_queue_bg; return R.menu.menu_play_queue_bg;
} }
@Override
public boolean onPlayerOptionSelected(final MenuItem item) {
if (item.getItemId() == R.id.action_switch_popup) {
if (!PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
return true;
}
this.player.setRecovery();
NavigationHelper.playOnPopupPlayer(
getApplicationContext(), player.playQueue, this.player.isPlaying());
return true;
}
if (item.getItemId() == R.id.action_switch_background) {
this.player.setRecovery();
NavigationHelper.playOnBackgroundPlayer(
getApplicationContext(), player.playQueue, this.player.isPlaying());
return true;
}
return false;
}
@Override @Override
public void setupMenu(final Menu menu) { public void setupMenu(final Menu menu) {
if (player == null) { if (player == null) {

View File

@ -125,7 +125,7 @@ public abstract class BasePlayer implements
@NonNull @NonNull
public static final String RESUME_PLAYBACK = "resume_playback"; public static final String RESUME_PLAYBACK = "resume_playback";
@NonNull @NonNull
public static final String START_PAUSED = "start_paused"; public static final String PLAY_WHEN_READY = "play_when_ready";
@NonNull @NonNull
public static final String SELECT_ON_APPEND = "select_on_append"; public static final String SELECT_ON_APPEND = "select_on_append";
@NonNull @NonNull
@ -224,7 +224,7 @@ public abstract class BasePlayer implements
this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
final TrackSelection.Factory trackSelectionFactory = PlayerHelper final TrackSelection.Factory trackSelectionFactory = PlayerHelper
.getQualitySelector(context); .getQualitySelector();
this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory); this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory);
this.loadControl = new LoadController(); this.loadControl = new LoadController();
@ -302,6 +302,7 @@ public abstract class BasePlayer implements
final boolean samePlayQueue = playQueue != null && playQueue.equals(queue); final boolean samePlayQueue = playQueue != null && playQueue.equals(queue);
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode()); final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final boolean playWhenReady = intent.getBooleanExtra(PLAY_WHEN_READY, true);
final boolean isMuted = intent final boolean isMuted = intent
.getBooleanExtra(IS_MUTED, simpleExoPlayer != null && isMuted()); .getBooleanExtra(IS_MUTED, simpleExoPlayer != null && isMuted());
@ -327,16 +328,20 @@ public abstract class BasePlayer implements
simpleExoPlayer.retry(); simpleExoPlayer.retry();
} }
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition()); simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
return; simpleExoPlayer.setPlayWhenReady(playWhenReady);
} else if (samePlayQueue && !playQueue.isDisposed() && simpleExoPlayer != null) { } else if (simpleExoPlayer != null
&& samePlayQueue
&& playQueue != null
&& !playQueue.isDisposed()) {
// Do not re-init the same PlayQueue. Save time // Do not re-init the same PlayQueue. Save time
// Player can have state = IDLE when playback is stopped or failed // Player can have state = IDLE when playback is stopped or failed
// and we should retry() in this case // and we should retry() in this case
if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) { if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) {
simpleExoPlayer.retry(); simpleExoPlayer.retry();
} }
return; simpleExoPlayer.setPlayWhenReady(playWhenReady);
} else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false)
&& isPlaybackResumeEnabled() && isPlaybackResumeEnabled()
&& !samePlayQueue) { && !samePlayQueue) {
@ -351,7 +356,7 @@ public abstract class BasePlayer implements
state -> { state -> {
queue.setRecovery(queue.getIndex(), state.getProgressTime()); queue.setRecovery(queue.getIndex(), state.getProgressTime());
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, initPlayback(queue, repeatMode, playbackSpeed, playbackPitch,
playbackSkipSilence, true, isMuted); playbackSkipSilence, playWhenReady, isMuted);
}, },
error -> { error -> {
if (DEBUG) { if (DEBUG) {
@ -359,24 +364,22 @@ public abstract class BasePlayer implements
} }
// In case any error we can start playback without history // In case any error we can start playback without history
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, initPlayback(queue, repeatMode, playbackSpeed, playbackPitch,
playbackSkipSilence, true, isMuted); playbackSkipSilence, playWhenReady, isMuted);
}, },
() -> { () -> {
// Completed but not found in history // Completed but not found in history
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, initPlayback(queue, repeatMode, playbackSpeed, playbackPitch,
playbackSkipSilence, true, isMuted); playbackSkipSilence, playWhenReady, isMuted);
} }
); );
databaseUpdateReactor.add(stateLoader); databaseUpdateReactor.add(stateLoader);
return;
}
} }
} else {
// Good to go... // Good to go...
// In a case of equal PlayQueues we can re-init old one but only when it is disposed // In a case of equal PlayQueues we can re-init old one but only when it is disposed
initPlayback(samePlayQueue ? playQueue : queue, repeatMode, initPlayback(samePlayQueue ? playQueue : queue, repeatMode, playbackSpeed,
playbackSpeed, playbackPitch, playbackSkipSilence, playbackPitch, playbackSkipSilence, playWhenReady, isMuted);
!intent.getBooleanExtra(START_PAUSED, false), }
isMuted);
} }
private PlaybackParameters retrievePlaybackParametersFromPreferences() { private PlaybackParameters retrievePlaybackParametersFromPreferences() {

View File

@ -30,6 +30,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -231,6 +232,7 @@ public final class MainPlayer extends Service {
return metrics.heightPixels < metrics.widthPixels; return metrics.heightPixels < metrics.widthPixels;
} }
@Nullable
public View getView() { public View getView() {
if (playerImpl == null) { if (playerImpl == null) {
return null; return null;
@ -240,7 +242,7 @@ public final class MainPlayer extends Service {
} }
public void removeViewFromParent() { public void removeViewFromParent() {
if (getView().getParent() != null) { if (getView() != null && getView().getParent() != null) {
if (playerImpl.getParentActivity() != null) { if (playerImpl.getParentActivity() != null) {
// This means view was added to fragment // This means view was added to fragment
final ViewGroup parent = (ViewGroup) getView().getParent(); final ViewGroup parent = (ViewGroup) getView().getParent();

View File

@ -27,9 +27,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
@ -42,9 +40,9 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
import org.schabi.newpipe.util.Constants;
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.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.util.Collections; import java.util.Collections;
@ -113,9 +111,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
public abstract int getPlayerOptionMenuResource(); public abstract int getPlayerOptionMenuResource();
public abstract boolean onPlayerOptionSelected(MenuItem item);
public abstract void setupMenu(Menu m); public abstract void setupMenu(Menu m);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Activity Lifecycle // Activity Lifecycle
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -187,12 +184,22 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
return true; return true;
case R.id.action_switch_main: case R.id.action_switch_main:
this.player.setRecovery(); this.player.setRecovery();
getApplicationContext().startActivity( NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true);
getSwitchIntent(MainActivity.class, MainPlayer.PlayerType.VIDEO) return true;
.putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())); case R.id.action_switch_popup:
if (PermissionHelper.isPopupEnabled(this)) {
this.player.setRecovery();
NavigationHelper.playOnPopupPlayer(this, player.playQueue, true);
} else {
PermissionHelper.showPopupEnablementToast(this);
}
return true;
case R.id.action_switch_background:
this.player.setRecovery();
NavigationHelper.playOnBackgroundPlayer(this, player.playQueue, true);
return true; return true;
} }
return onPlayerOptionSelected(item) || super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override @Override
@ -201,24 +208,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
unbind(); unbind();
} }
protected Intent getSwitchIntent(final Class clazz, final MainPlayer.PlayerType playerType) {
return NavigationHelper.getPlayerIntent(getApplicationContext(), clazz,
this.player.getPlayQueue(), this.player.getRepeatMode(),
this.player.getPlaybackSpeed(), this.player.getPlaybackPitch(),
this.player.getPlaybackSkipSilence(),
null,
true,
!this.player.isPlaying(),
this.player.isMuted())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM)
.putExtra(Constants.KEY_URL, this.player.getVideoUrl())
.putExtra(Constants.KEY_TITLE, this.player.getVideoTitle())
.putExtra(Constants.KEY_SERVICE_ID,
this.player.getCurrentMetadata().getMetadata().getServiceId())
.putExtra(VideoPlayer.PLAYER_TYPE, playerType);
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Service Connection // Service Connection
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -367,7 +356,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1,
Menu.NONE, R.string.play_queue_stream_detail); Menu.NONE, R.string.play_queue_stream_detail);
detail.setOnMenuItemClickListener(menuItem -> { detail.setOnMenuItemClickListener(menuItem -> {
onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle()); // playQueue is null since we don't want any queue change
NavigationHelper.openVideoDetail(this, item.getServiceId(), item.getUrl(),
item.getTitle(), null, false);
return true; return true;
}); });
@ -454,11 +445,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
}; };
} }
private void onOpenDetail(final int serviceId, final String videoUrl,
final String videoTitle) {
NavigationHelper.openVideoDetail(this, serviceId, videoUrl, videoTitle);
}
private void scrollToSelected() { private void scrollToSelected() {
if (player == null) { if (player == null) {
return; return;

View File

@ -76,9 +76,7 @@ import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
@ -97,7 +95,6 @@ import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KoreUtil; import org.schabi.newpipe.util.KoreUtil;
import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ListHelper;
@ -260,7 +257,12 @@ public class VideoPlayerImpl extends VideoPlayer
onQueueClosed(); onQueueClosed();
// Android TV: without it focus will frame the whole player // Android TV: without it focus will frame the whole player
playPauseButton.requestFocus(); playPauseButton.requestFocus();
if (simpleExoPlayer.getPlayWhenReady()) {
onPlay(); onPlay();
} else {
onPause();
}
} }
NavigationHelper.sendPlayerStartedEvent(service); NavigationHelper.sendPlayerStartedEvent(service);
} }
@ -756,40 +758,6 @@ public class VideoPlayerImpl extends VideoPlayer
setupScreenRotationButton(); setupScreenRotationButton();
} }
public void switchFromPopupToMain() {
if (DEBUG) {
Log.d(TAG, "switchFromPopupToMain() called");
}
if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) {
return;
}
setRecovery();
service.removeViewFromParent();
final Intent intent = NavigationHelper.getPlayerIntent(
service,
MainActivity.class,
this.getPlayQueue(),
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
null,
true,
!isPlaying(),
isMuted()
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_SERVICE_ID,
getCurrentMetadata().getMetadata().getServiceId());
intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
intent.putExtra(Constants.KEY_URL, getVideoUrl());
intent.putExtra(Constants.KEY_TITLE, getVideoTitle());
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
service.onDestroy();
context.startActivity(intent);
}
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
super.onClick(v); super.onClick(v);
@ -817,7 +785,9 @@ public class VideoPlayerImpl extends VideoPlayer
} else if (v.getId() == openInBrowser.getId()) { } else if (v.getId() == openInBrowser.getId()) {
onOpenInBrowserClicked(); onOpenInBrowserClicked();
} else if (v.getId() == fullscreenButton.getId()) { } else if (v.getId() == fullscreenButton.getId()) {
switchFromPopupToMain(); setRecovery();
NavigationHelper.playOnMainPlayer(context, getPlayQueue(), true);
return;
} else if (v.getId() == screenRotationButton.getId()) { } else if (v.getId() == screenRotationButton.getId()) {
// Only if it's not a vertical video or vertical video but in landscape with locked // Only if it's not a vertical video or vertical video but in landscape with locked
// orientation a screen orientation can be changed automatically // orientation a screen orientation can be changed automatically

View File

@ -63,7 +63,7 @@ abstract class BasePlayerGestureListener(
private var isMovingInPopup = false private var isMovingInPopup = false
private var isResizing = false private var isResizing = false
private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service) private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity()
// [popup] initial coordinates and distance between fingers // [popup] initial coordinates and distance between fingers
private var initPointerDistance = -1.0 private var initPointerDistance = -1.0
@ -104,9 +104,6 @@ abstract class BasePlayerGestureListener(
} }
private fun onTouchInPopup(v: View, event: MotionEvent): Boolean { private fun onTouchInPopup(v: View, event: MotionEvent): Boolean {
if (playerImpl == null) {
return false
}
playerImpl.gestureDetector.onTouchEvent(event) playerImpl.gestureDetector.onTouchEvent(event)
if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) { if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) {
if (DEBUG) { if (DEBUG) {

View File

@ -111,10 +111,10 @@ public class PlayerGestureListener
} }
if (playerType == MainPlayer.PlayerType.VIDEO) { if (playerType == MainPlayer.PlayerType.VIDEO) {
if (portion == DisplayPortion.LEFT_HALF) { if (portion == DisplayPortion.LEFT_HALF) {
onScrollMainVolume(distanceX, distanceY); onScrollMainBrightness(distanceX, distanceY);
} else /* DisplayPortion.RIGHT_HALF */ { } else /* DisplayPortion.RIGHT_HALF */ {
onScrollMainBrightness(distanceX, distanceY); onScrollMainVolume(distanceX, distanceY);
} }
} else /* MainPlayer.PlayerType.POPUP */ { } else /* MainPlayer.PlayerType.POPUP */ {

View File

@ -164,7 +164,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An
@Override @Override
public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) {
if (!PlayerHelper.isUsingDSP(context)) { if (!PlayerHelper.isUsingDSP()) {
return; return;
} }

View File

@ -295,7 +295,7 @@ public final class PlayerHelper {
return 60000; return 60000;
} }
public static TrackSelection.Factory getQualitySelector(@NonNull final Context context) { public static TrackSelection.Factory getQualitySelector() {
return new AdaptiveTrackSelection.Factory( return new AdaptiveTrackSelection.Factory(
1000, 1000,
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
@ -303,11 +303,11 @@ public final class PlayerHelper {
AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION); AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION);
} }
public static boolean isUsingDSP(@NonNull final Context context) { public static boolean isUsingDSP() {
return true; return true;
} }
public static int getTossFlingVelocity(@NonNull final Context context) { public static int getTossFlingVelocity() {
return 2500; return 2500;
} }

View File

@ -49,6 +49,17 @@ public final class PlayerHolder {
return player.getPlayerType(); return player.getPlayerType();
} }
public static boolean isPlaying() {
if (player == null) {
return false;
}
return player.isPlaying();
}
public static boolean isPlayerOpen() {
return player != null;
}
public static void setListener(final PlayerServiceExtendedEventListener newListener) { public static void setListener(final PlayerServiceExtendedEventListener newListener) {
listener = newListener; listener = newListener;
// Force reload data from service // Force reload data from service

View File

@ -1,12 +1,8 @@
package org.schabi.newpipe.player.playqueue; package org.schabi.newpipe.player.playqueue;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.player.playqueue.events.AppendEvent; import org.schabi.newpipe.player.playqueue.events.AppendEvent;
import org.schabi.newpipe.player.playqueue.events.ErrorEvent; import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
@ -43,7 +39,6 @@ import io.reactivex.subjects.BehaviorSubject;
* </p> * </p>
*/ */
public abstract class PlayQueue implements Serializable { public abstract class PlayQueue implements Serializable {
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
public static final boolean DEBUG = MainActivity.DEBUG; public static final boolean DEBUG = MainActivity.DEBUG;
private ArrayList<PlayQueueItem> backup; private ArrayList<PlayQueueItem> backup;
@ -55,7 +50,6 @@ public abstract class PlayQueue implements Serializable {
private transient BehaviorSubject<PlayQueueEvent> eventBroadcast; private transient BehaviorSubject<PlayQueueEvent> eventBroadcast;
private transient Flowable<PlayQueueEvent> broadcastReceiver; private transient Flowable<PlayQueueEvent> broadcastReceiver;
private transient Subscription reportingReactor;
private transient boolean disposed; private transient boolean disposed;
@ -87,10 +81,6 @@ public abstract class PlayQueue implements Serializable {
broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER) broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.startWith(new InitEvent()); .startWith(new InitEvent());
if (DEBUG) {
broadcastReceiver.subscribe(getSelfReporter());
}
} }
/** /**
@ -100,13 +90,9 @@ public abstract class PlayQueue implements Serializable {
if (eventBroadcast != null) { if (eventBroadcast != null) {
eventBroadcast.onComplete(); eventBroadcast.onComplete();
} }
if (reportingReactor != null) {
reportingReactor.cancel();
}
eventBroadcast = null; eventBroadcast = null;
broadcastReceiver = null; broadcastReceiver = null;
reportingReactor = null;
disposed = true; disposed = true;
} }
@ -544,35 +530,5 @@ public abstract class PlayQueue implements Serializable {
eventBroadcast.onNext(event); eventBroadcast.onNext(event);
} }
} }
private Subscriber<PlayQueueEvent> getSelfReporter() {
return new Subscriber<PlayQueueEvent>() {
@Override
public void onSubscribe(final Subscription s) {
if (reportingReactor != null) {
reportingReactor.cancel();
}
reportingReactor = s;
reportingReactor.request(1);
}
@Override
public void onNext(final PlayQueueEvent event) {
Log.d(TAG, "Received broadcast: " + event.type().name() + ". "
+ "Current index: " + getIndex() + ", play queue length: " + size() + ".");
reportingReactor.request(1);
}
@Override
public void onError(final Throwable t) {
Log.e(TAG, "Received broadcast error", t);
}
@Override
public void onComplete() {
Log.d(TAG, "Broadcast is shutting down.");
}
};
}
} }

View File

@ -64,6 +64,20 @@ public class PlayQueueItem implements Serializable {
this.recoveryPosition = RECOVERY_UNSET; this.recoveryPosition = RECOVERY_UNSET;
} }
@Override
public boolean equals(final Object o) {
if (o instanceof PlayQueueItem) {
return url.equals(((PlayQueueItem) o).url);
} else {
return false;
}
}
@Override
public int hashCode() {
return url.hashCode();
}
@NonNull @NonNull
public String getTitle() { public String getTitle() {
return title; return title;

View File

@ -18,7 +18,6 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceManager;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
@ -38,7 +37,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment;
@ -52,13 +50,14 @@ import org.schabi.newpipe.player.BackgroundPlayerActivity;
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.VideoPlayer;
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.settings.SettingsActivity; import org.schabi.newpipe.settings.SettingsActivity;
import java.util.ArrayList; import java.util.ArrayList;
@SuppressWarnings({"unused"})
public final class NavigationHelper { public final class NavigationHelper {
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
@ -73,7 +72,6 @@ public final class NavigationHelper {
public static <T> Intent getPlayerIntent(@NonNull final Context context, public static <T> Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class<T> targetClazz, @NonNull final Class<T> targetClazz,
@Nullable final PlayQueue playQueue, @Nullable final PlayQueue playQueue,
@Nullable final String quality,
final boolean resumePlayback) { final boolean resumePlayback) {
final Intent intent = new Intent(context, targetClazz); final Intent intent = new Intent(context, targetClazz);
@ -83,9 +81,6 @@ public final class NavigationHelper {
intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
} }
} }
if (quality != null) {
intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
}
intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback); intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback);
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO); intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO);
@ -96,8 +91,10 @@ public final class NavigationHelper {
public static <T> Intent getPlayerIntent(@NonNull final Context context, public static <T> Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class<T> targetClazz, @NonNull final Class<T> targetClazz,
@Nullable final PlayQueue playQueue, @Nullable final PlayQueue playQueue,
final boolean resumePlayback) { final boolean resumePlayback,
return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback); final boolean playWhenReady) {
return getPlayerIntent(context, targetClazz, playQueue, resumePlayback)
.putExtra(BasePlayer.PLAY_WHEN_READY, playWhenReady);
} }
@NonNull @NonNull
@ -111,61 +108,25 @@ public final class NavigationHelper {
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend); .putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
} }
@NonNull
public static <T> Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class<T> targetClazz,
@Nullable final PlayQueue playQueue,
final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final boolean playbackSkipSilence,
@Nullable final String playbackQuality,
final boolean resumePlayback,
final boolean startPaused,
final boolean isMuted) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.START_PAUSED, startPaused)
.putExtra(BasePlayer.IS_MUTED, isMuted);
}
public static void playOnMainPlayer(final AppCompatActivity activity, public static void playOnMainPlayer(final AppCompatActivity activity,
@NonNull final PlayQueue playQueue) {
final PlayQueueItem item = playQueue.getItem();
assert item != null;
openVideoDetailFragment(activity, activity.getSupportFragmentManager(),
item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, false);
}
public static void playOnMainPlayer(final Context context,
@NonNull final PlayQueue playQueue,
final boolean switchingPlayers) {
final PlayQueueItem item = playQueue.getItem();
assert item != null;
openVideoDetail(context,
item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, switchingPlayers);
}
public static void playOnPopupPlayer(final Context context,
final PlayQueue queue, final PlayQueue queue,
final boolean autoPlay) {
playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay);
}
public static void playOnMainPlayer(final FragmentManager fragmentManager,
final PlayQueue queue,
final boolean autoPlay) {
final PlayQueueItem currentStream = queue.getItem();
openVideoDetailFragment(
fragmentManager,
currentStream.getServiceId(),
currentStream.getUrl(),
currentStream.getTitle(),
autoPlay,
queue);
}
public static void playOnMainPlayer(@NonNull final Context context,
@Nullable final PlayQueue queue,
@NonNull final StreamingService.LinkType linkType,
@NonNull final String url,
@NonNull final String title,
final boolean autoPlay,
final boolean resumePlayback) {
final Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_LINK_TYPE, linkType);
intent.putExtra(Constants.KEY_URL, url);
intent.putExtra(Constants.KEY_TITLE, title);
intent.putExtra(VideoDetailFragment.AUTO_PLAY, autoPlay);
context.startActivity(intent);
}
public static void playOnPopupPlayer(final Context context, final PlayQueue queue,
final boolean resumePlayback) { final boolean resumePlayback) {
if (!PermissionHelper.isPopupEnabled(context)) { if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context); PermissionHelper.showPopupEnablementToast(context);
@ -300,9 +261,6 @@ public final class NavigationHelper {
.setNegativeButton(R.string.cancel, (dialog, which) .setNegativeButton(R.string.cancel, (dialog, which)
-> Log.i("NavigationHelper", "You unlocked a secret unicorn.")) -> Log.i("NavigationHelper", "You unlocked a secret unicorn."))
.show(); .show();
// Log.e("NavigationHelper",
// "Either no Streaming player for audio was installed, "
// + "or something important crashed:");
} else { } else {
Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show();
} }
@ -358,41 +316,6 @@ public final class NavigationHelper {
.commit(); .commit();
} }
public static void openVideoDetailFragment(final FragmentManager fragmentManager,
final int serviceId, final String url,
final String title) {
openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null);
}
public static void openVideoDetailFragment(
final FragmentManager fragmentManager,
final int serviceId,
final String url,
final String title,
final boolean autoPlay,
final PlayQueue playQueue) {
final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder);
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
expandMainPlayer(fragment.requireActivity());
final VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
detailFragment.setAutoplay(autoPlay);
detailFragment
.selectAndLoadVideo(serviceId, url, title == null ? "" : title, playQueue);
detailFragment.scrollToTop();
return;
}
final VideoDetailFragment instance = VideoDetailFragment
.getInstance(serviceId, url, title == null ? "" : title, playQueue);
instance.setAutoplay(autoPlay);
defaultTransaction(fragmentManager)
.replace(R.id.fragment_player_holder, instance)
.runOnCommit(() -> expandMainPlayer(instance.requireActivity()))
.commit();
}
public static void expandMainPlayer(final Context context) { public static void expandMainPlayer(final Context context) {
context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER)); context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER));
} }
@ -409,33 +332,76 @@ public final class NavigationHelper {
.commitAllowingStateLoss(); .commitAllowingStateLoss();
} }
public static void openChannelFragment(final FragmentManager fragmentManager, private interface RunnableWithVideoDetailFragment {
final int serviceId, final String url, void run(VideoDetailFragment detailFragment);
final String name) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url,
name == null ? "" : name))
.addToBackStack(null)
.commit();
} }
public static void openCommentsFragment(final FragmentManager fragmentManager, public static void openVideoDetailFragment(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
final int serviceId,
@Nullable final String url,
@NonNull final String title,
@Nullable final PlayQueue playQueue,
final boolean switchingPlayers) {
final boolean autoPlay;
@Nullable final MainPlayer.PlayerType playerType = PlayerHolder.getType();
if (playerType == null) {
// no player open
autoPlay = PlayerHelper.isAutoplayAllowedByUser(context);
} else if (switchingPlayers) {
// switching player to main player
autoPlay = PlayerHolder.isPlaying(); // keep play/pause state
} else if (playerType == MainPlayer.PlayerType.VIDEO) {
// opening new stream while already playing in main player
autoPlay = PlayerHelper.isAutoplayAllowedByUser(context);
} else {
// opening new stream while already playing in another player
autoPlay = false;
}
final RunnableWithVideoDetailFragment onVideoDetailFragmentReady = (detailFragment) -> {
expandMainPlayer(detailFragment.requireActivity());
detailFragment.setAutoPlay(autoPlay);
if (switchingPlayers) {
// Situation when user switches from players to main player. All needed data is
// here, we can start watching (assuming newQueue equals playQueue).
detailFragment.openVideoPlayer();
} else {
detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue);
}
detailFragment.scrollToTop();
};
final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder);
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
onVideoDetailFragmentReady.run((VideoDetailFragment) fragment);
} else {
final VideoDetailFragment instance = VideoDetailFragment
.getInstance(serviceId, url, title, playQueue);
instance.setAutoPlay(autoPlay);
defaultTransaction(fragmentManager)
.replace(R.id.fragment_player_holder, instance)
.runOnCommit(() -> onVideoDetailFragmentReady.run(instance))
.commit();
}
}
public static void openChannelFragment(final FragmentManager fragmentManager,
final int serviceId, final String url, final int serviceId, final String url,
final String name) { @NonNull final String name) {
fragmentManager.beginTransaction() defaultTransaction(fragmentManager)
.setCustomAnimations(R.anim.switch_service_in, R.anim.switch_service_out) .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name))
.replace(R.id.fragment_holder, CommentsFragment.getInstance(serviceId, url,
name == null ? "" : name))
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
public static void openPlaylistFragment(final FragmentManager fragmentManager, public static void openPlaylistFragment(final FragmentManager fragmentManager,
final int serviceId, final String url, final int serviceId, final String url,
final String name) { @NonNull final String name) {
defaultTransaction(fragmentManager) defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name))
name == null ? "" : name))
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
@ -511,33 +477,26 @@ public final class NavigationHelper {
context.startActivity(mIntent); context.startActivity(mIntent);
} }
public static void openChannel(final Context context, final int serviceId, final String url) { public static void openVideoDetail(final Context context,
openChannel(context, serviceId, url, null); final int serviceId,
} final String url,
@NonNull final String title,
@Nullable final PlayQueue playQueue,
final boolean switchingPlayers) {
public static void openChannel(final Context context, final int serviceId, final Intent intent = getOpenIntent(context, url, serviceId,
final String url, final String name) {
final Intent openIntent = getOpenIntent(context, url, serviceId,
StreamingService.LinkType.CHANNEL);
if (name != null && !name.isEmpty()) {
openIntent.putExtra(Constants.KEY_TITLE, name);
}
context.startActivity(openIntent);
}
public static void openVideoDetail(final Context context, final int serviceId,
final String url) {
openVideoDetail(context, serviceId, url, null);
}
public static void openVideoDetail(final Context context, final int serviceId,
final String url, final String title) {
final Intent openIntent = getOpenIntent(context, url, serviceId,
StreamingService.LinkType.STREAM); StreamingService.LinkType.STREAM);
if (title != null && !title.isEmpty()) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
openIntent.putExtra(Constants.KEY_TITLE, title); intent.putExtra(Constants.KEY_TITLE, title);
intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers);
if (playQueue != null) {
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
if (cacheKey != null) {
intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
} }
context.startActivity(openIntent); }
context.startActivity(intent);
} }
public static void openMainActivity(final Context context) { public static void openMainActivity(final Context context) {
@ -550,7 +509,6 @@ public final class NavigationHelper {
public static void openRouterActivity(final Context context, final String url) { public static void openRouterActivity(final Context context, final String url) {
final Intent mIntent = new Intent(context, RouterActivity.class); final Intent mIntent = new Intent(context, RouterActivity.class);
mIntent.setData(Uri.parse(url)); mIntent.setData(Uri.parse(url));
mIntent.putExtra(RouterActivity.INTERNAL_ROUTE_KEY, true);
context.startActivity(mIntent); context.startActivity(mIntent);
} }
@ -564,14 +522,12 @@ public final class NavigationHelper {
context.startActivity(intent); context.startActivity(intent);
} }
public static boolean openDownloads(final Activity activity) { public static void openDownloads(final Activity activity) {
if (!PermissionHelper.checkStoragePermissions( if (PermissionHelper.checkStoragePermissions(
activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) {
return false;
}
final Intent intent = new Intent(activity, DownloadActivity.class); final Intent intent = new Intent(activity, DownloadActivity.class);
activity.startActivity(intent); activity.startActivity(intent);
return true; }
} }
public static Intent getPlayQueueActivityIntent(final Context context) { public static Intent getPlayQueueActivityIntent(final Context context) {
@ -600,7 +556,8 @@ public final class NavigationHelper {
return getIntentByLink(context, NewPipe.getServiceByUrl(url), url); return getIntentByLink(context, NewPipe.getServiceByUrl(url), url);
} }
public static Intent getIntentByLink(final Context context, final StreamingService service, public static Intent getIntentByLink(final Context context,
final StreamingService service,
final String url) throws ExtractionException { final String url) throws ExtractionException {
final StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); final StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
@ -609,15 +566,7 @@ public final class NavigationHelper {
+ " url=" + url); + " url=" + url);
} }
final Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType); return getOpenIntent(context, url, service.getServiceId(), linkType);
if (linkType == StreamingService.LinkType.STREAM) {
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
context.getString(R.string.autoplay_through_intent_key), false));
}
return rIntent;
} }
private static Uri openMarketUrl(final String packageName) { private static Uri openMarketUrl(final String packageName) {

View File

@ -21,7 +21,6 @@
<string name="use_external_video_player_key" translatable="false">use_external_video_player</string> <string name="use_external_video_player_key" translatable="false">use_external_video_player</string>
<string name="use_external_audio_player_key" translatable="false">use_external_audio_player</string> <string name="use_external_audio_player_key" translatable="false">use_external_audio_player</string>
<string name="autoplay_through_intent_key" translatable="false">autoplay_through_intent</string>
<string name="use_old_player_key" translatable="false">use_oldplayer</string> <string name="use_old_player_key" translatable="false">use_oldplayer</string>
<string name="volume_gesture_control_key" translatable="false">volume_gesture_control</string> <string name="volume_gesture_control_key" translatable="false">volume_gesture_control</string>