Merge pull request #7482 from TeamNewPipe/unify-error-reporting
Unify error reporting and add error notification
This commit is contained in:
commit
4058277b7a
|
@ -5,6 +5,9 @@ import android.os.Bundle;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.util.PicassoHelper;
|
import org.schabi.newpipe.util.PicassoHelper;
|
||||||
|
|
||||||
import leakcanary.LeakCanary;
|
import leakcanary.LeakCanary;
|
||||||
|
@ -20,10 +23,16 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||||
= findPreference(getString(R.string.show_image_indicators_key));
|
= findPreference(getString(R.string.show_image_indicators_key));
|
||||||
final Preference crashTheAppPreference
|
final Preference crashTheAppPreference
|
||||||
= findPreference(getString(R.string.crash_the_app_key));
|
= findPreference(getString(R.string.crash_the_app_key));
|
||||||
|
final Preference showErrorSnackbarPreference
|
||||||
|
= findPreference(getString(R.string.show_error_snackbar_key));
|
||||||
|
final Preference createErrorNotificationPreference
|
||||||
|
= findPreference(getString(R.string.create_error_notification_key));
|
||||||
|
|
||||||
assert showMemoryLeaksPreference != null;
|
assert showMemoryLeaksPreference != null;
|
||||||
assert showImageIndicatorsPreference != null;
|
assert showImageIndicatorsPreference != null;
|
||||||
assert crashTheAppPreference != null;
|
assert crashTheAppPreference != null;
|
||||||
|
assert showErrorSnackbarPreference != null;
|
||||||
|
assert createErrorNotificationPreference != null;
|
||||||
|
|
||||||
showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
|
showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(LeakCanary.INSTANCE.newLeakDisplayActivityIntent());
|
startActivity(LeakCanary.INSTANCE.newLeakDisplayActivityIntent());
|
||||||
|
@ -38,5 +47,17 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||||
crashTheAppPreference.setOnPreferenceClickListener(preference -> {
|
crashTheAppPreference.setOnPreferenceClickListener(preference -> {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
showErrorSnackbarPreference.setOnPreferenceClickListener(preference -> {
|
||||||
|
ErrorUtil.showUiErrorSnackbar(DebugSettingsFragment.this,
|
||||||
|
"Dummy", new RuntimeException("Dummy"));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
createErrorNotificationPreference.setOnPreferenceClickListener(preference -> {
|
||||||
|
ErrorUtil.createNotification(requireContext(),
|
||||||
|
new ErrorInfo(new RuntimeException("Dummy"), UserAction.UI_ERROR, "Dummy"));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ import org.acra.ACRA;
|
||||||
import org.acra.config.ACRAConfigurationException;
|
import org.acra.config.ACRAConfigurationException;
|
||||||
import org.acra.config.CoreConfiguration;
|
import org.acra.config.CoreConfiguration;
|
||||||
import org.acra.config.CoreConfigurationBuilder;
|
import org.acra.config.CoreConfigurationBuilder;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
@ -217,7 +217,7 @@ public class App extends MultiDexApplication {
|
||||||
ACRA.init(this, acraConfig);
|
ACRA.init(this, acraConfig);
|
||||||
} catch (final ACRAConfigurationException exception) {
|
} catch (final ACRAConfigurationException exception) {
|
||||||
exception.printStackTrace();
|
exception.printStackTrace();
|
||||||
ErrorActivity.reportError(this, new ErrorInfo(exception,
|
ErrorUtil.openActivity(this, new ErrorInfo(exception,
|
||||||
UserAction.SOMETHING_ELSE, "Could not initialize ACRA crash report"));
|
UserAction.SOMETHING_ELSE, "Could not initialize ACRA crash report"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,28 +227,35 @@ public class App extends MultiDexApplication {
|
||||||
// the main and update channels
|
// the main and update channels
|
||||||
final NotificationChannelCompat mainChannel = new NotificationChannelCompat
|
final NotificationChannelCompat mainChannel = new NotificationChannelCompat
|
||||||
.Builder(getString(R.string.notification_channel_id),
|
.Builder(getString(R.string.notification_channel_id),
|
||||||
NotificationManagerCompat.IMPORTANCE_LOW)
|
NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
.setName(getString(R.string.notification_channel_name))
|
.setName(getString(R.string.notification_channel_name))
|
||||||
.setDescription(getString(R.string.notification_channel_description))
|
.setDescription(getString(R.string.notification_channel_description))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final NotificationChannelCompat appUpdateChannel = new NotificationChannelCompat
|
final NotificationChannelCompat appUpdateChannel = new NotificationChannelCompat
|
||||||
.Builder(getString(R.string.app_update_notification_channel_id),
|
.Builder(getString(R.string.app_update_notification_channel_id),
|
||||||
NotificationManagerCompat.IMPORTANCE_LOW)
|
NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
.setName(getString(R.string.app_update_notification_channel_name))
|
.setName(getString(R.string.app_update_notification_channel_name))
|
||||||
.setDescription(getString(R.string.app_update_notification_channel_description))
|
.setDescription(getString(R.string.app_update_notification_channel_description))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final NotificationChannelCompat hashChannel = new NotificationChannelCompat
|
final NotificationChannelCompat hashChannel = new NotificationChannelCompat
|
||||||
.Builder(getString(R.string.hash_channel_id),
|
.Builder(getString(R.string.hash_channel_id),
|
||||||
NotificationManagerCompat.IMPORTANCE_HIGH)
|
NotificationManagerCompat.IMPORTANCE_HIGH)
|
||||||
.setName(getString(R.string.hash_channel_name))
|
.setName(getString(R.string.hash_channel_name))
|
||||||
.setDescription(getString(R.string.hash_channel_description))
|
.setDescription(getString(R.string.hash_channel_description))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
final NotificationChannelCompat errorReportChannel = new NotificationChannelCompat
|
||||||
|
.Builder(getString(R.string.error_report_channel_id),
|
||||||
|
NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
|
.setName(getString(R.string.error_report_channel_name))
|
||||||
|
.setDescription(getString(R.string.error_report_channel_description))
|
||||||
|
.build();
|
||||||
|
|
||||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||||
notificationManager.createNotificationChannelsCompat(Arrays.asList(mainChannel,
|
notificationManager.createNotificationChannelsCompat(Arrays.asList(mainChannel,
|
||||||
appUpdateChannel, hashChannel));
|
appUpdateChannel, hashChannel, errorReportChannel));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDisposedRxExceptionsReported() {
|
protected boolean isDisposedRxExceptionsReported() {
|
||||||
|
|
|
@ -21,8 +21,8 @@ import com.grack.nanojson.JsonObject;
|
||||||
import com.grack.nanojson.JsonParser;
|
import com.grack.nanojson.JsonParser;
|
||||||
import com.grack.nanojson.JsonParserException;
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.downloader.Response;
|
import org.schabi.newpipe.extractor.downloader.Response;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
|
@ -64,7 +64,7 @@ public final class CheckForNewAppVersion extends IntentService {
|
||||||
signatures = PackageInfoCompat.getSignatures(application.getPackageManager(),
|
signatures = PackageInfoCompat.getSignatures(application.getPackageManager(),
|
||||||
application.getPackageName());
|
application.getPackageName());
|
||||||
} catch (final PackageManager.NameNotFoundException e) {
|
} catch (final PackageManager.NameNotFoundException e) {
|
||||||
ErrorActivity.reportError(application, new ErrorInfo(e,
|
ErrorUtil.createNotification(application, new ErrorInfo(e,
|
||||||
UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not find package info"));
|
UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not find package info"));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ public final class CheckForNewAppVersion extends IntentService {
|
||||||
final CertificateFactory cf = CertificateFactory.getInstance("X509");
|
final CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||||||
c = (X509Certificate) cf.generateCertificate(input);
|
c = (X509Certificate) cf.generateCertificate(input);
|
||||||
} catch (final CertificateException e) {
|
} catch (final CertificateException e) {
|
||||||
ErrorActivity.reportError(application, new ErrorInfo(e,
|
ErrorUtil.createNotification(application, new ErrorInfo(e,
|
||||||
UserAction.CHECK_FOR_NEW_APP_VERSION, "Certificate error"));
|
UserAction.CHECK_FOR_NEW_APP_VERSION, "Certificate error"));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ public final class CheckForNewAppVersion extends IntentService {
|
||||||
final byte[] publicKey = md.digest(c.getEncoded());
|
final byte[] publicKey = md.digest(c.getEncoded());
|
||||||
return byte2HexFormatted(publicKey);
|
return byte2HexFormatted(publicKey);
|
||||||
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
|
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
|
||||||
ErrorActivity.reportError(application, new ErrorInfo(e,
|
ErrorUtil.createNotification(application, new ErrorInfo(e,
|
||||||
UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not retrieve SHA1 key"));
|
UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not retrieve SHA1 key"));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ import org.schabi.newpipe.databinding.DrawerHeaderBinding;
|
||||||
import org.schabi.newpipe.databinding.DrawerLayoutBinding;
|
import org.schabi.newpipe.databinding.DrawerLayoutBinding;
|
||||||
import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding;
|
import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding;
|
||||||
import org.schabi.newpipe.databinding.ToolbarLayoutBinding;
|
import org.schabi.newpipe.databinding.ToolbarLayoutBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
@ -157,7 +157,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
try {
|
try {
|
||||||
setupDrawer();
|
setupDrawer();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Setting up drawer", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Setting up drawer", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DeviceUtils.isTv(this)) {
|
if (DeviceUtils.isTv(this)) {
|
||||||
|
@ -214,7 +214,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
/**
|
/**
|
||||||
* Builds the drawer menu for the current service.
|
* Builds the drawer menu for the current service.
|
||||||
*
|
*
|
||||||
* @throws ExtractionException
|
* @throws ExtractionException if the service didn't provide available kiosks
|
||||||
*/
|
*/
|
||||||
private void addDrawerMenuForCurrentService() throws ExtractionException {
|
private void addDrawerMenuForCurrentService() throws ExtractionException {
|
||||||
//Tabs
|
//Tabs
|
||||||
|
@ -266,7 +266,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
try {
|
try {
|
||||||
tabSelected(item);
|
tabSelected(item);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Selecting main page tab", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Selecting main page tab", e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.menu_options_about_group:
|
case R.id.menu_options_about_group:
|
||||||
|
@ -372,7 +372,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
try {
|
try {
|
||||||
addDrawerMenuForCurrentService();
|
addDrawerMenuForCurrentService();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Showing main page tabs", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Showing main page tabs", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
drawerHeaderBinding.drawerHeaderActionButton.setContentDescription(
|
drawerHeaderBinding.drawerHeaderActionButton.setContentDescription(
|
||||||
getString(R.string.drawer_header_description) + selectedServiceName);
|
getString(R.string.drawer_header_description) + selectedServiceName);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Setting up service toggle", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Setting up service toggle", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
final SharedPreferences sharedPreferences
|
final SharedPreferences sharedPreferences
|
||||||
|
@ -785,7 +785,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
|
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Handling intent", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Handling intent", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@ import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
||||||
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.Info;
|
import org.schabi.newpipe.extractor.Info;
|
||||||
|
@ -231,7 +231,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
|
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
|
||||||
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(context, errorInfo);
|
ErrorUtil.createNotification(context, errorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context instanceof RouterActivity) {
|
if (context instanceof RouterActivity) {
|
||||||
|
|
|
@ -41,8 +41,8 @@ import com.nononsenseapps.filepicker.Utils;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.DownloadDialogBinding;
|
import org.schabi.newpipe.databinding.DownloadDialogBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
@ -402,7 +402,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
== R.id.video_button) {
|
== R.id.video_button) {
|
||||||
setupVideoSpinner();
|
setupVideoSpinner();
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorActivity.reportErrorInSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading video stream size",
|
"Downloading video stream size",
|
||||||
currentInfo.getServiceId()))));
|
currentInfo.getServiceId()))));
|
||||||
|
@ -412,7 +412,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
== R.id.audio_button) {
|
== R.id.audio_button) {
|
||||||
setupAudioSpinner();
|
setupAudioSpinner();
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorActivity.reportErrorInSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading audio stream size",
|
"Downloading audio stream size",
|
||||||
currentInfo.getServiceId()))));
|
currentInfo.getServiceId()))));
|
||||||
|
@ -422,7 +422,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
== R.id.subtitle_button) {
|
== R.id.subtitle_button) {
|
||||||
setupSubtitleSpinner();
|
setupSubtitleSpinner();
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorActivity.reportErrorInSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading subtitle stream size",
|
"Downloading subtitle stream size",
|
||||||
currentInfo.getServiceId()))));
|
currentInfo.getServiceId()))));
|
||||||
|
@ -799,7 +799,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
mainStorage.getTag());
|
mainStorage.getTag());
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportErrorInSnackbar(this,
|
ErrorUtil.createNotification(requireContext(),
|
||||||
new ErrorInfo(e, UserAction.DOWNLOAD_FAILED, "Getting storage"));
|
new ErrorInfo(e, UserAction.DOWNLOAD_FAILED, "Getting storage"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,11 @@ public class AcraReportSender implements ReportSender {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(@NonNull final Context context, @NonNull final CrashReportData report) {
|
public void send(@NonNull final Context context, @NonNull final CrashReportData report) {
|
||||||
ErrorActivity.reportError(context, new ErrorInfo(
|
ErrorUtil.openActivity(context, new ErrorInfo(
|
||||||
new String[]{report.getString(ReportField.STACK_TRACE)},
|
new String[]{report.getString(ReportField.STACK_TRACE)},
|
||||||
UserAction.UI_ERROR,
|
UserAction.UI_ERROR,
|
||||||
ErrorInfo.SERVICE_NONE,
|
ErrorInfo.SERVICE_NONE,
|
||||||
"ACRA report",
|
"ACRA report",
|
||||||
R.string.app_ui_crash,
|
R.string.app_ui_crash));
|
||||||
null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
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.
|
|
||||||
* <br/>
|
|
||||||
* 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<Throwable> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,10 @@
|
||||||
package org.schabi.newpipe.error;
|
package org.schabi.newpipe.error;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -11,15 +12,12 @@ import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
import com.grack.nanojson.JsonWriter;
|
import com.grack.nanojson.JsonWriter;
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
|
@ -27,15 +25,13 @@ import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.ActivityErrorBinding;
|
import org.schabi.newpipe.databinding.ActivityErrorBinding;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 24.10.15.
|
* Created by Christian Schabesberger on 24.10.15.
|
||||||
*
|
*
|
||||||
|
@ -56,6 +52,10 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This activity is used to show error details and allow reporting them in various ways. Use {@link
|
||||||
|
* ErrorUtil#openActivity(Context, ErrorInfo)} to correctly open this activity.
|
||||||
|
*/
|
||||||
public class ErrorActivity extends AppCompatActivity {
|
public class ErrorActivity extends AppCompatActivity {
|
||||||
// LOG TAGS
|
// LOG TAGS
|
||||||
public static final String TAG = ErrorActivity.class.toString();
|
public static final String TAG = ErrorActivity.class.toString();
|
||||||
|
@ -77,67 +77,6 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ActivityErrorBinding activityErrorBinding;
|
private ActivityErrorBinding activityErrorBinding;
|
||||||
|
|
||||||
/**
|
|
||||||
* Reports a new error by starting a new activity.
|
|
||||||
* <br/>
|
|
||||||
* Ensure that the data within errorInfo is serializable otherwise
|
|
||||||
* an exception will be thrown!<br/>
|
|
||||||
* {@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);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reportErrorInSnackbar(final Context context, final ErrorInfo errorInfo) {
|
|
||||||
final View rootView = context instanceof Activity
|
|
||||||
? ((Activity) context).findViewById(android.R.id.content) : null;
|
|
||||||
reportErrorInSnackbar(context, rootView, errorInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reportErrorInSnackbar(final Fragment fragment, final ErrorInfo errorInfo) {
|
|
||||||
View rootView = fragment.getView();
|
|
||||||
if (rootView == null && fragment.getActivity() != null) {
|
|
||||||
rootView = fragment.getActivity().findViewById(android.R.id.content);
|
|
||||||
}
|
|
||||||
reportErrorInSnackbar(fragment.requireContext(), rootView, errorInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reportUiErrorInSnackbar(final Context context,
|
|
||||||
final String request,
|
|
||||||
final Throwable throwable) {
|
|
||||||
reportErrorInSnackbar(context, new ErrorInfo(throwable, UserAction.UI_ERROR, request));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reportUiErrorInSnackbar(final Fragment fragment,
|
|
||||||
final String request,
|
|
||||||
final Throwable throwable) {
|
|
||||||
reportErrorInSnackbar(fragment, new ErrorInfo(throwable, UserAction.UI_ERROR, request));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utils
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private static void reportErrorInSnackbar(final Context context,
|
|
||||||
@Nullable final View rootView,
|
|
||||||
final ErrorInfo errorInfo) {
|
|
||||||
if (rootView != null) {
|
|
||||||
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
|
||||||
.setActionTextColor(Color.YELLOW)
|
|
||||||
.setAction(context.getString(R.string.error_snackbar_action).toUpperCase(), v ->
|
|
||||||
reportError(context, errorInfo)).show();
|
|
||||||
} else {
|
|
||||||
reportError(context, errorInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
// Activity lifecycle
|
// Activity lifecycle
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.schabi.newpipe.error
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException
|
||||||
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.extractor.Info
|
import org.schabi.newpipe.extractor.Info
|
||||||
|
@ -21,11 +23,14 @@ class ErrorInfo(
|
||||||
val userAction: UserAction,
|
val userAction: UserAction,
|
||||||
val serviceName: String,
|
val serviceName: String,
|
||||||
val request: String,
|
val request: String,
|
||||||
val messageStringId: Int,
|
val messageStringId: Int
|
||||||
@Transient // no need to store throwable, all data for report is in other variables
|
|
||||||
var throwable: Throwable? = null
|
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
// no need to store throwable, all data for report is in other variables
|
||||||
|
// also, the throwable might not be serializable, see TeamNewPipe/NewPipe#7302
|
||||||
|
@IgnoredOnParcel
|
||||||
|
var throwable: Throwable? = null
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
throwable: Throwable,
|
throwable: Throwable,
|
||||||
userAction: UserAction,
|
userAction: UserAction,
|
||||||
|
@ -36,9 +41,10 @@ class ErrorInfo(
|
||||||
userAction,
|
userAction,
|
||||||
serviceName,
|
serviceName,
|
||||||
request,
|
request,
|
||||||
getMessageStringId(throwable, userAction),
|
getMessageStringId(throwable, userAction)
|
||||||
throwable
|
) {
|
||||||
)
|
this.throwable = throwable
|
||||||
|
}
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
throwable: List<Throwable>,
|
throwable: List<Throwable>,
|
||||||
|
@ -50,9 +56,10 @@ class ErrorInfo(
|
||||||
userAction,
|
userAction,
|
||||||
serviceName,
|
serviceName,
|
||||||
request,
|
request,
|
||||||
getMessageStringId(throwable.firstOrNull(), userAction),
|
getMessageStringId(throwable.firstOrNull(), userAction)
|
||||||
throwable.firstOrNull()
|
) {
|
||||||
)
|
this.throwable = throwable.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
// constructors with single throwable
|
// constructors with single throwable
|
||||||
constructor(throwable: Throwable, userAction: UserAction, request: String) :
|
constructor(throwable: Throwable, userAction: UserAction, request: String) :
|
||||||
|
@ -102,6 +109,13 @@ class ErrorInfo(
|
||||||
throwable is ContentNotSupportedException -> R.string.content_not_supported
|
throwable is ContentNotSupportedException -> R.string.content_not_supported
|
||||||
throwable is DeobfuscateException -> R.string.youtube_signature_deobfuscation_error
|
throwable is DeobfuscateException -> R.string.youtube_signature_deobfuscation_error
|
||||||
throwable is ExtractionException -> R.string.parsing_error
|
throwable is ExtractionException -> R.string.parsing_error
|
||||||
|
throwable is ExoPlaybackException -> {
|
||||||
|
when (throwable.type) {
|
||||||
|
ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure
|
||||||
|
ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure
|
||||||
|
else -> R.string.player_unrecoverable_failure
|
||||||
|
}
|
||||||
|
}
|
||||||
action == UserAction.UI_ERROR -> R.string.app_ui_crash
|
action == UserAction.UI_ERROR -> R.string.app_ui_crash
|
||||||
action == UserAction.REQUESTED_COMMENTS -> R.string.error_unable_to_load_comments
|
action == UserAction.REQUESTED_COMMENTS -> R.string.error_unable_to_load_comments
|
||||||
action == UserAction.SUBSCRIPTION_CHANGE -> R.string.subscription_change_failed
|
action == UserAction.SUBSCRIPTION_CHANGE -> R.string.subscription_change_failed
|
||||||
|
|
|
@ -118,7 +118,7 @@ class ErrorPanelHelper(
|
||||||
showAndSetErrorButtonAction(
|
showAndSetErrorButtonAction(
|
||||||
R.string.error_snackbar_action
|
R.string.error_snackbar_action
|
||||||
) {
|
) {
|
||||||
ErrorActivity.reportError(context, errorInfo)
|
ErrorUtil.openActivity(context, errorInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
errorTextView.setText(getExceptionDescription(errorInfo.throwable))
|
errorTextView.setText(getExceptionDescription(errorInfo.throwable))
|
||||||
|
@ -178,7 +178,7 @@ class ErrorPanelHelper(
|
||||||
val DEBUG: Boolean = MainActivity.DEBUG
|
val DEBUG: Boolean = MainActivity.DEBUG
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
public fun getExceptionDescription(throwable: Throwable?): Int {
|
fun getExceptionDescription(throwable: Throwable?): Int {
|
||||||
return when (throwable) {
|
return when (throwable) {
|
||||||
is AgeRestrictedContentException -> R.string.restricted_video_no_stream
|
is AgeRestrictedContentException -> R.string.restricted_video_no_stream
|
||||||
is GeographicRestrictionException -> R.string.georestricted_content
|
is GeographicRestrictionException -> R.string.georestricted_content
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package org.schabi.newpipe.error
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.schabi.newpipe.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains all of the methods that should be used to let the user know that an error has
|
||||||
|
* occurred in the least intrusive way possible for each case. This class is for unexpected errors,
|
||||||
|
* for handled errors (e.g. network errors) use e.g. [ErrorPanelHelper] instead.
|
||||||
|
* - Use a snackbar if the exception is not critical and it happens in a place where a root view
|
||||||
|
* is available.
|
||||||
|
* - Use a notification if the exception happens inside a background service (player, subscription
|
||||||
|
* import, ...) or there is no activity/fragment from which to extract a root view.
|
||||||
|
* - Finally use the error activity only as a last resort in case the exception is critical and
|
||||||
|
* happens in an open activity (since the workflow would be interrupted anyway in that case).
|
||||||
|
*/
|
||||||
|
class ErrorUtil {
|
||||||
|
companion object {
|
||||||
|
private const val ERROR_REPORT_NOTIFICATION_ID = 5340681
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new error activity allowing the user to report the provided error. Only use this
|
||||||
|
* method directly as a last resort in case the exception is critical and happens in an open
|
||||||
|
* activity (since the workflow would be interrupted anyway in that case). So never use this
|
||||||
|
* for background services.
|
||||||
|
*
|
||||||
|
* @param context the context to use to start the new activity
|
||||||
|
* @param errorInfo the error info to be reported
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun openActivity(context: Context, errorInfo: ErrorInfo) {
|
||||||
|
context.startActivity(getErrorActivityIntent(context, errorInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a bottom snackbar to the user, with a report button that opens the error activity.
|
||||||
|
* Use this method if the exception is not critical and it happens in a place where a root
|
||||||
|
* view is available.
|
||||||
|
*
|
||||||
|
* @param context will be used to obtain the root view if it is an [Activity]; if no root
|
||||||
|
* view can be found an error notification is shown instead
|
||||||
|
* @param errorInfo the error info to be reported
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun showSnackbar(context: Context, errorInfo: ErrorInfo) {
|
||||||
|
val rootView = if (context is Activity) context.findViewById<View>(R.id.content) else null
|
||||||
|
showSnackbar(context, rootView, errorInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a bottom snackbar to the user, with a report button that opens the error activity.
|
||||||
|
* Use this method if the exception is not critical and it happens in a place where a root
|
||||||
|
* view is available.
|
||||||
|
*
|
||||||
|
* @param fragment will be used to obtain the root view if it has a connected [Activity]; if
|
||||||
|
* no root view can be found an error notification is shown instead
|
||||||
|
* @param errorInfo the error info to be reported
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun showSnackbar(fragment: Fragment, errorInfo: ErrorInfo) {
|
||||||
|
var rootView = fragment.view
|
||||||
|
if (rootView == null && fragment.activity != null) {
|
||||||
|
rootView = fragment.requireActivity().findViewById(R.id.content)
|
||||||
|
}
|
||||||
|
showSnackbar(fragment.requireContext(), rootView, errorInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to calling [showSnackbar] with an [ErrorInfo] of type [UserAction.UI_ERROR]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun showUiErrorSnackbar(context: Context, request: String, throwable: Throwable) {
|
||||||
|
showSnackbar(context, ErrorInfo(throwable, UserAction.UI_ERROR, request))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to calling [showSnackbar] with an [ErrorInfo] of type [UserAction.UI_ERROR]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun showUiErrorSnackbar(fragment: Fragment, request: String, throwable: Throwable) {
|
||||||
|
showSnackbar(fragment, ErrorInfo(throwable, UserAction.UI_ERROR, request))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error notification. Tapping on the notification opens the error activity. Use
|
||||||
|
* this method if the exception happens inside a background service (player, subscription
|
||||||
|
* import, ...) or there is no activity/fragment from which to extract a root view.
|
||||||
|
*
|
||||||
|
* @param context the context to use to show the notification
|
||||||
|
* @param errorInfo the error info to be reported; the error message
|
||||||
|
* [ErrorInfo.messageStringId] will be shown in the notification
|
||||||
|
* description
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun createNotification(context: Context, errorInfo: ErrorInfo) {
|
||||||
|
val notificationManager =
|
||||||
|
ContextCompat.getSystemService(context, NotificationManager::class.java)
|
||||||
|
if (notificationManager == null) {
|
||||||
|
// this should never happen, but just in case open error activity
|
||||||
|
openActivity(context, errorInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
pendingIntentFlags = pendingIntentFlags or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
val notificationBuilder: NotificationCompat.Builder =
|
||||||
|
NotificationCompat.Builder(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.error_report_channel_id)
|
||||||
|
)
|
||||||
|
.setSmallIcon(R.drawable.ic_bug_report)
|
||||||
|
.setContentTitle(context.getString(R.string.error_report_notification_title))
|
||||||
|
.setContentText(context.getString(errorInfo.messageStringId))
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
getErrorActivityIntent(context, errorInfo),
|
||||||
|
pendingIntentFlags
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
notificationManager!!.notify(ERROR_REPORT_NOTIFICATION_ID, notificationBuilder.build())
|
||||||
|
|
||||||
|
// since the notification is silent, also show a toast, otherwise the user is confused
|
||||||
|
Toast.makeText(context, R.string.error_report_notification_toast, Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getErrorActivityIntent(context: Context, errorInfo: ErrorInfo): Intent {
|
||||||
|
val intent = Intent(context, ErrorActivity::class.java)
|
||||||
|
intent.putExtra(ErrorActivity.ERROR_INFO, errorInfo)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSnackbar(context: Context, rootView: View?, errorInfo: ErrorInfo) {
|
||||||
|
if (rootView == null) {
|
||||||
|
// fallback to showing a notification if no root view is available
|
||||||
|
createNotification(context, errorInfo)
|
||||||
|
} else {
|
||||||
|
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
||||||
|
.setActionTextColor(Color.YELLOW)
|
||||||
|
.setAction(context.getString(R.string.error_snackbar_action).uppercase()) {
|
||||||
|
openActivity(context, errorInfo)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,12 +7,13 @@ import android.widget.ProgressBar;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.schabi.newpipe.BaseFragment;
|
import org.schabi.newpipe.BaseFragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
import org.schabi.newpipe.error.ErrorPanelHelper;
|
import org.schabi.newpipe.error.ErrorPanelHelper;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.util.InfoCache;
|
import org.schabi.newpipe.util.InfoCache;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
@ -198,9 +199,8 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a SnackBar and only call
|
* Directly calls {@link ErrorUtil#showSnackbar(Fragment, ErrorInfo)}, that shows a snackbar if
|
||||||
* {@link ErrorActivity#reportErrorInSnackbar(androidx.fragment.app.Fragment, ErrorInfo)}
|
* a valid view can be found, otherwise creates an error report notification.
|
||||||
* IF we a find a valid view (otherwise the error screen appears).
|
|
||||||
*
|
*
|
||||||
* @param errorInfo The error information
|
* @param errorInfo The error information
|
||||||
*/
|
*/
|
||||||
|
@ -208,6 +208,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "showSnackBarError() called with: errorInfo = [" + errorInfo + "]");
|
Log.d(TAG, "showSnackBarError() called with: errorInfo = [" + errorInfo + "]");
|
||||||
}
|
}
|
||||||
ErrorActivity.reportErrorInSnackbar(this, errorInfo);
|
ErrorUtil.showSnackbar(this, errorInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import com.google.android.material.tabs.TabLayout;
|
||||||
import org.schabi.newpipe.BaseFragment;
|
import org.schabi.newpipe.BaseFragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.FragmentMainBinding;
|
import org.schabi.newpipe.databinding.FragmentMainBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.settings.tabs.Tab;
|
import org.schabi.newpipe.settings.tabs.Tab;
|
||||||
import org.schabi.newpipe.settings.tabs.TabsManager;
|
import org.schabi.newpipe.settings.tabs.TabsManager;
|
||||||
|
@ -145,7 +145,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
NavigationHelper.openSearchFragment(getFM(),
|
NavigationHelper.openSearchFragment(getFM(),
|
||||||
ServiceHelper.getSelectedServiceId(activity), "");
|
ServiceHelper.getSelectedServiceId(activity), "");
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening search fragment", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Opening search fragment", e);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -227,16 +227,11 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
public Fragment getItem(final int position) {
|
public Fragment getItem(final int position) {
|
||||||
final Tab tab = internalTabsList.get(position);
|
final Tab tab = internalTabsList.get(position);
|
||||||
|
|
||||||
Throwable throwable = null;
|
final Fragment fragment;
|
||||||
Fragment fragment = null;
|
|
||||||
try {
|
try {
|
||||||
fragment = tab.getFragment(context);
|
fragment = tab.getFragment(context);
|
||||||
} catch (final ExtractionException e) {
|
} catch (final ExtractionException e) {
|
||||||
throwable = e;
|
ErrorUtil.showUiErrorSnackbar(context, "Getting fragment item", e);
|
||||||
}
|
|
||||||
|
|
||||||
if (throwable != null) {
|
|
||||||
ErrorActivity.reportUiErrorInSnackbar(context, "Getting fragment item", throwable);
|
|
||||||
return new BlankFragment();
|
return new BlankFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,8 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
@ -533,7 +533,7 @@ public final class VideoDetailFragment
|
||||||
NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
|
NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
|
||||||
subChannelUrl, subChannelName);
|
subChannelUrl, subChannelName);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Opening channel fragment", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1681,9 +1681,8 @@ public final class VideoDetailFragment
|
||||||
|
|
||||||
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportErrorInSnackbar(activity,
|
ErrorUtil.showSnackbar(activity, new ErrorInfo(e, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
new ErrorInfo(e, UserAction.DOWNLOAD_OPEN_DIALOG, "Showing download dialog",
|
"Showing download dialog", currentInfo));
|
||||||
currentInfo));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.PignateFooterBinding;
|
import org.schabi.newpipe.databinding.PignateFooterBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
|
@ -293,7 +293,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||||
selectedItem.getUrl(),
|
selectedItem.getUrl(),
|
||||||
selectedItem.getName());
|
selectedItem.getName());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(
|
ErrorUtil.showUiErrorSnackbar(
|
||||||
BaseListFragment.this, "Opening channel fragment", e);
|
BaseListFragment.this, "Opening channel fragment", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||||
selectedItem.getUrl(),
|
selectedItem.getUrl(),
|
||||||
selectedItem.getName());
|
selectedItem.getName());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(BaseListFragment.this,
|
ErrorUtil.showUiErrorSnackbar(BaseListFragment.this,
|
||||||
"Opening playlist fragment", e);
|
"Opening playlist fragment", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||||
import org.schabi.newpipe.databinding.ChannelHeaderBinding;
|
import org.schabi.newpipe.databinding.ChannelHeaderBinding;
|
||||||
import org.schabi.newpipe.databinding.FragmentChannelBinding;
|
import org.schabi.newpipe.databinding.FragmentChannelBinding;
|
||||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
|
@ -407,7 +407,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
||||||
currentInfo.getParentChannelUrl(),
|
currentInfo.getParentChannelUrl(),
|
||||||
currentInfo.getParentChannelName());
|
currentInfo.getParentChannelName());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Opening channel fragment", e);
|
||||||
}
|
}
|
||||||
} else if (DEBUG) {
|
} else if (DEBUG) {
|
||||||
Log.i(TAG, "Can't open parent channel because we got no channel URL");
|
Log.i(TAG, "Can't open parent channel because we got no channel URL");
|
||||||
|
|
|
@ -24,8 +24,8 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||||
import org.schabi.newpipe.databinding.PlaylistHeaderBinding;
|
import org.schabi.newpipe.databinding.PlaylistHeaderBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
|
@ -310,7 +310,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
NavigationHelper.openChannelFragment(getFM(), result.getServiceId(),
|
NavigationHelper.openChannelFragment(getFM(), result.getServiceId(),
|
||||||
result.getUploaderUrl(), result.getUploaderName());
|
result.getUploaderUrl(), result.getUploaderName());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Opening channel fragment", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Opening channel fragment", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
@ -223,8 +223,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
try {
|
try {
|
||||||
service = NewPipe.getService(serviceId);
|
service = NewPipe.getService(serviceId);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this,
|
ErrorUtil.showUiErrorSnackbar(this, "Getting service for id " + serviceId, e);
|
||||||
"Getting service for id " + serviceId, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import android.widget.TextView;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
@ -171,7 +171,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
item.getUploaderUrl(),
|
item.getUploaderUrl(),
|
||||||
item.getUploaderName());
|
item.getUploaderName());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(activity, "Opening channel fragment", e);
|
ErrorUtil.showUiErrorSnackbar(activity, "Opening channel fragment", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ import androidx.core.text.util.LinkifyCompat;
|
||||||
|
|
||||||
import org.schabi.newpipe.BaseFragment;
|
import org.schabi.newpipe.BaseFragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
@ -86,12 +86,11 @@ public class SubscriptionsImportFragment extends BaseFragment {
|
||||||
|
|
||||||
setupServiceVariables();
|
setupServiceVariables();
|
||||||
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
|
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
|
||||||
ErrorActivity.reportErrorInSnackbar(activity,
|
ErrorUtil.showSnackbar(activity,
|
||||||
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
|
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
|
||||||
NewPipe.getNameOfService(currentServiceId),
|
NewPipe.getNameOfService(currentServiceId),
|
||||||
"Service does not support importing subscriptions",
|
"Service does not support importing subscriptions",
|
||||||
R.string.general_error,
|
R.string.general_error));
|
||||||
null));
|
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,8 @@ import androidx.core.app.ServiceCompat;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||||
|
@ -153,7 +153,7 @@ public abstract class BaseImportExportService extends Service {
|
||||||
|
|
||||||
protected void stopAndReportError(final Throwable throwable, final String request) {
|
protected void stopAndReportError(final Throwable throwable, final String request) {
|
||||||
stopService();
|
stopService();
|
||||||
ErrorActivity.reportError(this, new ErrorInfo(
|
ErrorUtil.createNotification(this, new ErrorInfo(
|
||||||
throwable, UserAction.SUBSCRIPTION_IMPORT_EXPORT, request));
|
throwable, UserAction.SUBSCRIPTION_IMPORT_EXPORT, request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,9 @@ import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.PlayerBinding;
|
import org.schabi.newpipe.databinding.PlayerBinding;
|
||||||
import org.schabi.newpipe.databinding.PlayerPopupCloseOverlayBinding;
|
import org.schabi.newpipe.databinding.PlayerPopupCloseOverlayBinding;
|
||||||
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamSegment;
|
import org.schabi.newpipe.extractor.stream.StreamSegment;
|
||||||
|
@ -165,7 +168,6 @@ import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||||
import org.schabi.newpipe.player.playback.PlayerMediaSession;
|
import org.schabi.newpipe.player.playback.PlayerMediaSession;
|
||||||
import org.schabi.newpipe.player.playback.SurfaceHolderCallback;
|
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.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
|
@ -268,8 +270,6 @@ public final class Player implements
|
||||||
@Nullable private MediaSourceTag currentMetadata;
|
@Nullable private MediaSourceTag currentMetadata;
|
||||||
@Nullable private Bitmap currentThumbnail;
|
@Nullable private Bitmap currentThumbnail;
|
||||||
|
|
||||||
@NonNull private PlayerErrorHandler playerErrorHandler;
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player
|
// Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -413,8 +413,6 @@ public final class Player implements
|
||||||
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
||||||
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
||||||
|
|
||||||
playerErrorHandler = new PlayerErrorHandler(context);
|
|
||||||
|
|
||||||
windowManager = ContextCompat.getSystemService(context, WindowManager.class);
|
windowManager = ContextCompat.getSystemService(context, WindowManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2518,29 +2516,30 @@ public final class Player implements
|
||||||
|
|
||||||
saveStreamProgressState();
|
saveStreamProgressState();
|
||||||
|
|
||||||
|
// create error notification
|
||||||
|
final ErrorInfo errorInfo;
|
||||||
|
if (currentMetadata == null) {
|
||||||
|
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
|
||||||
|
"Player error[type=" + error.type + "] occurred, currentMetadata is null");
|
||||||
|
} else {
|
||||||
|
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
|
||||||
|
"Player error[type=" + error.type + "] occurred while playing "
|
||||||
|
+ currentMetadata.getMetadata().getUrl(),
|
||||||
|
currentMetadata.getMetadata());
|
||||||
|
}
|
||||||
|
ErrorUtil.createNotification(context, errorInfo);
|
||||||
|
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case ExoPlaybackException.TYPE_SOURCE:
|
case ExoPlaybackException.TYPE_SOURCE:
|
||||||
processSourceError(error.getSourceException());
|
processSourceError(error.getSourceException());
|
||||||
playerErrorHandler.showPlayerError(
|
|
||||||
error,
|
|
||||||
currentMetadata.getMetadata(),
|
|
||||||
R.string.player_stream_failure);
|
|
||||||
break;
|
break;
|
||||||
case ExoPlaybackException.TYPE_UNEXPECTED:
|
case ExoPlaybackException.TYPE_UNEXPECTED:
|
||||||
playerErrorHandler.showPlayerError(
|
|
||||||
error,
|
|
||||||
currentMetadata.getMetadata(),
|
|
||||||
R.string.player_recoverable_failure);
|
|
||||||
setRecovery();
|
setRecovery();
|
||||||
reloadPlayQueueManager();
|
reloadPlayQueueManager();
|
||||||
break;
|
break;
|
||||||
case ExoPlaybackException.TYPE_REMOTE:
|
case ExoPlaybackException.TYPE_REMOTE:
|
||||||
case ExoPlaybackException.TYPE_RENDERER:
|
case ExoPlaybackException.TYPE_RENDERER:
|
||||||
default:
|
default:
|
||||||
playerErrorHandler.showPlayerError(
|
|
||||||
error,
|
|
||||||
currentMetadata.getMetadata(),
|
|
||||||
R.string.player_unrecoverable_failure);
|
|
||||||
onPlaybackShutdown();
|
onPlaybackShutdown();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
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.EnsureExceptionSerializable;
|
|
||||||
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);
|
|
||||||
// This will show the toast as fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
EnsureExceptionSerializable.ensureSerializable(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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,7 @@ import androidx.preference.PreferenceManager;
|
||||||
import org.schabi.newpipe.DownloaderImpl;
|
import org.schabi.newpipe.DownloaderImpl;
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||||
|
@ -205,7 +205,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||||
saveLastImportExportDataUri(exportDataUri); // save export path only on success
|
saveLastImportExportDataUri(exportDataUri); // save export path only on success
|
||||||
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Exporting database", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Exporting database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||||
finishImport(importDataUri);
|
finishImport(importDataUri);
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Importing database", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Importing database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.InfoCache;
|
import org.schabi.newpipe.util.InfoCache;
|
||||||
|
@ -64,7 +64,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(context,
|
howManyDeleted -> Toast.makeText(context,
|
||||||
R.string.watch_history_states_deleted, Toast.LENGTH_SHORT).show(),
|
R.string.watch_history_states_deleted, Toast.LENGTH_SHORT).show(),
|
||||||
throwable -> ErrorActivity.reportError(context,
|
throwable -> ErrorUtil.openActivity(context,
|
||||||
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
||||||
"Delete playback states")));
|
"Delete playback states")));
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(context,
|
howManyDeleted -> Toast.makeText(context,
|
||||||
R.string.watch_history_deleted, Toast.LENGTH_SHORT).show(),
|
R.string.watch_history_deleted, Toast.LENGTH_SHORT).show(),
|
||||||
throwable -> ErrorActivity.reportError(context,
|
throwable -> ErrorUtil.openActivity(context,
|
||||||
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
||||||
"Delete from history")));
|
"Delete from history")));
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> { },
|
howManyDeleted -> { },
|
||||||
throwable -> ErrorActivity.reportError(context,
|
throwable -> ErrorUtil.openActivity(context,
|
||||||
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
||||||
"Clear orphaned records")));
|
"Clear orphaned records")));
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(context,
|
howManyDeleted -> Toast.makeText(context,
|
||||||
R.string.search_history_deleted, Toast.LENGTH_SHORT).show(),
|
R.string.search_history_deleted, Toast.LENGTH_SHORT).show(),
|
||||||
throwable -> ErrorActivity.reportError(context,
|
throwable -> ErrorUtil.openActivity(context,
|
||||||
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
|
||||||
"Delete search history")));
|
"Delete search history")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
||||||
import org.schabi.newpipe.util.PicassoHelper;
|
import org.schabi.newpipe.util.PicassoHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
@ -153,7 +153,7 @@ public class SelectChannelFragment extends DialogFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(@NonNull final Throwable exception) {
|
public void onError(@NonNull final Throwable exception) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(SelectChannelFragment.this,
|
ErrorUtil.showUiErrorSnackbar(SelectChannelFragment.this,
|
||||||
"Loading subscription", exception);
|
"Loading subscription", exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.util.KioskTranslator;
|
import org.schabi.newpipe.util.KioskTranslator;
|
||||||
|
@ -48,7 +48,6 @@ import java.util.Vector;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SelectKioskFragment extends DialogFragment {
|
public class SelectKioskFragment extends DialogFragment {
|
||||||
private RecyclerView recyclerView = null;
|
|
||||||
private SelectKioskAdapter selectKioskAdapter = null;
|
private SelectKioskAdapter selectKioskAdapter = null;
|
||||||
|
|
||||||
private OnSelectedListener onSelectedListener = null;
|
private OnSelectedListener onSelectedListener = null;
|
||||||
|
@ -76,12 +75,12 @@ public class SelectKioskFragment extends DialogFragment {
|
||||||
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
|
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
|
||||||
final Bundle savedInstanceState) {
|
final Bundle savedInstanceState) {
|
||||||
final View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false);
|
final View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false);
|
||||||
recyclerView = v.findViewById(R.id.items_list);
|
final RecyclerView recyclerView = v.findViewById(R.id.items_list);
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
try {
|
try {
|
||||||
selectKioskAdapter = new SelectKioskAdapter();
|
selectKioskAdapter = new SelectKioskAdapter();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Selecting kiosk", e);
|
ErrorUtil.showUiErrorSnackbar(this, "Selecting kiosk", e);
|
||||||
}
|
}
|
||||||
recyclerView.setAdapter(selectKioskAdapter);
|
recyclerView.setAdapter(selectKioskAdapter);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.schabi.newpipe.settings;
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -21,8 +20,8 @@ import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
|
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
||||||
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
||||||
|
@ -105,8 +104,7 @@ public class SelectPlaylistFragment extends DialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onError(final Throwable e) {
|
protected void onError(final Throwable e) {
|
||||||
final Activity activity = requireActivity();
|
ErrorUtil.showSnackbar(requireActivity(), new ErrorInfo(e,
|
||||||
ErrorActivity.reportErrorInSnackbar(activity, new ErrorInfo(e,
|
|
||||||
UserAction.UI_ERROR, "Loading playlists"));
|
UserAction.UI_ERROR, "Loading playlists"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import android.util.Log;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ public final class SettingMigrations {
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// save the version with the last successful migration and report the error
|
// save the version with the last successful migration and report the error
|
||||||
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
|
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
|
||||||
ErrorActivity.reportError(context, new ErrorInfo(
|
ErrorUtil.openActivity(context, new ErrorInfo(
|
||||||
e,
|
e,
|
||||||
UserAction.PREFERENCES_MIGRATION,
|
UserAction.PREFERENCES_MIGRATION,
|
||||||
"Migrating preferences from version " + lastPrefVersion + " to "
|
"Migrating preferences from version " + lastPrefVersion + " to "
|
||||||
|
|
|
@ -27,8 +27,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.settings.SelectChannelFragment;
|
import org.schabi.newpipe.settings.SelectChannelFragment;
|
||||||
|
@ -182,7 +182,7 @@ public class ChooseTabsFragment extends Fragment {
|
||||||
final Tab.Type type = typeFrom(tabId);
|
final Tab.Type type = typeFrom(tabId);
|
||||||
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
ErrorActivity.reportErrorInSnackbar(this,
|
ErrorUtil.showSnackbar(this,
|
||||||
new ErrorInfo(new IllegalStateException("Tab id not found: " + tabId),
|
new ErrorInfo(new IllegalStateException("Tab id not found: " + tabId),
|
||||||
UserAction.SOMETHING_ELSE, "Choosing tabs on settings"));
|
UserAction.SOMETHING_ELSE, "Choosing tabs on settings"));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -12,8 +12,8 @@ import com.grack.nanojson.JsonSink;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.LocalItem.LocalItemType;
|
import org.schabi.newpipe.database.LocalItem.LocalItemType;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
@ -506,7 +506,7 @@ public abstract class Tab {
|
||||||
final StreamingService service = NewPipe.getService(kioskServiceId);
|
final StreamingService service = NewPipe.getService(kioskServiceId);
|
||||||
kioskId = service.getKioskList().getDefaultKioskId();
|
kioskId = service.getKioskList().getDefaultKioskId();
|
||||||
} catch (final ExtractionException e) {
|
} catch (final ExtractionException e) {
|
||||||
ErrorActivity.reportErrorInSnackbar(context, new ErrorInfo(e,
|
ErrorUtil.showSnackbar(context, new ErrorInfo(e,
|
||||||
UserAction.REQUESTED_KIOSK, "Loading default kiosk for selected service"));
|
UserAction.REQUESTED_KIOSK, "Loading default kiosk for selected service"));
|
||||||
}
|
}
|
||||||
return kioskId;
|
return kioskId;
|
||||||
|
|
|
@ -39,8 +39,8 @@ import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
@ -581,9 +581,9 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
||||||
service = ErrorInfo.SERVICE_NONE;
|
service = ErrorInfo.SERVICE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorActivity.reportError(mContext,
|
ErrorUtil.createNotification(mContext,
|
||||||
new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action,
|
new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action,
|
||||||
service, request.toString(), reason, null));
|
service, request.toString(), reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearFinishedDownloads(boolean delete) {
|
public void clearFinishedDownloads(boolean delete) {
|
||||||
|
|
|
@ -89,8 +89,6 @@
|
||||||
<item>@string/never</item>
|
<item>@string/never</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string name="report_player_errors_key" translatable="false">report_player_errors_key</string>
|
|
||||||
|
|
||||||
<string name="seekbar_preview_thumbnail_key" translatable="false">seekbar_preview_thumbnail_key</string>
|
<string name="seekbar_preview_thumbnail_key" translatable="false">seekbar_preview_thumbnail_key</string>
|
||||||
<string name="seekbar_preview_thumbnail_high_quality" translatable="false">seekbar_preview_thumbnail_high_quality</string>
|
<string name="seekbar_preview_thumbnail_high_quality" translatable="false">seekbar_preview_thumbnail_high_quality</string>
|
||||||
<string name="seekbar_preview_thumbnail_low_quality" translatable="false">seekbar_preview_thumbnail_low_quality</string>
|
<string name="seekbar_preview_thumbnail_low_quality" translatable="false">seekbar_preview_thumbnail_low_quality</string>
|
||||||
|
@ -188,9 +186,11 @@
|
||||||
<string name="allow_disposed_exceptions_key" translatable="false">allow_disposed_exceptions_key</string>
|
<string name="allow_disposed_exceptions_key" translatable="false">allow_disposed_exceptions_key</string>
|
||||||
<string name="show_original_time_ago_key" translatable="false">show_original_time_ago_key</string>
|
<string name="show_original_time_ago_key" translatable="false">show_original_time_ago_key</string>
|
||||||
<string name="disable_media_tunneling_key" translatable="false">disable_media_tunneling_key</string>
|
<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_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>
|
<string name="show_crash_the_player_key" translatable="false">show_crash_the_player_key</string>
|
||||||
|
<string name="crash_the_app_key" translatable="false">crash_the_app_key</string>
|
||||||
|
<string name="show_error_snackbar_key" translatable="false">show_error_snackbar_key</string>
|
||||||
|
<string name="create_error_notification_key" translatable="false">create_error_notification_key</string>
|
||||||
|
|
||||||
<!-- THEMES -->
|
<!-- THEMES -->
|
||||||
<string name="theme_key" translatable="false">theme</string>
|
<string name="theme_key" translatable="false">theme</string>
|
||||||
|
|
|
@ -53,8 +53,6 @@
|
||||||
<string name="show_play_with_kodi_title">Show \"Play with Kodi\" option</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="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="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>
|
<string name="notification_scale_to_square_image_title">Scale thumbnail to 1:1 aspect ratio</string>
|
||||||
<string name="notification_scale_to_square_image_summary">Scale the video thumbnail shown in the notification from 16:9 to 1:1 aspect ratio (may introduce distortions)</string>
|
<string name="notification_scale_to_square_image_summary">Scale the video thumbnail shown in the notification from 16:9 to 1:1 aspect ratio (may introduce distortions)</string>
|
||||||
<string name="notification_action_0_title">First action button</string>
|
<string name="notification_action_0_title">First action button</string>
|
||||||
|
@ -182,14 +180,17 @@
|
||||||
<string name="just_once">Just Once</string>
|
<string name="just_once">Just Once</string>
|
||||||
<string name="file">File</string>
|
<string name="file">File</string>
|
||||||
<string name="notification_channel_id" translatable="false">newpipe</string>
|
<string name="notification_channel_id" translatable="false">newpipe</string>
|
||||||
<string name="notification_channel_name">NewPipe Notification</string>
|
<string name="notification_channel_name">NewPipe notification</string>
|
||||||
<string name="notification_channel_description">Notifications for NewPipe background and popup players</string>
|
<string name="notification_channel_description">Notifications for NewPipe\'s player</string>
|
||||||
<string name="app_update_notification_channel_id" translatable="false">newpipeAppUpdate</string>
|
<string name="app_update_notification_channel_id" translatable="false">newpipeAppUpdate</string>
|
||||||
<string name="app_update_notification_channel_name">App Update Notification</string>
|
<string name="app_update_notification_channel_name">App update notification</string>
|
||||||
<string name="app_update_notification_channel_description">Notifications for new NewPipe version</string>
|
<string name="app_update_notification_channel_description">Notifications for new NewPipe versions</string>
|
||||||
<string name="hash_channel_id" translatable="false">newpipeHash</string>
|
<string name="hash_channel_id" translatable="false">newpipeHash</string>
|
||||||
<string name="hash_channel_name">Video Hash Notification</string>
|
<string name="hash_channel_name">Video hash notification</string>
|
||||||
<string name="hash_channel_description">Notifications for video hashing progress</string>
|
<string name="hash_channel_description">Notifications for video hashing progress</string>
|
||||||
|
<string name="error_report_channel_id" translatable="false">newpipeErrorReport</string>
|
||||||
|
<string name="error_report_channel_name">Error report notification</string>
|
||||||
|
<string name="error_report_channel_description">Notifications to report errors</string>
|
||||||
<string name="unknown_content">[Unknown]</string>
|
<string name="unknown_content">[Unknown]</string>
|
||||||
<string name="switch_to_background">Switch to Background</string>
|
<string name="switch_to_background">Switch to Background</string>
|
||||||
<string name="switch_to_popup">Switch to Popup</string>
|
<string name="switch_to_popup">Switch to Popup</string>
|
||||||
|
@ -243,6 +244,8 @@
|
||||||
<string name="restore_defaults_confirmation">Do you want to restore defaults?</string>
|
<string name="restore_defaults_confirmation">Do you want to restore defaults?</string>
|
||||||
<string name="permission_display_over_apps">Give permission to display over other apps</string>
|
<string name="permission_display_over_apps">Give permission to display over other apps</string>
|
||||||
<!-- error activity -->
|
<!-- error activity -->
|
||||||
|
<string name="error_report_notification_title">NewPipe encountered an error, tap to report</string>
|
||||||
|
<string name="error_report_notification_toast">An error occurred, see the notification</string>
|
||||||
<string name="sorry_string">Sorry, that should not have happened.</string>
|
<string name="sorry_string">Sorry, that should not have happened.</string>
|
||||||
<string name="guru_meditation" translatable="false">Guru Meditation.</string>
|
<string name="guru_meditation" translatable="false">Guru Meditation.</string>
|
||||||
<string name="error_report_button_text">Report this error via e-mail</string>
|
<string name="error_report_button_text">Report this error via e-mail</string>
|
||||||
|
@ -475,9 +478,11 @@
|
||||||
<string name="disable_media_tunneling_summary">Disable media tunneling if you experience a black screen or stuttering on video playback</string>
|
<string name="disable_media_tunneling_summary">Disable media tunneling if you experience a black screen or stuttering on video playback</string>
|
||||||
<string name="show_image_indicators_title">Show image indicators</string>
|
<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="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_title">Show \"crash the player\"</string>
|
||||||
<string name="show_crash_the_player_summary">Shows a crash option when using the player</string>
|
<string name="show_crash_the_player_summary">Shows a crash option when using the player</string>
|
||||||
|
<string name="crash_the_app">Crash the app</string>
|
||||||
|
<string name="show_error_snackbar">Show an error snackbar</string>
|
||||||
|
<string name="create_error_notification">Create an error notification</string>
|
||||||
<!-- Subscriptions import/export -->
|
<!-- Subscriptions import/export -->
|
||||||
<string name="import_title">Import</string>
|
<string name="import_title">Import</string>
|
||||||
<string name="import_from">Import from</string>
|
<string name="import_from">Import from</string>
|
||||||
|
|
|
@ -51,10 +51,9 @@
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="@string/report_player_errors_key"
|
android:key="@string/show_crash_the_player_key"
|
||||||
android:summary="@string/report_player_errors_summary"
|
android:summary="@string/show_crash_the_player_summary"
|
||||||
android:title="@string/report_player_errors_title"
|
android:title="@string/show_crash_the_player_title"
|
||||||
app:singleLineTitle="false"
|
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
@ -63,12 +62,15 @@
|
||||||
app:singleLineTitle="false"
|
app:singleLineTitle="false"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<Preference
|
||||||
android:layout_width="wrap_content"
|
android:key="@string/show_error_snackbar_key"
|
||||||
android:layout_height="wrap_content"
|
android:title="@string/show_error_snackbar"
|
||||||
android:defaultValue="false"
|
app:singleLineTitle="false"
|
||||||
android:key="@string/show_crash_the_player_key"
|
app:iconSpaceReserved="false" />
|
||||||
android:summary="@string/show_crash_the_player_summary"
|
|
||||||
android:title="@string/show_crash_the_player_title"
|
<Preference
|
||||||
|
android:key="@string/create_error_notification_key"
|
||||||
|
android:title="@string/create_error_notification"
|
||||||
|
app:singleLineTitle="false"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
Loading…
Reference in New Issue