fix(SponsorBlock): Show create new segment error messages using a dialog

This commit is contained in:
inotia00
2024-12-21 11:42:56 +09:00
parent b2b64c1404
commit c3b2e5c0fc
12 changed files with 123 additions and 61 deletions

View File

@ -7,11 +7,13 @@ import static app.revanced.extension.music.sponsorblock.objects.CategoryBehaviou
import androidx.annotation.NonNull;
import app.revanced.extension.music.patches.utils.PatchStatus;
import app.revanced.extension.music.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.shared.settings.LongSetting;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.shared.utils.Utils;
@ -218,6 +220,14 @@ public class Settings extends BaseSettings {
// SB settings not exported
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
static {
// region SB import/export callbacks
Setting.addImportExportCallback(SponsorBlockSettings.SB_IMPORT_EXPORT_CALLBACK);
// endregion
}
public static final String OPEN_DEFAULT_APP_SETTINGS = "revanced_default_app_settings";
/**

View File

@ -236,7 +236,7 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
.setView(container)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(str("revanced_extended_settings_import_copy"), (dialog, which) -> Utils.setClipboard(textView.getText().toString(), str("revanced_share_copy_settings_success")))
.setPositiveButton(str("revanced_extended_settings_import"), (dialog, which) -> importSettings(textView.getText().toString()))
.setPositiveButton(str("revanced_extended_settings_import"), (dialog, which) -> importSettings(activity, textView.getText().toString()))
.show();
} catch (Exception ex) {
Logger.printException(() -> "importExportEditTextDialogBuilder failure", ex);
@ -326,7 +326,7 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
bufferedReader.close();
fileReader.close();
final boolean restartNeeded = Setting.importFromJSON(sb.toString(), false);
final boolean restartNeeded = Setting.importFromJSON(context, sb.toString());
if (restartNeeded) {
ReVancedPreferenceFragment.showRebootDialog();
}
@ -336,13 +336,13 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
}
}
private void importSettings(String replacementSettings) {
private void importSettings(Activity mActivity, String replacementSettings) {
try {
existingSettings = Setting.exportToJson(null);
if (replacementSettings.equals(existingSettings)) {
return;
}
final boolean restartNeeded = Setting.importFromJSON(replacementSettings, false);
final boolean restartNeeded = Setting.importFromJSON(mActivity, replacementSettings);
if (restartNeeded) {
ReVancedPreferenceFragment.showRebootDialog();
}

View File

@ -1,6 +1,9 @@
package app.revanced.extension.music.sponsorblock;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.UUID;
@ -9,6 +12,17 @@ import app.revanced.extension.music.sponsorblock.objects.SegmentCategory;
import app.revanced.extension.shared.settings.Setting;
public class SponsorBlockSettings {
public static final Setting.ImportExportCallback SB_IMPORT_EXPORT_CALLBACK = new Setting.ImportExportCallback() {
@Override
public void settingsImported(@Nullable Context context) {
SegmentCategory.loadAllCategoriesFromSettings();
}
@Override
public void settingsExported(@Nullable Context context) {
}
};
private static boolean initialized;
/**
@ -42,11 +56,4 @@ public class SponsorBlockSettings {
SegmentCategory.updateEnabledCategories();
}
/**
* Updates internal data based on {@link Setting} values.
*/
public static void updateFromImportedSettings() {
SegmentCategory.loadAllCategoriesFromSettings();
}
}

View File

