Added a "Crash the player" debug option

This commit is contained in:
litetex 2021-09-21 21:01:10 +02:00
parent e632fab4d0
commit 769791af7a
6 changed files with 226 additions and 3 deletions

View File

@ -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);

View File

@ -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<Context> contextSupplier;
@NonNull
private final Supplier<LayoutInflater> layoutInflaterSupplier;
public VideoDetailPlayerCrasher(
@NonNull final Supplier<Context> contextSupplier,
@NonNull final Supplier<LayoutInflater> layoutInflaterSupplier
) {
this.contextSupplier = contextSupplier;
this.layoutInflaterSupplier = layoutInflaterSupplier;
}
private static Map<String, Supplier<ExoPlaybackException>> getExceptionTypes() {
final String defaultMsg = "Dummy";
final Map<String, Supplier<ExoPlaybackException>> 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<String, Supplier<ExoPlaybackException>> 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;
}
}

View File

@ -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" />
<!--HIDING ROOT-->
<LinearLayout
@ -547,6 +547,22 @@
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="@drawable/ic_cast" />
<TextView
android:id="@+id/detail_controls_crash_the_player"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/crash_the_player"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/crash_the_player"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="@drawable/ic_bug_report" />
</LinearLayout>
<View

View File

@ -190,6 +190,7 @@
<string name="disable_media_tunneling_key" translatable="false">disable_media_tunneling_key</string>
<string name="crash_the_app_key" translatable="false">crash_the_app_key</string>
<string name="show_image_indicators_key" translatable="false">show_image_indicators_key</string>
<string name="show_crash_the_player_key" translatable="false">show_crash_the_player_key</string>
<!-- THEMES -->
<string name="theme_key" translatable="false">theme</string>

View File

@ -52,6 +52,7 @@
<string name="kore_package" translatable="false">org.xbmc.kore</string>
<string name="show_play_with_kodi_title">Show \"Play with Kodi\" option</string>
<string name="show_play_with_kodi_summary">Display an option to play a video via Kodi media center</string>
<string name="crash_the_player">Crash the player</string>
<string name="report_player_errors_title">Report player errors</string>
<string name="report_player_errors_summary">Reports player errors in full detail instead of showing a short-lived toast message (useful for diagnosing problems)</string>
<string name="notification_scale_to_square_image_title">Scale thumbnail to 1:1 aspect ratio</string>
@ -475,6 +476,8 @@
<string name="show_image_indicators_title">Show image indicators</string>
<string name="show_image_indicators_summary">Show Picasso colored ribbons on top of images indicating their source: red for network, blue for disk and green for memory</string>
<string name="crash_the_app">Crash the app</string>
<string name="show_crash_the_player_title">Show \"crash the player\"</string>
<string name="show_crash_the_player_summary">Shows a crash option when using the player</string>
<!-- Subscriptions import/export -->
<string name="import_title">Import</string>
<string name="import_from">Import from</string>

View File

@ -54,4 +54,13 @@
android:title="@string/crash_the_app"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:defaultValue="false"
android:key="@string/show_crash_the_player_key"
android:summary="@string/show_crash_the_player_summary"
android:title="@string/show_crash_the_player_title"
app:iconSpaceReserved="false" />
</PreferenceScreen>