Add report/solve-recaptcha button in error panel

It will be shown even when nothing could be loaded not due to a network error, and the user can choose to ignore or report it.

Also improve error reporting arguments
Also completely refactor error activity
Also improve some code here and there
This commit is contained in:
Stypox 2020-12-11 14:55:47 +01:00
parent bb398189d6
commit 0ea6b4928c
59 changed files with 886 additions and 1262 deletions

View file

@ -21,13 +21,11 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorActivity;
import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.error.ErrorActivity;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.ZipHelper;
@ -198,7 +196,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
} catch (final Exception e) {
onError(e);
ErrorActivity.reportUiError(getActivity(), null, "Exporting database", e);
}
}
@ -243,20 +241,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
System.exit(0);
}
} catch (final Exception e) {
onError(e);
ErrorActivity.reportUiError(getActivity(), null, "Importing database", e);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
}

View file

@ -1,8 +1,10 @@
package org.schabi.newpipe.settings;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
@ -46,120 +48,103 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
public boolean onPreferenceTreeClick(final Preference preference) {
if (preference.getKey().equals(cacheWipeKey)) {
InfoCache.getInstance().clearCache();
Toast.makeText(preference.getContext(), R.string.metadata_cache_wipe_complete_notice,
Toast.LENGTH_SHORT).show();
Toast.makeText(requireContext(),
R.string.metadata_cache_wipe_complete_notice, Toast.LENGTH_SHORT).show();
} else if (preference.getKey().equals(viewsHistoryClearKey)) {
openDeleteWatchHistoryDialog(requireContext(), recordManager, disposables);
} else if (preference.getKey().equals(playbackStatesClearKey)) {
openDeletePlaybackStatesDialog(requireContext(), recordManager, disposables);
} else if (preference.getKey().equals(searchHistoryClearKey)) {
openDeleteSearchHistoryDialog(requireContext(), recordManager, disposables);
} else {
return super.onPreferenceTreeClick(preference);
}
return true;
}
if (preference.getKey().equals(viewsHistoryClearKey)) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.delete_view_history_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
final Disposable onDeletePlaybackStates
= recordManager.deleteCompleteStreamStateHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),
R.string.watch_history_states_deleted,
Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
ErrorInfo.make(
UserAction.DELETE_FROM_HISTORY,
"none",
"Delete playback states",
R.string.general_error)));
private static Disposable getDeletePlaybackStatesDisposable(
@NonNull final Context context, final HistoryRecordManager recordManager) {
return recordManager.deleteCompleteStreamStateHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(context,
R.string.watch_history_states_deleted, Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(context, SettingsActivity.class,
null, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
"Delete playback states")));
}
final Disposable onDelete = recordManager.deleteWholeStreamHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),
R.string.watch_history_deleted,
Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
ErrorInfo.make(
UserAction.DELETE_FROM_HISTORY,
"none",
"Delete view history",
R.string.general_error)));
private static Disposable getWholeStreamHistoryDisposable(
@NonNull final Context context, final HistoryRecordManager recordManager) {
return recordManager.deleteWholeStreamHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(context,
R.string.watch_history_deleted, Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(context, SettingsActivity.class,
null, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
"Delete from history")));
}
final Disposable onClearOrphans = recordManager.removeOrphanedRecords()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {
},
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
ErrorInfo.make(
UserAction.DELETE_FROM_HISTORY,
"none",
"Delete search history",
R.string.general_error)));
disposables.add(onDeletePlaybackStates);
disposables.add(onClearOrphans);
disposables.add(onDelete);
}))
.create()
.show();
}
private static Disposable getRemoveOrphanedRecordsDisposable(
@NonNull final Context context, final HistoryRecordManager recordManager) {
return recordManager.removeOrphanedRecords()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {},
throwable -> ErrorActivity.reportError(context, SettingsActivity.class,
null, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
"Clear orphaned records")));
}
if (preference.getKey().equals(playbackStatesClearKey)) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.delete_playback_states_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
private static Disposable getDeleteSearchHistoryDisposable(
@NonNull final Context context, final HistoryRecordManager recordManager) {
return recordManager.deleteCompleteSearchHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(context,
R.string.search_history_deleted, Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(context, SettingsActivity.class,
null, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY,
"Delete search history")));
}
final Disposable onDeletePlaybackStates
= recordManager.deleteCompleteStreamStateHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),
R.string.watch_history_states_deleted,
Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
ErrorInfo.make(
UserAction.DELETE_FROM_HISTORY,
"none",
"Delete playback states",
R.string.general_error)));
public static void openDeleteWatchHistoryDialog(@NonNull final Context context,
final HistoryRecordManager recordManager,
final CompositeDisposable disposables) {
new AlertDialog.Builder(context)
.setTitle(R.string.delete_view_history_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
disposables.add(getDeletePlaybackStatesDisposable(context, recordManager));
disposables.add(getWholeStreamHistoryDisposable(context, recordManager));
disposables.add(getRemoveOrphanedRecordsDisposable(context, recordManager));
}))
.create()
.show();
}
disposables.add(onDeletePlaybackStates);
}))
.create()
.show();
}
public static void openDeletePlaybackStatesDialog(@NonNull final Context context,
final HistoryRecordManager recordManager,
final CompositeDisposable disposables) {
new AlertDialog.Builder(context)
.setTitle(R.string.delete_playback_states_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) ->
disposables.add(getDeletePlaybackStatesDisposable(context, recordManager))))
.create()
.show();
}
if (preference.getKey().equals(searchHistoryClearKey)) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.delete_search_history_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) -> {
final Disposable onDelete = recordManager.deleteCompleteSearchHistory()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> Toast.makeText(getActivity(),
R.string.search_history_deleted,
Toast.LENGTH_SHORT).show(),
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
ErrorInfo.make(
UserAction.DELETE_FROM_HISTORY,
"none",
"Delete search history",
R.string.general_error)));
disposables.add(onDelete);
}))
.create()
.show();
}
return super.onPreferenceTreeClick(preference);
public static void openDeleteSearchHistoryDialog(@NonNull final Context context,
final HistoryRecordManager recordManager,
final CompositeDisposable disposables) {
new AlertDialog.Builder(context)
.setTitle(R.string.delete_search_history_alert)
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setPositiveButton(R.string.delete, ((dialog, which) ->
disposables.add(getDeleteSearchHistoryDisposable(context, recordManager))))
.create()
.show();
}
}

