diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index ac1b76270..2d8c1a830 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -97,7 +97,6 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -166,6 +165,7 @@ import org.schabi.newpipe.player.playback.MediaSourceManager; import org.schabi.newpipe.player.playback.PlaybackListener; import org.schabi.newpipe.player.playback.PlayerMediaSession; import org.schabi.newpipe.player.playback.SurfaceHolderCallback; +import org.schabi.newpipe.player.playererror.PlayerErrorHandler; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -268,7 +268,7 @@ public final class Player implements @Nullable private MediaSourceTag currentMetadata; @Nullable private Bitmap currentThumbnail; - @Nullable private Toast errorToast; + @NonNull private PlayerErrorHandler playerErrorHandler; /*////////////////////////////////////////////////////////////////////////// // Player @@ -413,6 +413,8 @@ public final class Player implements videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver()); audioResolver = new AudioPlaybackResolver(context, dataSource); + playerErrorHandler = new PlayerErrorHandler(context); + windowManager = ContextCompat.getSystemService(context, WindowManager.class); } @@ -2512,30 +2514,33 @@ public final class Player implements */ @Override public void onPlayerError(@NonNull final ExoPlaybackException error) { - if (DEBUG) { - Log.d(TAG, "ExoPlayer - onPlayerError() called with: " + "error = [" + error + "]"); - } - if (errorToast != null) { - errorToast.cancel(); - errorToast = null; - } + Log.e(TAG, "ExoPlayer - onPlayerError() called with:", error); saveStreamProgressState(); switch (error.type) { case ExoPlaybackException.TYPE_SOURCE: processSourceError(error.getSourceException()); - showStreamError(error); + playerErrorHandler.showPlayerError( + error, + currentMetadata.getMetadata(), + R.string.player_stream_failure); break; case ExoPlaybackException.TYPE_UNEXPECTED: - showRecoverableError(error); + playerErrorHandler.showPlayerError( + error, + currentMetadata.getMetadata(), + R.string.player_recoverable_failure); setRecovery(); reloadPlayQueueManager(); break; case ExoPlaybackException.TYPE_REMOTE: case ExoPlaybackException.TYPE_RENDERER: default: - showUnrecoverableError(error); + playerErrorHandler.showPlayerError( + error, + currentMetadata.getMetadata(), + R.string.player_unrecoverable_failure); onPlaybackShutdown(); break; } @@ -2557,37 +2562,6 @@ public final class Player implements playQueue.error(); } } - - private void showStreamError(final Exception exception) { - exception.printStackTrace(); - - if (errorToast == null) { - errorToast = Toast - .makeText(context, R.string.player_stream_failure, Toast.LENGTH_SHORT); - errorToast.show(); - } - } - - private void showRecoverableError(final Exception exception) { - exception.printStackTrace(); - - if (errorToast == null) { - errorToast = Toast - .makeText(context, R.string.player_recoverable_failure, Toast.LENGTH_SHORT); - errorToast.show(); - } - } - - private void showUnrecoverableError(final Exception exception) { - exception.printStackTrace(); - - if (errorToast != null) { - errorToast.cancel(); - } - errorToast = Toast - .makeText(context, R.string.player_unrecoverable_failure, Toast.LENGTH_SHORT); - errorToast.show(); - } //endregion diff --git a/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java b/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java new file mode 100644 index 000000000..8e9ad9d15 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java @@ -0,0 +1,86 @@ +package org.schabi.newpipe.player.playererror; + +import android.content.Context; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.preference.PreferenceManager; + +import com.google.android.exoplayer2.ExoPlaybackException; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorActivity; +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.UserAction; +import org.schabi.newpipe.extractor.Info; + +/** + * Handles (exoplayer)errors that occur in the player. + */ +public class PlayerErrorHandler { + // This has to be <= 23 chars on devices running Android 7 or lower (API <= 25) + // or it fails with an IllegalArgumentException + // https://stackoverflow.com/a/54744028 + private static final String TAG = "PlayerErrorHandler"; + + @Nullable + private Toast errorToast; + + @NonNull + private final Context context; + + public PlayerErrorHandler(@NonNull final Context context) { + this.context = context; + } + + public void showPlayerError( + @NonNull final ExoPlaybackException exception, + @NonNull final Info info, + @StringRes final int textResId) { + // Hide existing toast message + if (errorToast != null) { + Log.d(TAG, "Trying to cancel previous player error error toast"); + errorToast.cancel(); + errorToast = null; + } + + if (shouldReportError()) { + try { + reportError(exception, info); + // When a report pops up we need no toast + return; + } catch (final Exception ex) { + Log.w(TAG, "Unable to report error:", ex); + } + } + + Log.d(TAG, "Showing player error toast"); + errorToast = Toast.makeText(context, textResId, Toast.LENGTH_SHORT); + errorToast.show(); + } + + private void reportError(@NonNull final ExoPlaybackException exception, + @NonNull final Info info) { + ErrorActivity.reportError( + context, + new ErrorInfo( + exception, + UserAction.PLAY_STREAM, + "Player error[type=" + exception.type + "] occurred while playing: " + + info.getUrl(), + info + ) + ); + } + + private boolean shouldReportError() { + return PreferenceManager + .getDefaultSharedPreferences(context) + .getBoolean( + context.getString(R.string.report_player_errors_key), + false); + } +} diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 9db147deb..eda0a3ad0 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -89,6 +89,8 @@ @string/never + report_player_errors_key + seekbar_preview_thumbnail_key seekbar_preview_thumbnail_high_quality seekbar_preview_thumbnail_low_quality diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c8fd98ab..89434a2cf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,8 @@ org.xbmc.kore Show \"Play with Kodi\" option Display an option to play a video via Kodi media center + Report player errors + Reports player errors in full detail instead of showing a short-lived toast message (useful for diagnosing problems) Scale thumbnail to 1:1 aspect ratio Scale the video thumbnail shown in the notification from 16:9 to 1:1 aspect ratio (may introduce distortions) First action button diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index f605fbe17..0bd6b47d5 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -89,6 +89,7 @@ android:title="@string/show_play_with_kodi_title" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + +