Move things back to its original place

This commit is contained in:
wb9688 2020-04-02 13:51:10 +02:00
parent fda5405e48
commit 63bcc04eff
35 changed files with 630 additions and 499 deletions

View File

@ -204,7 +204,7 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
* *
* @param versionName Name of new version * @param versionName Name of new version
* @param apkLocationUrl Url with the new apk * @param apkLocationUrl Url with the new apk
* @param versionCode V * @param versionCode Code of new version
*/ */
private void compareAppVersionAndShowNotification(final String versionName, private void compareAppVersionAndShowNotification(final String versionName,
final String apkLocationUrl, final String apkLocationUrl,

View File

@ -86,35 +86,41 @@ public class DownloadDialog extends DialogFragment
private static final String TAG = "DialogFragment"; private static final String TAG = "DialogFragment";
private static final boolean DEBUG = MainActivity.DEBUG; private static final boolean DEBUG = MainActivity.DEBUG;
private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230; private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230;
private final CompositeDisposable disposables = new CompositeDisposable();
@State @State
protected StreamInfo currentInfo; StreamInfo currentInfo;
@State @State
protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty(); StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty();
@State @State
protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty(); StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty();
@State @State
protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty(); StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty();
@State @State
protected int selectedVideoIndex = 0; int selectedVideoIndex = 0;
@State @State
protected int selectedAudioIndex = 0; int selectedAudioIndex = 0;
@State @State
protected int selectedSubtitleIndex = 0; int selectedSubtitleIndex = 0;
private StoredDirectoryHelper mainStorageAudio = null; private StoredDirectoryHelper mainStorageAudio = null;
private StoredDirectoryHelper mainStorageVideo = null; private StoredDirectoryHelper mainStorageVideo = null;
private DownloadManager downloadManager = null; private DownloadManager downloadManager = null;
private ActionMenuItemView okButton = null; private ActionMenuItemView okButton = null;
private Context context; private Context context;
private boolean askForSavePath; private boolean askForSavePath;
private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter; private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter;
private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter; private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter;
private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter; private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter;
private final CompositeDisposable disposables = new CompositeDisposable();
private EditText nameEditText; private EditText nameEditText;
private Spinner streamsSpinner; private Spinner streamsSpinner;
private RadioGroup radioStreamsGroup; private RadioGroup radioStreamsGroup;
private TextView threadsCountTextView; private TextView threadsCountTextView;
private SeekBar threadsSeekBar; private SeekBar threadsSeekBar;
private SharedPreferences prefs; private SharedPreferences prefs;
public static DownloadDialog newInstance(final StreamInfo info) { public static DownloadDialog newInstance(final StreamInfo info) {

View File

@ -109,54 +109,55 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener,
View.OnClickListener, View.OnLongClickListener { View.OnClickListener, View.OnLongClickListener {
public static final String AUTO_PLAY = "auto_play"; 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 RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2; private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4; private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
private static final int COMMENTS_UPDATE_FLAG = 0x8; 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 boolean autoPlayEnabled;
private static final String EMPTY_TAB_TAG = "EMPTY TAB"; private boolean showRelatedStreams;
private static final String INFO_KEY = "info_key"; private boolean showComments;
private static final String STACK_KEY = "stack_key"; private String selectedTabTag;
/**
* Stack that contains the "navigation history".<br>
* The peek is the current video.
*/
private final LinkedList<StackItem> stack = new LinkedList<>();
@State @State
protected int serviceId = Constants.NO_SERVICE_ID; protected int serviceId = Constants.NO_SERVICE_ID;
@State @State
protected String name; protected String name;
@State @State
protected String url; protected String url;
private int updateFlags = 0;
private boolean autoPlayEnabled;
private boolean showRelatedStreams;
private boolean showComments;
private String selectedTabTag;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private StreamInfo currentInfo; private StreamInfo currentInfo;
private Disposable currentWorker; private Disposable currentWorker;
@NonNull @NonNull
private CompositeDisposable disposables = new CompositeDisposable(); private CompositeDisposable disposables = new CompositeDisposable();
@Nullable @Nullable
private Disposable positionSubscriber = null; private Disposable positionSubscriber = null;
private List<VideoStream> sortedVideoStreams; private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1; private int selectedVideoStreamIndex = -1;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private Menu menu; private Menu menu;
private Spinner spinnerToolbar; private Spinner spinnerToolbar;
private LinearLayout contentRootLayoutHiding; private LinearLayout contentRootLayoutHiding;
private View thumbnailBackgroundButton; private View thumbnailBackgroundButton;
private ImageView thumbnailImageView; private ImageView thumbnailImageView;
private ImageView thumbnailPlayButton; private ImageView thumbnailPlayButton;
private AnimatedProgressBar positionView; private AnimatedProgressBar positionView;
private View videoTitleRoot; private View videoTitleRoot;
private TextView videoTitleTextView; private TextView videoTitleTextView;
private ImageView videoTitleToggleArrow; private ImageView videoTitleToggleArrow;
private TextView videoCountView; private TextView videoCountView;
private TextView detailControlsBackground; private TextView detailControlsBackground;
private TextView detailControlsPopup; private TextView detailControlsPopup;
private TextView detailControlsAddToPlaylist; private TextView detailControlsAddToPlaylist;
@ -164,30 +165,42 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
private TextView appendControlsDetail; private TextView appendControlsDetail;
private TextView detailDurationView; private TextView detailDurationView;
private TextView detailPositionView; private TextView detailPositionView;
private LinearLayout videoDescriptionRootLayout; private LinearLayout videoDescriptionRootLayout;
private TextView videoUploadDateView; private TextView videoUploadDateView;
private TextView videoDescriptionView; private TextView videoDescriptionView;
private View uploaderRootLayout; private View uploaderRootLayout;
private TextView uploaderTextView; private TextView uploaderTextView;
private ImageView uploaderThumb; private ImageView uploaderThumb;
private TextView thumbsUpTextView; private TextView thumbsUpTextView;
private ImageView thumbsUpImageView; private ImageView thumbsUpImageView;
private TextView thumbsDownTextView; private TextView thumbsDownTextView;
private ImageView thumbsDownImageView; private ImageView thumbsDownImageView;
private TextView thumbsDisabledTextView; private TextView thumbsDisabledTextView;
private AppBarLayout appBarLayout; private AppBarLayout appBarLayout;
private ViewPager viewPager; private ViewPager viewPager;
/*////////////////////////////////////////////////////////////////////////*/
private TabAdaptor pageAdapter; private TabAdaptor pageAdapter;
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
private TabLayout tabLayout; private TabLayout tabLayout;
private FrameLayout relatedStreamsLayout; 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, public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl,
final String name) { final String name) {
VideoDetailFragment instance = new VideoDetailFragment(); VideoDetailFragment instance = new VideoDetailFragment();
@ -195,6 +208,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
return instance; return instance;
} }
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -285,10 +303,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
disposables = null; disposables = null;
} }
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onDestroyView() { public void onDestroyView() {
if (DEBUG) { if (DEBUG) {
@ -336,6 +350,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onSaveInstanceState(final Bundle outState) { public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
@ -351,10 +369,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
outState.putSerializable(STACK_KEY, stack); outState.putSerializable(STACK_KEY, stack);
} }
/*//////////////////////////////////////////////////////////////////////////
// OnClick
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
protected void onRestoreInstanceState(@NonNull final Bundle savedState) { protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
super.onRestoreInstanceState(savedState); super.onRestoreInstanceState(savedState);
@ -371,9 +385,12 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
//noinspection unchecked //noinspection unchecked
stack.addAll((Collection<? extends StackItem>) serializable); stack.addAll((Collection<? extends StackItem>) serializable);
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// OnClick
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
if (isLoading.get() || currentInfo == null) { if (isLoading.get() || currentInfo == null) {
@ -449,10 +466,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
private void toggleTitleAndDescription() { private void toggleTitleAndDescription() {
if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) { if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) {
videoTitleTextView.setMaxLines(1); videoTitleTextView.setMaxLines(1);
@ -465,6 +478,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
@ -553,11 +570,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
}; };
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
private void initThumbnailViews(@NonNull final StreamInfo info) { private void initThumbnailViews(@NonNull final StreamInfo info) {
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(info.getThumbnailUrl())) { if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
@ -581,6 +593,10 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreateOptionsMenu(final Menu m, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu m, final MenuInflater inflater) {
this.menu = m; this.menu = m;
@ -654,10 +670,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
Log.e("-----", "missing code"); Log.e("-----", "missing code");
} }
/*//////////////////////////////////////////////////////////////////////////
// OwnStack
//////////////////////////////////////////////////////////////////////////*/
private void setupActionBar(final StreamInfo info) { private void setupActionBar(final StreamInfo info) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]"); 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) { if (DEBUG) {
Log.d(TAG, "pushToStack() called with: serviceId = [" Log.d(TAG, "pushToStack() called with: serviceId = ["
+ sid + "], videoUrl = [" + videoUrl + "], title = [" + title + "]"); + sid + "], videoUrl = [" + videoUrl + "], title = [" + title + "]");
@ -706,7 +722,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
stack.push(new StackItem(sid, videoUrl, title)); 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()) { if (title != null && !title.isEmpty()) {
for (StackItem stackItem : stack) { for (StackItem stackItem : stack) {
if (stack.peek().getServiceId() == sid if (stack.peek().getServiceId() == sid
@ -755,7 +771,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
prepareAndLoadInfo(); prepareAndLoadInfo();
} }
public void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) { private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "prepareAndHandleInfo() called with: " Log.d(TAG, "prepareAndHandleInfo() called with: "
+ "info = [" + info + "], scrollToTop = [" + scrollToTop + "]"); + "info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
@ -774,7 +790,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
} }
protected void prepareAndLoadInfo() { private void prepareAndLoadInfo() {
appBarLayout.setExpanded(true, true); appBarLayout.setExpanded(true, true);
pushToStack(serviceId, url, name); pushToStack(serviceId, url, name);
startLoading(false); startLoading(false);

View File

@ -44,20 +44,22 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
implements ListViewContract<I, N>, StateSaver.WriteRead, implements ListViewContract<I, N>, StateSaver.WriteRead,
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
protected StateSaver.SavedState savedState;
private boolean useDefaultStateSaving = true;
private int updateFlags = 0;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
protected InfoListAdapter infoListAdapter; protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList; protected RecyclerView itemsList;
protected StateSaver.SavedState savedState;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// LifeCycle // LifeCycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private boolean useDefaultStateSaving = true;
private int updateFlags = 0;
@Override @Override
public void onAttach(final Context context) { public void onAttach(final Context context) {
@ -81,10 +83,6 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
.registerOnSharedPreferenceChangeListener(this); .registerOnSharedPreferenceChangeListener(this);
} }
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onDestroy() { public void onDestroy() {
super.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. * If the default implementation of {@link StateSaver.WriteRead} should be used.
* *

View File

@ -70,6 +70,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private SubscriptionManager subscriptionManager; private SubscriptionManager subscriptionManager;
private View headerRootLayout; private View headerRootLayout;
private ImageView headerChannelBanner; private ImageView headerChannelBanner;
@ -83,10 +84,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private LinearLayout headerBackgroundButton; private LinearLayout headerBackgroundButton;
private MenuItem menuRssButton; private MenuItem menuRssButton;
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
public static ChannelFragment getInstance(final int serviceId, final String url, public static ChannelFragment getInstance(final int serviceId, final String url,
final String name) { final String name) {
ChannelFragment instance = new ChannelFragment(); ChannelFragment instance = new ChannelFragment();
@ -104,6 +101,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onAttach(final Context context) { public void onAttach(final Context context) {
super.onAttach(context); super.onAttach(context);
@ -117,10 +118,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
return inflater.inflate(R.layout.fragment_channel, container, false); return inflater.inflate(R.layout.fragment_channel, container, false);
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -133,7 +130,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Menu // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected View getListHeader() { protected View getListHeader() {
@ -154,6 +151,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
return headerRootLayout; return headerRootLayout;
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@ -179,10 +180,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Channel Subscription
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
@ -208,6 +205,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Channel Subscription
//////////////////////////////////////////////////////////////////////////*/
private void monitorSubscription(final ChannelInfo info) { private void monitorSubscription(final ChannelInfo info) {
final Consumer<Throwable> onError = (Throwable throwable) -> { final Consumer<Throwable> onError = (Throwable throwable) -> {
animateView(headerSubscribeButton, false, 100); animateView(headerSubscribeButton, false, 100);

View File

@ -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); headerUploaderName.setText(R.string.playlist_no_uploader);
} }

View File

@ -80,7 +80,6 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage> public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
implements BackPressable { implements BackPressable {
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Search // Search
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -97,35 +96,45 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
*/ */
private static final int SUGGESTIONS_DEBOUNCE = 120; //ms private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
private final PublishSubject<String> suggestionPublisher = PublishSubject.create(); private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
private final CompositeDisposable disposables = new CompositeDisposable();
@State @State
protected int filterItemCheckedId = -1; int filterItemCheckedId = -1;
@State @State
protected int serviceId = Constants.NO_SERVICE_ID; protected int serviceId = Constants.NO_SERVICE_ID;
// this three represet the current search query
// these three represents the current search query
@State @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. * be aware of this when implementing an extractor.
*/ */
@State @State
protected String[] contentFilter = new String[0]; String[] contentFilter = new String[0];
@State @State
protected String sortFilter; String sortFilter;
// these represtent the last search
// these represents the last search
@State @State
protected String lastSearchedString; String lastSearchedString;
@State @State
protected boolean wasSearchFocused = false; boolean wasSearchFocused = false;
private Map<Integer, String> menuItemToFilterName; private Map<Integer, String> menuItemToFilterName;
private StreamingService service; private StreamingService service;
private String currentPageUrl; private String currentPageUrl;
private String nextPageUrl; private String nextPageUrl;
private String contentCountry; private String contentCountry;
private boolean isSuggestionsEnabled = true; private boolean isSuggestionsEnabled = true;
private Disposable searchDisposable; private Disposable searchDisposable;
private Disposable suggestionDisposable; private Disposable suggestionDisposable;
private final CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter; private SuggestionListAdapter suggestionListAdapter;
private HistoryRecordManager historyRecordManager; private HistoryRecordManager historyRecordManager;
@ -141,6 +150,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
private RecyclerView suggestionsRecyclerView; private RecyclerView suggestionsRecyclerView;
/*////////////////////////////////////////////////////////////////////////*/ /*////////////////////////////////////////////////////////////////////////*/
private TextWatcher textWatcher; private TextWatcher textWatcher;
public static SearchFragment getInstance(final int serviceId, final String searchString) { public static SearchFragment getInstance(final int serviceId, final String searchString) {
@ -154,10 +164,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
return searchFragment; return searchFragment;
} }
/*//////////////////////////////////////////////////////////////////////////
// Fragment's LifeCycle
//////////////////////////////////////////////////////////////////////////*/
/** /**
* Set wasLoading to true so when the fragment onResume is called, the initial search is done. * 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); wasLoading.set(true);
} }
/*//////////////////////////////////////////////////////////////////////////
// Fragment's LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onAttach(final Context context) { public void onAttach(final Context context) {
super.onAttach(context); super.onAttach(context);
@ -287,10 +297,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) { switch (requestCode) {
@ -310,7 +316,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// State Saving // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
@ -344,6 +350,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear); searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
} }
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void writeTo(final Queue<Object> objectsToSave) { public void writeTo(final Queue<Object> objectsToSave) {
super.writeTo(objectsToSave); super.writeTo(objectsToSave);
@ -358,10 +368,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
nextPageUrl = (String) savedObjects.poll(); nextPageUrl = (String) savedObjects.poll();
} }
/*//////////////////////////////////////////////////////////////////////////
// Init's
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onSaveInstanceState(final Bundle bundle) { public void onSaveInstanceState(final Bundle bundle) {
searchString = searchEditText != null searchString = searchEditText != null
@ -371,7 +377,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Menu // Init's
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
@ -390,6 +396,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@ -430,10 +440,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Search
//////////////////////////////////////////////////////////////////////////*/
private void restoreFilterChecked(final Menu menu, final int itemId) { private void restoreFilterChecked(final Menu menu, final int itemId) {
if (itemId != -1) { if (itemId != -1) {
MenuItem item = menu.findItem(itemId); MenuItem item = menu.findItem(itemId);
@ -445,6 +451,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Search
//////////////////////////////////////////////////////////////////////////*/
private void showSearchOnStart() { private void showSearchOnStart() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "showSearchOnStart() called, searchQuery → " Log.d(TAG, "showSearchOnStart() called, searchQuery → "

View File

@ -34,16 +34,15 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
private static final String INFO_KEY = "related_info_key"; private static final String INFO_KEY = "related_info_key";
private CompositeDisposable disposables = new CompositeDisposable(); private CompositeDisposable disposables = new CompositeDisposable();
private RelatedStreamInfo relatedStreamInfo; private RelatedStreamInfo relatedStreamInfo;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private View headerRootLayout; private View headerRootLayout;
private Switch aSwitch; private Switch aSwitch;
private boolean mIsVisibleToUser = false;
/*////////////////////////////////////////////////////////////////////////// private boolean mIsVisibleToUser = false;
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
public static RelatedVideosFragment getInstance(final StreamInfo info) { public static RelatedVideosFragment getInstance(final StreamInfo info) {
RelatedVideosFragment instance = new RelatedVideosFragment(); RelatedVideosFragment instance = new RelatedVideosFragment();
@ -57,6 +56,10 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
mIsVisibleToUser = isVisibleToUser; mIsVisibleToUser = isVisibleToUser;
} }
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onAttach(final Context context) { public void onAttach(final Context context) {
super.onAttach(context); super.onAttach(context);
@ -141,10 +144,6 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// OnError
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void handleNextItems(final ListExtractor.InfoItemsPage result) { public void handleNextItems(final ListExtractor.InfoItemsPage result) {
super.handleNextItems(result); super.handleNextItems(result);
@ -159,7 +158,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Utils // OnError
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
@ -174,6 +173,10 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void setTitle(final String title) { public void setTitle(final String title) {
return; return;

View File

@ -92,10 +92,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
return inflater.inflate(R.layout.fragment_bookmarks, container, false); return inflater.inflate(R.layout.fragment_bookmarks, container, false);
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void setUserVisibleHint(final boolean isVisibleToUser) { public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser); super.setUserVisibleHint(isVisibleToUser);
@ -104,15 +100,15 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
} }
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected void initListeners() { protected void initListeners() {
super.initListeners(); super.initListeners();
@ -149,7 +145,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction // Fragment LifeCycle - Loading
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@Override @Override
@ -163,6 +159,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
.subscribe(getPlaylistsSubscriber()); .subscribe(getPlaylistsSubscriber());
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
@ -183,10 +183,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
databaseSubscription = null; databaseSubscription = null;
} }
///////////////////////////////////////////////////////////////////////////
// Subscriptions Loader
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -200,6 +196,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
itemsListState = null; itemsListState = null;
} }
///////////////////////////////////////////////////////////////////////////
// Subscriptions Loader
///////////////////////////////////////////////////////////////////////////
private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() { private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() {
return new Subscriber<List<PlaylistLocalItem>>() { return new Subscriber<List<PlaylistLocalItem>>() {
@Override @Override
@ -229,9 +229,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
public void onComplete() { } public void onComplete() { }
}; };
} }
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void handleResult(@NonNull final List<PlaylistLocalItem> result) { public void handleResult(@NonNull final List<PlaylistLocalItem> result) {
@ -252,6 +249,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
hideLoading(); hideLoading();
} }
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected boolean onError(final Throwable exception) { protected boolean onError(final Throwable exception) {
if (super.onError(exception)) { if (super.onError(exception)) {
@ -263,10 +264,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
return true; return true;
} }
///////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected void resetFragment() { protected void resetFragment() {
super.resetFragment(); super.resetFragment();
@ -275,6 +272,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
} }
} }
///////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////
private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) { private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) {
showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid())); showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid()));
} }

View File

@ -80,16 +80,16 @@ public class StatisticsPlaylistFragment
} }
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Creation
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
recordManager = new HistoryRecordManager(getContext()); recordManager = new HistoryRecordManager(getContext());
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Creation
///////////////////////////////////////////////////////////////////////////
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container, @Nullable final ViewGroup container,
@ -111,6 +111,10 @@ public class StatisticsPlaylistFragment
inflater.inflate(R.menu.menu_history, menu); inflater.inflate(R.menu.menu_history, menu);
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
@ -119,10 +123,6 @@ public class StatisticsPlaylistFragment
} }
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected View getListHeader() { protected View getListHeader() {
final View headerRootLayout = activity.getLayoutInflater() final View headerRootLayout = activity.getLayoutInflater()
@ -210,6 +210,10 @@ public class StatisticsPlaylistFragment
return true; return true;
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void startLoading(final boolean forceLoad) { public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad); super.startLoading(forceLoad);
@ -219,7 +223,7 @@ public class StatisticsPlaylistFragment
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading // Fragment LifeCycle - Destruction
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@Override @Override
@ -228,10 +232,6 @@ public class StatisticsPlaylistFragment
itemsListState = itemsList.getLayoutManager().onSaveInstanceState(); itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
} }
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
@ -262,6 +262,10 @@ public class StatisticsPlaylistFragment
itemsListState = null; itemsListState = null;
} }
///////////////////////////////////////////////////////////////////////////
// Statistics Loader
///////////////////////////////////////////////////////////////////////////
private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() { private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() {
return new Subscriber<List<StreamStatisticsEntry>>() { return new Subscriber<List<StreamStatisticsEntry>>() {
@Override @Override
@ -294,10 +298,6 @@ public class StatisticsPlaylistFragment
}; };
} }
///////////////////////////////////////////////////////////////////////////
// Statistics Loader
///////////////////////////////////////////////////////////////////////////
@Override @Override
public void handleResult(@NonNull final List<StreamStatisticsEntry> result) { public void handleResult(@NonNull final List<StreamStatisticsEntry> result) {
super.handleResult(result); super.handleResult(result);
@ -331,6 +331,10 @@ public class StatisticsPlaylistFragment
hideLoading(); hideLoading();
} }
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected void resetFragment() { protected void resetFragment() {
super.resetFragment(); super.resetFragment();
@ -338,9 +342,6 @@ public class StatisticsPlaylistFragment
databaseSubscription.cancel(); databaseSubscription.cancel();
} }
} }
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override @Override
protected boolean onError(final Throwable exception) { protected boolean onError(final Throwable exception) {
@ -353,6 +354,10 @@ public class StatisticsPlaylistFragment
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void toggleSortMode() { private void toggleSortMode() {
if (sortMode == StatisticSortMode.LAST_PLAYED) { if (sortMode == StatisticSortMode.LAST_PLAYED) {
sortMode = StatisticSortMode.MOST_PLAYED; sortMode = StatisticSortMode.MOST_PLAYED;
@ -370,10 +375,6 @@ public class StatisticsPlaylistFragment
startLoading(true); startLoading(true);
} }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private PlayQueue getPlayQueueStartingAt(final StreamStatisticsEntry infoItem) { private PlayQueue getPlayQueueStartingAt(final StreamStatisticsEntry infoItem) {
return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0)); return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0));
} }

View File

@ -56,12 +56,14 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
// Save the list 10 seconds after the last change occurred // Save the list 10 seconds after the last change occurred
private static final long SAVE_DEBOUNCE_MILLIS = 10000; private static final long SAVE_DEBOUNCE_MILLIS = 10000;
private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12;
@State @State
protected Long playlistId; protected Long playlistId;
@State @State
protected String name; protected String name;
@State @State
protected Parcelable itemsListState; Parcelable itemsListState;
private View headerRootLayout; private View headerRootLayout;
private TextView headerTitleView; private TextView headerTitleView;
private TextView headerStreamCount; private TextView headerStreamCount;

View File

@ -47,18 +47,20 @@ public class SubscriptionsImportFragment extends BaseFragment {
private static final int REQUEST_IMPORT_FILE_CODE = 666; private static final int REQUEST_IMPORT_FILE_CODE = 666;
@State @State
protected int currentServiceId = Constants.NO_SERVICE_ID; int currentServiceId = Constants.NO_SERVICE_ID;
private List<SubscriptionExtractor.ContentSource> supportedSources; private List<SubscriptionExtractor.ContentSource> supportedSources;
private String relatedUrl; private String relatedUrl;
@StringRes @StringRes
private int instructionsString; private int instructionsString;
private TextView infoTextView;
private EditText inputText;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private TextView infoTextView;
private EditText inputText;
private Button inputButton; private Button inputButton;
public static SubscriptionsImportFragment getInstance(final int serviceId) { public static SubscriptionsImportFragment getInstance(final int serviceId) {
@ -67,7 +69,7 @@ public class SubscriptionsImportFragment extends BaseFragment {
return instance; return instance;
} }
public void setInitialData(final int serviceId) { private void setInitialData(final int serviceId) {
this.currentServiceId = serviceId; this.currentServiceId = serviceId;
} }

View File

@ -53,9 +53,16 @@ import io.reactivex.processors.PublishProcessor;
public abstract class BaseImportExportService extends Service { public abstract class BaseImportExportService extends Service {
protected final String TAG = this.getClass().getSimpleName(); protected final String TAG = this.getClass().getSimpleName();
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
protected final CompositeDisposable disposables = new CompositeDisposable(); protected final CompositeDisposable disposables = new CompositeDisposable();
protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create(); 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 currentProgress = new AtomicInteger(-1);
protected final AtomicInteger maxProgress = new AtomicInteger(-1); protected final AtomicInteger maxProgress = new AtomicInteger(-1);
protected final ImportExportEventListener eventListener = new ImportExportEventListener() { protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
@ -71,13 +78,7 @@ public abstract class BaseImportExportService extends Service {
notificationUpdater.onNext(itemName); notificationUpdater.onNext(itemName);
} }
}; };
protected NotificationManagerCompat notificationManager;
protected NotificationCompat.Builder notificationBuilder;
protected SubscriptionManager subscriptionManager;
/*//////////////////////////////////////////////////////////////////////////
// Notification Impl
//////////////////////////////////////////////////////////////////////////*/
protected Toast toast; protected Toast toast;
@Nullable @Nullable
@ -103,6 +104,10 @@ public abstract class BaseImportExportService extends Service {
disposables.clear(); disposables.clear();
} }
/*//////////////////////////////////////////////////////////////////////////
// Notification Impl
//////////////////////////////////////////////////////////////////////////*/
protected abstract int getNotificationId(); protected abstract int getNotificationId();
@StringRes @StringRes

View File

@ -67,15 +67,18 @@ public class SubscriptionsImportService extends BaseImportExportService {
*/ */
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription" public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription"
+ ".services.SubscriptionsImportService.IMPORT_COMPLETE"; + ".services.SubscriptionsImportService.IMPORT_COMPLETE";
/** /**
* How many extractions running in parallel. * How many extractions running in parallel.
*/ */
public static final int PARALLEL_EXTRACTIONS = 8; public static final int PARALLEL_EXTRACTIONS = 8;
/** /**
* Number of items to buffer to mass-insert in the subscriptions table, * 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. * this leads to a better performance as we can then use db transactions.
*/ */
public static final int BUFFER_COUNT_BEFORE_INSERT = 50; public static final int BUFFER_COUNT_BEFORE_INSERT = 50;
private Subscription subscription; private Subscription subscription;
private int currentMode; private int currentMode;
private int currentServiceId; private int currentServiceId;
@ -131,10 +134,6 @@ public class SubscriptionsImportService extends BaseImportExportService {
return 4568; return 4568;
} }
/*//////////////////////////////////////////////////////////////////////////
// Imports
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public int getTitle() { public int getTitle() {
return R.string.import_ongoing; return R.string.import_ongoing;
@ -148,6 +147,10 @@ public class SubscriptionsImportService extends BaseImportExportService {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Imports
//////////////////////////////////////////////////////////////////////////*/
private void startImport() { private void startImport() {
showToast(R.string.import_ongoing); showToast(R.string.import_ongoing);

View File

@ -99,10 +99,22 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
@SuppressWarnings({"WeakerAccess"}) @SuppressWarnings({"WeakerAccess"})
public abstract class BasePlayer implements public abstract class BasePlayer implements
Player.EventListener, PlaybackListener, ImageLoadingListener { Player.EventListener, PlaybackListener, ImageLoadingListener {
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@NonNull @NonNull
public static final String TAG = "BasePlayer"; 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 @NonNull
public static final String REPEAT_MODE = "repeat_mode"; public static final String REPEAT_MODE = "repeat_mode";
@NonNull @NonNull
@ -123,26 +135,43 @@ public abstract class BasePlayer implements
public static final String START_PAUSED = "start_paused"; public static final String START_PAUSED = "start_paused";
@NonNull @NonNull
public static final String SELECT_ON_APPEND = "select_on_append"; public static final String SELECT_ON_APPEND = "select_on_append";
/*//////////////////////////////////////////////////////////////////////////
// Intent
//////////////////////////////////////////////////////////////////////////*/
@NonNull @NonNull
public static final String IS_MUTED = "is_muted"; 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 // 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 @NonNull
protected final Context context; protected final Context context;
@NonNull @NonNull
@ -158,39 +187,17 @@ public abstract class BasePlayer implements
@NonNull @NonNull
private final LoadControl loadControl; private final LoadControl loadControl;
/*//////////////////////////////////////////////////////////////////////////
// Player
//////////////////////////////////////////////////////////////////////////*/
@NonNull @NonNull
private final RenderersFactory renderFactory; private final RenderersFactory renderFactory;
@NonNull @NonNull
private final SerialDisposable progressUpdateReactor; private final SerialDisposable progressUpdateReactor;
@NonNull @NonNull
private final CompositeDisposable databaseUpdateReactor; 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 boolean isPrepared = false;
private Disposable stateLoader; private Disposable stateLoader;
/*////////////////////////////////////////////////////////////////////////// protected int currentState = STATE_PREFLIGHT;
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
public BasePlayer(@NonNull final Context context) { public BasePlayer(@NonNull final Context context) {
this.context = context; this.context = context;
@ -247,8 +254,7 @@ public abstract class BasePlayer implements
registerBroadcastReceiver(); registerBroadcastReceiver();
} }
public void initListeners() { public void initListeners() { }
}
public void handleIntent(final Intent intent) { public void handleIntent(final Intent intent) {
if (DEBUG) { if (DEBUG) {
@ -324,10 +330,6 @@ public abstract class BasePlayer implements
/*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted); /*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted);
} }
/*//////////////////////////////////////////////////////////////////////////
// Broadcast Receiver
//////////////////////////////////////////////////////////////////////////*/
protected void initPlayback(@NonNull final PlayQueue queue, protected void initPlayback(@NonNull final PlayQueue queue,
@Player.RepeatMode final int repeatMode, @Player.RepeatMode final int repeatMode,
final float playbackSpeed, final float playbackSpeed,
@ -398,9 +400,12 @@ public abstract class BasePlayer implements
databaseUpdateReactor.clear(); databaseUpdateReactor.clear();
progressUpdateReactor.set(null); progressUpdateReactor.set(null);
} }
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
private void initThumbnail(final String url) { private void initThumbnail(final String url) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Thumbnail - initThumbnail() called"); Log.d(TAG, "Thumbnail - initThumbnail() called");
@ -413,10 +418,6 @@ public abstract class BasePlayer implements
.loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this); .loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this);
} }
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onLoadingStarted(final String imageUri, final View view) { public void onLoadingStarted(final String imageUri, final View view) {
if (DEBUG) { if (DEBUG) {
@ -453,6 +454,10 @@ public abstract class BasePlayer implements
currentThumbnail = null; currentThumbnail = null;
} }
/*//////////////////////////////////////////////////////////////////////////
// Broadcast Receiver
//////////////////////////////////////////////////////////////////////////*/
/** /**
* Add your action in the intentFilter. * Add your action in the intentFilter.
* *
@ -488,6 +493,10 @@ public abstract class BasePlayer implements
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
public void changeState(final int state) { public void changeState(final int state) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "changeState() called with: state = [" + state + "]"); Log.d(TAG, "changeState() called with: state = [" + state + "]");
@ -1328,6 +1337,7 @@ public abstract class BasePlayer implements
playQueue.append(autoQueue.getStreams()); playQueue.append(autoQueue.getStreams());
} }
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Getters and Setters // Getters and Setters
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View File

@ -1181,11 +1181,14 @@ public final class MainVideoPlayer extends AppCompatActivity
private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener
implements View.OnTouchListener { implements View.OnTouchListener {
private static final int MOVEMENT_THRESHOLD = 40; private static final int MOVEMENT_THRESHOLD = 40;
private final boolean isVolumeGestureEnabled = PlayerHelper private final boolean isVolumeGestureEnabled = PlayerHelper
.isVolumeGestureEnabled(getApplicationContext()); .isVolumeGestureEnabled(getApplicationContext());
private final boolean isBrightnessGestureEnabled = PlayerHelper private final boolean isBrightnessGestureEnabled = PlayerHelper
.isBrightnessGestureEnabled(getApplicationContext()); .isBrightnessGestureEnabled(getApplicationContext());
private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume(); private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private boolean isMoving; private boolean isMoving;
@Override @Override

View File

@ -54,14 +54,19 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
View.OnClickListener, PlaybackParameterDialog.Callback { View.OnClickListener, PlaybackParameterDialog.Callback {
private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47; private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47;
private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80; private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80;
protected BasePlayer player; protected BasePlayer player;
private boolean serviceBound; private boolean serviceBound;
private ServiceConnection serviceConnection; private ServiceConnection serviceConnection;
private boolean seeking;
private boolean redraw;
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
private boolean seeking;
private boolean redraw;
private View rootView; private View rootView;
private RecyclerView itemsList; private RecyclerView itemsList;

View File

@ -90,51 +90,69 @@ public abstract class VideoPlayer extends BasePlayer
Player.EventListener, Player.EventListener,
PopupMenu.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener,
PopupMenu.OnDismissListener { PopupMenu.OnDismissListener {
public final String TAG;
public static final boolean DEBUG = BasePlayer.DEBUG; public static final boolean DEBUG = BasePlayer.DEBUG;
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Player // Player
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
protected static final int RENDERER_UNAVAILABLE = -1; protected static final int RENDERER_UNAVAILABLE = -1;
public final String TAG;
@NonNull @NonNull
private final VideoPlaybackResolver resolver; private final VideoPlaybackResolver resolver;
private final Handler controlsVisibilityHandler = new Handler();
private final int qualityPopupMenuGroupId = 69; private List<VideoStream> availableStreams;
private final int playbackSpeedPopupMenuGroupId = 79; private int selectedStreamIndex;
protected boolean wasPlaying = false;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private final int captionPopupMenuGroupId = 89;
protected boolean wasPlaying = false;
boolean isSomePopupMenuVisible = false;
private List<VideoStream> availableStreams;
private int selectedStreamIndex;
private View rootView; private View rootView;
private AspectRatioFrameLayout aspectRatioFrameLayout; private AspectRatioFrameLayout aspectRatioFrameLayout;
private SurfaceView surfaceView; private SurfaceView surfaceView;
private View surfaceForeground; private View surfaceForeground;
private View loadingPanel; private View loadingPanel;
private ImageView endScreen; private ImageView endScreen;
private ImageView controlAnimationView; private ImageView controlAnimationView;
private View controlsRoot; private View controlsRoot;
private TextView currentDisplaySeek; private TextView currentDisplaySeek;
private View bottomControlsRoot; private View bottomControlsRoot;
private SeekBar playbackSeekBar; private SeekBar playbackSeekBar;
private TextView playbackCurrentTime; private TextView playbackCurrentTime;
private TextView playbackEndTime; private TextView playbackEndTime;
private TextView playbackLiveSync; private TextView playbackLiveSync;
private TextView playbackSpeedTextView; private TextView playbackSpeedTextView;
private View topControlsRoot; private View topControlsRoot;
private TextView qualityTextView; private TextView qualityTextView;
private SubtitleView subtitleView; private SubtitleView subtitleView;
private TextView resizeView; private TextView resizeView;
private TextView captionTextView; private TextView captionTextView;
private ValueAnimator controlViewAnimator; private ValueAnimator controlViewAnimator;
private final Handler controlsVisibilityHandler = new Handler();
boolean isSomePopupMenuVisible = false;
private final int qualityPopupMenuGroupId = 69;
private PopupMenu qualityPopupMenu; private PopupMenu qualityPopupMenu;
private final int playbackSpeedPopupMenuGroupId = 79;
private PopupMenu playbackSpeedPopupMenu; private PopupMenu playbackSpeedPopupMenu;
private final int captionPopupMenuGroupId = 89;
private PopupMenu captionPopupMenu; private PopupMenu captionPopupMenu;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -238,10 +256,6 @@ public abstract class VideoPlayer extends BasePlayer
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// UI Builders
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void handleIntent(final Intent intent) { public void handleIntent(final Intent intent) {
if (intent == null) { if (intent == null) {
@ -255,6 +269,10 @@ public abstract class VideoPlayer extends BasePlayer
super.handleIntent(intent); super.handleIntent(intent);
} }
/*//////////////////////////////////////////////////////////////////////////
// UI Builders
//////////////////////////////////////////////////////////////////////////*/
public void buildQualityMenu() { public void buildQualityMenu() {
if (qualityPopupMenu == null) { if (qualityPopupMenu == null) {
return; return;
@ -354,9 +372,6 @@ public abstract class VideoPlayer extends BasePlayer
} }
captionPopupMenu.setOnDismissListener(this); captionPopupMenu.setOnDismissListener(this);
} }
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
private void updateStreamRelatedViews() { private void updateStreamRelatedViews() {
if (getCurrentMetadata() == null) { if (getCurrentMetadata() == null) {
@ -413,6 +428,10 @@ public abstract class VideoPlayer extends BasePlayer
playbackSpeedTextView.setVisibility(View.VISIBLE); playbackSpeedTextView.setVisibility(View.VISIBLE);
} }
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver(); protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver();
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
@ -420,16 +439,16 @@ public abstract class VideoPlayer extends BasePlayer
updateStreamRelatedViews(); updateStreamRelatedViews();
} }
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
@Nullable @Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) { public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
return resolver.resolve(info); return resolver.resolve(info);
} }
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onBlocked() { public void onBlocked() {
super.onBlocked(); super.onBlocked();
@ -494,10 +513,6 @@ public abstract class VideoPlayer extends BasePlayer
showAndAnimateControl(-1, true); showAndAnimateControl(-1, true);
} }
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCompleted() { public void onCompleted() {
super.onCompleted(); super.onCompleted();
@ -510,6 +525,10 @@ public abstract class VideoPlayer extends BasePlayer
animateView(surfaceForeground, true, 100); animateView(surfaceForeground, true, 100);
} }
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onTracksChanged(final TrackGroupArray trackGroups, public void onTracksChanged(final TrackGroupArray trackGroups,
final TrackSelectionArray trackSelections) { final TrackSelectionArray trackSelections) {
@ -537,15 +556,15 @@ public abstract class VideoPlayer extends BasePlayer
aspectRatioFrameLayout.setAspectRatio(((float) width) / height); aspectRatioFrameLayout.setAspectRatio(((float) width) / height);
} }
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Track Updates
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onRenderedFirstFrame() { public void onRenderedFirstFrame() {
animateView(surfaceForeground, false, 100); animateView(surfaceForeground, false, 100);
} }
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Track Updates
//////////////////////////////////////////////////////////////////////////*/
private void onTextTrackUpdate() { private void onTextTrackUpdate() {
final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT); final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT);

View File

@ -20,17 +20,20 @@ import java.io.File;
/* package-private */ class CacheFactory implements DataSource.Factory { /* package-private */ class CacheFactory implements DataSource.Factory {
private static final String TAG = "CacheFactory"; private static final String TAG = "CacheFactory";
private static final String CACHE_FOLDER_NAME = "exoplayer"; private static final String CACHE_FOLDER_NAME = "exoplayer";
private static final int CACHE_FLAGS = CacheDataSource.FLAG_BLOCK_ON_CACHE private static final int CACHE_FLAGS = CacheDataSource.FLAG_BLOCK_ON_CACHE
| CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR; | 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 // Creating cache on every instance may cause problems with multiple players when
// sources are not ExtractorMediaSource // sources are not ExtractorMediaSource
// see: https://stackoverflow.com/questions/28700391/using-cache-in-exoplayer // see: https://stackoverflow.com/questions/28700391/using-cache-in-exoplayer
// todo: make this a singleton? // todo: make this a singleton?
private static SimpleCache cache; private static SimpleCache cache;
private final DefaultDataSourceFactory dataSourceFactory;
private final File cacheDir;
private final long maxFileSize;
CacheFactory(@NonNull final Context context, CacheFactory(@NonNull final Context context,
@NonNull final String userAgent, @NonNull final String userAgent,

View File

@ -23,19 +23,23 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class PlaybackParameterDialog extends DialogFragment { public class PlaybackParameterDialog extends DialogFragment {
// Minimum allowable range in ExoPlayer // Minimum allowable range in ExoPlayer
public static final double MINIMUM_PLAYBACK_VALUE = 0.10f; private static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
public static final double MAXIMUM_PLAYBACK_VALUE = 3.00f; private static final double MAXIMUM_PLAYBACK_VALUE = 3.00f;
public static final char STEP_UP_SIGN = '+';
public static final char STEP_DOWN_SIGN = '-'; private static final char STEP_UP_SIGN = '+';
public static final double STEP_ONE_PERCENT_VALUE = 0.01f; private static final char STEP_DOWN_SIGN = '-';
public static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
public static final double STEP_TEN_PERCENT_VALUE = 0.10f; private static final double STEP_ONE_PERCENT_VALUE = 0.01f;
public static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f; private static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
public static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f; private static final double STEP_TEN_PERCENT_VALUE = 0.10f;
public static final double DEFAULT_TEMPO = 1.00f; private static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f;
public static final double DEFAULT_PITCH = 1.00f; private static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 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 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 @NonNull
private static final String TAG = "PlaybackParameterDialog"; private static final String TAG = "PlaybackParameterDialog";
@NonNull @NonNull
@ -49,18 +53,22 @@ public class PlaybackParameterDialog extends DialogFragment {
private static final String PITCH_KEY = "pitch_key"; private static final String PITCH_KEY = "pitch_key";
@NonNull @NonNull
private static final String STEP_SIZE_KEY = "step_size_key"; private static final String STEP_SIZE_KEY = "step_size_key";
@NonNull @NonNull
private final SliderStrategy strategy = new SliderStrategy.Quadratic( private final SliderStrategy strategy = new SliderStrategy.Quadratic(
MINIMUM_PLAYBACK_VALUE, MAXIMUM_PLAYBACK_VALUE, MINIMUM_PLAYBACK_VALUE, MAXIMUM_PLAYBACK_VALUE,
/*centerAt=*/1.00f, /*sliderGranularity=*/10000); /*centerAt=*/1.00f, /*sliderGranularity=*/10000);
@Nullable @Nullable
private Callback callback; private Callback callback;
private double initialTempo = DEFAULT_TEMPO; private double initialTempo = DEFAULT_TEMPO;
private double initialPitch = DEFAULT_PITCH; private double initialPitch = DEFAULT_PITCH;
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE; private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
private double tempo = DEFAULT_TEMPO; private double tempo = DEFAULT_TEMPO;
private double pitch = DEFAULT_PITCH; private double pitch = DEFAULT_PITCH;
private double stepSize = DEFAULT_STEP; private double stepSize = DEFAULT_STEP;
@Nullable @Nullable
private SeekBar tempoSlider; private SeekBar tempoSlider;
@Nullable @Nullable
@ -96,25 +104,10 @@ public class PlaybackParameterDialog extends DialogFragment {
return dialog; return dialog;
} }
@NonNull
private static String getStepUpPercentString(final double percent) {
return STEP_UP_SIGN + getPercentString(percent);
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Lifecycle // 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 @Override
public void onAttach(final Context context) { public void onAttach(final Context context) {
super.onAttach(context); super.onAttach(context);
@ -125,10 +118,6 @@ public class PlaybackParameterDialog extends DialogFragment {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Dialog
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
assureCorrectAppLanguage(getContext()); assureCorrectAppLanguage(getContext());
@ -143,10 +132,6 @@ public class PlaybackParameterDialog extends DialogFragment {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Control Views
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onSaveInstanceState(final Bundle outState) { public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
@ -158,6 +143,10 @@ public class PlaybackParameterDialog extends DialogFragment {
outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize()); outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize());
} }
/*//////////////////////////////////////////////////////////////////////////
// Dialog
//////////////////////////////////////////////////////////////////////////*/
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
@ -179,6 +168,10 @@ public class PlaybackParameterDialog extends DialogFragment {
return dialogBuilder.create(); return dialogBuilder.create();
} }
/*//////////////////////////////////////////////////////////////////////////
// Control Views
//////////////////////////////////////////////////////////////////////////*/
private void setupControlViews(@NonNull final View rootView) { private void setupControlViews(@NonNull final View rootView) {
setupHookingControl(rootView); setupHookingControl(rootView);
setupSkipSilenceControl(rootView); setupSkipSilenceControl(rootView);
@ -273,10 +266,6 @@ public class PlaybackParameterDialog extends DialogFragment {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Sliders
//////////////////////////////////////////////////////////////////////////*/
private void setupStepSizeSelector(@NonNull final View rootView) { private void setupStepSizeSelector(@NonNull final View rootView) {
TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent); TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent); TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
@ -355,6 +344,10 @@ public class PlaybackParameterDialog extends DialogFragment {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Sliders
//////////////////////////////////////////////////////////////////////////*/
private SeekBar.OnSeekBarChangeListener getOnTempoChangedListener() { private SeekBar.OnSeekBarChangeListener getOnTempoChangedListener() {
return new SeekBar.OnSeekBarChangeListener() { return new SeekBar.OnSeekBarChangeListener() {
@Override @Override
@ -430,10 +423,6 @@ public class PlaybackParameterDialog extends DialogFragment {
setPitchSlider(newValue); setPitchSlider(newValue);
} }
/*//////////////////////////////////////////////////////////////////////////
// Helper
//////////////////////////////////////////////////////////////////////////*/
private void setTempoSlider(final double newTempo) { private void setTempoSlider(final double newTempo) {
if (tempoSlider == null) { if (tempoSlider == null) {
return; return;
@ -448,6 +437,10 @@ public class PlaybackParameterDialog extends DialogFragment {
pitchSlider.setProgress(strategy.progressOf(newPitch)); pitchSlider.setProgress(strategy.progressOf(newPitch));
} }
/*//////////////////////////////////////////////////////////////////////////
// Helper
//////////////////////////////////////////////////////////////////////////*/
private void setCurrentPlaybackParameters() { private void setCurrentPlaybackParameters() {
setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence()); setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence());
} }
@ -483,6 +476,21 @@ public class PlaybackParameterDialog extends DialogFragment {
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked(); 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 { public interface Callback {
void onPlaybackParameterChanged(float playbackTempo, float playbackPitch, void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
boolean playbackSkipSilence); boolean playbackSkipSilence);

View File

@ -58,6 +58,10 @@ public final class PlayerHelper {
private PlayerHelper() { } private PlayerHelper() { }
////////////////////////////////////////////////////////////////////////////
// Exposed helpers
////////////////////////////////////////////////////////////////////////////
public static String getTimeString(final int milliSeconds) { public static String getTimeString(final int milliSeconds) {
int seconds = (milliSeconds % 60000) / 1000; int seconds = (milliSeconds % 60000) / 1000;
int minutes = (milliSeconds % 3600000) / 60000; 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("%d:%02d:%02d", hours, minutes, seconds).toString()
: STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString(); : STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString();
} }
////////////////////////////////////////////////////////////////////////////
// Exposed helpers
////////////////////////////////////////////////////////////////////////////
public static String formatSpeed(final double speed) { public static String formatSpeed(final double speed) {
return SPEED_FORMATTER.format(speed); return SPEED_FORMATTER.format(speed);
@ -177,14 +178,14 @@ public final class PlayerHelper {
? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0));
} }
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
return isResumeAfterAudioFocusGain(context, false);
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Settings Resolution // Settings Resolution
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
return isResumeAfterAudioFocusGain(context, false);
}
public static boolean isVolumeGestureEnabled(@NonNull final Context context) { public static boolean isVolumeGestureEnabled(@NonNull final Context context) {
return isVolumeGestureEnabled(context, true); return isVolumeGestureEnabled(context, true);
} }
@ -322,15 +323,15 @@ public final class PlayerHelper {
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis()); setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
} }
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
@NonNull @NonNull
private static SharedPreferences getPreferences(@NonNull final Context context) { private static SharedPreferences getPreferences(@NonNull final Context context) {
return PreferenceManager.getDefaultSharedPreferences(context); return PreferenceManager.getDefaultSharedPreferences(context);
} }
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
private static boolean isResumeAfterAudioFocusGain(@NonNull final Context context, private static boolean isResumeAfterAudioFocusGain(@NonNull final Context context,
final boolean b) { final boolean b) {
return getPreferences(context) return getPreferences(context)

View File

@ -44,16 +44,21 @@ import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfo
import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG; import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG;
public class MediaSourceManager { public class MediaSourceManager {
@NonNull
private final String TAG = "MediaSourceManager@" + hashCode();
/** /**
* Determines how many streams before and after the current stream should be loaded. * Determines how many streams before and after the current stream should be loaded.
* The default value (1) ensures seamless playback under typical network settings. * 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 * The streams after the current will be loaded into the playlist timeline while the
* streams before will only be cached for future usage. * streams before will only be cached for future usage.
* </p>
* *
* @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource) * @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource)
*/ */
private static final int WINDOW_SIZE = 1; private static final int WINDOW_SIZE = 1;
/** /**
* Determines the maximum number of disposables allowed in the {@link #loaderReactor}. * Determines the maximum number of disposables allowed in the {@link #loaderReactor}.
* Once exceeded, new calls to {@link #loadImmediate()} will evict all disposables in the * Once exceeded, new calls to {@link #loadImmediate()} will evict all disposables in the
@ -63,12 +68,12 @@ public class MediaSourceManager {
* @see #maybeLoadItem(PlayQueueItem) * @see #maybeLoadItem(PlayQueueItem)
*/ */
private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1; private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
@NonNull
private final String TAG = "MediaSourceManager@" + hashCode();
@NonNull @NonNull
private final PlaybackListener playbackListener; private final PlaybackListener playbackListener;
@NonNull @NonNull
private final PlayQueue playQueue; private final PlayQueue playQueue;
/** /**
* Determines the gap time between the playback position and the playback duration which * Determines the gap time between the playback position and the playback duration which
* the {@link #getEdgeIntervalSignal()} begins to request loading. * the {@link #getEdgeIntervalSignal()} begins to request loading.
@ -76,35 +81,45 @@ public class MediaSourceManager {
* @see #progressUpdateIntervalMillis * @see #progressUpdateIntervalMillis
*/ */
private final long playbackNearEndGapMillis; private final long playbackNearEndGapMillis;
/** /**
* Determines the interval which the {@link #getEdgeIntervalSignal()} waits for between * Determines the interval which the {@link #getEdgeIntervalSignal()} waits for between
* each request for loading, once {@link #playbackNearEndGapMillis} has reached. * each request for loading, once {@link #playbackNearEndGapMillis} has reached.
*/ */
private final long progressUpdateIntervalMillis; private final long progressUpdateIntervalMillis;
@NonNull @NonNull
private final Observable<Long> nearEndIntervalSignal; private final Observable<Long> nearEndIntervalSignal;
/** /**
* Process only the last load order when receiving a stream of load orders (lessens I/O). * 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. * The higher it is, the less loading occurs during rapid noncritical timeline changes.
* <br><br> * </p>
* <p>
* Not recommended to go below 100ms. * Not recommended to go below 100ms.
* </p>
* *
* @see #loadDebounced() * @see #loadDebounced()
*/ */
private final long loadDebounceMillis; private final long loadDebounceMillis;
@NonNull @NonNull
private final Disposable debouncedLoader; private final Disposable debouncedLoader;
@NonNull @NonNull
private final PublishSubject<Long> debouncedSignal; private final PublishSubject<Long> debouncedSignal;
@NonNull
private Subscription playQueueReactor;
@NonNull @NonNull
private final CompositeDisposable loaderReactor; private final CompositeDisposable loaderReactor;
@NonNull @NonNull
private final Set<PlayQueueItem> loadingItems; private final Set<PlayQueueItem> loadingItems;
@NonNull @NonNull
private final AtomicBoolean isBlocked; private final AtomicBoolean isBlocked;
@NonNull
private Subscription playQueueReactor;
@NonNull @NonNull
private ManagedMediaSourcePlaylist playlist; private ManagedMediaSourcePlaylist playlist;
@ -160,42 +175,6 @@ public class MediaSourceManager {
// Exposed Methods // 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. * Dispose the manager and releases all message buses and loaders.
*/ */
@ -211,6 +190,10 @@ public class MediaSourceManager {
loaderReactor.dispose(); loaderReactor.dispose();
} }
/*//////////////////////////////////////////////////////////////////////////
// Event Reactor
//////////////////////////////////////////////////////////////////////////*/
private Subscriber<PlayQueueEvent> getReactor() { private Subscriber<PlayQueueEvent> getReactor() {
return new Subscriber<PlayQueueEvent>() { return new Subscriber<PlayQueueEvent>() {
@Override @Override
@ -233,10 +216,6 @@ public class MediaSourceManager {
}; };
} }
/*//////////////////////////////////////////////////////////////////////////
// Playback Locking
//////////////////////////////////////////////////////////////////////////*/
private void onPlayQueueChanged(final PlayQueueEvent event) { private void onPlayQueueChanged(final PlayQueueEvent event) {
if (playQueue.isEmpty() && playQueue.isComplete()) { if (playQueue.isEmpty() && playQueue.isComplete()) {
playbackListener.onPlaybackShutdown(); playbackListener.onPlaybackShutdown();
@ -298,6 +277,10 @@ public class MediaSourceManager {
playQueueReactor.request(1); playQueueReactor.request(1);
} }
/*//////////////////////////////////////////////////////////////////////////
// Playback Locking
//////////////////////////////////////////////////////////////////////////*/
private boolean isPlayQueueReady() { private boolean isPlayQueueReady() {
final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE; final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE;
return playQueue.isComplete() || isWindowLoaded; return playQueue.isComplete() || isWindowLoaded;
@ -332,10 +315,6 @@ public class MediaSourceManager {
isBlocked.set(true); isBlocked.set(true);
} }
/*//////////////////////////////////////////////////////////////////////////
// Metadata Synchronization
//////////////////////////////////////////////////////////////////////////*/
private void maybeUnblock() { private void maybeUnblock() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "maybeUnblock() called."); Log.d(TAG, "maybeUnblock() called.");
@ -347,6 +326,10 @@ public class MediaSourceManager {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Metadata Synchronization
//////////////////////////////////////////////////////////////////////////*/
private void maybeSync() { private void maybeSync() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "maybeSync() called."); Log.d(TAG, "maybeSync() called.");
@ -360,10 +343,6 @@ public class MediaSourceManager {
playbackListener.onPlaybackSynchronize(currentItem); playbackListener.onPlaybackSynchronize(currentItem);
} }
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Loading
//////////////////////////////////////////////////////////////////////////*/
private synchronized void maybeSynchronizePlayer() { private synchronized void maybeSynchronizePlayer() {
if (isPlayQueueReady() && isPlaybackReady()) { if (isPlayQueueReady() && isPlaybackReady()) {
maybeUnblock(); maybeUnblock();
@ -371,6 +350,10 @@ public class MediaSourceManager {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Loading
//////////////////////////////////////////////////////////////////////////*/
private Observable<Long> getEdgeIntervalSignal() { private Observable<Long> getEdgeIntervalSignal() {
return Observable.interval(progressUpdateIntervalMillis, TimeUnit.MILLISECONDS) return Observable.interval(progressUpdateIntervalMillis, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -523,9 +506,6 @@ public class MediaSourceManager {
} }
playlist.invalidate(currentIndex, removeMediaSourceHandler, this::loadImmediate); playlist.invalidate(currentIndex, removeMediaSourceHandler, this::loadImmediate);
} }
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Playlist Helpers
//////////////////////////////////////////////////////////////////////////*/
private void maybeClearLoaders() { private void maybeClearLoaders() {
if (DEBUG) { if (DEBUG) {
@ -538,6 +518,10 @@ public class MediaSourceManager {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Playlist Helpers
//////////////////////////////////////////////////////////////////////////*/
private void resetSources() { private void resetSources() {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "resetSources() called."); 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 { private static class ItemsToLoad {
@NonNull @NonNull
private final PlayQueueItem center; private final PlayQueueItem center;

View File

@ -16,10 +16,11 @@ import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue { abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
final int serviceId;
final String baseUrl;
boolean isInitial; boolean isInitial;
private boolean isComplete; private boolean isComplete;
final int serviceId;
final String baseUrl;
String nextUrl; String nextUrl;
private transient Disposable fetchReactor; 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()); 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(); protected abstract String getTag();
@Override @Override
@ -134,4 +125,14 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
} }
fetchReactor = null; 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;
}
} }

View File

@ -36,17 +36,22 @@ import io.reactivex.subjects.BehaviorSubject;
* <p> * <p>
* This class contains basic manipulation of a playlist while also functions as a * 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. * message bus, providing all listeners with new updates to the play queue.
* </p>
* <p> * <p>
* This class can be serialized for passing intents, but in order to start the * This class can be serialized for passing intents, but in order to start the
* message bus, it must be initialized. * message bus, it must be initialized.
* </p>
*/ */
public abstract class PlayQueue implements Serializable { public abstract class PlayQueue implements Serializable {
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
@NonNull public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private final AtomicInteger queueIndex;
private ArrayList<PlayQueueItem> backup; private ArrayList<PlayQueueItem> backup;
private ArrayList<PlayQueueItem> streams; private ArrayList<PlayQueueItem> streams;
@NonNull
private final AtomicInteger queueIndex;
private transient BehaviorSubject<PlayQueueEvent> eventBroadcast; private transient BehaviorSubject<PlayQueueEvent> eventBroadcast;
private transient Flowable<PlayQueueEvent> broadcastReceiver; private transient Flowable<PlayQueueEvent> broadcastReceiver;
private transient Subscription reportingReactor; private transient Subscription reportingReactor;

View File

@ -28,19 +28,23 @@ import io.reactivex.disposables.Disposable;
* <p> * <p>
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* InfoListAdapter.java is part of NewPipe. * InfoListAdapter.java is part of NewPipe.
* </p>
* <p> * <p>
* NewPipe is free software: you can redistribute it and/or modify * NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* </p>
* <p> * <p>
* NewPipe is distributed in the hope that it will be useful, * NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* </p>
* <p> * <p>
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
* </p>
*/ */
public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

View File

@ -32,6 +32,7 @@ public class VideoPlaybackResolver implements PlaybackResolver {
private final PlayerDataSource dataSource; private final PlayerDataSource dataSource;
@NonNull @NonNull
private final QualityResolver qualityResolver; private final QualityResolver qualityResolver;
@Nullable @Nullable
private String playbackQuality; private String playbackQuality;

View File

@ -181,25 +181,6 @@ public class ErrorActivity extends AppCompatActivity {
return out; 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 @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
assureCorrectAppLanguage(this); assureCorrectAppLanguage(this);
@ -315,6 +296,25 @@ public class ErrorActivity extends AppCompatActivity {
return text.toString(); 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() { private void goToReturnActivity() {
Class<? extends Activity> checkedReturnActivity = getReturnActivity(returnActivity); Class<? extends Activity> checkedReturnActivity = getReturnActivity(returnActivity);
if (checkedReturnActivity == null) { if (checkedReturnActivity == null) {

View File

@ -54,17 +54,20 @@ import io.reactivex.schedulers.Schedulers;
public class PeertubeInstanceListFragment extends Fragment { public class PeertubeInstanceListFragment extends Fragment {
private static final int MENU_ITEM_RESTORE_ID = 123456; private static final int MENU_ITEM_RESTORE_ID = 123456;
public InstanceListAdapter instanceListAdapter;
private List<PeertubeInstance> instanceList = new ArrayList<>(); private List<PeertubeInstance> instanceList = new ArrayList<>();
private PeertubeInstance selectedInstance; private PeertubeInstance selectedInstance;
private String savedInstanceListKey; private String savedInstanceListKey;
private InstanceListAdapter instanceListAdapter;
private ProgressBar progressBar; private ProgressBar progressBar;
private SharedPreferences sharedPreferences; private SharedPreferences sharedPreferences;
private CompositeDisposable disposables = new CompositeDisposable();
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Lifecycle // Lifecycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private CompositeDisposable disposables = new CompositeDisposable();
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -122,9 +125,6 @@ public class PeertubeInstanceListFragment extends Fragment {
super.onPause(); super.onPause();
saveChanges(); saveChanges();
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onDestroy() { public void onDestroy() {
@ -135,6 +135,10 @@ public class PeertubeInstanceListFragment extends Fragment {
disposables = null; disposables = null;
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@ -284,10 +288,6 @@ public class PeertubeInstanceListFragment extends Fragment {
instanceListAdapter.notifyDataSetChanged(); instanceListAdapter.notifyDataSetChanged();
} }
/*//////////////////////////////////////////////////////////////////////////
// List Handling
//////////////////////////////////////////////////////////////////////////*/
private ItemTouchHelper.SimpleCallback getItemTouchCallback() { private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) { ItemTouchHelper.START | ItemTouchHelper.END) {
@ -348,6 +348,10 @@ public class PeertubeInstanceListFragment extends Fragment {
}; };
} }
/*//////////////////////////////////////////////////////////////////////////
// List Handling
//////////////////////////////////////////////////////////////////////////*/
private class InstanceListAdapter private class InstanceListAdapter
extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> { extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> {
private final LayoutInflater inflater; private final LayoutInflater inflater;

View File

@ -57,18 +57,18 @@ public class SelectChannelFragment extends DialogFragment {
/** /**
* This contains the base display options for images. * 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(); = new DisplayImageOptions.Builder().cacheInMemory(true).build();
private final ImageLoader imageLoader = ImageLoader.getInstance();
OnSelectedLisener onSelectedLisener = null;
OnCancelListener onCancelListener = null;
private ProgressBar progressBar;
/*////////////////////////////////////////////////////////////////////////// private final ImageLoader imageLoader = ImageLoader.getInstance();
// Interfaces
//////////////////////////////////////////////////////////////////////////*/ private OnSelectedLisener onSelectedLisener = null;
private OnCancelListener onCancelListener = null;
private ProgressBar progressBar;
private TextView emptyView; private TextView emptyView;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private List<SubscriptionEntity> subscriptions = new Vector<>(); private List<SubscriptionEntity> subscriptions = new Vector<>();
public void setOnSelectedLisener(final OnSelectedLisener listener) { public void setOnSelectedLisener(final OnSelectedLisener listener) {
@ -79,6 +79,10 @@ public class SelectChannelFragment extends DialogFragment {
onCancelListener = listener; onCancelListener = listener;
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) { final Bundle savedInstanceState) {
@ -105,7 +109,7 @@ public class SelectChannelFragment extends DialogFragment {
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Init // Handle actions
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
@ -116,11 +120,6 @@ public class SelectChannelFragment extends DialogFragment {
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
private void clickedItem(final int position) { private void clickedItem(final int position) {
if (onSelectedLisener != null) { if (onSelectedLisener != null) {
SubscriptionEntity entry = subscriptions.get(position); SubscriptionEntity entry = subscriptions.get(position);
@ -130,6 +129,10 @@ public class SelectChannelFragment extends DialogFragment {
dismiss(); dismiss();
} }
/*//////////////////////////////////////////////////////////////////////////
// Item handling
//////////////////////////////////////////////////////////////////////////*/
private void displayChannels(final List<SubscriptionEntity> newSubscriptions) { private void displayChannels(final List<SubscriptionEntity> newSubscriptions) {
this.subscriptions = newSubscriptions; this.subscriptions = newSubscriptions;
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
@ -141,10 +144,6 @@ public class SelectChannelFragment extends DialogFragment {
} }
/*//////////////////////////////////////////////////////////////////////////
// Item handling
//////////////////////////////////////////////////////////////////////////*/
private Observer<List<SubscriptionEntity>> getSubscriptionObserver() { private Observer<List<SubscriptionEntity>> getSubscriptionObserver() {
return new Observer<List<SubscriptionEntity>>() { return new Observer<List<SubscriptionEntity>>() {
@Override @Override
@ -165,29 +164,28 @@ public class SelectChannelFragment extends DialogFragment {
}; };
} }
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) { protected void onError(final Throwable e) {
final Activity activity = getActivity(); final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
} }
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
public interface OnSelectedLisener { public interface OnSelectedLisener {
void onChannelSelected(int serviceId, String url, String name); void onChannelSelected(int serviceId, String url, String name);
} }
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
public interface OnCancelListener { public interface OnCancelListener {
void onCancel(); void onCancel();
} }
/*//////////////////////////////////////////////////////////////////////////
// ImageLoaderOptions
//////////////////////////////////////////////////////////////////////////*/
private class SelectChannelAdapter private class SelectChannelAdapter
extends RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> { extends RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> {
@Override @Override
@ -219,8 +217,8 @@ public class SelectChannelFragment extends DialogFragment {
public class SelectChannelItemHolder extends RecyclerView.ViewHolder { public class SelectChannelItemHolder extends RecyclerView.ViewHolder {
public final View view; public final View view;
public final CircleImageView thumbnailView; final CircleImageView thumbnailView;
public final TextView titleView; final TextView titleView;
SelectChannelItemHolder(final View v) { SelectChannelItemHolder(final View v) {
super(v); super(v);
this.view = v; this.view = v;

View File

@ -50,9 +50,6 @@ public class SelectKioskFragment extends DialogFragment {
private RecyclerView recyclerView = null; private RecyclerView recyclerView = null;
private SelectKioskAdapter selectKioskAdapter = null; private SelectKioskAdapter selectKioskAdapter = null;
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
private OnSelectedLisener onSelectedLisener = null; private OnSelectedLisener onSelectedLisener = null;
private OnCancelListener onCancelListener = null; private OnCancelListener onCancelListener = null;
@ -80,6 +77,10 @@ public class SelectKioskFragment extends DialogFragment {
return v; return v;
} }
/*//////////////////////////////////////////////////////////////////////////
// Handle actions
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCancel(final DialogInterface dialogInterface) { public void onCancel(final DialogInterface dialogInterface) {
super.onCancel(dialogInterface); super.onCancel(dialogInterface);
@ -96,7 +97,7 @@ public class SelectKioskFragment extends DialogFragment {
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Handle actions // Error
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) { 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)); .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
} }
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
public interface OnSelectedLisener { public interface OnSelectedLisener {
void onKioskSelected(int serviceId, String kioskId, String kioskName); void onKioskSelected(int serviceId, String kioskId, String kioskName);
} }
@ -113,10 +118,6 @@ public class SelectKioskFragment extends DialogFragment {
void onCancel(); void onCancel();
} }
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
private class SelectKioskAdapter private class SelectKioskAdapter
extends RecyclerView.Adapter<SelectKioskAdapter.SelectKioskItemHolder> { extends RecyclerView.Adapter<SelectKioskAdapter.SelectKioskItemHolder> {
private final List<Entry> kioskList = new Vector<>(); private final List<Entry> kioskList = new Vector<>();

View File

@ -45,10 +45,11 @@ import static org.schabi.newpipe.settings.tabs.Tab.typeFrom;
public class ChooseTabsFragment extends Fragment { public class ChooseTabsFragment extends Fragment {
private static final int MENU_ITEM_RESTORE_ID = 123456; private static final int MENU_ITEM_RESTORE_ID = 123456;
private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
private TabsManager tabsManager; private TabsManager tabsManager;
private List<Tab> tabList = new ArrayList<>(); private List<Tab> tabList = new ArrayList<>();
private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Lifecycle // Lifecycle
@ -93,16 +94,16 @@ public class ChooseTabsFragment extends Fragment {
updateTitle(); updateTitle();
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
saveChanges(); saveChanges();
} }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, 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<>(); final ArrayList<ChooseTabListItem> returnList = new ArrayList<>();
for (Tab.Type type : Tab.Type.values()) { for (Tab.Type type : Tab.Type.values()) {

View File

@ -39,6 +39,10 @@ public abstract class Tab {
readDataFromJson(jsonObject); readDataFromJson(jsonObject);
} }
/*//////////////////////////////////////////////////////////////////////////
// Tab Handling
//////////////////////////////////////////////////////////////////////////*/
@Nullable @Nullable
public static Tab from(@NonNull final JsonObject jsonObject) { public static Tab from(@NonNull final JsonObject jsonObject) {
final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1); final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1);
@ -85,10 +89,6 @@ public abstract class Tab {
return type.getTab(); return type.getTab();
} }
/*//////////////////////////////////////////////////////////////////////////
// JSON Handling
//////////////////////////////////////////////////////////////////////////*/
public abstract int getTabId(); public abstract int getTabId();
public abstract String getTabName(Context context); public abstract String getTabName(Context context);
@ -104,10 +104,6 @@ public abstract class Tab {
*/ */
public abstract Fragment getFragment(Context context) throws ExtractionException; public abstract Fragment getFragment(Context context) throws ExtractionException;
/*//////////////////////////////////////////////////////////////////////////
// Tab Handling
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (obj == this) { if (obj == this) {
@ -118,6 +114,10 @@ public abstract class Tab {
&& ((Tab) obj).getTabId() == this.getTabId(); && ((Tab) obj).getTabId() == this.getTabId();
} }
/*//////////////////////////////////////////////////////////////////////////
// JSON Handling
//////////////////////////////////////////////////////////////////////////*/
public void writeJsonOn(final JsonSink jsonSink) { public void writeJsonOn(final JsonSink jsonSink) {
jsonSink.object(); jsonSink.object();

View File

@ -41,10 +41,6 @@ public final class TabsManager {
sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply(); sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply();
} }
/*//////////////////////////////////////////////////////////////////////////
// Listener
//////////////////////////////////////////////////////////////////////////*/
public void resetTabs() { public void resetTabs() {
sharedPreferences.edit().remove(savedTabsKey).apply(); sharedPreferences.edit().remove(savedTabsKey).apply();
} }
@ -53,6 +49,10 @@ public final class TabsManager {
return TabsJsonHelper.getDefaultTabs(); return TabsJsonHelper.getDefaultTabs();
} }
/*//////////////////////////////////////////////////////////////////////////
// Listener
//////////////////////////////////////////////////////////////////////////*/
public void setSavedTabsListener(final SavedTabsChangeListener listener) { public void setSavedTabsListener(final SavedTabsChangeListener listener) {
if (preferenceChangeListener != null) { if (preferenceChangeListener != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener); sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
@ -83,12 +83,4 @@ public final class TabsManager {
public interface SavedTabsChangeListener { public interface SavedTabsChangeListener {
void onTabsChanged(); void onTabsChanged();
} }
} }

View File

@ -32,15 +32,17 @@ import org.schabi.newpipe.extractor.InfoItem;
import java.util.Map; import java.util.Map;
public final class InfoCache { public final class InfoCache {
private final String TAG = getClass().getSimpleName();
private static final boolean DEBUG = MainActivity.DEBUG; private static final boolean DEBUG = MainActivity.DEBUG;
private static final InfoCache INSTANCE = new InfoCache(); private static final InfoCache INSTANCE = new InfoCache();
private static final int MAX_ITEMS_ON_CACHE = 60; private static final int MAX_ITEMS_ON_CACHE = 60;
/** /**
* Trim the cache to this size. * Trim the cache to this size.
*/ */
private static final int TRIM_CACHE_TO = 30; private static final int TRIM_CACHE_TO = 30;
private static final LruCache<String, CacheData> LRU_CACHE = new LruCache<>(MAX_ITEMS_ON_CACHE); private static final LruCache<String, CacheData> LRU_CACHE = new LruCache<>(MAX_ITEMS_ON_CACHE);
private final String TAG = getClass().getSimpleName();
private InfoCache() { private InfoCache() {
// no instance // no instance

View File

@ -47,26 +47,26 @@ import static org.schabi.newpipe.MainActivity.DEBUG;
* A view that can be fully collapsed and expanded. * A view that can be fully collapsed and expanded.
*/ */
public class CollapsibleView extends LinearLayout { 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 COLLAPSED = 0;
public static final int EXPANDED = 1; 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 @State
@ViewMode @ViewMode
int currentState = COLLAPSED; int currentState = COLLAPSED;
/*//////////////////////////////////////////////////////////////////////////
// Collapse/expand logic
//////////////////////////////////////////////////////////////////////////*/
private boolean readyToChangeState; private boolean readyToChangeState;
private int targetHeight = -1; private int targetHeight = -1;
private ValueAnimator currentAnimator; private ValueAnimator currentAnimator;
private final List<StateListener> listeners = new ArrayList<>();
public CollapsibleView(final Context context) { public CollapsibleView(final Context context) {
super(context); super(context);
} }
public CollapsibleView(final Context context, @Nullable final AttributeSet attrs) { public CollapsibleView(final Context context, @Nullable final AttributeSet attrs) {
super(context, attrs); super(context, attrs);
} }
@ -75,12 +75,17 @@ public class CollapsibleView extends LinearLayout {
final int defStyleAttr) { final int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
} }
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CollapsibleView(final Context context, final AttributeSet attrs, final int defStyleAttr, public CollapsibleView(final Context context, final AttributeSet attrs, final int defStyleAttr,
final int defStyleRes) { final int defStyleRes) {
super(context, attrs, defStyleAttr, 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 * 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). * some child changes (e.g. add new views, change text).
@ -198,6 +203,10 @@ public class CollapsibleView extends LinearLayout {
listeners.remove(listener); listeners.remove(listener);
} }
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Nullable @Nullable
@Override @Override
public Parcelable onSaveInstanceState() { public Parcelable onSaveInstanceState() {
@ -212,7 +221,7 @@ public class CollapsibleView extends LinearLayout {
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// State Saving // Internal
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public String getDebugLogString(final String description) { public String getDebugLogString(final String description) {
@ -226,12 +235,7 @@ public class CollapsibleView extends LinearLayout {
@Retention(SOURCE) @Retention(SOURCE)
@IntDef({COLLAPSED, EXPANDED}) @IntDef({COLLAPSED, EXPANDED})
public @interface ViewMode { public @interface ViewMode { }
}
/*//////////////////////////////////////////////////////////////////////////
// Internal
//////////////////////////////////////////////////////////////////////////*/
/** /**
* Simple interface used for listening state changes of the {@link CollapsibleView}. * Simple interface used for listening state changes of the {@link CollapsibleView}.