@ -8,7 +8,6 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;
@ -74,6 +73,30 @@ public abstract class Setting<T> {
};
}
/**
* Callback for importing/exporting settings.
*/
public interface ImportExportCallback {
/**
* Called after all settings have been imported.
*/
void settingsImported(@Nullable Context context);
/**
* Called after all settings have been exported.
*/
void settingsExported(@Nullable Context context);
}
private static final List<ImportExportCallback> importExportCallbacks = new ArrayList<>();
/**
* Adds a callback for {@link #importFromJSON(Context, String)} and {@link #exportToJson(Context)}.
*/
public static void addImportExportCallback(@NonNull ImportExportCallback callback) {
importExportCallbacks.add(Objects.requireNonNull(callback));
}
/**
* All settings that were instantiated.
* When a new setting is created, it is automatically added to this list.
@ -232,6 +255,10 @@ public abstract class Setting<T> {
* Migrate a setting value if the path is renamed but otherwise the old and new settings are identical.
*/
public static <T> void migrateOldSettingToNew(@NonNull Setting<T> oldSetting, @NonNull Setting<T> newSetting) {
if (oldSetting == newSetting) {
throw new IllegalArgumentException();
}
if (!oldSetting.isSetToDefault()) {
Logger.printInfo(() -> "Migrating old setting value: " + oldSetting + " into replacement setting: " + newSetting);
newSetting.save(oldSetting.value);
@ -335,7 +362,7 @@ public abstract class Setting<T> {
return value.equals(defaultValue);
}
@NotNull
@NonNull
@Override
public String toString() {
return key + "=" + get();
@ -393,8 +420,9 @@ public abstract class Setting<T> {
setting.writeToJSON(json, importExportKey);
}
}
if (alertDialogContext != null) {
app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings.showExportWarningIfNeeded(alertDialogContext);
for (ImportExportCallback callback : importExportCallbacks) {
callback.settingsExported(alertDialogContext);
}
if (json.length() == 0) {
@ -412,14 +440,10 @@ public abstract class Setting<T> {
}
}
public static boolean importFromJSON(@NonNull String settingsJsonString) {
return importFromJSON(settingsJsonString, true);
}
/**
* @return if any settings that require a reboot were changed.
*/
public static boolean importFromJSON(@NonNull String settingsJsonString, boolean isYouTube) {
public static boolean importFromJSON(@NonNull Context alertDialogContext, @NonNull String settingsJsonString) {
try {
if (!settingsJsonString.matches("[\\s\\S]*\\{")) {
settingsJsonString = '{' + settingsJsonString + '}'; // Restore outer JSON braces
@ -445,15 +469,8 @@ public abstract class Setting<T> {
}
}
// SB Enum categories are saved using StringSettings.
// Which means they need to reload again if changed by other code (such as here).
// This call could be removed by creating a custom Setting class that manages the
// "String <-> Enum" logic or by adding an event hook of when settings are imported.
// But for now this is simple and works.
if (isYouTube) {
app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings.updateFromImportedSettings();
} else {
app.revanced.extension.music.sponsorblock.SponsorBlockSettings.updateFromImportedSettings();
for (ImportExportCallback callback : importExportCallbacks) {
callback.settingsImported(alertDialogContext);
}
Utils.showToastLong(numberOfSettingsImported == 0

View File

@ -77,20 +77,20 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
Utils.setClipboard(getEditText().getText().toString())
).setPositiveButton(
str("revanced_extended_settings_import"), (dialog, which) ->
importSettings(getEditText().getText().toString())
importSettings(builder.getContext(), getEditText().getText().toString())
);
} catch (Exception ex) {
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
}
}
private void importSettings(String replacementSettings) {
private void importSettings(Context context, String replacementSettings) {
try {
if (replacementSettings.equals(existingSettings)) {
return;
}
AbstractPreferenceFragment.settingImportInProgress = true;
final boolean rebootNeeded = Setting.importFromJSON(replacementSettings);
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
if (rebootNeeded) {
AbstractPreferenceFragment.showRestartDialog(getContext());
}

View File

@ -662,5 +662,11 @@ public class Settings extends BaseSettings {
}
}
// endregion
// region SB import/export callbacks
Setting.addImportExportCallback(SponsorBlockSettings.SB_IMPORT_EXPORT_CALLBACK);
// endregion
}
}

View File

@ -76,19 +76,19 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
builder.setNeutralButton(str("revanced_extended_settings_import_copy"), (dialog, which) ->
Utils.setClipboard(getEditText().getText().toString(), str("revanced_share_copy_settings_success")))
.setPositiveButton(str("revanced_extended_settings_import"), (dialog, which) ->
importSettings(getEditText().getText().toString()));
importSettings(builder.getContext(), getEditText().getText().toString()));
} catch (Exception ex) {
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
}
}
private void importSettings(String replacementSettings) {
private void importSettings(Context context, String replacementSettings) {
try {
if (replacementSettings.equals(existingSettings)) {
return;
}
ReVancedPreferenceFragment.settingImportInProgress = true;
final boolean rebootNeeded = Setting.importFromJSON(replacementSettings, true);
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
if (rebootNeeded) {
AbstractPreferenceFragment.showRestartDialog(getContext());
}

View File

@ -675,7 +675,7 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
bufferedReader.close();
fileReader.close();
final boolean restartNeeded = Setting.importFromJSON(sb.toString(), true);
final boolean restartNeeded = Setting.importFromJSON(context, sb.toString());
if (restartNeeded) {
showRestartDialog(getActivity());
}

View File

@ -338,7 +338,7 @@ public class SponsorBlockSettingsPreference extends ReVancedPreferenceFragment {
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
} else {
preference.setText(userName); // revert to previous
Utils.showToastLong(errorMessage);
SponsorBlockUtils.showErrorDialog(errorMessage);
}
});
});

View File

@ -28,6 +28,18 @@ public class SponsorBlockSettings {
*/
private static final int SB_PRIVATE_USER_ID_MINIMUM_LENGTH = 30;
public static final Setting.ImportExportCallback SB_IMPORT_EXPORT_CALLBACK = new Setting.ImportExportCallback() {
@Override
public void settingsImported(@Nullable Context context) {
SegmentCategory.loadAllCategoriesFromSettings();
SponsorBlockSettingsPreference.updateSegmentCategories();
}
@Override
public void settingsExported(@Nullable Context context) {
showExportWarningIfNeeded(context);
}
};
public static void importDesktopSettings(@NonNull String json) {
Utils.verifyOnMainThread();
try {
@ -162,7 +174,7 @@ public class SponsorBlockSettings {
/**
* Export the categories using flatten json (no embedded dictionaries or arrays).
*/
public static void showExportWarningIfNeeded(@Nullable Context dialogContext) {
private static void showExportWarningIfNeeded(@Nullable Context dialogContext) {
Utils.verifyOnMainThread();
initialize();
@ -193,6 +205,7 @@ public class SponsorBlockSettings {
// Verify url is only the server address and does not contain a path such as: "https://sponsor.ajay.app/api/"
// Could use Patterns.compile, but this is simpler
final int lastDotIndex = serverAddress.lastIndexOf('.');
//noinspection RedundantIfStatement
if (lastDotIndex != -1 && serverAddress.substring(lastDotIndex).contains("/")) {
return false;
}
@ -235,12 +248,4 @@ public class SponsorBlockSettings {
SegmentCategory.updateEnabledCategories();
}
/**
* Updates internal data based on {@link Setting} values.
*/
public static void updateFromImportedSettings() {
SegmentCategory.loadAllCategoriesFromSettings();
SponsorBlockSettingsPreference.updateSegmentCategories();
}
}

View File

@ -360,6 +360,16 @@ public class SponsorBlockUtils {
}
}
public static void showErrorDialog(String dialogMessage) {
Utils.runOnMainThreadNowOrLater(() ->
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
.setMessage(dialogMessage)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.show()
);
}
public static void onEditByHandClicked() {
try {
Utils.verifyOnMainThread();

View File

@ -24,6 +24,7 @@ import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
@ -125,25 +126,28 @@ public class SBRequester {
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
final int responseCode = connection.getResponseCode();
final String messageToToast = switch (responseCode) {
String userMessage = switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS -> str("revanced_sb_submit_succeeded");
case 409 -> str("revanced_sb_submit_failed_duplicate");
case 403 ->
str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
case 403 -> str("revanced_sb_submit_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
case 429 -> str("revanced_sb_submit_failed_rate_limit");
case 400 ->
str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
default ->
str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
case 400 -> str("revanced_sb_submit_failed_invalid",
Requester.parseErrorStringAndDisconnect(connection));
default -> str("revanced_sb_submit_failed_unknown_error",
responseCode, connection.getResponseMessage());
};
Utils.showToastLong(messageToToast);
// Message might be about the users account or an error too large to show in a toast.
// Use a dialog instead.
SponsorBlockUtils.showErrorDialog(userMessage);
} catch (SocketTimeoutException ex) {
// Always show, even if show connection toasts is turned off
Logger.printDebug(() -> "Timeout", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
} catch (IOException ex) {
Logger.printDebug(() -> "IOException", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
} catch (Exception ex) {
Logger.printException(() -> "failed to submit segments", ex);
Logger.printException(() -> "failed to submit segments", ex); // Should never happen.
}
}
@ -184,19 +188,22 @@ public class SBRequester {
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
final int responseCode = connection.getResponseCode();
String userMessage;
switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS:
Logger.printDebug(() -> "Vote success for segment: " + segment);
break;
return;
case 403:
Utils.showToastLong(
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
userMessage = str("revanced_sb_vote_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
break;
default:
Utils.showToastLong(
str("revanced_sb_vote_failed_unknown_error", responseCode, connection.getResponseMessage()));
userMessage = str("revanced_sb_vote_failed_unknown_error",
responseCode, connection.getResponseMessage());
break;
}
SponsorBlockUtils.showErrorDialog(userMessage);
} catch (SocketTimeoutException ex) {
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
} catch (IOException ex) {