Merge pull request #5148 from Stypox/error-panel
Show improved error panel instead of annoying snackbar or crashing
This commit is contained in:
commit
714c9fb397
73 changed files with 1148 additions and 1511 deletions
|
|
@ -1,48 +1,23 @@
|
|||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.jakewharton.rxbinding4.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.PaidContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.ErrorInfo;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ErrorPanelHelper;
|
||||
import org.schabi.newpipe.util.InfoCache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||
|
||||
|
|
@ -56,11 +31,10 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
@Nullable
|
||||
private ProgressBar loadingProgressBar;
|
||||
|
||||
private Disposable errorDisposable;
|
||||
|
||||
protected View errorPanelRoot;
|
||||
private Button errorButtonRetry;
|
||||
private TextView errorTextView;
|
||||
private ErrorPanelHelper errorPanelHelper;
|
||||
@Nullable
|
||||
@State
|
||||
protected ErrorInfo lastPanelError = null;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
|
||||
|
|
@ -74,12 +48,18 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
wasLoading.set(isLoading.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (lastPanelError != null) {
|
||||
showError(lastPanelError);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (errorDisposable != null) {
|
||||
errorDisposable.dispose();
|
||||
}
|
||||
errorPanelHelper.dispose();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -89,22 +69,9 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
@Override
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
emptyStateView = rootView.findViewById(R.id.empty_state_view);
|
||||
loadingProgressBar = rootView.findViewById(R.id.loading_progress_bar);
|
||||
|
||||
errorPanelRoot = rootView.findViewById(R.id.error_panel);
|
||||
errorButtonRetry = rootView.findViewById(R.id.error_button_retry);
|
||||
errorTextView = rootView.findViewById(R.id.error_message_view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initListeners() {
|
||||
super.initListeners();
|
||||
errorDisposable = RxView.clicks(errorButtonRetry)
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(o -> onRetryButtonClicked());
|
||||
errorPanelHelper = new ErrorPanelHelper(this, rootView, this::onRetryButtonClicked);
|
||||
}
|
||||
|
||||
protected void onRetryButtonClicked() {
|
||||
|
|
@ -143,7 +110,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
if (loadingProgressBar != null) {
|
||||
animate(loadingProgressBar, true, 400);
|
||||
}
|
||||
animate(errorPanelRoot, false, 150);
|
||||
hideErrorPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -154,10 +121,9 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
if (loadingProgressBar != null) {
|
||||
animate(loadingProgressBar, false, 0);
|
||||
}
|
||||
animate(errorPanelRoot, false, 150);
|
||||
hideErrorPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmptyState() {
|
||||
isLoading.set(false);
|
||||
if (emptyStateView != null) {
|
||||
|
|
@ -166,26 +132,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
if (loadingProgressBar != null) {
|
||||
animate(loadingProgressBar, false, 0);
|
||||
}
|
||||
animate(errorPanelRoot, false, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showError() called with: "
|
||||
+ "message = [" + message + "], showRetryButton = [" + showRetryButton + "]");
|
||||
}
|
||||
isLoading.set(false);
|
||||
InfoCache.getInstance().clearCache();
|
||||
hideLoading();
|
||||
|
||||
errorTextView.setText(message);
|
||||
if (showRetryButton) {
|
||||
animate(errorButtonRetry, true, 600);
|
||||
} else {
|
||||
animate(errorButtonRetry, false, 0);
|
||||
}
|
||||
animate(errorPanelRoot, true, 300);
|
||||
hideErrorPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -196,138 +143,69 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
hideLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError() {
|
||||
isLoading.set(false);
|
||||
InfoCache.getInstance().clearCache();
|
||||
if (emptyStateView != null) {
|
||||
animate(emptyStateView, false, 150);
|
||||
}
|
||||
if (loadingProgressBar != null) {
|
||||
animate(loadingProgressBar, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Error handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Default implementation handles some general exceptions.
|
||||
*
|
||||
* @param exception The exception that should be handled
|
||||
* @return If the exception was handled
|
||||
*/
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
isLoading.set(false);
|
||||
public final void showError(final ErrorInfo errorInfo) {
|
||||
handleError();
|
||||
|
||||
if (isDetached() || isRemoving()) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "onError() is detached or removing = [" + exception + "]");
|
||||
Log.w(TAG, "showError() is detached or removing = [" + errorInfo + "]");
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExceptionUtils.isInterruptedCaused(exception)) {
|
||||
errorPanelHelper.showError(errorInfo);
|
||||
lastPanelError = errorInfo;
|
||||
}
|
||||
|
||||
public final void showTextError(@NonNull final String errorString) {
|
||||
handleError();
|
||||
|
||||
if (isDetached() || isRemoving()) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "onError() isInterruptedCaused! = [" + exception + "]");
|
||||
Log.w(TAG, "showTextError() is detached or removing = [" + errorString + "]");
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception instanceof ReCaptchaException) {
|
||||
onReCaptchaException((ReCaptchaException) exception);
|
||||
return true;
|
||||
} else if (ExceptionUtils.isNetworkRelated(exception)) {
|
||||
showError(getString(R.string.network_error), true);
|
||||
return true;
|
||||
} else if (exception instanceof AgeRestrictedContentException) {
|
||||
showError(getString(R.string.restricted_video_no_stream), false);
|
||||
return true;
|
||||
} else if (exception instanceof GeographicRestrictionException) {
|
||||
showError(getString(R.string.georestricted_content), false);
|
||||
return true;
|
||||
} else if (exception instanceof PaidContentException) {
|
||||
showError(getString(R.string.paid_content), false);
|
||||
return true;
|
||||
} else if (exception instanceof PrivateContentException) {
|
||||
showError(getString(R.string.private_content), false);
|
||||
return true;
|
||||
} else if (exception instanceof SoundCloudGoPlusContentException) {
|
||||
showError(getString(R.string.soundcloud_go_plus_content), false);
|
||||
return true;
|
||||
} else if (exception instanceof YoutubeMusicPremiumContentException) {
|
||||
showError(getString(R.string.youtube_music_premium_content), false);
|
||||
return true;
|
||||
} else if (exception instanceof ContentNotAvailableException) {
|
||||
showError(getString(R.string.content_not_available), false);
|
||||
return true;
|
||||
} else if (exception instanceof ContentNotSupportedException) {
|
||||
showError(getString(R.string.content_not_supported), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
errorPanelHelper.showTextError(errorString);
|
||||
}
|
||||
|
||||
public void onReCaptchaException(final ReCaptchaException exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onReCaptchaException() called");
|
||||
}
|
||||
Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
final Intent intent = new Intent(activity, ReCaptchaActivity.class);
|
||||
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, exception.getUrl());
|
||||
startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST);
|
||||
|
||||
showError(getString(R.string.recaptcha_request_toast), false);
|
||||
public final void hideErrorPanel() {
|
||||
errorPanelHelper.hide();
|
||||
lastPanelError = null;
|
||||
}
|
||||
|
||||
public void onUnrecoverableError(final Throwable exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
onUnrecoverableError(Collections.singletonList(exception), userAction, serviceName,
|
||||
request, errorId);
|
||||
}
|
||||
|
||||
public void onUnrecoverableError(final List<Throwable> exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, null,
|
||||
ErrorInfo.make(userAction, serviceName == null ? "none" : serviceName,
|
||||
request == null ? "none" : request, errorId));
|
||||
}
|
||||
|
||||
public void showSnackBarError(final Throwable exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
showSnackBarError(Collections.singletonList(exception), userAction, serviceName, request,
|
||||
errorId);
|
||||
public final boolean isErrorPanelVisible() {
|
||||
return errorPanelHelper.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a SnackBar and only call
|
||||
* {@link ErrorActivity#reportError(Context, List, Class, View, ErrorInfo)}
|
||||
* {@link ErrorActivity#reportErrorInSnackbar(androidx.fragment.app.Fragment, ErrorInfo)}
|
||||
* IF we a find a valid view (otherwise the error screen appears).
|
||||
*
|
||||
* @param exception List of the exceptions to show
|
||||
* @param userAction The user action that caused the exception
|
||||
* @param serviceName The service where the exception happened
|
||||
* @param request The page that was requested
|
||||
* @param errorId The ID of the error
|
||||
* @param errorInfo The error information
|
||||
*/
|
||||
public void showSnackBarError(final List<Throwable> exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
public void showSnackBarError(final ErrorInfo errorInfo) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showSnackBarError() called with: "
|
||||
+ "exception = [" + exception + "], userAction = [" + userAction + "], "
|
||||
+ "request = [" + request + "], errorId = [" + errorId + "]");
|
||||
Log.d(TAG, "showSnackBarError() called with: errorInfo = [" + errorInfo + "]");
|
||||
}
|
||||
View rootView = activity != null ? activity.findViewById(android.R.id.content) : null;
|
||||
if (rootView == null && getView() != null) {
|
||||
rootView = getView();
|
||||
}
|
||||
if (rootView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
|
||||
ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
ErrorActivity.reportErrorInSnackbar(this, errorInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import android.view.ViewGroup;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround;
|
||||
|
|
@ -25,10 +24,8 @@ import com.google.android.material.tabs.TabLayout;
|
|||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.databinding.FragmentMainBinding;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.ErrorInfo;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.settings.tabs.Tab;
|
||||
import org.schabi.newpipe.settings.tabs.TabsManager;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
|
@ -128,7 +125,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu,
|
||||
@NonNull final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
|
|
@ -144,15 +142,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_search:
|
||||
try {
|
||||
NavigationHelper.openSearchFragment(getFM(),
|
||||
ServiceHelper.getSelectedServiceId(activity), "");
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
}
|
||||
return true;
|
||||
if (item.getItemId() == R.id.action_search) {
|
||||
try {
|
||||
NavigationHelper.openSearchFragment(getFM(),
|
||||
ServiceHelper.getSelectedServiceId(activity), "");
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening search fragment", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
|
@ -241,8 +238,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
if (throwable != null) {
|
||||
ErrorActivity.reportError(context, throwable, null, null, ErrorInfo
|
||||
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
|
||||
ErrorActivity.reportUiErrorInSnackbar(context, "Getting fragment item", throwable);
|
||||
return new BlankFragment();
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +250,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(final Object object) {
|
||||
public int getItemPosition(@NonNull final Object object) {
|
||||
// Causes adapter to reload all Fragments when
|
||||
// notifyDataSetChanged is called
|
||||
return POSITION_NONE;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ public interface ViewContract<I> {
|
|||
|
||||
void showEmptyState();
|
||||
|
||||
void showError(String message, boolean showRetryButton);
|
||||
|
||||
void handleResult(I result);
|
||||
|
||||
void handleError();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,10 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
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;
|
||||
|
|
@ -56,14 +54,15 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
|
|||
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
||||
import org.schabi.newpipe.download.DownloadDialog;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
|
|
@ -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;
|
||||
|
|
@ -86,9 +86,6 @@ import org.schabi.newpipe.player.helper.PlayerHolder;
|
|||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.ErrorInfo;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
|
@ -151,6 +148,7 @@ public final class VideoDetailFragment
|
|||
private static final String COMMENTS_TAB_TAG = "COMMENTS";
|
||||
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
||||
private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB";
|
||||
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
||||
|
||||
// tabs
|
||||
private boolean showComments;
|
||||
|
|
@ -526,7 +524,7 @@ public final class VideoDetailFragment
|
|||
NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
|
||||
subChannelUrl, subChannelName);
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -684,13 +682,12 @@ public final class VideoDetailFragment
|
|||
binding.detailThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
|
||||
|
||||
if (!isEmpty(info.getThumbnailUrl())) {
|
||||
final String infoServiceName = NewPipe.getNameOfService(info.getServiceId());
|
||||
final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingFailed(final String imageUri, final View view,
|
||||
final FailReason failReason) {
|
||||
showSnackBarError(failReason.getCause(), UserAction.LOAD_IMAGE,
|
||||
infoServiceName, imageUri, R.string.could_not_load_thumbnails);
|
||||
showSnackBarError(new ErrorInfo(failReason.getCause(), UserAction.LOAD_IMAGE,
|
||||
imageUri, info));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -906,10 +903,8 @@ public final class VideoDetailFragment
|
|||
openVideoPlayer();
|
||||
}
|
||||
}
|
||||
}, throwable -> {
|
||||
isLoading.set(false);
|
||||
onError(throwable);
|
||||
});
|
||||
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||
url == null ? "no url" : url, serviceId)));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -932,18 +927,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) {
|
||||
|
|
@ -1327,8 +1326,8 @@ public final class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
public void handleError() {
|
||||
super.handleError();
|
||||
setErrorImage(R.drawable.not_available_monkey);
|
||||
|
||||
if (binding.relatedStreamsLayout != null) { // hide related streams for tablets
|
||||
|
|
@ -1341,8 +1340,8 @@ public final class VideoDetailFragment
|
|||
}
|
||||
|
||||
private void hideAgeRestrictedContent() {
|
||||
showError(getString(R.string.restricted_video,
|
||||
getString(R.string.show_age_restricted_content_title)), false);
|
||||
showTextError(getString(R.string.restricted_video,
|
||||
getString(R.string.show_age_restricted_content_title)));
|
||||
}
|
||||
|
||||
private void setupBroadcastReceiver() {
|
||||
|
|
@ -1548,11 +1547,8 @@ public final class VideoDetailFragment
|
|||
}
|
||||
|
||||
if (!info.getErrors().isEmpty()) {
|
||||
showSnackBarError(info.getErrors(),
|
||||
UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(info.getServiceId()),
|
||||
info.getUrl(),
|
||||
0);
|
||||
showSnackBarError(new ErrorInfo(info.getErrors(),
|
||||
UserAction.REQUESTED_STREAM, info.getUrl(), info));
|
||||
}
|
||||
|
||||
binding.detailControlsDownload.setVisibility(info.getStreamType() == StreamType.LIVE_STREAM
|
||||
|
|
@ -1592,6 +1588,10 @@ public final class VideoDetailFragment
|
|||
}
|
||||
|
||||
public void openDownloadDialog() {
|
||||
if (currentInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
|
||||
downloadDialog.setVideoStreams(sortedVideoStreams);
|
||||
|
|
@ -1601,18 +1601,9 @@ public final class VideoDetailFragment
|
|||
|
||||
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
||||
} catch (final Exception e) {
|
||||
final ErrorInfo info = ErrorInfo.make(UserAction.UI_ERROR,
|
||||
ServiceList.all()
|
||||
.get(currentInfo
|
||||
.getServiceId())
|
||||
.getServiceInfo()
|
||||
.getName(), "",
|
||||
R.string.could_not_setup_download_menu);
|
||||
|
||||
ErrorActivity.reportError(activity,
|
||||
e,
|
||||
activity.getClass(),
|
||||
activity.findViewById(android.R.id.content), info);
|
||||
ErrorActivity.reportErrorInSnackbar(activity,
|
||||
new ErrorInfo(e, UserAction.DOWNLOAD_OPEN_DIALOG, "Showing download dialog",
|
||||
currentInfo));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1620,24 +1611,6 @@ public final class VideoDetailFragment
|
|||
// Stream Results
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int errorId = exception instanceof YoutubeStreamExtractor.DeobfuscateException
|
||||
? R.string.youtube_signature_deobfuscation_error
|
||||
: exception instanceof ExtractionException
|
||||
? R.string.parsing_error
|
||||
: R.string.general_error;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(serviceId), url, errorId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateProgressInfo(@NonNull final StreamInfo info) {
|
||||
if (positionSubscriber != null) {
|
||||
positionSubscriber.dispose();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import android.view.View;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
|
@ -22,6 +21,7 @@ import androidx.viewbinding.ViewBinding;
|
|||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.databinding.PignateFooterBinding;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
|
|
@ -33,7 +33,6 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
|||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.util.KoreUtil;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
|
@ -47,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<I, N> extends BaseStateFragment<I>
|
||||
implements ListViewContract<I, N>, StateSaver.WriteRead,
|
||||
|
|
@ -292,7 +292,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||
selectedItem.getUrl(),
|
||||
selectedItem.getName());
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
ErrorActivity.reportUiErrorInSnackbar(
|
||||
BaseListFragment.this, "Opening channel fragment", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -307,7 +308,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||
selectedItem.getUrl(),
|
||||
selectedItem.getName());
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
ErrorActivity.reportUiErrorInSnackbar(BaseListFragment.this,
|
||||
"Opening playlist fragment", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -406,23 +408,23 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animateHideRecyclerViewAllowingScrolling(itemsList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
super.hideLoading();
|
||||
animate(itemsList, true, 300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
showListFooter(false);
|
||||
animate(itemsList, false, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmptyState() {
|
||||
super.showEmptyState();
|
||||
showListFooter(false);
|
||||
animateHideRecyclerViewAllowingScrolling(itemsList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -439,6 +441,13 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||
isLoading.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError() {
|
||||
super.handleError();
|
||||
showListFooter(false);
|
||||
animateHideRecyclerViewAllowingScrolling(itemsList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
|
||||
final String key) {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,17 @@ import android.view.View;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.ListInfo;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.views.NewPipeRecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import icepick.State;
|
||||
|
|
@ -30,10 +35,15 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
@State
|
||||
protected String url;
|
||||
|
||||
private final UserAction errorUserAction;
|
||||
protected I currentInfo;
|
||||
protected Page currentNextPage;
|
||||
protected Disposable currentWorker;
|
||||
|
||||
protected BaseListInfoFragment(final UserAction errorUserAction) {
|
||||
this.errorUserAction = errorUserAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
|
@ -133,7 +143,9 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
currentInfo = result;
|
||||
currentNextPage = result.getNextPage();
|
||||
handleResult(result);
|
||||
}, this::onError);
|
||||
}, throwable ->
|
||||
showError(new ErrorInfo(throwable, errorUserAction,
|
||||
"Start loading: " + url, serviceId)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -161,10 +173,9 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
.subscribe((@NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> {
|
||||
isLoading.set(false);
|
||||
handleNextItems(InfoItemsPage);
|
||||
}, (@NonNull Throwable throwable) -> {
|
||||
isLoading.set(false);
|
||||
onError(throwable);
|
||||
});
|
||||
}, (@NonNull Throwable throwable) ->
|
||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
|
||||
errorUserAction, "Loading more items: " + url, serviceId)));
|
||||
}
|
||||
|
||||
private void forbidDownwardFocusScroll() {
|
||||
|
|
@ -182,10 +193,16 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
currentNextPage = result.getNextPage();
|
||||
infoListAdapter.addInfoItemList(result.getItems());
|
||||
|
||||
showListFooter(hasMoreItems());
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), errorUserAction,
|
||||
"Get next items of: " + url, serviceId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -213,6 +230,18 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
showEmptyState();
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
final List<Throwable> 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);
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(),
|
||||
errorUserAction, "Start loading: " + url, serviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -224,4 +253,14 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
this.url = u;
|
||||
this.name = !TextUtils.isEmpty(title) ? title : "";
|
||||
}
|
||||
|
||||
private void dynamicallyShowErrorPanelOrSnackbar(final ErrorInfo errorInfo) {
|
||||
if (infoListAdapter.getItemCount() == 0) {
|
||||
// show error panel only if no items already visible
|
||||
showError(errorInfo);
|
||||
} else {
|
||||
isLoading.set(false);
|
||||
showSnackBarError(errorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import android.widget.Button;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
|
|
@ -27,20 +26,19 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
|||
import org.schabi.newpipe.databinding.ChannelHeaderBinding;
|
||||
import org.schabi.newpipe.databinding.FragmentChannelBinding;
|
||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.ktx.AnimationType;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
|
@ -91,6 +89,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
return instance;
|
||||
}
|
||||
|
||||
public ChannelFragment() {
|
||||
super(UserAction.REQUESTED_CHANNEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
|
|
@ -217,9 +219,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
private void monitorSubscription(final ChannelInfo info) {
|
||||
final Consumer<Throwable> onError = (Throwable throwable) -> {
|
||||
animate(headerBinding.channelSubscribeButton, false, 100);
|
||||
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
|
||||
NewPipe.getNameOfService(currentInfo.getServiceId()),
|
||||
"Get subscription status", 0);
|
||||
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET,
|
||||
"Get subscription status", currentInfo));
|
||||
};
|
||||
|
||||
final Observable<List<SubscriptionEntity>> observable = subscriptionManager
|
||||
|
|
@ -269,11 +270,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
};
|
||||
|
||||
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
||||
onUnrecoverableError(throwable,
|
||||
UserAction.SUBSCRIPTION,
|
||||
NewPipe.getNameOfService(info.getServiceId()),
|
||||
"Updating Subscription for " + info.getUrl(),
|
||||
R.string.subscription_update_failed);
|
||||
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_UPDATE,
|
||||
"Updating subscription for " + info.getUrl(), info));
|
||||
|
||||
disposables.add(subscriptionManager.updateChannelInfo(info)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
|
@ -290,11 +288,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
};
|
||||
|
||||
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
||||
onUnrecoverableError(throwable,
|
||||
UserAction.SUBSCRIPTION,
|
||||
NewPipe.getNameOfService(currentInfo.getServiceId()),
|
||||
"Subscription Change",
|
||||
R.string.subscription_change_failed);
|
||||
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_CHANGE,
|
||||
"Changing subscription for " + currentInfo.getUrl(), currentInfo));
|
||||
|
||||
/* Emit clicks from main thread unto io thread */
|
||||
return RxView.clicks(subscribeButton)
|
||||
|
|
@ -408,7 +403,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
currentInfo.getParentChannelUrl(),
|
||||
currentInfo.getParentChannelName());
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
||||
}
|
||||
} else if (DEBUG) {
|
||||
Log.i(TAG, "Can't open parent channel because we got no channel URL");
|
||||
|
|
@ -469,27 +464,13 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
|
||||
playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
|
||||
|
||||
final List<Throwable> errors = new ArrayList<>(result.getErrors());
|
||||
if (!errors.isEmpty()) {
|
||||
|
||||
// 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 -> {
|
||||
if (throwable instanceof ContentNotSupportedException) {
|
||||
showContentNotSupported();
|
||||
}
|
||||
return throwable instanceof ContentNotSupportedException;
|
||||
});
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
showSnackBarError(errors, UserAction.REQUESTED_CHANNEL,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
for (final Throwable throwable : result.getErrors()) {
|
||||
if (throwable instanceof ContentNotSupportedException) {
|
||||
showContentNotSupported();
|
||||
}
|
||||
}
|
||||
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
disposables.clear();
|
||||
if (subscribeButtonMonitor != null) {
|
||||
subscribeButtonMonitor.dispose();
|
||||
}
|
||||
|
|
@ -539,38 +520,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
currentInfo.getNextPage(), streamItems, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_CHANNEL,
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url,
|
||||
R.string.general_error);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// OnError
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int errorId = exception instanceof ExtractionException
|
||||
? R.string.parsing_error : R.string.general_error;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL,
|
||||
NewPipe.getNameOfService(serviceId), url, errorId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfo;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.ktx.ViewUtils;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
|
@ -25,13 +24,17 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|||
public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
public static CommentsFragment getInstance(final int serviceId, final String url,
|
||||
public static CommentsFragment getInstance(final int serviceId, final String url,
|
||||
final String name) {
|
||||
final CommentsFragment instance = new CommentsFragment();
|
||||
instance.setInitialData(serviceId, url, name);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public CommentsFragment() {
|
||||
super(UserAction.REQUESTED_COMMENTS);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -67,52 +70,13 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull final CommentsInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
ViewUtils.slideUp(requireView(), 120, 150, 0.06f);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
disposables.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(serviceId), "Get next page of: " + url,
|
||||
R.string.general_error);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// OnError
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
showSnackBarError(exception, UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(serviceId), url, R.string.error_unable_to_load_comments);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
|
|||
|
|
@ -2,14 +2,16 @@ package org.schabi.newpipe.fragments.list.kiosk;
|
|||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskList;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
public class DefaultKioskFragment extends KioskFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -46,8 +48,8 @@ public class DefaultKioskFragment extends KioskFragment {
|
|||
currentInfo = null;
|
||||
currentNextPage = null;
|
||||
} catch (final ExtractionException e) {
|
||||
onUnrecoverableError(e, UserAction.REQUESTED_KIOSK, "none",
|
||||
"Loading default kiosk from selected service", 0);
|
||||
showError(new ErrorInfo(e, UserAction.REQUESTED_KIOSK,
|
||||
"Loading default kiosk for selected service"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.app.ActionBar;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
|
|
@ -20,7 +22,6 @@ import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
|||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
|
@ -28,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.
|
||||
* <p>
|
||||
|
|
@ -82,6 +81,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
return instance;
|
||||
}
|
||||
|
||||
public KioskFragment() {
|
||||
super(UserAction.REQUESTED_KIOSK);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -102,9 +105,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
try {
|
||||
setTitle(kioskTranslatedName);
|
||||
} catch (final Exception e) {
|
||||
onUnrecoverableError(e, UserAction.UI_ERROR,
|
||||
"none",
|
||||
"none", R.string.app_ui_crash);
|
||||
showSnackBarError(new ErrorInfo(e, UserAction.UI_ERROR, "Setting kiosk title"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,34 +158,11 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animate(itemsList, false, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull final KioskInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
name = kioskTranslatedName;
|
||||
setTitle(kioskTranslatedName);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_KIOSK,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import android.view.ViewGroup;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
|
|
@ -25,11 +24,12 @@ import org.schabi.newpipe.R;
|
|||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||
import org.schabi.newpipe.databinding.PlaylistHeaderBinding;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
|
@ -40,8 +40,6 @@ import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
|||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.KoreUtil;
|
||||
|
|
@ -62,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<PlaylistInfo> {
|
||||
|
|
@ -87,6 +86,10 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
return instance;
|
||||
}
|
||||
|
||||
public PlaylistFragment() {
|
||||
super(UserAction.REQUESTED_PLAYLIST);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -262,7 +265,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
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);
|
||||
|
|
@ -284,7 +287,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
NavigationHelper.openChannelFragment(getFM(), result.getServiceId(),
|
||||
result.getUploaderUrl(), result.getUploaderName());
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -315,8 +318,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
.localizeStreamCount(getContext(), result.getStreamCount()));
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
|
||||
result.getUrl(), result));
|
||||
}
|
||||
|
||||
remotePlaylistManager.getPlaylist(result)
|
||||
|
|
@ -363,33 +366,6 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(serviceId), "Get next page of: " + url, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// OnError
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int errorId = exception instanceof ExtractionException
|
||||
? R.string.parsing_error : R.string.general_error;
|
||||
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(serviceId), url, errorId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -434,8 +410,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable t) {
|
||||
PlaylistFragment.this.onError(t);
|
||||
public void onError(final Throwable throwable) {
|
||||
showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
||||
"Get playlist bookmarks"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -460,12 +437,16 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
if (currentInfo != null && playlistEntity == null) {
|
||||
action = remotePlaylistManager.onBookmark(currentInfo)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> { /* Do nothing */ }, this::onError);
|
||||
.subscribe(ignored -> { /* Do nothing */ }, throwable ->
|
||||
showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
||||
"Adding playlist bookmark")));
|
||||
} else if (playlistEntity != null) {
|
||||
action = remotePlaylistManager.deletePlaylist(playlistEntity.getUid())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doFinally(() -> playlistEntity = null)
|
||||
.subscribe(ignored -> { /* Do nothing */ }, this::onError);
|
||||
.subscribe(ignored -> { /* Do nothing */ }, throwable ->
|
||||
showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
|
||||
"Deleting playlist bookmark")));
|
||||
} else {
|
||||
action = Disposable.empty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,16 +35,18 @@ import androidx.recyclerview.widget.ItemTouchHelper;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.search.SearchInfo;
|
||||
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
|
||||
|
|
@ -54,9 +56,6 @@ import org.schabi.newpipe.fragments.list.BaseListFragment;
|
|||
import org.schabi.newpipe.ktx.AnimationType;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.ErrorInfo;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
|
@ -162,11 +161,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
private EditText searchEditText;
|
||||
private View searchClear;
|
||||
|
||||
private TextView correctSuggestion;
|
||||
private TextView metaInfoTextView;
|
||||
private View metaInfoSeparator;
|
||||
|
||||
private View suggestionsPanel;
|
||||
private boolean suggestionsPanelVisible = false;
|
||||
|
||||
/*////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -258,20 +252,23 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
try {
|
||||
service = NewPipe.getService(serviceId);
|
||||
} catch (final Exception e) {
|
||||
ErrorActivity.reportError(getActivity(), e, requireActivity().getClass(),
|
||||
requireActivity().findViewById(android.R.id.content),
|
||||
ErrorInfo.make(UserAction.UI_ERROR,
|
||||
"",
|
||||
"", R.string.general_error));
|
||||
ErrorActivity.reportUiErrorInSnackbar(this,
|
||||
"Getting service for id " + serviceId, e);
|
||||
}
|
||||
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
||||
initSuggestionObserver();
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(searchString)) {
|
||||
if (wasLoading.getAndSet(false)) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
return;
|
||||
} else if (infoListAdapter.getItemsList().isEmpty()) {
|
||||
if (savedState == null) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
} else if (!isLoading.get() && !wasSearchFocused) {
|
||||
return;
|
||||
} else if (!isLoading.get() && !wasSearchFocused && lastPanelError == null) {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
showEmptyState();
|
||||
}
|
||||
|
|
@ -281,11 +278,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
handleSearchSuggestion();
|
||||
|
||||
disposables.add(showMetaInfoInTextView(metaInfo == null ? null : Arrays.asList(metaInfo),
|
||||
metaInfoTextView, metaInfoSeparator));
|
||||
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
||||
initSuggestionObserver();
|
||||
}
|
||||
searchBinding.searchMetaInfoTextView, searchBinding.searchMetaInfoSeparator));
|
||||
|
||||
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
|
||||
showKeyboardSearch();
|
||||
|
|
@ -367,10 +360,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
|
||||
searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
|
||||
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
|
||||
|
||||
correctSuggestion = rootView.findViewById(R.id.correct_suggestion);
|
||||
metaInfoTextView = rootView.findViewById(R.id.search_meta_info_text_view);
|
||||
metaInfoSeparator = rootView.findViewById(R.id.search_meta_info_separator);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -413,7 +402,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
searchEditText.setText("");
|
||||
showKeyboardSearch();
|
||||
}
|
||||
animate(errorPanelRoot, false, 200);
|
||||
hideErrorPanel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -540,7 +529,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
}
|
||||
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
|
||||
if (isSuggestionsEnabled && !isErrorPanelVisible()) {
|
||||
showSuggestionsPanel();
|
||||
}
|
||||
if (DeviceUtils.isTv(getContext())) {
|
||||
|
|
@ -553,8 +542,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
Log.d(TAG, "onFocusChange() called with: "
|
||||
+ "v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
||||
}
|
||||
if (isSuggestionsEnabled && hasFocus
|
||||
&& errorPanelRoot.getVisibility() != View.VISIBLE) {
|
||||
if (isSuggestionsEnabled && hasFocus && !isErrorPanelVisible()) {
|
||||
showSuggestionsPanel();
|
||||
}
|
||||
});
|
||||
|
|
@ -704,9 +692,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.subscribe(
|
||||
howManyDeleted -> suggestionPublisher
|
||||
.onNext(searchEditText.getText().toString()),
|
||||
throwable -> showSnackBarError(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY, "none",
|
||||
"Deleting item failed", R.string.general_error));
|
||||
throwable -> showSnackBarError(new ErrorInfo(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY,
|
||||
"Deleting item failed")));
|
||||
disposables.add(onDelete);
|
||||
})
|
||||
.show();
|
||||
|
|
@ -733,14 +721,12 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
suggestionDisposable.dispose();
|
||||
}
|
||||
|
||||
final Observable<String> observable = suggestionPublisher
|
||||
suggestionDisposable = suggestionPublisher
|
||||
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
|
||||
.startWithItem(searchString != null
|
||||
? searchString
|
||||
: "")
|
||||
.filter(ss -> isSuggestionsEnabled);
|
||||
|
||||
suggestionDisposable = observable
|
||||
.filter(ss -> isSuggestionsEnabled)
|
||||
.switchMap(query -> {
|
||||
final Flowable<List<SearchHistoryEntry>> flowable = historyRecordManager
|
||||
.getRelatedSearches(query, 3, 25);
|
||||
|
|
@ -763,8 +749,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.suggestionsFor(serviceId, query)
|
||||
.onErrorReturn(throwable -> {
|
||||
if (!ExceptionUtils.isNetworkRelated(throwable)) {
|
||||
showSnackBarError(throwable, UserAction.GET_SUGGESTIONS,
|
||||
NewPipe.getNameOfService(serviceId), searchString, 0);
|
||||
showSnackBarError(new ErrorInfo(throwable,
|
||||
UserAction.GET_SUGGESTIONS, searchString, serviceId));
|
||||
}
|
||||
return new ArrayList<>();
|
||||
})
|
||||
|
|
@ -800,7 +786,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
if (listNotification.isOnNext()) {
|
||||
handleSuggestions(listNotification.getValue());
|
||||
} else if (listNotification.isOnError()) {
|
||||
onSuggestionError(listNotification.getError());
|
||||
showError(new ErrorInfo(listNotification.getError(),
|
||||
UserAction.GET_SUGGESTIONS, searchString, serviceId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -832,8 +819,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.subscribe(intent -> {
|
||||
getFM().popBackStackImmediate();
|
||||
activity.startActivity(intent);
|
||||
}, throwable ->
|
||||
showError(getString(R.string.unsupported_url), false)));
|
||||
}, throwable -> showTextError(getString(R.string.unsupported_url))));
|
||||
return;
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
|
|
@ -844,15 +830,16 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
this.searchString = theSearchString;
|
||||
infoListAdapter.clearStreamItemList();
|
||||
hideSuggestionsPanel();
|
||||
showMetaInfoInTextView(null, searchBinding.searchMetaInfoTextView,
|
||||
searchBinding.searchMetaInfoSeparator);
|
||||
hideKeyboardSearch();
|
||||
|
||||
disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {
|
||||
},
|
||||
error -> showSnackBarError(error, UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), theSearchString, 0)
|
||||
ignored -> { },
|
||||
throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED,
|
||||
theSearchString, serviceId))
|
||||
));
|
||||
suggestionPublisher.onNext(theSearchString);
|
||||
startLoading(false);
|
||||
|
|
@ -872,7 +859,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
|
||||
.subscribe(this::handleResult, this::onError);
|
||||
.subscribe(this::handleResult, this::onItemError);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -895,7 +882,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
|
||||
.subscribe(this::handleNextItems, this::onError);
|
||||
.subscribe(this::handleNextItems, this::onItemError);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -909,6 +896,15 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
hideKeyboardSearch();
|
||||
}
|
||||
|
||||
private void onItemError(final Throwable exception) {
|
||||
if (exception instanceof SearchExtractor.NothingFoundException) {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
showEmptyState();
|
||||
} else {
|
||||
showError(new ErrorInfo(exception, UserAction.SEARCHED, searchString, serviceId));
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -945,26 +941,11 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
searchBinding.suggestionsList.smoothScrollToPosition(0);
|
||||
searchBinding.suggestionsList.post(() -> suggestionListAdapter.setItems(suggestions));
|
||||
|
||||
if (suggestionsPanelVisible && errorPanelRoot.getVisibility() == View.VISIBLE) {
|
||||
if (suggestionsPanelVisible && isErrorPanelVisible()) {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
public void onSuggestionError(final Throwable exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
if (super.onError(exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int errorId = exception instanceof ParsingException
|
||||
? R.string.parsing_error
|
||||
: R.string.general_error;
|
||||
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
|
||||
NewPipe.getNameOfService(serviceId), searchString, errorId);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -975,13 +956,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
showListFooter(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
hideSuggestionsPanel();
|
||||
hideKeyboardSearch();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Search Results
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -992,8 +966,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
if (!exceptions.isEmpty()
|
||||
&& !(exceptions.size() == 1
|
||||
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
|
||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), searchString, 0);
|
||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
||||
searchString, serviceId));
|
||||
}
|
||||
|
||||
searchSuggestion = result.getSearchSuggestion();
|
||||
|
|
@ -1002,8 +976,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
// List<MetaInfo> cannot be bundled without creating some containers
|
||||
metaInfo = new MetaInfo[result.getMetaInfo().size()];
|
||||
metaInfo = result.getMetaInfo().toArray(metaInfo);
|
||||
disposables.add(showMetaInfoInTextView(result.getMetaInfo(), metaInfoTextView,
|
||||
metaInfoSeparator));
|
||||
disposables.add(showMetaInfoInTextView(result.getMetaInfo(),
|
||||
searchBinding.searchMetaInfoTextView, searchBinding.searchMetaInfoSeparator));
|
||||
|
||||
handleSearchSuggestion();
|
||||
|
||||
|
|
@ -1061,33 +1035,20 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
nextPage = result.getNextPage();
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
||||
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
|
||||
+ "pageIds: " + nextPage.getIds() + ", "
|
||||
+ "pageCookies: " + nextPage.getCookies(), 0);
|
||||
+ "pageCookies: " + nextPage.getCookies(),
|
||||
serviceId));
|
||||
}
|
||||
super.handleNextItems(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exception instanceof SearchExtractor.NothingFoundException) {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
showEmptyState();
|
||||
} else {
|
||||
final int errorId = exception instanceof ParsingException
|
||||
? R.string.parsing_error
|
||||
: R.string.general_error;
|
||||
onUnrecoverableError(exception, UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), searchString, errorId);
|
||||
}
|
||||
|
||||
return true;
|
||||
public void handleError() {
|
||||
super.handleError();
|
||||
hideSuggestionsPanel();
|
||||
hideKeyboardSearch();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1113,9 +1074,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
.subscribe(
|
||||
howManyDeleted -> suggestionPublisher
|
||||
.onNext(searchEditText.getText().toString()),
|
||||
throwable -> showSnackBarError(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY, "none",
|
||||
"Deleting item failed", R.string.general_error));
|
||||
throwable -> showSnackBarError(new ErrorInfo(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY, "Deleting item failed")));
|
||||
disposables.add(onDelete);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,11 @@ import androidx.viewbinding.ViewBinding;
|
|||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.databinding.RelatedStreamsHeaderBinding;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.ktx.ViewUtils;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.RelatedStreamInfo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -47,6 +46,10 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
return instance;
|
||||
}
|
||||
|
||||
public RelatedVideosFragment() {
|
||||
super(UserAction.REQUESTED_STREAM);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -125,43 +128,9 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
}
|
||||
ViewUtils.slideUp(requireView(), 120, 96, 0.06f);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
disposables.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url,
|
||||
R.string.general_error);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// OnError
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
showSnackBarError(exception, UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(serviceId), url, R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -190,11 +159,9 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
|
||||
super.onRestoreInstanceState(savedState);
|
||||
if (savedState != null) {
|
||||
final Serializable serializable = savedState.getSerializable(INFO_KEY);
|
||||
if (serializable instanceof RelatedStreamInfo) {
|
||||
this.relatedStreamInfo = (RelatedStreamInfo) serializable;
|
||||
}
|
||||
final Serializable serializable = savedState.getSerializable(INFO_KEY);
|
||||
if (serializable instanceof RelatedStreamInfo) {
|
||||
this.relatedStreamInfo = (RelatedStreamInfo) serializable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue