mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-26 04:47:38 +00:00 
			
		
		
		
	Merge pull request #4562 from Stypox/fix-detail-open
Fix opening VideoDetailFragment and much more
This commit is contained in:
		| @@ -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; | ||||
| @@ -69,10 +69,11 @@ import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||
| import org.schabi.newpipe.fragments.list.search.SearchFragment; | ||||
| import org.schabi.newpipe.player.VideoPlayer; | ||||
| 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.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; | ||||
| @@ -87,6 +88,7 @@ import org.schabi.newpipe.views.FocusOverlayView; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; | ||||
|  | ||||
| @@ -152,7 +154,7 @@ public class MainActivity extends AppCompatActivity { | ||||
|         if (DeviceUtils.isTv(this)) { | ||||
|             FocusOverlayView.setupFocusObserver(this); | ||||
|         } | ||||
|         setupBroadcastReceiver(); | ||||
|         openMiniPlayerUponPlayerStarted(); | ||||
|     } | ||||
|  | ||||
|     private void setupDrawer() throws Exception { | ||||
| @@ -758,32 +760,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)) { | ||||
| @@ -805,34 +811,47 @@ public class MainActivity extends AppCompatActivity { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void setupBroadcastReceiver() { | ||||
|         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() | ||||
|                             .findFragmentById(R.id.fragment_player_holder); | ||||
|                     if (fragmentPlayer == null) { | ||||
|                         /* | ||||
|                          * We still don't have a fragment attached to the activity. | ||||
|                          * It can happen when a user started popup or background players | ||||
|                          * without opening a stream inside the fragment. | ||||
|                          * Adding it in a collapsed state (only mini player will be visible) | ||||
|                          * */ | ||||
|                         NavigationHelper.showMiniPlayer(getSupportFragmentManager()); | ||||
|     private void openMiniPlayerIfMissing() { | ||||
|         final Fragment fragmentPlayer = getSupportFragmentManager() | ||||
|                 .findFragmentById(R.id.fragment_player_holder); | ||||
|         if (fragmentPlayer == null) { | ||||
|             // We still don't have a fragment attached to the activity. It can happen when a user | ||||
|             // started popup or background players without opening a stream inside the fragment. | ||||
|             // Adding it in a collapsed state (only mini player will be visible). | ||||
|             NavigationHelper.showMiniPlayer(getSupportFragmentManager()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|                         broadcastReceiver = null; | ||||
|                     } | ||||
|                     /* | ||||
|                     * 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); | ||||
|                     broadcastReceiver = null; | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         final IntentFilter intentFilter = new IntentFilter(); | ||||
|         intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); | ||||
|         registerReceiver(broadcastReceiver, intentFilter); | ||||
|             }; | ||||
|             final IntentFilter intentFilter = new IntentFilter(); | ||||
|             intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); | ||||
|             registerReceiver(broadcastReceiver, intentFilter); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean bottomSheetHiddenOrCollapsed() { | ||||
|   | ||||
| @@ -40,7 +40,9 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| 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.PlayerHolder; | ||||
| import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||
| @@ -60,8 +62,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 +116,6 @@ public class RouterActivity extends AppCompatActivity { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false); | ||||
|  | ||||
|         setTheme(ThemeHelper.isLightThemeSelected(this) | ||||
|                 ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); | ||||
|     } | ||||
| @@ -398,14 +396,22 @@ public class RouterActivity extends AppCompatActivity { | ||||
|                 // show both "show info" and "video player", they are two different activities | ||||
|                 returnList.add(showInfo); | ||||
|                 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 { | ||||
|                 // show only "show info" if video player is not applicable or autoplay is disabled | ||||
|                 returnList.add(showInfo); | ||||
|                 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); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (capabilities.contains(VIDEO)) { | ||||
| @@ -492,12 +498,7 @@ 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(); | ||||
|                     }, throwable -> handleError(throwable, currentUrl)) | ||||
|             ); | ||||
| @@ -515,7 +516,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 +533,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 +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 { | ||||
|         final String description; | ||||
|         final String key; | ||||
| @@ -725,50 +666,34 @@ public class RouterActivity extends AppCompatActivity { | ||||
|                 final boolean isExtAudioEnabled = preferences.getBoolean( | ||||
|                         getString(R.string.use_external_audio_player_key), false); | ||||
|  | ||||
|                 PlayQueue playQueue; | ||||
|                 final String playerChoice = choice.playerChoice; | ||||
|  | ||||
|                 final PlayQueue playQueue; | ||||
|                 if (info instanceof StreamInfo) { | ||||
|                     if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { | ||||
|                     if (choice.playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { | ||||
|                         NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info); | ||||
|  | ||||
|                     } else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { | ||||
|                         return; | ||||
|                     } else if (choice.playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { | ||||
|                         NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info); | ||||
|  | ||||
|                     } else { | ||||
|                         playQueue = new SinglePlayQueue((StreamInfo) info); | ||||
|  | ||||
|                         if (playerChoice.equals(videoPlayerKey)) { | ||||
|                             openMainPlayer(playQueue, choice); | ||||
|                         } else if (playerChoice.equals(backgroundPlayerKey)) { | ||||
|                             NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); | ||||
|                         } else if (playerChoice.equals(popupPlayerKey)) { | ||||
|                             NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true); | ||||
|                         } | ||||
|                         return; | ||||
|                     } | ||||
|                     playQueue = new SinglePlayQueue((StreamInfo) info); | ||||
|                 } else if (info instanceof ChannelInfo) { | ||||
|                     playQueue = new ChannelPlayQueue((ChannelInfo) info); | ||||
|                 } else if (info instanceof PlaylistInfo) { | ||||
|                     playQueue = new PlaylistPlayQueue((PlaylistInfo) info); | ||||
|                 } else { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (info instanceof ChannelInfo || info instanceof PlaylistInfo) { | ||||
|                     playQueue = info instanceof ChannelInfo | ||||
|                             ? new ChannelPlayQueue((ChannelInfo) info) | ||||
|                             : new PlaylistPlayQueue((PlaylistInfo) info); | ||||
|  | ||||
|                     if (playerChoice.equals(videoPlayerKey)) { | ||||
|                         openMainPlayer(playQueue, choice); | ||||
|                     } else if (playerChoice.equals(backgroundPlayerKey)) { | ||||
|                         NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); | ||||
|                     } else if (playerChoice.equals(popupPlayerKey)) { | ||||
|                         NavigationHelper.playOnPopupPlayer(this, playQueue, true); | ||||
|                     } | ||||
|                 if (choice.playerChoice.equals(videoPlayerKey)) { | ||||
|                     NavigationHelper.playOnMainPlayer(this, playQueue, false); | ||||
|                 } else if (choice.playerChoice.equals(backgroundPlayerKey)) { | ||||
|                     NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); | ||||
|                 } else if (choice.playerChoice.equals(popupPlayerKey)) { | ||||
|                     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 | ||||
|         public void onDestroy() { | ||||
|             super.onDestroy(); | ||||
|   | ||||
| @@ -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<StreamInfo> | ||||
|         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; | ||||
|  | ||||
| @@ -284,6 +289,7 @@ public class VideoDetailFragment | ||||
|                 || (currentInfo != null | ||||
|                 && isAutoplayEnabled() | ||||
|                 && player.getParentActivity() == null)) { | ||||
|             autoPlayEnabled = true; // forcefully start playing | ||||
|             openVideoPlayer(); | ||||
|         } | ||||
|     } | ||||
| @@ -298,8 +304,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 +452,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"); | ||||
|                 } | ||||
| @@ -514,6 +522,7 @@ public class VideoDetailFragment | ||||
|                 } | ||||
|                 break; | ||||
|             case R.id.detail_thumbnail_root_layout: | ||||
|                 autoPlayEnabled = true; // forcefully start playing | ||||
|                 openVideoPlayer(); | ||||
|                 break; | ||||
|             case R.id.detail_title_root_layout: | ||||
| @@ -530,6 +539,7 @@ public class VideoDetailFragment | ||||
|                     player.hideControls(0, 0); | ||||
|                     showSystemUi(); | ||||
|                 } else { | ||||
|                     autoPlayEnabled = true; // forcefully start playing | ||||
|                     openVideoPlayer(); | ||||
|                 } | ||||
|  | ||||
| @@ -791,7 +801,7 @@ public class VideoDetailFragment | ||||
|                 player.onPause(); | ||||
|             } | ||||
|             restoreDefaultOrientation(); | ||||
|             setAutoplay(false); | ||||
|             setAutoPlay(false); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
| @@ -819,14 +829,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 +867,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 +962,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 +983,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 +1074,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(); | ||||
| @@ -1094,7 +1100,7 @@ public class VideoDetailFragment | ||||
|  | ||||
|     private void openMainPlayer() { | ||||
|         if (playerService == null) { | ||||
|             PlayerHolder.startService(App.getApp(), true, this); | ||||
|             PlayerHolder.startService(App.getApp(), autoPlayEnabled, this); | ||||
|             return; | ||||
|         } | ||||
|         if (currentInfo == null) { | ||||
| @@ -1105,11 +1111,13 @@ public class VideoDetailFragment | ||||
|  | ||||
|         // Video view can have elements visible from popup, | ||||
|         // We hide it here but once it ready the view will be shown in handleIntent() | ||||
|         playerService.getView().setVisibility(View.GONE); | ||||
|         if (playerService.getView() != null) { | ||||
|             playerService.getView().setVisibility(View.GONE); | ||||
|         } | ||||
|         addVideoPlayerView(); | ||||
|  | ||||
|         final Intent playerIntent = NavigationHelper | ||||
|                 .getPlayerIntent(requireContext(), MainPlayer.class, queue, null, true); | ||||
|                 .getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled); | ||||
|         activity.startService(playerIntent); | ||||
|     } | ||||
|  | ||||
| @@ -1143,8 +1151,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 +1174,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 +1310,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 +1410,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 +1455,7 @@ public class VideoDetailFragment | ||||
|             } | ||||
|         } | ||||
|         animateView(thumbnailPlayButton, true, 200); | ||||
|         videoTitleTextView.setText(name); | ||||
|         videoTitleTextView.setText(title); | ||||
|  | ||||
|         if (!TextUtils.isEmpty(info.getSubChannelName())) { | ||||
|             displayBothUploaderAndSubChannel(info); | ||||
| @@ -1738,7 +1748,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 +1833,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 +2047,7 @@ public class VideoDetailFragment | ||||
|     private void checkLandscape() { | ||||
|         if ((!player.isPlaying() && player.getPlayQueue() != playQueue) | ||||
|                 || player.getPlayQueue() == null) { | ||||
|             setAutoplay(true); | ||||
|             setAutoPlay(true); | ||||
|         } | ||||
|  | ||||
|         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 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)) { | ||||
|   | ||||
| @@ -321,8 +321,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> | ||||
|  | ||||
|     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() { | ||||
|   | ||||
| @@ -517,7 +517,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> | ||||
|         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 | ||||
|   | ||||
| @@ -319,7 +319,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> { | ||||
|                 .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 -> | ||||
|   | ||||
| @@ -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 -> | ||||
|   | ||||
| @@ -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<List<PlaylistSt | ||||
|             @Override | ||||
|             public void selected(final LocalItem selectedItem) { | ||||
|                 if (selectedItem instanceof PlaylistStreamEntry) { | ||||
|                     final PlaylistStreamEntry item = (PlaylistStreamEntry) selectedItem; | ||||
|                     NavigationHelper.openVideoDetailFragment(getFM(), | ||||
|                             item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(), | ||||
|                             item.getStreamEntity().getTitle()); | ||||
|                     final StreamEntity item = | ||||
|                             ((PlaylistStreamEntry) selectedItem).getStreamEntity(); | ||||
|                     NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), | ||||
|                             item.getServiceId(), item.getUrl(), item.getTitle(), null, false); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -494,7 +495,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         setVideoCount(itemListAdapter.getItemsList().size()); | ||||
|  | ||||
|         headerPlayAllButton.setOnClickListener(view -> | ||||
|                 NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); | ||||
|                 NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); | ||||
|         headerPopupButton.setOnClickListener(view -> | ||||
|                 NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); | ||||
|         headerBackgroundButton.setOnClickListener(view -> | ||||
|   | ||||
| @@ -2,11 +2,8 @@ package org.schabi.newpipe.player; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
|  | ||||
| public final class BackgroundPlayerActivity extends ServicePlayerActivity { | ||||
|  | ||||
| @@ -46,31 +43,6 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity { | ||||
|         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 | ||||
|     public void setupMenu(final Menu menu) { | ||||
|         if (player == null) { | ||||
|   | ||||
| @@ -125,7 +125,7 @@ public abstract class BasePlayer implements | ||||
|     @NonNull | ||||
|     public static final String RESUME_PLAYBACK = "resume_playback"; | ||||
|     @NonNull | ||||
|     public static final String START_PAUSED = "start_paused"; | ||||
|     public static final String PLAY_WHEN_READY = "play_when_ready"; | ||||
|     @NonNull | ||||
|     public static final String SELECT_ON_APPEND = "select_on_append"; | ||||
|     @NonNull | ||||
| @@ -224,7 +224,7 @@ public abstract class BasePlayer implements | ||||
|         this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); | ||||
|  | ||||
|         final TrackSelection.Factory trackSelectionFactory = PlayerHelper | ||||
|                 .getQualitySelector(context); | ||||
|                 .getQualitySelector(); | ||||
|         this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory); | ||||
|  | ||||
|         this.loadControl = new LoadController(); | ||||
| @@ -302,6 +302,7 @@ public abstract class BasePlayer implements | ||||
|         final boolean samePlayQueue = playQueue != null && playQueue.equals(queue); | ||||
|  | ||||
|         final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode()); | ||||
|         final boolean playWhenReady = intent.getBooleanExtra(PLAY_WHEN_READY, true); | ||||
|         final boolean isMuted = intent | ||||
|                 .getBooleanExtra(IS_MUTED, simpleExoPlayer != null && isMuted()); | ||||
|  | ||||
| @@ -327,16 +328,20 @@ public abstract class BasePlayer implements | ||||
|                 simpleExoPlayer.retry(); | ||||
|             } | ||||
|             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 | ||||
|             // Player can have state = IDLE when playback is stopped or failed | ||||
|             // and we should retry() in this case | ||||
|             if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) { | ||||
|                 simpleExoPlayer.retry(); | ||||
|             } | ||||
|             return; | ||||
|             simpleExoPlayer.setPlayWhenReady(playWhenReady); | ||||
|  | ||||
|         } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) | ||||
|                 && isPlaybackResumeEnabled() | ||||
|                 && !samePlayQueue) { | ||||
| @@ -351,7 +356,7 @@ public abstract class BasePlayer implements | ||||
|                                 state -> { | ||||
|                                     queue.setRecovery(queue.getIndex(), state.getProgressTime()); | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, | ||||
|                                             playbackSkipSilence, true, isMuted); | ||||
|                                             playbackSkipSilence, playWhenReady, isMuted); | ||||
|                                 }, | ||||
|                                 error -> { | ||||
|                                     if (DEBUG) { | ||||
| @@ -359,24 +364,22 @@ public abstract class BasePlayer implements | ||||
|                                     } | ||||
|                                     // In case any error we can start playback without history | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, | ||||
|                                             playbackSkipSilence, true, isMuted); | ||||
|                                             playbackSkipSilence, playWhenReady, isMuted); | ||||
|                                 }, | ||||
|                                 () -> { | ||||
|                                     // Completed but not found in history | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, | ||||
|                                             playbackSkipSilence, true, isMuted); | ||||
|                                             playbackSkipSilence, playWhenReady, isMuted); | ||||
|                                 } | ||||
|                         ); | ||||
|                 databaseUpdateReactor.add(stateLoader); | ||||
|                 return; | ||||
|             } | ||||
|         } else { | ||||
|             // Good to go... | ||||
|             // In a case of equal PlayQueues we can re-init old one but only when it is disposed | ||||
|             initPlayback(samePlayQueue ? playQueue : queue, repeatMode, playbackSpeed, | ||||
|                     playbackPitch, playbackSkipSilence, playWhenReady, isMuted); | ||||
|         } | ||||
|         // Good to go... | ||||
|         // In a case of equal PlayQueues we can re-init old one but only when it is disposed | ||||
|         initPlayback(samePlayQueue ? playQueue : queue, repeatMode, | ||||
|                 playbackSpeed, playbackPitch, playbackSkipSilence, | ||||
|                 !intent.getBooleanExtra(START_PAUSED, false), | ||||
|                 isMuted); | ||||
|     } | ||||
|  | ||||
|     private PlaybackParameters retrievePlaybackParametersFromPreferences() { | ||||
|   | ||||
| @@ -30,6 +30,7 @@ import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.WindowManager; | ||||
|  | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.core.content.ContextCompat; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| @@ -231,6 +232,7 @@ public final class MainPlayer extends Service { | ||||
|         return metrics.heightPixels < metrics.widthPixels; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     public View getView() { | ||||
|         if (playerImpl == null) { | ||||
|             return null; | ||||
| @@ -240,7 +242,7 @@ public final class MainPlayer extends Service { | ||||
|     } | ||||
|  | ||||
|     public void removeViewFromParent() { | ||||
|         if (getView().getParent() != null) { | ||||
|         if (getView() != null && getView().getParent() != null) { | ||||
|             if (playerImpl.getParentActivity() != null) { | ||||
|                 // This means view was added to fragment | ||||
|                 final ViewGroup parent = (ViewGroup) getView().getParent(); | ||||
|   | ||||
| @@ -27,9 +27,7 @@ import androidx.recyclerview.widget.RecyclerView; | ||||
| import com.google.android.exoplayer2.PlaybackParameters; | ||||
| import com.google.android.exoplayer2.Player; | ||||
|  | ||||
| import org.schabi.newpipe.MainActivity; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.StreamingService; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||
| 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.PlayQueueItemHolder; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
|  | ||||
| import java.util.Collections; | ||||
| @@ -113,9 +111,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|  | ||||
|     public abstract int getPlayerOptionMenuResource(); | ||||
|  | ||||
|     public abstract boolean onPlayerOptionSelected(MenuItem item); | ||||
|  | ||||
|     public abstract void setupMenu(Menu m); | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Activity Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
| @@ -187,12 +184,22 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|                 return true; | ||||
|             case R.id.action_switch_main: | ||||
|                 this.player.setRecovery(); | ||||
|                 getApplicationContext().startActivity( | ||||
|                         getSwitchIntent(MainActivity.class, MainPlayer.PlayerType.VIDEO) | ||||
|                                 .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())); | ||||
|                 NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true); | ||||
|                 return true; | ||||
|             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 onPlayerOptionSelected(item) || super.onOptionsItemSelected(item); | ||||
|         return super.onOptionsItemSelected(item); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -201,24 +208,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|         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 | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
| @@ -367,7 +356,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, false); | ||||
|             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() { | ||||
|         if (player == null) { | ||||
|             return; | ||||
|   | ||||
| @@ -76,9 +76,7 @@ import com.google.android.exoplayer2.ui.SubtitleView; | ||||
| import com.google.android.material.floatingactionbutton.FloatingActionButton; | ||||
| import com.nostra13.universalimageloader.core.assist.FailReason; | ||||
|  | ||||
| import org.schabi.newpipe.MainActivity; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.StreamingService; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| 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.VideoPlaybackResolver; | ||||
| import org.schabi.newpipe.util.AnimationUtils; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.DeviceUtils; | ||||
| import org.schabi.newpipe.util.KoreUtil; | ||||
| import org.schabi.newpipe.util.ListHelper; | ||||
| @@ -260,7 +257,12 @@ public class VideoPlayerImpl extends VideoPlayer | ||||
|             onQueueClosed(); | ||||
|             // Android TV: without it focus will frame the whole player | ||||
|             playPauseButton.requestFocus(); | ||||
|             onPlay(); | ||||
|  | ||||
|             if (simpleExoPlayer.getPlayWhenReady()) { | ||||
|                 onPlay(); | ||||
|             } else { | ||||
|                 onPause(); | ||||
|             } | ||||
|         } | ||||
|         NavigationHelper.sendPlayerStartedEvent(service); | ||||
|     } | ||||
| @@ -756,40 +758,6 @@ public class VideoPlayerImpl extends VideoPlayer | ||||
|         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 | ||||
|     public void onClick(final View v) { | ||||
|         super.onClick(v); | ||||
| @@ -817,7 +785,9 @@ public class VideoPlayerImpl extends VideoPlayer | ||||
|         } else if (v.getId() == openInBrowser.getId()) { | ||||
|             onOpenInBrowserClicked(); | ||||
|         } else if (v.getId() == fullscreenButton.getId()) { | ||||
|             switchFromPopupToMain(); | ||||
|             setRecovery(); | ||||
|             NavigationHelper.playOnMainPlayer(context, getPlayQueue(), true); | ||||
|             return; | ||||
|         } else if (v.getId() == screenRotationButton.getId()) { | ||||
|             // Only if it's not a vertical video or vertical video but in landscape with locked | ||||
|             // orientation a screen orientation can be changed automatically | ||||
|   | ||||
| @@ -63,7 +63,7 @@ abstract class BasePlayerGestureListener( | ||||
|     private var isMovingInPopup = false | ||||
|     private var isResizing = false | ||||
|  | ||||
|     private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service) | ||||
|     private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity() | ||||
|  | ||||
|     // [popup] initial coordinates and distance between fingers | ||||
|     private var initPointerDistance = -1.0 | ||||
| @@ -104,9 +104,6 @@ abstract class BasePlayerGestureListener( | ||||
|     } | ||||
|  | ||||
|     private fun onTouchInPopup(v: View, event: MotionEvent): Boolean { | ||||
|         if (playerImpl == null) { | ||||
|             return false | ||||
|         } | ||||
|         playerImpl.gestureDetector.onTouchEvent(event) | ||||
|         if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) { | ||||
|             if (DEBUG) { | ||||
|   | ||||
| @@ -111,10 +111,10 @@ public class PlayerGestureListener | ||||
|         } | ||||
|         if (playerType == MainPlayer.PlayerType.VIDEO) { | ||||
|             if (portion == DisplayPortion.LEFT_HALF) { | ||||
|                 onScrollMainVolume(distanceX, distanceY); | ||||
|                 onScrollMainBrightness(distanceX, distanceY); | ||||
|  | ||||
|             } else /* DisplayPortion.RIGHT_HALF */ { | ||||
|                 onScrollMainBrightness(distanceX, distanceY); | ||||
|                 onScrollMainVolume(distanceX, distanceY); | ||||
|             } | ||||
|  | ||||
|         } else /* MainPlayer.PlayerType.POPUP */ { | ||||
|   | ||||
| @@ -164,7 +164,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An | ||||
|  | ||||
|     @Override | ||||
|     public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { | ||||
|         if (!PlayerHelper.isUsingDSP(context)) { | ||||
|         if (!PlayerHelper.isUsingDSP()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -295,7 +295,7 @@ public final class PlayerHelper { | ||||
|         return 60000; | ||||
|     } | ||||
|  | ||||
|     public static TrackSelection.Factory getQualitySelector(@NonNull final Context context) { | ||||
|     public static TrackSelection.Factory getQualitySelector() { | ||||
|         return new AdaptiveTrackSelection.Factory( | ||||
|                 1000, | ||||
|                 AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, | ||||
| @@ -303,11 +303,11 @@ public final class PlayerHelper { | ||||
|                 AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION); | ||||
|     } | ||||
|  | ||||
|     public static boolean isUsingDSP(@NonNull final Context context) { | ||||
|     public static boolean isUsingDSP() { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public static int getTossFlingVelocity(@NonNull final Context context) { | ||||
|     public static int getTossFlingVelocity() { | ||||
|         return 2500; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -49,6 +49,17 @@ public final class PlayerHolder { | ||||
|         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) { | ||||
|         listener = newListener; | ||||
|         // Force reload data from service | ||||
|   | ||||
| @@ -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; | ||||
|  * </p> | ||||
|  */ | ||||
| public abstract class PlayQueue implements Serializable { | ||||
|     private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); | ||||
|     public static final boolean DEBUG = MainActivity.DEBUG; | ||||
|  | ||||
|     private ArrayList<PlayQueueItem> backup; | ||||
| @@ -55,7 +50,6 @@ public abstract class PlayQueue implements Serializable { | ||||
|  | ||||
|     private transient BehaviorSubject<PlayQueueEvent> eventBroadcast; | ||||
|     private transient Flowable<PlayQueueEvent> 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<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."); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -64,6 +64,20 @@ public class PlayQueueItem implements Serializable { | ||||
|         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 | ||||
|     public String getTitle() { | ||||
|         return title; | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
| @@ -38,7 +37,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| import org.schabi.newpipe.fragments.MainFragment; | ||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||
| 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.playlist.PlaylistFragment; | ||||
| 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.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"; | ||||
| @@ -73,7 +72,6 @@ public final class NavigationHelper { | ||||
|     public static <T> Intent getPlayerIntent(@NonNull final Context context, | ||||
|                                              @NonNull final Class<T> targetClazz, | ||||
|                                              @Nullable final PlayQueue playQueue, | ||||
|                                              @Nullable final String quality, | ||||
|                                              final boolean resumePlayback) { | ||||
|         final Intent intent = new Intent(context, targetClazz); | ||||
|  | ||||
| @@ -83,9 +81,6 @@ public final class NavigationHelper { | ||||
|                 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.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO); | ||||
|  | ||||
| @@ -96,8 +91,10 @@ public final class NavigationHelper { | ||||
|     public static <T> Intent getPlayerIntent(@NonNull final Context context, | ||||
|                                              @NonNull final Class<T> targetClazz, | ||||
|                                              @Nullable final PlayQueue playQueue, | ||||
|                                              final boolean resumePlayback) { | ||||
|         return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback); | ||||
|                                              final boolean resumePlayback, | ||||
|                                              final boolean playWhenReady) { | ||||
|         return getPlayerIntent(context, targetClazz, playQueue, resumePlayback) | ||||
|                 .putExtra(BasePlayer.PLAY_WHEN_READY, playWhenReady); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
| @@ -111,61 +108,25 @@ public final class NavigationHelper { | ||||
|                 .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, | ||||
|                                         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 boolean switchingPlayers) { | ||||
|         final PlayQueueItem item = playQueue.getItem(); | ||||
|         assert item != null; | ||||
|         openVideoDetail(context, | ||||
|                 item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, switchingPlayers); | ||||
|     } | ||||
|  | ||||
|     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 +261,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 +316,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 +332,76 @@ public final class NavigationHelper { | ||||
|                 .commitAllowingStateLoss(); | ||||
|     } | ||||
|  | ||||
|     public static void openChannelFragment(final FragmentManager fragmentManager, | ||||
|                                            final int serviceId, final String url, | ||||
|                                            final String name) { | ||||
|         defaultTransaction(fragmentManager) | ||||
|                 .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, | ||||
|                         name == null ? "" : name)) | ||||
|                 .addToBackStack(null) | ||||
|                 .commit(); | ||||
|     private interface RunnableWithVideoDetailFragment { | ||||
|         void run(VideoDetailFragment detailFragment); | ||||
|     } | ||||
|  | ||||
|     public static void openCommentsFragment(final FragmentManager fragmentManager, | ||||
|                                             final int serviceId, final String url, | ||||
|                                             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)) | ||||
|     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, | ||||
|                                            @NonNull final String name) { | ||||
|         defaultTransaction(fragmentManager) | ||||
|                 .replace(R.id.fragment_holder, ChannelFragment.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 +477,26 @@ 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 openVideoDetail(final Context context, | ||||
|                                        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 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, | ||||
|         final Intent intent = getOpenIntent(context, url, serviceId, | ||||
|                 StreamingService.LinkType.STREAM); | ||||
|         if (title != null && !title.isEmpty()) { | ||||
|             openIntent.putExtra(Constants.KEY_TITLE, title); | ||||
|         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|         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) { | ||||
| @@ -550,7 +509,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 +522,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 +556,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 +566,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) { | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  | ||||
|     <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="autoplay_through_intent_key" translatable="false">autoplay_through_intent</string> | ||||
|     <string name="use_old_player_key" translatable="false">use_oldplayer</string> | ||||
|  | ||||
|     <string name="volume_gesture_control_key" translatable="false">volume_gesture_control</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Tobias Groza
					Tobias Groza