diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index e72d4609e..1d911c944 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -30,9 +30,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import androidx.preference.PreferenceManager; import android.util.Log; - import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -46,6 +44,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -55,6 +54,7 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.preference.PreferenceManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.navigation.NavigationView; @@ -71,8 +71,8 @@ import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.report.ErrorActivity; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; @@ -758,32 +758,36 @@ public class MainActivity extends AppCompatActivity { if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { final String url = intent.getStringExtra(Constants.KEY_URL); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); - final String title = intent.getStringExtra(Constants.KEY_TITLE); - switch (((StreamingService.LinkType) intent - .getSerializableExtra(Constants.KEY_LINK_TYPE))) { + String title = intent.getStringExtra(Constants.KEY_TITLE); + if (title == null) { + title = ""; + } + + final StreamingService.LinkType linkType = ((StreamingService.LinkType) intent + .getSerializableExtra(Constants.KEY_LINK_TYPE)); + assert linkType != null; + switch (linkType) { case STREAM: - final boolean autoPlay = intent - .getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); - final String intentCacheKey = intent - .getStringExtra(VideoPlayer.PLAY_QUEUE_KEY); + final String intentCacheKey = intent.getStringExtra( + VideoPlayer.PLAY_QUEUE_KEY); final PlayQueue playQueue = intentCacheKey != null ? SerializedCache.getInstance() .take(intentCacheKey, PlayQueue.class) : 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; case CHANNEL: NavigationHelper.openChannelFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; case PLAYLIST: NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; } } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 388d7683a..ca145bb9a 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -60,8 +60,6 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import icepick.Icepick; @@ -116,8 +114,6 @@ public class RouterActivity extends AppCompatActivity { } } - internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false); - setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); } @@ -492,10 +488,6 @@ public class RouterActivity extends AppCompatActivity { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(intent -> { - if (!internalRoute) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - } startActivity(intent); finish(); @@ -515,7 +507,7 @@ public class RouterActivity extends AppCompatActivity { @SuppressLint("CheckResult") private void openDownloadDialog() { - ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) + disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((@NonNull StreamInfo result) -> { @@ -532,10 +524,10 @@ public class RouterActivity extends AppCompatActivity { downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.show(fm, "downloadDialog"); fm.executePendingTransactions(); - downloadDialog.getDialog().setOnDismissListener(dialog -> finish()); + downloadDialog.requireDialog().setOnDismissListener(dialog -> finish()); }, (@NonNull Throwable throwable) -> { showUnsupportedUrlDialog(currentUrl); - }); + })); } @Override @@ -553,66 +545,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 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 { final String description; final String key; @@ -739,11 +671,11 @@ public class RouterActivity extends AppCompatActivity { playQueue = new SinglePlayQueue((StreamInfo) info); if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); + NavigationHelper.playOnMainPlayer(this, playQueue); } else if (playerChoice.equals(backgroundPlayerKey)) { - NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); + NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); } else if (playerChoice.equals(popupPlayerKey)) { - NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true); + NavigationHelper.playOnPopupPlayer(this, playQueue, true); } } } @@ -754,7 +686,7 @@ public class RouterActivity extends AppCompatActivity { : new PlaylistPlayQueue((PlaylistInfo) info); if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); + NavigationHelper.playOnMainPlayer(this, playQueue); } else if (playerChoice.equals(backgroundPlayerKey)) { NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); } else if (playerChoice.equals(popupPlayerKey)) { @@ -764,11 +696,6 @@ public class RouterActivity extends AppCompatActivity { }; } - private void openMainPlayer(final PlayQueue playQueue, final Choice choice) { - NavigationHelper.playOnMainPlayer(this, playQueue, choice.linkType, - choice.url, "", true, true); - } - @Override public void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index d5da006f7..ffba7f521 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -110,6 +110,7 @@ import org.schabi.newpipe.views.LargeTextMovementMethod; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; 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.util.AnimationUtils.animateView; -public class VideoDetailFragment +public final class VideoDetailFragment extends BaseStateFragment implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, @@ -136,7 +137,7 @@ public class VideoDetailFragment View.OnLongClickListener, PlayerServiceExtendedEventListener, 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 COMMENTS_UPDATE_FLAG = 0x2; @@ -167,19 +168,23 @@ public class VideoDetailFragment @State protected int serviceId = Constants.NO_SERVICE_ID; @State - protected String name; + @NonNull + protected String title = ""; @State - protected String url; - protected static PlayQueue playQueue; + @Nullable + protected String url = null; + @Nullable + protected PlayQueue playQueue = null; @State int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; @State protected boolean autoPlayEnabled = true; - private static StreamInfo currentInfo; + @Nullable + private StreamInfo currentInfo = null; private Disposable currentWorker; @NonNull - private CompositeDisposable disposables = new CompositeDisposable(); + private final CompositeDisposable disposables = new CompositeDisposable(); @Nullable private Disposable positionSubscriber = null; @@ -298,8 +303,10 @@ public class VideoDetailFragment /*////////////////////////////////////////////////////////////////////////*/ - public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, - final String name, final PlayQueue queue) { + public static VideoDetailFragment getInstance(final int serviceId, + @Nullable final String videoUrl, + @NonNull final String name, + @Nullable final PlayQueue queue) { final VideoDetailFragment instance = new VideoDetailFragment(); instance.setInitialData(serviceId, videoUrl, name, queue); return instance; @@ -444,8 +451,8 @@ public class VideoDetailFragment switch (requestCode) { case ReCaptchaActivity.RECAPTCHA_REQUEST: if (resultCode == Activity.RESULT_OK) { - NavigationHelper - .openVideoDetailFragment(getFM(), serviceId, url, name); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + serviceId, url, title, null, false); } else { Log.e(TAG, "ReCaptcha failed"); } @@ -791,7 +798,7 @@ public class VideoDetailFragment player.onPause(); } restoreDefaultOrientation(); - setAutoplay(false); + setAutoPlay(false); return true; } @@ -819,14 +826,11 @@ public class VideoDetailFragment } private void setupFromHistoryItem(final StackItem item) { - setAutoplay(false); + setAutoPlay(false); hideMainPlayer(); - setInitialData( - item.getServiceId(), - item.getUrl(), - !TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "", - item.getPlayQueue()); + setInitialData(item.getServiceId(), item.getUrl(), + item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue()); startLoading(false); // Maybe an item was deleted in background activity @@ -860,18 +864,17 @@ public class VideoDetailFragment } } - public void selectAndLoadVideo(final int sid, final String videoUrl, final String title, - final PlayQueue queue) { - // Situation when user switches from players to main player. - // All needed data is here, we can start watching - if (this.playQueue != null && this.playQueue.equals(queue)) { - openVideoPlayer(); - return; - } - setInitialData(sid, videoUrl, title, queue); - if (player != null) { + public void selectAndLoadVideo(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newQueue) { + if (player != null && newQueue != null && playQueue != null + && !Objects.equals(newQueue.getItem(), playQueue.getItem())) { + // Preloading can be disabled since playback is surely being replaced. player.disablePreloadingOfCurrentTrack(); } + + setInitialData(newServiceId, newUrl, newTitle, newQueue); startLoading(false, true); } @@ -956,7 +959,7 @@ public class VideoDetailFragment playQueue = new SinglePlayQueue(result); } 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()) { @@ -977,7 +980,7 @@ public class VideoDetailFragment if (shouldShowComments()) { pageAdapter.addFragment( - CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG); + CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG); } if (showRelatedStreams && null == relatedStreamsLayout) { @@ -1068,7 +1071,7 @@ public class VideoDetailFragment } } - private void openVideoPlayer() { + public void openVideoPlayer() { if (PreferenceManager.getDefaultSharedPreferences(activity) .getBoolean(this.getString(R.string.use_external_video_player_key), false)) { showExternalPlaybackDialog(); @@ -1143,8 +1146,8 @@ public class VideoDetailFragment // Utils //////////////////////////////////////////////////////////////////////////*/ - public void setAutoplay(final boolean autoplay) { - this.autoPlayEnabled = autoplay; + public void setAutoPlay(final boolean autoPlay) { + this.autoPlayEnabled = autoPlay; } private void startOnExternalPlayer(@NonNull final Context context, @@ -1166,7 +1169,7 @@ public class VideoDetailFragment .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 private boolean isAutoplayEnabled() { return autoPlayEnabled @@ -1302,12 +1305,14 @@ public class VideoDetailFragment contentRootLayoutHiding.setVisibility(View.VISIBLE); } - protected void setInitialData(final int sid, final String u, final String title, - final PlayQueue queue) { - this.serviceId = sid; - this.url = u; - this.name = !TextUtils.isEmpty(title) ? title : ""; - this.playQueue = queue; + protected void setInitialData(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newPlayQueue) { + this.serviceId = newServiceId; + this.url = newUrl; + this.title = newTitle; + this.playQueue = newPlayQueue; } private void setErrorImage(final int imageResource) { @@ -1400,7 +1405,7 @@ public class VideoDetailFragment animateView(detailPositionView, false, 100); animateView(positionView, false, 50); - videoTitleTextView.setText(name != null ? name : ""); + videoTitleTextView.setText(title); videoTitleTextView.setMaxLines(1); animateView(videoTitleTextView, true, 0); @@ -1445,7 +1450,7 @@ public class VideoDetailFragment } } animateView(thumbnailPlayButton, true, 200); - videoTitleTextView.setText(name); + videoTitleTextView.setText(title); if (!TextUtils.isEmpty(info.getSubChannelName())) { displayBothUploaderAndSubChannel(info); @@ -1738,7 +1743,7 @@ public class VideoDetailFragment if (DEBUG) { Log.d(TAG, "onQueueUpdate() called with: serviceId = [" + serviceId + "], videoUrl = [" + url + "], name = [" - + name + "], playQueue = [" + playQueue + "]"); + + title + "], playQueue = [" + playQueue + "]"); } // This should be the only place where we push data to stack. @@ -1823,7 +1828,7 @@ public class VideoDetailFragment currentInfo = info; setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue); - setAutoplay(false); + setAutoPlay(false); // 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) @@ -2037,7 +2042,7 @@ public class VideoDetailFragment private void checkLandscape() { if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null) { - setAutoplay(true); + setAutoPlay(true); } player.checkLandscape(); @@ -2287,10 +2292,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 thumbnailUrl) { - overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title); + overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle); overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); if (!TextUtils.isEmpty(thumbnailUrl)) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 79e1854e9..41263bc34 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -321,8 +321,9 @@ public abstract class BaseListFragment extends BaseStateFragment private void onStreamSelected(final StreamInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(getFM(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(), + null, false); } protected void onScrollToBottom() { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 58e28df49..6ec818909 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -517,7 +517,7 @@ public class ChannelFragment extends BaseListInfoFragment monitorSubscription(result); headerPlayAllButton.setOnClickListener(view -> NavigationHelper - .playOnMainPlayer(activity, getPlayQueue(), true)); + .playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper .playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> NavigationHelper diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 67f1a007a..71b51f9a1 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -319,7 +319,7 @@ public class PlaylistFragment extends BaseListInfoFragment { .subscribe(getPlaylistBookmarkSubscriber()); headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index b4398d873..8cd4e4c7e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -25,6 +25,7 @@ import org.reactivestreams.Subscription; import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; 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.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; @@ -149,11 +150,10 @@ public class StatisticsPlaylistFragment @Override public void selected(final LocalItem selectedItem) { if (selectedItem instanceof StreamStatisticsEntry) { - final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; - NavigationHelper.openVideoDetailFragment(getFM(), - item.getStreamEntity().getServiceId(), - item.getStreamEntity().getUrl(), - item.getStreamEntity().getTitle()); + final StreamEntity item = + ((StreamStatisticsEntry) selectedItem).getStreamEntity(); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + item.getServiceId(), item.getUrl(), item.getTitle(), null, false); } } @@ -325,7 +325,7 @@ public class StatisticsPlaylistFragment } headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 32fac9de0..71df07a4b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -30,6 +30,7 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.history.model.StreamHistoryEntry; 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.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; @@ -178,10 +179,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 8b606260a..fee189295 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -367,7 +367,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail); 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); return true; }); @@ -454,11 +456,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() { if (player == null) { return; diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 97007f0f6..765eb148b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -785,7 +785,7 @@ public class VideoPlayerImpl extends VideoPlayer 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); + intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, true); service.onDestroy(); context.startActivity(intent); } diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java index 4f35f98f5..b8bb677e0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java @@ -1,12 +1,8 @@ package org.schabi.newpipe.player.playqueue; -import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.player.playqueue.events.AppendEvent; import org.schabi.newpipe.player.playqueue.events.ErrorEvent; @@ -43,7 +39,6 @@ import io.reactivex.subjects.BehaviorSubject; *

*/ public abstract class PlayQueue implements Serializable { - private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); public static final boolean DEBUG = MainActivity.DEBUG; private ArrayList backup; @@ -55,7 +50,6 @@ public abstract class PlayQueue implements Serializable { private transient BehaviorSubject eventBroadcast; private transient Flowable broadcastReceiver; - private transient Subscription reportingReactor; private transient boolean disposed; @@ -87,10 +81,6 @@ public abstract class PlayQueue implements Serializable { broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER) .observeOn(AndroidSchedulers.mainThread()) .startWith(new InitEvent()); - - if (DEBUG) { - broadcastReceiver.subscribe(getSelfReporter()); - } } /** @@ -100,13 +90,9 @@ public abstract class PlayQueue implements Serializable { if (eventBroadcast != null) { eventBroadcast.onComplete(); } - if (reportingReactor != null) { - reportingReactor.cancel(); - } eventBroadcast = null; broadcastReceiver = null; - reportingReactor = null; disposed = true; } @@ -544,35 +530,5 @@ public abstract class PlayQueue implements Serializable { eventBroadcast.onNext(event); } } - - private Subscriber getSelfReporter() { - return new Subscriber() { - @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."); - } - }; - } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 6761fce59..a96730552 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -18,7 +18,6 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.preference.PreferenceManager; import com.nostra13.universalimageloader.core.ImageLoader; @@ -52,13 +51,14 @@ import org.schabi.newpipe.player.BackgroundPlayerActivity; import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; 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.PlayQueueItem; import org.schabi.newpipe.settings.SettingsActivity; import java.util.ArrayList; -@SuppressWarnings({"unused"}) public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; @@ -130,42 +130,22 @@ public final class NavigationHelper { } public static void playOnMainPlayer(final AppCompatActivity activity, - final PlayQueue queue, - final boolean autoPlay) { - playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay); + @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 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(final Context context, + @NonNull final PlayQueue playQueue) { + final PlayQueueItem item = playQueue.getItem(); + assert item != null; + openVideoDetail(context, item.getServiceId(), item.getUrl(), item.getTitle(), playQueue); } - 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, + public static void playOnPopupPlayer(final Context context, + final PlayQueue queue, final boolean resumePlayback) { if (!PermissionHelper.isPopupEnabled(context)) { PermissionHelper.showPopupEnablementToast(context); @@ -300,9 +280,6 @@ public final class NavigationHelper { .setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn.")) .show(); -// Log.e("NavigationHelper", -// "Either no Streaming player for audio was installed, " -// + "or something important crashed:"); } else { Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show(); } @@ -358,41 +335,6 @@ public final class NavigationHelper { .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) { context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER)); } @@ -409,33 +351,86 @@ public final class NavigationHelper { .commitAllowingStateLoss(); } + private interface RunnableWithVideoDetailFragment { + void run(VideoDetailFragment detailFragment); + } + + 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 = true; + } 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 String name) { + @NonNull final String name) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } public static void openCommentsFragment(final FragmentManager fragmentManager, final int serviceId, final String url, - final String name) { + @NonNull final String name) { fragmentManager.beginTransaction() .setCustomAnimations(R.anim.switch_service_in, R.anim.switch_service_out) - .replace(R.id.fragment_holder, CommentsFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, CommentsFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, - final String name) { + @NonNull final String name) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } @@ -511,33 +506,31 @@ public final class NavigationHelper { context.startActivity(mIntent); } - public static void openChannel(final Context context, final int serviceId, final String url) { - openChannel(context, serviceId, url, null); - } - public static void openChannel(final Context context, final int serviceId, - final String url, final String name) { + final String url, @NonNull final String name) { final Intent openIntent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL); - if (name != null && !name.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, name); - } + 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, + @NonNull final String title, + @Nullable final PlayQueue playQueue) { - public static void openVideoDetail(final Context context, final int serviceId, - final String url, final String title) { - final Intent openIntent = getOpenIntent(context, url, serviceId, + final Intent intent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM); - if (title != null && !title.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, title); + intent.putExtra(Constants.KEY_TITLE, title); + + 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) { @@ -550,7 +543,6 @@ public final class NavigationHelper { public static void openRouterActivity(final Context context, final String url) { final Intent mIntent = new Intent(context, RouterActivity.class); mIntent.setData(Uri.parse(url)); - mIntent.putExtra(RouterActivity.INTERNAL_ROUTE_KEY, true); context.startActivity(mIntent); } @@ -564,14 +556,12 @@ public final class NavigationHelper { context.startActivity(intent); } - public static boolean openDownloads(final Activity activity) { - if (!PermissionHelper.checkStoragePermissions( + public static void openDownloads(final Activity activity) { + if (PermissionHelper.checkStoragePermissions( activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { - return false; + final Intent intent = new Intent(activity, DownloadActivity.class); + activity.startActivity(intent); } - final Intent intent = new Intent(activity, DownloadActivity.class); - activity.startActivity(intent); - return true; } public static Intent getPlayQueueActivityIntent(final Context context) { @@ -600,7 +590,8 @@ public final class NavigationHelper { 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 StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); @@ -609,15 +600,7 @@ public final class NavigationHelper { + " url=" + url); } - final Intent rIntent = 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; + return getOpenIntent(context, url, service.getServiceId(), linkType); } private static Uri openMarketUrl(final String packageName) { diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 1aa781fe2..9c9fa4853 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -21,7 +21,6 @@ use_external_video_player use_external_audio_player - autoplay_through_intent use_oldplayer volume_gesture_control