mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-29 21:44:26 +02:00
feat: Import and export manager settings (#2268)
This commit is contained in:
parent
8c8df698d4
commit
a45d9598cc
@ -197,6 +197,12 @@
|
|||||||
"deleteTempDirLabel": "Delete temporary files",
|
"deleteTempDirLabel": "Delete temporary files",
|
||||||
"deleteTempDirHint": "Delete unused temporary files",
|
"deleteTempDirHint": "Delete unused temporary files",
|
||||||
"deletedTempDir": "Temporary files deleted",
|
"deletedTempDir": "Temporary files deleted",
|
||||||
|
"exportSettingsLabel": "Export settings",
|
||||||
|
"exportSettingsHint": "Export settings to a JSON file",
|
||||||
|
"exportedSettings": "Settings exported",
|
||||||
|
"importSettingsLabel": "Import settings",
|
||||||
|
"importSettingsHint": "Import settings from a JSON file",
|
||||||
|
"importedSettings": "Settings imported",
|
||||||
"exportPatchesLabel": "Export patch selection",
|
"exportPatchesLabel": "Export patch selection",
|
||||||
"exportPatchesHint": "Export patch selection to a JSON file",
|
"exportPatchesHint": "Export patch selection to a JSON file",
|
||||||
"exportedPatches": "Patch selection exported",
|
"exportedPatches": "Patch selection exported",
|
||||||
|
@ -50,6 +50,7 @@ Learn how to configure ReVanced Manager.
|
|||||||
- 🔑 Keystore used to sign patched apps
|
- 🔑 Keystore used to sign patched apps
|
||||||
- 📄 Remembered selection of patches for each app
|
- 📄 Remembered selection of patches for each app
|
||||||
- ⚙️ Remembered patch options
|
- ⚙️ Remembered patch options
|
||||||
|
- 🛠️ Remembered settings
|
||||||
|
|
||||||
> ℹ️ Note
|
> ℹ️ Note
|
||||||
> These can be used to backup and restore or reset settings to default in case of issues.
|
> These can be used to backup and restore or reset settings to default in case of issues.
|
||||||
|
@ -755,6 +755,36 @@ class ManagerAPI {
|
|||||||
return jsonDecode(string);
|
return jsonDecode(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String exportSettings() {
|
||||||
|
final Map<String, dynamic> settings = _prefs
|
||||||
|
.getKeys()
|
||||||
|
.fold<Map<String, dynamic>>({}, (Map<String, dynamic> map, String key) {
|
||||||
|
map[key] = _prefs.get(key);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
return jsonEncode(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> importSettings(String settings) async {
|
||||||
|
final Map<String, dynamic> settingsMap = jsonDecode(settings);
|
||||||
|
settingsMap.forEach((key, value) {
|
||||||
|
if (value is bool) {
|
||||||
|
_prefs.setBool(key, value);
|
||||||
|
} else if (value is int) {
|
||||||
|
_prefs.setInt(key, value);
|
||||||
|
} else if (value is double) {
|
||||||
|
_prefs.setDouble(key, value);
|
||||||
|
} else if (value is String) {
|
||||||
|
_prefs.setString(key, value);
|
||||||
|
} else if (value is List<dynamic>) {
|
||||||
|
_prefs.setStringList(
|
||||||
|
key,
|
||||||
|
value.map((a) => json.encode(a.toJson())).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void resetAllOptions() {
|
void resetAllOptions() {
|
||||||
_prefs.getKeys().where((key) => key.startsWith('patchOption-')).forEach(
|
_prefs.getKeys().where((key) => key.startsWith('patchOption-')).forEach(
|
||||||
(key) {
|
(key) {
|
||||||
|
@ -222,6 +222,53 @@ class SettingsViewModel extends BaseViewModel {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> exportSettings() async {
|
||||||
|
try {
|
||||||
|
final String settings = _managerAPI.exportSettings();
|
||||||
|
final Directory tempDir = await getTemporaryDirectory();
|
||||||
|
final String filePath = '${tempDir.path}/manager_settings.json';
|
||||||
|
final File file = File(filePath);
|
||||||
|
await file.writeAsString(settings);
|
||||||
|
final String? result = await FlutterFileDialog.saveFile(
|
||||||
|
params: SaveFileDialogParams(
|
||||||
|
sourceFilePath: file.path,
|
||||||
|
fileName: 'manager_settings.json',
|
||||||
|
mimeTypesFilter: ['application/json'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
_toast.showBottom(t.settingsView.exportedSettings);
|
||||||
|
}
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> importSettings() async {
|
||||||
|
try {
|
||||||
|
final String? result = await FlutterFileDialog.pickFile(
|
||||||
|
params: const OpenFileDialogParams(
|
||||||
|
fileExtensionsFilter: ['json'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
final File inFile = File(result);
|
||||||
|
final String settings = inFile.readAsStringSync();
|
||||||
|
inFile.delete();
|
||||||
|
_managerAPI.importSettings(settings);
|
||||||
|
_toast.showBottom(t.settingsView.importedSettings);
|
||||||
|
_toast.showBottom(t.settingsView.restartAppForChanges);
|
||||||
|
}
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
_toast.showBottom(t.settingsView.jsonSelectorErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> exportPatches() async {
|
Future<void> exportPatches() async {
|
||||||
try {
|
try {
|
||||||
final File outFile = File(_managerAPI.storedPatchesFile);
|
final File outFile = File(_managerAPI.storedPatchesFile);
|
||||||
|
@ -14,6 +14,30 @@ class SExportSection extends StatelessWidget {
|
|||||||
return SettingsSection(
|
return SettingsSection(
|
||||||
title: t.settingsView.exportSectionTitle,
|
title: t.settingsView.exportSectionTitle,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
title: Text(
|
||||||
|
t.settingsView.exportSettingsLabel,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(t.settingsView.exportSettingsHint),
|
||||||
|
onTap: () => _settingsViewModel.exportSettings(),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
title: Text(
|
||||||
|
t.settingsView.importSettingsLabel,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(t.settingsView.importSettingsHint),
|
||||||
|
onTap: () => _settingsViewModel.importSettings(),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
title: Text(
|
title: Text(
|
||||||
@ -114,7 +138,6 @@ class SExportSection extends StatelessWidget {
|
|||||||
subtitle: Text(t.settingsView.regenerateKeystoreHint),
|
subtitle: Text(t.settingsView.regenerateKeystoreHint),
|
||||||
onTap: () => _showDeleteKeystoreDialog(context),
|
onTap: () => _showDeleteKeystoreDialog(context),
|
||||||
),
|
),
|
||||||
// SManageKeystorePasswordUI(),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user