mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	Move things back to its original place
This commit is contained in:
		| @@ -204,7 +204,7 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> { | ||||
|      * | ||||
|      * @param versionName    Name of new version | ||||
|      * @param apkLocationUrl Url with the new apk | ||||
|      * @param versionCode    V | ||||
|      * @param versionCode    Code of new version | ||||
|      */ | ||||
|     private void compareAppVersionAndShowNotification(final String versionName, | ||||
|                                                       final String apkLocationUrl, | ||||
|   | ||||
| @@ -86,35 +86,41 @@ public class DownloadDialog extends DialogFragment | ||||
|     private static final String TAG = "DialogFragment"; | ||||
|     private static final boolean DEBUG = MainActivity.DEBUG; | ||||
|     private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230; | ||||
|     private final CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     @State | ||||
|     protected StreamInfo currentInfo; | ||||
|     StreamInfo currentInfo; | ||||
|     @State | ||||
|     protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty(); | ||||
|     StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty(); | ||||
|     @State | ||||
|     protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty(); | ||||
|     StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty(); | ||||
|     @State | ||||
|     protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty(); | ||||
|     StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty(); | ||||
|     @State | ||||
|     protected int selectedVideoIndex = 0; | ||||
|     int selectedVideoIndex = 0; | ||||
|     @State | ||||
|     protected int selectedAudioIndex = 0; | ||||
|     int selectedAudioIndex = 0; | ||||
|     @State | ||||
|     protected int selectedSubtitleIndex = 0; | ||||
|     int selectedSubtitleIndex = 0; | ||||
|  | ||||
|     private StoredDirectoryHelper mainStorageAudio = null; | ||||
|     private StoredDirectoryHelper mainStorageVideo = null; | ||||
|     private DownloadManager downloadManager = null; | ||||
|     private ActionMenuItemView okButton = null; | ||||
|     private Context context; | ||||
|     private boolean askForSavePath; | ||||
|  | ||||
|     private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter; | ||||
|     private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter; | ||||
|     private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter; | ||||
|  | ||||
|     private final CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     private EditText nameEditText; | ||||
|     private Spinner streamsSpinner; | ||||
|     private RadioGroup radioStreamsGroup; | ||||
|     private TextView threadsCountTextView; | ||||
|     private SeekBar threadsSeekBar; | ||||
|  | ||||
|     private SharedPreferences prefs; | ||||
|  | ||||
|     public static DownloadDialog newInstance(final StreamInfo info) { | ||||
|   | ||||
| @@ -109,54 +109,55 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, | ||||
|         View.OnClickListener, View.OnLongClickListener { | ||||
|     public static final String AUTO_PLAY = "auto_play"; | ||||
|  | ||||
|     private int updateFlags = 0; | ||||
|     private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1; | ||||
|     private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2; | ||||
|     private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4; | ||||
|     private static final int COMMENTS_UPDATE_FLAG = 0x8; | ||||
|     private static final String COMMENTS_TAB_TAG = "COMMENTS"; | ||||
|     private static final String RELATED_TAB_TAG = "NEXT VIDEO"; | ||||
|     private static final String EMPTY_TAB_TAG = "EMPTY TAB"; | ||||
|     private static final String INFO_KEY = "info_key"; | ||||
|     private static final String STACK_KEY = "stack_key"; | ||||
|     /** | ||||
|      * Stack that contains the "navigation history".<br> | ||||
|      * The peek is the current video. | ||||
|      */ | ||||
|     private final LinkedList<StackItem> stack = new LinkedList<>(); | ||||
|  | ||||
|     private boolean autoPlayEnabled; | ||||
|     private boolean showRelatedStreams; | ||||
|     private boolean showComments; | ||||
|     private String selectedTabTag; | ||||
|  | ||||
|     @State | ||||
|     protected int serviceId = Constants.NO_SERVICE_ID; | ||||
|     @State | ||||
|     protected String name; | ||||
|     @State | ||||
|     protected String url; | ||||
|     private int updateFlags = 0; | ||||
|     private boolean autoPlayEnabled; | ||||
|     private boolean showRelatedStreams; | ||||
|     private boolean showComments; | ||||
|     private String selectedTabTag; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private StreamInfo currentInfo; | ||||
|     private Disposable currentWorker; | ||||
|     @NonNull | ||||
|     private CompositeDisposable disposables = new CompositeDisposable(); | ||||
|     @Nullable | ||||
|     private Disposable positionSubscriber = null; | ||||
|  | ||||
|     private List<VideoStream> sortedVideoStreams; | ||||
|     private int selectedVideoStreamIndex = -1; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private Menu menu; | ||||
|  | ||||
|     private Spinner spinnerToolbar; | ||||
|  | ||||
|     private LinearLayout contentRootLayoutHiding; | ||||
|  | ||||
|     private View thumbnailBackgroundButton; | ||||
|     private ImageView thumbnailImageView; | ||||
|     private ImageView thumbnailPlayButton; | ||||
|     private AnimatedProgressBar positionView; | ||||
|  | ||||
|     private View videoTitleRoot; | ||||
|     private TextView videoTitleTextView; | ||||
|     private ImageView videoTitleToggleArrow; | ||||
|     private TextView videoCountView; | ||||
|  | ||||
|     private TextView detailControlsBackground; | ||||
|     private TextView detailControlsPopup; | ||||
|     private TextView detailControlsAddToPlaylist; | ||||
| @@ -164,30 +165,42 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|     private TextView appendControlsDetail; | ||||
|     private TextView detailDurationView; | ||||
|     private TextView detailPositionView; | ||||
|  | ||||
|     private LinearLayout videoDescriptionRootLayout; | ||||
|     private TextView videoUploadDateView; | ||||
|     private TextView videoDescriptionView; | ||||
|  | ||||
|     private View uploaderRootLayout; | ||||
|     private TextView uploaderTextView; | ||||
|     private ImageView uploaderThumb; | ||||
|  | ||||
|     private TextView thumbsUpTextView; | ||||
|     private ImageView thumbsUpImageView; | ||||
|     private TextView thumbsDownTextView; | ||||
|     private ImageView thumbsDownImageView; | ||||
|     private TextView thumbsDisabledTextView; | ||||
|  | ||||
|     private AppBarLayout appBarLayout; | ||||
|     private ViewPager viewPager; | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////*/ | ||||
|     private TabAdaptor pageAdapter; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment's Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private TabLayout tabLayout; | ||||
|     private FrameLayout relatedStreamsLayout; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private static final String COMMENTS_TAB_TAG = "COMMENTS"; | ||||
|     private static final String RELATED_TAB_TAG = "NEXT VIDEO"; | ||||
|     private static final String EMPTY_TAB_TAG = "EMPTY TAB"; | ||||
|  | ||||
|     private static final String INFO_KEY = "info_key"; | ||||
|     private static final String STACK_KEY = "stack_key"; | ||||
|  | ||||
|     /** | ||||
|      * Stack that contains the "navigation history".<br> | ||||
|      * The peek is the current video. | ||||
|      */ | ||||
|     private final LinkedList<StackItem> stack = new LinkedList<>(); | ||||
|  | ||||
|     public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, | ||||
|                                                   final String name) { | ||||
|         VideoDetailFragment instance = new VideoDetailFragment(); | ||||
| @@ -195,6 +208,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment's Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
| @@ -285,10 +303,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         disposables = null; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         if (DEBUG) { | ||||
| @@ -336,6 +350,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onSaveInstanceState(final Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
| @@ -351,10 +369,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         outState.putSerializable(STACK_KEY, stack); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OnClick | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     protected void onRestoreInstanceState(@NonNull final Bundle savedState) { | ||||
|         super.onRestoreInstanceState(savedState); | ||||
| @@ -371,9 +385,12 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|             //noinspection unchecked | ||||
|             stack.addAll((Collection<? extends StackItem>) serializable); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OnClick | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onClick(final View v) { | ||||
|         if (isLoading.get() || currentInfo == null) { | ||||
| @@ -449,10 +466,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void toggleTitleAndDescription() { | ||||
|         if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) { | ||||
|             videoTitleTextView.setMaxLines(1); | ||||
| @@ -465,6 +478,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     protected void initViews(final View rootView, final Bundle savedInstanceState) { | ||||
|         super.initViews(rootView, savedInstanceState); | ||||
| @@ -553,11 +570,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void initThumbnailViews(@NonNull final StreamInfo info) { | ||||
|         thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); | ||||
|         if (!TextUtils.isEmpty(info.getThumbnailUrl())) { | ||||
| @@ -581,6 +593,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(final Menu m, final MenuInflater inflater) { | ||||
|         this.menu = m; | ||||
| @@ -654,10 +670,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         Log.e("-----", "missing code"); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OwnStack | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setupActionBar(final StreamInfo info) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]"); | ||||
| @@ -687,7 +699,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public void pushToStack(final int sid, final String videoUrl, final String title) { | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OwnStack | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void pushToStack(final int sid, final String videoUrl, final String title) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "pushToStack() called with: serviceId = [" | ||||
|                     + sid + "], videoUrl = [" + videoUrl + "], title = [" + title + "]"); | ||||
| @@ -706,7 +722,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         stack.push(new StackItem(sid, videoUrl, title)); | ||||
|     } | ||||
|  | ||||
|     public void setTitleToUrl(final int sid, final String videoUrl, final String title) { | ||||
|     private void setTitleToUrl(final int sid, final String videoUrl, final String title) { | ||||
|         if (title != null && !title.isEmpty()) { | ||||
|             for (StackItem stackItem : stack) { | ||||
|                 if (stack.peek().getServiceId() == sid | ||||
| @@ -755,7 +771,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|         prepareAndLoadInfo(); | ||||
|     } | ||||
|  | ||||
|     public void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) { | ||||
|     private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "prepareAndHandleInfo() called with: " | ||||
|                     + "info = [" + info + "], scrollToTop = [" + scrollToTop + "]"); | ||||
| @@ -774,7 +790,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected void prepareAndLoadInfo() { | ||||
|     private void prepareAndLoadInfo() { | ||||
|         appBarLayout.setExpanded(true, true); | ||||
|         pushToStack(serviceId, url, name); | ||||
|         startLoading(false); | ||||
|   | ||||
| @@ -44,20 +44,22 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||
| public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> | ||||
|         implements ListViewContract<I, N>, StateSaver.WriteRead, | ||||
|         SharedPreferences.OnSharedPreferenceChangeListener { | ||||
|     private static final int LIST_MODE_UPDATE_FLAG = 0x32; | ||||
|     protected StateSaver.SavedState savedState; | ||||
|  | ||||
|     private boolean useDefaultStateSaving = true; | ||||
|     private int updateFlags = 0; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private static final int LIST_MODE_UPDATE_FLAG = 0x32; | ||||
|     protected InfoListAdapter infoListAdapter; | ||||
|     protected RecyclerView itemsList; | ||||
|     protected StateSaver.SavedState savedState; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private boolean useDefaultStateSaving = true; | ||||
|     private int updateFlags = 0; | ||||
|  | ||||
|     @Override | ||||
|     public void onAttach(final Context context) { | ||||
| @@ -81,10 +83,6 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> | ||||
|                 .registerOnSharedPreferenceChangeListener(this); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
| @@ -111,6 +109,10 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /** | ||||
|      * If the default implementation of {@link StateSaver.WriteRead} should be used. | ||||
|      * | ||||
|   | ||||
| @@ -70,6 +70,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private SubscriptionManager subscriptionManager; | ||||
|     private View headerRootLayout; | ||||
|     private ImageView headerChannelBanner; | ||||
| @@ -83,10 +84,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|     private LinearLayout headerBackgroundButton; | ||||
|     private MenuItem menuRssButton; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public static ChannelFragment getInstance(final int serviceId, final String url, | ||||
|                                               final String name) { | ||||
|         ChannelFragment instance = new ChannelFragment(); | ||||
| @@ -104,6 +101,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onAttach(final Context context) { | ||||
|         super.onAttach(context); | ||||
| @@ -117,10 +118,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|         return inflater.inflate(R.layout.fragment_channel, container, false); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
| @@ -133,7 +130,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected View getListHeader() { | ||||
| @@ -154,6 +151,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|         return headerRootLayout; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { | ||||
|         super.onCreateOptionsMenu(menu, inflater); | ||||
| @@ -179,10 +180,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Channel Subscription | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(final MenuItem item) { | ||||
|         switch (item.getItemId()) { | ||||
| @@ -208,6 +205,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Channel Subscription | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void monitorSubscription(final ChannelInfo info) { | ||||
|         final Consumer<Throwable> onError = (Throwable throwable) -> { | ||||
|             animateView(headerSubscribeButton, false, 100); | ||||
|   | ||||
| @@ -294,7 +294,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> { | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } else { // Else say we have no uploader | ||||
|         } else { // Otherwise say we have no uploader | ||||
|             headerUploaderName.setText(R.string.playlist_no_uploader); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -80,7 +80,6 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||
|  | ||||
| public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage> | ||||
|         implements BackPressable { | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Search | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
| @@ -97,35 +96,45 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|      */ | ||||
|     private static final int SUGGESTIONS_DEBOUNCE = 120; //ms | ||||
|     private final PublishSubject<String> suggestionPublisher = PublishSubject.create(); | ||||
|     private final CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     @State | ||||
|     protected int filterItemCheckedId = -1; | ||||
|     int filterItemCheckedId = -1; | ||||
|  | ||||
|     @State | ||||
|     protected int serviceId = Constants.NO_SERVICE_ID; | ||||
|     // this three represet the current search query | ||||
|  | ||||
|     // these three represents the current search query | ||||
|     @State | ||||
|     protected String searchString; | ||||
|     String searchString; | ||||
|  | ||||
|     /** | ||||
|      * No content filter should add like contentfilter = all | ||||
|      * No content filter should add like contentFilter = all | ||||
|      * be aware of this when implementing an extractor. | ||||
|      */ | ||||
|     @State | ||||
|     protected String[] contentFilter = new String[0]; | ||||
|     String[] contentFilter = new String[0]; | ||||
|  | ||||
|     @State | ||||
|     protected String sortFilter; | ||||
|     // these represtent the last search | ||||
|     String sortFilter; | ||||
|  | ||||
|     // these represents the last search | ||||
|     @State | ||||
|     protected String lastSearchedString; | ||||
|     String lastSearchedString; | ||||
|  | ||||
|     @State | ||||
|     protected boolean wasSearchFocused = false; | ||||
|     boolean wasSearchFocused = false; | ||||
|  | ||||
|     private Map<Integer, String> menuItemToFilterName; | ||||
|     private StreamingService service; | ||||
|     private String currentPageUrl; | ||||
|     private String nextPageUrl; | ||||
|     private String contentCountry; | ||||
|     private boolean isSuggestionsEnabled = true; | ||||
|  | ||||
|     private Disposable searchDisposable; | ||||
|     private Disposable suggestionDisposable; | ||||
|     private final CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     private SuggestionListAdapter suggestionListAdapter; | ||||
|     private HistoryRecordManager historyRecordManager; | ||||
|  | ||||
| @@ -141,6 +150,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|     private RecyclerView suggestionsRecyclerView; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private TextWatcher textWatcher; | ||||
|  | ||||
|     public static SearchFragment getInstance(final int serviceId, final String searchString) { | ||||
| @@ -154,10 +164,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         return searchFragment; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment's LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /** | ||||
|      * Set wasLoading to true so when the fragment onResume is called, the initial search is done. | ||||
|      */ | ||||
| @@ -165,6 +171,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         wasLoading.set(true); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment's LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onAttach(final Context context) { | ||||
|         super.onAttach(context); | ||||
| @@ -287,10 +297,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { | ||||
|         switch (requestCode) { | ||||
| @@ -310,7 +316,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
| @@ -344,6 +350,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void writeTo(final Queue<Object> objectsToSave) { | ||||
|         super.writeTo(objectsToSave); | ||||
| @@ -358,10 +368,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         nextPageUrl = (String) savedObjects.poll(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init's | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onSaveInstanceState(final Bundle bundle) { | ||||
|         searchString = searchEditText != null | ||||
| @@ -371,7 +377,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     // Init's | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
| @@ -390,6 +396,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { | ||||
|         super.onCreateOptionsMenu(menu, inflater); | ||||
| @@ -430,10 +440,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Search | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void restoreFilterChecked(final Menu menu, final int itemId) { | ||||
|         if (itemId != -1) { | ||||
|             MenuItem item = menu.findItem(itemId); | ||||
| @@ -445,6 +451,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Search | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void showSearchOnStart() { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "showSearchOnStart() called, searchQuery → " | ||||
|   | ||||
| @@ -34,16 +34,15 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf | ||||
|     private static final String INFO_KEY = "related_info_key"; | ||||
|     private CompositeDisposable disposables = new CompositeDisposable(); | ||||
|     private RelatedStreamInfo relatedStreamInfo; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private View headerRootLayout; | ||||
|     private Switch aSwitch; | ||||
|     private boolean mIsVisibleToUser = false; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private boolean mIsVisibleToUser = false; | ||||
|  | ||||
|     public static RelatedVideosFragment getInstance(final StreamInfo info) { | ||||
|         RelatedVideosFragment instance = new RelatedVideosFragment(); | ||||
| @@ -57,6 +56,10 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf | ||||
|         mIsVisibleToUser = isVisibleToUser; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onAttach(final Context context) { | ||||
|         super.onAttach(context); | ||||
| @@ -141,10 +144,6 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // OnError | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void handleNextItems(final ListExtractor.InfoItemsPage result) { | ||||
|         super.handleNextItems(result); | ||||
| @@ -159,7 +158,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     // OnError | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
| @@ -174,6 +173,10 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void setTitle(final String title) { | ||||
|         return; | ||||
|   | ||||
| @@ -92,10 +92,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         return inflater.inflate(R.layout.fragment_bookmarks, container, false); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Views | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void setUserVisibleHint(final boolean isVisibleToUser) { | ||||
|         super.setUserVisibleHint(isVisibleToUser); | ||||
| @@ -104,15 +100,15 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Views | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected void initViews(final View rootView, final Bundle savedInstanceState) { | ||||
|         super.initViews(rootView, savedInstanceState); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Loading | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected void initListeners() { | ||||
|         super.initListeners(); | ||||
| @@ -149,7 +145,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Destruction | ||||
|     // Fragment LifeCycle - Loading | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
| @@ -163,6 +159,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|                 .subscribe(getPlaylistsSubscriber()); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Destruction | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void onPause() { | ||||
|         super.onPause(); | ||||
| @@ -183,10 +183,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         databaseSubscription = null; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Subscriptions Loader | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
| @@ -200,6 +196,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         itemsListState = null; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Subscriptions Loader | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() { | ||||
|         return new Subscriber<List<PlaylistLocalItem>>() { | ||||
|             @Override | ||||
| @@ -229,9 +229,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|             public void onComplete() { } | ||||
|         }; | ||||
|     } | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment Error Handling | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void handleResult(@NonNull final List<PlaylistLocalItem> result) { | ||||
| @@ -252,6 +249,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         hideLoading(); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment Error Handling | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected boolean onError(final Throwable exception) { | ||||
|         if (super.onError(exception)) { | ||||
| @@ -263,10 +264,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected void resetFragment() { | ||||
|         super.resetFragment(); | ||||
| @@ -275,6 +272,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) { | ||||
|         showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid())); | ||||
|     } | ||||
|   | ||||
| @@ -80,16 +80,16 @@ public class StatisticsPlaylistFragment | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Creation | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         recordManager = new HistoryRecordManager(getContext()); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Creation | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, | ||||
|                              @Nullable final ViewGroup container, | ||||
| @@ -111,6 +111,10 @@ public class StatisticsPlaylistFragment | ||||
|         inflater.inflate(R.menu.menu_history, menu); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Views | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected void initViews(final View rootView, final Bundle savedInstanceState) { | ||||
|         super.initViews(rootView, savedInstanceState); | ||||
| @@ -119,10 +123,6 @@ public class StatisticsPlaylistFragment | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Views | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected View getListHeader() { | ||||
|         final View headerRootLayout = activity.getLayoutInflater() | ||||
| @@ -210,6 +210,10 @@ public class StatisticsPlaylistFragment | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Loading | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void startLoading(final boolean forceLoad) { | ||||
|         super.startLoading(forceLoad); | ||||
| @@ -219,7 +223,7 @@ public class StatisticsPlaylistFragment | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Loading | ||||
|     // Fragment LifeCycle - Destruction | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
| @@ -228,10 +232,6 @@ public class StatisticsPlaylistFragment | ||||
|         itemsListState = itemsList.getLayoutManager().onSaveInstanceState(); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment LifeCycle - Destruction | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
| @@ -262,6 +262,10 @@ public class StatisticsPlaylistFragment | ||||
|         itemsListState = null; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Statistics Loader | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() { | ||||
|         return new Subscriber<List<StreamStatisticsEntry>>() { | ||||
|             @Override | ||||
| @@ -294,10 +298,6 @@ public class StatisticsPlaylistFragment | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Statistics Loader | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     public void handleResult(@NonNull final List<StreamStatisticsEntry> result) { | ||||
|         super.handleResult(result); | ||||
| @@ -331,6 +331,10 @@ public class StatisticsPlaylistFragment | ||||
|         hideLoading(); | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment Error Handling | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected void resetFragment() { | ||||
|         super.resetFragment(); | ||||
| @@ -338,9 +342,6 @@ public class StatisticsPlaylistFragment | ||||
|             databaseSubscription.cancel(); | ||||
|         } | ||||
|     } | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // Fragment Error Handling | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @Override | ||||
|     protected boolean onError(final Throwable exception) { | ||||
| @@ -353,6 +354,10 @@ public class StatisticsPlaylistFragment | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void toggleSortMode() { | ||||
|         if (sortMode == StatisticSortMode.LAST_PLAYED) { | ||||
|             sortMode = StatisticSortMode.MOST_PLAYED; | ||||
| @@ -370,10 +375,6 @@ public class StatisticsPlaylistFragment | ||||
|         startLoading(true); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private PlayQueue getPlayQueueStartingAt(final StreamStatisticsEntry infoItem) { | ||||
|         return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0)); | ||||
|     } | ||||
|   | ||||
| @@ -56,12 +56,14 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|     // Save the list 10 seconds after the last change occurred | ||||
|     private static final long SAVE_DEBOUNCE_MILLIS = 10000; | ||||
|     private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; | ||||
|  | ||||
|     @State | ||||
|     protected Long playlistId; | ||||
|     @State | ||||
|     protected String name; | ||||
|     @State | ||||
|     protected Parcelable itemsListState; | ||||
|     Parcelable itemsListState; | ||||
|  | ||||
|     private View headerRootLayout; | ||||
|     private TextView headerTitleView; | ||||
|     private TextView headerStreamCount; | ||||
|   | ||||
| @@ -47,18 +47,20 @@ public class SubscriptionsImportFragment extends BaseFragment { | ||||
|     private static final int REQUEST_IMPORT_FILE_CODE = 666; | ||||
|  | ||||
|     @State | ||||
|     protected int currentServiceId = Constants.NO_SERVICE_ID; | ||||
|     int currentServiceId = Constants.NO_SERVICE_ID; | ||||
|  | ||||
|     private List<SubscriptionExtractor.ContentSource> supportedSources; | ||||
|     private String relatedUrl; | ||||
|  | ||||
|     @StringRes | ||||
|     private int instructionsString; | ||||
|     private TextView infoTextView; | ||||
|     private EditText inputText; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private TextView infoTextView; | ||||
|     private EditText inputText; | ||||
|     private Button inputButton; | ||||
|  | ||||
|     public static SubscriptionsImportFragment getInstance(final int serviceId) { | ||||
| @@ -67,7 +69,7 @@ public class SubscriptionsImportFragment extends BaseFragment { | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
|     public void setInitialData(final int serviceId) { | ||||
|     private void setInitialData(final int serviceId) { | ||||
|         this.currentServiceId = serviceId; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -53,9 +53,16 @@ import io.reactivex.processors.PublishProcessor; | ||||
|  | ||||
| public abstract class BaseImportExportService extends Service { | ||||
|     protected final String TAG = this.getClass().getSimpleName(); | ||||
|     private static final int NOTIFICATION_SAMPLING_PERIOD = 2500; | ||||
|  | ||||
|     protected final CompositeDisposable disposables = new CompositeDisposable(); | ||||
|     protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create(); | ||||
|  | ||||
|     protected NotificationManagerCompat notificationManager; | ||||
|     protected NotificationCompat.Builder notificationBuilder; | ||||
|     protected SubscriptionManager subscriptionManager; | ||||
|  | ||||
|     private static final int NOTIFICATION_SAMPLING_PERIOD = 2500; | ||||
|  | ||||
|     protected final AtomicInteger currentProgress = new AtomicInteger(-1); | ||||
|     protected final AtomicInteger maxProgress = new AtomicInteger(-1); | ||||
|     protected final ImportExportEventListener eventListener = new ImportExportEventListener() { | ||||
| @@ -71,13 +78,7 @@ public abstract class BaseImportExportService extends Service { | ||||
|             notificationUpdater.onNext(itemName); | ||||
|         } | ||||
|     }; | ||||
|     protected NotificationManagerCompat notificationManager; | ||||
|     protected NotificationCompat.Builder notificationBuilder; | ||||
|     protected SubscriptionManager subscriptionManager; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Notification Impl | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     protected Toast toast; | ||||
|  | ||||
|     @Nullable | ||||
| @@ -103,6 +104,10 @@ public abstract class BaseImportExportService extends Service { | ||||
|         disposables.clear(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Notification Impl | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected abstract int getNotificationId(); | ||||
|  | ||||
|     @StringRes | ||||
|   | ||||
| @@ -67,15 +67,18 @@ public class SubscriptionsImportService extends BaseImportExportService { | ||||
|      */ | ||||
|     public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription" | ||||
|             + ".services.SubscriptionsImportService.IMPORT_COMPLETE"; | ||||
|  | ||||
|     /** | ||||
|      * How many extractions running in parallel. | ||||
|      */ | ||||
|     public static final int PARALLEL_EXTRACTIONS = 8; | ||||
|  | ||||
|     /** | ||||
|      * Number of items to buffer to mass-insert in the subscriptions table, | ||||
|      * this leads to a better performance as we can then use db transactions. | ||||
|      */ | ||||
|     public static final int BUFFER_COUNT_BEFORE_INSERT = 50; | ||||
|  | ||||
|     private Subscription subscription; | ||||
|     private int currentMode; | ||||
|     private int currentServiceId; | ||||
| @@ -131,10 +134,6 @@ public class SubscriptionsImportService extends BaseImportExportService { | ||||
|         return 4568; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Imports | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public int getTitle() { | ||||
|         return R.string.import_ongoing; | ||||
| @@ -148,6 +147,10 @@ public class SubscriptionsImportService extends BaseImportExportService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Imports | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void startImport() { | ||||
|         showToast(R.string.import_ongoing); | ||||
|  | ||||
|   | ||||
| @@ -99,10 +99,22 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ | ||||
| @SuppressWarnings({"WeakerAccess"}) | ||||
| public abstract class BasePlayer implements | ||||
|         Player.EventListener, PlaybackListener, ImageLoadingListener { | ||||
|  | ||||
|     public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); | ||||
|     @NonNull | ||||
|     public static final String TAG = "BasePlayer"; | ||||
|  | ||||
|     public static final int STATE_PREFLIGHT = -1; | ||||
|     public static final int STATE_BLOCKED = 123; | ||||
|     public static final int STATE_PLAYING = 124; | ||||
|     public static final int STATE_BUFFERING = 125; | ||||
|     public static final int STATE_PAUSED = 126; | ||||
|     public static final int STATE_PAUSED_SEEK = 127; | ||||
|     public static final int STATE_COMPLETED = 128; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Intent | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @NonNull | ||||
|     public static final String REPEAT_MODE = "repeat_mode"; | ||||
|     @NonNull | ||||
| @@ -123,26 +135,43 @@ public abstract class BasePlayer implements | ||||
|     public static final String START_PAUSED = "start_paused"; | ||||
|     @NonNull | ||||
|     public static final String SELECT_ON_APPEND = "select_on_append"; | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Intent | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     @NonNull | ||||
|     public static final String IS_MUTED = "is_muted"; | ||||
|     public static final int STATE_PREFLIGHT = -1; | ||||
|     public static final int STATE_BLOCKED = 123; | ||||
|     public static final int STATE_PLAYING = 124; | ||||
|     public static final int STATE_BUFFERING = 125; | ||||
|     public static final int STATE_PAUSED = 126; | ||||
|     public static final int STATE_PAUSED_SEEK = 127; | ||||
|     public static final int STATE_COMPLETED = 128; | ||||
|     protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f}; | ||||
|     protected static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds | ||||
|     protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     protected static final int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds | ||||
|  | ||||
|     protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f}; | ||||
|  | ||||
|     protected PlayQueue playQueue; | ||||
|     protected PlayQueueAdapter playQueueAdapter; | ||||
|  | ||||
|     @Nullable | ||||
|     protected MediaSourceManager playbackManager; | ||||
|  | ||||
|     @Nullable | ||||
|     private PlayQueueItem currentItem; | ||||
|     @Nullable | ||||
|     private MediaSourceTag currentMetadata; | ||||
|     @Nullable | ||||
|     private Bitmap currentThumbnail; | ||||
|  | ||||
|     @Nullable | ||||
|     protected Toast errorToast; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Player | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds | ||||
|     protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500; | ||||
|  | ||||
|     protected SimpleExoPlayer simpleExoPlayer; | ||||
|     protected AudioReactor audioReactor; | ||||
|     protected MediaSessionManager mediaSessionManager; | ||||
|  | ||||
|  | ||||
|     @NonNull | ||||
|     protected final Context context; | ||||
|     @NonNull | ||||
| @@ -158,39 +187,17 @@ public abstract class BasePlayer implements | ||||
|     @NonNull | ||||
|     private final LoadControl loadControl; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Player | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     @NonNull | ||||
|     private final RenderersFactory renderFactory; | ||||
|     @NonNull | ||||
|     private final SerialDisposable progressUpdateReactor; | ||||
|     @NonNull | ||||
|     private final CompositeDisposable databaseUpdateReactor; | ||||
|     protected PlayQueue playQueue; | ||||
|     protected PlayQueueAdapter playQueueAdapter; | ||||
|     @Nullable | ||||
|     protected MediaSourceManager playbackManager; | ||||
|     @Nullable | ||||
|     protected Toast errorToast; | ||||
|     protected SimpleExoPlayer simpleExoPlayer; | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     protected AudioReactor audioReactor; | ||||
|     protected MediaSessionManager mediaSessionManager; | ||||
|     protected int currentState = STATE_PREFLIGHT; | ||||
|     @Nullable | ||||
|     private PlayQueueItem currentItem; | ||||
|     @Nullable | ||||
|     private MediaSourceTag currentMetadata; | ||||
|     @Nullable | ||||
|     private Bitmap currentThumbnail; | ||||
|     private boolean isPrepared = false; | ||||
|     private Disposable stateLoader; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Thumbnail Loading | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     protected int currentState = STATE_PREFLIGHT; | ||||
|  | ||||
|     public BasePlayer(@NonNull final Context context) { | ||||
|         this.context = context; | ||||
| @@ -247,8 +254,7 @@ public abstract class BasePlayer implements | ||||
|         registerBroadcastReceiver(); | ||||
|     } | ||||
|  | ||||
|     public void initListeners() { | ||||
|     } | ||||
|     public void initListeners() { } | ||||
|  | ||||
|     public void handleIntent(final Intent intent) { | ||||
|         if (DEBUG) { | ||||
| @@ -324,10 +330,6 @@ public abstract class BasePlayer implements | ||||
|                 /*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Broadcast Receiver | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected void initPlayback(@NonNull final PlayQueue queue, | ||||
|                                 @Player.RepeatMode final int repeatMode, | ||||
|                                 final float playbackSpeed, | ||||
| @@ -398,9 +400,12 @@ public abstract class BasePlayer implements | ||||
|  | ||||
|         databaseUpdateReactor.clear(); | ||||
|         progressUpdateReactor.set(null); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Thumbnail Loading | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void initThumbnail(final String url) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "Thumbnail - initThumbnail() called"); | ||||
| @@ -413,10 +418,6 @@ public abstract class BasePlayer implements | ||||
|                 .loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // States Implementation | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onLoadingStarted(final String imageUri, final View view) { | ||||
|         if (DEBUG) { | ||||
| @@ -453,6 +454,10 @@ public abstract class BasePlayer implements | ||||
|         currentThumbnail = null; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Broadcast Receiver | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /** | ||||
|      * Add your action in the intentFilter. | ||||
|      * | ||||
| @@ -488,6 +493,10 @@ public abstract class BasePlayer implements | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // States Implementation | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public void changeState(final int state) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "changeState() called with: state = [" + state + "]"); | ||||
| @@ -1328,6 +1337,7 @@ public abstract class BasePlayer implements | ||||
|             playQueue.append(autoQueue.getStreams()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Getters and Setters | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|   | ||||
| @@ -1181,11 +1181,14 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|     private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener | ||||
|             implements View.OnTouchListener { | ||||
|         private static final int MOVEMENT_THRESHOLD = 40; | ||||
|  | ||||
|         private final boolean isVolumeGestureEnabled = PlayerHelper | ||||
|                 .isVolumeGestureEnabled(getApplicationContext()); | ||||
|         private final boolean isBrightnessGestureEnabled = PlayerHelper | ||||
|                 .isBrightnessGestureEnabled(getApplicationContext()); | ||||
|  | ||||
|         private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume(); | ||||
|  | ||||
|         private boolean isMoving; | ||||
|  | ||||
|         @Override | ||||
|   | ||||
| @@ -54,14 +54,19 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|         View.OnClickListener, PlaybackParameterDialog.Callback { | ||||
|     private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47; | ||||
|     private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80; | ||||
|  | ||||
|     protected BasePlayer player; | ||||
|  | ||||
|     private boolean serviceBound; | ||||
|     private ServiceConnection serviceConnection; | ||||
|  | ||||
|     private boolean seeking; | ||||
|     private boolean redraw; | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     private boolean seeking; | ||||
|     private boolean redraw; | ||||
|  | ||||
|     private View rootView; | ||||
|  | ||||
|     private RecyclerView itemsList; | ||||
|   | ||||
| @@ -90,51 +90,69 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         Player.EventListener, | ||||
|         PopupMenu.OnMenuItemClickListener, | ||||
|         PopupMenu.OnDismissListener { | ||||
|     public final String TAG; | ||||
|     public static final boolean DEBUG = BasePlayer.DEBUG; | ||||
|     public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Player | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis | ||||
|     public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000;  // 2 Seconds | ||||
|     protected static final int RENDERER_UNAVAILABLE = -1; | ||||
|     public final String TAG; | ||||
|  | ||||
|     @NonNull | ||||
|     private final VideoPlaybackResolver resolver; | ||||
|     private final Handler controlsVisibilityHandler = new Handler(); | ||||
|     private final int qualityPopupMenuGroupId = 69; | ||||
|     private final int playbackSpeedPopupMenuGroupId = 79; | ||||
|  | ||||
|     private List<VideoStream> availableStreams; | ||||
|     private int selectedStreamIndex; | ||||
|  | ||||
|     protected boolean wasPlaying = false; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private final int captionPopupMenuGroupId = 89; | ||||
|     protected boolean wasPlaying = false; | ||||
|     boolean isSomePopupMenuVisible = false; | ||||
|     private List<VideoStream> availableStreams; | ||||
|     private int selectedStreamIndex; | ||||
|  | ||||
|     private View rootView; | ||||
|  | ||||
|     private AspectRatioFrameLayout aspectRatioFrameLayout; | ||||
|     private SurfaceView surfaceView; | ||||
|     private View surfaceForeground; | ||||
|  | ||||
|     private View loadingPanel; | ||||
|     private ImageView endScreen; | ||||
|     private ImageView controlAnimationView; | ||||
|  | ||||
|     private View controlsRoot; | ||||
|     private TextView currentDisplaySeek; | ||||
|  | ||||
|     private View bottomControlsRoot; | ||||
|     private SeekBar playbackSeekBar; | ||||
|     private TextView playbackCurrentTime; | ||||
|     private TextView playbackEndTime; | ||||
|     private TextView playbackLiveSync; | ||||
|     private TextView playbackSpeedTextView; | ||||
|  | ||||
|     private View topControlsRoot; | ||||
|     private TextView qualityTextView; | ||||
|  | ||||
|     private SubtitleView subtitleView; | ||||
|  | ||||
|     private TextView resizeView; | ||||
|     private TextView captionTextView; | ||||
|  | ||||
|     private ValueAnimator controlViewAnimator; | ||||
|     private final Handler controlsVisibilityHandler = new Handler(); | ||||
|  | ||||
|     boolean isSomePopupMenuVisible = false; | ||||
|  | ||||
|     private final int qualityPopupMenuGroupId = 69; | ||||
|     private PopupMenu qualityPopupMenu; | ||||
|  | ||||
|     private final int playbackSpeedPopupMenuGroupId = 79; | ||||
|     private PopupMenu playbackSpeedPopupMenu; | ||||
|  | ||||
|     private final int captionPopupMenuGroupId = 89; | ||||
|     private PopupMenu captionPopupMenu; | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
| @@ -238,10 +256,6 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // UI Builders | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void handleIntent(final Intent intent) { | ||||
|         if (intent == null) { | ||||
| @@ -255,6 +269,10 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         super.handleIntent(intent); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // UI Builders | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public void buildQualityMenu() { | ||||
|         if (qualityPopupMenu == null) { | ||||
|             return; | ||||
| @@ -354,9 +372,6 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         } | ||||
|         captionPopupMenu.setOnDismissListener(this); | ||||
|     } | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void updateStreamRelatedViews() { | ||||
|         if (getCurrentMetadata() == null) { | ||||
| @@ -413,6 +428,10 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         playbackSpeedTextView.setVisibility(View.VISIBLE); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver(); | ||||
|  | ||||
|     protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { | ||||
| @@ -420,16 +439,16 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         updateStreamRelatedViews(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // States Implementation | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     @Nullable | ||||
|     public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) { | ||||
|         return resolver.resolve(info); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // States Implementation | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onBlocked() { | ||||
|         super.onBlocked(); | ||||
| @@ -494,10 +513,6 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         showAndAnimateControl(-1, true); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // ExoPlayer Video Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCompleted() { | ||||
|         super.onCompleted(); | ||||
| @@ -510,6 +525,10 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         animateView(surfaceForeground, true, 100); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // ExoPlayer Video Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onTracksChanged(final TrackGroupArray trackGroups, | ||||
|                                 final TrackSelectionArray trackSelections) { | ||||
| @@ -537,15 +556,15 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         aspectRatioFrameLayout.setAspectRatio(((float) width) / height); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // ExoPlayer Track Updates | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onRenderedFirstFrame() { | ||||
|         animateView(surfaceForeground, false, 100); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // ExoPlayer Track Updates | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void onTextTrackUpdate() { | ||||
|         final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT); | ||||
|  | ||||
|   | ||||
| @@ -20,17 +20,20 @@ import java.io.File; | ||||
|  | ||||
| /* package-private */ class CacheFactory implements DataSource.Factory { | ||||
|     private static final String TAG = "CacheFactory"; | ||||
|  | ||||
|     private static final String CACHE_FOLDER_NAME = "exoplayer"; | ||||
|     private static final int CACHE_FLAGS = CacheDataSource.FLAG_BLOCK_ON_CACHE | ||||
|             | CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR; | ||||
|  | ||||
|     private final DefaultDataSourceFactory dataSourceFactory; | ||||
|     private final File cacheDir; | ||||
|     private final long maxFileSize; | ||||
|  | ||||
|     // Creating cache on every instance may cause problems with multiple players when | ||||
|     // sources are not ExtractorMediaSource | ||||
|     // see: https://stackoverflow.com/questions/28700391/using-cache-in-exoplayer | ||||
|     // todo: make this a singleton? | ||||
|     private static SimpleCache cache; | ||||
|     private final DefaultDataSourceFactory dataSourceFactory; | ||||
|     private final File cacheDir; | ||||
|     private final long maxFileSize; | ||||
|  | ||||
|     CacheFactory(@NonNull final Context context, | ||||
|                  @NonNull final String userAgent, | ||||
|   | ||||
| @@ -23,19 +23,23 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; | ||||
|  | ||||
| public class PlaybackParameterDialog extends DialogFragment { | ||||
|     // Minimum allowable range in ExoPlayer | ||||
|     public static final double MINIMUM_PLAYBACK_VALUE = 0.10f; | ||||
|     public static final double MAXIMUM_PLAYBACK_VALUE = 3.00f; | ||||
|     public static final char STEP_UP_SIGN = '+'; | ||||
|     public static final char STEP_DOWN_SIGN = '-'; | ||||
|     public static final double STEP_ONE_PERCENT_VALUE = 0.01f; | ||||
|     public static final double STEP_FIVE_PERCENT_VALUE = 0.05f; | ||||
|     public static final double STEP_TEN_PERCENT_VALUE = 0.10f; | ||||
|     public static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f; | ||||
|     public static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f; | ||||
|     public static final double DEFAULT_TEMPO = 1.00f; | ||||
|     public static final double DEFAULT_PITCH = 1.00f; | ||||
|     public static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE; | ||||
|     public static final boolean DEFAULT_SKIP_SILENCE = false; | ||||
|     private static final double MINIMUM_PLAYBACK_VALUE = 0.10f; | ||||
|     private static final double MAXIMUM_PLAYBACK_VALUE = 3.00f; | ||||
|  | ||||
|     private static final char STEP_UP_SIGN = '+'; | ||||
|     private static final char STEP_DOWN_SIGN = '-'; | ||||
|  | ||||
|     private static final double STEP_ONE_PERCENT_VALUE = 0.01f; | ||||
|     private static final double STEP_FIVE_PERCENT_VALUE = 0.05f; | ||||
|     private static final double STEP_TEN_PERCENT_VALUE = 0.10f; | ||||
|     private static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f; | ||||
|     private static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f; | ||||
|  | ||||
|     private static final double DEFAULT_TEMPO = 1.00f; | ||||
|     private static final double DEFAULT_PITCH = 1.00f; | ||||
|     private static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE; | ||||
|     private static final boolean DEFAULT_SKIP_SILENCE = false; | ||||
|  | ||||
|     @NonNull | ||||
|     private static final String TAG = "PlaybackParameterDialog"; | ||||
|     @NonNull | ||||
| @@ -49,18 +53,22 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|     private static final String PITCH_KEY = "pitch_key"; | ||||
|     @NonNull | ||||
|     private static final String STEP_SIZE_KEY = "step_size_key"; | ||||
|  | ||||
|     @NonNull | ||||
|     private final SliderStrategy strategy = new SliderStrategy.Quadratic( | ||||
|             MINIMUM_PLAYBACK_VALUE, MAXIMUM_PLAYBACK_VALUE, | ||||
|             /*centerAt=*/1.00f, /*sliderGranularity=*/10000); | ||||
|  | ||||
|     @Nullable | ||||
|     private Callback callback; | ||||
|  | ||||
|     private double initialTempo = DEFAULT_TEMPO; | ||||
|     private double initialPitch = DEFAULT_PITCH; | ||||
|     private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE; | ||||
|     private double tempo = DEFAULT_TEMPO; | ||||
|     private double pitch = DEFAULT_PITCH; | ||||
|     private double stepSize = DEFAULT_STEP; | ||||
|  | ||||
|     @Nullable | ||||
|     private SeekBar tempoSlider; | ||||
|     @Nullable | ||||
| @@ -96,25 +104,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         return dialog; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getStepUpPercentString(final double percent) { | ||||
|         return STEP_UP_SIGN + getPercentString(percent); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getStepDownPercentString(final double percent) { | ||||
|         return STEP_DOWN_SIGN + getPercentString(percent); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getPercentString(final double percent) { | ||||
|         return PlayerHelper.formatPitch(percent); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onAttach(final Context context) { | ||||
|         super.onAttach(context); | ||||
| @@ -125,10 +118,6 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Dialog | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
|         assureCorrectAppLanguage(getContext()); | ||||
| @@ -143,10 +132,6 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Control Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onSaveInstanceState(final Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
| @@ -158,6 +143,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize()); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Dialog | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { | ||||
| @@ -179,6 +168,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         return dialogBuilder.create(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Control Views | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setupControlViews(@NonNull final View rootView) { | ||||
|         setupHookingControl(rootView); | ||||
|         setupSkipSilenceControl(rootView); | ||||
| @@ -273,10 +266,6 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Sliders | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setupStepSizeSelector(@NonNull final View rootView) { | ||||
|         TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent); | ||||
|         TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent); | ||||
| @@ -355,6 +344,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Sliders | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private SeekBar.OnSeekBarChangeListener getOnTempoChangedListener() { | ||||
|         return new SeekBar.OnSeekBarChangeListener() { | ||||
|             @Override | ||||
| @@ -430,10 +423,6 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         setPitchSlider(newValue); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Helper | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setTempoSlider(final double newTempo) { | ||||
|         if (tempoSlider == null) { | ||||
|             return; | ||||
| @@ -448,6 +437,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         pitchSlider.setProgress(strategy.progressOf(newPitch)); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Helper | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setCurrentPlaybackParameters() { | ||||
|         setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence()); | ||||
|     } | ||||
| @@ -483,6 +476,21 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|         return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked(); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getStepUpPercentString(final double percent) { | ||||
|         return STEP_UP_SIGN + getPercentString(percent); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getStepDownPercentString(final double percent) { | ||||
|         return STEP_DOWN_SIGN + getPercentString(percent); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     private static String getPercentString(final double percent) { | ||||
|         return PlayerHelper.formatPitch(percent); | ||||
|     } | ||||
|  | ||||
|     public interface Callback { | ||||
|         void onPlaybackParameterChanged(float playbackTempo, float playbackPitch, | ||||
|                                         boolean playbackSkipSilence); | ||||
|   | ||||
| @@ -58,6 +58,10 @@ public final class PlayerHelper { | ||||
|  | ||||
|     private PlayerHelper() { } | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Exposed helpers | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     public static String getTimeString(final int milliSeconds) { | ||||
|         int seconds = (milliSeconds % 60000) / 1000; | ||||
|         int minutes = (milliSeconds % 3600000) / 60000; | ||||
| @@ -72,9 +76,6 @@ public final class PlayerHelper { | ||||
|                 ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds).toString() | ||||
|                 : STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString(); | ||||
|     } | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Exposed helpers | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     public static String formatSpeed(final double speed) { | ||||
|         return SPEED_FORMATTER.format(speed); | ||||
| @@ -177,14 +178,14 @@ public final class PlayerHelper { | ||||
|                 ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); | ||||
|     } | ||||
|  | ||||
|     public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { | ||||
|         return isResumeAfterAudioFocusGain(context, false); | ||||
|     } | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Settings Resolution | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { | ||||
|         return isResumeAfterAudioFocusGain(context, false); | ||||
|     } | ||||
|  | ||||
|     public static boolean isVolumeGestureEnabled(@NonNull final Context context) { | ||||
|         return isVolumeGestureEnabled(context, true); | ||||
|     } | ||||
| @@ -322,15 +323,15 @@ public final class PlayerHelper { | ||||
|         setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis()); | ||||
|     } | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Private helpers | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     @NonNull | ||||
|     private static SharedPreferences getPreferences(@NonNull final Context context) { | ||||
|         return PreferenceManager.getDefaultSharedPreferences(context); | ||||
|     } | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Private helpers | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     private static boolean isResumeAfterAudioFocusGain(@NonNull final Context context, | ||||
|                                                        final boolean b) { | ||||
|         return getPreferences(context) | ||||
|   | ||||
| @@ -44,16 +44,21 @@ import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfo | ||||
| import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG; | ||||
|  | ||||
| public class MediaSourceManager { | ||||
|     @NonNull | ||||
|     private final String TAG = "MediaSourceManager@" + hashCode(); | ||||
|  | ||||
|     /** | ||||
|      * Determines how many streams before and after the current stream should be loaded. | ||||
|      * The default value (1) ensures seamless playback under typical network settings. | ||||
|      * <br><br> | ||||
|      * <p> | ||||
|      * The streams after the current will be loaded into the playlist timeline while the | ||||
|      * streams before will only be cached for future usage. | ||||
|      * </p> | ||||
|      * | ||||
|      * @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource) | ||||
|      */ | ||||
|     private static final int WINDOW_SIZE = 1; | ||||
|  | ||||
|     /** | ||||
|      * Determines the maximum number of disposables allowed in the {@link #loaderReactor}. | ||||
|      * Once exceeded, new calls to {@link #loadImmediate()} will evict all disposables in the | ||||
| @@ -63,12 +68,12 @@ public class MediaSourceManager { | ||||
|      * @see #maybeLoadItem(PlayQueueItem) | ||||
|      */ | ||||
|     private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1; | ||||
|     @NonNull | ||||
|     private final String TAG = "MediaSourceManager@" + hashCode(); | ||||
|  | ||||
|     @NonNull | ||||
|     private final PlaybackListener playbackListener; | ||||
|     @NonNull | ||||
|     private final PlayQueue playQueue; | ||||
|  | ||||
|     /** | ||||
|      * Determines the gap time between the playback position and the playback duration which | ||||
|      * the {@link #getEdgeIntervalSignal()} begins to request loading. | ||||
| @@ -76,35 +81,45 @@ public class MediaSourceManager { | ||||
|      * @see #progressUpdateIntervalMillis | ||||
|      */ | ||||
|     private final long playbackNearEndGapMillis; | ||||
|  | ||||
|     /** | ||||
|      * Determines the interval which the {@link #getEdgeIntervalSignal()} waits for between | ||||
|      * each request for loading, once {@link #playbackNearEndGapMillis} has reached. | ||||
|      */ | ||||
|     private final long progressUpdateIntervalMillis; | ||||
|  | ||||
|     @NonNull | ||||
|     private final Observable<Long> nearEndIntervalSignal; | ||||
|  | ||||
|     /** | ||||
|      * Process only the last load order when receiving a stream of load orders (lessens I/O). | ||||
|      * <br><br> | ||||
|      * <p> | ||||
|      * The higher it is, the less loading occurs during rapid noncritical timeline changes. | ||||
|      * <br><br> | ||||
|      * </p> | ||||
|      * <p> | ||||
|      * Not recommended to go below 100ms. | ||||
|      * </p> | ||||
|      * | ||||
|      * @see #loadDebounced() | ||||
|      */ | ||||
|     private final long loadDebounceMillis; | ||||
|  | ||||
|     @NonNull | ||||
|     private final Disposable debouncedLoader; | ||||
|     @NonNull | ||||
|     private final PublishSubject<Long> debouncedSignal; | ||||
|  | ||||
|     @NonNull | ||||
|     private Subscription playQueueReactor; | ||||
|  | ||||
|     @NonNull | ||||
|     private final CompositeDisposable loaderReactor; | ||||
|     @NonNull | ||||
|     private final Set<PlayQueueItem> loadingItems; | ||||
|  | ||||
|     @NonNull | ||||
|     private final AtomicBoolean isBlocked; | ||||
|     @NonNull | ||||
|     private Subscription playQueueReactor; | ||||
|  | ||||
|     @NonNull | ||||
|     private ManagedMediaSourcePlaylist playlist; | ||||
|  | ||||
| @@ -160,42 +175,6 @@ public class MediaSourceManager { | ||||
|     // Exposed Methods | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Manager Helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     @Nullable | ||||
|     private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) { | ||||
|         // The current item has higher priority | ||||
|         final int currentIndex = playQueue.getIndex(); | ||||
|         final PlayQueueItem currentItem = playQueue.getItem(currentIndex); | ||||
|         if (currentItem == null) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // The rest are just for seamless playback | ||||
|         // Although timeline is not updated prior to the current index, these sources are still | ||||
|         // loaded into the cache for faster retrieval at a potentially later time. | ||||
|         final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE); | ||||
|         final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1; | ||||
|         final int rightBound = Math.min(playQueue.size(), rightLimit); | ||||
|         final Set<PlayQueueItem> neighbors = new ArraySet<>( | ||||
|                 playQueue.getStreams().subList(leftBound, rightBound)); | ||||
|  | ||||
|         // Do a round robin | ||||
|         final int excess = rightLimit - playQueue.size(); | ||||
|         if (excess >= 0) { | ||||
|             neighbors.addAll(playQueue.getStreams() | ||||
|                     .subList(0, Math.min(playQueue.size(), excess))); | ||||
|         } | ||||
|         neighbors.remove(currentItem); | ||||
|  | ||||
|         return new ItemsToLoad(currentItem, neighbors); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Event Reactor | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /** | ||||
|      * Dispose the manager and releases all message buses and loaders. | ||||
|      */ | ||||
| @@ -211,6 +190,10 @@ public class MediaSourceManager { | ||||
|         loaderReactor.dispose(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Event Reactor | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private Subscriber<PlayQueueEvent> getReactor() { | ||||
|         return new Subscriber<PlayQueueEvent>() { | ||||
|             @Override | ||||
| @@ -233,10 +216,6 @@ public class MediaSourceManager { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback Locking | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void onPlayQueueChanged(final PlayQueueEvent event) { | ||||
|         if (playQueue.isEmpty() && playQueue.isComplete()) { | ||||
|             playbackListener.onPlaybackShutdown(); | ||||
| @@ -298,6 +277,10 @@ public class MediaSourceManager { | ||||
|         playQueueReactor.request(1); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback Locking | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private boolean isPlayQueueReady() { | ||||
|         final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE; | ||||
|         return playQueue.isComplete() || isWindowLoaded; | ||||
| @@ -332,10 +315,6 @@ public class MediaSourceManager { | ||||
|         isBlocked.set(true); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Metadata Synchronization | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void maybeUnblock() { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "maybeUnblock() called."); | ||||
| @@ -347,6 +326,10 @@ public class MediaSourceManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Metadata Synchronization | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void maybeSync() { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "maybeSync() called."); | ||||
| @@ -360,10 +343,6 @@ public class MediaSourceManager { | ||||
|         playbackListener.onPlaybackSynchronize(currentItem); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // MediaSource Loading | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private synchronized void maybeSynchronizePlayer() { | ||||
|         if (isPlayQueueReady() && isPlaybackReady()) { | ||||
|             maybeUnblock(); | ||||
| @@ -371,6 +350,10 @@ public class MediaSourceManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // MediaSource Loading | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private Observable<Long> getEdgeIntervalSignal() { | ||||
|         return Observable.interval(progressUpdateIntervalMillis, TimeUnit.MILLISECONDS) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
| @@ -523,9 +506,6 @@ public class MediaSourceManager { | ||||
|         } | ||||
|         playlist.invalidate(currentIndex, removeMediaSourceHandler, this::loadImmediate); | ||||
|     } | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // MediaSource Playlist Helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void maybeClearLoaders() { | ||||
|         if (DEBUG) { | ||||
| @@ -538,6 +518,10 @@ public class MediaSourceManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // MediaSource Playlist Helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void resetSources() { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "resetSources() called."); | ||||
| @@ -554,6 +538,39 @@ public class MediaSourceManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Manager Helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Nullable | ||||
|     private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) { | ||||
|         // The current item has higher priority | ||||
|         final int currentIndex = playQueue.getIndex(); | ||||
|         final PlayQueueItem currentItem = playQueue.getItem(currentIndex); | ||||
|         if (currentItem == null) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // The rest are just for seamless playback | ||||
|         // Although timeline is not updated prior to the current index, these sources are still | ||||
|         // loaded into the cache for faster retrieval at a potentially later time. | ||||
|         final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE); | ||||
|         final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1; | ||||
|         final int rightBound = Math.min(playQueue.size(), rightLimit); | ||||
|         final Set<PlayQueueItem> neighbors = new ArraySet<>( | ||||
|                 playQueue.getStreams().subList(leftBound, rightBound)); | ||||
|  | ||||
|         // Do a round robin | ||||
|         final int excess = rightLimit - playQueue.size(); | ||||
|         if (excess >= 0) { | ||||
|             neighbors.addAll(playQueue.getStreams() | ||||
|                     .subList(0, Math.min(playQueue.size(), excess))); | ||||
|         } | ||||
|         neighbors.remove(currentItem); | ||||
|  | ||||
|         return new ItemsToLoad(currentItem, neighbors); | ||||
|     } | ||||
|  | ||||
|     private static class ItemsToLoad { | ||||
|         @NonNull | ||||
|         private final PlayQueueItem center; | ||||
|   | ||||
| @@ -16,10 +16,11 @@ import io.reactivex.annotations.NonNull; | ||||
| import io.reactivex.disposables.Disposable; | ||||
|  | ||||
| abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue { | ||||
|     final int serviceId; | ||||
|     final String baseUrl; | ||||
|     boolean isInitial; | ||||
|     private boolean isComplete; | ||||
|  | ||||
|     final int serviceId; | ||||
|     final String baseUrl; | ||||
|     String nextUrl; | ||||
|  | ||||
|     private transient Disposable fetchReactor; | ||||
| @@ -40,16 +41,6 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext | ||||
|         this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     private static List<PlayQueueItem> extractListItems(final List<StreamInfoItem> infos) { | ||||
|         List<PlayQueueItem> result = new ArrayList<>(); | ||||
|         for (final InfoItem stream : infos) { | ||||
|             if (stream instanceof StreamInfoItem) { | ||||
|                 result.add(new PlayQueueItem((StreamInfoItem) stream)); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     protected abstract String getTag(); | ||||
|  | ||||
|     @Override | ||||
| @@ -134,4 +125,14 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext | ||||
|         } | ||||
|         fetchReactor = null; | ||||
|     } | ||||
|  | ||||
|     private static List<PlayQueueItem> extractListItems(final List<StreamInfoItem> infos) { | ||||
|         List<PlayQueueItem> result = new ArrayList<>(); | ||||
|         for (final InfoItem stream : infos) { | ||||
|             if (stream instanceof StreamInfoItem) { | ||||
|                 result.add(new PlayQueueItem((StreamInfoItem) stream)); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,17 +36,22 @@ import io.reactivex.subjects.BehaviorSubject; | ||||
|  * <p> | ||||
|  * This class contains basic manipulation of a playlist while also functions as a | ||||
|  * message bus, providing all listeners with new updates to the play queue. | ||||
|  * </p> | ||||
|  * <p> | ||||
|  * This class can be serialized for passing intents, but in order to start the | ||||
|  * message bus, it must be initialized. | ||||
|  * </p> | ||||
|  */ | ||||
| public abstract class PlayQueue implements Serializable { | ||||
|     public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); | ||||
|     private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); | ||||
|     @NonNull | ||||
|     private final AtomicInteger queueIndex; | ||||
|     public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); | ||||
|  | ||||
|     private ArrayList<PlayQueueItem> backup; | ||||
|     private ArrayList<PlayQueueItem> streams; | ||||
|  | ||||
|     @NonNull | ||||
|     private final AtomicInteger queueIndex; | ||||
|  | ||||
|     private transient BehaviorSubject<PlayQueueEvent> eventBroadcast; | ||||
|     private transient Flowable<PlayQueueEvent> broadcastReceiver; | ||||
|     private transient Subscription reportingReactor; | ||||
|   | ||||
| @@ -28,19 +28,23 @@ import io.reactivex.disposables.Disposable; | ||||
|  * <p> | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * InfoListAdapter.java is part of NewPipe. | ||||
|  * </p> | ||||
|  * <p> | ||||
|  * NewPipe is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * </p> | ||||
|  * <p> | ||||
|  * NewPipe is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * </p> | ||||
|  * <p> | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * </p> | ||||
|  */ | ||||
|  | ||||
| public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
|   | ||||
| @@ -32,6 +32,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { | ||||
|     private final PlayerDataSource dataSource; | ||||
|     @NonNull | ||||
|     private final QualityResolver qualityResolver; | ||||
|  | ||||
|     @Nullable | ||||
|     private String playbackQuality; | ||||
|  | ||||
|   | ||||
| @@ -181,25 +181,6 @@ public class ErrorActivity extends AppCompatActivity { | ||||
|         return out; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the checked activity. | ||||
|      * | ||||
|      * @param returnActivity the activity to return to | ||||
|      * @return the casted return activity or null | ||||
|      */ | ||||
|     @Nullable | ||||
|     static Class<? extends Activity> getReturnActivity(final Class<?> returnActivity) { | ||||
|         Class<? extends Activity> checkedReturnActivity = null; | ||||
|         if (returnActivity != null) { | ||||
|             if (Activity.class.isAssignableFrom(returnActivity)) { | ||||
|                 checkedReturnActivity = returnActivity.asSubclass(Activity.class); | ||||
|             } else { | ||||
|                 checkedReturnActivity = MainActivity.class; | ||||
|             } | ||||
|         } | ||||
|         return checkedReturnActivity; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(final Bundle savedInstanceState) { | ||||
|         assureCorrectAppLanguage(this); | ||||
| @@ -315,6 +296,25 @@ public class ErrorActivity extends AppCompatActivity { | ||||
|         return text.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the checked activity. | ||||
|      * | ||||
|      * @param returnActivity the activity to return to | ||||
|      * @return the casted return activity or null | ||||
|      */ | ||||
|     @Nullable | ||||
|     static Class<? extends Activity> getReturnActivity(final Class<?> returnActivity) { | ||||
|         Class<? extends Activity> checkedReturnActivity = null; | ||||
|         if (returnActivity != null) { | ||||
|             if (Activity.class.isAssignableFrom(returnActivity)) { | ||||
|                 checkedReturnActivity = returnActivity.asSubclass(Activity.class); | ||||
|             } else { | ||||
|                 checkedReturnActivity = MainActivity.class; | ||||
|             } | ||||
|         } | ||||
|         return checkedReturnActivity; | ||||
|     } | ||||
|  | ||||
|     private void goToReturnActivity() { | ||||
|         Class<? extends Activity> checkedReturnActivity = getReturnActivity(returnActivity); | ||||
|         if (checkedReturnActivity == null) { | ||||
|   | ||||
| @@ -54,17 +54,20 @@ import io.reactivex.schedulers.Schedulers; | ||||
|  | ||||
| public class PeertubeInstanceListFragment extends Fragment { | ||||
|     private static final int MENU_ITEM_RESTORE_ID = 123456; | ||||
|     public InstanceListAdapter instanceListAdapter; | ||||
|  | ||||
|     private List<PeertubeInstance> instanceList = new ArrayList<>(); | ||||
|     private PeertubeInstance selectedInstance; | ||||
|     private String savedInstanceListKey; | ||||
|     private InstanceListAdapter instanceListAdapter; | ||||
|  | ||||
|     private ProgressBar progressBar; | ||||
|     private SharedPreferences sharedPreferences; | ||||
|  | ||||
|     private CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private CompositeDisposable disposables = new CompositeDisposable(); | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
| @@ -122,9 +125,6 @@ public class PeertubeInstanceListFragment extends Fragment { | ||||
|         super.onPause(); | ||||
|         saveChanges(); | ||||
|     } | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
| @@ -135,6 +135,10 @@ public class PeertubeInstanceListFragment extends Fragment { | ||||
|         disposables = null; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { | ||||
|         super.onCreateOptionsMenu(menu, inflater); | ||||
| @@ -284,10 +288,6 @@ public class PeertubeInstanceListFragment extends Fragment { | ||||
|         instanceListAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // List Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private ItemTouchHelper.SimpleCallback getItemTouchCallback() { | ||||
|         return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, | ||||
|                 ItemTouchHelper.START | ItemTouchHelper.END) { | ||||
| @@ -348,6 +348,10 @@ public class PeertubeInstanceListFragment extends Fragment { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // List Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private class InstanceListAdapter | ||||
|             extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> { | ||||
|         private final LayoutInflater inflater; | ||||
|   | ||||
| @@ -57,18 +57,18 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|     /** | ||||
|      * This contains the base display options for images. | ||||
|      */ | ||||
|     public static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS | ||||
|     private static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS | ||||
|             = new DisplayImageOptions.Builder().cacheInMemory(true).build(); | ||||
|     private final ImageLoader imageLoader = ImageLoader.getInstance(); | ||||
|     OnSelectedLisener onSelectedLisener = null; | ||||
|     OnCancelListener onCancelListener = null; | ||||
|     private ProgressBar progressBar; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Interfaces | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private final ImageLoader imageLoader = ImageLoader.getInstance(); | ||||
|  | ||||
|     private OnSelectedLisener onSelectedLisener = null; | ||||
|     private OnCancelListener onCancelListener = null; | ||||
|  | ||||
|     private ProgressBar progressBar; | ||||
|     private TextView emptyView; | ||||
|     private RecyclerView recyclerView; | ||||
|  | ||||
|     private List<SubscriptionEntity> subscriptions = new Vector<>(); | ||||
|  | ||||
|     public void setOnSelectedLisener(final OnSelectedLisener listener) { | ||||
| @@ -79,6 +79,10 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|         onCancelListener = listener; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, | ||||
|                              final Bundle savedInstanceState) { | ||||
| @@ -105,7 +109,7 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Init | ||||
|     // Handle actions | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
| @@ -116,11 +120,6 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Handle actions | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void clickedItem(final int position) { | ||||
|         if (onSelectedLisener != null) { | ||||
|             SubscriptionEntity entry = subscriptions.get(position); | ||||
| @@ -130,6 +129,10 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|         dismiss(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Item handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void displayChannels(final List<SubscriptionEntity> newSubscriptions) { | ||||
|         this.subscriptions = newSubscriptions; | ||||
|         progressBar.setVisibility(View.GONE); | ||||
| @@ -141,10 +144,6 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Item handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private Observer<List<SubscriptionEntity>> getSubscriptionObserver() { | ||||
|         return new Observer<List<SubscriptionEntity>>() { | ||||
|             @Override | ||||
| @@ -165,29 +164,28 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Error | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected void onError(final Throwable e) { | ||||
|         final Activity activity = getActivity(); | ||||
|         ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo | ||||
|                 .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Interfaces | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public interface OnSelectedLisener { | ||||
|         void onChannelSelected(int serviceId, String url, String name); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Error | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public interface OnCancelListener { | ||||
|         void onCancel(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // ImageLoaderOptions | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private class SelectChannelAdapter | ||||
|             extends RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> { | ||||
|         @Override | ||||
| @@ -219,8 +217,8 @@ public class SelectChannelFragment extends DialogFragment { | ||||
|  | ||||
|         public class SelectChannelItemHolder extends RecyclerView.ViewHolder { | ||||
|             public final View view; | ||||
|             public final CircleImageView thumbnailView; | ||||
|             public final TextView titleView; | ||||
|             final CircleImageView thumbnailView; | ||||
|             final TextView titleView; | ||||
|             SelectChannelItemHolder(final View v) { | ||||
|                 super(v); | ||||
|                 this.view = v; | ||||
|   | ||||
| @@ -50,9 +50,6 @@ public class SelectKioskFragment extends DialogFragment { | ||||
|     private RecyclerView recyclerView = null; | ||||
|     private SelectKioskAdapter selectKioskAdapter = null; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Interfaces | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private OnSelectedLisener onSelectedLisener = null; | ||||
|     private OnCancelListener onCancelListener = null; | ||||
|  | ||||
| @@ -80,6 +77,10 @@ public class SelectKioskFragment extends DialogFragment { | ||||
|         return v; | ||||
|     } | ||||
|  | ||||
|    /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Handle actions | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCancel(final DialogInterface dialogInterface) { | ||||
|         super.onCancel(dialogInterface); | ||||
| @@ -95,8 +96,8 @@ public class SelectKioskFragment extends DialogFragment { | ||||
|         dismiss(); | ||||
|     } | ||||
|  | ||||
|    /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Handle actions | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Error | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     protected void onError(final Throwable e) { | ||||
| @@ -105,6 +106,10 @@ public class SelectKioskFragment extends DialogFragment { | ||||
|                 .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Interfaces | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public interface OnSelectedLisener { | ||||
|         void onKioskSelected(int serviceId, String kioskId, String kioskName); | ||||
|     } | ||||
| @@ -113,10 +118,6 @@ public class SelectKioskFragment extends DialogFragment { | ||||
|         void onCancel(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Error | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private class SelectKioskAdapter | ||||
|             extends RecyclerView.Adapter<SelectKioskAdapter.SelectKioskItemHolder> { | ||||
|         private final List<Entry> kioskList = new Vector<>(); | ||||
|   | ||||
| @@ -45,10 +45,11 @@ import static org.schabi.newpipe.settings.tabs.Tab.typeFrom; | ||||
|  | ||||
| public class ChooseTabsFragment extends Fragment { | ||||
|     private static final int MENU_ITEM_RESTORE_ID = 123456; | ||||
|     private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter; | ||||
|  | ||||
|     private TabsManager tabsManager; | ||||
|  | ||||
|     private List<Tab> tabList = new ArrayList<>(); | ||||
|     private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Lifecycle | ||||
| @@ -93,16 +94,16 @@ public class ChooseTabsFragment extends Fragment { | ||||
|         updateTitle(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onPause() { | ||||
|         super.onPause(); | ||||
|         saveChanges(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Menu | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { | ||||
|         super.onCreateOptionsMenu(menu, inflater); | ||||
| @@ -216,7 +217,7 @@ public class ChooseTabsFragment extends Fragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public ChooseTabListItem[] getAvailableTabs(final Context context) { | ||||
|     private ChooseTabListItem[] getAvailableTabs(final Context context) { | ||||
|         final ArrayList<ChooseTabListItem> returnList = new ArrayList<>(); | ||||
|  | ||||
|         for (Tab.Type type : Tab.Type.values()) { | ||||
|   | ||||
| @@ -39,6 +39,10 @@ public abstract class Tab { | ||||
|         readDataFromJson(jsonObject); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Tab Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Nullable | ||||
|     public static Tab from(@NonNull final JsonObject jsonObject) { | ||||
|         final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1); | ||||
| @@ -85,10 +89,6 @@ public abstract class Tab { | ||||
|         return type.getTab(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // JSON Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public abstract int getTabId(); | ||||
|  | ||||
|     public abstract String getTabName(Context context); | ||||
| @@ -104,10 +104,6 @@ public abstract class Tab { | ||||
|      */ | ||||
|     public abstract Fragment getFragment(Context context) throws ExtractionException; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Tab Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(final Object obj) { | ||||
|         if (obj == this) { | ||||
| @@ -118,6 +114,10 @@ public abstract class Tab { | ||||
|                 && ((Tab) obj).getTabId() == this.getTabId(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // JSON Handling | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public void writeJsonOn(final JsonSink jsonSink) { | ||||
|         jsonSink.object(); | ||||
|  | ||||
|   | ||||
| @@ -41,10 +41,6 @@ public final class TabsManager { | ||||
|         sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public void resetTabs() { | ||||
|         sharedPreferences.edit().remove(savedTabsKey).apply(); | ||||
|     } | ||||
| @@ -53,6 +49,10 @@ public final class TabsManager { | ||||
|         return TabsJsonHelper.getDefaultTabs(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public void setSavedTabsListener(final SavedTabsChangeListener listener) { | ||||
|         if (preferenceChangeListener != null) { | ||||
|             sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener); | ||||
| @@ -83,12 +83,4 @@ public final class TabsManager { | ||||
|     public interface SavedTabsChangeListener { | ||||
|         void onTabsChanged(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -32,18 +32,20 @@ import org.schabi.newpipe.extractor.InfoItem; | ||||
| import java.util.Map; | ||||
|  | ||||
| public final class InfoCache { | ||||
|     private final String TAG = getClass().getSimpleName(); | ||||
|     private static final boolean DEBUG = MainActivity.DEBUG; | ||||
|  | ||||
|     private static final InfoCache INSTANCE = new InfoCache(); | ||||
|     private static final int MAX_ITEMS_ON_CACHE = 60; | ||||
|     /** | ||||
|      * Trim the cache to this size. | ||||
|      */ | ||||
|     private static final int TRIM_CACHE_TO = 30; | ||||
|  | ||||
|     private static final LruCache<String, CacheData> LRU_CACHE = new LruCache<>(MAX_ITEMS_ON_CACHE); | ||||
|     private final String TAG = getClass().getSimpleName(); | ||||
|  | ||||
|     private InfoCache() { | ||||
|         //no instance | ||||
|         // no instance | ||||
|     } | ||||
|  | ||||
|     public static InfoCache getInstance() { | ||||
|   | ||||
| @@ -47,26 +47,26 @@ import static org.schabi.newpipe.MainActivity.DEBUG; | ||||
|  * A view that can be fully collapsed and expanded. | ||||
|  */ | ||||
| public class CollapsibleView extends LinearLayout { | ||||
|     private static final String TAG = CollapsibleView.class.getSimpleName(); | ||||
|  | ||||
|     private static final int ANIMATION_DURATION = 420; | ||||
|  | ||||
|     public static final int COLLAPSED = 0; | ||||
|     public static final int EXPANDED = 1; | ||||
|     private static final String TAG = CollapsibleView.class.getSimpleName(); | ||||
|     private static final int ANIMATION_DURATION = 420; | ||||
|     private final List<StateListener> listeners = new ArrayList<>(); | ||||
|  | ||||
|     @State | ||||
|     @ViewMode | ||||
|     int currentState = COLLAPSED; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Collapse/expand logic | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private boolean readyToChangeState; | ||||
|  | ||||
|     private int targetHeight = -1; | ||||
|     private ValueAnimator currentAnimator; | ||||
|     private final List<StateListener> listeners = new ArrayList<>(); | ||||
|  | ||||
|     public CollapsibleView(final Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     public CollapsibleView(final Context context, @Nullable final AttributeSet attrs) { | ||||
|         super(context, attrs); | ||||
|     } | ||||
| @@ -75,12 +75,17 @@ public class CollapsibleView extends LinearLayout { | ||||
|                            final int defStyleAttr) { | ||||
|         super(context, attrs, defStyleAttr); | ||||
|     } | ||||
|  | ||||
|     @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) | ||||
|     public CollapsibleView(final Context context, final AttributeSet attrs, final int defStyleAttr, | ||||
|                            final int defStyleRes) { | ||||
|         super(context, attrs, defStyleAttr, defStyleRes); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Collapse/expand logic | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /** | ||||
|      * This method recalculates the height of this view so it <b>must</b> be called when | ||||
|      * some child changes (e.g. add new views, change text). | ||||
| @@ -198,6 +203,10 @@ public class CollapsibleView extends LinearLayout { | ||||
|         listeners.remove(listener); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Parcelable onSaveInstanceState() { | ||||
| @@ -212,7 +221,7 @@ public class CollapsibleView extends LinearLayout { | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // State Saving | ||||
|     // Internal | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     public String getDebugLogString(final String description) { | ||||
| @@ -226,12 +235,7 @@ public class CollapsibleView extends LinearLayout { | ||||
|  | ||||
|     @Retention(SOURCE) | ||||
|     @IntDef({COLLAPSED, EXPANDED}) | ||||
|     public @interface ViewMode { | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Internal | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     public @interface ViewMode { } | ||||
|  | ||||
|     /** | ||||
|      * Simple interface used for listening state changes of the {@link CollapsibleView}. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 wb9688
					wb9688