View file

@ -1,6 +1,5 @@
package org.schabi.newpipe.settings;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -20,10 +19,8 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.error.ErrorActivity;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.List;
@ -108,7 +105,7 @@ public class SelectChannelFragment extends DialogFragment {
emptyView.setVisibility(View.GONE);
final SubscriptionManager subscriptionManager = new SubscriptionManager(getContext());
final SubscriptionManager subscriptionManager = new SubscriptionManager(requireContext());
subscriptionManager.subscriptions().toObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -122,7 +119,7 @@ public class SelectChannelFragment extends DialogFragment {
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCancel(final DialogInterface dialogInterface) {
public void onCancel(@NonNull final DialogInterface dialogInterface) {
super.onCancel(dialogInterface);
if (onCancelListener != null) {
onCancelListener.onCancel();
@ -156,16 +153,16 @@ public class SelectChannelFragment extends DialogFragment {
private Observer<List<SubscriptionEntity>> getSubscriptionObserver() {
return new Observer<List<SubscriptionEntity>>() {
@Override
public void onSubscribe(final Disposable d) { }
public void onSubscribe(@NonNull final Disposable disposable) { }
@Override
public void onNext(final List<SubscriptionEntity> newSubscriptions) {
public void onNext(@NonNull final List<SubscriptionEntity> newSubscriptions) {
displayChannels(newSubscriptions);
}
@Override
public void onError(final Throwable exception) {
SelectChannelFragment.this.onError(exception);
public void onError(@NonNull final Throwable exception) {
ErrorActivity.reportUiError(requireContext(), null, "Loading subscription", exception);
}
@Override
@ -173,16 +170,6 @@ public class SelectChannelFragment extends DialogFragment {
};
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorInfo
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
}
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/
@ -197,6 +184,7 @@ public class SelectChannelFragment extends DialogFragment {
private class SelectChannelAdapter
extends RecyclerView.Adapter<SelectChannelAdapter.SelectChannelItemHolder> {
@NonNull
@Override
public SelectChannelItemHolder onCreateViewHolder(final ViewGroup parent,
final int viewType) {

View file

@ -1,6 +1,5 @@
package org.schabi.newpipe.settings;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -19,8 +18,6 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.error.ErrorActivity;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
@ -83,7 +80,7 @@ public class SelectKioskFragment extends DialogFragment {
try {
selectKioskAdapter = new SelectKioskAdapter();
} catch (final Exception e) {
onError(e);
ErrorActivity.reportUiError(getActivity(), null, "Selecting kiosk", e);
}
recyclerView.setAdapter(selectKioskAdapter);
@ -109,16 +106,6 @@ public class SelectKioskFragment extends DialogFragment {
dismiss();
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
protected void onError(final Throwable e) {
final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorInfo
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
}
/*//////////////////////////////////////////////////////////////////////////
// Interfaces
//////////////////////////////////////////////////////////////////////////*/

View file

@ -115,8 +115,8 @@ public class SelectPlaylistFragment extends DialogFragment {
protected void onError(final Throwable e) {
final Activity activity = requireActivity();
ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorInfo
.make(UserAction.UI_ERROR, "none", "load_playlists", R.string.app_ui_crash));
ErrorActivity.reportError(activity, activity.getClass(), null, new ErrorInfo(e,
UserAction.UI_ERROR, "Loading playlists"));
}
/*//////////////////////////////////////////////////////////////////////////

View file

@ -95,15 +95,13 @@ public final class SettingMigrations {
} catch (final Exception e) {
// save the version with the last successful migration and report the error
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
final ErrorInfo errorInfo = ErrorInfo.make(
ErrorActivity.reportError(context, SettingMigrations.class, null, new ErrorInfo(
e,
UserAction.PREFERENCES_MIGRATION,
"none",
"Migrating preferences from version " + lastPrefVersion + " to "
+ VERSION + ". "
+ "Error at " + currentVersion + " => " + ++currentVersion,
0
);
ErrorActivity.reportError(context, e, SettingMigrations.class, null, errorInfo);
+ "Error at " + currentVersion + " => " + ++currentVersion
));
return;
}
}

View file

@ -183,10 +183,9 @@ public class ChooseTabsFragment extends Fragment {
final Tab.Type type = typeFrom(tabId);
if (type == null) {
ErrorActivity.reportError(requireContext(),
new IllegalStateException("Tab id not found: " + tabId), null, null,
ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Choosing tabs on settings", 0));
ErrorActivity.reportError(requireContext(), null, null,
new ErrorInfo(new IllegalStateException("Tab id not found: " + tabId),
UserAction.SOMETHING_ELSE, "Choosing tabs on settings"));
return;
}

View file

@ -483,9 +483,8 @@ public abstract class Tab {
final StreamingService service = NewPipe.getService(kioskServiceId);
kioskId = service.getKioskList().getDefaultKioskId();
} catch (final ExtractionException e) {
ErrorActivity.reportError(context, e, null, null,
ErrorInfo.make(UserAction.REQUESTED_KIOSK, "none",
"Loading default kiosk from selected service", 0));
ErrorActivity.reportError(context, null, null, new ErrorInfo(e,
UserAction.REQUESTED_KIOSK, "Loading default kiosk for selected service"));
}
return kioskId;
}