From fb362022f732ed81fcc60725b094bb6cf80707aa Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 15 Jan 2022 17:05:40 +0100 Subject: [PATCH] Load enough initial data into BaseListFragment --- .../fragments/list/BaseListFragment.java | 63 ++++++++++++++++++- .../fragments/list/BaseListInfoFragment.java | 19 ++++-- .../fragments/list/search/SearchFragment.java | 14 ++++- 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 3c2e65bb7..d0a07ffc4 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -271,7 +271,7 @@ public abstract class BaseListFragment extends BaseStateFragment @Override protected void initListeners() { super.initListeners(); - infoListAdapter.setOnStreamSelectedListener(new OnClickGesture() { + infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() { @Override public void selected(final StreamInfoItem selectedItem) { onStreamSelected(selectedItem); @@ -418,7 +418,66 @@ public abstract class BaseListFragment extends BaseStateFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ - protected abstract void loadMoreItems(); + /** + * If more items are loadable and the itemList is not scrollable -> load more data. + *
+ * Should be called once the initial items inside {@link #startLoading(boolean)} + * has been loaded and added to the {@link #itemsList}. + *
+ * Otherwise the loading indicator is always shown but no data can be loaded + * because the view is not scrollable; see also #1974. + */ + protected void ifMoreItemsLoadableLoadUntilScrollable() { + ifMoreItemsLoadableLoadUntilScrollable(0); + } + + /** + * If more items are loadable and the itemList is not scrollable -> load more data. + * + * @param recursiveCallCount Amount of recursive calls that occurred + * @see #ifMoreItemsLoadableLoadUntilScrollable() + */ + protected void ifMoreItemsLoadableLoadUntilScrollable(final int recursiveCallCount) { + // Try to prevent malfunction / stackoverflow + if (recursiveCallCount > 100) { + Log.w(TAG, "loadEnoughInitialData - Too many recursive calls - Aborting"); + return; + } + if (!hasMoreItems()) { + if (DEBUG) { + Log.d(TAG, "loadEnoughInitialData - OK: No more items to load"); + } + return; + } + if (itemsList.canScrollVertically(1) + || itemsList.canScrollVertically(-1)) { + if (DEBUG) { + Log.d(TAG, "loadEnoughInitial - OK: itemList is scrollable"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "loadEnoughInitialData - View is not scrollable " + + "but it could load more items -> Loading more"); + } + loadMoreItems(() -> + ifMoreItemsLoadableLoadUntilScrollable(recursiveCallCount + 1)); + } + + /** + * Loads more items. + * @param initialDataLoadCallback + * Callback used in {@link #ifMoreItemsLoadableLoadUntilScrollable()}. + *
+ * Execute it once the data was loaded and added to the {@link #itemsList}. + *
+ * Might be null. + */ + protected abstract void loadMoreItems(@Nullable Runnable initialDataLoadCallback); + + protected void loadMoreItems() { + loadMoreItems(null); + } protected abstract boolean hasMoreItems(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index e98dc9fda..87f031c12 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -6,6 +6,7 @@ import android.util.Log; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -65,7 +66,7 @@ public abstract class BaseListInfoFragment super.onResume(); // Check if it was loading when the fragment was stopped/paused, if (wasLoading.getAndSet(false)) { - if (hasMoreItems() && infoListAdapter.getItemsList().size() > 0) { + if (hasMoreItems() && !infoListAdapter.getItemsList().isEmpty()) { loadMoreItems(); } else { doInitialLoadLogic(); @@ -105,6 +106,7 @@ public abstract class BaseListInfoFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ + @Override protected void doInitialLoadLogic() { if (DEBUG) { Log.d(TAG, "doInitialLoadLogic() called"); @@ -144,6 +146,7 @@ public abstract class BaseListInfoFragment currentInfo = result; currentNextPage = result.getNextPage(); handleResult(result); + ifMoreItemsLoadableLoadUntilScrollable(); }, throwable -> showError(new ErrorInfo(throwable, errorUserAction, "Start loading: " + url, serviceId))); @@ -158,7 +161,8 @@ public abstract class BaseListInfoFragment */ protected abstract Single loadMoreItemsLogic(); - protected void loadMoreItems() { + @Override + protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { isLoading.set(true); if (currentWorker != null) { @@ -171,9 +175,12 @@ public abstract class BaseListInfoFragment .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doFinally(this::allowDownwardFocusScroll) - .subscribe((@NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> { + .subscribe(infoItemsPage -> { isLoading.set(false); - handleNextItems(InfoItemsPage); + handleNextItems(infoItemsPage); + if (initialDataLoadCallback != null) { + initialDataLoadCallback.run(); + } }, (@NonNull Throwable throwable) -> dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable, errorUserAction, "Loading more items: " + url, serviceId))); @@ -223,7 +230,7 @@ public abstract class BaseListInfoFragment setTitle(name); if (infoListAdapter.getItemsList().isEmpty()) { - if (result.getRelatedItems().size() > 0) { + if (!result.getRelatedItems().isEmpty()) { infoListAdapter.addInfoItemList(result.getRelatedItems()); showListFooter(hasMoreItems()); } else { @@ -240,7 +247,7 @@ public abstract class BaseListInfoFragment final List errors = new ArrayList<>(result.getErrors()); // handling ContentNotSupportedException not to show the error but an appropriate string // so that crashes won't be sent uselessly and the user will understand what happened - errors.removeIf(throwable -> throwable instanceof ContentNotSupportedException); + errors.removeIf(ContentNotSupportedException.class::isInstance); if (!errors.isEmpty()) { dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 055c27733..35bb2c349 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -868,12 +868,15 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(this::handleResult, this::onItemError); + .subscribe(result -> { + handleResult(result); + ifMoreItemsLoadableLoadUntilScrollable(); + }, this::onItemError); } @Override - protected void loadMoreItems() { + protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { if (!Page.isValid(nextPage)) { return; } @@ -891,7 +894,12 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(this::handleNextItems, this::onItemError); + .subscribe(itemsPage -> { + handleNextItems(itemsPage); + if (initialDataLoadCallback != null) { + initialDataLoadCallback.run(); + } + }, this::onItemError); } @Override