diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index 0a43c0525..73f63b131 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -1,9 +1,11 @@ package org.schabi.newpipe.fragments; +import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ProgressBar; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -135,7 +137,12 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC public void handleError() { isLoading.set(false); InfoCache.getInstance().clearCache(); - hideLoading(); + if (emptyStateView != null) { + animateView(emptyStateView, false, 150); + } + if (loadingProgressBar != null) { + animateView(loadingProgressBar, false, 0); + } } /*////////////////////////////////////////////////////////////////////////// @@ -178,7 +185,7 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC /** * Show a SnackBar and only call - * {@link ErrorActivity.reportErrorInSnackbar(androidx.fragment.app.Fragment, ErrorInfo)} + * {@link ErrorActivity#reportErrorInSnackbar(androidx.fragment.app.Fragment, ErrorInfo)} * IF we a find a valid view (otherwise the error screen appears). * * @param errorInfo The error information diff --git a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java index 62f823c73..fbf2711bc 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java @@ -11,9 +11,18 @@ import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; public class EmptyFragment extends BaseFragment { + final boolean showMessage; + + public EmptyFragment(final boolean showMessage) { + this.showMessage = showMessage; + } + @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, final Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_empty, container, false); + final View view = inflater.inflate(R.layout.fragment_empty, container, false); + view.findViewById(R.id.empty_state_view).setVisibility( + showMessage ? View.VISIBLE : View.GONE); + return view; } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index dc35a86f0..797aa2a03 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -41,7 +41,6 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import com.google.android.exoplayer2.ExoPlaybackException; @@ -71,6 +70,7 @@ import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BaseStateFragment; +import org.schabi.newpipe.fragments.EmptyFragment; import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment; import org.schabi.newpipe.ktx.AnimationType; @@ -926,18 +926,22 @@ public final class VideoDetailFragment } if (showRelatedStreams && binding.relatedStreamsLayout == null) { - //temp empty fragment. will be updated in handleResult - pageAdapter.addFragment(new Fragment(), RELATED_TAB_TAG); + // temp empty fragment. will be updated in handleResult + pageAdapter.addFragment(new EmptyFragment(false), RELATED_TAB_TAG); tabIcons.add(R.drawable.ic_art_track_white_24dp); tabContentDescriptions.add(R.string.related_streams_tab_description); } if (showDescription) { // temp empty fragment. will be updated in handleResult - pageAdapter.addFragment(new Fragment(), DESCRIPTION_TAB_TAG); + pageAdapter.addFragment(new EmptyFragment(false), DESCRIPTION_TAB_TAG); tabIcons.add(R.drawable.ic_description_white_24dp); tabContentDescriptions.add(R.string.description_tab_description); } + + if (pageAdapter.getCount() == 0) { + pageAdapter.addFragment(new EmptyFragment(true), EMPTY_TAB_TAG); + } pageAdapter.notifyDataSetUpdate(); if (pageAdapter.getCount() >= 2) { 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 257ccde43..3c37bd128 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 @@ -46,6 +46,7 @@ import java.util.List; import java.util.Queue; import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; public abstract class BaseListFragment extends BaseStateFragment implements ListViewContract, StateSaver.WriteRead, @@ -407,6 +408,12 @@ public abstract class BaseListFragment extends BaseStateFragment // Contract //////////////////////////////////////////////////////////////////////////*/ + @Override + public void showLoading() { + super.showLoading(); + animateHideRecyclerViewAllowingScrolling(itemsList); + } + @Override public void hideLoading() { super.hideLoading(); @@ -417,6 +424,7 @@ public abstract class BaseListFragment extends BaseStateFragment public void showEmptyState() { super.showEmptyState(); showListFooter(false); + animateHideRecyclerViewAllowingScrolling(itemsList); } @Override @@ -437,7 +445,7 @@ public abstract class BaseListFragment extends BaseStateFragment public void handleError() { super.handleError(); showListFooter(false); - animate(itemsList, false, 200); + animateHideRecyclerViewAllowingScrolling(itemsList); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 3184ba442..882bb021d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -29,8 +29,6 @@ import org.schabi.newpipe.util.Localization; import icepick.State; import io.reactivex.rxjava3.core.Single; -import static org.schabi.newpipe.ktx.ViewUtils.animate; - /** * Created by Christian Schabesberger on 23.09.17. *

@@ -160,12 +158,6 @@ public class KioskFragment extends BaseListInfoFragment { // Contract //////////////////////////////////////////////////////////////////////////*/ - @Override - public void showLoading() { - super.showLoading(); - animate(itemsList, false, 100); - } - @Override public void handleResult(@NonNull final KioskInfo result) { super.handleResult(result); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 326013885..114947923 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -60,6 +60,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr; public class PlaylistFragment extends BaseListInfoFragment { @@ -264,7 +265,7 @@ public class PlaylistFragment extends BaseListInfoFragment { public void showLoading() { super.showLoading(); animate(headerBinding.getRoot(), false, 200); - animate(itemsList, false, 100); + animateHideRecyclerViewAllowingScrolling(itemsList); IMAGE_LOADER.cancelDisplayTask(headerBinding.uploaderAvatarView); animate(headerBinding.uploaderLayout, false, 200); diff --git a/app/src/main/java/org/schabi/newpipe/ktx/View.kt b/app/src/main/java/org/schabi/newpipe/ktx/View.kt index 2b1f36bc7..fe3ab7112 100644 --- a/app/src/main/java/org/schabi/newpipe/ktx/View.kt +++ b/app/src/main/java/org/schabi/newpipe/ktx/View.kt @@ -35,11 +35,11 @@ inline var View.backgroundTintListCompat: ColorStateList? */ @JvmOverloads fun View.animate( - enterOrExit: Boolean, - duration: Long, - animationType: AnimationType = AnimationType.ALPHA, - delay: Long = 0, - execOnEnd: Runnable? = null + enterOrExit: Boolean, + duration: Long, + animationType: AnimationType = AnimationType.ALPHA, + delay: Long = 0, + execOnEnd: Runnable? = null ) { if (MainActivity.DEBUG) { val id = try { @@ -48,8 +48,8 @@ fun View.animate( id.toString() } val msg = String.format( - "%8s → [%s:%s] [%s %s:%s] execOnEnd=%s", enterOrExit, - javaClass.simpleName, id, animationType, duration, delay, execOnEnd + "%8s → [%s:%s] [%s %s:%s] execOnEnd=%s", enterOrExit, + javaClass.simpleName, id, animationType, duration, delay, execOnEnd ) Log.d(TAG, "animate(): $msg") } @@ -93,10 +93,10 @@ fun View.animate( fun View.animateBackgroundColor(duration: Long, @ColorInt colorStart: Int, @ColorInt colorEnd: Int) { if (MainActivity.DEBUG) { Log.d( - TAG, - "animateBackgroundColor() called with: " + - "view = [" + this + "], duration = [" + duration + "], " + - "colorStart = [" + colorStart + "], colorEnd = [" + colorEnd + "]" + TAG, + "animateBackgroundColor() called with: " + + "view = [" + this + "], duration = [" + duration + "], " + + "colorStart = [" + colorStart + "], colorEnd = [" + colorEnd + "]" ) } val empty = arrayOf(IntArray(0)) @@ -121,9 +121,9 @@ fun View.animateBackgroundColor(duration: Long, @ColorInt colorStart: Int, @Colo fun View.animateHeight(duration: Long, targetHeight: Int): ValueAnimator { if (MainActivity.DEBUG) { Log.d( - TAG, - "animateHeight: duration = [" + duration + "], " + - "from " + height + " to → " + targetHeight + " in: " + this + TAG, + "animateHeight: duration = [" + duration + "], " + + "from " + height + " to → " + targetHeight + " in: " + this ) } val animator = ValueAnimator.ofFloat(height.toFloat(), targetHeight.toFloat()) @@ -152,9 +152,9 @@ fun View.animateHeight(duration: Long, targetHeight: Int): ValueAnimator { fun View.animateRotation(duration: Long, targetRotation: Int) { if (MainActivity.DEBUG) { Log.d( - TAG, - "animateRotation: duration = [" + duration + "], " + - "from " + rotation + " to → " + targetRotation + " in: " + this + TAG, + "animateRotation: duration = [" + duration + "], " + + "from " + rotation + " to → " + targetRotation + " in: " + this ) } animate().setListener(null).cancel() @@ -319,6 +319,17 @@ fun View.slideUp(duration: Long, delay: Long, @FloatRange(from = 0.0, to = 1.0) .start() } + +/** + * Instead of hiding normally using [animate], which would make + * the recycler view unable to capture touches after being hidden, this just animates the alpha + * value setting it to `0.0` after `200` milliseconds. + */ +fun View.animateHideRecyclerViewAllowingScrolling() { + // not hiding normally because the view needs to still capture touches and allow scroll + animate().alpha(0.0f).setDuration(200).start() +} + enum class AnimationType { ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA, SLIDE_AND_ALPHA, LIGHT_SLIDE_AND_ALPHA } diff --git a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java index 1e4260ba3..78fb20029 100644 --- a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java @@ -24,6 +24,7 @@ import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.list.ListViewContract; import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; /** * This fragment is design to be used with persistent data such as @@ -184,7 +185,7 @@ public abstract class BaseLocalListFragment extends BaseStateFragment public void showLoading() { super.showLoading(); if (itemsList != null) { - animate(itemsList, false, 200); + animateHideRecyclerViewAllowingScrolling(itemsList); } if (headerRootBinding != null) { animate(headerRootBinding.getRoot(), false, 200); @@ -243,7 +244,7 @@ public abstract class BaseLocalListFragment extends BaseStateFragment showListFooter(false); if (itemsList != null) { - animate(itemsList, false, 200); + animateHideRecyclerViewAllowingScrolling(itemsList); } if (headerRootBinding != null) { animate(headerRootBinding.getRoot(), false, 200); diff --git a/app/src/main/res/layout/fragment_empty.xml b/app/src/main/res/layout/fragment_empty.xml index 6a8d3c1c6..60c7d9e4a 100644 --- a/app/src/main/res/layout/fragment_empty.xml +++ b/app/src/main/res/layout/fragment_empty.xml @@ -1,6 +1,5 @@ @@ -14,16 +13,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:layout_marginTop="90dp" - tools:visibility="visible" /> - + android:layout_marginTop="90dp" /> - - -