Skip to content

Commit af80d96

Browse files
authored
Merge pull request #7659 from litetex/load-enough-initial-data
Load enough initial items (into BaseListFragment and descendants)
2 parents 750490c + 2acaefd commit af80d96

7 files changed

Lines changed: 189 additions & 192 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
import androidx.preference.PreferenceManager;
1818
import androidx.recyclerview.widget.GridLayoutManager;
1919
import androidx.recyclerview.widget.RecyclerView;
20-
import androidx.viewbinding.ViewBinding;
2120

2221
import org.schabi.newpipe.R;
23-
import org.schabi.newpipe.databinding.PignateFooterBinding;
2422
import org.schabi.newpipe.error.ErrorUtil;
2523
import org.schabi.newpipe.extractor.InfoItem;
2624
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
@@ -44,6 +42,7 @@
4442
import java.util.Arrays;
4543
import java.util.List;
4644
import java.util.Queue;
45+
import java.util.function.Supplier;
4746

4847
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
4948
import 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();

app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public void onResume() {
6565
super.onResume();
6666
// Check if it was loading when the fragment was stopped/paused,
6767
if (wasLoading.getAndSet(false)) {
68-
if (hasMoreItems() && infoListAdapter.getItemsList().size() > 0) {
68+
if (hasMoreItems() && !infoListAdapter.getItemsList().isEmpty()) {
6969
loadMoreItems();
7070
} else {
7171
doInitialLoadLogic();
@@ -105,6 +105,7 @@ public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception
105105
// Load and handle
106106
//////////////////////////////////////////////////////////////////////////*/
107107

108+
@Override
108109
protected void doInitialLoadLogic() {
109110
if (DEBUG) {
110111
Log.d(TAG, "doInitialLoadLogic() called");
@@ -158,6 +159,7 @@ public void startLoading(final boolean forceLoad) {
158159
*/
159160
protected abstract Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic();
160161

162+
@Override
161163
protected void loadMoreItems() {
162164
isLoading.set(true);
163165

@@ -171,9 +173,9 @@ protected void loadMoreItems() {
171173
.subscribeOn(Schedulers.io())
172174
.observeOn(AndroidSchedulers.mainThread())
173175
.doFinally(this::allowDownwardFocusScroll)
174-
.subscribe((@NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> {
176+
.subscribe(infoItemsPage -> {
175177
isLoading.set(false);
176-
handleNextItems(InfoItemsPage);
178+
handleNextItems(infoItemsPage);
177179
}, (@NonNull Throwable throwable) ->
178180
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
179181
errorUserAction, "Loading more items: " + url, serviceId)));
@@ -223,7 +225,7 @@ public void handleResult(@NonNull final I result) {
223225
setTitle(name);
224226

225227
if (infoListAdapter.getItemsList().isEmpty()) {
226-
if (result.getRelatedItems().size() > 0) {
228+
if (!result.getRelatedItems().isEmpty()) {
227229
infoListAdapter.addInfoItemList(result.getRelatedItems());
228230
showListFooter(hasMoreItems());
229231
} else {
@@ -240,7 +242,7 @@ public void handleResult(@NonNull final I result) {
240242
final List<Throwable> errors = new ArrayList<>(result.getErrors());
241243
// handling ContentNotSupportedException not to show the error but an appropriate string
242244
// so that crashes won't be sent uselessly and the user will understand what happened
243-
errors.removeIf(throwable -> throwable instanceof ContentNotSupportedException);
245+
errors.removeIf(ContentNotSupportedException.class::isInstance);
244246

245247
if (!errors.isEmpty()) {
246248
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(),

app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.schabi.newpipe.fragments.list.channel;
22

3+
import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor;
4+
import static org.schabi.newpipe.ktx.ViewUtils.animate;
5+
import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor;
6+
37
import android.content.Context;
48
import android.os.Bundle;
59
import android.text.TextUtils;
@@ -17,7 +21,6 @@
1721
import androidx.annotation.Nullable;
1822
import androidx.appcompat.app.ActionBar;
1923
import androidx.core.content.ContextCompat;
20-
import androidx.viewbinding.ViewBinding;
2124

2225
import com.jakewharton.rxbinding4.view.RxView;
2326

@@ -29,7 +32,6 @@
2932
import org.schabi.newpipe.error.ErrorInfo;
3033
import org.schabi.newpipe.error.ErrorUtil;
3134
import org.schabi.newpipe.error.UserAction;
32-
import org.schabi.newpipe.extractor.InfoItem;
3335
import org.schabi.newpipe.extractor.ListExtractor;
3436
import org.schabi.newpipe.extractor.channel.ChannelInfo;
3537
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
@@ -43,13 +45,14 @@
4345
import org.schabi.newpipe.util.ExtractorHelper;
4446
import org.schabi.newpipe.util.Localization;
4547
import org.schabi.newpipe.util.NavigationHelper;
46-
import org.schabi.newpipe.util.external_communication.ShareUtils;
4748
import org.schabi.newpipe.util.PicassoHelper;
4849
import org.schabi.newpipe.util.ThemeHelper;
50+
import org.schabi.newpipe.util.external_communication.ShareUtils;
4951

50-
import java.util.ArrayList;
5152
import java.util.List;
5253
import java.util.concurrent.TimeUnit;
54+
import java.util.function.Supplier;
55+
import java.util.stream.Collectors;
5356

5457
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
5558
import io.reactivex.rxjava3.core.Observable;
@@ -61,10 +64,6 @@
6164
import io.reactivex.rxjava3.functions.Function;
6265
import io.reactivex.rxjava3.schedulers.Schedulers;
6366

64-
import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor;
65-
import static org.schabi.newpipe.ktx.ViewUtils.animate;
66-
import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor;
67-
6867
public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
6968
implements View.OnClickListener {
7069

@@ -145,12 +144,12 @@ public void onDestroy() {
145144
//////////////////////////////////////////////////////////////////////////*/
146145

147146
@Override
148-
protected ViewBinding getListHeader() {
147+
protected Supplier<View> getListHeaderSupplier() {
149148
headerBinding = ChannelHeaderBinding
150149
.inflate(activity.getLayoutInflater(), itemsList, false);
151150
playlistControlBinding = headerBinding.playlistControl;
152151

153-
return headerBinding;
152+
return headerBinding::getRoot;
154153
}
155154

156155
@Override
@@ -183,21 +182,17 @@ public void onCreateOptionsMenu(@NonNull final Menu menu,
183182
}
184183
}
185184

186-
private void openRssFeed() {
187-
final ChannelInfo info = currentInfo;
188-
if (info != null) {
189-
ShareUtils.openUrlInBrowser(requireContext(), info.getFeedUrl(), false);
190-
}
191-
}
192-
193185
@Override
194186
public boolean onOptionsItemSelected(final MenuItem item) {
195187
switch (item.getItemId()) {
196188
case R.id.action_settings:
197189
NavigationHelper.openSettings(requireContext());
198190
break;
199191
case R.id.menu_item_rss:
200-
openRssFeed();
192+
if (currentInfo != null) {
193+
ShareUtils.openUrlInBrowser(
194+
requireContext(), currentInfo.getFeedUrl(), false);
195+
}
201196
break;
202197
case R.id.menu_item_openInBrowser:
203198
if (currentInfo != null) {
@@ -516,12 +511,11 @@ private PlayQueue getPlayQueue() {
516511
}
517512

518513
private PlayQueue getPlayQueue(final int index) {
519-
final List<StreamInfoItem> streamItems = new ArrayList<>();
520-
for (final InfoItem i : infoListAdapter.getItemsList()) {
521-
if (i instanceof StreamInfoItem) {
522-
streamItems.add((StreamInfoItem) i);
523-
}
524-
}
514+
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
515+
.filter(StreamInfoItem.class::isInstance)
516+
.map(StreamInfoItem.class::cast)
517+
.collect(Collectors.toList());
518+
525519
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
526520
currentInfo.getNextPage(), streamItems, index);
527521
}

0 commit comments

Comments
 (0)