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" />
+
+
+