feat: Add import from & export settings to a file

Co-authored-by: ILoveOpenSourceApplications <117499019+iloveopensourceapplications@users.noreply.github.com>
This commit is contained in:
oSumAtrIX 2026-03-21 19:52:43 +01:00
parent f063cf69bd
commit 1ebd990051
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
5 changed files with 290 additions and 120 deletions

View file

@ -2,7 +2,7 @@ package app.revanced.extension.shared.settings;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
import android.content.Context; import android.app.Activity;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -122,18 +122,18 @@ public abstract class Setting<T> {
/** /**
* Called after all settings have been imported. * Called after all settings have been imported.
*/ */
void settingsImported(@Nullable Context context); void settingsImported(@Nullable Activity context);
/** /**
* Called after all settings have been exported. * Called after all settings have been exported.
*/ */
void settingsExported(@Nullable Context context); void settingsExported(@Nullable Activity context);
} }
private static final List<ImportExportCallback> importExportCallbacks = new ArrayList<>(); private static final List<ImportExportCallback> importExportCallbacks = new ArrayList<>();
/** /**
* Adds a callback for {@link #importFromJSON(Context, String)} and {@link #exportToJson(Context)}. * Adds a callback for {@link #importFromJSON(Activity, String)} and {@link #exportToJson(Activity)}.
*/ */
public static void addImportExportCallback(ImportExportCallback callback) { public static void addImportExportCallback(ImportExportCallback callback) {
importExportCallbacks.add(Objects.requireNonNull(callback)); importExportCallbacks.add(Objects.requireNonNull(callback));
@ -413,7 +413,7 @@ public abstract class Setting<T> {
json.put(importExportKey, value); json.put(importExportKey, value);
} }
public static String exportToJson(@Nullable Context alertDialogContext) { public static String exportToJson(@Nullable Activity alertDialogContext) {
try { try {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
for (Setting<?> setting : allLoadedSettingsSorted()) { for (Setting<?> setting : allLoadedSettingsSorted()) {
@ -439,11 +439,17 @@ public abstract class Setting<T> {
String export = json.toString(0); String export = json.toString(0);
// Remove the outer JSON braces to make the output more compact, if (export.startsWith("{") && export.endsWith("}")) {
// and leave less chance of the user forgetting to copy it // Remove the outer JSON braces to make the output more compact,
return export.substring(2, export.length() - 2); // and leave less chance of the user forgetting to copy it
export = export.substring(1, export.length() - 1);
}
export = export.replaceAll("^\\n+", "").replaceAll("\\n+$", "");
return export + ",";
} catch (JSONException e) { } catch (JSONException e) {
Logger.printException(() -> "Export failure", e); // should never happen Logger.printException(() -> "Export failure", e); // Should never happen
return ""; return "";
} }
} }
@ -451,10 +457,16 @@ public abstract class Setting<T> {
/** /**
* @return if any settings that require a reboot were changed. * @return if any settings that require a reboot were changed.
*/ */
public static boolean importFromJSON(Context alertDialogContext, String settingsJsonString) { public static boolean importFromJSON(Activity alertDialogContext, String settingsJsonString) {
try { try {
if (!settingsJsonString.matches("[\\s\\S]*\\{")) { settingsJsonString = settingsJsonString.trim();
settingsJsonString = '{' + settingsJsonString + '}'; // Restore outer JSON braces
if (settingsJsonString.endsWith(",")) {
settingsJsonString = settingsJsonString.substring(0, settingsJsonString.length() - 1);
}
if (!settingsJsonString.trim().startsWith("{")) {
settingsJsonString = "{\n" + settingsJsonString + "\n}"; // Restore outer JSON braces
} }
JSONObject json = new JSONObject(settingsJsonString); JSONObject json = new JSONObject(settingsJsonString);

View file

@ -3,8 +3,10 @@ package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
@ -16,11 +18,18 @@ import android.preference.SwitchPreference;
import android.preference.EditTextPreference; import android.preference.EditTextPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.util.Pair; import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
import java.util.Scanner;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.ResourceType;
@ -33,6 +42,9 @@ import app.revanced.extension.shared.ui.CustomDialog;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public abstract class AbstractPreferenceFragment extends PreferenceFragment { public abstract class AbstractPreferenceFragment extends PreferenceFragment {
@SuppressLint("StaticFieldLeak")
public static AbstractPreferenceFragment instance;
/** /**
* Indicates that if a preference changes, * Indicates that if a preference changes,
* to apply the change from the Setting to the UI component. * to apply the change from the Setting to the UI component.
@ -56,6 +68,12 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
@Nullable @Nullable
protected static CharSequence restartDialogTitle, restartDialogMessage, restartDialogButtonText, confirmDialogTitle; protected static CharSequence restartDialogTitle, restartDialogMessage, restartDialogButtonText, confirmDialogTitle;
private static final int READ_REQUEST_CODE = 42;
private static final int WRITE_REQUEST_CODE = 43;
private String existingSettings = "";
private EditText currentImportExportEditText;
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> { private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try { try {
if (updatingPreference) { if (updatingPreference) {
@ -198,8 +216,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
return listPref.getValue().equals(defaultValueString); return listPref.getValue().equals(defaultValueString);
} }
throw new IllegalStateException("Must override method to handle " throw new IllegalStateException("Must override method to handle preference type: " + pref.getClass());
+ "preference type: " + pref.getClass());
} }
/** /**
@ -332,10 +349,230 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
dialogPair.first.show(); dialogPair.first.show();
} }
/**
* Import / Export Subroutines
*/
@NonNull
private Button createDialogButton(Context context, String text, int marginLeft, int marginRight, View.OnClickListener listener) {
int height = (int) android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, 36f, context.getResources().getDisplayMetrics());
int paddingHorizontal = (int) android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, 16f, context.getResources().getDisplayMetrics());
float radius = android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, 20f, context.getResources().getDisplayMetrics());
Button btn = new Button(context, null, 0);
btn.setText(text);
btn.setAllCaps(false);
btn.setTextSize(14);
btn.setSingleLine(true);
btn.setEllipsize(android.text.TextUtils.TruncateAt.END);
btn.setGravity(android.view.Gravity.CENTER);
btn.setPadding(paddingHorizontal, 0, paddingHorizontal, 0);
btn.setTextColor(Utils.isDarkModeEnabled() ? android.graphics.Color.WHITE : android.graphics.Color.BLACK);
android.graphics.drawable.GradientDrawable bg = new android.graphics.drawable.GradientDrawable();
bg.setCornerRadius(radius);
bg.setColor(Utils.getCancelOrNeutralButtonBackgroundColor());
btn.setBackground(bg);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, height, 1.0f);
params.setMargins(marginLeft, 0, marginRight, 0);
btn.setLayoutParams(params);
btn.setOnClickListener(listener);
return btn;
}
public void showImportExportTextDialog() {
try {
Activity context = getActivity();
// Must set text before showing dialog,
// otherwise text is non-selectable if this preference is later reopened.
existingSettings = Setting.exportToJson(context);
currentImportExportEditText = getEditText(context);
// Create a custom dialog with the EditText.
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
context,
str("revanced_pref_import_export_title"), // Title.
null, // No message (EditText replaces it).
currentImportExportEditText, // Pass the EditText.
str("revanced_settings_save"), // OK button text.
() -> importSettingsText(context, currentImportExportEditText.getText().toString()), // OK button action.
() -> {}, // Cancel button action (dismiss only).
str("revanced_settings_import_copy"), // Neutral button (Copy) text.
() -> Utils.setClipboard(currentImportExportEditText.getText().toString()), // Neutral button (Copy) action. Show the user the settings in JSON format.
true // Dismiss dialog when onNeutralClick.
);
LinearLayout fileButtonsContainer = getLinearLayout(context);
int margin = (int) android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, 4f, context.getResources().getDisplayMetrics());
Button btnExport = createDialogButton(context, str("revanced_settings_export_file"), 0, margin, v -> exportActivity());
Button btnImport = createDialogButton(context, str("revanced_settings_import_file"), margin, 0, v -> importActivity());
fileButtonsContainer.addView(btnExport);
fileButtonsContainer.addView(btnImport);
dialogPair.second.addView(fileButtonsContainer, 2);
dialogPair.first.setOnDismissListener(d -> currentImportExportEditText = null);
// If there are no settings yet, then show the on-screen keyboard and bring focus to
// the edit text. This makes it easier to paste saved settings after a reinstallation.
dialogPair.first.setOnShowListener(dialogInterface -> {
if (existingSettings.isEmpty() && currentImportExportEditText != null) {
currentImportExportEditText.postDelayed(() -> {
if (currentImportExportEditText != null) {
currentImportExportEditText.requestFocus();
android.view.inputmethod.InputMethodManager imm = (android.view.inputmethod.InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) imm.showSoftInput(currentImportExportEditText, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
}
}, 100);
}
});
// Show the dialog.
dialogPair.first.show();
} catch (Exception ex) {
Logger.printException(() -> "showImportExportTextDialog failure", ex);
}
}
@NonNull
private static LinearLayout getLinearLayout(Context context) {
LinearLayout fileButtonsContainer = new LinearLayout(context);
fileButtonsContainer.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams fbParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
int marginTop = (int) android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, 16f, context.getResources().getDisplayMetrics());
fbParams.setMargins(0, marginTop, 0, 0);
fileButtonsContainer.setLayoutParams(fbParams);
return fileButtonsContainer;
}
@NonNull
private EditText getEditText(Context context) {
EditText editText = new EditText(context);
editText.setText(existingSettings);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
editText.setAutofillHints((String) null);
}
editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS |
android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE);
editText.setSingleLine(false);
editText.setTextSize(14);
return editText;
}
public void exportActivity() {
try {
Setting.exportToJson(getActivity());
String formatDate = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US).format(new java.util.Date());
String fileName = "revanced_Settings_" + formatDate + ".txt";
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, WRITE_REQUEST_CODE);
} catch (Exception ex) {
Logger.printException(() -> "exportActivity failure", ex);
}
}
public void importActivity() {
try {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
} catch (Exception ex) {
Logger.printException(() -> "importActivity failure", ex);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == WRITE_REQUEST_CODE && resultCode == android.app.Activity.RESULT_OK && data != null) {
exportTextToFile(data.getData());
} else if (requestCode == READ_REQUEST_CODE && resultCode == android.app.Activity.RESULT_OK && data != null) {
importTextFromFile(data.getData());
}
}
protected static void showLocalizedToast(String resourceKey, String fallbackMessage) {
if (ResourceUtils.getIdentifier(ResourceType.STRING, resourceKey) != 0) {
Utils.showToastLong(str(resourceKey));
} else {
Utils.showToastLong(fallbackMessage);
}
}
private void exportTextToFile(android.net.Uri uri) {
try {
OutputStream out = getContext().getContentResolver().openOutputStream(uri);
if (out != null) {
String textToExport = existingSettings;
if (currentImportExportEditText != null) {
textToExport = currentImportExportEditText.getText().toString();
}
out.write(textToExport.getBytes(StandardCharsets.UTF_8));
out.close();
showLocalizedToast("revanced_settings_export_file_success", "Settings exported successfully");
}
} catch (Exception e) {
showLocalizedToast("revanced_settings_export_file_failed", "Failed to export settings");
Logger.printException(() -> "exportTextToFile failure", e);
}
}
@SuppressWarnings("CharsetObjectCanBeUsed")
private void importTextFromFile(android.net.Uri uri) {
try {
InputStream in = getContext().getContentResolver().openInputStream(uri);
if (in != null) {
Scanner scanner = new Scanner(in, StandardCharsets.UTF_8.name()).useDelimiter("\\A");
String result = scanner.hasNext() ? scanner.next() : "";
in.close();
if (currentImportExportEditText != null) {
currentImportExportEditText.setText(result);
showLocalizedToast("revanced_settings_import_file_success", "Settings imported successfully, tap Save to apply");
} else {
importSettingsText(getContext(), result);
}
}
} catch (Exception e) {
showLocalizedToast("revanced_settings_import_file_failed", "Failed to import settings");
Logger.printException(() -> "importTextFromFile failure", e);
}
}
private void importSettingsText(Context context, String replacementSettings) {
try {
existingSettings = Setting.exportToJson(null);
if (replacementSettings.equals(existingSettings)) {
return;
}
settingImportInProgress = true;
final boolean rebootNeeded = Setting.importFromJSON(getActivity(), replacementSettings);
if (rebootNeeded) {
showRestartDialog(context);
}
} catch (Exception ex) {
Logger.printException(() -> "importSettingsText failure", ex);
} finally {
settingImportInProgress = false;
}
}
@SuppressLint("ResourceType") @SuppressLint("ResourceType")
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
instance = this;
try { try {
PreferenceManager preferenceManager = getPreferenceManager(); PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setSharedPreferencesName(Setting.preferences.name); preferenceManager.setSharedPreferencesName(Setting.preferences.name);
@ -354,6 +591,9 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
@Override @Override
public void onDestroy() { public void onDestroy() {
if (instance == this) {
instance = null;
}
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener); getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
super.onDestroy(); super.onDestroy();
} }

View file

@ -4,40 +4,13 @@ import static app.revanced.extension.shared.StringRef.str;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference; import android.preference.Preference;
import android.text.InputType;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.ui.CustomDialog;
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener { public class ImportExportPreference extends Preference implements Preference.OnPreferenceClickListener {
private String existingSettings;
private void init() {
setSelectable(true);
EditText editText = getEditText();
editText.setTextIsSelectable(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
editText.setAutofillHints((String) null);
}
editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
editText.setTextSize(14);
setOnPreferenceClickListener(this);
}
public ImportExportPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public ImportExportPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
@ -56,78 +29,20 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
init(); init();
} }
private void init() {
setOnPreferenceClickListener(this);
}
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
try { try {
// Must set text before showing dialog, if (AbstractPreferenceFragment.instance != null) {
// otherwise text is non-selectable if this preference is later reopened. AbstractPreferenceFragment.instance.showImportExportTextDialog();
existingSettings = Setting.exportToJson(getContext()); }
getEditText().setText(existingSettings);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "showDialog failure", ex); Logger.printException(() -> "onPreferenceClick failure", ex);
} }
return true; return true;
} }
@Override
protected void showDialog(Bundle state) {
try {
Context context = getContext();
EditText editText = getEditText();
// Create a custom dialog with the EditText.
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
context,
str("revanced_pref_import_export_title"), // Title.
null, // No message (EditText replaces it).
editText, // Pass the EditText.
str("revanced_settings_import"), // OK button text.
() -> importSettings(context, editText.getText().toString()), // OK button action.
() -> {}, // Cancel button action (dismiss only).
str("revanced_settings_import_copy"), // Neutral button (Copy) text.
() -> {
// Neutral button (Copy) action. Show the user the settings in JSON format.
Utils.setClipboard(editText.getText());
},
true // Dismiss dialog when onNeutralClick.
);
// If there are no settings yet, then show the on screen keyboard and bring focus to
// the edit text. This makes it easier to paste saved settings after a reinstall.
dialogPair.first.setOnShowListener(dialogInterface -> {
if (existingSettings.isEmpty()) {
editText.postDelayed(() -> {
editText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager)
editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
}, 100);
}
});
// Show the dialog.
dialogPair.first.show();
} catch (Exception ex) {
Logger.printException(() -> "showDialog failure", ex);
}
}
private void importSettings(Context context, String replacementSettings) {
try {
if (replacementSettings.equals(existingSettings)) {
return;
}
AbstractPreferenceFragment.settingImportInProgress = true;
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
if (rebootNeeded) {
AbstractPreferenceFragment.showRestartDialog(context);
}
} catch (Exception ex) {
Logger.printException(() -> "importSettings failure", ex);
} finally {
AbstractPreferenceFragment.settingImportInProgress = false;
}
}
} }

