From e632fab4d0036545cbd1a407dd6268a07dd6bd2b Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sat, 18 Sep 2021 19:47:24 +0200
Subject: [PATCH 01/11] Added option to report player errors
* Added a new setting so that player errors are reported (under Video and Audio > Player)
* Moved the player error logic to separate class specially created for this purpose
---
.../org/schabi/newpipe/player/Player.java | 60 ++++---------
.../playererror/PlayerErrorHandler.java | 86 +++++++++++++++++++
app/src/main/res/values/settings_keys.xml | 2 +
app/src/main/res/values/strings.xml | 2 +
app/src/main/res/xml/video_audio_settings.xml | 9 ++
5 files changed, 116 insertions(+), 43 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java
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" />
+
+
+
Date: Tue, 21 Sep 2021 21:01:10 +0200
Subject: [PATCH 02/11] Added a "Crash the player" debug option
---
.../fragments/detail/VideoDetailFragment.java | 33 +++-
.../detail/VideoDetailPlayerCrasher.java | 165 ++++++++++++++++++
.../main/res/layout/fragment_video_detail.xml | 18 +-
app/src/main/res/values/settings_keys.xml | 1 +
app/src/main/res/values/strings.xml | 3 +
app/src/main/res/xml/debug_settings.xml | 9 +
6 files changed, 226 insertions(+), 3 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 5552cf73f..96aa43e7b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -205,6 +205,9 @@ public final class VideoDetailFragment
private Player player;
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
+ @Nullable
+ private VideoDetailPlayerCrasher videoDetailPlayerCrasher = null;
+
/*//////////////////////////////////////////////////////////////////////////
// Service management
//////////////////////////////////////////////////////////////////////////*/
@@ -594,6 +597,18 @@ public final class VideoDetailFragment
// Init
//////////////////////////////////////////////////////////////////////////*/
+ @Override
+ public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
+ super.onViewCreated(rootView, savedInstanceState);
+
+ if (DEBUG) {
+ this.videoDetailPlayerCrasher = new VideoDetailPlayerCrasher(
+ () -> this.getContext(),
+ () -> this.getLayoutInflater()
+ );
+ }
+ }
+
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
@@ -604,6 +619,18 @@ public final class VideoDetailFragment
binding.detailThumbnailRootLayout.requestFocus();
+ binding.detailControlsPlayWithKodi.setVisibility(
+ KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId)
+ ? View.VISIBLE
+ : View.GONE
+ );
+ binding.detailControlsCrashThePlayer.setVisibility(
+ DEBUG && PreferenceManager.getDefaultSharedPreferences(getContext())
+ .getBoolean(getString(R.string.show_crash_the_player_key), false)
+ ? View.VISIBLE
+ : View.GONE
+ );
+
if (DeviceUtils.isTv(getContext())) {
// remove ripple effects from detail controls
final int transparent = ContextCompat.getColor(requireContext(),
@@ -638,8 +665,10 @@ public final class VideoDetailFragment
binding.detailControlsShare.setOnClickListener(this);
binding.detailControlsOpenInBrowser.setOnClickListener(this);
binding.detailControlsPlayWithKodi.setOnClickListener(this);
- binding.detailControlsPlayWithKodi.setVisibility(KoreUtils.shouldShowPlayWithKodi(
- requireContext(), serviceId) ? View.VISIBLE : View.GONE);
+ if (DEBUG) {
+ binding.detailControlsCrashThePlayer.setOnClickListener(
+ v -> videoDetailPlayerCrasher.onCrashThePlayer(this.player));
+ }
binding.overlayThumbnail.setOnClickListener(this);
binding.overlayThumbnail.setOnLongClickListener(this);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
new file mode 100644
index 000000000..fc2377657
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -0,0 +1,165 @@
+package org.schabi.newpipe.fragments.detail;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.RendererCapabilities;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
+import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
+import org.schabi.newpipe.player.Player;
+import org.schabi.newpipe.util.ThemeHelper;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+public class VideoDetailPlayerCrasher {
+
+ private static final String TAG = "VideoDetPlayerCrasher";
+
+ @NonNull
+ private final Supplier contextSupplier;
+ @NonNull
+ private final Supplier layoutInflaterSupplier;
+
+ public VideoDetailPlayerCrasher(
+ @NonNull final Supplier contextSupplier,
+ @NonNull final Supplier layoutInflaterSupplier
+ ) {
+ this.contextSupplier = contextSupplier;
+ this.layoutInflaterSupplier = layoutInflaterSupplier;
+ }
+
+ private static Map> getExceptionTypes() {
+ final String defaultMsg = "Dummy";
+ final Map> exceptionTypes = new LinkedHashMap<>();
+ exceptionTypes.put(
+ "Source",
+ () -> ExoPlaybackException.createForSource(
+ new IOException(defaultMsg)
+ )
+ );
+ exceptionTypes.put(
+ "Renderer",
+ () -> ExoPlaybackException.createForRenderer(
+ new Exception(defaultMsg),
+ "Dummy renderer",
+ 0,
+ null,
+ RendererCapabilities.FORMAT_HANDLED
+ )
+ );
+ exceptionTypes.put(
+ "Unexpected",
+ () -> ExoPlaybackException.createForUnexpected(
+ new RuntimeException(defaultMsg)
+ )
+ );
+ exceptionTypes.put(
+ "Remote",
+ () -> ExoPlaybackException.createForRemote(defaultMsg)
+ );
+ exceptionTypes.put(
+ "Timeout",
+ () -> ExoPlaybackException.createForTimeout(
+ new TimeoutException(defaultMsg),
+ ExoPlaybackException.TIMEOUT_OPERATION_UNDEFINED
+ )
+ );
+
+ return exceptionTypes;
+ }
+
+ private Context getContext() {
+ return this.contextSupplier.get();
+ }
+
+ private LayoutInflater getLayoutInflater() {
+ return this.layoutInflaterSupplier.get();
+ }
+
+ private Context getThemeWrapperContext() {
+ return new ContextThemeWrapper(
+ getContext(),
+ ThemeHelper.isLightThemeSelected(getContext())
+ ? R.style.LightTheme
+ : R.style.DarkTheme);
+ }
+
+ public void onCrashThePlayer(final Player player) {
+ if (!isPlayerAvailable(player)) {
+ Log.d(TAG, "Player is not available");
+ Toast.makeText(getContext(), "Player is not available", Toast.LENGTH_SHORT)
+ .show();
+
+ return;
+ }
+
+ final Context themeWrapperContext = getThemeWrapperContext();
+
+ final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
+ final RadioGroup radioGroup = SingleChoiceDialogViewBinding.inflate(getLayoutInflater())
+ .list;
+
+ final AlertDialog alertDialog = new AlertDialog.Builder(getThemeWrapperContext())
+ .setTitle("Choose an exception")
+ .setView(radioGroup)
+ .setCancelable(true)
+ .setNegativeButton(R.string.cancel, null)
+ .create();
+
+ for (final Map.Entry> entry
+ : getExceptionTypes().entrySet()) {
+ final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater).getRoot();
+ radioButton.setText(entry.getKey());
+ radioButton.setChecked(false);
+ radioButton.setLayoutParams(
+ new RadioGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ );
+ radioButton.setOnClickListener(v -> {
+ tryCrashPlayerWith(player, entry.getValue().get());
+ if (alertDialog != null) {
+ alertDialog.cancel();
+ }
+ });
+ radioGroup.addView(radioButton);
+ }
+
+ alertDialog.show();
+ }
+
+ private void tryCrashPlayerWith(
+ @NonNull final Player player,
+ @NonNull final ExoPlaybackException exception
+ ) {
+ Log.d(TAG, "Crashing the player using player.onPlayerError(ex)");
+ try {
+ player.onPlayerError(exception);
+ } catch (final Exception exPlayer) {
+ Log.e(TAG,
+ "Run into an exception while crashing the player:",
+ exPlayer);
+ }
+ }
+
+ private boolean isPlayerAvailable(final Player player) {
+ return player != null;
+ }
+}
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index 3d9daa156..b7d97cac5 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -213,7 +213,7 @@
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:visibility="gone"
- tools:visibility="visible" />
+ tools:visibility="gone" />
+
+
disable_media_tunneling_key
crash_the_app_key
show_image_indicators_key
+ show_crash_the_player_key
theme
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 89434a2cf..a8bb4c788 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,6 +52,7 @@
org.xbmc.kore
Show \"Play with Kodi\" option
Display an option to play a video via Kodi media center
+ Crash the player
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
@@ -475,6 +476,8 @@
Show image indicators
Show Picasso colored ribbons on top of images indicating their source: red for network, blue for disk and green for memory
Crash the app
+ Show \"crash the player\"
+ Shows a crash option when using the player
Import
Import from
diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml
index 22abebcae..df1559c37 100644
--- a/app/src/main/res/xml/debug_settings.xml
+++ b/app/src/main/res/xml/debug_settings.xml
@@ -54,4 +54,13 @@
android:title="@string/crash_the_app"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
+
+
From e5c00a7ef4957e2a605d384170316d5ab0c2fc0e Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 21 Sep 2021 21:18:54 +0200
Subject: [PATCH 03/11] Added some doc
---
.../newpipe/fragments/detail/VideoDetailPlayerCrasher.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
index fc2377657..d8a4a99d1 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -145,6 +145,12 @@ public class VideoDetailPlayerCrasher {
alertDialog.show();
}
+ /**
+ * Note that this method does not crash the underlying exoplayer directly (it's not possible).
+ * It simply supplies a Exception to {@link Player#onPlayerError(ExoPlaybackException)}.
+ * @param player
+ * @param exception
+ */
private void tryCrashPlayerWith(
@NonNull final Player player,
@NonNull final ExoPlaybackException exception
From c3f1478fdec874f9b71e7ee513c340109dc7b8b1 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 21 Sep 2021 21:33:55 +0200
Subject: [PATCH 04/11] Support for debug option "Crash the player" on TVs
---
.../fragment_video_detail.xml | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml
index fe12776e1..98b24f511 100644
--- a/app/src/main/res/layout-large-land/fragment_video_detail.xml
+++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml
@@ -225,7 +225,7 @@
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:visibility="gone"
- tools:visibility="visible" />
+ tools:visibility="gone" />
+
+
Date: Tue, 21 Sep 2021 21:36:05 +0200
Subject: [PATCH 05/11] PlayerErrorHandler refactor + docs
---
.../schabi/newpipe/player/playererror/PlayerErrorHandler.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
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
index 8e9ad9d15..4c8d9bc5f 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java
@@ -39,7 +39,8 @@ public class PlayerErrorHandler {
public void showPlayerError(
@NonNull final ExoPlaybackException exception,
@NonNull final Info info,
- @StringRes final int textResId) {
+ @StringRes final int textResId
+ ) {
// Hide existing toast message
if (errorToast != null) {
Log.d(TAG, "Trying to cancel previous player error error toast");
@@ -54,6 +55,7 @@ public class PlayerErrorHandler {
return;
} catch (final Exception ex) {
Log.w(TAG, "Unable to report error:", ex);
+ // This will show the toast as fallback
}
}
From f18ee8e83d048ac3fb062c120387c546e47e887a Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 1 Oct 2021 18:44:47 +0200
Subject: [PATCH 06/11] Added a bit more documentation
---
.../fragments/detail/VideoDetailPlayerCrasher.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
index d8a4a99d1..a7c382f33 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -27,8 +27,14 @@ import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
+/**
+ * Outsourced logic for crashing the player in the {@link VideoDetailFragment}.
+ */
public class VideoDetailPlayerCrasher {
+ // 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 = "VideoDetPlayerCrasher";
@NonNull
@@ -109,6 +115,8 @@ public class VideoDetailPlayerCrasher {
return;
}
+ // -- Build the dialog/UI --
+
final Context themeWrapperContext = getThemeWrapperContext();
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
From aa28a85747008976a9ca7ae3d976af036858b9cd Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 24 Oct 2021 21:09:47 +0200
Subject: [PATCH 07/11] Added a workaround for not serializable exceptions
---
.../error/EnsureExceptionSerializable.java | 103 ++++++++++++++++++
.../schabi/newpipe/error/ErrorActivity.java | 10 ++
.../playererror/PlayerErrorHandler.java | 3 +-
3 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/error/EnsureExceptionSerializable.java
diff --git a/app/src/main/java/org/schabi/newpipe/error/EnsureExceptionSerializable.java b/app/src/main/java/org/schabi/newpipe/error/EnsureExceptionSerializable.java
new file mode 100644
index 000000000..db94de5e5
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/error/EnsureExceptionSerializable.java
@@ -0,0 +1,103 @@
+package org.schabi.newpipe.error;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Ensures that a Exception is serializable.
+ * This is
+ */
+public final class EnsureExceptionSerializable {
+ private static final String TAG = "EnsureExSerializable";
+
+ private EnsureExceptionSerializable() {
+ // No instance
+ }
+
+ /**
+ * Ensures that an exception is serializable.
+ *
+ * If that is not the case a {@link WorkaroundNotSerializableException} is created.
+ *
+ * @param exception
+ * @return if an exception is not serializable a new {@link WorkaroundNotSerializableException}
+ * otherwise the exception from the parameter
+ */
+ public static Exception ensureSerializable(@NonNull final Exception exception) {
+ return checkIfSerializable(exception)
+ ? exception
+ : WorkaroundNotSerializableException.create(exception);
+ }
+
+ public static boolean checkIfSerializable(@NonNull final Exception exception) {
+ try {
+ // Check by creating a new ObjectOutputStream which does the serialization
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos)
+ ) {
+ oos.writeObject(exception);
+ oos.flush();
+
+ bos.toByteArray();
+ }
+
+ return true;
+ } catch (final IOException ex) {
+ Log.d(TAG, "Exception is not serializable", ex);
+ return false;
+ }
+ }
+
+ public static class WorkaroundNotSerializableException extends Exception {
+ protected WorkaroundNotSerializableException(
+ final Throwable notSerializableException,
+ final Throwable cause) {
+ super(notSerializableException.toString(), cause);
+ setStackTrace(notSerializableException.getStackTrace());
+ }
+
+ protected WorkaroundNotSerializableException(final Throwable notSerializableException) {
+ super(notSerializableException.toString());
+ setStackTrace(notSerializableException.getStackTrace());
+ }
+
+ public static WorkaroundNotSerializableException create(
+ @NonNull final Exception notSerializableException
+ ) {
+ // Build a list of the exception + all causes
+ final List throwableList = new ArrayList<>();
+
+ int pos = 0;
+ Throwable throwableToProcess = notSerializableException;
+
+ while (throwableToProcess != null) {
+ throwableList.add(throwableToProcess);
+
+ pos++;
+ throwableToProcess = throwableToProcess.getCause();
+ }
+
+ // Reverse list so that it starts with the last one
+ Collections.reverse(throwableList);
+
+ // Build exception stack
+ WorkaroundNotSerializableException cause = null;
+ for (final Throwable t : throwableList) {
+ cause = cause == null
+ ? new WorkaroundNotSerializableException(t)
+ : new WorkaroundNotSerializableException(t, cause);
+ }
+
+ return cause;
+ }
+
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
index c0d88c8ec..db3a92d4f 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
@@ -77,6 +77,16 @@ public class ErrorActivity extends AppCompatActivity {
private ActivityErrorBinding activityErrorBinding;
+ /**
+ * Reports a new error by starting a new activity.
+ *
+ * Ensure that the data within errorInfo is serializable otherwise
+ * an exception will be thrown!
+ * {@link EnsureExceptionSerializable} might help.
+ *
+ * @param context
+ * @param errorInfo
+ */
public static void reportError(final Context context, final ErrorInfo errorInfo) {
final Intent intent = new Intent(context, ErrorActivity.class);
intent.putExtra(ERROR_INFO, errorInfo);
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
index 4c8d9bc5f..626200ae1 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playererror/PlayerErrorHandler.java
@@ -12,6 +12,7 @@ import androidx.preference.PreferenceManager;
import com.google.android.exoplayer2.ExoPlaybackException;
import org.schabi.newpipe.R;
+import org.schabi.newpipe.error.EnsureExceptionSerializable;
import org.schabi.newpipe.error.ErrorActivity;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
@@ -69,7 +70,7 @@ public class PlayerErrorHandler {
ErrorActivity.reportError(
context,
new ErrorInfo(
- exception,
+ EnsureExceptionSerializable.ensureSerializable(exception),
UserAction.PLAY_STREAM,
"Player error[type=" + exception.type + "] occurred while playing: "
+ info.getUrl(),
From 0d51eefbb9385883d2ebd3739fad38e72db26866 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 23 Nov 2021 20:11:36 +0100
Subject: [PATCH 08/11] Refactoring
---
.../fragments/detail/VideoDetailFragment.java | 13 +----
.../detail/VideoDetailPlayerCrasher.java | 50 +++++++------------
2 files changed, 21 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 96aa43e7b..b54a04d6f 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -205,9 +205,6 @@ public final class VideoDetailFragment
private Player player;
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
- @Nullable
- private VideoDetailPlayerCrasher videoDetailPlayerCrasher = null;
-
/*//////////////////////////////////////////////////////////////////////////
// Service management
//////////////////////////////////////////////////////////////////////////*/
@@ -600,13 +597,6 @@ public final class VideoDetailFragment
@Override
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
-
- if (DEBUG) {
- this.videoDetailPlayerCrasher = new VideoDetailPlayerCrasher(
- () -> this.getContext(),
- () -> this.getLayoutInflater()
- );
- }
}
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
@@ -667,7 +657,8 @@ public final class VideoDetailFragment
binding.detailControlsPlayWithKodi.setOnClickListener(this);
if (DEBUG) {
binding.detailControlsCrashThePlayer.setOnClickListener(
- v -> videoDetailPlayerCrasher.onCrashThePlayer(this.player));
+ v -> VideoDetailPlayerCrasher.onCrashThePlayer(this.player, getLayoutInflater())
+ );
}
binding.overlayThumbnail.setOnClickListener(this);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
index a7c382f33..773a77f50 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -22,6 +22,7 @@ import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
@@ -30,24 +31,18 @@ import java.util.function.Supplier;
/**
* Outsourced logic for crashing the player in the {@link VideoDetailFragment}.
*/
-public class VideoDetailPlayerCrasher {
+public final class VideoDetailPlayerCrasher {
// 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 = "VideoDetPlayerCrasher";
- @NonNull
- private final Supplier contextSupplier;
- @NonNull
- private final Supplier layoutInflaterSupplier;
+ private static final Map> AVAILABLE_EXCEPTION_TYPES =
+ getExceptionTypes();
- public VideoDetailPlayerCrasher(
- @NonNull final Supplier contextSupplier,
- @NonNull final Supplier layoutInflaterSupplier
- ) {
- this.contextSupplier = contextSupplier;
- this.layoutInflaterSupplier = layoutInflaterSupplier;
+ private VideoDetailPlayerCrasher() {
+ // No impls
}
private static Map> getExceptionTypes() {
@@ -87,29 +82,22 @@ public class VideoDetailPlayerCrasher {
)
);
- return exceptionTypes;
+ return Collections.unmodifiableMap(exceptionTypes);
}
- private Context getContext() {
- return this.contextSupplier.get();
- }
-
- private LayoutInflater getLayoutInflater() {
- return this.layoutInflaterSupplier.get();
- }
-
- private Context getThemeWrapperContext() {
+ private static Context getThemeWrapperContext(final Context context) {
return new ContextThemeWrapper(
- getContext(),
- ThemeHelper.isLightThemeSelected(getContext())
+ context,
+ ThemeHelper.isLightThemeSelected(context)
? R.style.LightTheme
: R.style.DarkTheme);
}
- public void onCrashThePlayer(final Player player) {
+ public static void onCrashThePlayer(final Player player, final LayoutInflater layoutInflater) {
+ final Context context = player.getContext();
if (!isPlayerAvailable(player)) {
Log.d(TAG, "Player is not available");
- Toast.makeText(getContext(), "Player is not available", Toast.LENGTH_SHORT)
+ Toast.makeText(context, "Player is not available", Toast.LENGTH_SHORT)
.show();
return;
@@ -117,13 +105,13 @@ public class VideoDetailPlayerCrasher {
// -- Build the dialog/UI --
- final Context themeWrapperContext = getThemeWrapperContext();
+ final Context themeWrapperContext = getThemeWrapperContext(context);
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
- final RadioGroup radioGroup = SingleChoiceDialogViewBinding.inflate(getLayoutInflater())
+ final RadioGroup radioGroup = SingleChoiceDialogViewBinding.inflate(layoutInflater)
.list;
- final AlertDialog alertDialog = new AlertDialog.Builder(getThemeWrapperContext())
+ final AlertDialog alertDialog = new AlertDialog.Builder(getThemeWrapperContext(context))
.setTitle("Choose an exception")
.setView(radioGroup)
.setCancelable(true)
@@ -131,7 +119,7 @@ public class VideoDetailPlayerCrasher {
.create();
for (final Map.Entry> entry
- : getExceptionTypes().entrySet()) {
+ : AVAILABLE_EXCEPTION_TYPES.entrySet()) {
final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater).getRoot();
radioButton.setText(entry.getKey());
radioButton.setChecked(false);
@@ -159,7 +147,7 @@ public class VideoDetailPlayerCrasher {
* @param player
* @param exception
*/
- private void tryCrashPlayerWith(
+ private static void tryCrashPlayerWith(
@NonNull final Player player,
@NonNull final ExoPlaybackException exception
) {
@@ -173,7 +161,7 @@ public class VideoDetailPlayerCrasher {
}
}
- private boolean isPlayerAvailable(final Player player) {
+ private static boolean isPlayerAvailable(final Player player) {
return player != null;
}
}
From 6992b2c308c18bcee526c2f3f0136abb84378945 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 23 Nov 2021 20:16:01 +0100
Subject: [PATCH 09/11] Moved report_player_errors to debug
---
app/src/main/res/xml/debug_settings.xml | 8 ++++++++
app/src/main/res/xml/video_audio_settings.xml | 8 --------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml
index df1559c37..5e2cc28ed 100644
--- a/app/src/main/res/xml/debug_settings.xml
+++ b/app/src/main/res/xml/debug_settings.xml
@@ -49,6 +49,14 @@
android:title="@string/show_image_indicators_title"
app:iconSpaceReserved="false" />
+
+
-
-
Date: Tue, 23 Nov 2021 20:21:59 +0100
Subject: [PATCH 10/11] Fixed build
---
.../fragments/detail/VideoDetailPlayerCrasher.java | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
index 773a77f50..9ddad9855 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -12,8 +12,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
+import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
-import com.google.android.exoplayer2.RendererCapabilities;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
@@ -25,7 +25,6 @@ import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
/**
@@ -61,7 +60,7 @@ public final class VideoDetailPlayerCrasher {
"Dummy renderer",
0,
null,
- RendererCapabilities.FORMAT_HANDLED
+ C.FORMAT_HANDLED
)
);
exceptionTypes.put(
@@ -74,13 +73,6 @@ public final class VideoDetailPlayerCrasher {
"Remote",
() -> ExoPlaybackException.createForRemote(defaultMsg)
);
- exceptionTypes.put(
- "Timeout",
- () -> ExoPlaybackException.createForTimeout(
- new TimeoutException(defaultMsg),
- ExoPlaybackException.TIMEOUT_OPERATION_UNDEFINED
- )
- );
return Collections.unmodifiableMap(exceptionTypes);
}
From 4c8dca53009cd6f8571f41f8a467117102623a4c Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Sun, 28 Nov 2021 13:42:26 +0100
Subject: [PATCH 11/11] Fixed NPE + Problems with context
---
.../fragments/detail/VideoDetailFragment.java | 5 ++++-
.../fragments/detail/VideoDetailPlayerCrasher.java | 14 +++++++-------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index b54a04d6f..8c6e01537 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -657,7 +657,10 @@ public final class VideoDetailFragment
binding.detailControlsPlayWithKodi.setOnClickListener(this);
if (DEBUG) {
binding.detailControlsCrashThePlayer.setOnClickListener(
- v -> VideoDetailPlayerCrasher.onCrashThePlayer(this.player, getLayoutInflater())
+ v -> VideoDetailPlayerCrasher.onCrashThePlayer(
+ this.getContext(),
+ this.player,
+ getLayoutInflater())
);
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
index 9ddad9855..9309a8a49 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java
@@ -10,6 +10,7 @@ import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.exoplayer2.C;
@@ -85,9 +86,12 @@ public final class VideoDetailPlayerCrasher {
: R.style.DarkTheme);
}
- public static void onCrashThePlayer(final Player player, final LayoutInflater layoutInflater) {
- final Context context = player.getContext();
- if (!isPlayerAvailable(player)) {
+ public static void onCrashThePlayer(
+ @NonNull final Context context,
+ @Nullable final Player player,
+ @NonNull final LayoutInflater layoutInflater
+ ) {
+ if (player == null) {
Log.d(TAG, "Player is not available");
Toast.makeText(context, "Player is not available", Toast.LENGTH_SHORT)
.show();
@@ -152,8 +156,4 @@ public final class VideoDetailPlayerCrasher {
exPlayer);
}
}
-
- private static boolean isPlayerAvailable(final Player player) {
- return player != null;
- }
}