1717import androidx .preference .PreferenceManager ;
1818import androidx .recyclerview .widget .GridLayoutManager ;
1919import androidx .recyclerview .widget .RecyclerView ;
20- import androidx .viewbinding .ViewBinding ;
2120
2221import org .schabi .newpipe .R ;
23- import org .schabi .newpipe .databinding .PignateFooterBinding ;
2422import org .schabi .newpipe .error .ErrorUtil ;
2523import org .schabi .newpipe .extractor .InfoItem ;
2624import org .schabi .newpipe .extractor .channel .ChannelInfoItem ;
4442import java .util .Arrays ;
4543import java .util .List ;
4644import java .util .Queue ;
45+ import java .util .function .Supplier ;
4746
4847import static org .schabi .newpipe .extractor .utils .Utils .isNullOrEmpty ;
4948import static org .schabi .newpipe .ktx .ViewUtils .animate ;
@@ -79,11 +78,6 @@ public void onAttach(@NonNull final Context context) {
7978 }
8079 }
8180
82- @ Override
83- public void onDetach () {
84- super .onDetach ();
85- }
86-
8781 @ Override
8882 public void onCreate (final Bundle savedInstanceState ) {
8983 super .onCreate (savedInstanceState );
@@ -220,14 +214,10 @@ public void onStart() {
220214 //////////////////////////////////////////////////////////////////////////*/
221215
222216 @ Nullable
223- protected ViewBinding getListHeader () {
217+ protected Supplier < View > getListHeaderSupplier () {
224218 return null ;
225219 }
226220
227- protected ViewBinding getListFooter () {
228- return PignateFooterBinding .inflate (activity .getLayoutInflater (), itemsList , false );
229- }
230-
231221 protected RecyclerView .LayoutManager getListLayoutManager () {
232222 return new SuperScrollLayoutManager (activity );
233223 }
@@ -252,11 +242,10 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
252242 itemsList .setLayoutManager (useGrid ? getGridLayoutManager () : getListLayoutManager ());
253243
254244 infoListAdapter .setUseGridVariant (useGrid );
255- infoListAdapter .setFooter (getListFooter ().getRoot ());
256245
257- final ViewBinding listHeader = getListHeader ();
258- if (listHeader != null ) {
259- infoListAdapter .setHeader ( listHeader . getRoot () );
246+ final Supplier < View > listHeaderSupplier = getListHeaderSupplier ();
247+ if (listHeaderSupplier != null ) {
248+ infoListAdapter .setHeaderSupplier ( listHeaderSupplier );
260249 }
261250
262251 itemsList .setAdapter (infoListAdapter );
@@ -271,7 +260,7 @@ protected void onItemSelected(final InfoItem selectedItem) {
271260 @ Override
272261 protected void initListeners () {
273262 super .initListeners ();
274- infoListAdapter .setOnStreamSelectedListener (new OnClickGesture <StreamInfoItem >() {
263+ infoListAdapter .setOnStreamSelectedListener (new OnClickGesture <>() {
275264 @ Override
276265 public void selected (final StreamInfoItem selectedItem ) {
277266 onStreamSelected (selectedItem );
@@ -315,22 +304,98 @@ public void selected(final PlaylistInfoItem selectedItem) {
315304 }
316305 });
317306
318- infoListAdapter .setOnCommentsSelectedListener (new OnClickGesture <CommentsInfoItem >() {
307+ infoListAdapter .setOnCommentsSelectedListener (new OnClickGesture <>() {
319308 @ Override
320309 public void selected (final CommentsInfoItem selectedItem ) {
321310 onItemSelected (selectedItem );
322311 }
323312 });
324313
314+ // Ensure that there is always a scroll listener (e.g. when rotating the device)
315+ useNormalItemListScrollListener ();
316+ }
317+
318+ /**
319+ * Removes all listeners and adds the normal scroll listener to the {@link #itemsList}.
320+ */
321+ protected void useNormalItemListScrollListener () {
322+ if (DEBUG ) {
323+ Log .d (TAG , "useNormalItemListScrollListener called" );
324+ }
325+ itemsList .clearOnScrollListeners ();
326+ itemsList .addOnScrollListener (new DefaultItemListOnScrolledDownListener ());
327+ }
328+
329+ /**
330+ * Removes all listeners and adds the initial scroll listener to the {@link #itemsList}.
331+ * <br/>
332+ * Which tries to load more items when not enough are in the view (not scrollable)
333+ * and more are available.
334+ * <br/>
335+ * Note: This method only works because "This callback will also be called if visible
336+ * item range changes after a layout calculation. In that case, dx and dy will be 0."
337+ * - which might be unexpected because no actual scrolling occurs...
338+ * <br/>
339+ * This listener will be replaced by DefaultItemListOnScrolledDownListener when
340+ * <ul>
341+ * <li>the view was actually scrolled</li>
342+ * <li>the view is scrollable</li>
343+ * <li>no more items can be loaded</li>
344+ * </ul>
345+ */
346+ protected void useInitialItemListLoadScrollListener () {
347+ if (DEBUG ) {
348+ Log .d (TAG , "useInitialItemListLoadScrollListener called" );
349+ }
325350 itemsList .clearOnScrollListeners ();
326- itemsList .addOnScrollListener (new OnScrollBelowItemsListener () {
351+ itemsList .addOnScrollListener (new DefaultItemListOnScrolledDownListener () {
327352 @ Override
328- public void onScrolledDown (final RecyclerView recyclerView ) {
329- onScrollToBottom ();
353+ public void onScrolled (final RecyclerView recyclerView , final int dx , final int dy ) {
354+ super .onScrolled (recyclerView , dx , dy );
355+
356+ if (dy != 0 ) {
357+ log ("Vertical scroll occurred" );
358+
359+ useNormalItemListScrollListener ();
360+ return ;
361+ }
362+ if (isLoading .get ()) {
363+ log ("Still loading data -> Skipping" );
364+ return ;
365+ }
366+ if (!hasMoreItems ()) {
367+ log ("No more items to load" );
368+
369+ useNormalItemListScrollListener ();
370+ return ;
371+ }
372+ if (itemsList .canScrollVertically (1 )
373+ || itemsList .canScrollVertically (-1 )) {
374+ log ("View is scrollable" );
375+
376+ useNormalItemListScrollListener ();
377+ return ;
378+ }
379+
380+ log ("Loading more data" );
381+ loadMoreItems ();
382+ }
383+
384+ private void log (final String msg ) {
385+ if (DEBUG ) {
386+ Log .d (TAG , "initItemListLoadScrollListener - " + msg );
387+ }
330388 }
331389 });
332390 }
333391
392+ class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener {
393+ @ Override
394+ public void onScrolledDown (final RecyclerView recyclerView ) {
395+ onScrollToBottom ();
396+ }
397+ }
398+
334399 private void onStreamSelected (final StreamInfoItem selectedItem ) {
335400 onItemSelected (selectedItem );
336401 NavigationHelper .openVideoDetailFragment (requireContext (), getFM (),
@@ -418,6 +483,12 @@ public void onCreateOptionsMenu(@NonNull final Menu menu,
418483 // Load and handle
419484 //////////////////////////////////////////////////////////////////////////*/
420485
486+ @ Override
487+ protected void startLoading (final boolean forceLoad ) {
488+ useInitialItemListLoadScrollListener ();
489+ super .startLoading (forceLoad );
490+ }
491+
421492 protected abstract void loadMoreItems ();
422493
423494 protected abstract boolean hasMoreItems ();
0 commit comments