View file

@ -2,8 +2,8 @@ package app.revanced.extension.youtube.sponsorblock;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.util.Pair; import android.util.Pair;
import android.util.Patterns; import android.util.Patterns;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -34,12 +34,12 @@ public class SponsorBlockSettings {
public static final Setting.ImportExportCallback SB_IMPORT_EXPORT_CALLBACK = new Setting.ImportExportCallback() { public static final Setting.ImportExportCallback SB_IMPORT_EXPORT_CALLBACK = new Setting.ImportExportCallback() {
@Override @Override
public void settingsImported(@Nullable Context context) { public void settingsImported(@Nullable Activity context) {
SegmentCategory.loadAllCategoriesFromSettings(); SegmentCategory.loadAllCategoriesFromSettings();
SponsorBlockPreferenceGroup.settingsImported = true; SponsorBlockPreferenceGroup.settingsImported = true;
} }
@Override @Override
public void settingsExported(@Nullable Context context) { public void settingsExported(@Nullable Activity context) {
showExportWarningIfNeeded(context); showExportWarningIfNeeded(context);
} }
}; };
@ -184,16 +184,16 @@ public class SponsorBlockSettings {
/** /**
* Export the categories using flatten JSON (no embedded dictionaries or arrays). * Export the categories using flatten JSON (no embedded dictionaries or arrays).
*/ */
private static void showExportWarningIfNeeded(@Nullable Context dialogContext) { private static void showExportWarningIfNeeded(@Nullable Activity activity) {
Utils.verifyOnMainThread(); Utils.verifyOnMainThread();
initialize(); initialize();
// If user has a SponsorBlock user ID then show a warning. // If user has a SponsorBlock user ID then show a warning.
if (dialogContext != null && SponsorBlockSettings.userHasSBPrivateID() if (activity != null && SponsorBlockSettings.userHasSBPrivateID()
&& !Settings.SB_HIDE_EXPORT_WARNING.get()) { && !Settings.SB_HIDE_EXPORT_WARNING.get()) {
// Create the custom dialog. // Create the custom dialog.
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create( Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
dialogContext, activity,
null, // No title. null, // No title.
str("revanced_sb_settings_revanced_export_user_id_warning"), // Message. str("revanced_sb_settings_revanced_export_user_id_warning"), // Message.
null, // No EditText. null, // No EditText.
@ -205,11 +205,7 @@ public class SponsorBlockSettings {
true // Dismiss dialog when onNeutralClick. true // Dismiss dialog when onNeutralClick.
); );
// Set dialog as non-cancelable. Utils.showDialog(activity, dialogPair.first, false, null);
dialogPair.first.setCancelable(false);
// Show the dialog.
dialogPair.first.show();
} }
} }

View file

@ -95,6 +95,13 @@ To translate new languages or improve the existing translations, visit translate
<string name="revanced_language_DEFAULT">App language</string> <string name="revanced_language_DEFAULT">App language</string>
<string name="revanced_pref_import_export_title">Import / Export</string> <string name="revanced_pref_import_export_title">Import / Export</string>
<string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string> <string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string>
<string name="revanced_settings_import_file">Import from file</string>
<string name="revanced_settings_import_file_success">Settings imported successfully, save to apply</string>
<string name="revanced_settings_import_file_failed">Failed to import settings</string>
<string name="revanced_settings_export_file">Export to file</string>
<string name="revanced_settings_export_file_success">Settings exported successfully</string>
<string name="revanced_settings_export_file_failed">Failed to export settings</string>
<!-- Settings about dialog. --> <!-- Settings about dialog. -->
<string name="revanced_settings_about_links_body">You are using ReVanced Patches version &lt;i>%s&lt;/i></string> <string name="revanced_settings_about_links_body">You are using ReVanced Patches version &lt;i>%s&lt;/i></string>
<string name="revanced_settings_about_links_dev_header">Note</string> <string name="revanced_settings_about_links_dev_header">Note</string>